snagr

How to Add Polar Payments to a Next.js App (2026 Guide)

·9 min read

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/nextjsadapter: 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 Checkouthandler 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]

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_dueor 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 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
  2. 2.Polar — official example projects (GitHub)

Try Snagr

Recover the revenue you're leaving on the table

Snagr connects to Polar in one click and automatically emails customers who abandoned a checkout, hit a failed renewal, or cancelled. A 5% holdback proves the lift is real.

Connect Polar — free50 recovery emails/mo free · no card

Selling on Polar? Connect Snagr free and start recovering lost revenue today.