PHP服务端 苹果支付(IAP)处理

news/2025/3/15 16:25:55/

 公司做的app需要做IAP订阅支付,开始觉得和微信的支付流程差不多,做起来还是有点麻烦,主要是网上的文章很少,不能拿来主义。自己做完总结一下,希望对小伙伴们有帮助我就很欣慰了。代码写的不好 不要喷我。。。

首先讲一下我的业务逻辑,也就是php服务端需要做什么事情。

先上图:

                                              

下面我详细的讲一下每一步做些什么,贴出来相应的代码供大家参考。

 第一步: app 调用创建订单接口,创建订单信息,保存下来。

 第二步: app 调用sdk 发起支付,传苹果给的 receipt(票据)和 订单号 给服务端,服务端通过验证receipt 获取订单信息保存

                数据库。至于票据长什么样子,怎么验证,后面贴代码出来。

 第三步: 订阅模式支付,首次支付苹果服务器会异步发送两次通知到服务端,之后你在app端操作的时候也会有通知,比如更                     改套餐,取消订阅等操作都会有通知,这个就比较坑了,如果异步处理的时候没弄清楚。

                很容易出问题,不能每次接收到通知就处理,沙盒模式下通知来的很奇怪,通知有很多状态,要区别开来处理。

  接下来是代码处理流程:php框架+tp 5.02

  首先是同步完成订单时候的验证:

  

/*** @title 验证支付票据 完成订单接口*/
public function verifyReceipt()
{$receipt = Request::instance()->param('receipt');  //票据$orderSn = Request::instance()->param('orderSn');  /订单号//判断订单是否存在 检查状态//写入日志  查看票据格式  记录日志的方法 这个方法不贴出来了 Tool::callAddLog('request-param',json_encode(['receipt'=>$receipt,'orderSn'=>$orderSn]));//password 是验证秘钥$jsonItem = json_encode(['receipt-data'=>$receipt, 'password'=>'XXXXXXXXXXXXXXXX']);//$url= 'https://buy.itunes.apple.com/verifyReceipt';      //正式$url= 'https://sandbox.itunes.apple.com/verifyReceipt';  //测试//模拟post提交   下面会贴出来 $result =  Tool::http_post_json($jsonItem,$url);if($result['status'] !== 0){//验证失败 返回app错误状态}//验证完成加入日志Tool::callAddLog('verifyReceipt_finish',json_encode($result));//找出时间最大的数组$receiptitem = $result['latest_receipt_info'];//这个是排序的方法  下面回帖出来 苹果会把这个会员往期的订单信息全部返回,需要找出来最近的那一组信息$item = Tool::arraySort($receiptitem,'purchase_date','desc');//判断一下过期时间 延长会员时间$orderThird = $item['transaction_id'];                //本次订阅的订单号$orderThirdFirst = $item['original_transaction_id'];  //这个是原始订单号if($orderThird == $orderThirdFirst){                  //首次订阅 两个相等}else{//判断过期时间和当前时间比较   expires_date_ms是毫秒单位if($item['expires_date_ms']/1000 > time()){}else{//过期处理  票据失效  }}//接下来处理订单业务逻辑,处理订单,修改订单信息,original_transaction_id 把这个单号和存入订单信息, 在异步回调的时候根据这个单号来查找订单信息来判断是哪个用户,苹果票据里面也有产品product_id,是app_store的产品id,不是自己业务的产品id,可以在后台把自己的产品ID和 product_id 关联,处理业务逻辑。}
//模拟post提交
public static function http_post_json($json,$url) {$ch = curl_init($url);curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);curl_setopt($ch, CURLOPT_POST, true);curl_setopt($ch, CURLOPT_POSTFIELDS, $json);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);$data = json_decode($response, true);return $data;
}
//查找最新数据的方法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]; // 数据返回
}
public static function http_post_json($json,$url) {$ch = curl_init($url);curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);curl_setopt($ch, CURLOPT_POST, true);curl_setopt($ch, CURLOPT_POSTFIELDS, $json);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);$data = json_decode($response, true);return $data;
}

------------------------------------------------------------------------------------------------------------------------------------------------------------------------

验证之后返回的状态

验证之前的票据是一个字符串:就是一个字符串

验证之后的信息:这个是重点

到了 这同步主动验证这边就结束了。

 

下面是异步通知这块:

异步通知的票据可以验证发送到苹果服务器验证一下有效性,也可以不要验证,它本身的信息就够用了。

 

  通知类型: notification_type 有很多种情况,有些通知接收存入日志,不用处理,

  需要处理的类型:RENEWAL           DID_CHANGE_RENEWAL_STATUS            

  但是 DID_CHANGE_RENEWAL_STATUS 这种通知 在首次订阅的时候 也会发一个过来,这个时候不处理,也就是当参数       auto_renew_status  等于 true 的时候 不处理,其余的时候都是需要处理的通知。

 

 

 

public function applePayReceive(){$str = file_get_contents('php://input');//写入通知日志$jsonItem = json_decode($str,true);if($jsonItem['notification_type'] == 'RENEWAL' || $jsonItem['notification_type'] == 'DID_CHANGE_RENEWAL_STATUS'){if($jsonItem['notification_type'] == 'DID_CHANGE_RENEWAL_STATUS'){if($jsonItem['auto_renew_status'] == 'true'){//第一次够买的时候 不处理通知die;}$expires_date = $jsonItem['latest_receipt_info']['expires_date'];$expires_date = $expires_date/1000;Tool::asynAddLog('request-param',$expires_date.'----'.time());if($expires_date > time()){}else{//时间不对 不处理}}//处理订阅产品的业务逻辑  echo '完成!';die;
}

好了,上面是我写的苹果支付所有流程和代码,业务代码部分我去掉了,大家自己写自己的业务逻辑就可以。

看到最后的同志们,我上面有个地方写错了 ,苹果正式环境,第一次购买的时候,异步的两个通知,notification_type 等于DID_CHANGE_RENEWAL_STATUS 这个的时候,auto_renew_status 值有可能不是ture,我开始没弄懂意思,现在我的理解是

等于true是自动续费的意思,就是说用户选择的是连续订阅,而等于false的时候是用户买了产品取消了订阅模式。才会出现这种情况。不是苹果坑,而是自己没理解,

 

其实根本不能用通知的类型来判断是否给用户重置,正确保险的方案是在主动通知的时候把苹果的原始订单号和本次交易的订单号存入订单,异步的时候判断订单是否完成。然后再写业务逻辑。

 

重点:异步通知有可能收不到,这种情况下,需要把用户首次订阅的凭证存下来,等到用户到期的时候 再去验证一下这个凭证,然后取出订阅相关信息,找到最近的一条,更新会员时间,会员时间用苹果提供的过期时间就可以。

沙盒模式下苹果的通知是乱的,请大家注意,不一定按照文档上面说的时间有规则的发送通知,所以异步通知不能保证业务的完整性,处理业务时一定要把用户首次支付的凭证持久化,以便后续通知收不到的情况,会员到期服务端主动去查询会员是否续费 情况。

 

 

 


http://www.ppmy.cn/news/510479.html

相关文章

一个画布有多个子图_如何把多张图拼成一张长图

如何把多张图拼成一张长图 相信很多小伙伴都有在使用苹果iPhone手机,在其中如何才能通过微信拼接多张截图呢?方法很简单,下面小编就来为大家介绍。 具体如下: 1. 首先,打开手机上的“微信”。进入首页后,点…

苹果计算机 win10,苹果怎么装win10苹果装win10详细教程【图文】

现如今越来越多的人在选择电子产品时都特别青睐苹果的产品,像苹果的手机,平板电脑以及笔记本电脑等,都是人们争相购买的产品。与同价位的产品相比较,苹果的产品在使用上更加方便,且安全性更出色。但是由于与人们常用的系统有所不同,在使用上一些人还是有些不习惯,尤其是…

计算机里的MAC,怎么在mac苹果电脑中查看电脑开机运行的时长

怎么在mac苹果电脑中查看电脑开机运行的时长 腾讯视频/爱奇艺/优酷/外卖 充值4折起 今天给大家介绍一下怎么在mac苹果电脑中查看电脑开机运行的时长的具体操作步骤。 1. 打开电脑,进入桌面,点击左上角的苹果图标。 2. 在打开的菜单中,选择‘关于本机’选项,点击。 3. 进入新…

第七章 MobileNetv2网络详解

系列文章目录 第一章 AlexNet网络详解 第二章 VGG网络详解 第三章 GoogLeNet网络详解 第四章 ResNet网络详解 第五章 ResNeXt网络详解 第六章 MobileNetv1网络详解 第七章 MobileNetv2网络详解 第八章 MobileNetv3网络详解 第九章 ShuffleNetv1网络详解 第十章…

查看局域网内的所有IP地址

打开电脑的cmd命令行界面,依次点击【开始】【运行】,输入【cmd】。 在命令行窗口输入【for /L %i IN (1,1,254) DO ping -w 1 -n 1 192.168.0.%i】。 执行完毕之后,输入【arp -a】,就可以看到局域网内在线的IP了。

查看局域网内的所有设备的IP地址

执行如下命令即可 1. for /L %i IN (1,1,254) DO ping -w 2 -n 1 192.168.0.%i2.等扫描完之后,输入arp -a

查看网段内所有ip设备

1、使用脚本查找指定网段设备 #!/bin/bash #multi process ping ip net172.17.164. #网段172.17.164 for i in {1..255} do {ip$net$iping $ip -c 3 &> /dev/null #使用ping命令#arping $ip -c 2 &> /dev/null #使用arping命令if [ $? 0 ];thenecho $ip is …

如何找到局域网内所有主机ip

在cmd中输入【for /L %i IN (1,1,254) DO ping -w 1 -n 1 192.168.60.%i】可以扫描局域网内的ip地址(原理就是批处理循环,254是针对子网掩码为/24(255.255.255.0)的,主机名有254个,然后192.168.60.0就是局域网网关) 执…