영수증 확인을 통해 사용자가 구매하지 않은 콘텐츠에 액세스하지 못하게 합니다.
영수증 검증은 애플리케이션 콘텐츠가 배포되는 시점에 수행하는 것이 좋습니다.
사용자가 구매한 콘텐츠가 장치에 이미 존재하는 경우, 애플리케이션은 콘텐츠의 해지 여부만 결정하면 됩니다.
Unity IAP는 Google Play와 Apple 스토어를 통해 콘텐츠를 숨기고 영수증을 파싱할 수 있는 툴을 제공합니다.
영수증 검증은 알려진 암호화 키를 사용해 진행됩니다. 이 키는 애플리케이션의 암호화된 Google Play 공용 키나 Apple 루트 인증서입니다.
사용자가 이 키를 교체할 수 있다면 영수증 확인 과정을 무력화할 수 있으므로, 사용자가 키를 찾아 수정하기 어렵도록 해야 합니다.
Unity IAP는 애플리케이션에서 암호화 키를 난독 처리할 수 있는 툴을 제공합니다. 이 툴은 키를 난독 처리하여 사용자가 액세스하기 어렵게 만듭니다. Unity 메뉴 바에서 Window > Unity IAP > IAP Receipt Validation Obfuscator 로 가야 합니다.
이 창은 Unity IAP와 번들된 Apple 루트 인증서와 애플리케이션의 Google Play Developer Console’s Services & APIs 페이지에서의 Google Play 공용 키를 둘 다 암호화하여 AppleTangle 과 GooglePlayTangle 이라는 두 개의 다른 C# 파일에 저장합니다. 이 파일은 프로젝트에 추가되어 다음 섹션에서 사용하게 됩니다.
Apple 스토어만 타겟하는 경우에는 Google Play 공용 키를 제공할 필요가 없으며, 그 반대도 마찬가지입니다.
CrossPlatformValidator
클래스를 사용하여 Google Play와 Apple 스토어에서 확인을 수행할 수 있습니다.
클래스에 Google Play 공용 키나 Apple 루트 인증서를 제공해야 하며, 두 플랫폼에서 확인을 수행하려면 둘 다 제공해야 합니다.
CrossPlatformValidator
는 다음 두 개의 검사를 수행합니다.
이 검증기는 Google Play와 Apple 플랫폼에서 생성된 영수증만 확인할 수 있습니다. 다른 플랫폼에서 생성한 영수증이나 에디터에서 생성한 모의 영수증을 확인하면 IAPSecurityException 오류가 발생합니다.
비밀 키를 제공하지 않은 플랫폼의 영수증을 확인하려고 시도하면 MissingStoreSecretException 오류가 발생합니다.
public PurchaseProcessingResult ProcessPurchase (PurchaseEventArgs e)
{
bool validPurchase = true; // Presume valid for platforms with no R.V.
// Unity IAP's validation logic is only included on these platforms.
#if UNITY_ANDROID || UNITY_IOS || UNITY_STANDALONE_OSX
// Prepare the validator with the secrets we prepared in the Editor
// obfuscation window.
var validator = new CrossPlatformValidator(GooglePlayTangle.Data(),
AppleTangle.Data(), Application.bundleIdentifier);
try {
// On Google Play, result has a single product ID.
// On Apple stores, receipts contain multiple products.
var result = validator.Validate(e.purchasedProduct.receipt);
// For informational purposes, we list the receipt(s)
Debug.Log("Receipt is valid. Contents:");
foreach (IPurchaseReceipt productReceipt in result) {
Debug.Log(productReceipt.productID);
Debug.Log(productReceipt.purchaseDate);
Debug.Log(productReceipt.transactionID);
}
} catch (IAPSecurityException) {
Debug.Log("Invalid receipt, not unlocking content");
validPurchase = false;
}
#endif
if (validPurchase) {
// Unlock the appropriate content here.
}
return PurchaseProcessingResult.Complete;
}
영수증의 유효성뿐만 아니라 해당 내용도 검사해야 합니다. 사용자가 구매를 하지 않고 콘텐츠에 액세스하는 흔한 방법 중 하나는 다른 제품이나 애플리케이션에서 영수증을 제공하는 것입니다. 영수증은 진품이고 확인 과정을 통과하기 때문에, CrossPlatformValidator 가 제품 ID를 파싱한 결과에 따라 판단을 내리도록 해야 합니다.
스토어마다 구매 영수증을 구성하는 필드가 다릅니다. 스토어별 필드에 액세스하려면 IPurchaseReceipt
를 두 개의 서브타입인 GooglePlayReceipt
과 AppleInAppPurchaseReceipt
에 적용하면 됩니다.
var result = validator.Validate(e.purchasedProduct.receipt);
Debug.Log("Receipt is valid. Contents:");
foreach (IPurchaseReceipt productReceipt in result) {
Debug.Log(productReceipt.productID);
Debug.Log(productReceipt.purchaseDate);
Debug.Log(productReceipt.transactionID);
GooglePlayReceipt google = productReceipt as GooglePlayReceipt;
if (null != google) {
// This is Google's Order ID.
// Note that it is null when testing in the sandbox
// because Google's sandbox does not provide Order IDs.
Debug.Log(google.transactionID);
Debug.Log(google.purchaseState);
Debug.Log(google.purchaseToken);
}
AppleInAppPurchaseReceipt apple = productReceipt as AppleInAppPurchaseReceipt;
if (null != apple) {
Debug.Log(apple.originalTransactionIdentifier);
Debug.Log(apple.subscriptionExpirationDate);
Debug.Log(apple.cancellationDate);
Debug.Log(apple.quantity);
}
}
AppleValidator
클래스를 사용하여 Apple 영수증 세부 정보를 추출할 수 있습니다. 이 클래스는 버전 7.0 이후의 iOS 앱 영수증에만 작동하며 그 이전 버전의 영수증에서는 작동하지 않습니다.
#if UNITY_ANDROID || UNITY_IOS || UNITY_STANDALONE_OSX
var builder = ConfigurationBuilder.Instance(StandardPurchasingModule.Instance());
// Get a reference to IAppleConfiguration during IAP initialization.
var appleConfig = builder.Configure<IAppleConfiguration>();
var receiptData = System.Convert.FromBase64String(appleConfig.appReceipt);
AppleReceipt receipt = new AppleValidator(AppleTangle.Data()).Validate(receiptData);
Debug.Log(receipt.bundleID);
Debug.Log(receipt.receiptCreationDate);
foreach (AppleInAppPurchaseReceipt productReceipt in receipt.inAppPurchaseReceipts) {
Debug.Log(productReceipt.transactionIdentifier);
Debug.Log(productReceipt.productIdentifier);
}
#endif
AppleReceipt
타입은 Apple의 ASN1 영수증 포맷을 모델링합니다. 각각의 필드에 대한 자세한 내용은 Apple 문서를 참조하십시오.