Stripe Webhooks
Learn how to handle Stripe events and keep your application in sync with Stripe.
Webhook Setup
Create a webhook endpoint to receive Stripe events:
// app/api/webhooks/stripe/route.tsimport { headers } from "next/headers";import { stripe } from "@/lib/stripe/client";export async function POST(request: Request) {const body = await request.text();const signature = headers().get("stripe-signature")!;try {const event = stripe.webhooks.constructEvent(body,signature,process.env.STRIPE_WEBHOOK_SECRET!);// Handle the eventswitch (event.type) {case "customer.subscription.created":await handleSubscriptionCreated(event.data.object);break;case "customer.subscription.updated":await handleSubscriptionUpdated(event.data.object);break;case "customer.subscription.deleted":await handleSubscriptionDeleted(event.data.object);break;// Add more event handlers as needed}return new Response(null, { status: 200 });} catch (error) {console.error("Error handling webhook:", error);return new Response(JSON.stringify({ error: "Webhook handler failed" }),{ status: 400 });}}
Event Handlers
Implement handlers for different Stripe events:
// Handle subscription createdasync function handleSubscriptionCreated(subscription: Stripe.Subscription) {await db.subscription.create({data: {id: subscription.id,userId: subscription.metadata.userId,status: subscription.status,priceId: subscription.items.data[0].price.id,currentPeriodEnd: new Date(subscription.current_period_end * 1000),cancelAtPeriodEnd: subscription.cancel_at_period_end,},});}// Handle subscription updatedasync function handleSubscriptionUpdated(subscription: Stripe.Subscription) {await db.subscription.update({where: { id: subscription.id },data: {status: subscription.status,priceId: subscription.items.data[0].price.id,currentPeriodEnd: new Date(subscription.current_period_end * 1000),cancelAtPeriodEnd: subscription.cancel_at_period_end,},});}// Handle subscription deletedasync function handleSubscriptionDeleted(subscription: Stripe.Subscription) {await db.subscription.update({where: { id: subscription.id },data: {status: subscription.status,currentPeriodEnd: new Date(subscription.current_period_end * 1000),},});}
Testing Webhooks
Use the Stripe CLI to test webhooks locally:
# Install Stripe CLIbrew install stripe/stripe-cli/stripe# Login to your Stripe accountstripe login# Forward webhooks to your local serverstripe listen --forward-to localhost:3000/api/webhooks/stripe# Trigger test eventsstripe trigger payment_intent.succeededstripe trigger customer.subscription.createdstripe trigger customer.subscription.updated
Important Events
Subscription Events
- customer.subscription.created
- customer.subscription.updated
- customer.subscription.deleted
- customer.subscription.trial_will_end
Payment Events
- payment_intent.succeeded
- payment_intent.payment_failed
- invoice.paid
- invoice.payment_failed
Best Practices
- Always verify webhook signatures
- Handle events idempotently
- Implement proper error handling
- Log webhook events for debugging
- Set up webhook monitoring