iOS自动续订订阅开发----验证收据和状态回调JSON解析

0.666字数 1831阅读 1645

一.基本信息

1.1订阅后,每次成功续期,打开App后会走- (void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray<SKPaymentTransaction *> *)transactions回调方法,因此启动就要监听[[SKPaymentQueue defaultQueue] addTransactionObserver:self];
并且之后在打开时调用,一直开着App是不行的,不过我没有对此做什么处理,后面会说到

1.2sandbox的续期时间是缩短了很多的


续期时间

并且会在5次续订(一共6条收据)后自动取消

二.验证收据(调用apple server的接口)

1.接口调用
自动订阅的收据验证接口和其他IAP一样
沙盒状态下使用:https://sandbox.itunes.apple.com/verifyReceipt来验证
生产环境下使用:https://buy.itunes.apple.com/verifyReceipt

但是传参不同
{
“receipt-data” : “(actual receipt bytes here)”
“password” : “(shared secret bytes here)”
}
receipt-data对应的即下面的receiveString(凭证)

NSURL * receiveUrl = [[NSBundle mainBundle] appStoreReceiptURL];
NSData * receiveData = [NSData dataWithContentsOfURL:receiveUrl];
NSString * receiveString = [receiveData base64EncodedStringWithOptions:NSDataBase64EncodingEndLineWithLineFeed];

password对应的是在App Store connect上创建的秘钥


秘钥

关于秘钥:

1.秘钥有公共和商品专用两种,如果创建使用公共秘钥,其他类型商品的对账也要传password,比如消耗型,原本App内有消耗型商品,对账只传了凭证,当我创建公共秘钥后,对账立即返回错误,加上password就可以了,不过我没有测试单个商品专用秘钥.
2.同样的,由于接口一样,使用公共秘钥后,password也必须传,现在不管是那种商品,传参都一样,因此对账的json会返回全部的票据,包括消耗型,订阅型等全部四种.没有更详细的测试,不管我猜测使用专用秘钥结果会不同.

2.JSON解析
2.1首先在第一次订阅或者回复订阅(也就是停止连续订阅后又买了)后,在- (void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray<SKPaymentTransaction *> *)transactions 回调中,
可以从SKPaymentTransaction中拿到 tran.originalTransaction.transactionIdentifier,这个标识符是绑定该appleID的,可以和App server的用户系统绑定起来,之后会继续用到.

但是这么做一旦用户使用一个appleId给多个账户充值,就会出现一对多的情况,因此这么解决
首先如果订阅不取消,是不能再购买订阅的,iOS会提示已经购买了该订阅
然后,如果取消了一个userid下的订阅,使用另一个userid购买订阅,后台在拿到original_transaction_id的时候,去数据
库查询有没有绑定了这个original_transaction_id的userid,有的话,删除绑定,这样就可以维持original_transaction_id和userid一对一的绑定,之后恢复订阅也会给这个新的userid恢复.

另外每次交易都有一个transactionIdentifier

for (SKPaymentTransaction * tran in transactions) {
    NSString *tranId = tran.transactionIdentifier;
}

这是每笔交易的票据号,需要传给后台

2.2JSON结构


JSON结构

state :0 表示正常
JSON里有两个收据列表,一个是receipt -> in_app,一个是latest_receipt_info,
前面说到的receiveString(凭证)是可以一直使用的,但是一直使用同一个receiveString,拿到的JSON里receipt -> in_app不会变化,latest_receipt_info会随着续期的期数而变化,并且会越来越多,并且如果使用公共秘钥,这里面会包含所有的票据

单个收据

前面说到的传给App server的transactionIdentifier,需要作为唯一的票据号去验证,
首先对于非连续订阅的其他类型:
都是单笔交易,每次拿到的都是新的凭证,因此in_app里都是有对应票据的,而且我发现非连续订阅的票据有时候不会在latest_receipt_info中出现, 因此这些类型是遍历in_app里的票据
如果找到transactionIdentifier相同的,那么本次交易就是有效的

然后对于连续订阅型:
1.第一次对账
遍历latest_receipt_info里的票据,如果transactionIdentifier相同,那么本次交易就是有效的,找到对应的票据后,其中最关键的是expires_date_ms这个时间戳,这个和expires_date字符串不相符, expires_date是GMT时间,少8个小时,而expires_date_ms转换后是正常的,因此不用把这个时间戳再加8小时.

2.之后的对账
由于苹果会在到期前充值,充值失败也会有回调通知,所以当数据库中的时间到期后,再去用之前存的凭据调用对账接口,
然后遍历latest_receipt_info根据product_id,也就是商品id,把连续订阅的票据筛选出来
现在筛选出来的这些就是所有的此商品的订阅票据了,而且顺序是按照时间排序的,新的在后面,如果不放心,可以自己根据里面的expires_date_ms(或者其他时间戳)再排序,然后创建订单,更新过期时间等等操作
官方文档有每个字段的含义说明

三.状态回调(apple 调用App server的接口)

这个接口(post请求)事先在App Store connect配置,在App信息一项里,并且测试时可以先填测试的url,之后再修改,应该是立即起效
配置这个url后,apple会在以下状态时调用


server to server的回调

第一个是首次订阅成功
第二个是取消了订阅
第三个是恢复了订阅
第四个也是恢复了订阅,在sandbox测试时,6次收据之后,重新订阅,就会是这个状态
第五个也是取消了订阅,在sandbox测试时,6次收据之后,就会变成这个状态
第六个是各种状态改变下都会调用,比如说首次购买会收到两次回调,一个是INITIAL_BUY一个是DID_CHANGE_RENEWAL_STATUS

取消时apple调用回调传的参数

上面是一个6次后自动取消的JSON,notification_type属于第六种.
其中auto_renew_status是false,表示续订取消,并且返回了上一次的凭证,可以获取到期时间,
并且返回了最关键的original_transaction_id,通过这个可以确定是哪个用户做了取消操作
这个回调很重要
官方文档有更详细的说明

总结一下
我使用了这种方法去标记订阅的有效期
首次订阅后App中得到receiveString和,App server存储receiveString和transactionId,绑定用户账户,
之后App server去调用验证收据接口,根据,获得original_transaction_id绑定用户,并且拿到过期时间,存储下来
快要过期时,App server再去调用收据接口,拿到最新的过期时间,然后更新
如果收到了Apple关于订阅取消的通知(DID_CHANGE_RENEWAL_STATUS并且auto_renew_status是false),一样是根据original_transaction_id更新到期时间
如果收到了apple关于恢复订阅的通知,也一样是根据original_transaction_id更新到期时间

四.关于审核

1.订阅和其他内购一样必须可以不登录购买
2.自动续订订阅只能在App Store connect创建商品,然后再App中购买.
3.有的App中含有走内购的虚拟币,因此想用虚拟币购买非自动续订订阅,这一点审核默认是不允许的,不过与审核沟通,有可能说服审核.
4.充值界面需要显示出自动续期订阅的说明,以及相关服务的协议(如会员)的连接,还有自动续期订阅的协议的连接


自动续期订阅的说明

推荐阅读更多精彩内容