Polar Setup
Configure Polar as your billing provider
Set up Polar by creating an access token, configuring environment variables, creating products in the Polar dashboard, and optionally setting up webhooks. Polar acts as a merchant of record, handling taxes and compliance for you.
This guide is part of the Billing & Subscriptions documentation.
Polar integration uses Better Auth's Polar plugin to handle checkout sessions, customer management, and subscription lifecycle - with Polar acting as the merchant of record for tax and compliance purposes.
Important: Organization Billing Limitations
Polar does not support true per-organization customers the way Stripe does. In Makerkit:
- Organization billing UI can still be shown (via
referenceId = organization.id) - But customer context is user-centric and organization “customerId” is not available
- The customer portal is effectively user-level, not organization-level
If you need strict B2B billing with a dedicated customer per organization, use Stripe.
Prerequisites
- A Polar account (sign up at polar.sh)
- Access to your project's
.envfile
Step 1: Enable Polar Provider
Set Polar as your billing provider in .env:
NEXT_PUBLIC_BILLING_PROVIDER=polarAlso ensure you have:
# Used for server-side return URLs in billing actionsNEXT_PUBLIC_SITE_URL=http://localhost:3000Step 2: Get Your Access Token
- Log in to your Polar Dashboard
- Navigate to Settings → Developers → Personal Access Tokens
- Click Create Token
- Give it a name and select appropriate scopes
- Copy the generated access token
Add to your .env:
POLAR_ACCESS_TOKEN=polar_at_...your_access_tokenWe recommend enabling the following access scopes:
benefits:readcheckouts:readcheckout:writecustomer_seats:readcustomer_portal:readcustomer_portal:writecustomer_sessions:writesubscriptions:readsubscriptions:writecustomers:readcustomers:writemeters:readmeters:write
You may want to add additional access scopes depending on your needs. For example, if you need to read/write orders, you can add the orders:read and orders:write scopes.
Step 3: Configure Environment
Set the environment mode:
# For development/testingPOLAR_ENVIRONMENT=sandbox# For productionPOLAR_ENVIRONMENT=productionThe default is sandbox if not specified.
Step 4: Create Products
Using 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
Example Product Setup
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)
Configure Product IDs
Add your Product IDs to .env:
POLAR_PRODUCT_STARTER_MONTHLY=prod_...POLAR_PRODUCT_STARTER_YEARLY=prod_...POLAR_PRODUCT_PRO_MONTHLY=prod_...POLAR_PRODUCT_PRO_YEARLY=prod_...Free Trial
In Polar, please specify the free trial during the product setup, not the configuration.
Step 5: Configure Billing Plans
Update your billing configuration to include the details of your products:
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: Configure Webhooks
Webhooks are optional. Enable them if you want server-side lifecycle hooks (e.g. logging, analytics, provisioning) to run in response to Polar events.
Production Webhooks
- 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_...NB; for local development, you can use a tunneling service like ngrok to expose your local server to the internet and receive these events on your local machine.
Local Development
For local testing, use a tunneling service like ngrok or Localtunnel.
# Start ngrokngrok http 3000# Copy the https URL and configure it in Polar webhook settings# Example: https://abc123.ngrok.io/api/auth/polar/webhookOnce started the proxy to your local server will be available at a URL like https://abc123.ngrok.io, you must use this URL in the Polar webhook settings.
Step 7: Test the Integration
Test Checkout
- Run the development server:
pnpm dev - Navigate to
/settings/billing - Select a plan and proceed to checkout
- Complete the checkout flow on Polar
Verify Webhooks
Monitor your server logs to verify webhook events are being received and processed.
Environment Variables Reference
Complete .env configuration for Polar:
# 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:3000Provider Capabilities
Polar supports the following features:
| 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 |
Cancel/Restore/Upgrade/Downgrade via Customer Portal
Customers can use the Polar customer portal to manage their subscriptions
Production Checklist
Before going live:
- [ ] Switch
POLAR_ENVIRONMENTtoproduction - [ ] Configure production webhook endpoint
- [ ] Test all webhook events in production
- [ ] Verify product IDs are correct and match your billing configuration
- [ ] Set up billing email notifications in Polar
Troubleshooting
Webhook Signature Verification Failed
Check that your POLAR_WEBHOOK_SECRET matches the webhook in Polar Dashboard.
Customer Not Created
Ensure the plugin is configured with createCustomerOnSignUp: true (default).
Product ID Not Found
Verify Product IDs exist in Polar Dashboard and match your billing configuration.
Environment Mismatch
Ensure POLAR_ENVIRONMENT matches your Polar account mode (sandbox vs production).
Lifecycle Hooks
Polar-specific hooks are available 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.
Common Pitfalls
- Using Price IDs instead of Product IDs: Polar uses Product IDs (
prod_...), not Price IDs like Stripe. This is the opposite of Stripe's setup. - Expecting organization-level customers: Polar is user-centric. The
organization.stripeCustomerIdequivalent doesn't exist - customer context is always the user. - Setting
freeTrialin billing config for Polar: Polar trials are configured in the product setup on their dashboard, not in MakerKit's config. - Forgetting to switch POLAR_ENVIRONMENT: Development uses
sandbox, production usesproduction. Mismatching environments causes auth failures. - Missing webhook configuration: Without webhooks, subscription state won't update after checkout. Unlike Stripe, there's no local CLI forwarder - use ngrok.
- Assuming cancel/restore work via API: Polar doesn't expose cancel/restore APIs. Users must use the customer portal for these operations.
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 →