Stripe throws "No signatures found matching the expected signature for payload" when constructEvent() receives a mutated body.
Copy-paste verified examples. Use the tab that matches your stack.
const stripe = require('stripe')(process.env.STRIPE_SECRET_KEY);
// express.raw() must run BEFORE express.json() — apply only to webhook route
app.post(
'/webhooks/stripe',
express.raw({ type: 'application/json' }),
async (req, res) => {
const sig = req.headers['stripe-signature'];
let event;
try {
event = stripe.webhooks.constructEvent(
req.body, // must be raw Buffer
sig,
process.env.STRIPE_WEBHOOK_SECRET // whsec_...
);
} catch (err) {
// "No signatures found" → raw body was parsed (use express.raw)
// "Timestamp outside tolerance" → replay expired; use Stripe CLI
console.error('Stripe error:', err.message);
return res.status(400).send('Webhook Error: ' + err.message);
}
switch (event.type) {
case 'payment_intent.succeeded':
await fulfillOrder(event.data.object);
break;
case 'customer.subscription.deleted':
await cancelSubscription(event.data.object);
break;
}
res.json({ received: true });
}
);Receive raw bytes → pass to Stripe SDK → handle event type → return 200.
Works with webhooks and other async event systems (including AI callbacks). Instead of guessing, inspecting the exact payload and headers can help debug faster.
Try the free webhook testerWas this page helpful?
Your feedback helps us improve the docs.