一.前言

IAP是Unity官方的一个购买插件,需要启动Unity Game Service才可以使用。其实谷歌与苹果也有自己的IAP,但是我没有找到提供给Unity的Package。所以就使用了Unity官方的插件。

二.下载

打开Unity窗口。 Window > General > Services 打开服务插件。

选择IAP插件下载。(这里如果过你没下载过会有Install按钮,点击即可)

打开这个窗口进行SDK的配置,因为我这里是公司后台的组织,就不需要配置了。配置这一步我就省略了。

三.IAP使用思路

首先在我们使用的时候需要去理清一下IAP这个购买逻辑的思路。

这里就是购买逻辑的大致思路。

初始化的逻辑:先进行SDK初始化,之后你的用户登录(IAP不包含登录模块),拉取全部的订阅信息(此文章暂不包含订阅相关),检查订阅是否到期,检查补单。

购买逻辑:点击购买,调用SDK购买等待回调,SDK购买失败回调调用传入的失败回调,弹出失败面板。SDK购买成功回调向服务器发送验证的订单。服务器验证失败,判断失败原因,未知错误就不结束订单,弹出失败面板,等待补单,校验错误就结束订单,因为该订单为假单,弹出失败面板。如果服务器验证成功那么就结束订单,直接发货即可。

注:unity提供了本地验证订单的方法,该方法可以提供给没有本地服务器的小伙伴使用。但是官方文档也说了,本地验证更容易被假单欺骗,所有如果不是做单机游戏感觉还是服务器去验证比较好一点。Unity - Manual: Receipt validation(Unity验证的官方文档)

注:这里再去补充一下服务器的验证大致逻辑就是,拿来前端发过来的商品信息订单信息,去和谷歌或者苹果那边的IAP去验证,等待结果返回给Unity就可以了。

四.示例代码

1.IAP脚本

这里的逻辑很简单,先初始化UnityGameService之后再去初始化SDK,为什么要先初始化UnityGameService?因为IAP是UnityGameService服务中的一个插件,也就是说IAP是UnityGameService的一部分,那么必须先初始化。

在初始化的时候需要提交关于商品的谷歌id或者苹果id 与 接收回调的脚本,那么就需要先调用RegistConfig进行商品的登记,在进行初始化。Pay是购买方法(这里最好加点成功或者失败的回调)。

  public class IAP : IPurchase
    {
        public IAPTool iapProxyTool { get; }
        public IAPData iapProxyData { get; }
        public IAPCallback iapProxyCallback { get;}

        public IAPProxy()
        {
            // 初始化工具和数据
            iapData = new IAPData();
            iapTool = new IAPTool(this);
            iapCallback = new IAPCallback(this);
        }
        // 初始化
        public async void Init()
        {
            try
            {
                if (UnityServices.State != ServicesInitializationState.Initialized)
                {
                    await UnityServices.InitializeAsync();
                }
                var builder = ConfigurationBuilder.Instance(StandardPurchasingModule.Instance());
                foreach (PurchasedCfg item in iapProxyData.GetPurchasedCfgList())
                {
                    builder.AddProduct(item.id, item.type);
                }
                UnityPurchasing.Initialize(iapProxyCallback, builder);
            }
            catch (Exception ex)
            {
                iapProxyData.productFetchState = ProductFetchState.InitializedFailure;
            }
        }
        //支付
        public void Pay(string productId, Action successCallback = null, Action failureCallback = null)
        {
            var storeController = iapProxyData.storeController; 
            var product = storeController.products.WithID(productId);
            if (!product.availableToPurchase)
            {
                return;
            }
            storeController.InitiatePurchase(product);
        }

        //向上层提供的SDK初始化所需要的函数
        public void RegistConfig(string productId, ProductCategory productType)
        {
            PurchasedCfg purchasedCfg = new PurchasedCfg();
            purchasedCfg.id = productId;
            purchasedCfg.type = (ProductType)productType;
            iapProxyData.AddPurchasedCfg(purchasedCfg);
        }
    }

 2.IAPCallback脚本

需要继承IDetailedStoreListener,实现IAP的官方回调,在SDK初始化时需要把这个脚本传进去。

public class IAPCallback: IDetailedStoreListener
    {
        private IAPTool _iapTool;

        public IAPProxyCallback(IAPProxy iapProxy)
        {
            _iapTool = iapProxy.iapTool;
        }
        
        public void OnInitialized(IStoreController controller, IExtensionProvider extensions)
        {
            //这里需要保存controller,extensions
        }
        //初始化失败回调(旧版)
        public void OnInitializeFailed(InitializationFailureReason error)
        {
            Debug.LogWarning("Initialization failed! failureReason: " + error);
        }
        //初始化失败回调
        public void OnInitializeFailed(InitializationFailureReason error, string message)
        {
            Debug.LogWarning("Initialization failed! failureReason: " + error + " message :" + message);
        }
        //购买成功回调
        public PurchaseProcessingResult ProcessPurchase(PurchaseEventArgs purchaseEvent)
        {
            Product product = purchaseEvent.purchasedProduct;
            Debug.Log("Purchase successful for product id: " + product.definition.id);
            OnProductPurchased(purchaseEvent.purchasedProduct);
            return PurchaseProcessingResult.Pending;
        }
        //购买失败回调(旧版)
        public void OnPurchaseFailed(Product product, PurchaseFailureReason failureReason)
        {
            
            Debug.LogWarning("Purchase failed for product id: " + product.definition.id + ", reason: " + failureReason);
            OnProductPurchasedFailed();
        }
        //购买失败回调
        
        public void OnPurchaseFailed(Product product, PurchaseFailureDescription failureDescription)
        {
            Debug.LogWarning("Purchase failed for product id: " + product.definition.id + ", reason: " + failureDescription.reason + " message: " + failureDescription.message);
            OnProductPurchasedFailed();
        }
        
        //支付成功调用
        private void OnProductPurchased(Product product)
        {
            Debug.Log("OnProductPurchased");
            HandlePurchased(product);

        }

        //向服务器验证订单
        private void HandlePurchased(Product product)
        {
            Debug.Log("订单验证中");
            //显示等得界面
            //网络请求成功回调,PurchasedNetSuccess()
            //网络请求失败回调,PurchasedNetFail()
            _iapTool.FinishProductItem(product.definition.id);
        }

        private void PurchasedNetSuccess()
        {
            //需要服务器传来订单号
            //关闭等待界面
            //先判断是否是订阅类型
            //再去获取修改时间(服务器端发来)
            //验证发货;
            //FinishProductItem();
        }
        
        private void PurchasedNetFail()
        {
            //需要服务器传来错误码与订单号
            //关闭等待页面
            //假单不发货
            //错误 1:校验失败 完成订单FinishProductItem()它是个假单
            //错误 2:未知错误 不完成订单
            //错误 3:没返回错误码 不完成订单,关闭界面 OnProductPurchasedFailed()
        }
        //购买失败(取消订单等情况)
        private void OnProductPurchasedFailed()
        {
            Debug.Log("OnProductPurchasedFailed");
            //关闭界面
            //调用失败回调
            //展示错误面板
        }

3.IAPToo脚本

提供一下工具方法。例如完成订单,获取商品信息。

public class IAPTool
    {
        
        
        //完成订单
        public void FinishProductItem(string productId)
        {
            DebugLog($"FinishProductItem productId:{productId}");
            var product = GetProductInformation(productId);
            if (product != null)
            {
                //利用之前缓存过的storeController,完成订单
                storeController.ConfirmPendingPurchase(product);
            }
        }
        //向sdk获取商品信息
        public Product GetProductInformation(string productId)
        {
            //利用之前缓存过的storeController,返回商品
            //storeController.products.WithID(productId);
        }
       
     }

五.一些坑

问题:你再初始化Unity Game Service的时候可能会出现一些错误。

查看你的Unity项目是否连接了Cloud。需要链接。

Logo

这里是一个专注于游戏开发的社区,我们致力于为广大游戏爱好者提供一个良好的学习和交流平台。我们的专区包含了各大流行引擎的技术博文,涵盖了从入门到进阶的各个阶段,无论你是初学者还是资深开发者,都能在这里找到适合自己的内容。除此之外,我们还会不定期举办游戏开发相关的活动,让大家更好地交流互动。加入我们,一起探索游戏开发的奥秘吧!

更多推荐