配置并獲取商戶appId、證書、v3密鑰、證書序列號等
官方有接入指引,按照要求申請即可順利獲?。?a target="_blank">https://pay.weixin.qq.com/index.php/core/cert/api_cert#/api-cert-manage

appid: wx*******************
v3密鑰:GM****************************
商戶號:16******************
證書序列號:12*********************************
composer安裝wechatpay官方推薦sdk
官方倉庫:https://github.com/wechatpay-apiv3/wechatpay-php/tree/main
進入項目目錄,并運行安裝命令:
cd path_to_project/tp8
composer require wechatpay/wechatpay
下載微信官方公鑰證書:
注意:前面下載的證書對是商戶自己的,這里需要使用官方工具才能下載微信的公鑰證書,用于返回數(shù)據(jù)的驗證簽名
cd path_to_project/tp8
# 查看使用幫助
php vendor/bin/CertificateDownloader.php -V
Usage: 微信支付平臺證書下載工具 [-hV]
-f=<privateKeyFilePath> -k=<apiV3key> -m=<merchantId>
-s=<serialNo> -o=[outputFilePath] -u=[baseUri]
Options:
-m, --mchid=<merchantId> 商戶號
-s, --serialno=<serialNo> 商戶證書的序列號
-f, --privatekey=<privateKeyFilePath>
商戶的私鑰文件
-k, --key=<apiV3key> ApiV3Key
-o, --output=[outputFilePath]
下載成功后保存證書的路徑,可選參數(shù),默認為臨時文件目錄夾
-u, --baseuri=[baseUri] 接入點,默認為 https://api.mch.weixin.qq.com/
-V, --version Print version information and exit.
-h, --help Show this help message and exit.
帶上參數(shù)執(zhí)行命令,獲取微信公鑰證書:
cd path_to_project/tp8
# 這里要帶上前面申請的證書相對路徑及v3密鑰,商戶號,序列號等參數(shù)
# php vendor/bin/CertificateDownloader.php -k ${apiV3key} -m ${mchId} -f ${mchPrivateKeyFilePath} -s ${mchSerialNo} -o ${outputFilePath}
php vendor/bin/CertificateDownloader.php -m 16****************** -f "certs/apiclient_key.pem" -k "GM****************************" -s "12*********************************" -o "certs/"
如果提示報錯:
- Trying [240e:e1:aa00:4000::94]:443...
- Connected to api.mch.weixin.qq.com (240e:e1:aa00:4000::94) port 443
- ALPN: curl offers http/1.1
- SSL certificate problem: unable to get local issuer certificate
- Closing connection
cURL error 60: SSL certificate problem: unable to get local issuer certificate (see https://curl.haxx.se/libcurl/c/libcurl-errors.html) for https://api.mch.weixin.qq.com/v3/certificates
需要進入到vendor/guzzlehttp/guzzle/src/Client.php,修改配置:
private function configureDefaults(array $config): void {
$defaults = [
'allow_redirects' => RedirectMiddleware::$defaultSettings,
'http_errors' => true,
'decode_content' => true,
// verify = ture 修改成 false,等證書下載完成再改回來
'verify' => false,
'cookies' => false,
'idn_conversion' => false,
];
...
}
再次執(zhí)行命令:
php vendor/bin/CertificateDownloader.php -m 16****************** -f "certs/apiclient_key.pem" -k "GM****************************" -s "12*********************************" -o "certs/"

集成到thinkphp8
1、 在app\model目錄下新建WxpayModel.php
<?php
namespace app\model;
use WeChatPay\Builder;
use WeChatPay\Crypto\Rsa;
use WeChatPay\Util\PemUtil;
class WxpayModel {
// 設(shè)置參數(shù)
public function Pay() {
// 商戶號
$merchantId = '1***********************';
// 從本地文件中加載「商戶API私鑰」,「商戶API私鑰」會用來生成請求的簽名 // 商戶私鑰,用來發(fā)送數(shù)據(jù)簽名
$merchantPrivateKeyFilePath = 'file://D:/projects/tp8/certs/apiclient_key.pem';
$merchantPrivateKeyInstance = Rsa::from($merchantPrivateKeyFilePath, Rsa::KEY_TYPE_PRIVATE);
// 「商戶API證書」的「證書序列號」
$merchantCertificateSerial = '1**********************************************************';
// 從本地文件中加載「微信支付平臺證書」,用來驗證微信支付應(yīng)答的簽名 // 微信公鑰,用來驗證返回數(shù)據(jù)的簽名
$platformCertificateFilePath = 'file://D:/projects/tp8/certs/cert.pem';
$platformPublicKeyInstance = Rsa::from($platformCertificateFilePath, Rsa::KEY_TYPE_PUBLIC);
// 從「微信支付平臺證書」中獲取「證書序列號」
$platformCertificateSerial = PemUtil::parseCertificateSerialNo($platformCertificateFilePath);
// 構(gòu)造一個 APIv3 客戶端實例
$instance = Builder::factory([
'mchid' => $merchantId,
'serial' => $merchantCertificateSerial,
'privateKey' => $merchantPrivateKeyInstance,
'certs' => [
$platformCertificateSerial => $platformPublicKeyInstance,
],
]);
try {
$resp = $instance
->chain('v3/pay/transactions/native') // 這里使用的native支付,其它接口參考官方文檔,如jsapi
->post(['json' => [
'mchid' => '1**************',
'out_trade_no' => 'native12177525012014070332333',
'appid' => 'wx*******************',
'description' => 'Image形象店-深圳騰大-QQ公仔',
'notify_url' => 'https://weixin.qq.com/',
'amount' => [
'total' => 1,
'currency' => 'CNY',
],
]]);
echo $resp->getStatusCode(), PHP_EOL;
echo $resp->getBody(), PHP_EOL;
} 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;
}
}
}
2、 到app\controllers目錄下Index.php添加測試喚起
<?php
namespace app\controller;
use app\model\WxpayModel;
class Index {
public function index() {
$wxpayModel = new WxpayModel();
$wxpayModel->pay();
}
}
瀏覽器訪問項目http://localhost/index/index.html
正解返回支付鏈接:
{"code_url":"weixin://wxpay/bizpayurl?pr=gUTJqa5zz"}
至此,demo通了。
-
回調(diào)通知處理
使用微信公鑰對接口數(shù)據(jù)進行簽名驗證:
官方文檔有步驟說明:
- 從請求頭部
Headers,拿到Wechatpay-Signature、Wechatpay-Nonce、Wechatpay-Timestamp、Wechatpay-Serial及Request-ID,商戶側(cè)Web解決方案可能有差異,請求頭可能大小寫不敏感,請根據(jù)自身應(yīng)用來定;- 獲取請求
body體的JSON純文本;- 檢查通知消息頭標記的
Wechatpay-Timestamp偏移量是否在5分鐘之內(nèi);- 調(diào)用
SDK內(nèi)置方法,構(gòu)造驗簽名串然后經(jīng)Rsa::verfify驗簽;- 消息體需要解密的,調(diào)用
SDK內(nèi)置方法解密;- 如遇到問題,請拿Request-ID聯(lián)系官方在線技術(shù)支持;
use WeChatPay\Crypto\Rsa;
use WeChatPay\Crypto\AesGcm;
use WeChatPay\Formatter;
use \think\facade\App;
// 提取需要的頭部信息
$inWechatpaySignature = isset($_SERVER['HTTP_WECHATPAY_SIGNATURE']) ? $_SERVER['HTTP_WECHATPAY_SIGNATURE'] : '';
$inWechatpayTimestamp = isset($_SERVER['HTTP_WECHATPAY_TIMESTAMP']) ? $_SERVER['HTTP_WECHATPAY_TIMESTAMP'] : '';
$inWechatpaySerial = isset($_SERVER['HTTP_WECHATPAY_SERIAL']) ? $_SERVER['HTTP_WECHATPAY_SERIAL'] : '';
$inWechatpayNonce = isset($_SERVER['HTTP_WECHATPAY_NONCE']) ? $_SERVER['HTTP_WECHATPAY_NONCE'] : '';
// 還有一個request_id,主要用于技術(shù)支持,如果沒問題,就不需要
// 這里要重點注意:獲取請求`body`體的`JSON`純文本,不能使用thinkphp框架的方法input()、$_POST、$_GET等
$inBody = file_get_contents('php://input');
$apiv3Key = 'Dg56*************************';// 在商戶平臺上設(shè)置的APIv3密鑰
// 獲取應(yīng)用實例
$app = app();
// 這里使用微信平臺的公鑰進行驗簽,如果數(shù)據(jù)庫存的是文件路徑:
// $platformPublicKeyInstance = Rsa::from('file://' . $app->getRootPath() . $config['provider_public_key'], Rsa::KEY_TYPE_PUBLIC);
// 這里使用微信平臺的公鑰進行驗簽,如果數(shù)據(jù)庫存的是公鑰串:
$platformPublicKeyInstance = Rsa::from($config['provider_public_key'], Rsa::KEY_TYPE_PUBLIC);
// 檢查通知時間偏移量,允許5分鐘之內(nèi)的偏移
$timeOffsetStatus = 300 >= abs(Formatter::timestamp() - (int)$inWechatpayTimestamp);
$verifiedStatus = Rsa::verify(
// 構(gòu)造驗簽名串
Formatter::joinedByLineFeed($inWechatpayTimestamp, $inWechatpayNonce, $inBody),
$inWechatpaySignature,
$platformPublicKeyInstance
);
if ($timeOffsetStatus && $verifiedStatus) {
// 轉(zhuǎn)換通知的JSON文本消息為PHP Array數(shù)組
$inBodyArray = (array)json_decode($inBody, true);
$inBodyArray = json_decode($inBody, true);
$ciphertext = $inBodyArray['resource']['ciphertext'];
$nonce = $inBodyArray['resource']['nonce'];
$aad = $inBodyArray['resource']['associated_data'];
// 加密文本消息解密
$inBodyResource = AesGcm::decrypt($ciphertext, $apiv3Key, $nonce, $aad);
// 把解密后的文本轉(zhuǎn)換為PHP Array數(shù)組
$inBodyResourceArray = (array)json_decode($inBodyResource, true);
// print_r($inBodyResourceArray);// 打印解密后的結(jié)果
return ['status' => true, 'data' => $inBodyResourceArray];
} else {
return ['status' => false, 'data' => []];
}
重點注意:獲取請求body體的JSON純文本,不能使用thinkphp框架的方法input()、$_POST、$_GET等
最后根據(jù)驗證簽名的結(jié)果和解密出的數(shù)據(jù)執(zhí)行后續(xù)操作即可。