Node.js关于微信支付V3版相关处理方法

Posted 袁代码

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Node.js关于微信支付V3版相关处理方法相关的知识,希望对你有一定的参考价值。

今天给大家写一个关于Node.js接入微信支付V3接口时一些毕竟复杂的点,主要就是请求签名Authorization、调起支付签名、回调参数解密等。

  • 请求签名Authorization

在微信支付V3接口中,商户需要使用自身的私钥对API URL、消息体等关键数据的组合进行SHA-256 with RSA签名。请求的签名信息通过HTTP头Authorization传递,具体说明可以去看签名生成指南。没有携带签名或者签名验证不通过的请求,都不会被执行,并返回401 Unauthorized 。

那么如何生成这个Authorization呢,这个请求头,最麻烦的地方就是如何去生成

signature,其中我们使用jsrsasign模块来进行SHA256 with RSA加密,可以查看如下代码:

const KJUR, hextob64 = require('jsrsasign')


rsaSign(content, privateKey, hash='SHA256withRSA')
    const signature = new KJUR.crypto.Signature(
            alg: hash,
            prvkeypem: privateKey
        )
    signature.updateString(content)
    const signData = signature.sign()
    // 将内容转成base64
    return hextob64(signData)



//调用这个函数
let signature = this.rsaSign(`$method\\n$pathname\\n$timestamp\\n$onece_str\\n$bodyParamsStr\\n`,this.private_key,'SHA256withRSA')


//获取到signature后就可以获取到Authorization了
 let Authorization = `WECHATPAY2-SHA256-RSA2048 mchid="$mchid",nonce_str="$onece_str",timestamp="$timestamp",signature="$signature",serial_no="$serial_no"`

其中mchid:商户号,onece_str:随机字符,timestamp:时间戳,serial_no:商户API证书序列号。这样在请求时在请求头里加入就可以了,如下:

headers:
     'Content-Type':'application/json',
     'Accept':'application/json',
     'Authorization':Authorization

调起支付签名

通过上述请求后可以得到预支付交易会话标识prepay_id,然后我们需要再次进行签名,用于调起微信支付,代码如下:

paysign(options)  
     let timeStamp = this.createTimeStamp(), //时间戳
         nonceStr = this.randomString(), //32位随机数
         Ppackage = `prepay_id=$options`, //prepay_id
         signType = 'RSA'; //加签方式
     let PpaySign = `$this.appId\\n$timeStamp\\n$nonceStr\\n$Ppackage\\n`; //需要加签的字段拼接
     let cryptStr = this.rsaSign(PpaySign, this.privateKey, 'SHA256withRSA'); //生成签名
     let paySign = cryptStr;
     return 
         timeStamp,
         nonceStr,
         package: Ppackage,
         signType,
         paySign
     ;

这里的rsaSign()函数就是上文提到的加密函数,只是这里的content参数有所不同而已,这样我们就可以直接调用起微信支付了。

回调参数解密

微信支付的回调都是需要验证解密之后才可以得到订单数据的,所以解密也是比较复杂的地方,这里我们使用crypto模块,对参数进行解密,代码如下:

const crypto = require("crypto");


 decode(params) 
        const AUTH_KEY_LENGTH = 16;
        // ciphertext = 密文,associated_data = 填充内容, nonce = 位移
        const  ciphertext, associated_data, nonce  = params;
        // 密钥
        const key_bytes = Buffer.from(this.apiv3_private_key, 'utf8');
        // 位移
        const nonce_bytes = Buffer.from(nonce, 'utf8');
        // 填充内容
        const associated_data_bytes = Buffer.from(associated_data, 'utf8');
        // 密文Buffer
        const ciphertext_bytes = Buffer.from(ciphertext, 'base64');
        // 计算减去16位长度
        const cipherdata_length = ciphertext_bytes.length - AUTH_KEY_LENGTH;
        // upodata
        const cipherdata_bytes = ciphertext_bytes.slice(0, cipherdata_length);
        // tag
        const auth_tag_bytes = ciphertext_bytes.slice(cipherdata_length, ciphertext_bytes.length);
        const decipher = crypto.createDecipheriv(
            'aes-256-gcm', key_bytes, nonce_bytes
        );
        decipher.setAuthTag(auth_tag_bytes);
        decipher.setAAD(Buffer.from(associated_data_bytes));


        const output = Buffer.concat([
            decipher.update(cipherdata_bytes),
            decipher.final(),
        ]);
        return output;

其中就是params回调返回值里的resource参数,这样就可以得到回调返回的信息了。

关于小程序 接入 支付宝支付 微信支付

有做过H5的支付 最近在做小程序的支付相关 就趁机来整理一波叭

首先在这里我们使用的是onemipay

先在composer下载如下类包

然后 我们接下来 写微信支付 支付宝支付相关,可以把这些方法封装一下。我们先讲接入微信支付

1:首先具当前小程序的对应的 APP_ID,SECRET,MCH_ID,API_KEY,记得登录商家后台 添加回对应的 授权回调目录

这里先构造支付网关,项目里的H5支付,小程序支付,APP,支付都可以根据此来延伸开。这里目前只有支付宝支付 微信支付两种方式

2:注意 小程序支付使用的是WechatPay_Js支付方式,在这里设置好 app_id ,mch_id _api_key等信息,对啦 再次还有一个微信回调地址 不要忘记设置哦

3:接下来就是支付宝支付,构造对应网关,设置相关信息,因为是在小程序中进行支付宝支付,不能直接跳转到支付宝,所以在这里是生成相应支付链接,到支付宝进行支付。此有一点是 ,由于项目中链接过长(请求参数/用户标识token) ,可进行一次短连接转化,这一部分可放在前端处理,也可以放在后端进行处理,根据项目实际情况来定。

支付接口贴代码

 /**
     * 支付
     * @param Request $request
     */
    public function pay(Request $request)
    
        if (!$request->has('order_id'))
            return new TheParameterIsEmpty();

        $au_id = $request->get('au_id');

        $orderId = $request->input('order_id');

        $payGenre = $request->input('pay_gateway');

        $WeChat_type = $request->input('WeChat_type', 2);//默认 JSAPI

        $return_url = $request->input('return_url', '');

        $order = ActivityOrder::getActivityOrder($orderId, $au_id);

        if (!$order instanceof ActivityOrder) return response_json(11029, config('code.11029'));

        $ActivityMarketing = ActivityMarket::getActByAmId($order->am_id);

        if (!$ActivityMarketing instanceof ActivityMarket) return response_json(11010, config('code.11010'));

        if ($ActivityMarketing->join_stint != 0 && $ActivityMarketing->join_stint <= $ActivityMarketing->join_stint_num)
            return response_json(11011, config('code.11011'));

        ActivityOrderRepository::setPayGenre($order, $payGenre);

        $gateway = PayGateFactory::getPayGate($payGenre);


        try 
            return $this->getPayParameter($order, $gateway, $return_url ,$WeChat_type);
         catch (Exception $exception) 
            Log::error($exception);
            return response_json(500, '服务器错误');
        
    

    /**
     * @param ActivityOrder $order
     * @param GatewayInterface $gateway
     * @param $return_url //支付宝网页支付成功跳转地址
     * @param $WeChat_type 2:JSAPI 3:H5
     * @return \\Illuminate\\Http\\JsonResponse
     */
    protected function getPayParameter(ActivityOrder $order, GatewayInterface $gateway, $return_url, $WeChat_type)
    

        $pay_type = $gateway instanceof AliPay ? 'AliPay' : 'WeChatPay';

        $parameter = 1;
        if ($pay_type == 'AliPay')
            $parameter = 2;//支付宝网页支付
         elseif ($pay_type == 'WeChatPay') 
            $parameter = $WeChat_type;
        

        $gateway->getGateway($parameter);

        $gateway->setNotifyUrl('shop-ay/ny');

        if ($pay_type == 'AliPay') $gateway->setReturnUrl($return_url);

        $gateway->setGatewayOrder($order);

//        if ($pay_type == 'WeChatPay') $gateway->setTimeExpire(date('yymdHms', time()+300));

        $data = $gateway->response();

        return response_json($data);
    

最后如果成功的话 请求返回应该是

注意:前端在对应调微信支付时  字段顺序要后对应后端加密的顺序一致哦 不然会出问题的

好啦 就到此为止啦

以上是关于Node.js关于微信支付V3版相关处理方法的主要内容,如果未能解决你的问题,请参考以下文章

微信支付v3开发 扫码并输入金额支付

关于区块链

微信支付V3 小程序支付API Java版

学相伴微信支付V3版实战课上线

jsapi微信支付v3版

微信支付开发 认清微信支付v2和v3