Thinkphp8 集成微信支付sdk

配置并獲取商戶appId、證書、v3密鑰、證書序列號等

官方有接入指引,按照要求申請即可順利獲?。?a target="_blank">https://pay.weixin.qq.com/index.php/core/cert/api_cert#/api-cert-manage

申請商戶證書.png

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/"

如果提示報錯:

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/"
下載微信公鑰證書.png

集成到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通了。

  1. 回調(diào)通知處理

使用微信公鑰對接口數(shù)據(jù)進行簽名驗證:
官方文檔有步驟說明:

  1. 從請求頭部Headers,拿到Wechatpay-Signature、Wechatpay-NonceWechatpay-Timestamp、Wechatpay-SerialRequest-ID,商戶側(cè)Web解決方案可能有差異,請求頭可能大小寫不敏感,請根據(jù)自身應(yīng)用來定;
  2. 獲取請求body體的JSON純文本;
  3. 檢查通知消息頭標記的Wechatpay-Timestamp偏移量是否在5分鐘之內(nèi);
  4. 調(diào)用SDK內(nèi)置方法,構(gòu)造驗簽名串然后經(jīng)Rsa::verfify驗簽;
  5. 消息體需要解密的,調(diào)用SDK內(nèi)置方法解密;
  6. 如遇到問題,請拿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ù)操作即可。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容