一.基本信息
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上创建的秘钥
关于秘钥:
如果创建了自动续订订阅,由于接口一样,不管是消耗型还是订阅,查单都必须传password.
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结构
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会在以下状态时调用
第一个是首次订阅成功
第二个是取消了订阅
第三个是恢复了订阅
第四个也是恢复了订阅,在sandbox测试时,6次收据之后,重新订阅,就会是这个状态
第五个也是取消了订阅,在sandbox测试时,6次收据之后,就会变成这个状态
第六个是各种状态改变下都会调用,比如说首次购买会收到两次回调,一个是INITIAL_BUY一个是DID_CHANGE_RENEWAL_STATUS
上面是一个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.由于App的设计不同,可能会要求必须实现未登录的订阅功能,或者说游客购买.
2.自动续订订阅是App设计的一种商品,在App Store connect创建商品,然后再App中购买,支付的是现金,不能实现虚拟币购买.
4.充值界面需要显示出自动续期订阅的说明,以及相关服务的协议(如会员)的连接,还有自动续期订阅的协议的连接
5.App Store connect的App说明也需要在最下面加上关于自动订阅的说明,具体可以在App Store找一个音乐App或者视频App看看.