安卓苹果内付
android(gl这个会被屏蔽锁定?)国内内付
先说说内付流程有些啥,Android和iOS程序启动就应该默认开启了内付初始化的流程(有账号时没账号得自己去在合适的时机初始化流程),这个流程需要干什么呢?
- 初始化会返回有购买未消耗的和订阅的回调信息,那么开发者需要根据商品id去处理服务器验证和下发奖励当然也是服务器来发好一些,iOS少一个消耗的机制,Android内付不消耗的东西再次购买是不能购买的.测试遇到这种情况怎么办?那么必须消耗掉此次购买
- 关于订单信息这些根本不需要客户端来处理此逻辑,为什么?兄台此言怎讲?那么有人可能跟我杠精了,客户端写个数据库存个plist不是很快的么? 对于此快有什么用? 删掉 卸载那还不是漏单还不是得交给服务器来处理,就算你想做客户端本地存储,不考虑信息服务器之间的同步?自己搞重传,卸载也没用啊,所以别找那么多麻烦的事了,美其名曰锻炼技术,实则浪费时间.熟不知服务器早就做好这些漏单的处理,iOS若没有调用finishTransactions的话,你就是付了钱,来不及下发奖励 卸载程序,下次你安装程序启动时还是会走到未完成的交易流程,在这里你可以再次做服务器验证下发奖励. 服务器验证和下发奖励两个网络请求都需要做重传么?这个简单服务器一个单只要服务端没下发奖励可以验多次就可以了
准备内容:
1.google 开发者账号 https://www.aliyun.com/jiaocheng/61191.html
2.可以选择(区域代理)节点的vpn 个人用的是影梭https://github.com/shadowsocks
3.console play 商店中创建构建应用,准备release版本apk 上传发布 配置商品信息 在beta和alpha状态发布,注意发布地区这个很重要,是美国 那么你得选择美国的vpn 反正不要选中国,选中国你就等着被坑吧
4.添加测试账号(必须可以支持Google play支付的账号)
5.国内手机大多无Google服务使用 Go谷歌安装器 安装服务(在翻墙的情况下)并且可以看到商店付费应用
接入准备
参考:https://www.cnblogs.com/lovexb/p/4595215.html
关于文中的GooglePlayServicesUtil检查是否支持库,
不知道国内原因还是怎么回事没找到因为这个工具库依赖的是Google 服务下的一些东西,
as上找不到相关依赖不知版本的问题还是啥,嘿嘿这个倒是对后来没啥影响其实
主要做的事
- 引入IInAppBillingService.aidl
- 导入util工具类
- mainfest 添加权限以及允许唤起google商店的权限
这里最好的方式是将demo下载下来跑一跑
https://github.com/anjlab/android-inapp-billing-v3
核心代码
下载的样例代码,对流程不太了解的建议先把demo中核心代码自己接一次,有些流程不太了解的坑还是有些莫名其妙,有个最重要的核心类IabHelper,其他的都是工具类,辅助类型转换处理异常以及一些数据模型类,Iventory 仓库类(可以查到购买的列表,商品列表) Purchase 购买信息数据相关 IabResult 包装包含成功错误信息用于回调
步骤:IabHelper 实例化 使用重要的方法
startSetup 用于初始化 这步完不成 后面就没得玩了
|
V
OnIabSetupFinishedListener 会回调初始化结果 -> 在成功的结果下
//这个主要用于先注册下google play应用和当前应用的进程间服务
mBroadcastReceiver = new IabBroadcastReceiver(MainActivity.this);
IntentFilter broadcastFilter = new IntentFilter(IabBroadcastReceiver.ACTION);
registerReceiver(mBroadcastReceiver, broadcastFilter);
try {
//查询接口
mHelper.queryInventoryAsync(mGotInventoryListener);
} catch (IabHelper.IabAsyncInProgressException e) {
complain("Error querying inventory. Another async operation in progress.");
}
实现IabBroadcastListener 接口
public void receivedBroadcast() {
// Received a broadcast notification that the inventory of items has changed
Log.d(TAG, "Received broadcast notification. Querying inventory.");
try {
mHelper.queryInventoryAsync(mGotInventoryListener);
} catch (IabHelper.IabAsyncInProgressException e) {
complain("Error querying inventory. Another async operation in progress.");
}
}
查询 接口IabHelper.QueryInventoryFinishedListener
mGotInventoryListener = new IabHelper.QueryInventoryFinishedListener() {
public void onQueryInventoryFinished(IabResult result, Inventory inventory) {
if (mHelper == null) return;
if (result.isFailure()) {
complain("Failed to query inventory: " + result);
return;
}
//处理仓库中购买的商品和商品列表展示在ui上
//加上具体已购买商品的sku判空 ...
// 自己最需要关心的是在这做
已购买商品和本服务器的验证
这个方法在没有consume的情况下
初次都会进入到这里进行合法验证,
因此不需要做本地化存储 和什么杂七杂八的订单这本就不应该让客户端来做
Purchase inventory.getPurchase("csku-25");
// 这里做验证 关于订单绑定的操作 在这里服务端需要拿着
inapp_data purchase.getOriginalJson()
signature purchase.getSignature()
可自定义添加 玩家相关信息 用于验证玩家后直接找到相关玩家
关于这个字段的返回可以launchPurchaseFlow 的extra方法上可以做字符串和玩家关联具体情况具体分析吧
//vertify(inapp_data,signature,userinfo, callback)
//callback下就可以消耗次物品 整个过程完毕 关于callback数据回调的设计可以参照具体平台数据怎么设计的比如 返回玩家id 下发奖励啥的客户端自己处理 下发奖励的流程
玩家消耗
try
{
mHelper.consumeAsync(purchase ,onConsumeFinishedListener);
}catch(PYHelper.IabAsyncInProgressException e){
}
实现消耗完毕 IabHelper.OnConsumeFinishedListener
mConsumeFinishedListener = new IabHelper.OnConsumeFinishedListener() {
public void onConsumeFinished(Purchase purchase, IabResult result) {
updateUi();
}
};
购买
try
{
mHelper.consumeAsync(purchase ,onConsumeFinishedListener);
}catch(PYHelper.IabAsyncInProgressException e){
}
实现 购买完成 IabHelper.OnIabPurchaseFinishedListener mPurchaseFinishedListener = new IabHelper.OnIabPurchaseFinishedListener() {
public void onIabPurchaseFinished(IabResult result, Purchase purchase) {
if (mHelper == null) return;
if (result.isFailure()) {
complain("Error purchasing: " + result);
setWaitScreen(false);
return;
}
// 这里做验证 关于订单绑定的操作 在这里服务端需要拿着
inapp_data purchase.getOriginalJson()
signature purchase.getSignature()
可自定义添加 玩家相关信息 用于验证玩家后直接找到相关玩家
关于这个字段的返回可以launchPurchaseFlow 的extra方法上可以做字符串和玩家关联具体情况具体分析吧
vertify(inapp_data,signature,userinfo, callback)
//callback下成功就可以消耗次物品 整个过程完毕
};
必须实现的方法其它
谷歌商店的回调支付结果
然后走handleActivityResult 下处理验证
并且返回到OnIabPurchaseFinishedListener
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
Log.d(TAG, "onActivityResult(" + requestCode + "," + resultCode + "," + data);
if (mHelper == null) return;
// Pass on the activity result to the helper for handling
if (!mHelper.handleActivityResult(requestCode, resultCode, data)) {
// not handled, so handle it ourselves (here's where you'd
// perform any handling of activity results not related to in-app
// billing...
super.onActivityResult(requestCode, resultCode, data);
} else {
Log.d(TAG, "onActivityResult handled by IABUtil.");
}
}
public void onDestroy() {
super.onDestroy();
// very important:
if (mBroadcastReceiver != null) {
unregisterReceiver(mBroadcastReceiver);
}
// very important:
Log.d(TAG, "Destroying helper.");
if (mHelper != null) {
mHelper.disposeWhenFinished();
mHelper = null;
}
}
期间遇到报错 google服务不支持 解决方式
* 检查商店谷歌配置发布的地区是否和vpn代理区域是一个地方 不一样会说不支持biling 3 unspport 检查手机是否有Google服务,不行的话将Google账号删除后再重新登上测试账号
* 关于测试时无法再次购买是因为没有消耗掉
再次购买时会有问题,
方法首次先在QueryInventoryFinishedListener 查询出来的商品消耗掉,然后验证流程自己玩通后再加入这块逻辑
* 关于测试支付这块相对来说较麻烦些
1.可以支持play支付的账号
2.选择vpn和发布应用地区最好相符 因为这是国内 其它的没试过
3.什么情况下是可以支持支付呢?你的Google play能看到付费应用这点很重要,不然查商品都查不到 ff-20 错误检索
4.关于测试服务下这个坑我心痛,翻墙下连不到局域网,连到局域网看不到谷歌付费应用,能上谷歌也是醉了,手机反正弄死只能这两个不通,
经过it网络部门的协助影梭上需要设置路由跳转方式 绕过仅大陆代理不到的局域网(之前只是选择绕过局域网),终于通了.因此这里测试好多都是业务逻辑上去模拟测试的🤣🤣
iOS 内付流程
准备内容
1.开发者账号创建app bundle id和构建app需一致 然后就是语言选择和测试沙盒下添加账号必须是同一个国家
2.创建购买内商品
3.创建测试沙盒 同样语言的选择 和创建APP是一样的国家
核心代码
导入Storekit
流程如下 实现SKPaymentTransactionObserver 相关方法
请求商品列表
购买
验签 成功 finish 未成功 网络错误(可以认为服务端后台的问题,我们给提示,已收到汇款,服务问题稍后补发奖励,或者重启应用啥的 )
1.合适的地方实现SKPaymentTransactionObserver 和购买回调 可以在程序启动的时候只添加一次paymentQueue 结果处理购买结果
初始化
SKPaymentQueue.default().add(self)
public func paymentQueueRestoreCompletedTransactionsFinished(_ queue: SKPaymentQueue) {
//恢复购买 quenu 可以拿到transition
//restoreCompletedTransactions方法会触发此方法
}
public func paymentQueue(_ queue: SKPaymentQueue, updatedTransactions transactions: [SKPaymentTransaction]) {
for pro in transactions {
switch(pro.transactionState){
case .purchased:
print(pro.tostring())
let receipurl = Bundle.main.appStoreReceiptURL
let data = NSData.init(contentsOf: receipurl!)
// 后台需要的reciptdata
let reciptdata = data!.base64EncodedString(options: Data.Base64EncodingOptions(rawValue: UInt(0)))
self.verifyPurchaseWithPaymentTransaction(queue,pro) { (trancition, response) in
//这里可以拿到 关于账号绑定的信息 transiction .applicationname
//回调下发奖励
// 奖励完成后 调用处 主动finish 当前trancition
}
break
case .purchasing:
print("购买中")
for delegate in self.purchasedelegate{
delegate.purchasing(trancition: pro,err: nil)
}
break
case .failed:
for delegate in self.purchasedelegate{
delegate.failed(trancition:pro)
}
break
case .restored:
for delegate in self.purchasedelegate{
delegate.restored(trancition: pro)
}
if self.purchaserestoreblock != nil{
self.purchaserestoreblock!(transactions)
}
else{
assertionFailure("has not set purchaserestoreblock ")
}
break
case .deferred:
for delegate in self.purchasedelegate{
delegate.deferred(trancition: pro)
}
break
}
}
}
2.查询商品 根据id 名 和 SKProductsRequest 发起请求 实现
public func startQuery(_ request:SKProductsRequest,ids:Set<String>){
skpayReqeust.delegate = self
skpayReqeust.start()
}
public func productsRequest(_ request: SKProductsRequest, didReceive response: SKProductsResponse) {
//response 下可以拿到商品列表详情
}
public func request(_ request: SKRequest, didFailWithError error: Error) {
//查询失败
}
3.商品购买
let payorder = SKMutablePayment()
//不知是改了还是怎么网上有人踩坑说这个字段无法被苹果记录但是现在这个是可以被记录的,因此可以跟账号相关做绑定 只要没有finish掉 transition , 再次SKPaymentQueue.default().add(self)时 会进入purchase中拿到transition 下applicationname是能拿到的
payorder.applicationname = "玩家相关信息"
SKPaymentQueue.default().add(payorder)
4.商品恢复购买
//恢复购买商品 这个是恢复非消耗类型的产品 开始有个误解以为消耗型的finish掉就还可以恢复本身这个想法就是错的 有些纠结
SKPaymentQueue.default().restoreCompletedTransactions()
5.deinit中 移除监听
SKPaymentQueue.default().remove(self)
苹果内付相对没遇到什么坑,主要是看了订单与玩家的绑定信息做了下验证,需要带上订单信息的话SKMutablePayment 中的属性
applicationname 网上部分有说不能持久化但是我这边经测试,是可以的这个持久化是苹果自己帮我们做了的,
只要你不finish 掉因为按照逻辑来说是正确的没有finish掉的再次进来始终都会进purchased
回调这种方式和谷歌内付必须消耗才能进行下一笔交易,是一个道理.