Next.js Discord

Discord Forum

NextJS and stripe webhook

Unanswered
California pilchard posted this in #help-forum
Open in Discord
Avatar
California pilchardOP
Okay im using Supabase as backend for my nextjs application, I have this route :
import Stripe from 'stripe';

import { stripe } from '@/lib/stripe/stripe';
import {
  upsertProductRecord,
  upsertPriceRecord,
  manageSubscriptionStatusChange,
  deletePriceRecord,
  deleteProductRecord,
} from '@/lib/supabase/supabase-admin';

export async function POST(request: Request) {
  const body = await request.text();
  const sig = request.headers.get('stripe-signature') as string;
  const webhookSecret = process.env.NEXT_PUBLIC_STRIPE_WEBHOOK_SECRET;
  let event: Stripe.Event;
  console.log(`🔔 Webhook received: sig:${sig}, body:${body}, webhookSecret:${webhookSecret}`);
  
  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 });
  }

  if (relevantEvents.has(event.type)) {
    try {
      switch (event.type) {
        case 'product.created':
        case 'product.updated':
          await upsertProductRecord(event.data.object as Stripe.Product);
          break;
        {...}
        default:
          throw new Error('Unhandled relevant event!');
      }
    } catch (error) {
      console.log(error);
      return new Response(
        'Webhook handler failed. View your Next.js function logs.',
        {
          status: 400
        }
      );
    }
  } else {
    return new Response(`Unsupported event type: ${event.type}`, {
      status: 400
    });
  }
  return new Response(JSON.stringify({ received: true }));
}

57 Replies

Avatar
California pilchardOP
Its very new because its was working before idk why but there is the log from stripe :
Webhook Error: No signatures found matching the expected signature for payload. Are you passing the raw request body you received from Stripe? 
Learn more about webhook signing and explore webhook integration examples for various frameworks at https://github.com/stripe/stripe-node#webhook-signing

I try on local (with dev nextjs) and stripe.webhooks.constructEvent seems to work properly
Avatar
uhm i see
the production build is on vercel?
i just read the docs and u need to parse the raw body
cause in your current setup u parse it to text
what happens if u directly pass request.body
Avatar
California pilchardOP
No im using Coolify with the Dockerfile give as example by nextjs
but all Supabase and Stripe examlpe do this
And seems weird if its working on dev mode but not after build in Coolify
Avatar
Image
i checked this
they are using the raw body
idk why the specifying a type but the body is raw
Avatar
California pilchardOP
Using request.body i have this :
The argument of type 'ReadableStream<Uint8Array> | null' is not assignable to the parameter of type 'string | Buffer'.
Cannot assign type 'null' to type 'string | Buffer'.ts(2345)
Avatar
uhh
try coverting it into a buffer
i dont know does nextjs have a helper for that
Avatar
California pilchardOP
But this seems weird because in production mode, I have already a console.log and I can see const body = await request.text(); return something good
even if stripe.webhooks.constructEvent fail
Avatar
yeah thats obviously works
but i think they expect the body as unparsed and not consumed
just raw buffer
i could be wrong
but try that
try this
const rawBody = await buffer(req);
const body = JSON.parse(rawBody.toString());
@California pilchard buffer function is from this lib -> micro
npm i micro
after that u can try passing rawBody directly and if that throws something try it with the parsed json body from the raw body
Avatar
California pilchardOP
I have this error on buffer :
The argument of type 'Request' is not assignable to the parameter of type 'IncomingMessage'. The type 'Request' is missing the following properties from type 'IncomingMessage': aborted, httpVersion, httpVersionMajor, httpVersionMinor, and 50 others.

Maybe I can try updating stripe lib ?
Avatar
where did that happen?
converting to buffer?
Avatar
California pilchardOP
When over buffer in vscode because my request variable was underline in red
Avatar
u used buffer from micro?
Avatar
California pilchardOP
yes
Avatar
can u try disabling typescript for that?
//@ts-ignore
i found a thread talking about this exact issue
Avatar
California pilchardOP
nah cant dodge this :
POST /api/stripe 500 in 345ms
 ⨯ HttpError: Invalid body
    at createError (webpack-internal:///(rsc)/./node_modules/micro/dist/src/lib/index.js:34:17)
    at eval (webpack-internal:///(rsc)/./node_modules/micro/dist/src/lib/index.js:139:43)
    at async POST (webpack-internal:///(rsc)/./src/app/api/stripe/route.ts:25:18)
    at async /Users/loup/Projects/DEV/Recomend/recomend-web-app/node_modules/next/dist/compiled/next-server/app-route.runtime.dev.js:6:53238
    at async e_.execute (/Users/loup/Projects/DEV/Recomend/recomend-web-app/node_modules/next/dist/compiled/next-server/app-route.runtime.dev.js:6:44501)
    at async e_.handle (/Users/loup/Projects/DEV/Recomend/recomend-web-app/node_modules/next/dist/compiled/next-server/app-route.runtime.dev.js:6:54492)
    at async doRender (/Users/loup/Projects/DEV/Recomend/recomend-web-app/node_modules/next/dist/server/base-server.js:1372:42)
    at async cacheEntry.responseCache.get.routeKind (/Users/loup/Projects/DEV/Recomend/recomend-web-app/node_modules/next/dist/server/base-server.js:1594:28)
Avatar
this happened when passing in the whole req?
Avatar
California pilchardOP
but this was for nextjs using page route not app router if I well understood
yes
Avatar
what happens when u just pass the body?
req.body
Avatar
California pilchardOP
⨯ TypeError: Cannot read properties of undefined (reading 'content-type')
    at eval (webpack-internal:///(rsc)/./node_modules/micro/dist/src/lib/index.js:119:29)
    at async POST (webpack-internal:///(rsc)/./src/app/api/stripe/route.ts:25:18)
    at async /Users/loup/Projects/DEV/Recomend/recomend-web-app/node_modules/next/dist/compiled/next-server/app-route.runtime.dev.js:6:53238
    at async e_.execute (/Users/loup/Projects/DEV/Recomend/recomend-web-app/node_modules/next/dist/compiled/next-server/app-route.runtime.dev.js:6:44501)
    at async e_.handle (/Users/loup/Projects/DEV/Recomend/recomend-web-app/node_modules/next/dist/compiled/next-server/app-route.runtime.dev.js:6:54492)
    at async doRender (/Users/loup/Projects/DEV/Recomend/recomend-web-app/node_modules/next/dist/server/base-server.js:1372:42)
    at async cacheEntry.responseCache.get.routeKind (/Users/loup/Projects/DEV/Recomend/recomend-web-app/node_modules/next/dist/server/base-server.js:1594:28)
 
Im gonna try to build image locally and run it in my computer
just to see if its depend of hoster
Avatar
i could help u out via vscode liveshare
alr
Avatar
California pilchardOP
well its working on my computer...
Maybe the problem come from coolify
Avatar
hello i also want to know on stripe can we schedule custom email before the subscription end on stripe webhook?
Avatar
California pilchardOP
sry idk about it, maybe open a help post for ur request
Avatar
okay thanks
Avatar
California pilchardOP
maybe the problem come from :
sig:t=1726852343,v1=54917084b5ffae814c0fff332aa43e90e4a922c4637d5e3a5bc098a23bbe4f4c,v0=a091a14f897e3b23cab124129011c10738480e174c6500c88292627c0a39b2bd

in fact in local i have t, v1 and v0 but when calling api route from production i have only t and v1
Avatar
California pilchardOP
arhh im tired of nextjs ahah so hard to debug
Avatar
California pilchardOP