Stripe sends notifications about events occurring in your account via webhooks. These events are crucial for keeping your application's state synchronized with Stripe's, enabling real-time updates for your users and backend processes. In this section, we'll explore some of the most common Stripe events and how to handle them effectively in your Next.js application.
The fundamental principle is to create an API route in your Next.js application that acts as your webhook endpoint. This endpoint will receive POST requests from Stripe containing event data. It's vital to verify the authenticity of these requests to prevent malicious actors from triggering actions in your system.
import { buffer } from 'micro';
import Stripe from 'stripe';
const stripe = new Stripe(process.env.STRIPE_SECRET_KEY);
export default async (req, res) => {
if (req.method === 'POST') {
const buf = await buffer(req);
const sig = req.headers['stripe-signature'];
let event;
try {
event = stripe.webhooks.constructEvent(buf, sig, process.env.STRIPE_WEBHOOK_SECRET);
} catch (err) {
console.log(`Webhook signature verification failed.`, err.message);
return res.status(400).send(`Webhook Error: ${err.message}`);
}
// Handle the event
switch (event.type) {
case 'payment_intent.succeeded':
const paymentIntent = event.data.object;
// Fulfill the order or provide access to the service
console.log(`PaymentIntent for ${paymentIntent.amount} was successful!`);
break;
case 'payment_method.attached':
const paymentMethod = event.data.object;
// Update customer payment methods, etc.
console.log(`PaymentMethod ${paymentMethod.id} was attached to a Customer!`);
break;
// ... handle other event types
default:
console.log(`Unhandled event type ${event.type}`);
}
// Return a 200 response to acknowledge receipt of the event
res.json({ received: true });
} else {
res.setHeader('Allow', 'POST');
res.status(405).end('Method Not Allowed');
}
};
export const config = {
api: {
bodyParser: false
}
};Let's break down the key events you'll likely encounter and how to handle them:
payment_intent.succeeded:
This is one of the most important events. It fires when a customer successfully pays for an order. In your webhook handler, you'll want to update your database to mark the order as paid, grant the user access to the purchased content or service, and potentially send confirmation emails.
case 'payment_intent.succeeded':
const paymentIntent = event.data.object;
// Find your order in the database using paymentIntent.id or metadata
// Update order status to 'paid'
// Grant access to the user
console.log(`PaymentIntent ${paymentIntent.id} succeeded.`);
break;checkout.session.completed:
This event is triggered when a customer completes the checkout process using Stripe Checkout. It's useful for scenarios where you're using Stripe Checkout as your primary payment interface. You'll often use this to fulfill orders, similar to payment_intent.succeeded.
case 'checkout.session.completed':
const session = event.data.object;
// If the session is in live mode, fulfill the purchase
if (session.payment_status === 'paid') {
// Retrieve the associated order/customer details from metadata
// Fulfill the order
console.log(`Checkout session ${session.id} completed and paid.`);
} else {
console.log(`Checkout session ${session.id} completed but not paid.`);
}
break;customer.subscription.created:
This event fires when a new subscription is created for a customer. You'll want to record the subscription details in your database, link it to the customer, and potentially provision access to premium features.
case 'customer.subscription.created':
const subscription = event.data.object;
// Update or create a subscription record for the customer in your database
console.log(`Subscription ${subscription.id} created for customer ${subscription.customer}.`);
break;customer.subscription.updated:
This event is sent when a subscription is modified (e.g., plan change, quantity update). You'll need to update your internal subscription records to reflect these changes.
case 'customer.subscription.updated':
const updatedSubscription = event.data.object;
// Update the subscription record in your database with new details
console.log(`Subscription ${updatedSubscription.id} updated.`);
break;customer.subscription.deleted:
This event is crucial for handling subscription cancellations. When a subscription is canceled (either by the customer or through expiration/non-payment), this event fires. You'll need to revoke access to premium features and update your database accordingly.
case 'customer.subscription.deleted':
const deletedSubscription = event.data.object;
// Find the subscription in your database and mark it as canceled
// Revoke access to premium features
console.log(`Subscription ${deletedSubscription.id} deleted.`);
break;invoice.payment_failed:
This event signals that an invoice payment has failed. This is important for recurring payments. You might want to notify the customer about the failed payment and prompt them to update their payment method.
case 'invoice.payment_failed':
const invoice = event.data.object;
// Notify the customer about the failed payment
// Potentially retry payment or prompt for update
console.log(`Invoice ${invoice.id} payment failed.`);
break;Beyond these common events, Stripe offers a comprehensive list of webhooks for various scenarios, including refunds, disputes, and more. It's essential to consult the Stripe API documentation for a complete list and to determine which events are relevant to your application's logic. Remember to always handle webhook events idempotently to avoid duplicate processing.
graph TD;
A[Stripe Server] --> B{Trigger Event (e.g., Payment Success)};
B --> C[Stripe Webhook System];
C --> D[Next.js App Webhook Endpoint];
D --> E{Verify Signature};
E -- Valid --> F{Process Event Data};
E -- Invalid --> G[Return 400 Error];
F --> H[Update Database/Perform Actions];
H --> I[Return 200 OK];