PHP 实现微信Native扫码支付


一、安装微信SDK

composer require wechatpay/wechatpay

二、准备请求资料

appid:开放平台

mchid:商户平台

商户API证书:商户平台-账户中心-api安全(申请)

商户API秘钥:商户平台-账户中心-api安全(申请)

商户证书序列号:开放平台-API管理

apiv3_key:商户平台-api安全

微信支付平台证书:(在项目根目录执行命令,保证sdk已安装 证书保存文件夹已经创建 outputFilePath 保存地址是项目地址中的文件夹)

php vendor/bin/CertificateDownloader.php -k ${apiV3key} -m ${mchId} -f ${mchPrivateKeyFilePath} -s ${mchSerialNo} -o ${outputFilePath}

${apiV3key} 替换成apiV3key秘钥

${mchId} 替换成商户号

${mchPrivateKeyFilePath} 替换成商户私钥路径

${mchSerialNo} 替换成商户证书序列号

${outputFilePath} 替换成微信支付证书要保存的路径

三、引入和定义

use WeChatPay\Builder;
use WeChatPay\Crypto\AesGcm;
use WeChatPay\Crypto\Rsa;
use WeChatPay\Formatter;
use WeChatPay\Util\PemUtil;

    private const APPID     = '';        //appid
    private const MCHID     = '';        //商户号
    private const CERT_NUM  = '';        //商户证书序列号
    private const APIV3_KEY = '';        //apiv3秘钥

四、Native下单

返回一个code_url 后端生成二维码显示给前端

    //Native下单
    public function paymentCode($out_trade_no, $description, $notify_url, $total)
    {
        // 微信配置
        $merchantId = self::MCHID;  // 商户号
        $appid      = self::APPID;  // appid
        // 从本地文件中加载「商户API私钥」,「商户API私钥」会用来生成请求的签名
        $merchantPrivateKeyFilePath = 'file://' . ROOT_PATH . '\app\...私钥路径..\apiclient_key.pem';
        $merchantPrivateKeyInstance =  Rsa::from($merchantPrivateKeyFilePath, Rsa::KEY_TYPE_PRIVATE);
        // 「商户API证书」的「证书序列号」
        $merchantCertificateSerial = self::CERT_NUM;
        // 从本地文件中加载「微信支付平台证书」,用来验证微信支付应答的签名
        $platformCertificateFilePath = 'file://' . ROOT_PATH . '\app\...支付证书路径...\wechatpay_123456.pem';
        $platformPublicKeyInstance = Rsa::from($platformCertificateFilePath, Rsa::KEY_TYPE_PUBLIC);
        // 从「微信支付平台证书」中获取「证书序列号」
        $platformCertificateSerial = PemUtil::parseCertificateSerialNo($platformCertificateFilePath);
        // 构造一个 APIv3 客户端实例
        $instance = Builder::factory([
            'mchid'      => $merchantId,
            'serial'     => $merchantCertificateSerial,
            'privateKey' => $merchantPrivateKeyInstance,
            'certs'      => [
                $platformCertificateSerial => $platformPublicKeyInstance,
            ],
        ]);
        try {
            $resp = $instance
                ->chain('v3/pay/transactions/native')
                ->post(['json' => [
                    'mchid'        => $merchantId,
                    'out_trade_no' => $out_trade_no,
                    'appid'        => $appid,
                    'description'  => $description,
                    'notify_url'   => $notify_url,
                    'amount'       => [
                        'total'    => $total * 100,
                        'currency' => 'CNY'
                    ],
                ]]);
            echo $resp->getStatusCode(), PHP_EOL;
            echo $resp->getBody(), PHP_EOL;        //返回的code_url
        } 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;
        }
    }

五、Native调起支付

用户扫码即调起

六、Native异步通知

    // 异步通知
    public function wxPayNotify()
    {
        $headers = $this->request->header();

        $inWechatpaySignature = $headers['wechatpay-signature'];   // 应答heade中对应数据:应答签名
        $inWechatpayTimestamp = $headers['wechatpay-timestamp'];   // 应答heade中对应数据:应答时间戳
        $inWechatpaySerial    = $headers['wechatpay-serial'];      // 应答heade中对应数据:应答平台证书序列号
        $inWechatpayNonce     = $headers['wechatpay-nonce'];       // 应答heade中对应数据:应答随机字符串
        $inBody               = file_get_contents('php://input');  // 应答主体:请根据实际情况获取,例如: file_get_contents('php://input');
        $apiv3Key             = self::APIV3_KEY;                   // 在商户平台上设置的APIv3密钥
        // 根据通知的平台证书序列号,查询本地平台证书文件,
        $platformPublicKeyInstance = Rsa::from('file://' . ROOT_PATH . '\app\..路径..\wxpay_cert\wechatpay_1234563.pem', Rsa::KEY_TYPE_PUBLIC);
        // 检查通知时间偏移量,允许5分钟之内的偏移
        $timeOffsetStatus = 300 >= abs(Formatter::timestamp() - (int)$inWechatpayTimestamp);
        $verifiedStatus   = Rsa::verify(
            // 构造验签名串
            Formatter::joinedByLineFeed($inWechatpayTimestamp, $inWechatpayNonce, $inBody),
            $inWechatpaySignature,
            $platformPublicKeyInstance
        );

        if ($timeOffsetStatus && $verifiedStatus) {
            // 转换通知的JSON文本消息为PHP Array数组
            $inBodyArray = (array)json_decode($inBody, true);

            // 使用PHP7的数据解构语法,从Array中解构并赋值变量
            ['resource' => [
                'ciphertext'      => $ciphertext,
                'nonce'           => $nonce,
                'associated_data' => $aad
            ]] = $inBodyArray;
            // 加密文本消息解密
            $inBodyResource = AesGcm::decrypt($ciphertext, $apiv3Key, $nonce, $aad);
            // 把解密后的文本转换为PHP Array数组
            $inBodyResourceArray = (array)json_decode($inBodyResource, true);

            // print_r($inBodyResourceArray); // 打印解密后的结果
            $out_trade_no = $inBodyResourceArray['out_trade_no'];
            $trade_state  = $inBodyResourceArray['trade_state'];
            $total        = $inBodyResourceArray['amount']['total'];
            if (Db::name('记录表')->where('out_trade_no', $out_trade_no)->find()) {
                http_response_code(200);
                $data =  [
                    'code'    => 'SUCCESS',
                    'message' => '成功'
                ];
                return json_encode($data);
            } else {
               //订单处理业务
                http_response_code(200);
                $data =  [
                    'code'    => 'SUCCESS',
                    'message' => '成功'
                ];
                return json_encode($data);
            }

        } else {
            // 调用微信查询订单API
        }
    }
}


  • 2
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值