앱 영수증은 장치 로컬 저장소에 저장되며 아래의 방법으로 읽어올 수 있습니다.
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 플랫폼의 경우 사용자가 비밀번호를 입력해야만 이전 거래 내역을 검색해서 가져올 수 있으므로 애플리케이션 내에 해당 과정을 진행할 수 있는 버튼이 제공되어야 합니다. 이 과정 동안 IStoreListener
의 ProcessPurchase
메서드가 사용자가 소유하고 있는 각각의 항목에 대해 호출됩니다.
/// <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.
}
});
}
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 시뮬레이션을 활성화하기 위해 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 홍보 구매는 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
을 이유로 초기화 실패를 수신하면 다음 체크리스트를 확인하십시오.
데스크톱 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 테스트 사용자 계정 로그인 정보를 입력해야 합니다. 이제 샌드박스 환경에서 테스트 구매를 진행할 수 있습니다.