In the world of e-commerce, real-time updates are crucial. When a customer makes a purchase, their payment status changes, and you need to know about it instantly to fulfill their order, send notifications, or update your database. Stripe's webhooks are the magical mechanism that makes this possible. They are a set of automated messages sent from Stripe to your application whenever specific events occur in your Stripe account. This section will guide you through setting up your very own webhook endpoint within your Next.js application.
A webhook endpoint is essentially a URL in your application that Stripe can send HTTP POST requests to. When a relevant event happens in Stripe (like a successful payment, a failed charge, or a subscription renewal), Stripe will send a payload containing all the details of that event to this URL. Your Next.js application will then receive this payload and process it accordingly.
For our Next.js application, we'll create a dynamic API route that will serve as our webhook endpoint. This allows Stripe to send requests to a specific URL that we can listen to. The common practice is to create a file like pages/api/webhooks/[stripe].js or app/api/webhooks/route.js (for App Router) to handle these events.
import { buffer } from 'micro';
import Stripe from 'stripe';
const stripe = new Stripe(process.env.STRIPE_SECRET_KEY);
export const config = {
api: {
bodyParser: false,
},
};
const webhookHandler = async (req, res) => {
const buf = await buffer(req);
const signature = req.headers['stripe-signature'];
let event;
try {
event = stripe.webhooks.constructEvent(
buf,
signature,
process.env.STRIPE_WEBHOOK_SECRET
);
} catch (err) {
console.error('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;
console.log(`PaymentIntent for ${paymentIntent.amount} was successful!`);
// Fulfill the order or update database
break;
case 'payment_intent.payment_failed':
const failedPaymentIntent = event.data.object;
console.log('PaymentIntent failed:', failedPaymentIntent.last_payment_error.message);
// Notify customer or take other action
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 });
};
export default webhookHandler;Let's break down the code above:
- Import necessary modules: We import
bufferfrommicroto read the raw request body and theStripeSDK. - Initialize Stripe: We instantiate the Stripe SDK using your
STRIPE_SECRET_KEYfrom environment variables. - Disable Body Parser:
export const config = { api: { bodyParser: false } };is crucial. Stripe sends its webhook payloads as raw buffers, and the default Next.js API route body parser would interfere with this. By disabling it, we can manually handle the request body. - Get the Raw Body and Signature: We use
buffer(req)to get the raw request body and extract thestripe-signatureheader. - Verify the Signature: This is a critical security step. Stripe signs its webhook requests. We use
stripe.webhooks.constructEvent()to verify that the request actually came from Stripe and hasn't been tampered with. This function takes the raw body, the signature, and your webhook signing secret (which you'll get from your Stripe dashboard and store in your.envfile asSTRIPE_WEBHOOK_SECRET). If the signature verification fails, we send a 400 error. - Handle Event Types: The
event.typeproperty tells us what kind of event occurred. We use aswitchstatement to handle different event types. In this example, we're handlingpayment_intent.succeededandpayment_intent.payment_failed. You can find a comprehensive list of supported event types in the Stripe documentation. - Process the Event Data: Inside each
case,event.data.objectcontains the relevant Stripe object (e.g., aPaymentIntentobject). You can then access its properties to perform actions like fulfilling orders, updating your database, or sending email notifications. - Acknowledge Receipt: Finally,
res.json({ received: true });sends a 200 OK response back to Stripe. This tells Stripe that you successfully received the webhook. If Stripe doesn't receive a 200 response, it will retry sending the we
Before you can test this, you'll need to obtain your Stripe webhook signing secret. You can find this by navigating to your Stripe Dashboard, then to 'Developers' -> 'Webhooks'. Click on 'Add endpoint', enter the URL where your webhook will be exposed (we'll discuss this in the next step), and select the events you want to listen to. Stripe will then provide you with a signing secret. Keep this secret secure and never commit it directly into your code. Use environment variables.
graph TD
A[Stripe Event Occurs] --> B{Stripe Sends Webhook Request};
B --> C[Your Next.js App];
C --> D{API Route /api/webhooks/[stripe].js};
D --> E[Read Raw Request Body];
D --> F[Extract Stripe-Signature Header];
E & F --> G[Verify Signature using Stripe SDK and Secret];
G -- Valid --> H[Identify Event Type (e.g., payment_intent.succeeded)];
G -- Invalid --> I[Return 400 Error];
H --> J[Process Event Data (e.g., Fulfill Order)];
J --> K[Send 200 OK Response to Stripe];
K --> L[Stripe Acknowledges Receipt];
The diagram above illustrates the flow of a webhook request from Stripe to your Next.js application, including the crucial signature verification step.