Polar Setup
Connect Polar billing to your Next.js Prisma application
Connect Polar by generating an access token, setting environment variables, creating products in the dashboard, and optionally configuring webhooks. As a merchant of record, Polar handles taxes and compliance.
This page is part of the Billing & Subscriptions documentation.
The Polar integration uses Better Auth's Polar plugin for checkout sessions, customer records, and subscription management, with Polar serving as merchant of record for tax and compliance.
Organization Billing Limitations
Polar lacks true per-organization customers like Stripe. In Makerkit:
- Organization billing UI works (using
referenceId = organization.id) - However, customer context remains user-centric with no org "customerId"
- The customer portal operates at user level, not organization level
For strict B2B billing with dedicated org customers, use Stripe instead.
Before You Start
- A Polar account (polar.sh)
- Access to your
.envfile
Step 1: Enable Polar
Set Polar as your provider in .env:
NEXT_PUBLIC_BILLING_PROVIDER=polarAlso add:
# Required for server-side return URLsNEXT_PUBLIC_SITE_URL=http://localhost:3000Step 2: Create Access Token
- Sign in to Polar Dashboard
- Go to Settings → Developers → Personal Access Tokens
- Click Create Token
- Name it and select required scopes
- Copy the token
Add to .env:
POLAR_ACCESS_TOKEN=polar_at_...your_access_tokenRecommended scopes:
benefits:readcheckouts:readcheckout:writecustomer_seats:readcustomer_portal:readcustomer_portal:writecustomer_sessions:writesubscriptions:readsubscriptions:writecustomers:readcustomers:writemeters:readmeters:write
Add more scopes as needed, like orders:read and orders:write for order management.
Step 3: Set Environment Mode
Configure the environment:
# For development/testingPOLAR_ENVIRONMENT=sandbox# For productionPOLAR_ENVIRONMENT=productionDefault is sandbox when not set.
Step 4: Create Products
Via Polar Dashboard
- Navigate to Products in your Polar dashboard
- Click Create Product
- Configure your product:
- Name: Set the name and description
- Pricing: Choose pricing (subscription or one-time)
- Billing Interval: Set billing interval (monthly/yearly)
- Trial Period: Set the trial period (optional)
- Meters: Optionally, add metered usage:
- Example: "requests": $0.10 per 1,000 requests
- Benefits: Optionally, add benefits
- Example: "core-features", "email-support", "priority-support"
- Copy each Product ID
Sample Products
Starter Product
- Monthly:
prod_starter_monthly($9.99/month) - Yearly:
prod_starter_yearly($99.99/year)
Pro Product
- Monthly:
prod_pro_monthly($19.99/month) - Yearly:
prod_pro_yearly($199.99/year)
Store Product IDs
Add Product IDs to .env:
POLAR_PRODUCT_STARTER_MONTHLY=prod_...POLAR_PRODUCT_STARTER_YEARLY=prod_...POLAR_PRODUCT_PRO_MONTHLY=prod_...POLAR_PRODUCT_PRO_YEARLY=prod_...Trial Periods
For Polar, configure free trials in the product setup, not the billing config.
Step 5: Map Plans in Config
Update your billing configuration:
import { BillingConfig } from '@kit/billing';export const billingConfig: BillingConfig = { products: [ { id: 'starter', name: 'Starter', description: 'For individuals and small teams', currency: 'USD', features: ['Core features', 'Email support'], plans: [ { name: 'starter-monthly', planId: process.env.POLAR_PRODUCT_STARTER_MONTHLY!, displayName: 'Starter Monthly', interval: 'month', cost: 9.99, }, ], }, ],};Step 6: Set Up Webhooks (Optional)
Webhooks enable server-side lifecycle hooks for logging, analytics, and provisioning in response to Polar events.
Production Setup
- In Polar Dashboard, navigate to Settings → Webhooks
- Click Add Webhook
- Set endpoint URL:
https://yourdomain.com/api/auth/polar/webhook - Select events to listen for:
subscription.createdsubscription.updatedsubscription.canceledorder.paid
- Copy the webhook signing secret
Add to your .env:
POLAR_WEBHOOK_SECRET=whsec_...For local development, use a tunneling service like ngrok to receive events.
Local Development
Use ngrok or Localtunnel for local testing:
# Start ngrokngrok http 3000# Use the https URL in Polar webhook settings# Example: https://abc123.ngrok.io/api/auth/polar/webhookThe proxy URL (e.g., https://abc123.ngrok.io) must be configured in Polar's webhook settings.
Step 7: Verify Integration
Test Checkout Flow
- Start development:
pnpm dev - Open
/settings/billing - Choose a plan and checkout
- Complete the flow on Polar
Confirm Webhooks
Check server logs for incoming webhook events.
Environment Variables
Full Polar configuration:
# Billing ProviderNEXT_PUBLIC_BILLING_PROVIDER=polar# Polar ConfigurationPOLAR_ACCESS_TOKEN=polar_at_...POLAR_ENVIRONMENT=sandbox # or 'production'POLAR_WEBHOOK_SECRET=whsec_...# Optional behavior toggle (default shown)CREATE_CUSTOMER_ON_SIGN_UP=true# Product IDs (from Polar Dashboard)POLAR_PRODUCT_STARTER_MONTHLY=prod_...POLAR_PRODUCT_STARTER_YEARLY=prod_...POLAR_PRODUCT_PRO_MONTHLY=prod_...POLAR_PRODUCT_PRO_YEARLY=prod_...# Application URL (for redirects)NEXT_PUBLIC_SITE_URL=http://localhost:3000Feature Support
Polar capabilities:
| Feature | Supported | Notes |
|---|---|---|
| Checkout | Yes | Via Better Auth API |
| Customer Portal | Yes | Customers manage subscriptions here |
| Cancel (API) | No | Use portal for cancellation |
| Restore (API) | No | Use portal for restoration |
| Benefits | Yes | Provider feature; not mapped to BillingClient entitlements in Makerkit |
| Usage Meters | Yes | Event-based metering |
| Organizations | Limited | No organization customer context; prefer Stripe for B2B billing |
Self-Service Portal
Customers manage subscriptions (cancel, restore, upgrade, downgrade) through Polar's customer portal.
Launch Checklist
Before going live:
- [ ] Set
POLAR_ENVIRONMENTtoproduction - [ ] Configure production webhook endpoint
- [ ] Verify webhook events in production
- [ ] Confirm product IDs match your config
- [ ] Enable billing notifications in Polar
Troubleshooting
Webhook Verification Fails
Verify POLAR_WEBHOOK_SECRET matches the Polar Dashboard webhook.
Customer Not Created
Confirm createCustomerOnSignUp: true (the default) is set.
Product ID Not Found
Check Product IDs exist in Polar Dashboard and match your config.
Environment Mismatch
Ensure POLAR_ENVIRONMENT matches your Polar mode (sandbox vs production).
Event Hooks
Polar-specific hooks live in packages/billing/polar/src/hooks/:
| Hook | When it's called |
|---|---|
onSubscriptionCreated | New subscription created |
onSubscriptionUpdated | Subscription changes |
onSubscriptionCanceled | Subscription canceled |
onOrderPaid | Order payment completed |
onCustomerCreated | Customer record created |
onCustomerStateChanged | Customer state changes |
See Lifecycle Hooks for implementation details.
Watch Out For
- Price IDs instead of Product IDs: Polar uses Product IDs (
prod_...), opposite of Stripe's Price IDs. - Expecting org-level customers: Polar is user-centric. No
organization.stripeCustomerIdequivalent exists. freeTrialin billing config: Polar trials are configured in their dashboard, not MakerKit's config.- Wrong
POLAR_ENVIRONMENT: Development usessandbox, production usesproduction. Mismatch causes auth failures. - No webhook setup: Without webhooks, subscription state stalls after checkout. No local CLI forwarder exists, so use ngrok.
- Expecting API cancel/restore: Polar lacks these APIs. Direct users to the customer portal instead.
Frequently Asked Questions
How do I test Polar without real payments?
Why can't I cancel subscriptions programmatically?
Can I use Polar for B2B (organization) billing?
How do I handle webhook development locally?
What happened to Polar benefits?
Next: Lifecycle Hooks →