PHP接入苹果支付
Ios苹果支付流程:
- 客户端先从苹果获取内购Id。
- 客户端将内购id,金额、用户id等传给服务端获取一个自己服务端生成的订单号。
- 客户端向苹果发起支付。
- 支付成功后,客户端从本地拿支付凭证、将支付凭证和订单号、用户id等参数传给服务端;服务端拿支付凭证向苹果发起验证订单是否有效,然后将结果反馈给客户端。
- 客户端刷新用户个人信息。
代码
/**
* 服务器二次验证代码
* @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(),
];
}
}
常见错误码
/**
* 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/17221209.html
新结构:
{
"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
- 第一种,有空格,使用字符串替换
$receipt = str_replace(' ',"+", $receipt);
- 第二种,json不对,直接拼字符串,不要json_encode
$postData = '{"receipt-data":"'. $receipt .'"}';
- 第三种,苹果那边的问题,什么都没改,第一天好的,第二天报错(沙盒报错,线上好的),第三天好了。
PHP接入苹果支付的更多相关文章
- 在Web应用中接入微信支付的流程之极简清晰版
在Web应用中接入微信支付的流程之极简清晰版 背景: 在Web应用中接入微信支付,我以为只是调用几个API稍作调试即可. 没想到微信的API和官方文档里隐坑无数,致我抱着怀疑人生的心情悲愤踩遍了丫们布 ...
- 在Web应用中接入微信支付的流程之极简清晰版 (转)
在Web应用中接入微信支付的流程之极简清晰版 背景: 在Web应用中接入微信支付,我以为只是调用几个API稍作调试即可. 没想到微信的API和官方文档里隐坑无数,致我抱着怀疑人生的心情悲愤踩遍了丫们布 ...
- TP3.2.3 接入银联支付
TP3.2.3 接入银联支付 项目接入银联支付的过程, 在此记录下,希望能帮助开发盆友平坑. 银联SKD链接:https://open.unionpay.com/ajweb/product/newPr ...
- Idea-Java接入银联支付的Demo
注:本文来源于:< Idea-Java接入银联支付的Demo > 接入银联支付的Demo,希望能给大家节约一点时间 https://github.com/wangfei0904306/un ...
- Android 接入支付宝支付实现
接上篇android接入微信支付文章,这篇我们带你来接入支付宝支付服务 简介 首先要说明的是个人感觉接入支付宝比微信简单多了,很轻松的,所以同学们不要紧张~ 当然还是老规矩啦,上来肯定的贴上官网地址, ...
- Android接入支付宝支付实现
接上篇android接入微信支付文章,这篇我们带你来接入支付宝支付服务 简介 首先要说明的是个人感觉接入支付宝比微信简单多了,很轻松的,所以同学们不要紧张~ 当然还是老规矩啦,上来肯定的贴上官网地址, ...
- 微信App支付接入步骤&支付中前后端交互流程
最近对微信App支付(App端集成微信支付SDK)申请步骤,以及终端在进行微信支付时商户App.商户Server.微信App.微信支付Server的交互流程进行了简单了解.这篇文章应该算是学习笔记,分 ...
- Unity接入微信支付SDK 2022年版安卓篇
最近1年转了UE开发,博客更新的比较少,技术栈宽了不少,以后有空尽量多更新,也方便总结记忆 Unity接入微信支付整个过程坑比较多,网上之前的教程要么比较老,要么比较零碎,只能东拼西凑摸索,跑通后还是 ...
- thinkphp5.0 微信公众号接入支付宝支付
---恢复内容开始--- 真是无力吐槽这个需求了,想骂客户,好端端的非要在微信公众号接入支付宝,都知道微信公众号是拒绝支付宝的,屏蔽了支付宝,所以在微信公众号接入支付宝的话就必须手动复制链接跳出微信内 ...
- php 微信公众号接入支付宝支付
真是无力吐槽这个需求了,好端端的非要在微信公众号接入支付宝,都知道微信公众号是拒绝支付宝的,屏蔽了支付宝,所以在微信公众号接入支付宝的话就必须手动复制链接跳出微信内置浏览器,强制性打开web浏览器完成 ...
随机推荐
- centos 7 开启 httpd 服务和 80 端口
centos 7 开启 httpd 服务和 80 端口 yum install -y httpd systemctl start httpd firewall-cmd --add-service=ht ...
- postgresql性能优化1:min和max的性能
select max(datatime) as id from mytable ---全表检索,时间慢执行时间5分钟 select max(datatime) as id from mytable w ...
- 详解GaussDB(DWS)中的行执行引擎
本文分享自华为云社区<GaussDB(DWS)行执行引擎详解>,作者:yd_227398895. 1.前言 GaussDB(DWS)包含三大引擎,一是SQL执行引擎,用来解析用户输入的SQ ...
- JDK源码阅读-------自学笔记(九)(常用类型Integer初探)
常用类 主要分为几部分需要学习: 包装类的介绍和使用 字符串的介绍和使用 时间类的介绍和使用 其他类型介绍和使用 包装类(Wrapper Class)基本知识: 1.基本数据类型不是对象,但有时需要将 ...
- 经验分享:春招零Offer,5月份还有机会吗?
先说答案:5 月份依然有拿到 Offer 的机会. 5月份春招结束了吗? 对于应届大学生来说(也就是今年暑假毕业的学生),5 月中旬春招就陆续结束了,但是 5 月份会有很多补录的机会. 对于非应届的大 ...
- PPO近端策略优化玩cartpole游戏
这个难度有些大,有两个policy,一个负责更新策略,另一个负责提供数据,实际这两个policy是一个东西,用policy1跑出一组数据给新的policy2训练,然后policy2跑数据给新的poli ...
- Pageoffice6 实现后台批量转PDF文档
在实际项目开发中如果遇到批量动态生成PDF文档的需求,只需参考后台批量生成PDF文档,目前网上也有一些针对此需求的方案,如果您想要了解这些方案的对比,请查看后台生成单个Word文档中的"方案 ...
- linux下vim的使用以及高效率的技巧
目录 一.关于vim编辑器 二.vim编辑器的模式 三.一般模式下的基础操作 四.V模式(列模式)的基础操作 五.命令模式下的基础操作 六.自定义vim环境 七.vim同时打开多个文件 八.比较两个文 ...
- 微信小程序预约视频号直播
一.概述 微信小程序提供了可以唤起预约视频号直播弹窗的接口 reserveChannelsLive,该接口接收一个参数 noticeId,通过 getChannelsLiveNoticeInfo 接口 ...
- 智影AI故事转视频创作神器!快速开启AI绘画小说推文之旅
1.前言 1.1 生成内容形式 生成内容形式主要包含三种,PGC(Professionally Generated Content).UGC(User Generated Content).AIGC( ...