多应用+插件架构,代码干净,二开方便,首家独创一键云编译技术,文档视频完善,免费商用码云13.8K 广告
## Ios苹果支付流程: 1. 客户端先从苹果获取内购Id。 2. 客户端将内购id,金额、用户id等传给服务端获取一个自己服务端生成的订单号。 3. 客户端向苹果发起支付。 4. 支付成功后,客户端从本地拿支付凭证、将支付凭证和订单号、用户id等参数传给服务端;服务端拿支付凭证向苹果发起验证订单是否有效,然后将结果反馈给客户端。 5. 客户端刷新用户个人信息。 ## 代码 ~~~ Copy Highlighter-hljs/** * 服务器二次验证代码 * @param $receipt * @param bool $isSandbox * @return array|void * @throws \Exception */ function getReceiptData($receipt, $isSandbox = false) { if ($isSandbox) { $endpoint = 'https://sandbox.itunes.apple.com/verifyReceipt'; } else { $endpoint = 'https://buy.itunes.apple.com/verifyReceipt'; } //$receipt = str_replace(' ',"+", $receipt); $postData = json_encode(["receipt-data" => $receipt]); //$postData = '{"receipt-data":"'. $receipt .'"}'; BLog::pay("苹果支付:postData:$postData"); $ch = curl_init($endpoint); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); curl_setopt($ch, CURLOPT_POST, true); curl_setopt($ch, CURLOPT_POSTFIELDS, $postData); curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0); //这两行一定要加,不加会报SSL 错误 curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 0); $response = curl_exec($ch); $errno = curl_errno($ch); $errmsg = curl_error($ch); curl_close($ch); //判断时候出错,抛出异常 if ($errno != 0) { return [ 'result' => false, 'txtMessage' => $errmsg, ]; } $data = json_decode($response); BLog::pay("苹果支付:response:$response"); //此处是看到先人们的指导,又看到apple的官方说法改的。否则会审核不过貌似是审核也会走沙盒测试者, //此处先判断一次返回的status是否=21007 这数据是从测试环境,但它发送到生产环境中进行验证。它发送到测试环境来代替。 if ($data->status == 21007) { $this->getReceiptData($receipt, true); return; } //判断返回的数据是否是对象 if (!is_object($data)) { throw new \Exception('Invalid response data'); } //判断购买时候成功 if (!isset($data->status) || $data->status != 0) { throw new \Exception('Invalid receipt'); } //返回产品的信息 return (array)$data->receipt; } /** * ios 支付验证 * @param $allData * @return array|string */ public function iosIAPPay($allData) { $receiptData = $allData['receiptData']; //获取 App 发送过来的数据,设置时候是沙盒状态 $receipt = $receiptData; $isSandbox = true; if ('server' == APP_ENV) { $isSandbox = false; } //开始执行验证 try { $info = $this->getReceiptData($receipt, $isSandbox); $package_name = $info['bid'] ?? ""; if (!$package_name) { $package_name = $info['bundle_id'] ?? ""; } if (!$package_name) { return [ 'result' => false, 'txtMessage' => 'bid或bundle_id缺失', ]; } $product_id = $info['product_id'] ?? ""; if (!$product_id) { $product_id = $info['in_app'][0]->product_id ?? ""; } if (!$product_id) { return [ 'result' => false, 'txtMessage' => 'product_id缺失', ]; } $transaction_id = $info['transaction_id'] ?? ""; if (!$transaction_id) { $transaction_id = $info['in_app'][0]->transaction_id ?? ""; } if (!$transaction_id) { return [ 'result' => false, 'txtMessage' => 'transaction_id缺失', ]; } $productInfo = DB::table('ios_payment_config') ->where('package_name', $package_name) ->where('product_id', $product_id) ->first(); if (!$productInfo) { BLog::pay("苹果支付:无该产品:" . json_encode($info)); return [ 'result' => false, 'txtMessage' => '无该产品', ]; } $user_open_id = $info['download_id'] ?? ""; $res = $this->doPay($allData['userId'], $allData['orderId'], $transaction_id, $user_open_id); BLog::pay("苹果支付:支付结果:" . json_encode($info) . json_encode($res)); return $res; } catch (\Exception $e) { return [ 'result' => false, 'txtMessage' => $e->getMessage(), ]; } } ~~~ ## 常见错误码[#](https://www.cnblogs.com/HappyTeemo/p/17148081.html#1809386724) ~~~ Copy Highlighter-hljs/** * 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环境的验证服务 */ ~~~ ## 遇到的坑[#](https://www.cnblogs.com/HappyTeemo/p/17148081.html#2188813096) ### 回包结构的修改[#](https://www.cnblogs.com/HappyTeemo/p/17148081.html#1732782073) [https://www.cnblogs.com/HappyTeemo/p/17221209.html](https://www.cnblogs.com/HappyTeemo/p/17221209.html) 新结构: ~~~ Copy Highlighter-hljs { "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-************Etc/GMT", "receipt_creation_date_ms": "1643************000", "receipt_creation_date_pst": "2022-0a************/Los_Angeles", "request_date": "2022-02-2************Etc/GMT", "request_date_ms": "164************7", "request_date_pst": "2022-************ngeles", "original_purchase_date": "201************tc/GMT", "original_purchase_date_ms": "13************00", "original_purchase_date_pst": "2013************geles", "original_application_version": "1.0", "in_app": [ //变成了数组 { "quantity": "1", "product_id": "10000",#产品ID "transaction_id": "", ##交易单号 "original_transaction_id": "", "purchase_date": "2022************Etc/GMT", "purchase_date_ms": "164************0", "purchase_date_pst": "2022-************les", "original_purchase_date": "2022************GMT", "original_purchase_date_ms": "164************0", "original_purchase_date_pst": "202************les", "is_trial_period": "false", "in_app_ownership_type": "PURCHASED"#交易状态 }, { "quantity": "1", "product_id": "10000",#产品ID "transaction_id": "", ##交易单号 "original_transaction_id": "", "purchase_date": "2022************Etc/GMT", "purchase_date_ms": "164************0", "purchase_date_pst": "2022-************les", "original_purchase_date": "2022************GMT", "original_purchase_date_ms": "164************0", "original_purchase_date_pst": "202************les", "is_trial_period": "false", "in_app_ownership_type": "PURCHASED"#交易状态 }, ] }, "environment": "Sandbox", //环境 "status": 0 } ~~~ 老版本是一个一维数组,没有in\_app ### post请求的坑,报21003[#](https://www.cnblogs.com/HappyTeemo/p/17148081.html#4042212618) * 第一种,有空格,使用字符串替换 ~~~ Copy Highlighter-hljs$receipt = str_replace(' ',"+", $receipt); ~~~ * 第二种,json不对,直接拼字符串,不要json\_encode ~~~ Copy Highlighter-hljs$postData = '{"receipt-data":"'. $receipt .'"}'; ~~~ * 第三种,苹果那边的问题,什么都没改,第一天好的,第二天报错(沙盒报错,线上好的),第三天好了。