微信的 h5 支付和 jsapi 支付
- 2023-08-10 江苏
本文字数:14752 字
阅读完需:约 48 分钟

🎈 申请商户号
如果你还没有微信商户号,请点击上面的链接进行申请,如果已经有了,可以跳过这一步
🎈 申请商户证书
首先点击
账户中心▶API安全▶申请API证书
🎈 设置 APIv3 密钥
首先点击
账户中心▶API安全▶设置APIv3密钥▶设置会看到有两个密钥,分别是
APIv2密钥和APIv3密钥,由于APIv2密钥已经逐渐废弃了,所以只需要申请APIv3密钥即可密钥可由数字大小写字母组合,输入任意的
32位字符,该密钥需要保存好,供后面使用
<?php// 生成32位的APIv3随机密钥$chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
echo substr(str_shuffle($chars), 0, $length);🎈 下载 SDK 开发包
微信官方提供了
JAVA、PHP、GO三种语言版本的开发库,请根据自己开发语言选择GO 语言: wechatpay-go ‹推荐›
由于
php实现支付相对简单,所以我将以php作为支付的讲解首先使用
composer安装sdk
# 初始化文件夹composer init
# 推荐使用 PHP 包管理工具 Composer 安装 SDKcomposer require wechatpay/wechatpay🎈 下载平台证书
平台证书跟上面申请的商户证书不是同一个东西,在后期请求中,平台证书和商户证书都要带上
上面命令执行完之后,会有一个
vendor/bin/CertificateDownloader.php文件如果你是第一次申请平台证书,需要执行命令:
php CertificateDownloader.php -k ${apiV3key} -m ${mchId} -f ${mchPrivateKeyFilePath} -s ${mchSerialNo} -o ${outputFilePath}-k:
apiv3秘钥,上面自己设置的 32 位数的密钥-m: 商户号,微信商户平台可以查询
-f: 微信商户 API 私钥文件目录,也就是第二步申请商户证书里面生成的
apiclient_key.pem路径-s: 证书序列号,在
账户中心▶API安全▶管理证书中可以看见,如果有多个证书,找到自己正在使用的证书序列号-o: 生成后的证书保存地址
cd vendor/bin/
php CertificateDownloader.php -k 241xxxxxxxxxxxxxxxxx44 -m 1xxxxxxx1 -f ../../cert/merchant/apiclient_key.pem -s Wxxxxxxxxxxxxxxxx4 -o ../../cert/wechatpay/
🎈 关联 AppID 账号
因为使用的是微信支付,所以用户支付后,需要通过微信号通知用户支付的一些信息,所以需要在商户号下至少关联一个公众号
🎈 开通 H5 支付
点击
产品中心▶我的产品▶H5支付▶点击开通开通后,选择
开发配置▶H5支付域名申请添加H5支付域名申请支付域名需要先做好产品的页面,申请的时候需要有页面的截图,截图中还要
截取到域名,支付的审核算是很严格的,如果申请不过,驳回后再申请,审核通过的时间会越来越长,所以最好一次性就把材料收集好,另外还要域名的备案的IPC截图IPC备案查询地址: https://beian.miit.gov.cn/关于域名的填写,如果只填写域名不填写具体域名路径,微信在支付的时候就只会校验域名,这也是最方便的,因为域名下有多个项目有支付功能的话,就不需要重复添加了
🎈 H5 支付流程
H5支付是在微信以外的浏览器使用的,如果是微信内的话,使用的是jsapi支付所以一般用户进入页面的第一件事,就是检测用户使用的环境是微信浏览器还是其他浏览器
前端传一些用户挑选商品后的参数,并请求后端处理接口,后端应该将一些参数进行入库,顺便请求
H5支付接口接口应该返回跳转链接
h5_url,如果你想用户付款之后到结果页面,需要添加redirect_url参数,这个参数一定要用encodeURIComponent进行处理由于官方在
jssapi支付中说明,不要相信前端的success结果,所以需要在结果页中,让用户自动触发查询结果,因此需要返回后端生成的订单号,用作在结果页的用户手动点击查询
// 判断是否微信浏览器function isWeChat() { var ua = navigator.userAgent.toLowerCase(); if (ua.match(/MicroMessenger/i) == 'micromessenger') { return true; } else { return false; }}
if(isWeChat()) { // 是微信中打开的产品页面 alert('微信内不支持h5支付,请在外部浏览器打开页面');} else { // 非微信内打开的产品页面,请求接口,获取支付的跳转链接 // 前端用户选的产品,以及产品的金额,传一些参数过去 let params = { total: 2, // 单位:元 description: 'Image形象店-深圳腾大-QQ公仔' // 产品的介绍 // ....更多入库参数 }; $.getJSON('后端接口地址/h5?' + $.param(params) + '&callback=?', function(res) { // 拉起微信支付界面,成功后会跳转到redirect_url链接 $(location).attr("href", res.data.h5_url + "&redirect_url=" + encodeURIComponent(`https://xxxxxx/finish?out_trade_no=${res.data.out_trade_no}`)) });}<?php// 仅仅用作展示,不可直接复制使用require_once('../vendor/autoload.php');
use WeChatPay\Builder;use WeChatPay\Crypto\Rsa;use WeChatPay\Util\PemUtil;
// 接受参数,相当于原生的$_GET$input = $request->only(['name', 'total', 'description', 'phone']);
// 生成商户订单号$out_trade_no = getOutTradeNo();
// 处理金额// 由于微信使用的是分作为单位,所以前端传的是元的话,需要转换一下$total = $input['total'] * 100;
// 商户号$merchantId = '1xxxxxx1';
// 从本地文件中加载「商户API私钥」,「商户API私钥」会用来生成请求的签名$merchantPrivateKeyFilePath = 'file://../cert/merchant/apiclient_key.pem';$merchantPrivateKeyInstance = Rsa::from($merchantPrivateKeyFilePath, Rsa::KEY_TYPE_PRIVATE);
// 「商户API证书」的「证书序列号」$merchantCertificateSerial = '1xxxxxxxxxxxxxxxxxxxxx91';
// 从本地文件中加载「微信支付平台证书」,用来验证微信支付应答的签名$platformCertificateFilePath = 'file://../cert/wechatpay/wechatpay_4xxxxxxxxxxxxxxxxxxx9.pem';$platformPublicKeyInstance = Rsa::from($platformCertificateFilePath, Rsa::KEY_TYPE_PUBLIC);
// 从「微信支付平台证书」中获取「证书序列号」$platformCertificateSerial = PemUtil::parseCertificateSerialNo($platformCertificateFilePath);
// 构造一个 APIv3 客户端实例$instance = Builder::factory([ 'mchid' => $merchantId, 'serial' => $merchantCertificateSerial, 'privateKey' => $merchantPrivateKeyInstance, 'certs' => [ $platformCertificateSerial => $platformPublicKeyInstance, ],]);
try { $resp = $instance ->chain('v3/pay/transactions/h5') ->post(['json' => [ 'mchid' => $merchantId, // 商户号 'out_trade_no' => $out_trade_no, // 商户订单号 'appid' => '********换成跟商户号绑定的公众号APPID**********', 'description' => $input['description'], //商品描述 'notify_url' => 'https://xxxxx/notify', // 用户支付后的回调地址,在这里修改订单的状态 'amount' => [ 'total' => $total, // 微信处理的单位是分 'currency' => 'CNY' ], 'scene_info' => [ 'payer_client_ip' => getClientIP(), // 有些框架有自带获取获取客户端IP 'h5_info' => [ 'type' => 'Wap' ] ] ]]); // 如果请求成功,需要将一些参数进行入库,这里仅作演示,非正式数据入库 $response = Db::table('order')->insert([ 'name' => $input['name'], 'description' => $input['description'], 'total' => $input['total'], 'phone' => $input['phone'], 'trade_state' => 'START', ]); // 入库成功后,将跳转链接和订单号传给前端,前端拿到跳转地址跳转即可 if($response) { return jsonp([ 'code' => 200, 'msg' => '操作成功', 'data' => [ 'out_trade_no' => $out_trade_no, 'h5_url' => json_decode($resp->getBody(), true)['h5_url'] ] ]); } else { return jsonp([ 'code' => 100, 'msg' => '操作失败' ]); }} catch (\Exception $e) { // 进行错误处理 if ($e instanceof \GuzzleHttp\Exception\RequestException && $e->hasResponse()) { $r = $e->getResponse(); echo $r->getBody(), PHP_EOL, PHP_EOL, PHP_EOL; }}
// 生成唯一商户订单号,订单号不能超过32位,并且在同一个商户下订单号不能重复// 如果并发不高,基本这样生成就可以,不会有重复的情况出现的function getOutTradeNo(){ $out_trade_no = date('ymdHis') . mt_rand(1000, 9999) . uniqid(); return mb_substr($out_trade_no, 0, 32);}
// 获取客户端的IPfunction getClientIP(){ if (@$_SERVER["HTTP_ALI_CDN_REAL_IP"]) { $ip = $_SERVER["HTTP_ALI_CDN_REAL_IP"]; } elseif (@$_SERVER["HTTP_X_FORWARDED_FOR"] ?: false) { $ips = explode(',', $_SERVER["HTTP_X_FORWARDED_FOR"]); $ip = $ips[0]; } elseif (@$_SERVER["HTTP_CDN_SRC_IP"] ?: false) { $ip = $_SERVER["HTTP_CDN_SRC_IP"]; } elseif (getenv('HTTP_CLIENT_IP')) { $ip = getenv('HTTP_CLIENT_IP'); } elseif (getenv('HTTP_X_FORWARDED')) { $ip = getenv('HTTP_X_FORWARDED'); } elseif (getenv('HTTP_FORWARDED_FOR')) { $ip = getenv('HTTP_FORWARDED_FOR'); } elseif (getenv('HTTP_FORWARDED')) { $ip = getenv('HTTP_FORWARDED'); } else { $ip = $_SERVER['REMOTE_ADDR']; }
$ip = str_replace(['::ffff:', '[', ']'], ['', '', ''], $ip); return $ip;}<?php// 回调处理,当用户支付订单后,微信会请求该接口,也就是上面在notify_url中填写的接口// 在这里我们可以修改订单的状态啥的public function notify(){ // 获取参数 $inBody = file_get_contents('php://input');
// APIv3密钥 $apiv3Key = 'xxxxxxxxxxxx';
// 转换通知的JSON文本消息为PHP Array数组 $inBodyArray = (array)json_decode($inBody, true); // 加密文本消息解密 $inBodyResource = AesGcm::decrypt( $inBodyArray['resource']['ciphertext'], $apiv3Key, $inBodyArray['resource']['nonce'], $inBodyArray['resource']['associated_data'] );
// 把解密后的文本转换为PHP Array数组 $inBodyResourceArray = (array)json_decode($inBodyResource, true);
try { // 获取订单信息 $order = Db::table('order')->where('out_trade_no', $inBodyResourceArray['out_trade_no'])->first();
Db::startTrans(); if ($order) { // 修改order订单的状态 Db::table('order')->where('id', $order['id'])->update([ 'openid' => $inBodyResourceArray['payer']['openid'], 'trade_state' => $inBodyResourceArray['trade_state'] ]); Db::table('payment')->insert([ 'out_trade_no' => $inBodyResourceArray['out_trade_no'], 'transaction_id' => $inBodyResourceArray['transaction_id'], 'trade_type' => $inBodyResourceArray['trade_type'], 'trade_state' => $inBodyResourceArray['trade_state'], 'trade_state_desc' => $inBodyResourceArray['trade_state_desc'], 'total_amount' => $inBodyResourceArray['amount']['total'], 'bank_type' => $inBodyResourceArray['bank_type'], 'success_time' => strtotime($inBodyResourceArray['success_time']) ]);
Db::commit(); } else { Db::rollback(); } } catch (\Exception $e) { Db::rollback(); }}🎈 开通 JSAPI 支付
点击
产品中心▶我的产品▶JSAPI支付▶点击开通开通后,选择
开发配置▶JSAPI支付域名申请添加JSAPI支付域名关于申请支付域名的流程基本都差不多要求也差不多,看上面的
H5支付域名申请就行,这里就不过多赘述了
🎈 JSAPI 支付流程
JSAPI支付是在微信内的浏览器使用的,如果用户是在微信外打开的话,需要提醒去微信内打开页面JSAPI支付需要使用微信内置的WeixinJSBridge.invoke方法由于
JSAPI调用支付需要用到用户的openid,所以需要想方设法在用户调用JSAPI之前获取到openid,点击查看获取 openid 的官方文档获取用户
openid,需要先获取code,这个经常做微信业务的人都知道,那么如何在用户无感知的情况下就获取到openid呢思路就是,一般支付最少会有 3 个页面,这里标注为
a、b、c三个页面,通常是在a页面挑选商品,在b页面确认商品,也就是付款页面,c页面查询支付状态由于
code的存在时间只有 5 分钟,所以注定code获得后不能长时间不使用,也就是说用户一旦在某个页面超过 5 分钟,这个code就失效了,因此最好的方法就是获取code后,立马获取openid那么就应该设计成从
a页面先跳转到获取code页面再跳转到b页面,而在b页面的一开始就去请求接口,获取用户的openid即可跳转到
b页面后,链接后自动带上code参数,链接应该是https://xxxx/b.html?code=xxxxxxxx
// a页面,仅做逻辑演示,更加具体的逻辑需要自己完善// 判断是否微信浏览器function isWeChat() { var ua = navigator.userAgent.toLowerCase(); if (ua.match(/MicroMessenger/i) == 'micromessenger') { return true; } else { return false; }}
if(!isWeChat()) { // 非微信内打开的产品页面 alert('微信外不支持JSAPI支付,请在微信中打开页面'); return false;}
// 用户挑选完商品后跳转,这里appid需要上面跟商户绑定的公众号appid// 微信授权分为静默授权和非静默授权,其中非静默授权,需要用户点击确认授权后,才可以获取code,// 因为这里主打一个用户无感知,而且我们只需要openid即可,所以我们只需要使用静默授权即可// 静默授权可以获取用户更多的信息,比如头像、昵称等,而静默授权只能获取openid,这点需要注意,具体情况选择不同// 非静默授权// $(location).attr('href', `https://open.weixin.qq.com/connect/oauth2/authorize?appid=xxxxxxxxxxx&redirect_uri=${encodeURIComponent('https://xxxx/b.html')}&response_type=code&scope=snsapi_userinfo#wechat_redirect`)// 静默授权$(location).attr('href', `https://open.weixin.qq.com/connect/oauth2/authorize?appid=xxxxxxxxxxx&redirect_uri=${encodeURIComponent('https://xxxx/b.html')}&response_type=code&scope=snsapi_base#wechat_redirect`)// b页面,仅做逻辑演示,更加具体的逻辑需要自己完善let openid = '';
// 获取code, 请求接口获取openidfunction getParamUrl(name, url) { if (!url) url = location.href; if (url.indexOf('?') == -1) return '';
try { var re = new RegExp("" + name + "=([^&?]*)", "ig"); return ((url.match(re)) ? (decodeURIComponent(url.match(re)[0].substr(name.length + 1))) : ''); } catch (_e) { return ''; }}
let code = getParamUrl('code');
$.getJSON('后端接口地址/openid?callback=?', function(res) { if(res.code == 200) { openid = res.data; } else { console.error(res.msg); }})
// 用户确定订单后,拉起支付let params = { total: 2, // 单位:元 description: 'Image形象店-深圳腾大-QQ公仔', // 产品的介绍 openid: openid //用户的openid // ....更多入库参数};
$.getJSON('后端接口地址/jssapi?' + $.param(params) + '&callback=?', function(res) { WeixinJSBridge.invoke('getBrandWCPayRequest', { 'appId': res.data.sign.appId, 'timeStamp': res.data.sign.timeStamp, 'nonceStr': res.data.sign.nonceStr, 'package': res.data.sign.package, 'signType': res.data.sign.signType, 'paySign': res.data.sign.paySign }, function (response) { if (response.err_msg == "get_brand_wcpay_request:ok") { $(location).attr("href", `https://xxxxxx/finish?out_trade_no=${res.data.out_trade_no}`) } else { // 有些用户调起了支付,但是未付款取消的处理方式,你可以给他简单简单提示 toast('支付异常取消')
// 当然有些用户是误操作,你可以提醒二次支付 if(confirm('检测到你操作有误,是否重新支付?')) { WeixinJSBridge.invoke('getBrandWCPayRequest', { 'appId': res.data.sign.appId, 'timeStamp': res.data.sign.timeStamp, 'nonceStr': res.data.sign.nonceStr, 'package': res.data.sign.package, 'signType': res.data.sign.signType, 'paySign': res.data.sign.paySign }, function (response) { if (response.err_msg == "get_brand_wcpay_request:ok") { $(location).attr("href", `https://xxxxxx/finish?out_trade_no=${res.data.out_trade_no}`) } }) } } });});<?php// 获取用户的openid
$input = $request->only(['code']);
$response = getCurl("https://api.weixin.qq.com/sns/oauth2/access_token?appid={$this->appid}&secret={$this->secret}&code={$input['code']}&grant_type=authorization_code");
$openid = json_decode($response, true)['openid'];
// 返回openidreturn jsonp([ 'code' => 200, 'msg' => '获取成功', 'data' => $openid]);
// 封装的GET请求function getCurl($url, $timeout = 5){ $ch = curl_init(); curl_setopt($ch, CURLOPT_URL, $url); curl_setopt($ch, CURLOPT_HEADER, 0); curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); curl_setopt($ch, CURLOPT_TIMEOUT, $timeout); curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE); curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, FALSE); $result = curl_exec($ch); curl_close($ch);
return $result;}<?php// 仅仅用作展示,不可直接复制使用require_once('../vendor/autoload.php');
use WeChatPay\Builder;use WeChatPay\Formatter;use WeChatPay\Crypto\Rsa;use WeChatPay\Util\PemUtil;
// 接受参数,相当于原生的$_GET,这里会比h5支付多一个openid$input = $request->only(['openid', 'name', 'total', 'description', 'phone']);
// 生成商户订单号$out_trade_no = getOutTradeNo();
// 处理金额// 由于微信使用的是分作为单位,所以前端传的是元的话,需要转换一下$total = $input['total'] * 100;
// 商户号$merchantId = '1xxxxxx1';
// 从本地文件中加载「商户API私钥」,「商户API私钥」会用来生成请求的签名$merchantPrivateKeyFilePath = 'file://../cert/merchant/apiclient_key.pem';$merchantPrivateKeyInstance = Rsa::from($merchantPrivateKeyFilePath, Rsa::KEY_TYPE_PRIVATE);
// 「商户API证书」的「证书序列号」$merchantCertificateSerial = '1xxxxxxxxxxxxxxxxxxxxx91';
// 从本地文件中加载「微信支付平台证书」,用来验证微信支付应答的签名$platformCertificateFilePath = 'file://../cert/wechatpay/wechatpay_4xxxxxxxxxxxxxxxxxxx9.pem';$platformPublicKeyInstance = Rsa::from($platformCertificateFilePath, Rsa::KEY_TYPE_PUBLIC);
// 从「微信支付平台证书」中获取「证书序列号」$platformCertificateSerial = PemUtil::parseCertificateSerialNo($platformCertificateFilePath);
// 构造一个 APIv3 客户端实例$instance = Builder::factory([ 'mchid' => $merchantId, 'serial' => $merchantCertificateSerial, 'privateKey' => $merchantPrivateKeyInstance, 'certs' => [ $platformCertificateSerial => $platformPublicKeyInstance, ],]);
try { // 调用 transactions/jsapi 接口后会生成prepay_id $resp = $this->instance() ->chain('v3/pay/transactions/jsapi') ->post(['json' => [ 'mchid' => $merchantId, // 商户号 'out_trade_no' => $out_trade_no, // 商户订单号 'appid' => '********换成跟商户号绑定的公众号APPID**********', 'description' => $input['description'], //商品描述 'notify_url' => 'https://xxxxx/notify', // 用户支付后的回调地址,在这里修改订单的状态 'amount' => [ 'total' => $total, 'currency' => 'CNY' ], 'payer' => [ 'openid' => $input['openid'] ] ]]); // 需要根据prepay_id去生成加密的信息 $prepay_id = json_decode($resp->getBody(), true)['prepay_id']; $sign = getSign($prepay_id); // 如果请求成功,需要将一些参数进行入库,这里仅作演示,非正式数据入库 $response = Db::table('order')->insert([ 'openid' => $input['openid'], 'name' => $input['name'], 'description' => $input['description'], 'total' => $input['total'], 'phone' => $input['phone'], 'trade_state' => 'START', ]); // 入库成功后,将跳转链接和订单号传给前端,前端拿到跳转地址跳转即可 if($response) { return jsonp([ 'code' => 200, 'msg' => '操作成功', 'data' => [ 'out_trade_no' => $out_trade_no, 'sign' => $sign ] ]); } else { return jsonp([ 'code' => 100, 'msg' => '操作失败' ]); }} catch (\Exception $e) { // 进行错误处理 if ($e instanceof \GuzzleHttp\Exception\RequestException && $e->hasResponse()) { $r = $e->getResponse(); echo $r->getBody(), PHP_EOL, PHP_EOL, PHP_EOL; }}
// 获取加密参数function getSign($prepay_id){ $merchantPrivateKeyInstance = Rsa::from($this->merchantPrivateKeyFilePath);
$params = [ 'appId' => $this->appid, 'timeStamp' => (string)Formatter::timestamp(), 'nonceStr' => Formatter::nonce(), 'package' => 'prepay_id=' . $prepay_id, ];
$params += ['paySign' => Rsa::sign( Formatter::joinedByLineFeed(...array_values($params)), $merchantPrivateKeyInstance ), 'signType' => 'RSA'];
return $params;}🎈 通用微信支付库封装
由于直接使用微信的支付库,代码非常的匀余,所以封装了一个微信支付库
由于只针对一些业务的
api封装,所以肯定不全,需要的可以自己添加需要的api微信支付 API 接口列表: https://pay.weixin.qq.com/wiki/doc/apiv3/apis/index.shtml
<?php/** * User: tinygeeker * Desc: 微信支付库封装 * Date: 2023/08/10 */
namespace App;
use App\Helper;use WeChatPay\Builder;use WeChatPay\Formatter;use WeChatPay\Crypto\Rsa;use WeChatPay\Util\PemUtil;
class WxPay{ // appid private $appid;
// 商户号 private $merchantId;
// 商户API私钥 private $merchantPrivateKeyFilePath;
// 证书序列号 private $merchantCertificateSerial;
// 微信支付平台证书 private $platformCertificateFilePath;
/** * @param $appid * @param $merchantId * @param $merchantCertificateSerial */ public function __construct($appid = '', $merchantId = '', $merchantCertificateSerial = '') { $this->appid = $appid ?: '换成自己的APPID'; $this->merchantId = $merchantId ?: '换成自己的商户号'; $this->merchantCertificateSerial = $merchantCertificateSerial ?: '换成自己的证书序列号';
$this->merchantPrivateKeyFilePath = 'file:///common/cert/merchant/apiclient_key.pem'; // 换成自己的 $this->platformCertificateFilePath = 'file:///common/cert/wechatpay/wechatpay_xxx.pem'; // 换成自己的 }
/** * @return \WeChatPay\BuilderChainable */ protected function instance() { $merchantPrivateKeyInstance = Rsa::from($this->merchantPrivateKeyFilePath, Rsa::KEY_TYPE_PRIVATE); $platformPublicKeyInstance = Rsa::from($this->platformCertificateFilePath, Rsa::KEY_TYPE_PUBLIC);
$platformCertificateSerial = PemUtil::parseCertificateSerialNo($this->platformCertificateFilePath);
$instance = Builder::factory([ 'mchid' => $this->merchantId, 'serial' => $this->merchantCertificateSerial, 'privateKey' => $merchantPrivateKeyInstance, 'certs' => [ $platformCertificateSerial => $platformPublicKeyInstance, ], ]);
return $instance; }
public function getSign($prepay_id) { $merchantPrivateKeyInstance = Rsa::from($this->merchantPrivateKeyFilePath);
$params = [ 'appId' => $this->appid, 'timeStamp' => (string)Formatter::timestamp(), 'nonceStr' => Formatter::nonce(), 'package' => 'prepay_id=' . $prepay_id, ];
$params += ['paySign' => Rsa::sign( Formatter::joinedByLineFeed(...array_values($params)), $merchantPrivateKeyInstance ), 'signType' => 'RSA'];
return $params; }
public function checkOutTradeNo($out_trade_no) { try { $resp = $this->instance() ->v3->pay->transactions->outTradeNo->_out_trade_no_ ->get([ // Query 参数 'query' => ['mchid' => $this->merchantId], // 变量名 => 变量值 'out_trade_no' => $out_trade_no, ]);
return $resp->getBody(); } catch (\Exception $e) { // 进行错误处理 echo $e->getMessage(), PHP_EOL; if ($e instanceof \GuzzleHttp\Exception\RequestException && $e->hasResponse()) { $r = $e->getResponse(); echo $r->getStatusCode() . ' ' . $r->getReasonPhrase(), PHP_EOL; echo $r->getBody(), PHP_EOL, PHP_EOL, PHP_EOL; } echo $e->getTraceAsString(), PHP_EOL; } } // h5下单 public function h5($total, $out_trade_no, $description, $notify_url) { try { $resp = $this->instance() ->chain('v3/pay/transactions/h5') ->post(['json' => [ 'mchid' => $this->merchantId, 'out_trade_no' => $out_trade_no, 'appid' => $this->appid, 'description' => $description, 'notify_url' => $notify_url, 'amount' => [ 'total' => $total, 'currency' => 'CNY' ], 'scene_info' => [ 'payer_client_ip' => Helper::getClientIp(), 'h5_info' => [ 'type' => 'Wap' ] ] ]]);
return $resp->getBody(); } catch (\Exception $e) { // 进行错误处理 echo $e->getMessage(), PHP_EOL; if ($e instanceof \GuzzleHttp\Exception\RequestException && $e->hasResponse()) { $r = $e->getResponse(); echo $r->getStatusCode() . ' ' . $r->getReasonPhrase(), PHP_EOL; echo $r->getBody(), PHP_EOL, PHP_EOL, PHP_EOL; } echo $e->getTraceAsString(), PHP_EOL; } } // jsapi下单 public function jsapi($openid, $total, $out_trade_no, $description, $notify_url) { try { $resp = $this->instance() ->chain('v3/pay/transactions/jsapi') ->post(['json' => [ 'mchid' => $this->merchantId, 'out_trade_no' => $out_trade_no, 'appid' => $this->appid, 'description' => $description, 'notify_url' => $notify_url, 'amount' => [ 'total' => $total, 'currency' => 'CNY' ], 'payer' => [ 'openid' => $openid ] ]]);
return $resp->getBody(); } catch (\Exception $e) { // 进行错误处理 echo $e->getMessage(), PHP_EOL; if ($e instanceof \GuzzleHttp\Exception\RequestException && $e->hasResponse()) { $r = $e->getResponse(); echo $r->getStatusCode() . ' ' . $r->getReasonPhrase(), PHP_EOL; echo $r->getBody(), PHP_EOL, PHP_EOL, PHP_EOL; } echo $e->getTraceAsString(), PHP_EOL; } } // todo... 更多接口可根据官方文档列表自行添加}<?php/** * User: tinygeeker * Desc: 工具库 * Date: 2023/08/10 */ namespace App;
class Helper{ /** * @return array|mixed|string|string[] */ static public function getClientIP() { if (@$_SERVER["HTTP_ALI_CDN_REAL_IP"]) { $ip = $_SERVER["HTTP_ALI_CDN_REAL_IP"]; } elseif (@$_SERVER["HTTP_X_FORWARDED_FOR"] ?: false) { $ips = explode(',', $_SERVER["HTTP_X_FORWARDED_FOR"]); $ip = $ips[0]; } elseif (@$_SERVER["HTTP_CDN_SRC_IP"] ?: false) { $ip = $_SERVER["HTTP_CDN_SRC_IP"]; } elseif (getenv('HTTP_CLIENT_IP')) { $ip = getenv('HTTP_CLIENT_IP'); } elseif (getenv('HTTP_X_FORWARDED')) { $ip = getenv('HTTP_X_FORWARDED'); } elseif (getenv('HTTP_FORWARDED_FOR')) { $ip = getenv('HTTP_FORWARDED_FOR'); } elseif (getenv('HTTP_FORWARDED')) { $ip = getenv('HTTP_FORWARDED'); } else { $ip = $_SERVER['REMOTE_ADDR']; }
$ip = str_replace(['::ffff:', '[', ']'], ['', '', ''], $ip); return $ip; }
/** * @param $length * @param $type * @return false|string */ static public function createRandomStr($length = 32, $type = 0) { switch ($type) { case 1: $chars = '0123456789'; break; case 2: $chars = 'abcdefghijklmnopqrstuvwxyz'; break; case 3: $chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'; break; case 4: $chars = 'abcdefghijklmnopqrstuvwxyz0123456789'; break; case 5: $chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'; break; default: $chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'; break; }
return substr(str_shuffle($chars), 0, $length); }
/** * @param $url * @param $timeout * @return bool|string */ static public function getCurl($url, $timeout = 5) { $ch = curl_init(); curl_setopt($ch, CURLOPT_URL, $url); curl_setopt($ch, CURLOPT_HEADER, 0); curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); curl_setopt($ch, CURLOPT_TIMEOUT, $timeout); curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE); curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, FALSE); $result = curl_exec($ch); curl_close($ch);
return $result; }
/** * @param $url * @param $data * @param $header * @param $timeout * @return bool|string */ static public function postCurl($url, $data, $header = [], $timeout = 5) { $ch = curl_init(); curl_setopt($ch, CURLOPT_URL, $url); if ($header) { curl_setopt($ch, CURLOPT_HTTPHEADER, $header); } curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); curl_setopt($ch, CURLOPT_POST, 1); curl_setopt($ch, CURLOPT_POSTFIELDS, $data); curl_setopt($ch, CURLOPT_TIMEOUT, $timeout); curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE); curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, FALSE); $result = curl_exec($ch); curl_close($ch);
return $result; }}版权声明: 本文为 InfoQ 作者【极客飞兔】的原创文章。
原文链接:【http://xie.infoq.cn/article/fa535aac9361daeafba61cf1f】。未经作者许可,禁止转载。
极客飞兔
风起于青萍之末,浪成于微澜之间 2023-03-02 加入
华为云云享专家 思否2022年度先锋榜单 CSDN专家博主,领域之星 阿里天猫精灵首席测评官,专家博主










评论