Replies: 12 comments 22 replies
-
|
try to use |
Beta Was this translation helpful? Give feedback.
-
|
Here's my code. This is in a Nextjs repo. I'm also getting the same error Code |
Beta Was this translation helpful? Give feedback.
-
|
Any solutions for this? I'm facing the same |
Beta Was this translation helpful? Give feedback.
-
|
@kirtirajsinh I think you're missing // EXPORT config to tell Next.js NOT to parse the body
export const config = {
api: {
bodyParser: false
}
}cc: @A7med3bdulBaset Credits / References: |
Beta Was this translation helpful? Give feedback.
-
|
Checking here to see any info has been found. I currently have a old project working fine on vercel, using the same api format for my new project works fine local but will not work on vercel.
import { NextApiRequest, NextApiResponse } from 'next';
import { buffer } from 'micro';
import Stripe from 'stripe';
import { doc, setDoc } from 'firebase/firestore';
import { db } from '../../../firebaseClient';
export const config = {
api: {
bodyParser: false,
},
};
const stripe = new Stripe(process.env.STRIPE_SECRET_KEY!, {
apiVersion: '2023-10-16',
});
export default async function handler(
req: NextApiRequest,
res: NextApiResponse
) {
if (req.method === 'POST') {
const buf = await buffer(req);
const sig = req.headers['stripe-signature']!;
let event: Stripe.Event;
try {
event = stripe.webhooks.constructEvent(buf.toString(), sig, process.env.STRIPE_WEBHOOK_SECRET!);
} catch (err) {
return res.status(400).send(`Webhook Error: ${(err as Error).message}`);
}
const subscription = event.data.object as Stripe.Subscription;
const userAddress = subscription.metadata.userAddress;
const userRef = doc(db, 'users', userAddress);
switch (event.type) {
case 'customer.subscription.created':
case 'customer.subscription.updated':
if (userAddress) {
await setDoc(userRef, { proUser: true }, { merge: true });
}
break;
case 'customer.subscription.deleted':
if (userAddress) {
await setDoc(userRef, { proUser: false }, { merge: true });
}
break;
case 'customer.subscription.paused':
if (userAddress) {
await setDoc(userRef, { proUser: false }, { merge: true });
}
break;
case 'customer.subscription.resumed':
if (userAddress) {
await setDoc(userRef, { proUser: true }, { merge: true });
}
break;
default:
}
res.json({ received: true });
} else {
res.setHeader('Allow', 'POST');
res.status(405).end('Method Not Allowed');
}
} |
Beta Was this translation helpful? Give feedback.
-
|
This worked for me: |
Beta Was this translation helpful? Give feedback.
-
|
For Next.js V14 -- this is the solution that works after trial and error, without using Buffer. The following 2 adjustments should do the trick. Also the const body = await body.text() // <------
// ...
// ...
try {
// ...
event = stripe.webhooks.constructEvent(body, sig as string, SECRET)
} catch (e) {
// ....
} |
Beta Was this translation helpful? Give feedback.
-
Beta Was this translation helpful? Give feedback.
-
|
everything works for me on NGROK hosting it locally, but whenever i host it to xyz.vercel.app/api/webhook/stripe it never works or responds to anything |
Beta Was this translation helpful? Give feedback.
-
|
There are a few concrete problems in the code you posted.
For the Pages Router version, the flow should be: export const config = {
api: { bodyParser: false },
}
export default async function handler(req, res) {
if (req.method !== "POST") {
res.setHeader("Allow", "POST")
return res.status(405).end("Method Not Allowed")
}
const rawBody = await buffer(req)
const signature = req.headers["stripe-signature"]
let event
try {
event = stripe.webhooks.constructEvent(
rawBody,
signature,
process.env.STRIPE_SIGNING_SECRET!,
)
} catch (err) {
return res.status(400).send(`Webhook Error: ${(err as Error).message}`)
}
if (event.type === "checkout.session.completed") {
await fulfilOrder(event.data.object)
}
return res.status(200).json({ received: true })
}If you are on the App Router instead, use I built HookLens for this exact local debugging loop because these failures often collapse into one vague signature error. Capturing the raw request and replaying it after the fix makes it much easier to separate wrong secret, mutated body, and no-response bugs. |
Beta Was this translation helpful? Give feedback.
-
|
This is still a wormhole in 2026. I've uncovered a new problem When using a Parcel + React Stack plus Vercel DEV in a Local Environment ( Here are some Techniques to process the raw request body that should work in PRODUCTION however, although the proper method largely depends on the integration being used. The important thing is to ensure that req.body is not consumed by any process (even console.log in some cases) before attempting to process the raw body. Using Buffer const chunks: Uint8Array[] = [];
for await (const chunk of req) {
chunks.push(chunk);
}
const rawBody = Buffer.concat(chunks);
const signature = req.headers['stripe-signature'] as string;
event = stripe.webhooks.constructEvent(rawBody, signature, WEBHOOK_SECRET); Using @raw-body import getRawBody from 'raw-body';
const rawBody = await getRawBody(req as any, {
encoding: null // ensures Buffer output
});
const signature = req.headers['stripe-signature'] as string;
event = stripe.webhooks.constructEvent(rawBody, signature, WEBHOOK_SECRET);
|
Beta Was this translation helpful? Give feedback.
-
|
For anyone hitting this on Next.js App Router (13+), the
import { NextRequest, NextResponse } from 'next/server'
import Stripe from 'stripe'
const stripe = new Stripe(process.env.STRIPE_SECRET_KEY!)
export async function POST(req: NextRequest) {
const body = await req.text()
const sig = req.headers.get('stripe-signature')
if (!sig) return NextResponse.json({ error: 'No signature' }, { status: 400 })
let event: Stripe.Event
try {
event = stripe.webhooks.constructEvent(body, sig, process.env.STRIPE_WEBHOOK_SECRET!)
} catch (err) {
console.error('Stripe webhook signature failed:', err)
return NextResponse.json({ error: 'Invalid signature' }, { status: 400 })
}
if (event.type === 'checkout.session.completed') {
const session = event.data.object as Stripe.Checkout.Session
// handle your session here
}
return NextResponse.json({ received: true })
}Key differences from Pages Router:
Works locally but fails on Vercel? Almost always the webhook secret. Your local CLI ( |
Beta Was this translation helpful? Give feedback.


Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
-
Summary
Please treat as urgent,
I am working on an app with next js and i am trying to use stripe for payment. i have successfully integrated stripe, it works fine locally on my machine but it doesn't on the live vercel version, logging an axios error(500) another problem i am facing on my local machine is;
when event data is sent from stripe to my db(firestore), i dont get any response even when i console log it?, when i trigger event, stripe successfully process the events but fail to send it to the database. it logs this error;
[ERROR] Failed to POST: Post "http://localhost:3000/api/webhook": context deadline exceeded (Client.Timeout exceeded while awaiting headers)
Additional information
Example
No response
Beta Was this translation helpful? Give feedback.
All reactions