---
title: "How to Add Polar Payments to a Next.js App"
description: "A step-by-step guide to accepting payments, subscriptions, and webhooks with Polar.sh in a Next.js app, using the official @polar-sh/nextjs adapter."
url: https://www.snagr.sh/blog/add-polar-payments-nextjs
source: https://www.snagr.sh/blog/add-polar-payments-nextjs.md
date_published: 2026-06-24
date_modified: 2026-06-24
keywords: ["polar payments nextjs", "polar.sh next.js", "@polar-sh/nextjs", "integrate polar payments next.js", "polar checkout nextjs", "polar subscriptions next.js"]
site: Snagr
---
# How to Add Polar Payments to a Next.js App (2026 Guide)

> A step-by-step guide to accepting payments, subscriptions, and webhooks with Polar.sh in a Next.js app, using the official @polar-sh/nextjs adapter.

[Polar.sh](https://polar.sh) is a developer-first billing platform and merchant of record, which means it handles payments, sales tax, and digital-product delivery for you. This guide walks through adding Polar to a Next.js App Router app using the official `@polar-sh/nextjs` adapter: checkout, a customer portal, and webhooks. The code is current as of 2026 and taken from Polar’s own documentation.[1]

## Prerequisites

- A Next.js project using the App Router.
- A Polar account, and at least one **product** created in the dashboard (note its product ID).
- An **organization access token** from Polar (Settings → Developers). Use a **sandbox** token while building.

## Step 1 — Install the adapter

Install the Next.js adapter and Zod (used for validation):

Terminal

```
pnpm add zod @polar-sh/nextjs
```

## Step 2 — Set environment variables

Add your access token and the URL Polar should send customers to after a successful checkout. Keep secrets in `.env.local`, never in client code.

.env.local

```
POLAR_ACCESS_TOKEN=polar_oat_...
SUCCESS_URL=https://myapp.com/success
POLAR_WEBHOOK_SECRET=whsec_...
```

## Step 3 — Create a checkout route

The adapter ships a `Checkout` handler that takes care of the redirect to Polar’s hosted checkout. Create a route handler:

app/checkout/route.ts

```
import { Checkout } from "@polar-sh/nextjs";

export const GET = Checkout({
 accessToken: process.env.POLAR_ACCESS_TOKEN,
 successUrl: process.env.SUCCESS_URL,
 server: "sandbox", // omit or use "production" when you go live
});
```

Link customers to it with the product ID as a query param. You can also prefill the customer:

Anywhere in your UI

```
<a href="/checkout?products=YOUR_PRODUCT_ID">Buy now</a>

// Optional params:
// ?products=ID&customerEmail=jane@example.com&customerName=Jane
```

## Step 4 — Add a customer portal

Give customers a portal to manage their orders and subscriptions. You resolve which Polar customer is logged in via `getCustomerId`:

app/portal/route.ts

```
import { CustomerPortal } from "@polar-sh/nextjs";

export const GET = CustomerPortal({
 accessToken: process.env.POLAR_ACCESS_TOKEN,
 getCustomerId: async (req) => {
 // Resolve and return the Polar customer ID for the signed-in user
 return "";
 },
 server: "sandbox",
});
```

## Step 5 — Handle webhooks

Webhooks are how your app learns about orders, subscriptions, and refunds. The `Webhooks` helper verifies the signature for you; add the webhook secret from your Polar dashboard.

app/api/webhook/polar/route.ts

```
import { Webhooks } from "@polar-sh/nextjs";

export const POST = Webhooks({
 webhookSecret: process.env.POLAR_WEBHOOK_SECRET!,
 onOrderPaid: async (payload) => {
 // Grant access, send a receipt, etc.
 },
 onSubscriptionActive: async (payload) => {
 // Mark the subscription active in your DB
 },
 onSubscriptionCanceled: async (payload) => {
 // Begin offboarding / win-back
 },
});
```

The handler exposes granular callbacks for nearly every event, including `onCheckoutCreated`, `onOrderPaid`, `onSubscriptionActive`, `onSubscriptionCanceled`, and `onSubscriptionRevoked`, plus a catch-all `onPayload`.[1] Register the endpoint URL in your Polar dashboard’s webhook settings.

## Step 6 — Test, then go live

Build against the **sandbox** environment first (`server: "sandbox"` and sandbox tokens). When you’re ready, create production tokens, set the webhook secret for production, and either remove the `server` option or set it to `"production"`. Polar’s example repos are a good reference for full working setups.[2]

Sandbox vs production

Sandbox and production are entirely separate environments with separate tokens, products, and webhook secrets. A token from one will not work against the other, so double-check which `server` you are pointing at when something “does not exist.”

## The step the tutorials skip: recovering what slips away

Once payments work, you will notice two leaks the integration itself does not handle. Polar fires `onCheckoutCreated` when someone _starts_ a checkout, but there is no built-in follow-up when they do not finish, and when a subscription goes `past_due` or cancels, recovery beyond Polar’s basic dunning is on you. Those abandoned checkouts and failed renewals are real revenue, and chasing them by hand means writing sweep logic, an email service, deliverability setup, and attribution.

That is the gap [Snagr](/blog/recover-abandoned-checkouts-polar) fills: it connects to the same Polar account in one click, watches these exact events, and sends recovery emails from your own domain, measured against a holdback so you know the revenue is real. Free for your first 50 recovery emails a month.

Sources

1. 1.
 [Polar — Next.js adapter documentation](https://docs.polar.sh/integrate/sdk/adapters/nextjs)
2. 2.
 [Polar — official example projects (GitHub)](https://github.com/polarsource/examples)
