Overview

aSaaSin integrates Polar for subscriptions and one‑time purchases. You’ll set environment variables, create products, map them to internal plans, and enable a webhook that keeps the database in sync.

Add to env

# Feature flag
ENABLE_POLAR=true

# Polar
POLAR_API_KEY=
POLAR_WEBHOOK_SECRET=
POLAR_SUCCESS_URL=https://your-domain/success?checkout_id={CHECKOUT_ID}

Save the file, then restart the dev server if it was running.

What each key is for

  • ENABLE_POLAR=true - Controls whether payment and subscription functionality is active. When false, payment operations are skipped safely without errors.
  • POLAR_API_KEY - API key from Polar dashboard for authenticating with Polar's API.
  • POLAR_WEBHOOK_SECRET - Secret for validating webhook signatures from Polar events.
  • POLAR_SUCCESS_URL - Redirect URL after successful checkout completion, must include {CHECKOUT_ID} placeholder for order tracking.

Create products

  1. In Polar, create products for one‑time purchases and/or subscription plans.
  2. Copy each product’s ID.
  3. Add these IDs to your DB table mapping.

Map products to plans

Plans live in subscription_plans with (name, billing_cycle) unique. Store Polar’s product_id per plan and capability flags in features JSONB (e.g., maxProjects, maxApiTokens). The webhook uses product_id to resolve the internal plan.

Subscription flow

  1. User clicks Subscribe on Pricing.
  2. If logged out, send them to Sign up with:
    • product_id=<polar-product>
    • redirect_to=/dashboard
  3. After auth, redirect to Dashboard with that product_id.
  4. SubscribeRedirector reads the URL, calls a server action to create Polar checkout, then redirects the user to Polar.

One‑time purchase flow

  1. User clicks Buy on Pricing.
  2. The button calls a server action (e.g., startCheckoutAction) with product_id.
  3. The action creates a Polar checkout and returns a redirectUrl.
  4. The client redirects to Polar (no authentication required).
  5. After payment, the user is redirected to POLAR_SUCCESS_URL.
// Server action
export async function startCheckoutAction(formData: FormData) {
  const productId = formData.get('product_id')?.toString();
  if (!productId) return { success: false, error: 'Missing product ID.' };

  const redirectUrl = await createPolarCheckout({ productId });
  return { success: true, data: { redirectUrl } };
}

Webhook configuration

  1. Endpoint URL (local or prod): /api/webhooks/polar
  2. Secret: set POLAR_WEBHOOK_SECRET in .env and verify signatures in the handler.
  3. Events to enable:
    • subscription.created
    • subscription.updated
    • subscription.canceled
    • order.created
    • order.refunded

What the handler does

  • Verify the signature and parse the event.
  • On subscription.created / subscription.updated: look up the user by e‑mail, map product_idsubscription_plans.id, then upsert subscriptions with: subscription_plan_id, subscription_id, customer_id, status, current_period_start, current_period_end, canceled_at.
  • On subscription.canceled: set status to canceled and record canceled_at.
  • On order.*: log or trigger fulfillment for one‑time purchases as needed.

Security & reliability

  • Use the Supabase service role only on the server.
  • Make writes idempotent (unique constraints + upserts) and return 200 even on duplicate deliveries you safely ignore.

Local development

Start a tunnel and wire it into your env and Polar Sandbox.

# Run your app
yarn dev

# Expose port 3000
ngrok http 3000

Use the generated ngrok URL in two places:

  1. .env: POLAR_SUCCESS_URL=https://<ngrok-id>.ngrok-free.app/success?checkout_id={CHECKOUT_ID}
  2. Polar Sandbox → Settings → Webhooks: https://<ngrok-id>.ngrok-free.app/api/webhooks/polar

Enable: order.created, order.refunded, subscription.created, subscription.canceled.

Production webhook

Configure the same events for your live endpoint (e.g., https://demo.asaasin.dev/api/webhooks/polar) and set a production POLAR_SUCCESS_URL pointing to your success page with the checkout_id placeholder.