行业知识
PHP服务端 苹果内购支付(IAP),免费试用连续订阅等功能讲解
2026-11-11 02:13:26
来源:互联网转载
公司最近项目app需要做苹果订阅支付,开始觉得和微信的支付流程差不多,做起来还是有点麻烦,主要是网上的 文章很少,不能直接硬抄。自己做完总结一下,希望对小伙伴们有帮助。首先讲一下我的业务逻辑,也就是php服务端需要做什么事情。先上图:
下面我详细的讲一下我的做法,并贴出来相应的代码供大家参考。其实支付是有两种(我理解的分两种) 1》第一种是主动直接购买,app创建订单,我们将订单信息保存,然后app调用SDK发起支付,这个时候会走苹果自 己的支付,不管支付结果咋样,客户端会获取到一个苹果给到的receipt(俗称票据),此时客户端将票据和我们 自己创建的订单号传给服务端进行验证,验证完成之后再进行相应的发货逻辑处理票据其实就是一串很长的字符串,我们拿它到苹果服务器验证完成之后,会解析成一堆明文数据,我们在进行相应的处理
下面我贴一下代码:/** * @title 验证支付票据 完成订单接口 */public function ios_pay() { $receipt_data = $_REQUEST['ios_billon'] ?? ''; $order_id = $_REQUEST['order_id'] ?? ''; $result = array('status' => false, 'message' => '非法参数'); if (empty($receipt_data) || empty($order_id)) { return_json($result); } // 请求验证 $appkey = "这里是你自己的苹果支付参数"; $html = $this->acurl($receipt_data, $appkey);#这里封装方法去请求苹果验证票据 $data = json_decode($html, true); // 如果是沙盒数据 则验证沙盒模式 if ($data['status'] == '21007') { // 请求验证 $html = $this->acurl($receipt_data, $appkey, 1); $data = json_decode($html, true); $data['sandbox'] = '1'; } $str = "**************下面是票证信息".date("Y-m-d H:i:s")."*****************". "\n\r"; file_put_contents("a.txt", $str.json_encode($data) . "\n\r", FILE_APPEND); // 判断是否购买成功 if (intval($data['status']) === 0) { if ($data['receipt']['bundle_id'] != IOS_BUNDLE_ID || empty($data ['receipt']['in_app'])) { return_json($result); } // 找出时间最大的凭据数组 $ks['status'] = $data['status']; $ks['receipt'] = $data['receipt']; unset($ks['receipt']['in_app']); $data['receipt']['in_app'][0] = $data['receipt']['latest_receipt_info'][0] ?? $data['receipt']['in_app'][0]; //取in_app信息 $k = array_merge($ks, $data['receipt']['in_app'][0]); $pay_date = date('Y-m-d H:i:s', $k['purchase_date_ms']/1000); // 购买日期 $transaction_id = $k['transaction_id']; ·········· 这里是自己逻辑处理 因为我们还有一个免费试用功能 ·········· if ($k['is_trial_period'] == 'true') { // 享受了免费试用 $update_data['money'] = 0; $update_data['ios_status'] = 1;//1代表免费试用中 $is_try = 1;//1代表免费试用中 } ······· 处理完成 ······· } else { $result = array('status' => false, 'message' => $this->err_msg[$data['status']] ?? $data['status']); } return_json($result); } public function acurl($receipt_data, $appkey = "", $sandbox = 0) { //小票信息 $POSTFIELDS = array("receipt-data" => $receipt_data); if (!empty($appkey)) { $POSTFIELDS = array("receipt-data" => $receipt_data, 'password' => $appkey); } $POSTFIELDS = json_encode($POSTFIELDS); //正式购买地址 沙盒购买地址 $url_buy = "https://buy.itunes.apple.com/verifyReceipt"; $url_sandbox = "https://sandbox.itunes.apple.com/verifyReceipt"; $url = $sandbox ? $url_sandbox : $url_buy; //简单的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; } 验证之前的票据
验证之后的信息
验证完返回的状态,处理订单业务逻辑,处理订单,修改订单信息,original_transaction_id 把这个 单号和存入订单信息,在异步回调的时候根据这个单号来查找订单信息来判断是哪个用户,苹果票据里面 也有产品product_id,是 app_store的产品id,不是自己业务的产品id,可以在后台把自己的产品ID和 product_id 关联,处理业务逻辑。这样主动购买逻辑就完成了。
接下来是第二种:2》通过订阅的方式购买,因为苹果订阅不一样,例如你购买一个会员之后,会在你的订阅中心有一条订阅记录,如果你不取消,到期之后,他会自动扣款进行续费,你也可以提前进行取消订阅,到期之后,就不会再进行一个扣款续费了。这里我建议这里与上面的接口分开,因为这个接口需要配置在苹果开发者后台的,等待他那边进行回调。了解逻辑之后,我们再来看代码,
通知类型: notification_type 有很多种情况,有些通知接收存入日志,不用处理,我们要根据自己的需求进行相关业务处理,我自己只处理了RENEWAL,DID_RENEW,INTERACTIVE_RENEWAL这些状态
public function ios_continu_pay() { $input_data = trim(file_get_contents("php://input")); $str = "**************下面是IOS最新信息".date("Y-m-d H:i:s")."**************** *". "\n\r"; file_put_contents("b.txt", $str . $input_data . "\n\r", FILE_APPEND); $resp_str = json_decode($input_data, true); if (!empty($resp_str)) { $data = $resp_str['unified_receipt']; //有时候苹果那边会传空数据调用 // notification_type 几种状态描述 // INITIAL_BUY 初次购买订阅。latest_receipt通过在App Store中验证,可以随时将您的服务器存储在服务器上以验证用户的订阅状态。 // CANCEL Apple客户支持取消了订阅。检查Cancellation Date以了解订阅取消的日期和时间。 // RENEWAL 已过期订阅的自动续订成功。检查Subscription Expiration Date以确定下一个续订日期和时间。 // INTERACTIVE_RENEWAL 客户通过使用用App Store中的App Store以交互方式续订订阅。服务立即可用。 // DID_CHANGE_RENEWAL_PREF 客户更改了在下次续订时生效的计划。当前的有效计划不受影响。 $notification_type = $resp_str['notification_type'];//通知类型 $password = $resp_str['password']; // 共享秘钥 if ($password == "你的苹果支付秘钥") { //latest_expired_receipt_info 好像只有更改续订状态才有 $receipt = $data['latest_receipt_info'] ?? $data['latest_expired_receipt_info']; //找出来最近的那一组信息 $receipt = self::arraySort($receipt, 'purchase_date', 'desc'); $original_transaction_id = $receipt['original_transaction_id']; // //原始交易ID $transaction_id = $receipt['transaction_id']; // //交易的标识 $purchaseDate = str_replace(' America/Los_Angeles', '', $receipt['purchase_date_pst']); ········· 进行你的业务处理逻辑 ········· } else { NewLog::log('通知传递的密码不正确--password:' . $password, 'ios_pay_continu_password'); } } } //查找最新数据的方法 public static function arraySort($arr,$key,$type='asc'){ $keyArr = []; // 初始化存放数组将要排序的字段值 foreach ($arr as $k=>$v){ $keyArr[$k] = $v[$key]; // 循环获取到将要排序的字段值 } if($type == 'asc'){ asort($keyArr); // 排序方式,将一维数组进行相应排序 }else{ arsort($keyArr); } foreach ($keyArr as $k=>$v){ $newArray[$k] = $arr[$k]; // 循环将配置的值放入响应的下标下 } $newArray = array_merge($newArray); // 重置下标 return $newArray[0]; // 数据返回} 好了,上面是我写的苹果支付所有流程和代码,业务代码部分我去掉了,大家自己写自己的业务逻辑就可以。大家在对接过程,可能会遇到与我不一样的一些地方,因为我们做了连续订阅,免费试用等等相关功能,所以只要思路一样就是可以了,希望这篇文章对你有所帮助。