Unity接入iOS内购

1.内购种类

  • consumable:可消费的,如游戏中的金币,用完还可以再购买。
  • non-consumable:不可销毁的,一次购买,永久生效。比如去广告,解锁游戏关卡,这种商品只能购买一次。
  • subscription:订阅的,这种一般用于新闻、杂志、或者app里面的月卡。可以按月或者按年收费。

对于subsription类型,其又分成3种:

  • non-renewing 到期后无需续订
  • auto-renewing 到期后自动续订,直到用户取消订阅
  • free-subscription 免费订阅

对于non-consumableauto-renewing类型,app内需要提供恢复购买功能(需要显著的按钮,否则无法过审),这样用户在切换或者添加设备的时候可以继续使用购买的商品。

2.商品

用户购买的商品分为两种类型:

  • built-in 内置商品,商品已经存在于应用内,购买后方可使用,比如游戏关卡等。
  • server-based 需要下载的商品,比如额外的资源包等。

server-based商品又分为两种:

  • self-hosted 开发者自己建立服务器存储商品资源。
  • App Store hosted 利用苹果app store存储商品资源。

3.流程

在这里插入图片描述
上图为官方支付流程
实际使用中,有些步骤不用特别拘泥,比如4和5是根据Apple Store返回的商品数据来展示在app上。实际一般游戏app内有商品相关的配置信息,界面的展示也一般是根据配置信息来设置的。

4.接入

确定证书中添加了IAP支付功能
确定app的bundle id和后台配置的一致

Unity官方文档:UnityIAP

4.1 IAP包安装

建议使用Unity自带的IAP来集成.
Window–>Package Manager打开包管理界面,搜索In App Purchasing,并安装
在这里插入图片描述
这样在unity中就可以打开IAP相关配置:
Window–>Unity IAP–>IAP Catalog
在这里插入图片描述
用户可以在这里配置内购相关信息。不过笔者不建议这里配置。

4.2

创建类IAPMgr:IStoreListener

  • 初始化
    public void Init()
    {
        var module = StandardPurchasingModule.Instance();
        ConfigurationBuilder builder = ConfigurationBuilder.Instance(module);
        builder.AddProduct("商品id1", ProductType.Consumable);
        builder.AddProduct("商品id2", ProductType.Consumable);        
        UnityPurchasing.Initialize(this, builder);
    }

商品描述信息通过apple后台配置。
商品id建议通过server透传给app,这样在调整商品id后可以避免重新出包。

  • 内置回调
    public void OnInitialized(IStoreController controller, IExtensionProvider extensions)
    {
        Debug.Log("IAP initialize success");
        this.controller = controller;

        this.appleExtensions = extensions.GetExtension<IAppleExtensions>();
        this.googlePlayStoreExtensions = extensions.GetExtension<IGooglePlayStoreExtensions>();
    }

    public void OnInitializeFailed(InitializationFailureReason error)
    {
        Debug.LogError("OnInitializeFailed, reason:" + error.ToString());
    }

    public void OnPurchaseFailed(Product i, PurchaseFailureReason p)
    {
        Debug.LogError("OnPurchaseFailed,reason:" + p.ToString());
        if (this.onPurchaseFailed != null)
        {
            this.onPurchaseFailed();
            this.onPurchaseFailed = null;
        }
    }

    public PurchaseProcessingResult ProcessPurchase(PurchaseEventArgs e)
    {
        Debug.LogError("purchase finished, apple return receipt:" + e.purchasedProduct.receipt);
        
        if (this.onPurchaseSuccess != null)
        {
            this.onPurchaseSuccess(e.purchasedProduct.receipt);
            this.onPurchaseSuccess = null;
        }
        return PurchaseProcessingResult.Complete;
    }
  • 发起购买
    private Action onPurchaseFailed;
    private Action<string> onPurchaseSuccess;
    public void PurchaseProduct(string productId, Action onFailed, Action<string> onSuccess)
    {
        this.onPurchaseFailed = onFailed;
        this.onPurchaseSuccess = onSuccess;

        if (controller != null)
        {
            var product = controller.products.WithID(productId);
            if (product != null && product.availableToPurchase)
            {
                controller.InitiatePurchase(productId);
            }
            else
            {
                Debug.LogError("no product with productId:" + productId);
                if (this.onPurchaseFailed != null)
                {
                    this.onPurchaseFailed();
                }
            }
        }
        else
        {
            Debug.LogError("controller is null,can not do purchase");
            if (this.onPurchaseFailed != null)
            {
                this.onPurchaseFailed();
            }
        }
    }
  • 掉单处理
    会存在iOS那边支付成功,返回receipt后,app这边往server验证的时候因为网络等各种原因失败,因此需要处理掉单。
    笔者这边是在apple那边返回成功后,往未处理的订单列表中写入相关信息。等收到server发货通知后再删除对应订单信息。
    在app运行的合适时机(比如第一次进入主界面)向server请求,完成补单。

5.测试

测试使用沙盒环境即可(需要使用非越狱手机),沙盒环境测试的包只能是adhoc或者develop签名的。需要注意服务端沙盒环境对receipt的验证网址和正式环境是不一样的,这个需要服务端查验相关文档。
沙盒帐号在AppStore Connect->用户和访问->沙盒进行配置。可以随便设置不存在的APPLE ID(邮箱)和密码。用做项目测试所用沙盒帐号。

测试流程:

  • 先登出设备的app store账号
  • 进入app点击付费,拉出要求你登录账号进行购买的界面
  • 登入沙盒账号,完成购买

补充:上述测试流程已过时,较新版本的ios系统,在 设置-->iTunes Store与App Store-->沙盒账户中可以配置沙盒帐号,配置好后,测试时会自动使用该沙盒账户。

需要注意的是在进行支付相关后台设置的时候会有个基准货币的概念,支付时会根据汇率,转换为你当地货币类型。

参考:
Unity苹果(iOS)内购接入
iOS内购—— In-App Purchase(消耗型)
Unity IAP (App Store & Google Paly)

UnityIAPAppleConfiguration
iOS IAP支付常见问题汇总与解决
iOS IAP应用内购详细步骤和问题总结指南

补充:
AppleTangle.cs和GooglePlayTangle.cs需要通过插件提供的工具来生成,IAP官方文档UnityIAPValidatingReceipts提到:For Unity 2021 or older: In the Unity menu bar, go to Services > In-App Purchasing > IAP Receipt Validation Obfuscator.

即对于Unity2021或者更老版本的Unity通过Unity menu bar来打开Validation Obfuscator面板。
对于2022以及以后版本通过ProjectSettings/Services/In-App Purchasing来打开。

Unity接入iOS时,如果遇到未实际买但买回调被触发的问题,可能的原因和解决方案如下: ### 1. 检查买流程中的验证逻辑 确保在买流程中正确处理了`SKPaymentTransactionStatePurchased`状态的验证。在iOS中,买回调可能会因为沙盒环境或本地缓存的问题而被错误触发。需要确保在接收到买回调时,验证交易的状态和产品的唯一标识符是否匹配。例如,在`SKPaymentTransactionObserver`的`updatedTransactions`方法中,确保正确处理了交易状态的变化: ```objective-c - (void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray<SKPaymentTransaction *> *)transactions { for (SKPaymentTransaction *transaction in transactions) { switch (transaction.transactionState) { case SKPaymentTransactionStatePurchased: // 验证产品标识符和交易状态 if ([transaction.payment.productIdentifier isEqualToString:@"your_product_id"]) { // 处理买成功逻辑 } break; case SKPaymentTransactionStateFailed: // 处理买失败逻辑 break; default: break; } } } ``` ### 2. 检查本地缓存和沙盒环境 iOS的沙盒环境可能会导致一些不一致的行为,特别是在测试时。如果使用了沙盒环境进行测试,确保清除设备上的买记录,避免旧的交易状态影响当前的买流程。可以通过以下步骤清除沙盒环境: 1. 在设备上进入设置 > 通达 > iTunes与App Store。 2. 点击Apple ID并选择“退出登录”。 3. 重新登录Apple ID并重新启动应用。 ### 3. 验证服务器端的收据验证 在iOS中,客户端的买回调可能会因为网络问题或本地缓存导致的错误而触发。为了确保买的真实性,必须在服务器端进行收据验证。通过将收据数据发送到Apple的验证服务器,确保买的真实性和有效性。例如,可以通过以下方式发送收据数据到Apple的服务器进行验证: ```csharp IEnumerator VerifyReceipt(string receiptData) { string url = "https://buy.itunes.apple.com/verifyReceipt"; var request = new UnityWebRequest(url, "POST"); var body = new Dictionary<string, string> { { "receipt-data", receiptData }, { "password", "your_shared_secret" } // 如果使用了共享密钥 }; string jsonBody = JsonUtility.ToJson(body); byte[] jsonBytes = Encoding.UTF8.GetBytes(jsonBody); request.uploadHandler = new UploadHandlerRaw(jsonBytes); request.downloadHandler = new DownloadHandlerBuffer(); request.SetRequestHeader("Content-Type", "application/json"); yield return request.SendWebRequest(); if (request.isNetworkError || request.isHttpError) { Debug.Log("Error: " + request.error); } else { // 处理验证结果 } } ``` ### 4. 检查Unity插件和iOS SDK版本 确保使用的Unity版本和iOS SDK版本兼容,并且插件支持最新的iOS功能。某些旧版本的插件或SDK可能会导致买回调的误触发,建议更新到最新的稳定版本。 ### 5. 调试和日志记录 在开发过程中,启用详细的日志记录,跟踪买流程中的每个步骤。通过日志分析,可以更清楚地了解买回调触发的具体原因。例如,在Unity中可以使用`Debug.Log`记录关键步骤的执行情况: ```csharp public void OnInitialized(IStoreController controller, IExtensionProvider extensions) { Debug.Log("Store initialized."); // 其他初始化逻辑 } public void ProcessPurchase(PurchaseEventArgs args) { Debug.Log("Purchase processed: " + args.purchasedProduct.definition.id); // 处理买逻辑 } ``` ### 相关问题 1. Unity iOS如何正确处理沙盒环境的测试流程? 2. 如何在Unity中实现iOS的收据验证? 3. 如何调试Unity iOS买回调问题? 4. Unity iOS插件的最新版本有哪些改进和修复? 5. 如何确保iOS买回调的可靠性?
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

iningwei

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值