对接苹果支付流程上相较于微信或者支付宝来说 后端做的代码是非常少的 但是需要注意的点很多
对于支付 最重要的是知道这笔支付到底是不是真正意义上成交了,以及成交价格是否对应我们的商品价格 这是我们需要去判断的
苹果支付对于微信支付和支付宝支付来说,有本质的区别。苹果支付的商品需要现在appstore里面去上架。所以整个支付流程如下
APP内请求苹果SDK发起支付。并完成购买。支付成功之后APP端会传一段receipt_data(同时需要带上APP支付成功的交易单号)给后端去做验证。后端拿到这个数据之后,会再次请求苹果服务器去解析数据。
结构如下:
{
"receipt": {
"receipt_type": "ProductionSandbox",
"adam_id": 0,
"app_item_id": 0,
"bundle_id": "申请苹果支付时的串号 固定的值",
"application_version": "24",
"download_id": 0,
"version_external_identifier": 0,
"receipt_creation_date": "2022-02-18 08:02:19 Etc/GMT",
"receipt_creation_date_ms": "1645171339000",
"receipt_creation_date_pst": "2022-02-18 00:02:19 America/Los_Angeles",
"request_date": "2022-02-23 13:21:51 Etc/GMT",
"request_date_ms": "1645622511107",
"request_date_pst": "2022-02-23 05:21:51 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": "10000",#产品ID
"transaction_id": "1000000972016787", ##交易单号
"original_transaction_id": "1000000972016787",
"purchase_date": "2022-02-18 08:02:19 Etc/GMT",
"purchase_date_ms": "1645171339000",
"purchase_date_pst": "2022-02-18 00:02:19 America/Los_Angeles",
"original_purchase_date": "2022-02-18 08:02:19 Etc/GMT",
"original_purchase_date_ms": "1645171339000",
"original_purchase_date_pst": "2022-02-18 00:02:19 America/Los_Angeles",
"is_trial_period": "false",
"in_app_ownership_type": "PURCHASED"#交易状态
},
{
"quantity": "1",
"product_id": "betterwe_camp_21",
"transaction_id": "1000000970947758",
"original_transaction_id": "1000000970947758",
"purchase_date": "2022-02-17 07:26:24 Etc/GMT",
"purchase_date_ms": "1645082784000",
"purchase_date_pst": "2022-02-16 23:26:24 America/Los_Angeles",
"original_purchase_date": "2022-02-17 07:26:24 Etc/GMT",
"original_purchase_date_ms": "1645082784000",
"original_purchase_date_pst": "2022-02-16 23:26:24 America/Los_Angeles",
"is_trial_period": "false",
"in_app_ownership_type": "PURCHASED"
},
]
},
"environment": "Sandbox",
"status": 0
}
订单总共如下几种状态
状态 | 描述 |
---|---|
Purchased | 购买成功 |
Restored | 恢复购买 |
Failed | 失败 |
Deferred | 等待确认,儿童模式需要询问家长同意 |
1) app从服务器获取产品标识列表
2) app从app store 获取产品信息
3) 用户选择需要购买的产品
4) app 发送 支付请求到app store
5) app store 处理支付请求,返回transaction信息
6) app 将transaction receipt 发送到服务器
7) 服务器收到收据后发送到app stroe验证收据的有效性
8) app store 返回收据的验证结果
9) 根据app store 返回的结果决定用户是否购买成功
基本的支付校验流程如下:
下面贴上具体的业务逻辑代码
public function actionsApplepay($receipt_data, $order_number, $transaction_id, $order_info, $is_test){
$ret = array();
$ret['status'] = 200;
$ret['msg'] = "ok";
try{
//修改状态为校验中
(new Order())::updateAppleOrderIsPaying($order_number);
if ($is_test == 1) { //沙盒购买地址
$url = "https://sandbox.itunes.apple.com/verifyReceipt";
}else{ //正式购买地址
$url = "https://buy.itunes.apple.com/verifyReceipt";
}
$receipt_data = str_replace(' ',"+", $receipt_data);
$post_data = json_encode(array("receipt-data" => $receipt_data));
$response = https_request($url, $post_data);
$res = json_decode($response, true);
$err_msg = array(
'21000' => 'App Store不能读取你提供的JSON对象',
'21002' => 'receipt-data域的数据有问题',
'21003' => 'receipt无法通过验证',
'21004' => '提供的shared secret不匹配你账号中的shared secret',
'21005' => 'receipt服务器当前不可用',
'21006' => 'receipt合法,但是订阅已过期。服务器接收到这个状态码时,receipt数据仍然会解码并一起发送',
'21007' => 'receipt是Sandbox receipt,但却发送至生产系统的验证服务',
'21008' => 'receipt是生产receipt,但却发送至Sandbox环境的验证服务'
);
// 判断是否购买成功
if(intval($res['status']) === 0){
//支付成功 更改订单状态
if($res['receipt']['bundle_id'] != 'xxxx'){
throw new Exception('非法请求!', -1);
}
$pay_status = false;//默认支付为失败态
if(count($res['receipt']['in_app'])){
foreach ($res['receipt']['in_app'] as $apple_order){
//满足二次验证的商品和客户端的商品一致 且 apple订单号一致 且支付状态为 PURCHASED 方式
if($apple_order['product_id'] == $order_info['goods_id'] && $apple_order['transaction_id'] == $transaction_id && $apple_order['in_app_ownership_type'] == 'PURCHASED'){
//如果查询到里面有满足条件的 将状态置为true
$pay_status = true;
}
}
}
if($pay_status == true){
$update_order_info_res = (new Order())->updateOrderIsPaySuccess($order_number, 3, $transaction_id);
if($update_order_info_res['status'] != 200){
throw new Exception($update_order_info_res['msg'], $update_order_info_res['status']);
}
}else{
throw new Exception('未查询到当前订单的支付成功记录', -3);
}
}else{
//沙盒情况下直接走完流程
if($res['status'] == 21007){
$update_order_info_res = (new Order())->updateOrderIsPaySuccess($order_number, 3, 'XXXXXXXXXX-SANDBOX');
if($update_order_info_res['status'] != 200){
throw new Exception($update_order_info_res['msg'], $update_order_info_res['status']);
}
}else{
(new Order())::recordOrderErrorLog($order_number, '购买失败 status:'.$res['status'].' - '.@$err_msg[$res['status']].'receipt_data:'.$receipt_data);
throw new Exception('购买失败 status:'.$res['status'].' - '.@$err_msg[$res['status']], $res['status']);
}
}
}catch (Exception $e) {
//修改状态为校验中
(new Order())::updateAppleOrderIsNoPaying($order_number);
$ret['status'] = $e->getCode();
$ret['msg'] = $e->getMessage();
}
return $ret;
}
上面为具体的业务逻辑代码