Version: 2020.1
Android 인앱 구매 스토어와 크로스 스토어 설치 문제
유니버설 Windows 플랫폼

iOS & Mac 앱 스토어

확장 기능

앱 영수증(App Receipt) 읽기

앱 영수증은 장치 로컬 저장소에 저장되며 아래의 방법으로 읽어올 수 있습니다.

var builder = ConfigurationBuilder.Instance(StandardPurchasingModule.Instance());
string receipt = builder.Configure<IAppleConfiguration>().appReceipt;

결제 제한사항 확인

앱 내 구매가 장치 설정으로 제한되어 있을 수도 있으며 아래의 방법으로 확인할 수 있습니다.

var builder = ConfigurationBuilder.Instance(StandardPurchasingModule.Instance());
bool canMakePayments = builder.Configure<IAppleConfiguration>().canMakePayments;

거래 복구

Apple 플랫폼의 경우 사용자가 비밀번호를 입력해야만 이전 거래 내역을 검색해서 가져올 수 있으므로 애플리케이션 내에 해당 과정을 진행할 수 있는 버튼이 제공되어야 합니다. 이 과정 동안 IStoreListenerProcessPurchase 메서드가 사용자가 소유하고 있는 각각의 항목에 대해 호출됩니다.

/// <summary>
/// Your IStoreListener implementation of OnInitialized.
/// </summary>
public void OnInitialized(IStoreController controller, IExtensionProvider extensions)
{
    extensions.GetExtension<IAppleExtensions> ().RestoreTransactions (result => {
        if (result) {
            // This does not mean anything was restored,
            // merely that the restoration process succeeded.
        } else {
            // Restoration failed.
        }
    });
}

앱 영수증(App Receipt) 새로고침

Apple은 로컬 저장소에 영수증이 현재 캐시되어 있지 않은 경우 일반적으로 사용하는 새로 앱 영수증을 서버에서 페치하는 메커니즘을 제공합니다. SKReceiptRefreshRequest.

이 경우 사용자가 비밀번호를 입력해야 한다는 점을 참고해야 합니다.

Unity IAP에서는 다음과 같은 방법으로 이 메서드를 사용할 수 있습니다.

/// <summary>
/// Your IStoreListener implementation of OnInitialized.
/// </summary>
public void OnInitialized(IStoreController controller, IExtensionProvider extensions)
{
    extensions.GetExtension<IAppleExtensions> ().RefreshAppReceipt (receipt => {
        // This handler is invoked if the request is successful.
        // Receipt will be the latest app receipt.
        Console.WriteLine(receipt);
    },
    () => {
        // This handler will be invoked if the request fails,
        // such as if the network is unavailable or the user
        // enters the wrong password.
    });
}

구매 허가

iOS 8에서는 새로운 보호자 관리 기능인 구매 허가를 도입했습니다.

구매 허가 기능은 부모의 승인이 있을 때까지 구매를 지연합니다. 이 기능을 사용하면 Unity IAP에서 다음과 같은 알림을 앱에 보냅니다.

/// This is called when Unity IAP has finished initialising.
public void OnInitialized(IStoreController controller, IExtensionProvider extensions)
{
    extensions.GetExtension<IAppleExtensions>().RegisterPurchaseDeferredListener(product => {
        Console.WriteLine(product.definition.id);
    });
}

Sandbox 앱 스토어에서 “Ask-to-Buy” 시뮬레이션 활성화

아래 예제 클래스는 Sandbox 앱 스토어에서 Ask-to-Buy 시뮬레이션을 활성화하기 위해 IAppleExtensions에 액세스하는 방법을 보여줍니다.

using UnityEngine;
using UnityEngine.Purchasing;

public class AppleSimulateAskToBuy : MonoBehaviour {
    public void SetSimulateAskToBuy(bool shouldSimulateAskToBuy) {
        if (Application.platform == RuntimePlatform.IPhonePlayer) {
            IAppleExtensions extensions = IAPButton.IAPButtonStoreManager.Instance.ExtensionProvider.GetExtension<IAppleExtensions>();
            extensions.simulateAskToBuy = shouldSimulateAskToBuy;
        }
    }
}

구매가 승인 또는 거절되면 스토어의 노멀 ProcessPurchase 또는 OnPurchaseFailed 리스너 메서드가 호출됩니다.

거래 영수증

때때로 소모품 Ask to Buy 구매가 앱 영수증에 표시되지 않을 수 있는데, 이 경우에는 영수증으로 구매를 확인할 수 없습니다. 하지만 iOS는 Ask to Buy를 비롯하여 모든 구매가 포함된 거래 영수증을 제공합니다. IAppleExtensions을 사용하여 특정 Product에 대한 최신 거래 영수증 문자열에 액세스하십시오.

참고: 거래 영수증은 Mac 빌드에서 제공되지 않습니다. Mac 빌드에서 거래 영수증을 요청하면 빈 문자열을 가져옵니다.

# if UNITY_PURCHASING

using System;
using UnityEngine;
using UnityEngine.Purchasing;

public class AskToBuy : MonoBehaviour, IStoreListener
{
    // Unity IAP objects
    private IStoreController m_Controller;
    private IAppleExtensions m_AppleExtensions;

    public AskToBuy ()
    {
        var builder = ConfigurationBuilder.Instance (StandardPurchasingModule.Instance ());
        builder.AddProduct ("100_gold_coins", ProductType.Consumable, new IDs {
            { "100_gold_coins_google", GooglePlay.Name },
            { "100_gold_coins_mac", MacAppStore.Name }
        });

        UnityPurchasing.Initialize (this, builder);
    }

    /// <summary>
    /// This will be called when Unity IAP has finished initialising.
    /// </summary>
    public void OnInitialized (IStoreController controller, IExtensionProvider extensions)
    {
        m_Controller = controller;
        m_AppleExtensions = extensions.GetExtension<IAppleExtensions> ();

        // On Apple platforms we need to handle deferred purchases caused by Apple's Ask to Buy feature.
        // On non-Apple platforms this will have no effect; OnDeferred will never be called.
        m_AppleExtensions.RegisterPurchaseDeferredListener (OnDeferred);
    }

    /// <summary>
    /// This will be called when a purchase completes.
    /// </summary>
    public PurchaseProcessingResult ProcessPurchase (PurchaseEventArgs e)
    {
        if (Application.platform == RuntimePlatform.IPhonePlayer ||
            Application.platform == RuntimePlatform.tvOS) {
            string transactionReceipt = m_AppleExtensions.GetTransactionReceiptForProduct (e.purchasedProduct);
            Console.WriteLine (transactionReceipt);
            // Send transaction receipt to server for validation
        }
        return PurchaseProcessingResult.Complete;
    }

    /// <summary>
    /// Called when Unity IAP encounters an unrecoverable initialization error.
    ///
    /// Note that this will not be called if Internet is unavailable; Unity IAP
    /// will attempt initialization until it becomes available.
    /// </summary>
    public void OnInitializeFailed (InitializationFailureReason error)
    {
    }

    /// <summary>
    /// Called when a purchase fails.
    /// </summary>
    public void OnPurchaseFailed (Product i, PurchaseFailureReason p)
    {
    }

    /// <summary>
    /// iOS Specific.
    /// This is called as part of Apple's 'Ask to buy' functionality,
    /// when a purchase is requested by a minor and referred to a parent
    /// for approval.
    ///
    /// When the purchase is approved or rejected, the normal purchase events
    /// will fire.
    /// </summary>
    /// <param name="item">Item.</param>
    private void OnDeferred (Product item)
    {
        Debug.Log ("Purchase deferred: " + item.definition.id);
    }
}

# endif // UNITY_PURCHASING

앱 영수증과 달리 거래 영수증은 로컬로 검증할 수 없습니다. 대신에 영수증 문자열을 원격 서버에 보내 검증해야 합니다. 이미 원격 서버를 사용하여 앱 영수증을 검증하는 경우에는 거래 영수증을 동일한 Apple 엔드포인트에 전송하여 JSON 리스폰스를 받아야 합니다.

JSON 리스폰스 예제:

{
    "receipt": {
        "original_purchase_date_pst": "2017-11-15 15:25:20 America/Los_Angeles",
        "purchase_date_ms": "1510788320209",
        "unique_identifier": "0ea7808637555b2c633eb07aa1cb0894c821a6f9",
        "original_transaction_id": "1000000352597239",
        "bvrs": "0",
        "transaction_id": "1000000352597239",
        "quantity": "1",
        "unique_vendor_identifier": "01B57C2E-9E91-42FF-9B0D-4983175D6694",
        "item_id": "1141751870",
        "original_purchase_date": "2017-11-15 23:25:20 Etc/GMT",
        "product_id": "100.gold.coins",
        "purchase_date": "2017-11-15 23:25:20 Etc/GMT",
        "is_trial_period": "false",
        "purchase_date_pst": "2017-11-15 15:25:20 America/Los_Angeles",
        "bid": "com.unity3d.unityiap.demo",
        "original_purchase_date_ms": "1510788320209"
    },
    "status": 0
}

Apple 홍보 구매 인터셉트

Apple은 앱의 상품 페이지를 통해 게임 내 구매를 홍보하도록 허용합니다. 기존 인앱 구매와 달리 Apple 홍보 구매는 iOS 및 tvOS의 앱 스토어에서 직접 시작됩니다. 그러면 앱 스토어가 거래를 완료할 수 있도록 앱을 실행합니다. 앱이 설치되지 않았다면 사용자가 앱을 다운로드하도록 유도합니다.

IAppleConfiguration SetApplePromotionalPurchaseInterceptor 콜백 메서드는 Apple 홍보 구매를 가로챕니다. 이 콜백을 사용하면 Apple에 구매를 전송하기 전에 Parental Gate(자녀 결제 보호)를 제공하거나, 분석 이벤트를 전송하거나, 기타 작업을 수행할 수 있습니다. 이 콜백은 사용자가 구매하려고 하는 Product를 사용합니다. 홍보 구매를 계속하려면 IAppleExtensions.ContinuePromotionalPurchases()를 호출해야 합니다. 그러면 대기열에 있는 모든 결제가 시작됩니다.

콜백을 설정하지 않으면 홍보 구매가 즉시 진행되고 그 결과로 ProcessPurchase를 호출합니다.

참고: 다른 플랫폼에서 이 API를 호출하면 아무런 영향도 발생하지 않습니다.

private IAppleExtensions m_AppleExtensions;

public void Awake() {
    var module = StandardPurchasingModule.Instance();
    var builder = ConfigurationBuilder.Instance(module);
        // On iOS and tvOS we can intercept promotional purchases that come directly from
        // the App Store.
        // On other platforms this will have no effect; OnPromotionalPurchase will never be
        // called.
    builder.Configure<IAppleConfiguration>().
     SetApplePromotionalPurchaseInterceptorCallback(OnPromotionalPurchase);
    Debug.Log("Setting Apple promotional purchase interceptor callback");
}

public void OnInitialized(IStoreController controller, IExtensionProvider extensions) {
    m_AppleExtensions = extensions.GetExtension<IAppleExtensions>();
    foreach (var item in controller.products.all) {
        if (item.availableToPurchase) {
            // Set all these products to be visible in the user's App Store
            m_AppleExtensions.SetStorePromotionVisibility(item, AppleStorePromotionVisibility.Show);
        }
    }
}

private void OnPromotionalPurchase(Product item) {
    Debug.Log("Attempted promotional purchase: " + item.definition.id);
    // Promotional purchase has been detected.
    // Handle this event by, e.g. presenting a parental gate.
    // Here, for demonstration purposes only, we will wait five seconds before continuing
    // the purchase.
    StartCoroutine(ContinuePromotionalPurchases());
}

private IEnumerator ContinuePromotionalPurchases() {
    Debug.Log("Continuing promotional purchases in 5 seconds");
    yield return new WaitForSeconds(5);
    Debug.Log("Continuing promotional purchases now");
    m_AppleExtensions.ContinuePromotionalPurchases (); // iOS and tvOS only
}

테스트

Apple 스토어에서 테스트를 진행하려면 iTunes Connect에서 생성할 수 있는 iTunes Connect 테스트 계정을 사용해야 합니다.

iOS 기기나 노트북에서 앱 스토어 로그아웃을 한 후 애플리케이션을 실행하고 구매나 거래 복원 시도를 하면 로그인 화면이 나타납니다.

NoProductsAvailable을 이유로 초기화 실패를 수신하면 다음 체크리스트를 확인하십시오.

  • iTunes Connect 제품 식별자가 Unity IAP에 제공된 제품 식별자와 정확하게 일치해야 합니다.
  • iTunes Connect 애플리케이션에 대해 애플리케이션 내 구매가 활성화되어 있어야 합니다.
  • 해당 상품이 iTunes Connect에서 판매 중이어야 합니다.
  • iTunes Connect 제품을 새로 생성한 경우 구매가 가능하려면 몇 시간이 소요될 수 있습니다.
  • 최신 iTunes Connect 개발자 약관에 동의하고 유효한 계좌 정보를 등록해야 합니다.

Mac 앱 스토어(Mac App Store)

데스크톱 Mac 빌드를 빌드하는 경우 Unity의 빌드 설정 내에서 Mac App Store validation을 선택해야 합니다.

앱을 빌드한 후 번들 식별자 및 버전 문자열을 사용해 앱의 info.plist 파일을 업데이트해야 합니다. .app 파일을 오른쪽 클릭하고 show package contents를 클릭한 후 info.plist 파일을 찾고 CFBundleIdentifier 문자열을 애플리케이션의 번들 식별자로 업데이트하십시오.

애플리케이션을 서명, 패키지, 설치해야 합니다. OSX 터미널에서 다음 커맨드를 실행해야 합니다.

codesign -f --deep -s "3rd Party Mac Developer Application: " your.app/Contents/Plugins/unitypurchasing.bundle
codesign -f --deep -s "3rd Party Mac Developer Application: " your.app
productbuild --component your.app /Applications --sign "3rd Party Mac Developer Installer: " your.pkg

번들을 서명하기 위해, 우선 Contents.meta 파일이 존재하는 경우 제거해야 합니다. your.app/Contents/Plugins/unitypurchasing.bundle/Contents.meta 경로에 있습니다.

패키지를 올바르게 설치하려면 새로 생성된 패키지를 실행하기 이전 언패키지된 .app 파일을 삭제해야 합니다.

그 다음, 앱을 Applications 폴더에서 실행해야 합니다. 처음 실행하면 iTunes 계정 정보를 입력하라는 창이 나타날 것이며, 여기에는 iTunes Connect 테스트 사용자 계정 로그인 정보를 입력해야 합니다. 이제 샌드박스 환경에서 테스트 구매를 진행할 수 있습니다.


  • 2018–05–30 페이지 게시됨
  • Unity 2017.3에서 구매 허가, 거래 영수증 및 영수증 인터셉트 추가됨 NewIn20173
Android 인앱 구매 스토어와 크로스 스토어 설치 문제
유니버설 Windows 플랫폼