IAP接入Unity(包括我自己踩的一些坑)

IAP接入Unity(包括我自己踩的一些坑)

一.前言

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。需要链接。

相关推荐