Back to Blog

Code a Stripe Subscription Model With React and Nextjs

Stripe subscriptions are a great way to monetize your AI tool. We'll show you how to set up a subscription model using React and NextJS

Posted by

A stripe dashboard showing a Monthly Recurring Revenue (MRR) chart

Introduction

Subscriptions are the lifeline of many online businesses. They provide predictable revenue and help shape a loyal user base. For your landing page generator app (AI tool), charging $15 per month, we'll guide you through setting up Stripe Subscriptions using NextJS.
If you want a more in-depth guide on how Stripe Subscriptions work, check out our Stripe Subscriptions Explained article.

Setting Up Stripe Subscriptions

No code yet! Start by configuring your Stripe account:

  1. Register an account at Stripe and sign in.
  2. Add your landing page generator as a new product under the "Products" section in Stripe.
  3. Create a monthly subscription plan for $15 in the "Pricing" section of your product.
  4. Note down the API keys from the "Developers" section for later use.

Creating a Stripe Checkout Session

To handle payments, you'll need a Stripe Checkout session. This calls for some coding:

// install with npm install stripe
import Stripe from 'stripe';
const stripe = new Stripe(process.env.STRIPE_SECRET_KEY);

export default async (req, res) => {
    const session = await stripe.checkout.sessions.create({
        payment_method_types: ['card'],
        line_items: [{
            price: 'price_xxx', // replace with your price ID
            quantity: 1,
        }],
        mode: 'subscription',
        success_url: 'https://yourdomain.com/success',
        cancel_url: 'https://yourdomain.com/cancel',
    });

    res.redirect(303, session.url);
};

Place the above serverless function within your NextJS project to initiate checkout sessions.

Handling Subscriptions on the Frontend

Create a subscription button on your app's frontend to allow users to start the checkout process. When clicked, it should call your serverless function to create a Stripe Checkout session:

// Replace with your actual API endpoint
const checkoutUrl = 'https://api.yourdomain.com/create-checkout-session';

const subscribeUser = async () => {
    const res = await fetch(checkoutUrl);
    const { url } = await res.json();
    window.location.href = url;
};

// Usage in your component
<button onClick={subscribeUser}>Subscribe Now</button>

Handling Subscription Webhook Events

Once your checkout session is complete, you will need to handle subscription changes. Stripe uses webhooks to notify you of subscription events in real time. In your landing page generator, you should listen for these events to activate or deactivate user subscriptions accordingly.

Setting Up Your Webhook Listener

Create a serverless function in your Vercel project to listen for Stripe webhook events:

export default async (req, res) => {
    const stripe = require('stripe')(process.env.STRIPE_SECRET_KEY);
    const endpointSecret = process.env.STRIPE_WEBHOOK_SECRET;

    if (req.method === 'POST') {
        const sig = req.headers['stripe-signature'];
        let event;

        try {
            event = stripe.webhooks.constructEvent(req.body, sig, endpointSecret);
        } 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 'checkout.session.completed':
                const session = event.data.object;
                // Handle checkout session completion, provide access
                break;
            case 'invoice.paid':
              // Continue to provision the subscription as payments keep coming in
                break;
            case 'invoice.payment_failed':
              // The payment failed or the subscription has a problem, alert the user, or revoke access
                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');
        return res.status(405).end('Method Not Allowed');
    }
};

Activating and Deactivating User Subscriptions

When handling these webhook events, you'll want to update user's subscription status in your own system. For example:

// pseudo-code: activate or deactivate user based on Stripe webhook event
function manageUserSubscription(event) {
    if (event.type === 'checkout.session.completed') {
        // Find user by checkout session ID or customer ID
        // Activate their subscription in your system
    } else if (event.type === 'customer.subscription.deleted' || event.type === 'invoice.payment_failed') {
        // Find user and deactivate subscription in your system
    }
    // Other webhook events can be handled similarly
}

Remember to protect your webhook endpoint. Only Stripe should be calling this function, so verify the event with stripe.webhooks.constructEvent as shown above. Also, ensure that your Stripe secret and endpoint secrets are stored securely and not hardcoded into your codebase.

By setting up webhook handling properly, you ensure that your application remains in sync with the reality of your users' subscription status on Stripe.

Cancellation and Refunds

Provide users the ability to cancel their subscriptions from within your app. Tie this front-end feature to a backend operation that alters the subscription status in Stripe:

// Serverless function to cancel subscription
export default async (req, res) => {
    const { subscriptionId } = req.body;
    const deleted = await stripe.subscriptions.del(subscriptionId);
    res.json({ confirmed: deleted });
};

Remember to clearly define your refund policy and execute refunds through the Stripe API or Dashboard as needed.

Summary

Throughout this guide, we have encapsulated how to utilize Stripe Subscriptions for a $15 monthly landing page generator.
Key takeaways include setting up subscriptions, leveraging Stripe for automated billing, responding to payment events with webhooks, and securely adapting to subscription changes via a Next.js serverless function.