In app purchases - kuimoani/defold GitHub Wiki

In-app purchases

인-μ•± ꡬ맀 (λ˜λŠ” 인-μ•± 결제)λŠ” μœ μ €λ‚˜ ν”Œλ ˆμ΄μ–΄μ—κ²Œ μΆ”κ°€ μ»¨ν…μΈ λ‚˜ μΆ”κ°€ κΈ°λŠ₯에 λŒ€ν•œ λΉ„μš©μ„ 청ꡬ할 수 μžˆμŠ΅λ‹ˆλ‹€. 이 맀뉴얼은 이 κΈ°λŠ₯을 μœ„ν•œ Defold APIλ₯Ό μ„€λͺ…ν•©λ‹ˆλ‹€.

DefoldλŠ” Apple의 iOS μ•± μŠ€ν† μ–΄μ˜ "in-app purchases" 와 Android μž₯μΉ˜μ—μ„œ Google Play λ˜λŠ” Amazon의 "in-app billing"λ₯Ό μœ„ν•œ μ‹¬ν”Œν•˜κ³  ν†΅μΌλœ μΈν„°νŽ˜μ΄μŠ€λ₯Ό μ œκ³΅ν•©λ‹ˆλ‹€. Facebook Canvas "game payments"λŠ” Facebook Canvasμ—μ„œ μ§€μ›λ©λ‹ˆλ‹€. 이듀 μ„œλΉ„μŠ€λŠ” λ‹€μŒκ³Ό 같이 μ œν’ˆμ„ νŒλ§€ν•  수 μžˆλŠ” 기회λ₯Ό μ œκ³΅ν•©λ‹ˆλ‹€.

  • μ†Œλͺ¨μ„± λ˜λŠ” λΉ„μ†Œλͺ¨μ„±μ˜ ν‘œμ€€ 인-μ•± μ œν’ˆ (1회 결제)
  • ꡬ독(Subscription) (반볡 결제, μžλ™ 결제)

ν˜„μž¬ Defold μΈν„°νŽ˜μ΄μŠ€λŠ” Apple의 Storekit κΈ°λŠ₯κ³Ό μ™„λ²½νžˆ μƒν˜Έμž‘μš© λ©λ‹ˆλ‹€. Google Play와 Facebook Canvas의 κ²½μš°λŠ” μΈν„°νŽ˜μ΄μŠ€λŠ” λ™μΌν•˜λ―€λ‘œ 각 ν”Œλž«νΌμ—μ„œ λ™μΌν•œ μ½”λ“œλ₯Ό μ‹€ν–‰ν•  수 μžˆμŠ΅λ‹ˆλ‹€. ν•˜μ§€λ§Œ 일뢀 ν”„λ‘œμ„ΈμŠ€ 흐름은 ν”Œλž«νΌ λ§ˆλ‹€ λ‹€λ₯Ό 수 μžˆμŠ΅λ‹ˆλ‹€. λ˜ν•œ, ν˜„μž¬ Mac Appstoreλ₯Ό 톡핸 OS X κ΅¬λ§€λŠ” μ§€μ›λ˜κ³  μžˆμ§€ μ•ŠμŠ΅λ‹ˆλ‹€.

Apple, Google, Amazon, Facebook μ—μ„œ μžμ„Έν•œ μ„€λͺ…을 λ³Ό 수 μžˆμŠ΅λ‹ˆλ‹€.

Testing Google Play Billing with static responses

Android μ—μ„œλŠ” Google Play λ‘œλΆ€ν„° 정적 응닡(static responses)을 μ‚¬μš©ν•˜μ—¬ μ•±μ—μ„œ IAP κ΅¬ν˜„μ„ μ‹œμž‘ν•˜λŠ” 것을 μΆ”μ²œν•©λ‹ˆλ‹€. μ΄λ ‡κ²Œ ν•˜λ©΄ 앱을 κ²Œμ‹œν•˜κΈ° 전에 λͺ¨λ“  μ•±μ˜ λ™μž‘μ΄ μ œλŒ€λ‘œ λŒμ•„κ°€λŠ”μ§€ μΈμ¦ν•˜λŠ” 것을 ν™œμ„±ν™” ν•  수 μžˆμŠ΅λ‹ˆλ‹€. μ—¬κΈ°μ—” 정적 인앱 빌링 응닡(static In-app Billing responses)을 ν…ŒμŠ€νŠΈν•˜κΈ° μœ„ν•œ 4개의 μ˜ˆμ•½λœ ν”„λ‘œλ•νŠΈ 아이디가 μžˆμŠ΅λ‹ˆλ‹€.

android.test.purchased

Google PlayλŠ” μ•„μ΄ν…œμ΄ μ„±κ³΅μ μœΌλ‘œ κ²°μ œλ˜μ—ˆλŠ”μ§€ μ‘λ‹΅ν•©λ‹ˆλ‹€. 이 응닡은 κ°€μ§œ ꡬ맀 정보 (예, κ°€μ§œ order ID)도 ν¬ν•¨ν•œ JSON λ¬Έμžμ—΄μ„ ν¬ν•¨ν•˜κ³  μžˆμŠ΅λ‹ˆλ‹€.

android.test.canceled

Google PlayλŠ” κ²°μ œκ°€ μ·¨μ†Œλ˜μ—ˆλŠ”μ§€ μ‘λ‹΅ν•˜λ‹ˆλ‹€. μ΄λŠ” μœ νš¨ν•˜μ§€ μ•Šμ€ μ‹ μš©μΉ΄λ“œμ΄κ±°λ‚˜ 결제 직전에 μ‚¬μš©μžκ°€ 주문을 μ·¨μ†Œ ν•˜λŠ” λ“±μ˜ μ£Όλ¬Έ κ³Όμ • 쀑 μ—λŸ¬κ°€ λ°œμƒν–ˆμ„ λ•Œ λ‚˜νƒ€λ‚  수 μžˆμŠ΅λ‹ˆλ‹€.

android.test.refunded

Google PlayλŠ” κ²°μ œκ°€ ν™˜λΆˆ λ˜μ—ˆλŠ”μ§€ μ‘λ‹΅ν•©λ‹ˆλ‹€.

android.test.item_unavailable

Google PlayλŠ” ꡬ맀쀑인 μ•„μ΄ν…œμ΄ μ‚¬μš©μžμ˜ μ–΄ν”Œλ¦¬μΌ€μ΄μ…˜ μ œν’ˆ λͺ©λ‘μ— 없을 경우 μ‘λ‹΅ν•©λ‹ˆλ‹€.

Setting up your app for purchases/billing

iOS 와 Android 의 ꡬ맀 μ ˆμ°¨λŠ” λΉ„μŠ·ν•©λ‹ˆλ‹€.

  1. Apple μ΄λ‚˜ Google Play 의 개발자둜 λ“±λ‘λ˜μ—ˆλŠ”μ§€ ν™•μΈν•©λ‹ˆλ‹€.

  2. λŒ€μƒ λ””λ°”μ΄μŠ€(target device)μ—μ„œ λ™μž‘ν•˜λ„λ‘ ν”„λ‘œμ νŠΈλ₯Ό μ„€μ •ν•©λ‹ˆλ‹€. iOS development 그리고 Android development κ°€μ΄λ“œλ₯Ό μ°Έκ³ ν•˜μ„Έμš”.

  3. ν…ŒμŠ€νŠΈμš© 앱을 μ„€μ •ν•©λ‹ˆλ‹€:

    • AndroidλŠ” Google Play Developer Console μ—μ„œ ν•  수 μžˆμŠ΅λ‹ˆλ‹€.
    • iOSλŠ” iTunes Connect μ—μ„œ ν•  수 μžˆμŠ΅λ‹ˆλ‹€. App ID (https://developer.apple.com 의 "Member Center"μ—μ„œ λ§Œλ“€μ—ˆλ˜) μ—μ„œ "In-App Purchase" κ°€ ν™œμ„±ν™” λ˜μ–΄μžˆλŠ”μ§€ ν™•μΈν•˜μ‹­μ‹œμ˜€. iTunes Connect and Google Play Dev Console
  4. Google Play의 경우, μ•ŒνŒŒ .apk νŒŒμΌμ„ μ—…λ‘œλ“œν•˜κ³  κ²Œμ‹œ(publish)ν•΄μ•Ό ν•©λ‹ˆλ‹€. iTunes Connect의 κ²½μš°μ—λŠ” μ–΄ν”Œλ¦¬μΌ€μ΄μ…˜μ΄ App Review μŠΉμΈμ„ 받을 λ•Œ κΉŒμ§€λŠ” λ°”μ΄λ„ˆλ¦¬λ₯Ό μ—…λ‘œλ“œ ν•˜μ§€ λ§ˆμ‹­μ‹œμ˜€. iTunes Connect에 μ—…λ‘œλ“œ ν–ˆλŠ”λ° μ œλŒ€λ‘œ λ™μž‘ν•˜μ§€ μ•ŠλŠ”λ‹€λ©΄ Apple이 이λ₯Ό 리젝(reject)ν•  μˆ˜λ„ μžˆμŠ΅λ‹ˆλ‹€.

  5. 앱을 μœ„ν•œ ν”„λ‘œλ•νŠΈλ₯Ό μƒμ„±ν•©λ‹ˆλ‹€. iTunes Products -- Google Play Products

  6. ν…ŒμŠ€νŠΈ μ‚¬μš©μžλ₯Ό μΆ”κ°€ν•©λ‹ˆλ‹€.

    • iTunes Connect νŽ˜μ΄μ§€μ˜ Users and Rolesμ—μ„œλŠ” μƒŒλ“œλ°•μŠ€ ν™˜κ²½μ—μ„œ 결제λ₯Ό ν…ŒμŠ€νŠΈν•  수 μžˆλŠ” μ‚¬μš©μžλ₯Ό μΆ”κ°€ν•  수 μžˆμŠ΅λ‹ˆλ‹€. Developer μΈμ¦μ„œλ₯Ό 앱에 μ„œλͺ…ν•˜κ³  ν…ŒμŠ€νŠΈ μž₯치의 Appstoreμ—μ„œ μƒŒλ“œλ°•μŠ€ 계정을 μ‚¬μš©ν•΄μ•Ό ν•©λ‹ˆλ‹€.

    • Google Play Developer Console μ—μ„œλŠ” Settings > Account Details μ—μ„œ License Testing μ„Ήμ…˜μ— μ‚¬μš©μž 이메일을 μΆ”κ°€ν•  수 μžˆμŠ΅λ‹ˆλ‹€. 이메일은 μ‰Όν‘œ(,)둜 ꡬ뢄해 μ—¬λŸ¬ 이메일을 μΆ”κ°€ν•  수 있으며, 이 μ‚¬μš©μžλ“€μ€ μ‹€μ œ λˆμ„ κ²°μ œν•˜μ§€ μ•Šκ³  ꡬ맀 절차λ₯Ό ν…ŒμŠ€νŠΈ ν•  수 μžˆμŠ΅λ‹ˆλ‹€.

    • λ˜ν•œ Google Play μ—μ„œλŠ” Google Group 을 μ„€μ •ν•΄μ„œ Alpha와 Beta μŠ€ν† μ–΄μ—μ„œ 앱을 λ‹€μš΄λ‘œλ“œ ν•  수 있게 ν…ŒμŠ€ν„° 그룹을 관리할 μˆ˜λ„ μžˆμŠ΅λ‹ˆλ‹€. Alpha Testing 탭을 ν΄λ¦­ν•˜κ³  Manage list of testersλ₯Ό λˆŒλŸ¬μ„œ Alpha ν…ŒμŠ€ν„°λ‘œ Google Group을 μΆ”κ°€ν•œ ν›„ λ‚˜νƒ€λ‚˜λŠ” 링크λ₯Ό κ³΅μœ ν•˜λ©΄ λ©λ‹ˆλ‹€.

Alpha testers

Facebookμ—μ„œμ˜ 절차:

  1. Facebook developer 에 λ“±λ‘λ˜μ–΄ μžˆλŠ”μ§€ ν™•μΈν•©λ‹ˆλ‹€. Facebook for developers의 "My Apps" κ³Ό "Register as a developer" 단계λ₯Ό 따라 ν•˜μ‹­μ‹œμ˜€.

  2. Facebook은 폭넓은 결제 κΈ°λŠ₯을 κ°€μ§€κ³  있으며 동기식(synchronous)κ³Ό 비동기(asynchronous)식 결제λ₯Ό μ§€μ›ν•˜λŠ” 것을 μš”κ΅¬ν•˜κ³  μžˆμŠ΅λ‹ˆλ‹€. 더 λ§Žμ€ μ •λ³΄λŠ” Payment overview μ—μ„œ 확인 λ°”λžλ‹ˆλ‹€.

  3. μ•± ν˜ΈμŠ€νŒ…(app hosting)κ³Ό 콜백 μ„œλ²„(callback server)λ₯Ό μ„€μ •ν•©λ‹ˆλ‹€:

    • ν”„λ‘œμ νŠΈλ₯Ό ν˜ΈμŠ€νŒ…ν•˜λŠ” secure canvas URL을 μ„€μ •ν•΄μ•Ό ν•©λ‹ˆλ‹€. μžμ„Έν•œ μ„€λͺ…은 Games on Facebook μ—μ„œ λ³Ό 수 μžˆμŠ΅λ‹ˆλ‹€.
    • λ‹€μŒμœΌλ‘œ 콜백 μ„œλ²„λ₯Ό μ„€μ •ν•˜κΈ° μœ„ν•΄ Setting up your callback server 단계λ₯Ό μˆ˜ν–‰ν•΄ μ£Όμ‹­μ‹œμ˜€.
  4. Facebook Developer Dashboardμ—μ„œ μΊ”λ²„μŠ€ μ•±(canvas app)을 μ„€μ •ν•©λ‹ˆλ‹€.

  5. μ•± λŒ€μ‰¬λ³΄λ“œμ˜ "Canvas Payments"μ—μ„œ ν…ŒμŠ€νŠΈ μ‚¬μš©μžλ₯Ό μΆ”κ°€ν•©λ‹ˆλ‹€.

  6. Defining products μ—μ„œ ν”„λ‘œλ•νŠΈ 앱을 μƒμ„±ν•©λ‹ˆλ‹€.

Asynchronous transactions

IAP APIλŠ” 비동기 λ°©μ‹μž…λ‹ˆλ‹€. 즉 ν”„λ‘œκ·Έλž¨μ΄ μ„œλ²„λ‘œ 각 μš”μ²­μ„ 보낸 후에도 ν”„λ‘œκ·Έλž¨μ„ μ€‘λ‹¨ν•˜μ§€ μ•Šμ€ μ±„λ‘œ 응닡을 κΈ°λ‹€λ¦¬λŠ” 것을 λ§ν•©λ‹ˆλ‹€. λŒ€μ‹  ν”„λ‘œκ·Έλž¨μ€ μ •μƒμ μœΌλ‘œ λ™μž‘ν•˜λ©° 응닡이 λ„μ°©ν•˜λ©΄ 이 응닡 데이터에 λ°˜μ‘ν•˜λŠ” 콜백 ν•¨μˆ˜(callback function)κ°€ 호좜(invoke)λ©λ‹ˆλ‹€.

μ‚¬μš©κ°€λŠ₯ν•œ λͺ¨λ“  μ œν’ˆ λͺ©λ‘μ„ κ°€μ Έμ˜€λ €λ©΄ μ•„λž˜μ™€ 같이 ν•  수 μžˆμŠ΅λ‹ˆλ‹€.

local COINS_ID = "com.defold.examples.coins"
local LOGO_ID = "com.defold.examples.logo"

local function product_list(self, products, error)
	if error == nil then
		for i,p in pairs(products) do
			print(p.ident)
			print(p.title)
			print(p.description)
			print(p.currency_code)
			print(p.price_string)
		end
	else
		print(error.error)
	end
end

function init(self)
	-- Initiate a fetch of products
	iap.list({ COINS_ID, LOGO_ID }, product_list)
end

μ‹€μ œ νŠΈλžœμž­μ…˜μ„ μˆ˜ν–‰ν•˜λ €λ©΄, λ¨Όμ € νŠΈλžœμž­μ…˜ κ²°κ³Όλ₯Ό μˆ˜μ‹ ν•˜λŠ” ν•¨μˆ˜λ₯Ό λ“±λ‘ν•˜κ³  μ λ‹Ήν•œ λ•Œμ— 이 ν•¨μˆ˜λ₯Ό ν˜ΈμΆœν•˜λ©΄ λ©λ‹ˆλ‹€.

    local function iap_listener(self, transaction, error)
	    if error == nil then
            if transaction.state == iap.TRANS_STATE_PURCHASING then
                print("Purchasing...")
            elseif transaction.state == iap.TRANS_STATE_PURCHASED then
                print("Purchased!")
            elseif transaction.state == iap.TRANS_STATE_UNVERIFIED then
                print("Unverified!")
            elseif transaction.state == iap.TRANS_STATE_FAILED then
                print("Failed!")
            elseif transaction.state == iap.TRANS_STATE_RESTORED then
                print("Restored")
            end
        else
            print(error.error)
        end
    end

    function on_message(self, message_id, message, sender)

        ...
    	-- IAP νŠΈλž˜μž­μ…˜μ„ μˆ˜μ‹ ν•˜λŠ” ν•¨μˆ˜λ₯Ό λ“±λ‘ν•˜κΈ°
        iap.set_listener(iap_listener)
        -- 코인 ꡬ맀 μ΄ˆκΈ°ν™”ν•˜κΈ°
        iap.buy(COINS_ID)
        ...

    end

μž₯치의 μš΄μ˜μ²΄μ œλŠ” μ‚¬μš©μžκ°€ ꡬ맀λ₯Ό ν•  수 μžˆλŠ” νŒμ—…μ°½μ„ μžλ™μœΌλ‘œ λ„μ›Œμ€λ‹ˆλ‹€. 이 μΈν„°νŽ˜μ΄μŠ€λŠ” ν…ŒμŠ€νŠΈ/μƒŒλ“œλ°•μŠ€ ν™˜κ²½μ—μ„œ 싀행쀑인지 μ—¬λΆ€λ₯Ό λ³΄μ—¬μ€λ‹ˆλ‹€.

Confirm purchase

Android purchase

Confirm purchase

Synchronous payments

λŒ€λΆ€λΆ„μ˜ 결제 제곡자(payment providers)λŠ” 동기식 κ²°μ œλ§Œμ„ μ§€μ›ν•©λ‹ˆλ‹€. 즉 ν΄λΌμ΄μ–ΈνŠΈ(λ‹Ήμ‹ μ˜ μ–΄ν”Œλ¦¬μΌ€μ΄μ…˜)은 κ²°μ œκ°€ μ™„λ£Œλ  λ•Œ TRANS_STATE_PURCHASED 와 같은 μ•Œλ¦Όμ„ λ°›λŠ”λ‹€λŠ” 것을 λœ»ν•©λ‹ˆλ‹€. μ΄λŠ” 결제의 λ§ˆμ§€λ§‰ μƒνƒœ(final state)이며 이 νŠΈλžœμž­μ…˜μ—μ„œ 더 μ΄μƒμ˜ μ½œλ°±μ„ μ œκ³΅ν•˜μ§€ μ•ŠμŠ΅λ‹ˆλ‹€.

Asynchronous payments

일뢀 결제 μ œκ³΅μžλŠ” 비동기 결제λ₯Ό 지원할 것을 μš”κ΅¬ν•˜κ³  μžˆμŠ΅λ‹ˆλ‹€. 즉 ν΄λΌμ΄μ–ΈνŠΈ(λ‹Ήμ‹ μ˜ μ–΄ν”Œλ¦¬μΌ€μ΄μ…˜)은 κ²°μ œκ°€ μ΄ˆκΈ°ν™” 될 λ•Œλ§Œ μ•Œλ¦Όμ„ 받을 수 μžˆμŠ΅λ‹ˆλ‹€. 결제 μ™„λ£Œλ₯Ό ν™•μΈν•˜κΈ° μœ„ν•΄μ„œλŠ” 개발자 μ„œλ²„(λ˜λŠ” ν΄λΌμ΄μ–ΈνŠΈ)와 ν†΅μ‹ ν•˜μ—¬ κ²°μ œκ°€ μœ νš¨ν•œμ§€ 확인해야 ν•©λ‹ˆλ‹€. 비동기 결제λ₯Ό μ΄ˆκΈ°ν™” ν–ˆμ„ 경우 iap μˆ˜μ‹ μž(iap listener)λŠ” TRANS_STATE_UNVERIFIED λ˜λŠ” TRANS_STATE_PURCHASED λ₯Ό 받을 수 μžˆμŠ΅λ‹ˆλ‹€. μ΄λŠ” 결제의 λ§ˆμ§€λ§‰ μƒνƒœ(final state)μ΄λ―€λ‘œ 이 νŠΈλžœμž­μ…˜μ—μ„œ 더 μ΄μƒμ˜ μ½œλ°±μ„ μ œκ³΅ν•˜μ§€ μ•ŠμŠ΅λ‹ˆλ‹€.

Purchase fulfillment

결제 μ œκ³΅μžλ‘œλΆ€ν„° ꡬ맀λ₯Ό μ™„λ£Œν•˜κΈ° μœ„ν•΄μ„œλŠ”, μ–΄ν”Œλ¦¬μΌ€μ΄μ…˜μ€ ꡬ맀 μ΄ν–‰μ„œ(purchase fulfillment)λ₯Ό 결제 μ œκ³΅μžμ—κ²Œ μ•Œλ¦΄ ν•„μš”κ°€ μžˆμŠ΅λ‹ˆλ‹€.(예: 개발자 μ„œλ²„ μ‚¬μ΄λ“œμ—μ„œ 확인). IapλŠ” ꡬ맀가 μ™„λ£Œλ  경우(κΈ°λ³Έ λ™μž‘) κ³΅κΈ‰μžμ—κ²Œ μžλ™μœΌλ‘œ μ΄ν–‰μ„œ(fulfillment)λ₯Ό μ•Œλ €μ£ΌλŠ” μžλ™-μ™„μ„± κΈ°λŠ₯을 μ§€μ›ν•©λ‹ˆλ‹€. λ˜ν•œ game project μ„€μ •μ—μ„œ 이 κΈ°λŠ₯을 λΉ„ν™œμ„±ν™” ν•  μˆ˜λ„ μžˆμŠ΅λ‹ˆλ‹€. 그러면 νŠΈλžœμž­μ…˜μ΄ μ™„λ£Œλ  λ•Œ iap.finish()λ₯Ό ν˜ΈμΆœν•΄μ„œ ꡬ맀 μ΄ν–‰μ„œλ₯Ό μ œκ³΅μžμ—κ²Œ λ„˜κΈΈ 수 μžˆμŠ΅λ‹ˆλ‹€.

Transaction receipt

영수증(receipt)은 κ²°μ œκ°€ μ„±κ³΅μ μœΌλ‘œ μ²˜λ¦¬λ˜μ—ˆλŠ”μ§€ ν™•μΈν•˜κΈ° μœ„ν•΄μ„œ App Store둜 μ „μ†‘ν•˜λŠ” μ„œλͺ…λœ λ°μ΄ν„°μ˜ 묢음(chunk)μž…λ‹ˆλ‹€. μ΄λŠ” λ³„λ„μ˜ μ„œλ²„λ₯Ό μ‚¬μš©ν•˜μ—¬ 결제 진행을 ν™•μΈν•˜λŠ” 상점을 섀계할 λ•Œ 맀우 μœ μš©ν•©λ‹ˆλ‹€.

Troubleshooting

Android iap.list()κ°€ "failed to fetch product"λ₯Ό λ¦¬ν„΄ν•©λ‹ˆλ‹€.

Google Play Developer Console 의 alphaλ‚˜ beta 채널에 .apkλ₯Ό μ—…λ‘œλ“œν•˜κ³  κ²Œμ‹œ(publish)ν•΄μ•Ό ν•©λ‹ˆλ‹€. λ˜ν•œ λ‹Ήμ‹ μ˜ μž₯치의 μ‹œκ°„μ΄λ‚˜ λ‚ μ§œκ°€ μ •ν™•ν•œμ§€ 확인해 λ³΄μ„Έμš”.

iOS iap.list() κ°€ 아무것도 λ¦¬ν„΄ν•˜μ§€ μ•ŠμŠ΅λ‹ˆλ‹€.

iOS Paid Applications 계정을 μš”μ²­ν•΄μ„œ μ˜¬λ°”λ₯Έ λ¬Έμ„œκ°€ μ œμΆœλ˜μ—ˆλŠ”μ§€ ν™•μΈν•©λ‹ˆλ‹€. μ μ ˆν•œ 승인(authorization) μ—†μ΄λŠ” iOS μ•± ꡬ맀(ν…ŒμŠ€νŠΈ ꡬ맀도)κ°€ μ œλŒ€λ‘œ λ™μž‘ν•˜μ§€ μ•ŠμŠ΅λ‹ˆλ‹€.

"Member Center" 의 AppIdμ—μ„œ in-app purchasesκ°€ ν™œμ„±ν™”(activated)λ˜μ–΄ μžˆλŠ”μ§€ ν™•μΈν•˜κ³  μ•±(λ˜λŠ” dev-app)에 ν”„λ‘œλΉ„μ Έλ‹ ν”„λ‘œνŒŒμΌμ΄ AppId와 μ˜¬λ°”λ₯Έ λ‚ μ§œλ‘œ μ„œλͺ…λ˜μ–΄ μžˆλŠ”μ§€ ν™•μΈν•©λ‹ˆλ‹€. ("Member Center"의 "Certificates, Identifiers & Profiles"μ—μ„œ provisioning profile details 의 "Enabled Services:" ν•„λ“œ 확인)

μ—¬κΈ°μ„œ In-App product IDκ°€ μƒŒλ“œλ°•μŠ€ ν™˜κ²½μœΌλ‘œ μ „νŒŒλ˜λŠ”λ° λͺ‡ μ‹œκ°„μ΄ 걸릴 μˆ˜λ„ μžˆμŠ΅λ‹ˆλ‹€.

iOS iap.list() fails logging error "Unexpected callback set"

iap.listλŠ” 쀑첩 호좜(nested calls)λ₯Ό μ§€μ›ν•˜μ§€ μ•ŠμŠ΅λ‹ˆλ‹€. iap.list μ½œλ°±ν•¨μˆ˜μ—μ„œ iap.list λ₯Ό ν˜ΈμΆœν•˜λŠ” 것은 이 μ—λŸ¬λ₯Ό μˆ˜μ§‘ν•˜λŠ” μ—”μ§„μ—μ„œ λ¬΄μ‹œν•˜κ²Œ λ©λ‹ˆλ‹€.

On iOS, the "price_string" field contains ~ characters

~ λ¬ΈμžλŠ” 폰트 νŒŒμΌμ—μ„œ μΌμΉ˜ν•˜λŠ” 문자λ₯Ό 찾을 수 μ—†λŠ” placeholdersμž…λ‹ˆλ‹€. iap.list()λ₯Ό μ‚¬μš©ν•˜λŠ” 경우 μ œν’ˆ λͺ©λ‘μ—μ„œ λ°˜ν™˜λœ "price_string" ν•„λ“œλŠ” 이 κ°’κ³Ό 화폐 λΆ„λͺ¨(currency denominator) μ‚¬μ΄μ—μ„œ 쀄 λ°”κΏˆ μ—†λŠ” 곡백(non breaking space (\u00a0))으둜 ν˜•μ‹ν™” λ©λ‹ˆλ‹€. λ§Œμ•½ GUIμ—μ„œ 이 λ¬Έμžμ—΄μ„ λ Œλ”λ§ν•˜λ €λ©΄, 폰트의 extra_characters ν•„λ“œμ— 이 문자λ₯Ό μΆ”κ°€ν•  ν•„μš”κ°€ μžˆμŠ΅λ‹ˆλ‹€. Mac OS Xμ—μ„œλŠ” 쀄 λ°”κΏˆ μ—†λŠ” 곡백(non breaking spaces)을 Option-<SPACE> λ₯Ό λˆŒλŸ¬μ„œ μž…λ ₯ν•  수 μžˆμŠ΅λ‹ˆλ‹€. μžμ„Έν•œ μ •λ³΄λŠ” https://ko.wikipedia.org/wiki/쀄 λ°”κΏˆ μ—†λŠ” 곡백 λ₯Ό 확인 λ°”λžλ‹ˆλ‹€.

⚠️ **GitHub.com Fallback** ⚠️