下载
首先先在项目根目录的composer.json文件的require里面添加这句话
"wechatpay/wechatpay": "^1.0",
添加之后把composer.lock文件删掉,然后在项目根目录执行composer install下载依赖
配置
新建及引入
在util里面新增一个WechatPay.php文件,然后引入这些方法
use GuzzleHttp\Exception\RequestException;
use WeChatPay\Builder;
use WeChatPay\Util\PemUtil;
use WeChatPay\Crypto\AesGcm;
use WeChatPay\Crypto\Rsa;
新增两个静态属性存储配置信息
// 支付连接客户端
public static $payCli;
// 连接配置
public static $payConfig;
初始化客户端
公钥客户端
// 获取支付连接客户端
private static function getCli(): void
{
// 商户号
$merchantId = config('wechat.developer.pay.machid');
// 商户私钥路径
$merchantPrivateKeyPath = 'file://' . config('wechat.developer.pay.macPrivateKey');
$merchantPrivateKey = Rsa::from($merchantPrivateKeyPath, Rsa::KEY_TYPE_PRIVATE);
// 商户API证书序列号
$merchantSerialNumber = config('wechat.developer.pay.mchsn');
// 商户公钥id
$platformPublicKeyId = config('wechat.developer.pay.publicsn');
// 商户公钥路径
$platformPublicKeyFilePath = 'file://' . config('wechat.developer.pay.macPublicKey');
$twoPlatformPublicKeyInstance = Rsa::from($platformPublicKeyFilePath, Rsa::KEY_TYPE_PUBLIC);
// 构造一个instance
$instance = Builder::factory([
'mchid' => $merchantId,
'serial' => $merchantSerialNumber,
'privateKey' => $merchantPrivateKey,
'certs' => [$platformPublicKeyId => $twoPlatformPublicKeyInstance]
]);
// 写入静态属性
self::$payCli = $instance;
}
平台证书客户端
如果还没有生成平台证书和获取平台证书序列号,先转到这里生成和获取:微信商户平台证书生成脚本
// 获取支付连接客户端
private static function getCli(): void
{
// 商户号
$merchantId = config('wechat.developer.pay.machid');
// 商户私钥路径
$merchantPrivateKeyPath = 'file://' . config('wechat.developer.pay.macPrivateKey');
$merchantPrivateKey = Rsa::from($merchantPrivateKeyPath, Rsa::KEY_TYPE_PRIVATE);
// 商户API证书序列号
$merchantSerialNumber = config('wechat.developer.pay.mchsn');
// 平台证书序列号:
$platformCertificateSerial = config('wechat.developer.pay.platform');
// 平台证书路径
$platformCertificateFilePath = 'file://' . config('wechat.developer.pay.platformPath');
$wechatpayCertificate = Rsa::from($platformCertificateFilePath, Rsa::KEY_TYPE_PUBLIC);
// 构造一个WechatPayMiddleware
$instance = Builder::factory([
'mchid' => $merchantId,
'serial' => $merchantSerialNumber,
'privateKey' => $merchantPrivateKey,
'certs' => [$wechatpayCertificateSerialNumber => $wechatpayCertificate]
]);
// 写入静态属性
self::$payCli = $instance;
}
获取微信支付信息配置
// 获取配置
private static function getConfig(): array
{
if (self::$payConfig) return self::$payConfig;
// 应用id
$appid = config('wechat.developer.app.appId');
// 商家号
$machid = config('wechat.developer.pay.machid');
// 商户私钥路径
$merchantPrivateKeyPath = config('wechat.developer.pay.macPrivateKey');
// 商户私钥
$merchantPrivateKey = PemUtil::loadPrivateKey($merchantPrivateKeyPath);
// 回调通知地址
$notify_url = '配置自己的微信回调接收地址';// 通知地址
self::$payConfig = compact('appid', 'merchantPrivateKey', 'machid', 'notify_url');
return self::$payConfig;
}
获取签名
public static function getSignStr($prepayId = ''): array
{
if (empty($prepayId)) throw new Exception('预支付id不可为空', 400);
// 获取配置
extract(self::getConfig());
// 时间戳
$timestamp = time();
// 随机串
$nonce = self::getRandStr($timestamp, 33);
// 构造签名串
$message = $appid . "\n" . $timestamp . "\n" . $nonce . "\n" . "prepay_id={$prepayId}" . "\n";
$timestamp = strval($timestamp);
// 使用 SHA256 with RSA 签名
openssl_sign($message, $raw_sign, $merchantPrivateKey, 'sha256WithRSAEncryption');
$sign = base64_encode($raw_sign);
return compact('nonce', 'timestamp', 'prepayId', 'sign');
}
private static function getRandStr($timestamp, $length = 32): string
{
$str = md5($timestamp) . md5(date('Y-m-d'));
return substr($str, 5, $length);
}
生成预支付交易单
public static function prepayment($orderId, $desc = '', $price = 0.01, $notifyUrl = '', $user = [])
{
if (empty(self::$payCli)) self::getCli();
// 获取配置
extract(self::getConfig());
// 接下来,正常使用Guzzle发起API请求
try {
$resp = self::$payCli->chain('v3/pay/transactions/jsapi')->post(['json' => [
'appid' => $appid,
'mchid' => $machid,
'description' => $desc,
'out_trade_no' => $orderId,
// 通知地址 https
'notify_url' => empty($notifyUrl) ? $notify_url : $notifyUrl,
'amount' => [
'total' => (int)($price * 100),
'currency' => 'CNY'
],
'payer' => [ // 添加payer字段
'openid' => $user['wx_openid'], // 使用用户的openid来标识付款人
]
]]);
return json_decode($resp->getBody(), true);
} catch (RequestException $e) {
// 进行错误处理
$msg = $e->getMessage();
$data = [];
if ($e->hasResponse()) {
$body = json_decode($e->getResponse()->getBody(), true);
$msg = $body['message'] ?? $msg;
$data['code'] = $e->getResponse()->getStatusCode();
$data['reason'] = $e->getResponse()->getReasonPhrase();
$data['body'] = $body;
}
return ['code' => 503, 'msg' => $msg, 'data' => $data];
}
}
申请退款
public static function refund($order, $refund, $refundPrice = 0.01, $price = 0.01)
{
if (empty(self::$payCli)) self::getCli();
// 获取配置
extract(self::getConfig());
// 接下来,正常使用Guzzle发起API请求
try {
$resp = self::$payCli->chain('v3/refund/domestic/refunds')->post(['json' => [
// 'transaction_id' => '',
'out_trade_no' => $order['order'],
'out_refund_no' => $refund,
"reason" => "取消场地预定订单",
'amount' => [
'refund' => (int)($refundPrice * 100),
'total' => (int)($price * 100),
'currency' => 'CNY'
],
]]);
return json_decode($resp->getBody(), true);
} catch (RequestException $e) {
// 进行错误处理
$msg = $e->getMessage();
$data = [];
if ($e->hasResponse()) {
$body = json_decode($e->getResponse()->getBody(), true);
$msg = $body['message'] ?? $msg;
$data['code'] = $e->getResponse()->getStatusCode();
$data['reason'] = $e->getResponse()->getReasonPhrase();
$data['body'] = $body;
}
return ['code' => 503, 'msg' => $msg, 'data' => $data];
}
}
解析回调信息
public static function getPayResult($data)
{
$resource = $data['resource'] ?? [];
if (empty($resource)) throw new Exception('数据不正确', 400);
// APIV3的秘钥
$aesKey = config('wechat.developer.pay.AesKey');
$aesUtil = new AesGcm();
$payResult = $aesUtil::decrypt($resource['ciphertext'], $aesKey, $resource['nonce'], $resource['associated_data']);
if (empty($payResult)) throw new Exception('数据不正确', 400);
return json_decode($payResult, true);
}
使用
获取前端所需预支付参数
public function wx_prepayment(): \think\response\Json
{
try {
// 请求预支付
$payParams = WechatPayUtil::prepayment(订单号, 订单名称, 金额, 回调地址, 用户信息);
if (empty($payParams)) throw new \Exception('预支付获取失败', 503);
if (isset($payParams['code'])) return ret($payParams);// 错误捕捉
// 获取具体的签名信息
$payParams = WechatPayUtil::getSignStr($payParams['prepay_id']);
if (empty($payParams)) return ret(400);
} catch (\Exception $e) {
return ret(['code' => $e->getCode(), 'msg' => $e->getMessage()]);
}
return ret($payParams);
}
退款
$payParams = WechatPayUtil::refund(订单信息, 退款编号, 退款金额, 订单金额);
支付回调
public function wxPay(): \think\response\Json
{
try {
// 获取参数
$data = $this->getWxPayParams();
// 解密
$parseData = WechatPayUtil::getPayResult($data);
接下来写自己的逻辑
} catch (Exception $e) {
return ret(['code' => $e->getCode(), 'msg' => $e->getMessage()]);
}
// 返回成功
return ret([
'code' => 'SUCCESS',
'message' => '成功'
]);
}
// 获取微信支付回调参数,并校验
private function getWxPayParams(): array
{
$data = [];
// 父级参数
$parentParams = ['id', 'create_time', 'resource_type', 'event_type', 'summary'];
foreach ($parentParams as $param) {
$v = input('post.' . $param);
if (empty($v)) throw new Exception('参数错误', 400);
$data[$param] = $v;
}
$resource = input('post.resource');
if (empty($resource) || !is_array($resource)) throw new Exception('参数错误', 400);
// 子级参数
$sunParams = ['original_type', 'algorithm', 'ciphertext', 'associated_data', 'nonce'];
foreach ($sunParams as $sunParam) {
if (!isset($resource[$sunParam]) || empty($resource[$sunParam]))
throw new Exception('参数错误', 400);
$data['resource'][$sunParam] = $resource[$sunParam];
}
return $data;
}
补充
前端调用
// 微信支付
weixinPay(data) {
let param = {
timeStamp: data.timestamp, // 时间戳必须是字符串类型
nonceStr: data.nonce, // 随机字符串
package: 'prepay_id=' + data.prepayId, // 统一下单接口返回的 prepay_id 参数值
signType: "RSA", // 签名方式,默认为MD5
paySign: data.sign, // 签名
}
wx.requestPayment({
timeStamp: param.timeStamp, // 时间戳必须是字符串类型
nonceStr: param.nonceStr, // 随机字符串
package: param.package, // 统一下单接口返回的 prepay_id 参数值
signType: param.signType, // 签名方式,默认为MD5
paySign: param.paySign, // 签名
success: async (res) => {
},
fail: (e) => {
if (e.errCode == -100) return uni.showToast({
title: '取消支付',
icon: 'none'
})
}
})
},