引入包
composer require wechatpay/wechatpay
代码示范
<?php
namespace App\Servers\Pay;
use Illuminate\Support\Facades\Log;
use WeChatPay\Builder;
use WeChatPay\Crypto\AesGcm;
use WeChatPay\Crypto\Rsa;
use WeChatPay\Formatter;
use WeChatPay\Util\PemUtil;
class WechatPayServer
{
protected $wechatPay;
public function __construct()
{
$merchantId = config('pay.wechat_pay_merchanid');
$merchantPrivateKeyFilePath = storage_path('wechatPayCertificate/commercial/apiclient_key.pem');
$merchantPrivateKeyFilePath = 'file://' . $merchantPrivateKeyFilePath;
$merchantPrivateKeyInstance = Rsa::from($merchantPrivateKeyFilePath, Rsa::KEY_TYPE_PRIVATE);
$merchantCertificateSerial = config('pay.wechat_pay_serial');
$platformCertificateFilePath = storage_path('wechatPayCertificate/private/wechatpay_cert.pem');
$platformCertificateFilePath = 'file://' . $platformCertificateFilePath;
$platformPublicKeyInstance = Rsa::from($platformCertificateFilePath, Rsa::KEY_TYPE_PUBLIC);
$platformCertificateSerial = PemUtil::parseCertificateSerialNo($platformCertificateFilePath);
$instance = Builder::factory([
'mchid' => $merchantId,
'serial' => $merchantCertificateSerial,
'privateKey' => $merchantPrivateKeyInstance,
'certs' => [
$platformCertificateSerial => $platformPublicKeyInstance,
],
]);
$this->wechatPay = $instance;
}
public function getPaySign($prepayId)
{
$merchantPrivateKeyFilePath = storage_path('wechatPayCertificate/commercial/apiclient_key.pem');
$merchantPrivateKeyFilePath = 'file://' . $merchantPrivateKeyFilePath;
$merchantPrivateKeyInstance = Rsa::from($merchantPrivateKeyFilePath);
$params = [
'appId' => config('pay.wechat_pay_appid'),
'timeStamp' => (string)Formatter::timestamp(),
'nonceStr' => Formatter::nonce(),
'prepay_id' => $prepayId,
];
$params += ['paySign' => Rsa::sign(
Formatter::joinedByLineFeed(...array_values($params)),
$merchantPrivateKeyInstance
), 'signType' => 'RSA'];
return $params;
}
public function createOrder($description, $outTradeNo, $money)
{
$money = $money * 100;
$mchid = config('pay.wechat_pay_merchanid');
$appid = config('pay.wechat_pay_appid');
try {
$params = ['json' => [
'appid' => $appid,
'mchid' => $mchid,
'out_trade_no' => $outTradeNo,
'description' => $description,
'notify_url' => config('app.url') . config('pay.wechat_pay_call_uri'),
'amount' => [
'total' => $money,
'currency' => 'CNY',
]
]];
$resp = $this->wechatPay->chain('v3/pay/transactions/app')->post($params);
$resp = $resp->getBody()->getContents();
} catch (\GuzzleHttp\Exception\RequestException $e) {
$r = $e->getResponse();
$resp = $r->getBody()->getContents();
Log::channel('weChatPay')->alert("微信支付创建订单 请求异常", ['StatusCode' => $r->getStatusCode() . ' ' . $r->getReasonPhrase(), 'Body' => $r->getBody(), 'outTradeNo' => $outTradeNo]);
} catch (\Exception $e) {
Log::channel('weChatPay')->alert("微信支付创建订单 异常", ['description' => $description, 'outTradeNo' => $outTradeNo, 'money' => $money, 'TraceAsString' => $e->getTraceAsString(), 'msg' => $e->getMessage()]);
throw $e;
}
$res = json_decode($resp, true);
$paySign = $this->getPaySign($res['prepay_id']);
$returnData = [
'appid' => $appid,
'partnerid' => $mchid,
'prepayid' => $res['prepay_id'],
'package' => "Sign=WXPay",
'noncestr' => $paySign['nonceStr'],
'timestamp' => $paySign['timeStamp'],
'sign' => $paySign['paySign'],
];
return $returnData;
}
public function analysisCallBackData($requestHead, $data)
{
$inWechatpaySignature = $requestHead['wechatpay-signature'][0];
$inWechatpayTimestamp = $requestHead['wechatpay-timestamp'][0];
$inWechatpayNonce = $requestHead['wechatpay-nonce'][0];
$inBody = file_get_contents('php://input');
$apiv3Key = config('pay.wechat_pay_apiv3_private_key');
$wechatpaySerialFilePath = 'file://' . storage_path('wechatPayCertificate/private/wechatpay_cert.pem');
$platformPublicKeyInstance = Rsa::from($wechatpaySerialFilePath, Rsa::KEY_TYPE_PUBLIC);
$timeOffsetStatus = 300 >= abs(Formatter::timestamp() - (int)$inWechatpayTimestamp);
if (!$timeOffsetStatus) {
Log::channel('weChatPay')->alert('超过5分钟才回调的信息', ['requestHead' => $requestHead, 'requestData' => $data]);
return false;
}
$verifiedStatus = Rsa::verify(
Formatter::joinedByLineFeed($inWechatpayTimestamp, $inWechatpayNonce, $inBody),
$inWechatpaySignature,
$platformPublicKeyInstance
);
if (!$verifiedStatus) {
Log::channel('weChatPay')->alert('验证签名失败', ['requestHead' => $requestHead, 'requestData' => $data]);
return false;
}
if ($timeOffsetStatus && $verifiedStatus) {
$inBodyArray = (array)json_decode($inBody, true);
['resource' => [
'ciphertext' => $ciphertext,
'nonce' => $nonce,
'associated_data' => $aad
]] = $inBodyArray;
$inBodyResource = AesGcm::decrypt($ciphertext, $apiv3Key, $nonce, $aad);
$inBodyResourceArray = (array)json_decode($inBodyResource, true);
return $inBodyResourceArray;
} else {
return null;
}
}
}