PayPal结账时未向我的Webhook发送PAYMENT.SALE.COMPLETED事件
PayPal Webhook 问题排查与修复
核心问题分析
你的接口存在两个关键问题:
- 原始Body处理错误:Express的
express.json()中间件会自动解析请求体为JSON对象,你用JSON.stringify(req.body)重新序列化后的内容,和PayPal发送的原始请求体(比如空格、键的顺序)不一致,导致签名验证失败,真实交易的请求被直接拒绝返回403。 - 真实交易无请求:大概率是Webhook配置错误、接口公网不可访问,或者签名验证逻辑错误导致PayPal无法成功回调。
修复方案
1. 正确处理原始请求体并使用官方SDK验证签名
不要自己实现HMAC验证,PayPal的签名机制包含证书验证等额外逻辑,官方SDK能确保验证准确性。同时必须捕获原始请求体,避免解析后序列化导致的内容不一致。
// 先添加中间件捕获原始请求体,放在express.json()之前 app.use('/paypal-webhook', (req, res, next) => { let rawBody = ''; req.on('data', chunk => { rawBody += chunk.toString(); }); req.on('end', () => { req.rawBody = rawBody; next(); }); }); // 启用JSON解析中间件 app.use(express.json()); app.post('/paypal-webhook', async (req, res) => { try { console.log('收到Webhook请求'); const rawBody = req.rawBody; // 获取PayPal请求头中的验证参数 const signature = req.headers['paypal-transmission-sig']; const transmissionId = req.headers['paypal-transmission-id']; const transmissionTime = req.headers['paypal-transmission-time']; const certUrl = req.headers['paypal-cert-url']; const webhookId = req.headers['paypal-webhook-id']; // 初始化PayPal SDK客户端 const paypal = require('@paypal/checkout-server-sdk'); const environment = process.env.NODE_ENV === 'production' ? new paypal.core.LiveEnvironment(process.env.PAYPAL_CLIENT_ID, process.env.PAYPAL_CLIENT_SECRET) : new paypal.core.SandboxEnvironment(process.env.PAYPAL_CLIENT_ID, process.env.PAYPAL_CLIENT_SECRET); const client = new paypal.core.PayPalHttpClient(environment); // 创建验证请求并执行 const verificationRequest = new paypal.notifications.WebhookVerificationRequest( transmissionId, transmissionTime, rawBody, webhookId, certUrl, signature ); await client.execute(verificationRequest); // 验证成功,解析事件数据 const event = JSON.parse(rawBody); if (event.event_type === 'PAYMENT.SALE.COMPLETED') { // 提取买家邮箱 const buyerEmail = event.resource.payer.payer_info.email; console.log('买家邮箱:', buyerEmail); // 这里处理你的业务逻辑(建议异步处理,避免超时) } // 立即返回200,告诉PayPal已收到 res.status(200).send('Success'); } catch (err) { console.error('Webhook验证失败:', err.message); res.status(403).send('Unauthorized'); } });
2. 排查真实交易无请求的问题
- 确认接口公网可访问:如果是本地开发,用端口穿透工具确保PayPal能访问你的Webhook URL;生产环境检查服务器防火墙/安全组,允许外部请求访问该接口。
- 检查PayPal Webhook配置:
- 确认URL完全匹配(必须是HTTPS,生产环境不能用HTTP)
- 确认订阅了
PAYMENT.SALE.COMPLETED事件(在PayPal开发者后台的Webhook配置中勾选) - 确认Webhook状态为"Active"
- 检查PayPal事件日志:在PayPal开发者后台的Webhook页面,查看"Event History",如果有失败记录,根据错误信息排查(比如URL不可达、验证失败)。
3. 修复模拟器重复触发的问题
模拟器重复发送请求是因为接口响应超时或返回错误状态码:
- 确保接口在5秒内返回200,PayPal会重试最多8次如果请求失败
- 验证成功后立即返回200,耗时的业务逻辑(比如发送邮件、更新数据库)放到异步队列中处理,不要阻塞响应。
内容的提问来源于stack exchange,提问作者karlo292
相关产品推荐
相关产品推荐

