收据验证可以帮助您防止用户访问他们未购买的内容。
最佳实践是在应用程序内容分发时验证收据。
重要提示:虽然 Unity IAP 提供了一种本地验证方法,但本地验证更容易受到欺诈的影响。在可能的情况下,在服务器端验证敏感交易被认为是最佳实践。有关更多信息,请参阅Apple 和Android关于欺诈预防的文档。
如果用户正在购买的内容已存在于设备上,则应用程序只需决定是否解锁它。
Unity IAP 提供工具来帮助您隐藏内容以及通过 Google Play 和 Apple 商店验证和解析收据。
收据验证是使用已知的加密密钥执行的。对于您的应用程序,这是一个加密的 Google Play 公钥和/或 Apple 的根证书。
如果用户可以替换这些密钥,他们就可以绕过您的收据验证检查,因此让用户难以轻松找到和修改这些密钥非常重要。
Unity IAP 提供了一个工具,可以帮助您混淆应用程序中的加密密钥。这会混淆或打乱密钥,从而使用户更难以访问它们。在 Unity 菜单栏中,转到窗口 > Unity IAPUnity 应用内购买的缩写
参见术语表 > IAP 收据验证混淆器。
此窗口将 Apple 的根证书(与 Unity IAP 捆绑在一起)和您的 Google Play 公钥(来自应用程序的Google Play 开发者控制台的服务和 API页面)编码到两个不同的 C# 文件中:AppleTangle 和 GooglePlayTangle。这些将添加到您的项目中,以便在下一节中使用。
请注意,如果您只针对 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 应用收据,不适用于 Apple 已弃用的交易收据。
#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 的文档。