앱 영수증은 장치 로컬 저장소에 저장되며 아래의 방법으로 읽어올 수 있습니다.
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);
});
}
구매가 승인 또는 거부되면 스토어의 일반 ProcessPurchase
또는 OnPurchaseFailed
리스너 메서드가 호출됩니다.
때때로 소모품의 구매 허가(Ask to Buy) 구매가 앱 영수증에 표시되지 않을 때가 있습니다. 이 경우에는 해당 영수증으로 검증이 불가능합니다. 하지만 iOS는 구매 허가를 비롯하여 모든 구매 내역이 포함된 거래 영수증을 제공합니다. 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에 구매를 전송하기 전에 부모 게이트를 제공하거나 분석 이벤트를 보내거나 기타 함수를 수행하십시오. 이 콜백은 사용자가 구매를 시도한 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 테스트 사용자 계정 로그인 정보를 입력해야 합니다. 이제 샌드박스 환경에서 테스트 구매를 진행할 수 있습니다.