Published on

How to Integrate Stripe into Next.js 15 in Under 30 Minutes

Integrating Stripe into a Next.js 15 application allows you to securely process credit card payments in under 30 minutes. By using the Stripe Node.js SDK (Software Development Kit) and the Stripe Checkout system, you can handle subscriptions or one-time purchases without ever storing sensitive card data on your own servers. This approach ensures your application remains PCI compliant (a set of security standards designed to ensure that all companies that accept, process, store or transmit credit card information maintain a secure environment).

Why is Stripe the best choice for Next.js 15?

Stripe provides a pre-built payment page called Checkout that handles the complex parts of the transaction for you. It automatically manages mobile responsiveness, international currency conversion, and fraud detection. Since Next.js 15 supports Server Components (code that runs on the server to improve performance), you can securely initialize payment sessions without exposing your secret keys to the browser.

Using a hosted checkout page also reduces your legal liability. Because the user enters their card details on Stripe’s domain, your website never touches the raw credit card numbers. This separation makes it much easier for beginners to build a professional-grade store without deep security expertise.

What do you need to get started?

Before writing any code, you must set up your environment with the correct tools. Ensure you are using a modern development environment to avoid compatibility issues with the latest AI-driven workflows.

What You'll Need:

  • A Stripe Account (Start in "Test Mode").
  • Node.js 20 or higher installed on your computer.
  • A Next.js 15 project (Created via npx create-next-app@latest).
  • Basic knowledge of TypeScript or JavaScript.

Once your Stripe account is ready, locate your API Keys in the Developers section of the dashboard. You will need the "Publishable Key" (starts with pk_test) and the "Secret Key" (starts with sk_test).

How do you install the required libraries?

Open your terminal in your project root folder. You need to install two specific packages to handle the frontend and backend logic.

Run this command:

npm install stripe @stripe/stripe-js

The stripe package is the official library for server-side operations, like creating a price. The @stripe/stripe-js package is used on the client side (the browser) to redirect your customers to the secure payment page.

How do you secure your API keys?

You must never hard-code your Secret Key into your files, as this would allow anyone to see it in your GitHub repository. Instead, use an .env.local file (a hidden file used to store sensitive configuration variables).

Create a file named .env.local in your root directory and add:

NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY=pk_test_your_key_here
STRIPE_SECRET_KEY=sk_test_your_key_here

The NEXT_PUBLIC_ prefix allows the browser to see the publishable key. The secret key does not have this prefix, keeping it safe and accessible only to your server.

How do you create the Checkout API route?

In Next.js 15, we use Route Handlers (scripts that handle HTTP requests like POST or GET) to talk to Stripe. Create a new folder and file at app/api/checkout/route.ts.

Add the following code to create a payment session:

import { NextResponse } from 'next/server';
import Stripe from 'stripe';

// Initialize Stripe with your secret key
const stripe = new Stripe(process.env.STRIPE_SECRET_KEY!, {
  apiVersion: '2024-12-18.acacia', // Use the latest stable API version
});

export async function POST() {
  try {
    // Create a Checkout Session with a test product
    const session = await stripe.checkout.sessions.create({
      payment_method_types: ['card'],
      line_items: [
        {
          price_data: {
            currency: 'usd',
            product_data: { name: 'Digital Starter Kit' },
            unit_amount: 2000, // Price in cents ($20.00)
          },
          quantity: 1,
        },
      ],
      mode: 'payment',
      success_url: `${process.env.NEXT_PUBLIC_BASE_URL}/success`,
      cancel_url: `${process.env.NEXT_PUBLIC_BASE_URL}/cancel`,
    });

    return NextResponse.json({ sessionId: session.id });
  } catch (err) {
    return NextResponse.json({ error: 'Error creating session' }, { status: 500 });
  }
}

This code tells Stripe exactly what the customer is buying and where to send them after the payment is finished. Note that unit_amount is always in the smallest currency unit, so 2000 equals 20 dollars.

How do you build the Checkout button?

The checkout button connects your user interface to the API route we just created. You need to create a Client Component (a file that allows user interaction like clicks) to handle the redirect.

Create a file called components/CheckoutButton.tsx:

'use client'; // This tells Next.js this is a client-side component

import { loadStripe } from '@stripe/stripe-js';

// Initialize Stripe outside the component to prevent re-loading
const stripePromise = loadStripe(process.env.NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY!);

export default function CheckoutButton() {
  const handleCheckout = async () => {
    // Step 1: Call your API to create a session
    const response = await fetch('/api/checkout', { method: 'POST' });
    const { sessionId } = await response.json();

    // Step 2: Redirect to Stripe Checkout
    const stripe = await stripePromise;
    await stripe?.redirectToCheckout({ sessionId });
  };

  return (
    <button 
      onClick={handleCheckout}
      className="bg-blue-600 text-white px-6 py-2 rounded-lg"
    >
      Buy Now
    </button>
  );
}

When the user clicks this button, they will momentarily leave your site and land on Stripe's high-security payment page. Once the payment is confirmed, Stripe sends them back to the success_url you defined earlier.

What are the common mistakes to avoid?

One frequent error is forgetting to set the NEXT_PUBLIC_BASE_URL in your environment variables. During local development, this is usually http://localhost:3000. If this is missing, Stripe won't know where to send the user after a successful purchase.

Another common pitfall is using the Secret Key on the frontend. We've found that beginners often accidentally prefix their Secret Key with NEXT_PUBLIC_, which exposes it to the public web. Always double-check that your STRIPE_SECRET_KEY remains private to the server environment.

Finally, remember that Stripe's unit_amount is an integer. If you want to charge $19.99, you must pass 1999 to the API. Passing a decimal like 19.99 will cause the API request to fail with a validation error.

How do you test the integration?

Stripe provides special test card numbers so you don't have to use your real money during development. The most common test card is 4242 4242 4242 4242.

  1. Run your app using npm run dev.
  2. Click your "Buy Now" button.
  3. Enter the 4242 test card number, any future expiry date, and any 3-digit CVC code.
  4. Click pay and verify that you are redirected back to your success page.

You can view all these test transactions in your Stripe Dashboard by toggling the "Test Mode" switch. This allows you to verify that the payments are reaching your account correctly before you go live.

Next Steps

Now that you have a basic payment flow, you might want to explore Webhooks (automated messages sent by Stripe when an event happens). Webhooks allow your server to know exactly when a payment is finished so you can fulfill an order or send a confirmation email. You can also look into the Stripe Customer Portal, which lets users manage their own subscriptions and billing info without you writing extra code.

For detailed guides, visit the official Next.js documentation.


Read the Next.js Documentation