Handling Stripe Subscription Callback with Next.js 14

13 August 2024

Stripe, a robust payment processing platform, provides an excellent solution for handling recurring billing and subscription payments. Integrating Stripe with modern web frameworks like Next.js 14 can streamline subscription management and enhance your application's functionality.

This article provides a detailed guide on how to handle Stripe subscription callbacks using the app router in Next.js 14. We'll cover setting up Stripe webhooks, handling various subscription events, and ensuring your integration is secure and reliable.

Setting Up Stripe Webhooks in Next.js 14

Understanding Webhooks

Webhooks are automated messages sent from apps when something happens. They enable real-time data sharing between applications, making them a cornerstone for managing events such as subscription updates, payment successes, and more.

In the context of Stripe, webhooks allow your application to receive notifications about events in your Stripe account, such as when a customer's subscription is created, updated, or canceled.

Creating a Webhook Endpoint

To handle Stripe webhook events in Next.js 14, you need to set up a dedicated endpoint that Stripe can send event notifications to. Below are the steps to create and configure a webhook endpoint in Next.js 14:

  1. Create a New API Route:

Create a new file in your Next.js project to define the route for your webhook endpoint. For this example, we'll create a route.ts file inside the api/stripe/webhook/ directory.

// src/app/api/stripe/webhook/route.ts
import Stripe from 'stripe';
import { NextApiRequest, NextApiResponse } from 'next';
const stripe = new Stripe(process.env.STRIPE_SECRET_KEY, { apiVersion: '2022-11-15' });

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

    let event;
    try {
      event = stripe.webhooks.constructEvent(req.body, sig, process.env.STRIPE_WEBHOOK_SECRET);
    } catch (err) {
      console.error('Error verifying webhook signature:', err);
      return res.status(400).send(`Webhook Error: ${err.message}`);
    }

    // Handle the event
    switch (event.type) {
      case 'customer.subscription.created':
        // handle subscription created event
        break;
      case 'customer.subscription.updated':
        // handle subscription updated event
        break;
      case 'customer.subscription.deleted':
        // handle subscription deleted event
        break;
      default:
        console.log(`Unhandled event type: ${event.type}`);
    }

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

export const config = { api: { bodyParser: false } };
  1. Configuring the Webhook Endpoint in Stripe:

After setting up the endpoint in your Next.js project, you need to register this endpoint in your Stripe Dashboard. Navigate to the webhooks section and add your endpoint URL (e.g., https://your-domain.com/api/stripe/webhook).

Best Practices for Webhooks

Security Measures

Verifying Webhook Signatures

To ensure that webhook events are sent by Stripe and haven't been tampered with, you need to verify the webhook signatures. Stripe includes a Stripe-Signature header in each webhook, which you can use to verify the payload.

Here’s how you can verify the signature:

import Stripe from 'stripe';
const stripe = new Stripe(process.env.STRIPE_SECRET_KEY);
const endpointSecret = process.env.STRIPE_WEBHOOK_SECRET;

export default async function handler(req, res) {
  const sig = req.headers['stripe-signature'];
  let event;
  try {
    event = stripe.webhooks.constructEvent(req.body, sig, endpointSecret);
  } catch (err) {
    console.error('Error verifying webhook signature:', err);
    return res.status(400).send(`Webhook Error: ${err.message}`);
  }
  // Process the event
}
Logging and Handling Duplicate Events

Log the event IDs you process to prevent handling the same event multiple times. Stripe may send the same event more than once in case of network issues or long response times.

const processedEvents = new Set();
if (processedEvents.has(event.id)) {
  console.log('Duplicate event received:', event.id);
  return res.status(200).send('Event already processed.');
}
processedEvents.add(event.id);

Quick Response

Return a 2xx HTTP status code as quickly as possible to acknowledge receipt of the event. Any complex logic or external API calls should be handled asynchronously after acknowledging the event.

Retry Mechanisms

Stripe retries events for up to three days with exponential back-off if your endpoint does not respond with a 2xx status code. Monitor and handle these retries to ensure all events are processed.

Testing Webhooks Locally

Using Stripe CLI

Stripe CLI is a powerful tool for testing webhooks locally. Follow these steps to set it up:

  1. Install Stripe CLI:

  2. Forward Webhooks to Localhost:

    • Use the following command to forward webhook events to your local endpoint:

      stripe listen --forward-to localhost:3000/api/stripe/webhook
      
  3. Trigger Test Events:

    • Manually trigger events to test your webhook handling logic using commands like:

      stripe trigger customer.subscription.created
      

Using Local Tunneling

Tools like ngrok can create secure tunnels to your local machine, making it easier to test webhooks with a public URL.

ngrok http 3000

Example: Handling a Stripe Subscription Event

Here’s a detailed example of handling a customer.subscription.created event in Next.js 14:

Code Snippet

import Stripe from 'stripe';
import { NextApiRequest, NextApiResponse } from 'next';
import { db, SubscriptionTable } from '@/db'; // Mock ORM setup

const stripe = new Stripe(process.env.STRIPE_SECRET_KEY, { apiVersion: '2022-11-15' });

export default async function handler(req: NextApiRequest, res: NextApiResponse) {
  if (req.method === 'POST') {
    const sig = req.headers['stripe-signature'];
    const payload = await req.text();
    const endpointSecret = process.env.STRIPE_WEBHOOK_SECRET;

    let event: Stripe.Event;
    try {
      event = stripe.webhooks.constructEvent(payload, sig, endpointSecret);
    } catch (err) {
      console.error('Webhook verification failed:', err);
      return res.status(400).send(`Webhook verification failed: ${err.message}`);
    }

    if (event.type === 'customer.subscription.created') {
      const subscription = event.data.object as Stripe.Subscription;
      await db.insert(SubscriptionTable).values({
        id: subscription.id,
        customer: subscription.customer,
        status: subscription.status,
        current_period_end: subscription.current_period_end,
      });
    }

    return res.status(200).send('Webhook received');
  } else {
    res.setHeader('Allow', 'POST');
    res.status(405).end('Method Not Allowed');
  }
}

export const config = { api: { bodyParser: false } };

Explanation

  1. Event Construction: The raw body from the request and signature header are used to construct and verify the Stripe event.

  2. Handling Specific Events: For the customer.subscription.created event, the subscription data is inserted into a database using a mock ORM setup.

  3. Response: A 200 OK response is returned to acknowledge receipt of the event.

Conclusion

Handling Stripe subscription callbacks efficiently is vital for maintaining a smooth subscription experience for your users. By setting up secure and reliable webhooks in Next.js 14, you can automate and manage subscription events effectively.

Get engineers' time back from marketing!

Don't let managing a blog on your site get in the way of your core product.

Wisp empowers your marketing team to create and manage content on your website without consuming more engineering hours.

Get started in few lines of codes.

Choosing a CMS
Related Posts
Integration of Stripe Subscription with Next.js 14 Application

Integration of Stripe Subscription with Next.js 14 Application

Maximize your app's potential with Next.js 14 and Stripe! Build efficient, subscription-based systems with our comprehensive guide. Get started with lightning fast development!

Read Full Story
Comprehensive Guide to Integrating Stripe Billing Customer Portal with Next.js 14

Comprehensive Guide to Integrating Stripe Billing Customer Portal with Next.js 14

Discover how to seamlessly integrate Stripe Billing with Next.js 14! Our guide covers every step with detailed code snippets and best practices.

Read Full Story
Next.js 14 App Router: GET & POST Examples (with TypeScript)

Next.js 14 App Router: GET & POST Examples (with TypeScript)

Ready to master Next.js 14's App Router? Learn to create GET & POST route handlers with ease. Discover practical uses and advanced features using TypeScript. Start coding smarter today!

Read Full Story