苹果支付分为沙盒环境和生产环境
苹果支付过程中离不开一个东西--receipt(凭证)
附收据(拿着凭证成功验单的收据json):
{
"status":0,
"environment":"Production",
"receipt":{
"receipt_type":"Production",
"adam_id":1494433742,
"app_item_id":1494433742,
"bundle_id":"cn.prod.u2",
"application_version":"1",
"download_id":35061442602062,
"version_external_identifier":835287540,
"receipt_creation_date":"2020-04-04 08:32:50 Etc/GMT",
"receipt_creation_date_ms":"1585989170000",
"receipt_creation_date_pst":"2020-04-04 01:32:50 America/Los_Angeles",
"request_date":"2020-04-15 08:31:12 Etc/GMT",
"request_date_ms":"1586939472671",
"request_date_pst":"2020-04-15 01:31:12 America/Los_Angeles",
"original_purchase_date":"2020-04-04 08:20:48 Etc/GMT",
"original_purchase_date_ms":"1585988448000",
"original_purchase_date_pst":"2020-04-04 01:20:48 America/Los_Angeles",
"original_application_version":"1",
"in_app":[
{
"quantity":"1",
"product_id":"vip.u2.18",
"transaction_id":"150000629315025",
"original_transaction_id":"150000629315025",
"purchase_date":"2020-04-04 08:32:14 Etc/GMT",
"purchase_date_ms":"1585989134000",
"purchase_date_pst":"2020-04-04 01:32:14 America/Los_Angeles",
"original_purchase_date":"2020-04-04 08:32:14 Etc/GMT",
"original_purchase_date_ms":"1585989134000",
"original_purchase_date_pst":"2020-04-04 01:32:14 America/Los_Angeles",
"expires_date":"2020-04-07 08:32:14 Etc/GMT",
"expires_date_ms":"1586248334000",
"expires_date_pst":"2020-04-07 01:32:14 America/Los_Angeles",
"web_order_line_item_id":"150000230093835",
"is_trial_period":"true",
"is_in_intro_offer_period":"false"
}
]
},
"latest_receipt_info":[
{
"quantity":"1",
"product_id":"vip.u2.18",
"transaction_id":"150000629315025",
"original_transaction_id":"150000629315025",
"purchase_date":"2020-04-04 08:32:14 Etc/GMT",
"purchase_date_ms":"1585989134000",
"purchase_date_pst":"2020-04-04 01:32:14 America/Los_Angeles",
"original_purchase_date":"2020-04-04 08:32:14 Etc/GMT",
"original_purchase_date_ms":"1585989134000",
"original_purchase_date_pst":"2020-04-04 01:32:14 America/Los_Angeles",
"expires_date":"2020-04-07 08:32:14 Etc/GMT",
"expires_date_ms":"1586248334000",
"expires_date_pst":"2020-04-07 01:32:14 America/Los_Angeles",
"web_order_line_item_id":"150000230093835",
"is_trial_period":"true",
"is_in_intro_offer_period":"false",
"subscription_group_identifier":"20608590"
}
],
"latest_receipt":"此处因凭证太长省略",
"pending_renewal_info":[
{
"expiration_intent":"2",
"auto_renew_product_id":"vip.u2.18",
"original_transaction_id":"150000629315025",
"is_in_billing_retry_period":"1",
"product_id":"vip.u2.18",
"auto_renew_status":"1"
}
]
}
下面以自动订阅为例
苹果支付和微信/支付宝支付逻辑不太一样,
若是支付宝/微信支付,我们首先需要创建预支付订单返回给APP,APP拿着预支付订单号去支付宝微信支付,服务端在等待回调就可以了
苹果支付可以直接验单,无需等待回调(官方提供了回调,可以再官网上面配置)
app发起支付,支付成功后会返回receipt凭证
app上线需要沙盒环境测试,本人开发也需要沙盒测试,所以代码需要同时兼容沙盒和生产环境
我们可以首先向生产环境发送请求
# 向生产环境发送请求 默认向生产环境发送
PASSWORD = "内购订阅秘钥"
def request_to_product(receipt, num=0, password=None):
num += 1
try:
# url = 'https://sandbox.itunes.apple.com/verifyReceipt'
url = 'https://buy.itunes.apple.com/verifyReceipt'
if password:
data = json.dumps({"receipt-data": receipt, 'password': password})
else:
data = json.dumps({"receipt-data": receipt})
headers = {"Content-type": "application/json"}
res = requests.post(url=url, data=data, headers=headers, verify=False).text
return res
except:
# 仅验证3次,当请求出错时
res = 'fail'
if num < 4:
res = request_to_product(receipt, num=num, password=PASSWORD)
return res
返回的json收据中会包含两个字段(参考上面的附录,收据json)
"status":0,
"environment":"Production",
status 字段含义
0:"验单成功!"
21000: "未使用HTTPPOST请求方法向AppStore发送请求。",
21001: "此状态代码不再由AppStore发送。",
21002: "该receipt - data属性中的数据格式错误或丢失。",
21003: "收据无法认证。",
21004: "您提供的共享密码与您帐户的文件共享密码不匹配。",
21005: "收据服务器当前不可用。",
21006: "该收据有效,但订阅已过期。当此状态代码返回到您的服务器时,收据数据也会被解码并作为响应的一部分返回。仅针对自动续订的iOS6样式的交易收据返回。",
21007: "该收据来自测试环境,但已发送到生产环境以进行验证。",
21008: "该收据来自生产环境,但是已发送到测试环境以进行验证。",
21009: "内部数据访问错误。稍后再试。",
21010: "找不到或删除了该用户帐户。",
若status == 0,收据获取成功!
若status == 21007 需要再次向测试环境发送请求
# 向沙盒境发送请求
PASSWORD = "内购订阅秘钥"
def request_to_sandbox(receipt, num=0, password=None):
num += 1
try:
url = 'https://sandbox.itunes.apple.com/verifyReceipt'
if password:
data = json.dumps({"receipt-data": receipt, 'password': password})
else:
data = json.dumps({"receipt-data": receipt})
headers = {"Content-type": "application/json"}
res = requests.post(url=url, data=data, headers=headers, verify=False).text
return res
except:
# 仅验证3次,当请求出错时
res = 'fail'
if num < 4:
res = request_to_sandbox(receipt, num=num, password=PASSWORD)
return res
因为苹果服务器在国外,可能会有延时,出错时候多请求几次没毛病
主要参数(对于订阅而言)
latest_receipt_info:所有订单信息
pending_renewal_info:订阅信息
pending_renewal_info中original_transaction_id是原始订单号,非常重要,我们可以把凭证和这个原始订单号绑定起来,存储起来,留验单使用
其他字段信息看看官方说明文档吧
订单过期时间可以直接使用latest_receipt_info中expires_date_ms字段,因为苹果可能会漏单,所以订阅到期时,可以自动任务拿着存储好的凭证去验单(防止漏单)
后面根据订单添加会员等权限就不啰嗦了。