IAP接入Unity(包括我自己踩的一些坑)_unity iap
一.前言
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。需要链接。