1.内购种类
- consumable:可消费的,如游戏中的金币,用完还可以再购买。
- non-consumable:不可销毁的,一次购买,永久生效。比如去广告,解锁游戏关卡,这种商品只能购买一次。
- subscription:订阅的,这种一般用于新闻、杂志、或者app里面的月卡。可以按月或者按年收费。
对于subsription类型,其又分成3种:
- non-renewing 到期后无需续订
- auto-renewing 到期后自动续订,直到用户取消订阅
- free-subscription 免费订阅
对于non-consumable和auto-renewing类型,app内需要提供恢复购买功能
(需要显著的按钮,否则无法过审),这样用户在切换或者添加设备的时候可以继续使用购买的商品。
2.商品
用户购买的商品分为两种类型:
- built-in 内置商品,商品已经存在于应用内,购买后方可使用,比如游戏关卡等。
- server-based 需要下载的商品,比如额外的资源包等。
server-based商品又分为两种:
- self-hosted 开发者自己建立服务器存储商品资源。
- App Store hosted 利用苹果app store存储商品资源。
3.流程
上图为官方支付流程
实际使用中,有些步骤不用特别拘泥,比如4和5是根据Apple Store返回的商品数据来展示在app上。实际一般游戏app内有商品相关的配置信息,界面的展示也一般是根据配置信息来设置的。
4.接入
确定证书中添加了IAP
支付功能
确定app的bundle id和后台配置的一致
Unity官方文档:UnityIAP
4.1 IAP包安装
建议使用Unity自带的IAP来集成.
Window–>Package Manager打开包管理界面,搜索In App Purchasing,并安装
这样在unity中就可以打开IAP相关配置:
Window–>Unity IAP–>IAP Catalog
用户可以在这里配置内购相关信息。不过笔者不建议这里配置。
4.2
创建类IAPMgr:IStoreListener
- 初始化
public void Init()
{
var module = StandardPurchasingModule.Instance();
ConfigurationBuilder builder = ConfigurationBuilder.Instance(module);
builder.AddProduct("商品id1", ProductType.Consumable);
builder.AddProduct("商品id2", ProductType.Consumable);
UnityPurchasing.Initialize(this, builder);
}
商品描述信息通过apple后台配置。
商品id建议通过server透传给app,这样在调整商品id后可以避免重新出包。
- 内置回调
public void OnInitialized(IStoreController controller, IExtensionProvider extensions)
{
Debug.Log("IAP initialize success");
this.controller = controller;
this.appleExtensions = extensions.GetExtension<IAppleExtensions>();
this.googlePlayStoreExtensions = extensions.GetExtension<IGooglePlayStoreExtensions>();
}
public void OnInitializeFailed(InitializationFailureReason error)
{
Debug.LogError("OnInitializeFailed, reason:" + error.ToString());
}
public void OnPurchaseFailed(Product i, PurchaseFailureReason p)
{
Debug.LogError("OnPurchaseFailed,reason:" + p.ToString());
if (this.onPurchaseFailed != null)
{
this.onPurchaseFailed();
this.onPurchaseFailed = null;
}
}
public PurchaseProcessingResult ProcessPurchase(PurchaseEventArgs e)
{
Debug.LogError("purchase finished, apple return receipt:" + e.purchasedProduct.receipt);
if (this.onPurchaseSuccess != null)
{
this.onPurchaseSuccess(e.purchasedProduct.receipt);
this.onPurchaseSuccess = null;
}
return PurchaseProcessingResult.Complete;
}
- 发起购买
private Action onPurchaseFailed;
private Action<string> onPurchaseSuccess;
public void PurchaseProduct(string productId, Action onFailed, Action<string> onSuccess)
{
this.onPurchaseFailed = onFailed;
this.onPurchaseSuccess = onSuccess;
if (controller != null)
{
var product = controller.products.WithID(productId);
if (product != null && product.availableToPurchase)
{
controller.InitiatePurchase(productId);
}
else
{
Debug.LogError("no product with productId:" + productId);
if (this.onPurchaseFailed != null)
{
this.onPurchaseFailed();
}
}
}
else
{
Debug.LogError("controller is null,can not do purchase");
if (this.onPurchaseFailed != null)
{
this.onPurchaseFailed();
}
}
}
- 掉单处理
会存在iOS那边支付成功,返回receipt后,app这边往server验证的时候因为网络等各种原因失败,因此需要处理掉单。
笔者这边是在apple那边返回成功后,往未处理的订单列表中写入相关信息。等收到server发货通知后再删除对应订单信息。
在app运行的合适时机(比如第一次进入主界面)向server请求,完成补单。
5.测试
测试使用沙盒环境即可(需要使用非越狱手机),沙盒环境测试的包只能是adhoc
或者develop
签名的。需要注意服务端沙盒环境对receipt的验证网址和正式环境是不一样的,这个需要服务端查验相关文档。
沙盒帐号在AppStore Connect->用户和访问->沙盒
进行配置。可以随便设置不存在的APPLE ID(邮箱)和密码。用做项目测试所用沙盒帐号。
测试流程:
- 先登出设备的app store账号
- 进入app点击付费,拉出要求你登录账号进行购买的界面
- 登入沙盒账号,完成购买
补充:上述测试流程已过时,较新版本的ios系统,在 设置-->iTunes Store与App Store-->沙盒账户
中可以配置沙盒帐号,配置好后,测试时会自动使用该沙盒账户。
需要注意的是在进行支付相关后台设置的时候会有个基准货币的概念,支付时会根据汇率,转换为你当地货币类型。
参考:
Unity苹果(iOS)内购接入
iOS内购—— In-App Purchase(消耗型)
Unity IAP (App Store & Google Paly)
UnityIAPAppleConfiguration
iOS IAP支付常见问题汇总与解决
iOS IAP应用内购详细步骤和问题总结指南
补充:
AppleTangle.cs和GooglePlayTangle.cs需要通过插件提供的工具来生成,IAP官方文档UnityIAPValidatingReceipts提到:For Unity 2021 or older: In the Unity menu bar, go to Services > In-App Purchasing > IAP Receipt Validation Obfuscator.
即对于Unity2021或者更老版本的Unity通过Unity menu bar来打开Validation Obfuscator面板。
对于2022以及以后版本通过ProjectSettings/Services/In-App Purchasing来打开。