I'm new to Next.js and I'm currently building an application where I'm trying to handle Stripe webhooks and update user subscription details in my Supabase database. I've set up my endpoint and I'm using the stripe.webhooks.constructEvent() function to verify the event and extract the session object from the event data.
I've been following the Stripe documentation and using the code from this the code from this GitHub repository to understand how to set up my own application.
Here's the relevant part of my code:
import { headers } from "next/headers"
import Stripe from "stripe"
import { env } from "@/env.mjs"
import { db } from "@/lib/db"
import { stripe } from "@/lib/stripe"
// Define an asynchronous function to handle POST requests
export async function POST(req: Request) {
// Extract the raw text of the request body and the Stripe signature from the headers
const body = await req.text()
const signature = headers().get("Stripe-Signature") as string
let event: Stripe.Event
// Construct the event and verify its signature
try {
event = stripe.webhooks.constructEvent(
body,
signature,
env.STRIPE_WEBHOOK_SECRET
)
} catch (error: any) { // Explicitly type error as any
// If the signature verification fails, return a response with a 400 status code
return new Response(`Webhook Error: ${error.message}`, { status: 400 })
}
const session = event.data.object as Stripe.Checkout.Session
if (event.type === "checkout.session.completed") {
// Retrieve the subscription details from Stripe
const subscription = await stripe.subscriptions.retrieve(
session.subscription as string
)
// Update the user's subscription details in the database
await db.user.update({
where: {
id: session?.metadata?.userId,
},
data: {
stripeSubscriptionId: subscription.id,
stripeCustomerId: subscription.customer as string,
stripePriceId: subscription.items.data[0].price.id,
stripeCurrentPeriodEnd: new Date(
subscription.current_period_end * 1000
),
},
})
}
// If the event type is 'invoice.payment_succeeded', handle the event
if (event.type === "invoice.payment_succeeded") {
// Retrieve the subscription details from Stripe
const subscription = await stripe.subscriptions.retrieve(
session.subscription as string
)
// Update the price id and set the new period end in the database
await db.user.update({
where: {
stripeSubscriptionId: subscription.id,
},
data: {
stripePriceId: subscription.items.data[0].price.id,
stripeCurrentPeriodEnd: new Date(
subscription.current_period_end * 1000
),
},
})
}
return new Response(null, { status: 200 })
}
The db and stripe modules are imported from my own library files, which are located in the same project directory (/lib/db and /lib/stripe). The env module is a custom module for handling environment variables.
When I test the webhook with Stripe CLI, I'm getting a 500 error for all events. I've checked and I'm using the correct secret key from Stripe. The strange thing is that the subscription details are being updated in Stripe, but not in my Supabase database.
I've also tried to log the event and error but I'm not seeing any output in my server logs.
As a beginner in Next.js, I'm not sure what I'm doing wrong here. Any help or guidance would be greatly appreciated. Thanks!
In attempting to solve this issue, I've taken several steps:
Verification of Stripe Webhook Secret: I've ensured that the Stripe webhook secret key used in the stripe.webhooks.constructEvent() function is correct. This key is stored in my environment variables and is correctly referenced in my code.
Logging of Events and Errors: I've tried to log the event and any errors that occur during the execution of the webhook handler function. However, I'm not seeing any output in my server logs, which makes it difficult to understand what's causing the 500 error.
Testing with Stripe CLI: I've used the Stripe CLI to send test webhook events to my local server. While I'm receiving a 500 error for all events, I've noticed that the subscription details are being updated in Stripe. This suggests that the stripe.subscriptions.retrieve() function and the subsequent update operation are working as expected.
Code Review and Comparison: I've reviewed my code and compared it with examples from the Stripe documentation and a similar implementation from a GitHub repository. As far as I can tell, my implementation follows the same general structure and uses the same functions.
Despite these steps, I'm still encountering a 500 error when handling Stripe webhook events. I was expecting that after correctly setting up the webhook handler and verifying the event with Stripe, I would be able to extract the session object from the event data and update the user's subscription details in my Supabase database. However, this update operation doesn't seem to be happening, and I'm not sure why.
I'm looking for guidance on best practices for handling Stripe webhooks in a Next.js application, and any suggestions on how to troubleshoot this issue. Specifically, I'm wondering if there's a better way to log events and errors, and if there's something I might be missing in my implementation that's causing the database update to fail.