From 225f2e0c5a770a070ed3471fcacb8ec4c3f00104 Mon Sep 17 00:00:00 2001 From: erio Date: Fri, 6 Mar 2026 22:25:58 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E5=BE=AE=E4=BF=A1=E6=94=AF=E4=BB=98?= =?UTF-8?q?=E5=9B=9E=E8=B0=83=E9=AA=8C=E7=AD=BE=E6=94=B9=E7=94=A8=E5=85=AC?= =?UTF-8?q?=E9=92=A5=E7=9B=B4=E6=8E=A5=E9=AA=8C=E8=AF=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit wechatpay-node-v3 的 verifySign 会尝试拉取平台证书, 但我们使用的是微信支付公钥模式,不需要平台证书。 改用 crypto.createVerify 直接用公钥做 RSA-SHA256 验签。 同时增加 serial 与 WXPAY_PUBLIC_KEY_ID 的匹配校验。 Co-Authored-By: Claude Opus 4.6 --- src/lib/wxpay/client.ts | 19 +++++++++++-------- src/lib/wxpay/provider.ts | 6 ++++++ 2 files changed, 17 insertions(+), 8 deletions(-) diff --git a/src/lib/wxpay/client.ts b/src/lib/wxpay/client.ts index 8ecd5f0..f3d96a6 100644 --- a/src/lib/wxpay/client.ts +++ b/src/lib/wxpay/client.ts @@ -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 { - 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'); } diff --git a/src/lib/wxpay/provider.ts b/src/lib/wxpay/provider.ts index e6572c7..82decfb 100644 --- a/src/lib/wxpay/provider.ts +++ b/src/lib/wxpay/provider.ts @@ -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');