Integrating Stripe in Next.js 14 - A step-by-step guide

Integrating Stripe with Next.js 14 for payment processing is a powerful combination for building modern, serverless e-commerce applications. In this blog post, I'll guide you through the process of setting up Stripe in a Next.js 14 project, covering the essentials from installation to creating a simple checkout process.

Why Stripe with Next.js 14?

Next.js 14 offers an optimized framework for building fast, server-rendered React applications with a lot of new features and improvements. Stripe, on the other hand, is a comprehensive payment processing platform that simplifies online transactions. Integrating Stripe with Next.js 14 allows developers to leverage the strengths of both platforms to create secure, scalable, and efficient online payment solutions.

Setting Up Your Next.js 14 Project

First, ensure you have Node.js installed on your system. Then, create a new Next.js 14 project if you haven't done so already:

npx create-next-app@latest my-stripe-shop
cd my-stripe-shop

Installing Stripe

Next, add Stripe to your project:

npm install @stripe/stripe-js @stripe/react-stripe-js

These packages include the Stripe JavaScript library and React components that make it easier to work with Stripe in your React application.

Setting Up Stripe

Stripe Account:

If you haven't already, sign up for a Stripe account at stripe.com. Once you're logged in, obtain your API keys from the dashboard.

Environment Variables:

Store your Stripe API keys securely using environment variables. Create a .env.local file in the root of your Next.js project and add your Stripe secret key:

NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY=pk_test_yourPublishableKey
STRIPE_SECRET_KEY=sk_test_yourSecretKey

Make sure to replace pk_test_yourPublishableKey and sk_test_yourSecretKey with your actual Stripe API keys.

Stripe Initialization:

Initialize Stripe in your Next.js application. Create a new file named stripe.js in a utils folder and initialize Stripe with your publishable key:

import { loadStripe } from "@stripe/stripe-js"

export const stripePromise = loadStripe(
  process.env.NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY
)

Creating a Checkout Page

Checkout Component:

Create a new component for your checkout page. Here, you'll use Stripe's useStripe and useElements hooks to create a simple checkout form:

import React from "react"
import { CardElement, useStripe, useElements } from "@stripe/react-stripe-js"

const CheckoutForm = () => {
  const stripe = useStripe()
  const elements = useElements()

  const handleSubmit = async (event) => {
    event.preventDefault()

    if (!stripe || !elements) {
      // Stripe.js has not loaded yet. Make sure to disable form submission until Stripe.js has loaded.
      return
    }

    const cardElement = elements.getElement(CardElement)

    const { error, paymentMethod } = await stripe.createPaymentMethod({
      type: "card",
      card: cardElement
    })

    if (error) {
      console.error(error)
    } else {
      console.log("Payment Method:", paymentMethod)
      // Process the payment here: send paymentMethod.id to your server, create a PaymentIntent, etc.
    }
  }

  return (
    <form onSubmit={handleSubmit}>
      <CardElement />
      <button type="submit" disabled={!stripe}>
        Pay
      </button>
    </form>
  )
}

export default CheckoutForm

Integrate the Checkout Form:

Now, integrate this form into your application, ideally on a dedicated checkout page.

Processing Payments on the Server Side

To process payments, you'll need to create a PaymentIntent on your server. Here's how you can set up an API route in Next.js to handle this:

Create an API Route:

In the pages/api directory, create a new file named create-payment-intent.js.

Implement the PaymentIntent Creation:

Use the Stripe Node library to create a PaymentIntent:

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

export default async function handler(req, res) {
  if (req.method === 'POST') {
    try {
      const { amount } = req.body;

      // Create a PaymentIntent with the order amount and currency
      const paymentIntent = await stripe.paymentIntents.create({
        amount,
        currency: 'usd',
      });

      res.status(200).send(paymentIntent.client_secret);
    } catch (err) {
      res.status(500).json({ statusCode: 500, message: err.message });
    }
  } else {
    res.setHeader('Allow', 'POST');