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 .env file

Step 1: Enable Polar Provider

Set Polar as your billing provider in .env:

NEXT_PUBLIC_BILLING_PROVIDER=polar

Also ensure you have:

# Used for server-side return URLs in billing actions
NEXT_PUBLIC_SITE_URL=http://localhost:3000

Step 2: Get Your Access Token

  1. Log in to your Polar Dashboard
  2. Navigate to SettingsDevelopersPersonal Access Tokens
  3. Click Create Token
  4. Give it a name and select appropriate scopes
  5. Copy the generated access token

Add to your .env:

POLAR_ACCESS_TOKEN=polar_at_...your_access_token

We recommend enabling the following access scopes:

  • benefits:read
  • checkouts:read
  • checkout:write
  • customer_seats:read
  • customer_portal:read
  • customer_portal:write
  • customer_sessions:write
  • subscriptions:read
  • subscriptions:write
  • customers:read
  • customers:write
  • meters:read
  • meters: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/testing
POLAR_ENVIRONMENT=sandbox
# For production
POLAR_ENVIRONMENT=production

The default is sandbox if not specified.

Step 4: Create Products

Using Polar Dashboard

  1. Navigate to Products in your Polar dashboard
  2. Click Create Product
  3. 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"
  1. 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

  1. In Polar Dashboard, navigate to SettingsWebhooks
  2. Click Add Webhook
  3. Set endpoint URL: https://yourdomain.com/api/auth/polar/webhook
  4. Select events to listen for:
    • subscription.created
    • subscription.updated
    • subscription.canceled
    • order.paid
  5. 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 ngrok
ngrok http 3000
# Copy the https URL and configure it in Polar webhook settings
# Example: https://abc123.ngrok.io/api/auth/polar/webhook

Once 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

  1. Run the development server: pnpm dev
  2. Navigate to /settings/billing
  3. Select a plan and proceed to checkout
  4. 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 Provider
NEXT_PUBLIC_BILLING_PROVIDER=polar
# Polar Configuration
POLAR_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:3000

Provider Capabilities

Polar supports the following features:

FeatureSupportedNotes
CheckoutYesVia Better Auth API
Customer PortalYesCustomers manage subscriptions here
Cancel (API)NoUse portal for cancellation
Restore (API)NoUse portal for restoration
BenefitsYesProvider feature; not mapped to BillingClient entitlements in Makerkit
Usage MetersYesEvent-based metering
OrganizationsLimitedNo 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_ENVIRONMENT to production
  • [ ] 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/:

HookWhen it's called
onSubscriptionCreatedNew subscription created
onSubscriptionUpdatedSubscription changes
onSubscriptionCanceledSubscription canceled
onOrderPaidOrder payment completed
onCustomerCreatedCustomer record created
onCustomerStateChangedCustomer 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.stripeCustomerId equivalent doesn't exist - customer context is always the user.
  • Setting freeTrial in 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 uses production. 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?
Set POLAR_ENVIRONMENT=sandbox. The sandbox environment provides test checkout flows without processing real payments.
Why can't I cancel subscriptions programmatically?
Polar doesn't expose cancel/restore APIs. Redirect users to the customer portal via billing.portal() for subscription management.
Can I use Polar for B2B (organization) billing?
The UI works, but Polar has no organization customer concept. For strict B2B billing with per-org customer records, use Stripe instead.
How do I handle webhook development locally?
Use ngrok or similar to expose your local server: ngrok http 3000. Then configure that URL in Polar's webhook settings.
What happened to Polar benefits?
Polar supports benefits, but MakerKit doesn't map them to BillingClient entitlements. Use billing.getProviderClient() to access benefits directly.

Next: Lifecycle Hooks →