前言
这个是之前用的,最开始用的curl那个,没找到官方文档,正好看到wechatpay-guzzle-middleware这个sdk有样例,就用的这个。这个也很方便,挺好用的。
一、wechatpay-guzzle-middleware下载
我用的是composer,简单。
composer require wechatpay/wechatpay-guzzle-middleware
二、接入jaspi支付
环境证书配置可以看上一篇:使用wechatpay-php实现JSAPI支付(服务商和普通商户)
这里不再赘述。
1.环境配置
我是把参数写到类初始化里面了。
代码如下(示例):
protected $config;
protected $mch_private_key;
protected $wechatpayCertificate;
protected $schema = 'WECHATPAY2-SHA256-RSA2048';
public function __construct()
{
$this->config = $this->getConfigGroup('1'); // 我是把很多参数写到配置里面了
$this->mch_private_key = PemUtil::loadPrivateKey(request()->domain() . "/cert/apiclient_key.pem"); //商户私钥文件路径
$this->wechatpayCertificate = PemUtil::loadCertificate(request()->domain() . '/cert/wechatpay_70CA41F8C0475EDFE07378C9895FB30FD6xxxxxx.pem'); // 微信支付平台证书文件路径
}
2.请求jsapi接口
代码如下(示例):
/**
* @param $order_no 订单号
* @param $name 商品名称
* @param $pay_price 支付金额
* @param $open_id openid
* @param $notify_url 回调地址
* @param array $attach 附加参数,这里面可以写使用优惠券之类的业务
* @return array|void
* 支付接口
*/
public function pay($order_no, $name, $pay_price, $open_id, $notify_url, $attach = [])
{
try {
$param = [
"mchid" => $this->config['mchid'], // 商户号
"out_trade_no" => $order_no,
"appid" => $this->config['appid'], // 微信小程序appid
"description" => "购买" . $name,
"notify_url" => $notify_url,
"amount" => [
"total" => intval($pay_price * 100), // 金额,单位分
"currency" => "CNY",
],
"payer" => [
"openid" => $open_id,
],
"attach" => json_encode($attach, JSON_UNESCAPED_UNICODE),
];
$authorization = $this->getAuthorization("POST", json_encode($param, JSON_UNESCAPED_UNICODE)); // 这个跟上篇写法基本一样,下面我会再更新下
// 商户相关配置
$merchantId = $this->config['mchid']; // 商户号
$merchantSerialNumber = $this->config['serial_no']; // 商户API证书序列号
$merchantPrivateKey = $this->mch_private_key; // 商户私钥文件路径
// 微信支付平台配置
$wechatpayCertificate = $this->wechatpayCertificate; // 微信支付平台证书文件路径
// 构造一个WechatPayMiddleware
$wechatpayMiddleware = WechatPayMiddleware::builder()
->withMerchant($merchantId, $merchantSerialNumber, $merchantPrivateKey) // 传入商户相关配置
->withWechatPay([$wechatpayCertificate]) // 可传入多个微信支付平台证书,参数类型为array
->build();
// 将WechatPayMiddleware添加到Guzzle的HandlerStack中
$stack = HandlerStack::create();
$stack->push($wechatpayMiddleware, 'wechatpay');
// 创建Guzzle HTTP Client时,将HandlerStack传入,接下来,正常使用Guzzle发起API请求,WechatPayMiddleware会自动地处理签名和验签
$client = new Client(['handler' => $stack]);
$resp = $client->request(
'POST',
'https://api.mch.weixin.qq.com/v3/pay/transactions/jsapi', //请求URL
[
// JSON请求体
'json' => $param,
'headers' => [
'Content-type' => 'application/json;charset=utf-8',
'Accept' => 'application/json',
'User-Agent' => '*/*',
'Authorization' => $this->schema . " " . $authorization // 注意这里面有个空格
],
]
);
$statusCode = $resp->getStatusCode();
if ($statusCode == 200) { //处理成功
// 这里是封装签名,给前端调用支付时使用
$array = json_decode($resp->getBody()->getContents(), true);
$merchantPrivateKeyInstance = Rsa::from($this->mch_private_key);
$params = [
'appId' => $this->config['appid'],
'timeStamp' => strval(Formatter::timestamp()),
'nonceStr' => Formatter::nonce(),
'package' => 'prepay_id=' . $array['prepay_id'],
];
$params += ['paySign' => Rsa::sign(
Formatter::joinedByLineFeed(...array_values($params)),
$merchantPrivateKeyInstance
), 'signType' => 'RSA'];
$params['out_trade_no'] = $order_no;
return $this->setSuccess('', $params);
} else if ($statusCode == 204) { //处理成功,无返回Body
return $this->setSuccess('');
}
} catch (RequestException|Exception|GuzzleException $e) {
// 进行错误处理
return $this->setError($e->getMessage());
}
}
3.获取Authorization
代码如下(示例):
/**
* @param $http_method
* @param $body
* @return string
*/
public function getAuthorization($http_method = "POST", $body = []): string
{
// Authorization: <schema> <token>
$url = "/v3/pay/transactions/jsapi"; //https://api.mch.weixin.qq.com
$serial_no = $this->config['serial_no']; // 商户证书序列号
$mchid = $this->config['mchid']; // 商户号
$timestamp = time();
$nonce = $this->nonce_str();
$url_parts = parse_url($url);
$canonical_url = ($url_parts['path'] . (!empty($url_parts['query']) ? "?${url_parts['query']}" : ""));
$message = $http_method . "\n" .
$canonical_url . "\n" .
$timestamp . "\n" .
$nonce . "\n" .
$body . "\n";
openssl_sign($message, $raw_sign, $this->mch_private_key, 'sha256WithRSAEncryption');
$sign = base64_encode($raw_sign);
return sprintf('mchid="%s",nonce_str="%s",timestamp="%d",serial_no="%s",signature="%s"',
$mchid, $nonce, $timestamp, $serial_no, $sign);
}
/**
* @return string
* 随机32位字符串
* 上一篇忘记写这个方法了,补上
*/
private function nonce_str(): string
{
$result = '';
$str = 'QWERTYUIOPASDFGHJKLZXVBNMqwertyuioplkjhgfdsamnbvcxz';
for ($i = 0; $i < 32; $i++) {
$result .= $str[rand(0, 48)];
}
return $result;
}
4.支付回调
代码如下(示例):
public function orderNotify(): bool
{
$notifyData = file_get_contents('php://input');
// 转换通知的JSON文本消息为PHP Array数组
$inBodyArray = (array)json_decode($notifyData, true);
// 使用PHP7的数据解构语法,从Array中解构并赋值变量
['resource' => [
'ciphertext' => $ciphertext,
'nonce' => $nonce,
'associated_data' => $aad
]] = $inBodyArray;
// 加密文本消息解密
$inBodyResource = AesGcm::decrypt($ciphertext, 'a296d789ca60d352dde0367a956883ce', $nonce, $aad);
// 把解密后的文本转换为PHP Array数组
$inBodyResourceArray = (array)json_decode($inBodyResource, true);
file_put_contents("Notify.txt", var_export($inBodyResourceArray, 1));
if ($inBodyResourceArray['trade_state'] == "SUCCESS") {
Db::startTrans();
try {
//编写支付成功的业务逻辑
$order_no = $inBodyResourceArray['out_trade_no'];
$attach = json_decode($inBodyResourceArray['attach'], true); // 如果传入信息的话,可以接收使用
Db::commit();
} catch (Exception $e) {
Log::write($e->getMessage(), 'error');
Db::rollback();
return true;
}
} else {
// 回调异常
file_put_contents("NotifyError.txt", var_export($inBodyResourceArray, 1));
}
return true;
}
总结
这个sdk跟wechatpay-php比起来差不太多,都挺清晰的。
按照上面步骤跑起来没啥问题。有问题可以留言,我印象中只有签名遇到坑了。