Lemon Squeezy and Next.js 13: A Detailed Guide to Effortless Payment Integration

Discover how to integrate payments into a Next.js SaaS App using Lemon Squeezy. Our guide covers creating API keys, managing subscriptions, handling webhooks, and storing updates. Learn to build a robust subscription setup with this powerful tech stack.

Lemon Squeezy and Next.js 13: A Detailed Guide to Effortless Payment Integration
Lemon Squeezy and Next.js 13: A Detailed Guide to Effortless Payment Integration

Dive into the power of Lemon Squeezy as we take a journey through setting up a fully functional SaaS App with subscriptions using Next.js App Directory. In this hands-on tutorial, you'll learn how to create an API key, display products in a pricing table using TailwindUI, and manage subscriptions with Lemon Squeezy.

We'll also cover how to handle Lemon Squeezy webhook events, using zod for input parsing, and how to store subscription updates in a Postgres database using drizzle ORM. Additionally, you'll learn how to synchronize product data between Lemon Squeezy and your own database, and manage user subscriptions using Vercel's server actions.

By the end of this guide, you'll be equipped with the skills to create a robust subscription setup, leveraging Lemon Squeezy's functionalities and a powerful tech stack. Let's get started!

Setting up Lemon Squeezy 🍋

As we walk through this process, ensure you've enabled the test mode - this allows us to experiment freely without the risk of errors.

In order to get started you should have created a store in Lemon Squeezy as well as some products and variants. In this tutorial I will focus on subscriptions, but it should work equally well for other products.

Lemon Squeezy Product Details View

Next, let's generate an API Key at https://app.lemonsqueezy.com/settings/api to connect to our store:

Lemon Squeezy API Key

Add this as an environment variable to your Next.js project:


Creating a Dynamic Pricing Table with Product Info

Now that we've laid the groundwork, our next step involves fetching all the product and variant information via API and presenting it in a pricing table. I am a huge fan of TailwindUI so I picked one of their pricing tables from here. This is what our final pricing table will look like:

Pricing table fetching data from Lemon Squeezy for BacklinkGPT.com

First, we need to fetch all the products with the getProductVariants function, which also has the createHeaders and createRequestOptions utility functions to build out the request:

Furthermore, with the help of GPT4 I also quickly created a zod model SLemonSqueezyRequest and the corresponding type TLemonSqueezyRequest to help me parsing the API output and enhanced type safety and auto-completion functionality with the data:

With the function and zod models ready, I created a pricing.tsx component to fetch all the data, which I then pass to the pricing table. One more important convenience function I created is the createCheckoutLink function to generate the hosted checkout link.

Finally, I ran into a small problem when I tried to show the variant description with checkmarks in my pricing table:

Lemon Squeezy Variant Description
Plan with Lemon Squeezy Product Fetch

To do this, I used the JSDOM parser to pull out all the feature elements:

{productVariants.data.map((variant, index) => {
  const dom = new JSDOM(variant.attributes.description)
  const document = dom.window.document

  // the first paragraph becomes the description
  const description = document.querySelector('p')?.textContent

  // the li elements become the features
  const features = Array.from(document.querySelectorAll('ul li p')).map(
    (li) => (li as HTMLElement).textContent,

  return (
    // Rendering Logic...

JSDOM Parser

When you're satisfied with your pricing table in the dev environment, it's easy to move the products from test mode to live mode. All you need to do is create a new API key for the live environment, add it to your environment variables, and you're done with the pricing table! 🥳

Lemon Squeezy Copy to Live Mode

Listening to Subscription Webhook Events

Now that the pricing table is ready, we need to know about any new subscription updates in our app to manage access and such. To do this, we can create a Route Handler. It listens for certain Lemon Squeezy Webhook Requests and saves the data in our database. Then, we can use this info to develop any logic we want.

In the example below, I use zod for input parsing and drizzle ORM to create the tables and store everything in my Postgres Neon Database.