苹果支付写完后遇到了苹果订单号重复问题,
是因为苹果拿到凭证后返回的数据是用户购买的所有数据的数组,而不是单次购买数据,之前看别人代码是取值第一个,还是会有这个错误,后来改成取值最后一个,也会有问题。这是为什么呢,我多次测试发现,苹果返回的in_app数据是没有固定排序的,所以需要咱自己手动排序后再取最新时间的就没有问题啦
贴代码
苹果官方文档地址(数据字段说明):https://developer.apple.com/documentation/appstorereceipts/responsebody
请求苹果:
/**
* 验证AppStore内付
* @param string $receipt_data 付款后凭证
* @return array 验证是否成功
*/
public function validateApplePay($receipt_data)
{
$result = $this->postIosPay($receipt_data);
//成功需要校验包名是否正确
if ($result['status'] == 1 && config('pay.iospay.bundle_id') != $result['data']['receipt']['bundle_id']) {
return [
'status' => 0,
'message' => '包名错误',
];
}
if ($result['status'] == 1) {//in_app按时间排序,苹果返回的数据排序是不稳定的,所以需要手动排序一下
$in_app = $result['data']['receipt']['in_app'];
array_multisort(array_column($in_app, 'purchase_date'),SORT_DESC,$in_app);
$result['data']['receipt']['in_app'] = $in_app;
}
return $result;
}
//请求苹果接口
public function postIosPay($receipt_data)
{
//小票信息
$POSTFIELDS = ["receipt-data" => $receipt_data];
//正式购买地址 沙盒购买地址
$mode = config('pay.iospay.mode');//normal正式,dev沙盒
if ($mode == 'normal') {
$url = "https://buy.itunes.apple.com/verifyReceipt";
} else if ($mode == 'dev') {
$url = "https://sandbox.itunes.apple.com/verifyReceipt";
} else {
return false;
}
/**
* 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环境的验证服务
*/
//postRequest这是我封装的网络请求,大家没有封装的话可以使用curl进行请求
//简单的curl
/*$ch = curl_init($url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, $POSTFIELDS);
$result = curl_exec($ch);
curl_close($ch);
return $result;*/
$html = postRequest($url,$POSTFIELDS);
if ($html) {
$data = json_decode($html, true);
if ($data['status'] == '0') {
return [
'status' => 1,
'message' => 'OK',
'data' => $data
];
} else if ($data['status'] == '21000') {
return [
'status' => 0,
'message' => 'App Store不能读取提供的JSON对象',
];
} else if ($data['status'] == '21002') {
return [
'status' => 0,
'message' => 'receipt-data域的数据有问题',
];
} else if ($data['status'] == '21003') {
return [
'status' => 0,
'message' => 'receipt无法通过验证',
];
} else if ($data['status'] == '21004') {
return [
'status' => 0,
'message' => '提供的shared secret不匹配你账号中的shared secret',
];
} else if ($data['status'] == '21005') {
return [
'status' => 2,
'message' => '苹果receipt服务器当前不可用',
];
} else if ($data['status'] == '21007') {
return [
'status' => 0,
'message' => 'receipt是Sandbox receipt,但却发送至生产系统的验证服务',
];
} else if ($data['status'] == '21008') {
return [
'status' => 0,
'message' => 'receipt是生产receipt,但却发送至Sandbox环境的验证服务',
];
}
}
return [
'status' => 2,//2会走队列从新请求接口
'message' => '未知的错误',
];
}
得到苹果请求数据后还需要校验商品id(product_id)和订单号(transaction_id)不能重复
//简单的校验说明
$data = $result['data'];
$in_app = $data['receipt']['in_app'];//所有的订单的信息
$k = 0;//取值第几个
$transaction_id = $in_app[$k]['transaction_id'];//订单号,需要校验自己数据库里有没有当前订单号
$apple_id = $in_app[$k]['product_id'];//商品id,需要校验商品id和下单的商品id是否一致