一.前言
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。需要链接。