Lemon Squeezy x Supabase x Next JS - Part 4

Create the billing page and render the Pricing Dialog


The complete guide is a work in progress, this fourth part is completed.

This is part four of the guide, for the full guide, start here.

If you are looking for a guide to help you out with setting up your very own Subscription using Next.JS, Lemon Squeezy and Supabase, you have come to the right place. In this first guide, we will look at how you can fetch products from Lemon Squeezy, so that you can populate your pricing table!

This guide is specifically created for Subscriptions with one product and multiple variants. You can see the difference in pricing strategy here.

The goal of this guide is that by the end you will be able to fetch your own products using Next.js 13 App Router.

The pricing page

The original page I used has been cut, as this is specific for my needs. Instead what remains is the most important part to make the guide work.

On top of that, we will finally start seeing some Supabase action!

Let's look at the completed page. At this point, all we have is a centered Button, which opens a dialog.

I specifically created it this way, so you have complete control of your styling, additional page information and layout.

page.tsx
  import { createServerComponentClient } from "@supabase/auth-helpers-nextjs";
  import { cookies } from "next/headers";
  import { redirect } from "next/navigation";
  import Pricing from "./pricing";
 
  export default async function Home() {
    const supabase = createServerComponentClient<Database>({ cookies });
    const {
      data: { session },
    } = await supabase.auth.getSession();
 
    if (!session) {
      redirect("/login");
    }
 
    const { data: companyId } = await supabase
      .from("user_info")
      .select("company_id")
      .eq("id", session?.user.id)
      .single();
 
    const { data: company } = await supabase
      .from("company_info")
      .select()
      .eq("company_id", companyId?.company_id)
      .single();
 
    return (
      <div className="m-auto">
        <Pricing company_id={company.company_id} user={session} />
      </div>
    );
  }

Deep dive into page.tsx

Before we go into more detail, the rest of this part of the guide will be internal linking between different components, how the parameters are linked and why we can't retrieve all information directly.

Imports

  import { createServerComponentClient } from "@supabase/auth-helpers-nextjs";
  import { cookies } from "next/headers";
  import { redirect } from "next/navigation";
  import Pricing from "./pricing";
  1. createServerComponentClient is a function that creates a client that can be used to access the Supabase API.
  2. cookies is a function that returns the cookies that are set on the request.
  3. redirect is a function that redirects the user to a new page.
  4. Pricing is a component that displays the pricing page.

The Page.tsx function

  export default async function Home() {
    // additional content
  }

As we are creating a server component, we are using the export default async function statement. This way we make sure we can also include server components in our page, and this page will be server static generated. Making it possible to prepopulate and be used for ranking in Google!

Supabase Client and Session

  const supabase = createServerComponentClient<Database>({ cookies });
  const {
    data: { session },
  } = await supabase.auth.getSession();
 
  if (!session) {
    redirect("/login");
  }

createServerComponentClient

The createServerComponentClient is a helper that creates an instance of the Supabase client for use in server components, which are used to retrieve the data from the server. We pass the function the object of cookies, which we get from next/headers in next.js app router. This way we can store the session data of the user, and we can retrieve the information based on this session.

supabase.auth.getSession()

This function calls Supabase to check the session object. This information contains information about the user, such as their email, the session id and their JWT token.

Check if the user is logged in

  1. We check if the user is logged in.
  2. If the user is not logged in, we redirect the user to the login page.

Getting the company ID based on the user

  const { data: companyId } = await supabase
    .from("user_info")
    .select("company_id")
    .eq("id", session?.user.id)
    .single();

We get the company ID from the user_info table, by executing this query. Because we run a server page, this function is directly called. Because we can run multiple requests directly, it is advised to change the { data } object to a specific name, using the colon then typing the name. In this example we renamed it to companyId.

  1. We select the company ID from the user info table
  2. We filter the user info table by the user ID we retrieve from the session

Getting the company info

 const { data: company } = await supabase
    .from("company_info")
    .select()
    .eq("company_id", companyId?.company_id)
    .single();

As we have retrieved the company id of the user, we can now alos request the company_info information. In this table I have stored the subscription id, the product id, expiration date of the subscription and much more Lemon Squeezy info inside of Supabase.

With this information available, we can check if the company of this user already has an active subscription.

In this guide we are not using this information, so for now we skip this part.

Returning the Dialog content with the proper values

After the content has been retrieved, we can now render the pricing component. This pricing component is ofcourse linked to the other components. So we create the final step into creating the pricing dialog and sending the user to the correct store variant.

  return (
    <div className="mx-auto">
      <Pricing company_id={company.company_id} user={session} />
    </div>
  );

The dialog trigger (a button) will be displayed on screen.

add subscription supabase nextjs next js lemon squeezy

And if you click on it a functional pricing dialog is shown!

pricing dialog supabase nextjs next js lemon squeezy

What is even better, if you then click on Buy now, you will be directed to the Checkout page of your Lemon Squeezy store. How cool is that?

pricing variants supabase lemon squeezy store

To render the information, we have created 3 components and one page.

Now that we have the first setup completed, let's look how they are related and they make it possible for you to render the data.

As we saw before, we have a combination of three components and one page. Out of these components, we have:

  • 1 Client component
  • 2 Server components

Let's look top down.

  1. page.tsx

On page.tsx, we get the data from Supabase. First we get the session, with the user id from the session we get the company id, and with the company id we get the billing information of the company. This information is then passed to the pricing component, which is rendered on the page.

  1. pricing.tsx

The pricing component, calls the function getProductVariants with the product id that is stored in the environment variables. This component imports two other components:

  • getProductVariants function from ./variants.tsx
  • PricingDialog from ./pricing-dialog.tsx

The pricing-dialog.tsx component is a client component, as the dialog component from shadcn ui is not usable as a server component.

  1. variants.tsx

This page fetches our Lemon Squeezy product variants. Which can later be called by a different component (pricing.tsx) to populate the other component.

  1. pricing-dialog.tsx

As mentioned, the "use client" component that renders the data for us. This component receives all parameters, to be able to render all the information and generate a checkout link based on the rendered variant from Lemon Squeezy:

  • productVariants
  • user
  • company_id
  • store_id

The good news: Users can pay you now

Great, users are able to pay you. However, you haven't registered this in your system. Now they can pay, but they can't use..

Let's fix that in the next part.

Like what you see? Do you prefer to have a ready made app instead of building the integration yourself?

No worries, we have got you covered.

With Supaboost, you will be able to skip at least 30 days development time, by having these features readily available:

  • Auth
  • Lemon Squeezy integration
  • Safe development with Typescript
  • Next.js App Router
  • Supabase SQL scripts
  • And much more.

Get Supaboost

View

Next step

Click here to continue to Webhooks

View