fix: 微信支付回调验签改用公钥直接验证

wechatpay-node-v3 的 verifySign 会尝试拉取平台证书,
但我们使用的是微信支付公钥模式,不需要平台证书。
改用 crypto.createVerify 直接用公钥做 RSA-SHA256 验签。
同时增加 serial 与 WXPAY_PUBLIC_KEY_ID 的匹配校验。

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
erio
2026-03-06 22:25:58 +08:00
parent 96962ec38e
commit 225f2e0c5a
2 changed files with 17 additions and 8 deletions

View File

@@ -1,4 +1,5 @@
import WxPay from 'wechatpay-node-v3';
import crypto from 'crypto';
import { getEnv } from '@/lib/config';
import type { WxpayPcOrderParams, WxpayH5OrderParams, WxpayRefundParams } from './types';
@@ -144,12 +145,14 @@ export async function verifyNotifySign(params: {
serial: string;
signature: string;
}): Promise<boolean> {
const pay = getPayInstance();
return pay.verifySign({
timestamp: params.timestamp,
nonce: params.nonce,
body: params.body,
serial: params.serial,
signature: params.signature,
});
const env = getEnv();
if (!env.WXPAY_PUBLIC_KEY) {
throw new Error('WXPAY_PUBLIC_KEY is required for signature verification');
}
// 微信支付公钥模式:直接用公钥验签,不拉取平台证书
const message = `${params.timestamp}\n${params.nonce}\n${params.body}\n`;
const verify = crypto.createVerify('RSA-SHA256');
verify.update(message);
return verify.verify(env.WXPAY_PUBLIC_KEY, params.signature, 'base64');
}

View File

@@ -107,6 +107,12 @@ export class WxpayProvider implements PaymentProvider {
if (!timestamp || !nonce || !signature || !serial) {
throw new Error('Missing required Wechatpay signature headers');
}
// 验证 serial 匹配我们配置的公钥 ID
if (env.WXPAY_PUBLIC_KEY_ID && serial !== env.WXPAY_PUBLIC_KEY_ID) {
throw new Error(`Wxpay serial mismatch: expected ${env.WXPAY_PUBLIC_KEY_ID}, got ${serial}`);
}
const valid = await verifyNotifySign({ timestamp, nonce, body, serial, signature });
if (!valid) {
throw new Error('Wxpay notification signature verification failed');