Logo
Published on

Integrating Stripe Checkout Mode with Next.js App Router

Authors
  • Name
    Twitter

Hey there! 👋
Since the release of the Next.js App router last year, there have been numerous integrations that require updating. Today we will look into the steps of integrating Stripe Checkout mode with Next.JS App Router.

If you came from Part I, that’s great. Let’s keep going. If not, you can use this guide to assist you in integrating Stripe checkout mode into your application. You will only need to make the necessary changes to your checkout button.

Alright, let’s dive right in!

Requirements

  • The keys required to make this project work will only be available to you if you have a Stripe account. If you don’t already have it, check out their website: www.stripe.com
  • This guide assumes you have a shopping cart set up in your project. If you don’t already have one, check my previous post: Creating a Modal Shopping Cart with Next.js 13 and Stripe
  • Considering the last point, it’s likely that you have a Next.JS 13 project going in your local.

First steps

To install the Stripe package and its dependencies, you can use the following command:

npm install --save stripe @stripe/stripe-js

To configure your project, make sure to have a .env.local file at the root directory and include the necessary Stripe API keys obtained from your Stripe Dashboard.

# Stripe keys
NEXT_PUBLIC_TEST_STRIPE_PUBLISHABLE_KEY={YOUR_STRIPE_PUBLISHABLE_KEY}
STRIPE_TEST_SECRET_KEY={YOUR_STRIPE_SECRET_KEY}

To utilize the keys, it is necessary to initialize a new instance of Stripe. We will create a new config/stripe.ts path. In this file, we will ensure that there’s an instance of our environment variable or raise a new error if it is not found. Please include your Stripe key within the new Stripe constructor instance and specify the API version to ensure compatibility with a specific version of the Stripe API.

import Stripe from "stripe";

if (!process.env.STRIPE_TEST_SECRET_KEY) {
  throw new Error(
    "STRIPE_TEST_SECRET_KEY is missing. Please set the environment variable."
  );
}

const stripe = new Stripe(process.env.STRIPE_TEST_SECRET_KEY, {
  apiVersion: "2023-08-16",
});

export default stripe;

Adding a new route handler

Let’s create a new api/ folder in our src/app/ directory, and inside of it, a checkout_sessions/ folder with a route.ts file. This handler will be responsible to create a Stripe session id, which will contain the cart items and other payment options.

├── app
│ ├── api
│ │ ├── checkout_sessions
│ ├──route.ts
├── config
│ ├── stripe.ts
import {NextRequest, NextResponse} from  "next/server";
import {headers} from  "next/headers";
import {CartItem} from  "@/types/type";
import stripe from  "@/config/stripe";


export  async  function  POST(req: NextRequest, res: NextResponse) {
const headersList = headers();
const {cartDetails} = await req.json();
const  cartDetailsArray: CartItem[] = Object.values(cartDetails) as  CartItem[];

const lineItems = cartDetailsArray.map((item: CartItem) => {
return {
price_data: {
currency: item.currency,
product_data: {
name: item.name,
},
unit_amount: item.price,
},
quantity: item.quantity,
};
});

try {
const session = await stripe.checkout.sessions.create({
payment_method_types: ["card"],
line_items: lineItems,
mode: "payment",
success_url: `${headersList.get("origin")}/thank-you`,
cancel_url: `${headersList.get("origin")}/`,
});

return  NextResponse.json({sessionId: session.id});
} catch (err) {
console.log(err)
return  NextResponse.json({error: "Error creating checkout session"});
}
}

Let’s break our route.ts down:

  1. To handle the request, let’s define a POST function.
  2. The receiving request includes cartDetails in its body from the next step. Then, we will convert it into an array.
  3. In order to align cartDetails with Stripe’s line Items format, we will need to make some adjustments.
  4. Following this, we will initiate a new checkout session, which, if successful, will result in the creation of a session id. You can configure the payment options available in your app. In this step, you have the option to pass a shipping method or specify the allowed countries. You have the flexibility to customize your success and cancel URLs according to your preferences.
  5. If there is a problem, it logs it and sends an error message in response.

Handling checkout button

If you have followed my previous guide, we can refactor our code by extracting the checkout button from the modal.tsx file and placing it in a separate checkout.tsx file. If your shopping cart was set up differently, go to the code for your checkout button and add a new handler. This is how the file looks like:

import {useShoppingCart} from  "use-shopping-cart";
import {loadStripe} from  "@stripe/stripe-js";

export  default  function  CheckoutButton() {
const {cartCount = 0, cartDetails} = useShoppingCart();

const  redirectToCheckout = async () => {
try {
const stripe = await  loadStripe(process.env.NEXT_PUBLIC_TEST_STRIPE_PUBLISHABLE_KEY  as string);

if (!stripe) throw  new  Error('Stripe failed to initialize.');

const checkoutResponse = await  fetch('/api/checkout_sessions', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({cartDetails}),
});

const {sessionId} = await checkoutResponse.json();
const stripeError = await stripe.redirectToCheckout({sessionId});

if (stripeError) {
console.error(stripeError);
}
} catch (error) {
console.error(error);
}
};

return (
<button
onClick={() => cartCount > 0 && redirectToCheckout()}
disabled={cartCount === 0}
className="rounded-md border border-transparent bg-sky-900 px-6 py-3 text-base font-medium text-white shadow-sm hover:bg-sky-700 mr-2 disabled:bg-gray-600">
Checkout
</button>
);
}

To sum up, this code shows how to add a redirect method to Stripe’s checkout mode. First, we utilize the useShoppingCart library hook to obtain the information about our cart. Once we have the information about the cart, we fetch our /api/checkout_sessions handler to start the checkout process.

For this flow to work, the sessionId we get back from our API call is very important. Next, we utilize the sessionId variable together with Stripe’s redirectToCheckout function. If everything is in order, the user will be automatically forwarded to Stripe’s checkout page, where Stripe will take care of processing the payment. After that, you can use your Stripe dashboard to keep an eye on the transaction and handle it.

That's it for today…

Hopefully this guide was useful, but if you run into any problems or want to contribute some ideas, please leave a comment.