内购类型有四种:
消耗型商品,非消耗型商品,非续期订阅,自动续期订阅. 其中最有难度的就是自动续期订阅的实现,开通自动续期订阅后,订阅会员的处理将会遇到如下难点问题:自动订阅的到期继续自动订阅的处理,订阅取消的处理,取消后又在App Store开启自动订阅的处理等一系列问题
异常情况说明提醒用户:
当用户使用appleid下买了一个月的应用会员,换个账号登录当前app,appleid不变,购买的时候提示我已经订阅过该项目,这种问题怎么处理
客户端开发者方面汇总
APP专用共享秘钥
App 专用共享密钥是用于接收此 App 自动续订订阅收据的唯一代码。如果您要将此 App 转让给其他开发者或不想公开主共享密钥,建议使用 App 专用共享密钥。
注: 经过验证:购买过自动续期订阅后,验证内购时(即使是消耗型商品)必须带上password字段,测试模式没有提供用户取消订阅或者进行退款的测试
订阅群组
创建自动续订类型的时候,当你创建一个订阅产品时点击创建后会自动让你创建一个订阅群组,以向用户提供一系列内容供应、服务等级或时限。名字自己随便起,就是给自己看的,有代表意义就行,一个群组下可以有多个自动续订订阅。如果你要搞促销优惠,那么每个顾客可以享受每个订阅群组的一个推介促销优惠一次。
创建两个订阅组,一个开发、测试,一个生产使用订阅组可有效避免用户同时订阅多个项目
注意 一个订阅群组中的订阅是 互斥 的,这意味着用户只能一次订阅一个群组中的一个选项。如果你希望用户能够一次购买多个订阅,你可以将这些 App 内购买项目放在不同的订阅群组中。
App Store 服务器通知网址
您服务器上的网址 (URL),可接收有关 App 内购买项目的重要事件(例如订阅状态变更或 App 内购买项目退款)的相关 App Store 通知。更多请看https://help.apple.com/app-store-connect/#/dev0067a330b
代码配置
在APP启动时候要增加一下监听方法以便查询到订单信息:
[[SKPaymentQueue defaultQueue] addTransactionObserver:self];
订阅订单再次购买与首次购买所执行的方法不一(如下图):
- (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;
}
}
}
测试验证
自动续费测试时需注意:
一天最多测试6次自动续费,如需更多测试验证需要多申请一些沙盒账号,用户在订阅期间同一个群组产品无法重复订阅,如需允许多次订阅请创建多个群组将产品归纳在不同的群组中,以便可以重复订阅。
APP自动续费条款
自动续订订阅,一定要在 app 中有详细的使用条款相关,类似下图:
参考文案如下:
Once you confirm your purchase, you will be charged to your Apple ID account. If purchase the auto-renewing subscription, unless you cancel the order at least 24 hours before the end of the current billing cycle, the subscription will automatically renew, and your account will charge the renewal fee within 24 hours before the end of the current billing cycle. You can unsubscribe from account settings in the app store. \r\nPrivacy Policy & Terms of Use
确认购买后,将向您的Apple ID账户收款。购买连续包年项目,除非您在当前计费周期结束前至少24小时取消订单,否则项目会自动续订,您的账户将在当前计费周期结束前24小时内收取续订费用。您可在App Store的账户设置中取消订阅。\r\n隐私政策和使用条款
服务端方面汇总
- 自动续费产品进行验证时需要凭证参数(receipt-data)和共享秘钥(password),否则在验证返回"status": 21003(沙盒环境)或(21004 你提供的共享密钥和账户的共享密钥不一致),秘钥获取如图所示:
** 再次温馨提醒:购买过自动续期订阅后,验证内购时(即使是消耗型商品)必须带上password字段。
- 请求验证
获取到票据以后我们通过App Store来验证票据是否真实
沙盒状态下使用:https://sandbox.itunes.apple.com/verifyReceipt来验证
生产环境下使用:https://buy.itunes.apple.com/verifyReceipt
2:服务端验证
经验实际验证得到以下结果:
in_app 与 latest_receipt_info
查看验证接口时发现,这两个字段的数值几乎相同,不过有几点需要注意:
1、自动续订订阅类型,在到期后会再生成一条购买记录,这条记录会出现在last_receipt_info里,但不会出现在in_app里
2、自动续订订阅类型可以配置试用,试用记录只有在latest_receipt_info里,is_trial_period字段才是true
3、消耗型购买记录未出现在latest_receipt_info,需要检查in_app来确保校验正确
4、消耗型产品是无法查看到取消购买或者退款状态
5、当订单信息中有 Cancellation Date 字段说明该订单被取消,不管该订阅的过期日期是什么,该订单都可以设为无效订单未进行扣款成功或者发起了退款
6、因为自动订阅可能重视的就是到期时间,所以应该关注的是“ expires_date”、“ expires_date_ms”、“ expires_date_pst”这三个字段,expires_date表示过期时间,expires_date_ms 表示过期时间毫秒数值,expires_date_pst表示的是太平洋时间
7、由于 expires_date 表示的是美国时间,所以一般都采用expires_date_ms数值来进行验证有效期
8、判断是否有自动续费产品,推荐循环 latest_receipt_info订单列表判断是否有大于当前时间日期,同时判断是否有 Cancellation Date 字段;
9、transaction.originalTransaction是 一组自动续订类型,购买过,里面originalTransactionId会一样。然后transaction.originalTransaction.transactionIdentifier这个会多单,每一单对应的就是用户的购买记录。周期到期,一般就expires_date时间到期,拿receipt去苹果服务端验证,用户是否续订了。
客户端上报
apple每期自动扣款后, 会生成一笔新的receipt, 客户端获取后发送给server校验, 成功后开通下一期会员权益
状态变更通知
用于自动续订订阅的服务器到服务器通知服务, 可以在苹果后台配置通知地址, 状态变更时, server会收到通知, 下面是摘自苹果官方的状态描述
说明:
1.扣款失败导致过期,不会有任何通知
2.正常扣款续订,不会有任何通知
3.服务端最好处理CANCEL类型。因为IAP存在黑产:比如买了一年会员,然后打电话给苹果客服退款,如果服务端不处理,这一年会员是生效的。(退款通知只有订阅和非订阅类型才有,消耗型无退款通知)
官方文档 https://developer.apple.com/documentation/storekit/#//apple_ref/doc/uid/TP40008267-CH7-SW13
会员状态轮询
server轮询 自动续订类型的收据, 每一期的latest_receipt_info中都会记录所有的交易(包含历史和新增), 可以轮询上一期(任意一期都可以)receipt, 通过latest_receipt_info 解析出用户最新的订阅状态.
关于用户取消和用户续订:
cancellation_date_ms
此字段表示 Apple 客户支持取消交易的时间和日期,或自动续订订阅计划升级的时间和日期,采用 UNIX 纪元时间格式,以毫秒为单位。此字段仅适用于 Apple 退还给用户的购买。使用此时间格式处理日期。
取消的应用内购买无限期保留在收据中。仅当退款用于非消耗性产品、自动续订订阅或非续订订阅时,才会出现此值。
此字段仅在生产中可用,不会出现在沙盒收据中。
您可以使用此值:
确定是否停止提供与购买相关的内容。
检查任何最新的续订交易,这可能表明用户重新开始或升级了他们的订阅,以进行自动续订订阅购买。
如用户取消,这个凭证里面有 cancellation_date_ms 字段判断。 检索订单如到期就不用管。 如果到期自动续订了,就要客户端重新传凭证给服务端,服务端去苹果验证订单。
因此,在判断订阅是否过期的时候,cancellation_date_ms 和 expires_date_ms 都需要判断。
苹果官方文档https://developer.apple.com/documentation/appstorereceipts/cancellation_date_ms
苹果验证结果说明
下面是凭证验证返回的详细结果:
当订阅产品验证后,新增返回了 latest_receipt_info 订阅与非订阅订单信息列表
非订阅和消耗品订单如下图:
in_app 返回结果
订阅和非订阅订单数据内容如下:
latest_receipt_info 返回结果
关于订阅订单参数做一个详细说明(参数后面有注释):
{
"quantity": "1",
"product_id": "产品Id", /// 产品Id
"transaction_id": "1000000850764418",
"original_transaction_id": "1000000850764418", /// 此id值不变
"purchase_date": "2021-07-30 03:13:27 Etc/GMT", ///最新的购买时间
"purchase_date_ms": "1627614807000", ///最新的购买时间毫秒
"purchase_date_pst": "2021-07-29 20:13:27 America/Los_Angeles", ///最新的购买时间(太平洋的时间)
"original_purchase_date": "2021-07-30 03:13:29 Etc/GMT", ///最初的购买时间
"original_purchase_date_ms": "1627614809000", ///最初的购买时间毫秒
"original_purchase_date_pst": "2021-07-29 20:13:29 America/Los_Angeles", /// 最初的购买时间(太平洋的时间)
"expires_date": "2021-07-30 03:18:27 Etc/GMT", /// 时间到期
"expires_date_ms": "1627615107000", ///到期时间毫秒
"expires_date_pst": "2021-07-29 20:18:27 America/Los_Angeles", / //到期时间(太平洋的时间)
"web_order_line_item_id": "1000000064562233",
"is_trial_period": "false", /// 首次购买
"is_in_intro_offer_period": "false", /// 是否是否是在试用期
"in_app_ownership_type": "PURCHASED",
"subscription_group_identifier": "20857364"
}