Stripe for Next.js: A Developer's Guide to Seamless Payments

Stripe Webhook Testing and Handling

Section 5

Testing and Deploying Your Stripe-Powered Next.js App

Stripe for Next.js: A Developer's Guide to Seamless PaymentsTesting and Deploying Your Stripe-Powered Next.js App

Webhooks are essential for keeping your Stripe integration in sync with your application. They allow Stripe to send real-time notifications to your server about events, such as successful payments, failed charges, or subscription updates. This section will guide you through testing and handling Stripe webhooks in your Next.js application.

Why are webhooks so important? In an e-commerce scenario, you can't rely solely on the user seeing a 'Payment Successful' page. A webhook confirms the payment on the server-side, enabling you to fulfill the order, grant access to digital goods, or trigger other business logic securely.

During development, you need a way to simulate Stripe sending webhooks to your local Next.js server. Stripe CLI is the recommended tool for this. It allows you to forward events from your Stripe test environment directly to your local machine.

First, ensure you have the Stripe CLI installed. You can find installation instructions on the Stripe website. Once installed, you can log in to your Stripe account using stripe login.

Next, you'll need to set up a webhook endpoint in your Next.js application. This will be a dedicated API route that Stripe can send POST requests to. Let's create a file at pages/api/webhooks.js.

export default async function handler(req, res) {
  if (req.method === 'POST') {
    const payload = req.body;
    const signature = req.headers['stripe-signature'];

    // Verify the webhook signature (crucial for security!)
    // ... verification logic here ...

    const event = payload;

    // Handle the event
    switch (event.type) {
      case 'payment_intent.succeeded':
        const paymentIntent = event.data.object;
        console.log(`PaymentIntent for ${paymentIntent.amount} was successful!`);
        // TODO: Fulfill the order, update database, etc.
        break;
      case 'payment_intent.payment_failed':
        const failedPaymentIntent = event.data.object;
        console.log('Payment failed:', failedPaymentIntent.last_payment_error.message);
        // TODO: Notify the customer, update UI, etc.
        break;
      // ... handle other event types ...
      default:
        console.log(`Unhandled event type ${event.type}`);
    }

    res.status(200).json({ received: true });
  } else {
    res.setHeader('Allow', ['POST']);
    res.status(405).end(`Method ${req.method} Not Allowed`);
  }
}

The most critical part of webhook handling is signature verification. This ensures that the request actually came from Stripe and wasn't tampered with. You'll need your webhook signing secret, which you can find in your Stripe Dashboard under Developers -> Webhooks. It's a good practice to store this secret securely in your environment variables (e.g., .env.local).

import Stripe from 'stripe';

const stripe = new Stripe(process.env.STRIPE_SECRET_KEY);

export default async function handler(req, res) {
  // ... (previous code) ...

  if (req.method === 'POST') {
    const payload = req.body;
    const signature = req.headers['stripe-signature'];

    let verifiedEvent;
    try {
      verifiedEvent = stripe.webhooks.constructEvent(
        payload,
        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}`);
    }

    const event = verifiedEvent;

    // ... (handle the event logic) ...
  }
  // ... (rest of the handler) ...
}
チャプターへ戻る