> For the complete documentation index, see [llms.txt](https://developer.paddle.com/llms.txt).

# Build and deploy Next.js app with Vercel and Supabase

Get a step-by-step overview of how to build a Next.js app with Paddle Billing, including a localized pricing page, integrated inline checkout, and screens for customers to manage their payments.

---

This tutorial walks through deploying the Paddle Next.js SaaS starter kit to Vercel, hooking it up to your Paddle account, and taking a test payment. By the end, you'll have a working three-tier subscription app integrated with Supabase for auth and Paddle webhooks for syncing customer data.

For a quick overview of what's in the kit before you dive in, see the [Next.js starter kit reference](https://developer.paddle.com/sdks/starter-kits/nextjs.md).

[Check out the live demo of the starter kit to explore how it works](https://paddle-billing.vercel.app/)

By the end of this tutorial you'll have:

- Set up Paddle Billing in sandbox.
- Created a notification destination for syncing data.
- Created products and prices for a SaaS app.
- Deployed a Next.js app to Vercel.
- Taken a test payment.
- Learned how to transition from sandbox to live.

## Before you begin

### Sign up for Paddle

Paddle is a complete merchant-of-record platform for modern software businesses. Our API-first platform takes care of payments, localization, and subscription management for you.

Sign up for a sandbox account for this tutorial at [sandbox-login.paddle.com/signup](https://sandbox-login.paddle.com/signup). You can transition to a live account later when you're ready to sell.

{% callout type="info" %}
Live accounts require account verification before you can launch a checkout or sell on the Paddle platform.
{% /callout %}

### Sign up for Vercel and Supabase

- [Vercel](https://vercel.com/) hosts and deploys web apps using serverless infrastructure, designed for [Next.js](https://nextjs.org/).
- [Supabase](https://supabase.com/) provides databases, authentication, and other backend features. The starter kit uses Supabase for user management and for syncing customer data with Paddle via webhooks.

You'll also need a Git provider to store your code. Vercel's deploy flow walks you through setting up an account with [GitHub](https://github.com/) (recommended), [GitLab](https://gitlab.com/), or [Bitbucket](https://bitbucket.org/).

### Set up your local environment

To work on the starter kit locally, you'll need an IDE like [Visual Studio Code](https://code.visualstudio.com/) plus:

- [Node.js](https://nodejs.org/en/download/package-manager/current) v20 or later
- [npm](https://www.npmjs.com/), [Yarn](https://yarnpkg.com/), or [pnpm](https://pnpm.io/)

You don't need a local environment to get a working demo on Vercel — come back to this later when you're ready to start building.

## Overview

Create and deploy a Next.js app integrated with Paddle Billing in four steps:

1. [**Start deploy to Vercel**](#deploy-vercel)  
   Clone the repo, integrate with Supabase, configure Paddle variables, and deploy.
2. [**Set up your catalog**](#create-catalog)  
   Create products and prices in Paddle, then update your app to use them.
3. [**Add your website and test**](#test-domain-approval)  
   Add your website to Paddle and take a test payment.
4. [**Build your app, then transition to live**](#go-live)  
   Build on top of the starter kit, then switch to a live account.

## Start deploy to Vercel {% step=true %}

Deploy the starter kit to Vercel to create a project ready to configure:

[Deploy Paddle.js SaaS starter kit to Vercel](https://vercel.com/new/clone?repository-url=https%3A%2F%2Fgithub.com%2FPaddleHQ%2Fpaddle-nextjs-starter-kit&env=PADDLE_API_KEY,PADDLE_NOTIFICATION_WEBHOOK_SECRET,NEXT_PUBLIC_PADDLE_ENV,NEXT_PUBLIC_PADDLE_CLIENT_TOKEN&integration-ids=oac_VqOgBHqhEoFTPzGkPd7L0iH6&external-id=https%3A%2F%2Fgithub.com%2FPaddleHQ%2Fpaddle-nextjs-starter-kit%2Ftree%2Fmain)

### Create Git repo

First, create a clone of the starter kit repo. This creates a copy of the code in a repo under your Git provider, so you can build your app on top of the project.

Click **Continue with GitHub**, **Continue with GitLab**, or **Continue with Bitbucket** to connect your Git provider to Vercel, then enter a name for your repo.

The repo name becomes the name of your project in Vercel and is used for deploy preview URLs. If the name is taken, Vercel appends characters to your project name when creating the URL.

### Integrate with Supabase

Next, click **Add** to walk through integrating with Supabase.

Give your project any name. We recommend using a password manager to generate and store a secure password.

Make sure **Create sample tables with seed data** is checked. This creates tables in Supabase to store customer and subscription data for the app.

### Configure Paddle variables

Supply variables so the app can interact with your Paddle account.

| Variable                              | Purpose                                                                                                                               |
| ------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------- |
| `PADDLE_API_KEY`                      | API key for backend interaction — syncing customer and subscription data with Supabase.                                               |
| `NEXT_PUBLIC_PADDLE_CLIENT_TOKEN`     | Client-side token for frontend interaction — localized prices and opening checkouts.                                                  |
| `PADDLE_NOTIFICATION_WEBHOOK_SECRET`  | Secret key for verifying that webhooks came from Paddle and haven't been tampered with.                                               |
| `NEXT_PUBLIC_PADDLE_ENV`              | `sandbox` for sandbox accounts; `production` for live accounts.                                                                       |

#### Get an API key and client-side token

[Client-side tokens](https://developer.paddle.com/paddle-js/client-side-tokens.md) and [API keys](https://developer.paddle.com/api-reference/about/api-keys.md) are used for authentication. Even if you already have them, create new ones for this app.

API keys need [permissions](https://developer.paddle.com/api-reference/about/permissions.md) to perform actions. For this app, you need **Subscription: Write** to read and cancel subscriptions, and **Transaction: Read** to access transactions.

{% instruction-steps %}

1. Go to **Paddle > Developer tools > Authentication**.
2. Click {% mock-button icon="carbon:add" %}New API key.
3. Fill in the details, then select the **Subscription: Write** permission.
4. Click Save, then Copy key.
5. Paste your key as `PADDLE_API_KEY` on the Vercel deploy screen.
6. On the authentication screen in Paddle, click the **Client-side tokens** tab.
7. Click New client-side token.
8. Give the token a name and description.
9. Click Save, then Copy key.
10. Paste the token as `NEXT_PUBLIC_PADDLE_CLIENT_TOKEN`.

{% /instruction-steps %}

{% /dashboard-instructions %}

As you extend your app, your API key may need more permissions. You can [edit the API key](https://developer.paddle.com/api-reference/about/api-keys#edit-api-key.md) to add them.

{% callout type="warning" %}
Treat your API key like a password. Keep it safe and never share it with apps or people you don't trust.
{% /callout %}

#### Create a webhook destination

Paddle sends webhooks when something important happens in your account — a customer is updated, a new subscription is created, and so on. The starter kit uses webhooks to keep the app in sync with Paddle.

{% instruction-steps %}

1. Go to **Paddle > Developer tools > Notifications**.
2. Click {% mock-button icon="carbon:add" %}New destination.
3. Give the destination a name.
4. Keep notification type as **webhook** (the default).
5. Enter `https://<PROJECTNAME>.vercel.app/api/webhook` as the URL, replacing `<PROJECTNAME>` with your Vercel project name.
6. Check **Select all events**.
7. Click Save destination.
8. From the destinations list, click  next to your destination, then Edit destination.
9. Copy the secret key and paste it as `PADDLE_NOTIFICATION_WEBHOOK_SECRET`.

{% /instruction-steps %}

{% /dashboard-instructions %}

See [create a notification destination](https://developer.paddle.com/webhooks/notification-destinations.md) for more.

#### Set your environment

For `NEXT_PUBLIC_PADDLE_ENV`:

- `sandbox` for a sandbox account.
- `production` for a live account.

{% callout type="info" %}
We recommend sandbox for this tutorial. Live accounts must be approved by Paddle before you can open checkouts.
{% /callout %}

### Review and deploy

Review your settings, then click **Deploy**. Wait for Vercel to build.

Your deploy preview won't work end-to-end yet — the pricing page doesn't display prices and the get-started buttons don't open checkouts. You'll fix these in the next step.

## Set up your product catalog {% step=true %}

Specify how products in your app map to products in Paddle.

### Model your pricing

A product in Paddle has:

- A product entity describing the item — name, description, image.
- At least one related price entity describing how much and how often it's billed.

The template's three-tier pricing page has `Free`, `Basic`, and `Pro`, each with monthly and annual options. Mirror this in Paddle as three products with two prices each.

### Create products and prices

You can [create products and prices](https://developer.paddle.com/build/products/create-products-prices.md) using the Paddle dashboard or the API.

{% instruction-steps %}

1. Go to **Paddle > Catalog > Products**.
2. Click {% mock-button icon="carbon:add" %}New product.
3. Enter details, then Save.
4. Under **Prices**, click New price.
5. Enter details. Set billing period to **Monthly** for a monthly price.
6. Click Save.
7. Repeat for an **Annually** price.

{% /instruction-steps %}

{% /dashboard-instructions %}

Repeat for each product until you have three products with two prices each.

### Update pricing constants

Update the app with your new price IDs. Clone your Git repo locally and open `src/constants/pricing-tier.ts` — or edit directly on your Git platform.

`pricing-tier.ts` contains constants used in the pricing page and checkout. Swap each `pri_` ID with one of your new price IDs:

```ts {% highlightLines="19,28,43" collapse=true %}
export interface Tier {
  name: string;
  id: 'starter' | 'pro' | 'advanced';
  icon: string;
  description: string;
  features: string[];
  featured: boolean;
  priceId: Record<string, string>;
}

export const PricingTier: Tier[] = [
  {
    name: 'Starter',
    id: 'starter',
    icon: '/assets/icons/price-tiers/free-icon.svg',
    description: 'Ideal for individuals who want to get started with simple design tasks.',
    features: ['1 workspace', 'Limited collaboration', 'Export to PNG and SVG'],
    featured: false,
    priceId: { month: 'pri_01hsxyh9txq4rzbrhbyngkhy46', year: 'pri_01hsxyh9txq4rzbrhbyngkhy46' },
  },
  {
    name: 'Pro',
    id: 'pro',
    icon: '/assets/icons/price-tiers/basic-icon.svg',
    description: 'Enhanced design tools for scaling teams who need more flexibility.',
    features: ['Integrations', 'Unlimited workspaces', 'Advanced editing tools', 'Everything in Starter'],
    featured: true,
    priceId: { month: 'pri_01hsxycme6m95sejkz7sbz5e9g', year: 'pri_01hsxyeb2bmrg618bzwcwvdd6q' },
  },
  {
    name: 'Advanced',
    id: 'advanced',
    icon: '/assets/icons/price-tiers/pro-icon.svg',
    description: 'Powerful tools designed for extensive collaboration and customization.',
    features: [
      'Single sign on (SSO)',
      'Advanced version control',
      'Assets library',
      'Guest accounts',
      'Everything in Pro',
    ],
    featured: false,
    priceId: { month: 'pri_01hsxyff091kyc9rjzx7zm6yqh', year: 'pri_01hsxyfysbzf90tkh2wqbfxwa5' },
  },
];
```

To get a price ID:

1. Go to **Paddle > Catalog > Products** and click a product.
2. Click  next to a price, then Copy price ID.
3. Paste the ID as `priceId.month` or `priceId.year`.

Add all your price IDs, then commit and push to `main`. Vercel rebuilds automatically.

When the build's done, your pricing page should display prices — but you can't checkout yet. That's the next step.

## Add your website to Paddle and test {% step=true %}

To keep the Paddle platform safe, you must add your website to Paddle before launching a checkout from it. This protects you as a seller — only you can sell your products.

### Get your website approved

{% callout type="info" %}
For sandbox accounts, website approval is instant. You still need to add your domain.
{% /callout %}

Website approval makes sure you own the domains where you use Paddle Checkout and that your products meet Paddle's acceptable use policy.

1. Go to **Paddle > Checkout > Website approval**.
2. Click Add a new domain, enter your Vercel demo app URL, then Submit for Approval.
3. Wait for approval.

For sandbox, you'll see a green "Approved" status immediately. Live accounts may take a few days while the Paddle verification team reviews your site.

[See website verification on the Paddle help center](https://www.paddle.com/help/start/account-verification/what-is-domain-verification)

### Set your default payment link

Your default payment link opens Paddle Checkout for a transaction. It's also used in emails from Paddle that let customers manage their subscription.

{% instruction-steps %}

1. Go to **Paddle > Checkout > Checkout settings**.
2. Enter your Vercel deploy URL under **Default payment link**.
3. Click {% mock-button %}Save.

{% /instruction-steps %}

{% /dashboard-instructions %}

See [set your default payment link](https://developer.paddle.com/build/transactions/default-payment-link.md).

### Check your notification destination endpoint

If your project name was taken, Vercel appended characters to your deploy URL. If that URL doesn't match `https://<PROJECTNAME>.vercel.app`, update the notification destination URL in Paddle. You can find your deploy URL in the Vercel dashboard.

### Test

Open your Vercel demo site. Prices should now render on the pricing page.

Click **Get started** to launch a checkout. Use [test card details](https://developer.paddle.com/concepts/payment-methods/credit-debit-card.md) for sandbox:

{% definition-list %}
{% definition term="Email address" %}
An email address you own
{% /definition %}
{% definition term="Country" %}
Any valid country supported by Paddle
{% /definition %}
{% definition term="ZIP code (if required)" %}
Any valid ZIP or postal code
{% /definition %}
{% definition term="Card number" %}
`4242 4242 4242 4242`
{% /definition %}
{% definition term="Name on card" %}
Any name
{% /definition %}
{% definition term="Expiration date" %}
Any valid date in the future.
{% /definition %}
{% definition term="Security code" %}
`100`
{% /definition %}
{% /definition-list %}

After checkout, click **Sign in**. The subscriptions and payments pages pull information from Paddle about the customer's subscriptions and transactions.

### Troubleshoot

If prices don't appear, the checkout doesn't load, or you see "Something went wrong," open your browser console for specific error messages from Paddle.js. On macOS Chrome, `⌘` + `⌥` + `J` opens it.

Check that:

- You pushed all changes to `main` and Vercel deployed successfully.
- Products and prices are created correctly. Prices should be recurring so Paddle creates a subscription when checkout completes.
- Your `pri_` IDs are correct. Sandbox and live IDs don't cross environments.
- Your default payment link matches the domain you're testing on.
- Your notification destination URL is `https://<YOUR-VERCEL-URL>/api/webhook`.
- `NEXT_PUBLIC_PADDLE_CLIENT_TOKEN` is a valid token (starts with `test_` for sandbox, `live_` for live).
- `NEXT_PUBLIC_PADDLE_ENV` is `sandbox` or `production` to match your account.
- `PADDLE_API_KEY` is valid.
- Supabase tables were created successfully.

Use the [Vercel](https://vercel.com/docs) or [Supabase](https://supabase.com/docs) docs for platform-specific issues.

## Build your app, then go live {% step=true %}

You're done. Use this starter kit as a basis for building a SaaS app on Paddle Billing. When you're ready to take real payments:

1. Sign up for a live account, then follow the [go-live checklist](https://developer.paddle.com/build/onboarding/go-live-checklist.md).
2. Update Vercel environment variables for your live account.
3. Create new schemas in Supabase for live data.
4. [Set up Paddle Retain](https://developer.paddle.com/build/retain/set-up-retain-profitwell.md) to recover failed payments.

## Next steps

That's it. Now you've built a Next.js app with Paddle Billing and deployed it to Vercel, you might like to hook into other features of the Paddle platform.

### Do more with Paddle.js

Our starter kit passes prices to Paddle.js to display localized pricing on our pricing page and open a checkout to create subscriptions. Paddle.js includes a bunch of properties and settings you can pass that give you more control over how opened checkouts work.

For example, you can prepopulate a discount, set the language that Paddle Checkout uses, or restrict payment options shown to customers.

{% card-group cols=3 %}
{% card title="Prefill checkout properties" url="/build/checkout/prefill-checkout-properties" %}
See how to pass customer and order information into Paddle.js to prefill checkout forms.
{% /card %}
{% card title="Pass checkout settings" url="/build/checkout/set-up-checkout-default-settings" %}
Control checkout behavior by passing additional settings to Paddle.js.
{% /card %}
{% card title="Brand inline checkout" url="/build/checkout/brand-customize-inline-checkout" %}
Customize the look and feel of your inline checkout to match your brand.
{% /card %}
{% /card-group %}

### Build advanced subscription functionality

Paddle Billing supports flexible subscription workflows: pause and resume, change billing dates, offer trials, and more.

{% card-group cols=3 %}
{% card title="Pause or resume a subscription" url="/build/subscriptions/pause-subscriptions" %}
Let customers temporarily suspend or reactivate their subscriptions with your app.
{% /card %}
{% card title="Change billing dates" url="/build/subscriptions/change-billing-dates" %}
Give customers the flexibility to adjust their billing dates to suit their needs.
{% /card %}
{% card title="Work with trials" url="/build/subscriptions/update-trials" %}
Learn how to offer and manage free trials to attract more signups.
{% /card %}
{% /card-group %}

### Integrate with Paddle Retain

[Paddle Retain](https://developer.paddle.com/concepts/retain/overview.md) combines subscription expertise with algorithms trained on billions of datapoints to recover failed payments, reduce churn, and proactively upgrade plans.

{% card-group cols=3 %}
{% card title="Configure Payment Recovery and dunning" url="/build/retain/configure-payment-recovery-dunning" %}
Set up rules and automations to recover failed payments and handle dunning intelligently.
{% /card %}
{% card title="Build cancellation surveys" url="/build/retain/configure-cancellation-flows-surveys" %}
Capture feedback at cancel and use flows to reduce churn in your SaaS.
{% /card %}
{% card title="Proactively upgrade plans" url="/build/retain/configure-term-optimization-automatic-upgrades" %}
Let Retain automatically upgrade customers when optimal for their billing cycle.
{% /card %}
{% /card-group %}