如何区分Unity IAP的正常购买与恢复购买?
区分Unity IAP正常购买与恢复购买的解决方案
要解决恢复购买误触发统计的问题,核心是通过唯一订单标识判断当前购买是否已经处理过:
- 每个合法IAP购买都有跨平台唯一的订单ID(Android对应Google Play的
orderId,iOS对应App Store的transactionIdentifier) - 把已处理的订单ID持久化存储,每次进入
ProcessPurchase先校验该订单是否已处理 - 只有未处理过的新订单(正常购买)才触发统计事件,已处理的订单(恢复购买)直接跳过
具体实现步骤
1. 获取唯一订单ID
利用Unity IAP的transaction.transactionIdentifier属性跨平台获取订单ID:
string transactionId = args.purchasedProduct.transaction.transactionIdentifier;
2. 校验订单是否已处理
用PlayerPrefs存储已处理记录,校验逻辑简单直接:
string processedKey = $"ProcessedTransaction_{transactionId}"; bool isAlreadyProcessed = PlayerPrefs.HasKey(processedKey);
3. 仅处理新订单的统计逻辑
把统计相关代码包裹在未处理判断中,处理完成后标记订单为已处理。
修改后的完整代码
public PurchaseProcessingResult ProcessPurchase(PurchaseEventArgs args) { if (String.Equals(args.purchasedProduct.definition.id, kProductIDNoAds, StringComparison.Ordinal)) { Debug.Log(string.Format("ProcessPurchase: PASS. Product: '{0}'", args.purchasedProduct.definition.id)); PlayerPrefs.SetInt("noads",1); BannerAdManager.Instance.HideBanner(); CheckNoAds(); // 获取唯一订单ID,判断是否是已处理过的恢复购买 string transactionId = args.purchasedProduct.transaction.transactionIdentifier; string processedKey = $"ProcessedTransaction_{transactionId}"; bool isAlreadyProcessed = PlayerPrefs.HasKey(processedKey); if (!isAlreadyProcessed) { var prodID = args.purchasedProduct.definition.id; var price = ReplaceCommas(args.purchasedProduct.metadata.localizedPrice.ToString()); var currency = args.purchasedProduct.metadata.isoCurrencyCode; var receipt = args.purchasedProduct.receipt; var param = new Dictionary<string, string>(); param["af_quantity"] = "1"; param["af_content_type"] = "general"; var recptToJSON = (Dictionary<string, object>)AFMiniJSON.Json.Deserialize(receipt); var receiptPayload = (Dictionary<string, object>)AFMiniJSON.Json.Deserialize((string)recptToJSON["Payload"]); var purchaseData = (string)receiptPayload["json"]; var signature = (string)receiptPayload["signature"]; Debug.LogError("Purchase Event Sent Start"); AppsFlyerAndroid appsFlyerAndroid = new AppsFlyerAndroid(); appsFlyerAndroid.validateAndSendInAppPurchase( "111", signature, purchaseData, price, currency, param, this); Debug.LogError("Purchase Event Sent Finish"); PaymentSucceed(args.purchasedProduct.definition.id, args.purchasedProduct.metadata.isoCurrencyCode, (float)args.purchasedProduct.metadata.localizedPrice, "noads"); // 标记订单已处理,避免重复统计 PlayerPrefs.SetInt(processedKey, 1); PlayerPrefs.Save(); } else { Debug.Log($"ProcessPurchase: SKIP. Order {transactionId} already processed (restore purchase)"); } } else { Debug.Log(string.Format("ProcessPurchase: FAIL. Unrecognized product: '{0}'", args.purchasedProduct.definition.id)); } return PurchaseProcessingResult.Complete; }
额外注意事项
- 消耗品无需处理:消耗品的恢复购买不会触发
ProcessPurchase,该方案仅针对非消耗品和订阅类产品 - 存储可选优化:如果担心
PlayerPrefs被清除,可改用本地文件或云存储保存已处理订单列表 - 订单ID唯一性:同一用户的同一购买(包括恢复操作)会返回相同的订单ID,能确保统计不重复
内容的提问来源于stack exchange,提问作者Davit Sedrakyan
相关产品推荐
相关产品推荐

