Skip to main content
Polar gives you a clean, modern checkout experience with great metadata support. The setup is straightforward — users pay on Polar’s checkout page, then Polar tells your app what happened through a webhook. Let’s wire it up.

What you’re setting up

Four things to make Polar work:
  1. Products inside Polar
  2. Checkout links your users can click
  3. A webhook endpoint Polar can call
  4. A way for the repo to know what each product means

Env vars

NEXT_PUBLIC_PAYMENT_PROVIDER=polar
NEXT_PUBLIC_CHECKOUT_URL_TEMPLATE=https://...
POLAR_WEBHOOK_SECRET=...

Setup

1

Create products in Polar

Head to your Polar dashboard and create the products you want to sell.For your first setup, keep it simple:
  • One Medium plan product
  • One Small credits product
Each product will need to map to a repo purchase type like plan-medium or credits-small.
2

Create checkout links

Once your products are ready, create hosted checkout links in Polar and add them to .env.local:
NEXT_PUBLIC_CHECKOUT_URL_PLAN_MEDIUM=https://polar.sh/checkout/...
NEXT_PUBLIC_CHECKOUT_URL_CREDITS_SMALL=https://polar.sh/checkout/...
These are the links your app will use for payment buttons.
3

Tell the repo what each product means

You have two ways to do this. Pick whichever you prefer.
4

Add the webhook in Polar

In your Polar settings, add this webhook endpoint:
https://yourdomain.com/api/payments/polar
Then copy the webhook secret into your env:
POLAR_WEBHOOK_SECRET=...
If the webhook isn’t configured, checkout may succeed in Polar but your app will never know about it. That means credits won’t be added and purchases won’t be stored.

How it works under the hood

When Polar sends a successful webhook:
  1. The repo verifies the webhook signature
  2. It reads the purchase type from metadata or POLAR_PRODUCT_MAP
  3. It stores the purchase in the purchases table
  4. It tries to match the purchase to a user (using external customer ID if available)
  5. It updates credits or purchase state

A quick example

Say you want to sell one credits pack through Polar. Here’s everything you’d set:
NEXT_PUBLIC_PAYMENT_PROVIDER=polar
NEXT_PUBLIC_CHECKOUT_URL_CREDITS_SMALL=https://polar.sh/checkout/...
POLAR_WEBHOOK_SECRET=...
POLAR_PRODUCT_MAP='{"prod_credits_small":"credits-small"}'
Now when a user buys that product:
  1. The checkout button sends them to Polar
  2. Polar charges them
  3. Polar calls your webhook
  4. The repo recognizes credits-small
  5. The repo adds the correct number of credits
Pretty straightforward, right?

Verify your setup

Your Polar setup is working if all of these are true:
  • The app opens the Polar checkout page
  • Polar shows a successful webhook delivery
  • A row appears in your purchases table
  • A credits purchase increases the user’s credits
  • A plan purchase updates the user’s access state
You probably forgot to save the checkout URL in your .env.local file. Without it, the app can’t build payment buttons.
You configured checkout but forgot the webhook. The webhook is what tells your app the payment happened — without it, your app is in the dark.
Make sure you’ve mapped the Polar product to a valid purchase type, either via metadata or POLAR_PRODUCT_MAP. If neither is set, the repo can’t determine what was bought.
Restart your dev server. Next.js doesn’t hot-reload environment variable changes.
Check that the webhook URL in Polar points to your production domain, not localhost or a dev tunnel URL.

Payments Overview

Go back to the shared payment architecture and basics.