Webhooks are essential for keeping your application in sync with Stripe events, such as successful payments, subscription renewals, or disputes. However, because they are initiated by Stripe and arrive at your server, they represent a significant security consideration. Without proper validation, an attacker could potentially send fake webhook events to your application, leading to unintended actions or data corruption. This section will guide you through best practices for handling Stripe webhooks securely in your Next.js application.
The first and most crucial step in securing your webhooks is verifying the origin of each incoming request. Stripe signs every webhook request it sends to your server with a signature. This signature is generated using your webhook's signing secret, which you can find in your Stripe dashboard under Developers > Webhooks. By comparing the signature received with a signature you generate locally using the request body and your signing secret, you can be confident that the request truly came from Stripe and hasn't been tampered with.
import { headers } from 'next/headers';
import { stripe } from '@/lib/stripe'; // Assume this is your Stripe SDK initialization
export async function POST(request) {
const body = await request.text();
const signature = headers().get('stripe-signature');
let event;
try {
event = stripe.webhooks.constructEvent(
body,
signature,
process.env.STRIPE_WEBHOOK_SECRET
);
} catch (err) {
console.error(`Webhook signature verification failed.`, err.message);
return new Response('Webhook signature verification failed.', {
status: 400,
});
}
// 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 customer's order
break;
case 'charge.refunded':
const charge = event.data.object;
console.log(`Charge ${charge.id} was refunded.`);
// Handle refunds
break;
// ... handle other event types
default:
console.log(`Unhandled event type: ${event.type}`);
}
// Return a 200 response to acknowledge receipt of the event
return new Response(null, {
status: 200,
});
}The code snippet above demonstrates how to implement webhook signature verification in a Next.js API route. Key points to note are:
- Reading the raw request body: It's crucial to read the request body as raw text before parsing it, as the signature is calculated based on the raw payload.
- Retrieving the signature: The signature is found in the
stripe-signatureheader. - Using
stripe.webhooks.constructEvent: This Stripe SDK method handles the signature verification process. It takes the raw body, the signature, and your webhook signing secret. - Handling verification failures: If the signature verification fails, it's important to return a 400 Bad Request response to indicate an invalid request. Never proceed with processing the event if verification fails.
- Storing the signing secret securely: Never hardcode your signing secret directly in your code. Use environment variables (e.g.,
process.env.STRIPE_WEBHOOK_SECRET) to store it securely. Ensure this environment variable is properly configured in your deployment environment.