nextjs-subscription-payments
nextjs-subscription-payments copied to clipboard
Fix: webhook error
Following the current project, when I deploy to Vercel and the webhook is triggered, the following error is displayed:
❌ Error message: No signatures found matching the expected signature for payload. Are you passing the raw request body you received from Stripe?
If a webhook request is being forwarded by a third-party tool, ensure that the exact request body, including JSON formatting and new line style, is preserved.
Learn more about webhook signing and explore webhook integration examples for various frameworks at https://github.com/stripe/stripe-node#webhook-signing
Reading the documentation, I discovered that it could be related to the payload passed to the stripe.webhooks.constructEvent()
function.
Currently:
...
export async function POST(req: Request) {
const body = await req.text();
const sig = req.headers.get('stripe-signature') as string;
const webhookSecret = process.env.STRIPE_WEBHOOK_SECRET;
let event: Stripe.Event;
try {
if (!sig || !webhookSecret)
return new Response('Webhook secret not found.', { status: 400 });
event = stripe.webhooks.constructEvent(body, sig, webhookSecret);
console.log(`🔔 Webhook received: ${event.type}`);
} catch (err: any) {
console.log(`❌ Error message: ${err.message}`);
return new Response(`Webhook Error: ${err.message}`, { status: 400 });
}
...
Solution:
...
import { NextRequest } from 'next/server';
import { headers } from 'next/headers';
export async function POST(req: NextRequest) {
const body = await req.text();
const sig = headers().get('stripe-signature') as string;
...
I changed the request type to NextRequest
, and also used headers
from Next for reading the header. With this change, I finally achieved success.
Note: When tested locally with Stripe CLI, it works perfectly; the error only occurs when deployed.
@joaoxmb is attempting to deploy a commit to the Vercel Solutions Team on Vercel.
A member of the Team first needs to authorize it.
I tried this and it didn't seem to work for me. I did look at the same stripe documentation suggested above and changed my api/webhooks
route to:
export async function POST(req: NextRequest) {
const body = await req.text();
// const sig = req.headers.get('stripe-signature') as string;
const sig = headers().get('stripe-signature') as string;
const webhookSecret = process.env.STRIPE_WEBHOOK_SECRET as string;
let event: Stripe.Event;
console.log('🔔 Webhook received body:', body);
console.log('🔔 Webhook received sig:', sig);
console.log('🔔 Webhook received secret:', webhookSecret);
const header = stripe.webhooks.generateTestHeaderString({
payload: body,
secret: webhookSecret
});
try {
if (!sig || !webhookSecret)
return new Response('Webhook secret not found.', { status: 400 });
// event = stripe.webhooks.constructEvent(body, sig, webhookSecret);
event = stripe.webhooks.constructEvent(body, header, webhookSecret);
console.log(`🔔 Webhook received: ${event.type}`);
} catch (err: any) {
console.log(`❌ Error message: ${err.message}`);
return new Response(`Webhook Error: ${err.message}`, { status: 400 });
}
and it worked bypassing sig
... which is NOT a good solution.
Doesn't this error suggest the problem is in the request body?
weird things is that running this locally does not produce an error. I forwarded my Stripe webhooks to my local machine and then I don't this error 😑
maybe some Vercel middleware intercepts the Stripe Payload and then forwards it to our webhook... and that's causing a signature miss-match ? 🤷
help ?