目录
如果你还没有微信商户号,请点击上面的链接进行申请,如果已经有了,可以跳过这一步
首先点击 账户中心
API安全
申请API证书
申请API证书
账户中心
API安全
设置APIv3密钥
设置
APIv2密钥
和 APIv3密钥
,由于 APIv2密钥
已经逐渐废弃了,所以只需要申请 APIv3密钥
即可32
位字符,该密钥需要保存好,供后面使用申请APIv3密钥
设置APIv3密钥
// 生成32位的APIv3随机密钥
$chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
echo substr(str_shuffle($chars), 0, $length);
微信官方提供了 JAVA
、PHP
、GO
三种语言版本的开发库,请根据自己开发语言选择
GO语言: wechatpay-go ‹推荐›
由于 php
实现支付相对简单,所以我将以 php
作为支付的讲解
首先使用 composer
安装 sdk
composer init
composer 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/
获取证书序列号
关联公众号
产品中心
我的产品
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);
}
// 获取客户端的IP
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;
}
<?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支付域名
关于申请支付域名的流程基本都差不多要求也差不多,看上面的 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, 请求接口获取openid
function 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'];
// 返回openid
return 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
/**
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
/**
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;
}
}
手机扫一扫
移动阅读更方便
你可能感兴趣的文章