如何手动模拟express.raw()解析请求?解决Stripe Webhook验证问题
解决MongoDB Realm中Stripe Webhook含特殊字符时的验证失败问题
Stripe Webhook签名验证的核心要求是必须使用请求体的原始字节内容,你当前用body.text()读取内容的方式会经过UTF-8解码再编码的过程,这会导致含特殊字符的请求体字节与Stripe签名生成时使用的原始字节不一致,最终验证失败。
解决方案:直接获取原始请求体字节
在MongoDB Realm的HTTP端点中,通过body.arrayBuffer()获取原始请求体的字节数据,再转换为Node.js Buffer,完全模拟express.raw({type: 'application/json'})的行为,确保签名验证时使用的内容与Stripe发送的完全一致。
完整代码示例
exports = async function({ query, headers, body }, response) { // 1. 获取原始请求体的字节数组,避免解码再编码的过程 const rawBodyBuffer = Buffer.from(await body.arrayBuffer()); // 2. 提取Stripe签名头和配置密钥 const stripeSignature = headers['stripe-signature']; const webhookSecret = 'whsec_xxxxxxxxxxxx'; // 替换为你的Webhook端点密钥 const stripeApiKey = 'sk_live_xxxxxxxxxxxx'; // 替换为你的Stripe API密钥 const stripe = require('stripe')(stripeApiKey); try { // 3. 用原始字节验证签名并构造事件 const event = stripe.webhooks.constructEvent( rawBodyBuffer, stripeSignature, webhookSecret ); // 4. 处理业务逻辑(示例:根据事件类型处理) switch (event.type) { case 'payment_intent.succeeded': const paymentIntent = event.data.object; console.log('支付成功:', paymentIntent.id); // 你的业务代码:更新数据库、发送通知等 break; case 'customer.created': const customer = event.data.object; console.log('客户创建:', customer.id); break; default: console.log(`未处理的事件类型: ${event.type}`); } // 返回成功响应给Stripe response.setStatusCode(200); response.setBody(JSON.stringify({ status: 'success', received: true })); } catch (err) { console.error('Webhook验证失败:', err); response.setStatusCode(400); response.setBody(`Webhook Error: ${err.message}`); return; } };
关键注意事项
- 绝对不要对原始请求体做任何修改:不要解析为JSON对象再重新字符串化,不要手动转码,必须保持字节完全一致。
- 确保Realm HTTP端点的请求方法配置为
POST,并允许application/json类型的请求体。 - 核对Stripe控制台中配置的Webhook端点密钥与代码中的
webhookSecret完全一致,API密钥权限需包含Webhook相关权限。
内容的提问来源于stack exchange,提问作者Mariano L
相关产品推荐
相关产品推荐

