
Stripe Webhook 签名验证失败通常意味着请求体处理不正确或签名验证参数存在问题。 正确配置 Express 中间件以接收原始请求体至关重要,同时需要确保签名验证参数正确无误。以下是详细的步骤和注意事项,帮助您解决该问题。
1. 正确配置 Express 中间件
当处理 Stripe Webhook 时,需要确保 Express 能够接收到原始的请求体(raw request body)。这是因为 Stripe 使用请求体的内容生成签名,而默认情况下,Express 会尝试解析请求体,这可能会导致签名验证失败。
正确的配置方式是使用 express.raw() 中间件,并指定 type 为 'application/json'。这告诉 Express 接收 JSON 格式的原始请求体,而不会进行任何解析。
错误的配置:
app.post("/webhook", express.raw({ type: "*/*" }), async (request, response) => {
// ...
});正确的配置:
app.post("/webhook", express.raw({ type: 'application/json' }), async (request, response) => {
// ...
});2. 正确使用 stripe.webhooks.constructEvent 方法
stripe.webhooks.constructEvent 方法用于验证 Stripe Webhook 的签名。该方法接收三个参数:
- payload: 原始的请求体。
- sig: 从请求头中获取的 stripe-signature。
- secret: 您的 Webhook 密钥。
一个常见的错误是在将 payload 传递给 stripe.webhooks.constructEvent 之前对其进行字符串化处理。stripe.webhooks.constructEvent 方法期望接收的是原始的 Buffer 或字符串,而不是 JSON 字符串。
错误的使用方式:
try {
event = stripe.webhooks.constructEvent(
JSON.stringify(payload),
sig,
process.env.WEBHOOK_SECRET
);
} catch (err) {
return response.status(400).send(`Webhook Error: ${err.message}`);
}正确的使用方式:
try {
event = stripe.webhooks.constructEvent(
payload,
sig,
process.env.WEBHOOK_SECRET
);
} catch (err) {
return response.status(400).send(`Webhook Error: ${err.message}`);
}3. 完整代码示例
以下是一个完整的代码示例,展示了如何正确处理 Stripe Webhook 的签名验证:
const express = require('express');
const stripe = require('stripe')(process.env.STRIPE_SECRET_KEY);
const app = express();
app.post("/webhook", express.raw({ type: 'application/json' }), async (request, response) => {
const payload = request.body;
const sig = request.headers["stripe-signature"];
let event;
try {
event = stripe.webhooks.constructEvent(
payload,
sig,
process.env.WEBHOOK_SECRET
);
} catch (err) {
return response.status(400).send(`Webhook Error: ${err.message}`);
}
if (event.type === "checkout.session.completed") {
// Retrieve the session. If you require line items in the response, you may include them by expanding line_items.
const sessionWithLineItems = await stripe.checkout.sessions.retrieve(
event.data.object.id,
{
expand: ["line_items"],
}
);
const lineItems = sessionWithLineItems.line_items;
// Fulfill the purchase...
console.log("Fulfilling purchase");
}
response.status(200).end();
});
app.listen(4242, () => console.log('Running on port 4242'));4. 注意事项
- 确保您的 Webhook 密钥正确配置在环境变量中。
- 在生产环境中,务必验证 Webhook 的来源,以防止恶意攻击。
- 仔细阅读 Stripe 官方文档,了解有关 Webhook 安全性的最佳实践。
5. 总结
解决 Stripe Webhook 签名验证失败的关键在于正确处理请求体和使用正确的签名验证方法。通过配置 express.raw() 中间件以接收原始请求体,并直接将 payload 传递给 stripe.webhooks.constructEvent 方法,可以有效地解决此问题。 遵循这些步骤,您可以确保您的 Webhook 安全可靠地处理 Stripe 事件。










