关于IAP支付,谷歌和苹果订阅商品——最白话,手把手教你做系列。

简述:最近项目要接入订阅商品,这里总结一下公司大佬们的经验和我整理后脚本。
一、关于订阅
1,跟消耗性和非消耗性的购买类似,开发者账后后台建订阅型商品。 订阅型商品分两种,自动续订和非自动续订的,一般用的是自动续订的。
2,有免费期,如1个月的订阅期,有3天的免费试用期,用户在购买后,前三天如果退订,是不收费的。 如果过了试用期后,用户可以再苹果的设置里面把后面的续订取消掉,这样,一个订阅周期结束后,苹果就不会再自动扣费了。
3、针对订阅商品我们需要解决的问题

a、用户卸载后重新安装,如何恢复订阅的功能

可以用苹果自带的一套恢复机制,如果一个用户订阅了这个app的一个商品,用户再次购买时,苹果会有自己的一套恢复机制。它会提示你之前已经订阅过改商品,为你恢复。所以恢复的机制我们可以直接采用苹果自带的方案

b、如何验证用户的订阅是否结束!

*用户每次购买的商品,苹果都会生成一个receipt,这个相当于我们购买时的一个凭证,这个凭证是可以用来验证这次的购买是否生效的。购买的凭证是苹果自己保存的,所以我们要验证这个产品是否购买过,只需要验证这个receipt就可以了。
验证的方式有两种:
一种是本地的验证,根据苹果的机制,解析到receipt后,会得到相应的字段。但是这种检测的机制已经被破解了,因为hacker可以修改本地的receipt,造成本地的验证失效。但是对于内购不是占据特别大的比例的产品,这个是可以使用的。像我们的休闲类小游戏,内购比重不大可以采用这种方式。
另外一种方式,是通过把receipt发给苹果,苹果通过服务器自己验证。这种验证是100%准确的,后台可以通过苹果解析后的字段,判断本次购买是否准确的。
对于订阅性的商品,如果要通过服务端验证,需要后台添加一个配置,这个字段在服务端针对订阅性商品做验证的时候需要用,这是跟消耗性和非消耗性不同的地方。

111
而谷歌判断有没有过期只需要autoRenewing属性就可以了 ,如果为true就处于订阅状态,false就是订阅失效了。

在这里插入图片描述

c、我们如何测试订阅

订阅的沙盒环境和其它类型的有些不一样,它的过期时间和后台设置的不一致,具体参照下表。
在这里插入图片描述

那么我们客户端要做的操作是什么呢?
一、购买

订阅商品的购买与一般商品购买没有区别,按照正常流程购买就好了。
这点有问题的可以参照我的另外一篇博客Unity IAP 谷歌支付,ios支付
注意一点,初始化商品类型的时候订阅产品要选对类型。
在这里插入图片描述如图位置所指位置,订阅商品要选订阅类型(ProductType.Subscription)。

二、验证

订阅商品的难点主要在于判断订阅商品是否到期

由于Unity自带的IAP插件没有找到解析receipt的接口,我们需要导入以下脚本用来解析receipt

using UnityEngine;

class GooglePurchaseData
{
    // INAPP_PURCHASE_DATA
    public string inAppPurchaseData;
    // INAPP_DATA_SIGNATURE
    public string inAppDataSignature;

    public GooglePurchaseJson json;

    [System.Serializable]
    private struct GooglePurchaseReceipt
    {
        public string Payload;
    }

    [System.Serializable]
    private struct GooglePurchasePayload
    {
        public string json;
        public string signature;
    }

    [System.Serializable]
    public struct GooglePurchaseJson
    {
        public string autoRenewing;
        public string orderId;
        public string packageName;
        public string productId;
        public string purchaseTime;
        public string purchaseState;
        public string developerPayload;
        public string purchaseToken;
    }

    public GooglePurchaseData(string receipt)
    {
        try
        {
            var purchaseReceipt = JsonUtility.FromJson<GooglePurchaseReceipt>(receipt);
            var purchasePayload = JsonUtility.FromJson<GooglePurchasePayload>(purchaseReceipt.Payload);
            var inAppJsonData = JsonUtility.FromJson<GooglePurchaseJson>(purchasePayload.json);

            inAppPurchaseData = purchasePayload.json;
            inAppDataSignature = purchasePayload.signature;
            json = inAppJsonData;
        }
        catch
        {
            Debug.Log("Could not parse receipt: " + receipt);
            inAppPurchaseData = "";
            inAppDataSignature = "";
        }
    }
}

脚本2 判断是否处于订阅状态

using UnityEngine.Purchasing;
using UnityEngine.Purchasing.Security;


/*
 以下两个方法都是封装好检测是否订阅过的方法。
 仅针对有一个订阅商品的检测,多个订阅商品可能需要区分ID。暂时未做测试。
 注意一点,判断订阅状态必须得在商品初始化之后判断。
*/
//订阅控制脚本
public class SubscriptionCtrl:MonoBehaviour
{

    public void Start()
    {
//每次进入游戏调用检查订阅方法
#if UNITY_ANDROID
        CheckSubscribeReceiptAndorid();
#elif UNITY_IOS || UNITY_STANDALONE_OSX
        CheckSubscribeReceipt();
#endif

    }
    public void CheckOk()
    {
        //如果检查处于订阅状态,可以在这里做客户端逻辑处理
    }
    public void CheckWrong()
    {
        //如果检查处于非订阅状态,可以在这里做客户端逻辑处理
    }
    //检查谷歌订阅状态的方法,该方法需要同时导入另外一个脚本GooglePurchaseData 解析谷歌支付的receipt
    public void CheckSubscribeReceiptAndorid()
    {
        foreach (Product p in Purchaser.m_StoreController.products.all)
        {
            if (p.hasReceipt)
            {
                Debug.Log("recepit all:" + p.receipt);
                GooglePurchaseData data = new GooglePurchaseData(p.receipt);
                Debug.Log("recepit autoRenewing:" + data.json.autoRenewing);
                /*
                Debug.Log("recepit orderId:" + data.json.orderId);
                Debug.Log("recepit packageName:" + data.json.packageName);
                Debug.Log("recepit productId:" + data.json.productId);
                Debug.Log("recepit purchaseTime:" + data.json.purchaseTime);
                Debug.Log("recepit purchaseState:" + data.json.purchaseState);
                Debug.Log("recepit purchaseToken:" + data.json.purchaseToken);
               */
                if (bool.Parse(data.json.autoRenewing))
                {
                    CheckOk();
                    Debug.Log("sub is active");
                }
                else
                {
                    CheckWrong();
                    Debug.Log("sub not active");
                }
                   
            }
        }
      
    }
    //检查IOS订阅状态的方法, 这里是通过receipt里面的expiredate和当前时间对比来判断当前的订阅商品是否过期。
    public void CheckSubscribeReceipt()
    {
        try
        {
            var builder = ConfigurationBuilder.Instance(StandardPurchasingModule.Instance());
            // Get a reference to IAppleConfiguration during IAP initialization.
            var appleConfig = builder.Configure<IAppleConfiguration>();
            if (appleConfig == null || string.IsNullOrEmpty(appleConfig.appReceipt))
            {
                return;
            }
            //Debug.LogError("appReceipt:" + appleConfig.appReceipt);
            var receiptData = System.Convert.FromBase64String(appleConfig.appReceipt);
            if (receiptData == null)
            {
                return;
            }
            AppleReceipt receipt = new AppleReceiptParser().Parse(receiptData);
            if (receipt == null)
            {
                return;
            }
            foreach (AppleInAppPurchaseReceipt productReceipt in receipt.inAppPurchaseReceipts)
            {

                if (productReceipt.productType == 3)
                {
                    Debug.Log("sub productid = " + productReceipt.productID);
                    DateTime expirationDate = productReceipt.subscriptionExpirationDate;
                    Debug.Log("sub ExpirationDate = " + expirationDate.ToString());

                    DateTime now = DateTime.Now.ToUniversalTime();
                    //DateTime cancellationDate = apple.cancellationDate;

                    if (DateTime.Compare(now, expirationDate) < 0)
                    {
                        Debug.Log("sub is active");
                        CheckOk();
                    }
                    else
                    {
                        CheckWrong();
                        Debug.Log("sub not active");
                    }
                }
            }
        }
        catch (Exception exp)
        {
            Debug.Log(exp);
        }
    }

}

以上 是客户端需要对订阅商品的操作。

另外,订阅页面在UI显示的方面也要遵循一些规则,具体可以参考官方文档。晚些也会更新一篇关于后台配置和遵循规则的博客。

发布了27 篇原创文章 · 获赞 26 · 访问量 3万+

猜你喜欢

转载自blog.csdn.net/qq_39860954/article/details/84190956