一、代码逻辑
关于iOS 订阅、自动订阅 本身功能开发很简单。跟正常的购买没什么大的差异。唯一需要特殊处理(自动订阅)的是,
在APP启动时候要增加侦听:
[[SKPaymentQueue defaultQueue] addTransactionObserver:self];
因为自动订阅,除了第一次购买行为是用户主动触发的。后续续费都是Apple自动完成的,一般在要过期的前24小时开始,苹果会尝试扣费,扣费成功的话 会在APP下次启动的时候主动推送给APP。所以,APP启动的时候一定要添加上面的那句话。
另外就是处理续费了:
- (void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray *)transactions { for (SKPaymentTransaction *transaction in transactions) { switch (transaction.transactionState) { case SKPaymentTransactionStatePurchasing: // 0 break; case SKPaymentTransactionStatePurchased: // 1 //订阅特殊处理 if(transaction.originalTransaction){ //如果是自动续费的订单originalTransaction会有内容 }else{ //普通购买,以及 第一次购买 自动订阅 } break; case SKPaymentTransactionStateFailed: // 2 [self failTracker:transaction]; break; case SKPaymentTransactionStateRestored: // 3 [self _restoreTransaction:transaction]; break; default: break; } } }
上述代码片段对 transaction.originalTransaction 进行了判断,如果有内容一定为订阅类型的。为什么在这加个判断处理,是因为续费 是发生在APP启动的时候,这时候你登录流程等可能还没有走完,因为有的游戏在跟服务器进行 校验的时候会传一些userid等信息,或是加密的信息,视情况而定,是否要区分处理。
注意点:就是在沙箱环境测试时候,APP启动可能得到5次的 - (void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray *)transactions ;订单处理,算是要并发处理case SKPaymentTransactionStatePurchased: 这种case,这时候你得注意你得网络请求队列,不要最后一个订单请求覆盖了 前几个。(可以用信号量处理下,比较简单)
二、服务器验证receipt
服务器在校验receipt时候也就有一个坑:
1、那就是创建自动订阅的时候需要新建一个共享秘钥,就是一串字母。
2、服务器在向苹果服务器校验receipt时候,不仅需要传receipt,还需要传秘钥。
{ “receipt-data” : “(actual receipt bytes here)” “password” : “(shared secret bytes here)” }
3、介绍下receipt结构
receipt通过base64解码可得:
{ "signature" = "dfreree...."; //也是base64 "purchase-info" = "ewoJIm9x....."; //也是base64,这个里面存放详细时间,流水号等 "environment" = "Sandbox"; "pod" = "100"; "signing-status" = "0"; }
"purchase-info"可以再次base64解码可得:
{ "original-purchase-date-pst" = "2017-08-29 23:52:45 America/Los_Angeles"; "purchase-date-ms" = "1504144439749"; "unique-identifier" = "a063c2c321dd885642a5cddd9160e0ad8291d978"; "original-transaction-id" = "1000000328915948"; "expires-date" = "1504144739749"; "transaction-id" = "1000000329310742"; "original-purchase-date-ms" = "1504075965000"; "web-order-line-item-id" = "1000000036091900"; "bvrs" = "1"; "unique-vendor-identifier" = "B78549AC-58D4-4750-8E6F-F4CCE6138A5A"; "expires-date-formatted-pst" = "2017-08-30 18:58:59 America/Los_Angeles"; "item-id" = "1276511095"; "expires-date-formatted" = "2017-08-31 01:58:59 Etc/GMT"; "product-id" = "lcm.denachina.pickle.38.1month"; "purchase-date" = "2017-08-31 01:53:59 Etc/GMT"; "original-purchase-date" = "2017-08-30 06:52:45 Etc/GMT"; "bid" = "com.denachina.pickle"; "purchase-date-pst" = "2017-08-30 18:53:59 America/Los_Angeles"; "quantity" = "1"; }
你想要的东西,都可以获取到。客户端可以做这些事情,但是没有多大必要,还是服务器处理得好。(对于无服务器APP只能客户端处理了)
附上一个在线base64解码的:http://base64.xpcha.com/
三、自动续费测试
重点都不是上面的,重点是测试,如何测试?尤其自动续费怎么测?
先看下Apple原文档:
When testing auto-renewable subscriptions in the test environment, keep in mind that the duration times are compressed. Additionally, test subscriptions only auto-renew a maximum of six times. Table 3-1 lists the compressed duration times.
Actual duration |
Test duration |
---|---|
1 week |
3 minutes |
1 month |
5 minutes |
2 months |
10 minutes |
3 months |
15 minutes |
6 months |
30 minutes |
1 year |
1 hour |
意思就是,沙箱环境 自动续费时间缩短了,一周 对应 三分钟,一月 对应 五分钟。。。
购买完一个一周 类型订阅,就不要在APP不退出的情况等待了,必须3分钟 或是 10分钟后重新登录,Apple才会主动告知你结果,也就是第一点提到的。
测试中会遇到几个问题:
1.沙箱环境自动续费是一定会自动续费的吗?
答案:不一定的,有时候会,有时候不会。所以要多测测,多建几个测试账号。
2.是否需要实现restoreCompletedTransactions ?
答案:视需求吧。有少量文章说2014年起苹果审核严格了,必须要有一个按钮实现restoreCompletedTransactions。另外,我听百度一位同学说,爱奇艺2015年因为这个被拒过。但是,目前来看很多使用了订阅的应用或是游戏,并没有这个功能。
我是感觉,看需求了。订阅 是跟着 userid 唯一呢? 还是跟着apple id 呢?在国内,一般都是前者。
四、讨论
1.自动订阅归属的问题:
a. 苹果设计自动订阅的初衷是 ,订阅一个服务, 这个服务需要跟着 Apple ID走。说白了,就是你A设备 用了Apple账号100001购买了,你换了B设备 用Apple账号100001登录app store,你同样能享受到服务。国外的一些音乐类型、杂志报刊等用的比较多,游戏类的少,苹果自己的Apple music也有自动订阅(首创)。
b. 目前国内的一些应用或是游戏,希望的是自动订阅 关联的是 APP的 user id ,而不是Apple ID。说白了,就是你购买了一个自动订阅服务,我不管你哪个apple id 支付的, 但是只能我一个 APP的 唯一用户可以享受服务。这时候就需要APP自身做处理了,就是记住首次购买的transaction-id,并且绑定某个用户。以后自动续费的话,都会有original-transaction-id,这个id 是第一次购买的transaction-id,根据这个服务器可以联系初始购买的服务。有点描述偏了,当transaction-id绑定了用户,再次收到其它用户transaction-id请求时候,视情况处理了。(你也可以根据unique-vendor-identifier处理)
2.同一个Apple ID购买完的自动订阅,可以再次点击购买吗(有效期内)?
答案:不可以,苹果自身会拦截,会出现这么个提示窗,如下图:
但是,sandbox测试环境,在第三大点的对应表格对应时间内,apple会拦截的,过了这个时间苹果是不会拦截的。
3.够买了自动订阅3个月的,可以换购 1年的 或是 1个月的吗?
答案:可以,苹果文档有提到,视为升级订阅套餐 或是 降级订阅套餐。
4.关于掉单的问题
答案:一定要在服务器校验完票据后,客户端收到服务器的反馈结果后再:
[[SKPaymentQueue defaultQueue] finishTransaction: transaction];
5.关于普通消费商品,如何防止黑卡、掉单、外币等?
我有时间会再写一篇。
五、了解更多
https://stackoverflow.com/questions/8033673/ios-sandbox-environment-auto-renewal-subscription
http://www.jianshu.com/p/28fc3cc8c49f
http://www.cnblogs.com/zidong0822/p/4701839.html
http://blog.csdn.net/xiaoyuanzhiying/article/details/46708043
http://www.jianshu.com/p/e9e4dc3dc9ee
https://www.raywenderlich.com/154737/app-purchases-auto-renewable-subscriptions-tutorial
http://www.360doc.com/content/14/1118/16/12282510_426165722.shtml
关于验证:
关于预防刷:
http://blog.csdn.net/skylin19840101/article/details/71757055