You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何区分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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.04 01:55:16