iOS苹果内购-IAP掉单问题优化

西门桃桃 2021-01-19 PM 7113℃ 1条

背景简介

In-App Purchase(以下简称IAP)是苹果为开发者提供的应用内购服务。开课吧客户端也接入了IAP服务用于充值虚拟币。最近收到客服小姐姐反馈有学员在APP上充了钱,但是没到账。于是就有了这篇关于IAP掉单问题优化的方案。

掉单问题分析

首先根据源码重新梳理之前的支付流程:

old_process.png

流程总的来说主要分为3步:

  1. 客户端调用Kaikeba Server创建订单
  2. iTunes store支付完成,Apple Server通知客户端
  3. 客户端调用Kaikeba Server验证订单

从流程上来看,导致掉单有以下几种可能:

  1. 用户支付成功,Apple Server回调客户端通信失败。
  2. Apple Server回调客户端后,客户端与Kaikeba Server通信失败。
  3. Kaikeba Server验证成功后,Kaikeba Server与客户端通信失败。

所以防止掉单就要从这几个方面入手。

解决方案

新的支付流程如下:

new_process.png

新的流程通过以下几种方式解决掉单问题:

  1. 网络异常重试,减少掉单
  2. 利用苹果事务机制保存交易,重启APP后自动补单
  3. 在使用applicationUsername传递OrderNo时,同时Keychain保存OrderNo,防止OrderNo丢失
  4. 对于丢失了OrderNo的订单,Kaikeba Server根据用户之前创建的订单获取

网络异常重试

网络异常重试:主要是为了避免在付款成功后,用户网络状况发生变化(如,乘坐地铁进入隧道)导致与Kaikeba Server通信失败或者Kaikeba Server服务器挂掉自动重启导致通信异常。

同时为了避免频繁的重试,设置5次重试间隔分别是:1,5,10,100,600

苹果事务机制

IAP中每一次支付行为都被抽象成一个事务(SKPaymentTransaction),只有事务被正常完结(调用finishTransaction:)本次支付行为才算完成。在每一次app启动时,通过调用addTransactionObserver:就会触发之前所有未完结的事务。详见:支付队列观察者。所以,由于事务机制的存在,我们只需做到以下两点就可以避免掉单:

  1. 对于每一个支付事务,在确保服务端处理完后再结束(finishTransaction:)该事务。
  2. App启动时,注册支付队列观察者(addTransactionObserver:)并添加相应补单逻辑。

业务OrderNo丢失

开课吧IAP中创建订单生成的OrderNo是通过SKMutablePayment的applicationUsername来传递的,但是Apple提供applicationUsername,是为了防止用户作弊而不是用于透传业务信息的。如果用户不停的卸载APP,OrderNo就会有可能丢失。

为了防止OrderNo丢失后导致掉单,所以我们把校验接口的OrderNo改为了非必传参数,如果OrderNo丢失,那么Kaikeba Server根据用户之前创建的订单重新获取。

注意事项

1、 [NSBundle mainBundle].appStoreReceiptURL只是一个URL,在用户付款成功后,系统会把receipt写入到这个位置。取receipt的时候要判空,如果文件不存在,就要从苹果服务器重新刷新下载receipt了。SKReceiptRefreshRequest刷新的时候,需要用户输入Apple ID,同时需要网络状态良好

NSURL *receiptURL = [NSBundle mainBundle].appStoreReceiptURL;
if(![[NSFileManager defaultManager] fileExistsAtPath:receiptURL.path])
{
    SKReceiptRefreshRequest *receiptRefreshRequest = [[SKReceiptRefreshRequest alloc] initWithReceiptProperties:nil];
    receiptRefreshRequest.delegate = self;
    [receiptRefreshRequest start];
    return;
}
NSData *receiptData = [NSData dataWithContentsOfURL:receiptURL];

2、 程序加入支付队列使用的SKMutablePayment属性具有读写权限,SKPayment属性只读,如果你要使用applicationUsername透传字段,那么就一定要使用SKMutablePayment`加入支付队列

3、 透传字段applicationUsername可能返回的是nil

4、 updatedTransactions:App整个生命周期只会走一次

5、 transactionReceiptData可以无限验证通过,也就是说一个凭证可以被校验多次,需要防止有人通过这个刷单

优化后的特性

  • 即使用户删除卸载开课吧APP 重新安装登录以后 也可以找回订单 并且实现自动补单
  • 如果用户购买完成之后,网络条件不好,APP会按照斐波那契数列来做为每一次重试的间隔,总共重试10次(2分半钟),10次后如果还是不行,就联系客服,如果中间中断了(用户退出APP)APP重启后会自动补单

结果

埋点数据统计出来了再补上。。。

标签: IAP

非特殊说明,本博所有文章均为博主原创。

评论啦~



唉呀 ~ 仅有一条评论


  1. javans
    javans

    能否把这个源码或者文档之类的资料 关于 iap的发给我一下呀

    回复 2022-01-17 16:53