fix: 支付安全审核修复(支付宝+微信)

支付宝:
- 回调增加 app_id 校验,防止跨商户通知
- 回调增加 sign_type 过滤,仅接受 RSA2
- 退款增加 out_request_no 保证幂等
- 金额解析增加精度保护
- timestamp 改用 CST 时区

微信:
- 自行实现 AES-GCM 解密替代库的 decipher_gcm(修复 AuthTag 未验证)
- WXPAY_PUBLIC_KEY_ID 改为必填
- serial 匹配检查改为强制
- 时间戳校验移到签名验证之前
- nonce 改用 crypto.randomBytes
- publicKey 不允许空 Buffer fallback

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
erio
2026-03-06 22:57:55 +08:00
parent 5253bc8d35
commit bdf2577f28
5 changed files with 38 additions and 14 deletions

View File

@@ -75,7 +75,7 @@ export class AlipayProvider implements PaymentProvider {
return {
tradeNo: result.trade_no || tradeNo,
status,
amount: parseFloat(result.total_amount || '0'),
amount: Math.round(parseFloat(result.total_amount || '0') * 100) / 100,
paidAt: result.send_pay_date ? new Date(result.send_pay_date) : undefined,
};
}
@@ -90,15 +90,25 @@ export class AlipayProvider implements PaymentProvider {
params[key] = value;
}
// sign_type 过滤:仅接受 RSA2
if (params.sign_type && params.sign_type !== 'RSA2') {
throw new Error('Unsupported sign_type, only RSA2 is accepted');
}
const sign = params.sign || '';
if (!env.ALIPAY_PUBLIC_KEY || !verifySign(params, env.ALIPAY_PUBLIC_KEY, sign)) {
throw new Error('Alipay notification signature verification failed');
}
// app_id 校验
if (params.app_id !== env.ALIPAY_APP_ID) {
throw new Error('Alipay notification app_id mismatch');
}
return {
tradeNo: params.trade_no || '',
orderId: params.out_trade_no || '',
amount: parseFloat(params.total_amount || '0'),
amount: Math.round(parseFloat(params.total_amount || '0') * 100) / 100,
status:
params.trade_status === 'TRADE_SUCCESS' || params.trade_status === 'TRADE_FINISHED' ? 'success' : 'failed',
rawData: params,
@@ -110,6 +120,7 @@ export class AlipayProvider implements PaymentProvider {
out_trade_no: request.orderId,
refund_amount: request.amount.toFixed(2),
refund_reason: request.reason || '',
out_request_no: request.orderId + '-refund',
});
return {