{'receipt-data': receiptData, 'password': 'xxxxxxxxxxxx'}
,password 为非自动订阅式购买必传响应示例:
{
'receipt':
{
'receipt_creation_date_ms': '1562909318000',
'adam_id': 0,
'receipt_creation_date': '2019-07-12 05:28:38 Etc/GMT',
'version_external_identifier': 0,
'original_purchase_date_pst': '2013-08-01 00:00:00 America/Los_Angeles',
'original_purchase_date_ms': '1375340400000',
'bundle_id': 'xxxxxxx',
'receipt_creation_date_pst': '2019-07-11 22:28:38 America/Los_Angeles',
'request_datea_ms': '1562909325495',
'app_item_id': 0,
'original_purchase_date': '2013-08-01 07:00:00 Etc/GMT',
'request_date_pst': '2019-07-11 22:28:45 America/Los_Angeles',
'original_application_version': '1.0',
'application_version': '1',
'receipt_type': 'ProductionSandbox',
'download_id': 0,
'request_date': '2019-07-12 05:28:45 Etc/GMT',
'in_app': [
{
'is_trial_period': 'false',
'original_purchase_date_ms': '1562908866000',
'quantity': '1',
'purchase_date_ms': '1562908866000',
'purchase_date_pst': '2019-07-11 22:21:06 America/Los_Angeles',
'product_id': 'xxxxxx',
'original_purchase_date': '2019-07-12 05:21:06 Etc/GMT',
'transaction_id': '100000xxxxx826',
'original_transaction_id': '10000005xxxx826',
'original_purchase_date_pst': '2019-07-11 22:21:06 America/Los_Angeles',
'purchase_date': '2019-07-12 05:21:06 Etc/GMT'},
{
'is_trial_period': 'false',
'original_purchase_date_ms': '1562909318000',
'quantity': '1',
'purchase_date_ms': '1562909318000',
'purchase_date_pst': '2019-07-11 22:28:38 America/Los_Angeles',
'product_id': 'xxxxxx',
'original_purchase_date': '2019-07-12 05:28:38 Etc/GMT',
'transaction_id': '1000000xxxxx664',
'original_transaction_id': '1000000xxxxx664',
'original_purchase_date_pst': '2019-07-11 22:28:38 America/Los_Angeles',
'purchase_date': '2019-07-12 05:28:38 Etc/GMT'
}
]
},
'status': 0,
'environment': 'Sandbox'
}
收据在 receipt 中的 in_app 字段里,这是数组包对象的形式,里面的每一个对象都是一次交易
恢复购买的校验,由 IOS 端将产品和用户的 AppleID 联系起来,可生成唯一的 uuid,通过校验此 uuid 直接获得用户的鉴权结果和订阅状态,也可单独作为用户身份的校验。
收据示例:
{
'status': 0,
'environment': 'Sandbox',
'receipt': {
'receipt_type': 'ProductionSandbox',
'adam_id': 0,
'app_item_id': 0,
'bundle_id': ‘…’,
'application_version': '1',
'download_id': 0,
'version_external_identifier': 0,
'receipt_creation_date': '2019-09-11 10:12:57 Etc/GMT',
'receipt_creation_date_ms': '1568196777000',
'receipt_creation_date_pst': '2019-09-11 03:12:57 America/Los_Angeles',
'request_date': '2019-09-11 11:38:36 Etc/GMT',
'request_date_ms': '1568201916879',
'request_date_pst': '2019-09-11 04:38:36 America/Los_Angeles',
'original_purchase_date': '2013-08-01 07:00:00 Etc/GMT',
'original_purchase_date_ms': '1375340400000',
'original_purchase_date_pst': '2013-08-01 00:00:00 America/Los_Angeles',
'original_application_version': '1.0',
'in_app': [
{
'quantity': '1',
'product_id': ‘…’,
'transaction_id': '1000000567124099',
'original_transaction_id': '1000000567124099',
'purchase_date': '2019-09-11 10:06:34 Etc/GMT',
'purchase_date_ms': '1568196394000',
'purchase_date_pst': '2019-09-11 03:06:34 America/Los_Angeles',
'original_purchase_date': '2019-09-11 10:06:35 Etc/GMT',
'original_purchase_date_ms': '1568196395000',
'original_purchase_date_pst': '2019-09-11 03:06:35 America/Los_Angeles',
'expires_date': '2019-09-11 11:06:34 Etc/GMT',
'expires_date_ms': '1568199994000',
'expires_date_pst': '2019-09-11 04:06:34 America/Los_Angeles',
'web_order_line_item_id': '1000000046836911',
'is_trial_period':'false',
'is_in_intro_offer_period': 'false'
}
]
},
'latest_receipt_info': [
{
'quantity': '1',
'product_id': ‘…’,
'transaction_id': '1000000567124099',
'original_transaction_id': '1000000567124099',
'purchase_date': '2019-09-11 10:06:34 Etc/GMT',
'purchase_date_ms': '1568196394000',
'purchase_date_pst': '2019-09-11 03:06:34 America/Los_Angeles',
'original_purchase_date': '2019-09-11 10:06:35 Etc/GMT',
'original_purchase_date_ms': '1568196395000',
'original_purchase_date_pst': '2019-09-11 03:06:35 America/Los_Angeles',
'expires_date': '2019-09-11 11:06:34 Etc/GMT',
'expires_date_ms': '1568199994000',
'expires_date_pst': '2019-09-11 04:06:34 America/Los_Angeles',
'web_order_line_item_id': '1000000046836911',
'is_trial_period': 'false', 'is_in_intro_offer_period': 'false'
},
{
'quantity': '1',
'product_id': ‘…’,
'transaction_id': '1000000567154188',
'original_transaction_id': '1000000567124099',
'purchase_date': '2019-09-11 11:06:34 Etc/GMT',
'purchase_date_ms': '1568199994000',
'purchase_date_pst': '2019-09-11 04:06:34 America/Los_Angeles',
'original_purchase_date': '2019-09-11 10:06:35 Etc/GMT',
'original_purchase_date_ms': '1568196395000',
'original_purchase_date_pst': '2019-09-11 03:06:35 America/Los_Angeles',
'expires_date': '2019-09-11 12:06:34 Etc/GMT',
'expires_date_ms': '1568203594000',
'expires_date_pst': '2019-09-11 05:06:34 America/Los_Angeles',
'web_order_line_item_id': '1000000046836912',
'is_trial_period': 'false',
'is_in_intro_offer_period': 'false'
}
],
'latest_receipt': 'MIIVYwYJKoZIhvcNAQ…..cCoIBsnXUDnzo8pPMuV2A==',
'pending_renewal_info': [
{
'auto_renew_product_id': ‘…’,
'original_transaction_id': '1000000567124099',
'product_id': ‘…’,
'auto_renew_status': '1'
}
]
}
自动订阅要处理的情况包括:
走正常的校验流程,不同之处在于,收据里会多一个字段 latest_receipt_info,这里面存的是同一次订阅的所有交易数据,包括首次订阅,和之后的自动续费记录。简言之,一次订阅,在 in_app 字段里有一条记录,在 latest_receipt_info 字段里,会有多条记录。如图上收据示例,in_app 字段中一条记录,transaction_id 为 1000000567124099,在 latest_receipt_info 中有两条记录,transaction_id 虽然不一样,但是original_transaction_id 都是1000000567124099,证明这两次交易都是属于同一次订阅活动。所以,先取 latest_receipt_info 字段,如果为空,再取 in_app 字段,两者的内容格式是一样的。
取 latest_receipt_info 字段,直接对比 expires_date_ms。因为每个订阅产品都是经过苹果审核,时长也很明确,一经订阅,苹果既然知道订阅付费的时间,肯定也知道过期时间,这个过期时间就存在 expires_date_ms 字段中,是毫秒级的时间戳。我是每次取 latest_receipt_info 最后一条记录的 expires_date_ms,与用户的过期时间对比,如果比库中存的过期时间长,那就是有新的续费,将过期时间一改,完事。(这样做的基础是得做好完整的、基于订阅和过期时间的用户订阅状态鉴权)
第一,苹果服务器的主动通知,做过的都知道,苹果的 StatusUpdateNotification,会主动向你的服务器通知某个用户的首次订阅、续费、退订、更换订阅产品等状态变更消息。但是我接收到的内容为空,查询无果,所以我做了第二方案。如果你成功采用了这个机制,请联系我,感谢。
第二,在主动像苹果服务器校验的时候,有个字段叫 pending_renewal_info,这里面有用户的自动订阅状态,1 为自动订阅,0 为取消订阅,可以通过这个字段得知用户是否已经取消。如果取消了,由 expiration_intent 字段可得知用户的取消原因,1 为用户手动取消。
pending_renewal_info = result.get('pending_renewal_info')
if pending_renewal_info:
pending_renewal_info = pending_renewal_info[-1]
auto_renew_status = pending_renewal_info.get('auto_renew_status')
if auto_renew_status == '0':
expiration_intent = pending_renewal_info.get('expiration_intent')
if expiration_intent == '1':
... # 用户已经手动取消订阅