TheNorthMemory / wechatpay-axios-plugin

微信支付 WeChatPay OpenAPI v2&v3' SDK,以命令行方式与接口交互,play the openapi requests over command line
MIT License
239 stars 37 forks source link

请问微信支付通知的签名验证和内容解密部分有代码样例吗? #45

Closed highmore closed 1 year ago

highmore commented 1 year ago

RT

TheNorthMemory commented 1 year ago

在用Koa项目代码片段,供参考:

import { readFileSync } from 'fs';
import Router from 'koa-router';
import { Rsa, Formatter, Aes } from 'wechatpay-axios-plugin';

const base64 = 'base64', SUCCESS = 'SUCCESS', WechatpayNonce = 'wechatpay-nonce', WechatpaySerial = 'wechatpay-serial', WechatpaySignature = 'wechatpay-signature', WechatpayTimestamp = 'wechatpay-timestamp';
const WXPAY_APIV3 = 'exposed_your_key_here_have_risks';
const PUBKEY = {
    'ABCD': readFileSync('/path/to/wechatpay/cert.pem'),
};

export default (new Router)
  .post('/', async ctx => {
    const { headers: { [WechatpaySerial]: wechatpaySerial, [WechatpayNonce]: wechatpayNonce, [WechatpaySignature]: wechatpaySignature, [WechatpayTimestamp]: wechatpayTimestamp, } } = ctx;

    wechatpaySerial && wechatpayNonce && wechatpaySignature && wechatpayTimestamp || ctx.throw(401, `Missing of the headers.`);

    PUBKEY[wechatpaySerial] || ctx.throw(401, `Abuse of the header's ${WechatpaySerial}: ${wechatpaySerial}.`);

    Math.abs(wechatpayTimestamp*1 - Formatter.timestamp()) > 300 && ctx.throw(403, `Abuse of the header's ${WechatpayTimestamp}: ${wechatpayTimestamp}.`);

    Rsa.verify(Formatter.joinedByLineFeed(wechatpayTimestamp, wechatpayNonce, ctx.request.rawBody), wechatpaySignature, PUBKEY[wechatpaySerial]) || ctx.throw(403, `Abuse of the header's ${WechatpaySignature}: ${wechatpaySignature}.`);

    const { id: event_id, event_type, create_time, resource: { ciphertext, associated_data, nonce } } = ctx.request.body;
    const decrypted = JSON.parse(Aes.AesGcm.decrypt(nonce, Buffer.from(WXPAY_APIV3, base64), ciphertext, associated_data));

    //退款通知 @see https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter3_1_11.shtml
    console.debug(event_id, event_type, create_time, decrypted);
    // 更多逻辑...

    ctx.body = {code: SUCCESS, message: `Thanks for the notification.` };
  });
highmore commented 1 year ago

非常感谢您的回答,还有一个小问题: 代码中的 WXPAY_APIV3 是指微信支付证书里面的 apiclient_key.pem 还是 apiclient_cert.pem 或者是 API_V3 密匙?

TheNorthMemory commented 1 year ago

非常感谢您的回答,还有一个小问题: 代码中的 WXPAY_APIV3 是指微信支付证书里面的 apiclient_key.pem 还是 apiclient_cert.pem 或者是 API_V3 密匙?

highmore commented 1 year ago

噢,感谢耐心解答。您提供的代码中有一段: const decrypted = JSON.parse(Aes.AesGcm.decrypt(nonce, Buffer.from(WXPAY_APIV3, base64), ciphertext, associated_data)); 这里的 Buffer.from(WXPAY_APIV3, base64) 把我弄糊涂了,应该是 Buffer.from(ciphertext, base64) 才对吧? 最后再请教一个问题:您提供的Koa代码中 Rsa.verify(Formatter.joinedByLineFeed(wechatpayTimestamp, wechatpayNonce, ctx.request.rawBody), wechatpaySignature, PUBKEY[wechatpaySerial]) || ctx.throw(403, ‘Abuse of the header's ${WechatpaySignature}: ${wechatpaySignature}.’); 使用了 ctx.request.rawBody 来验证签名 因为我使用的是 Express,怎样才能拿到 rawBody ?

TheNorthMemory commented 1 year ago

借用中间件能力 https://github.com/expressjs/body-parser ,例如:

app.use(bodyParser.json({
    verify: function (req, res, buf, encoding) {
        req.rawBody = buf;
    }
}));
highmore commented 1 year ago

感谢,完美搞定!