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
- In Polar, create products for one‑time purchases and/or subscription plans.
- Copy each product’s ID.
- 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
- User clicks Subscribe on Pricing.
- If logged out, send them to Sign up with:
product_id=<polar-product>redirect_to=/dashboard
- After auth, redirect to Dashboard with that
product_id. SubscribeRedirectorreads the URL, calls a server action to create Polar checkout, then redirects the user to Polar.
One‑time purchase flow
- User clicks Buy on Pricing.
- The button calls a server action (e.g.,
startCheckoutAction) withproduct_id. - The action creates a Polar checkout and returns a
redirectUrl. - The client redirects to Polar (no authentication required).
- 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
- Endpoint URL (local or prod):
/api/webhooks/polar - Secret: set
POLAR_WEBHOOK_SECRETin.envand verify signatures in the handler. - Events to enable:
subscription.createdsubscription.updatedsubscription.canceledorder.createdorder.refunded
What the handler does
- Verify the signature and parse the event.
- On
subscription.created/subscription.updated: look up the user by e‑mail, mapproduct_id→subscription_plans.id, then upsertsubscriptionswith:subscription_plan_id,subscription_id,customer_id,status,current_period_start,current_period_end,canceled_at. - On
subscription.canceled: set status tocanceledand recordcanceled_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
200even 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 3000Use the generated ngrok URL in two places:
.env:POLAR_SUCCESS_URL=https://<ngrok-id>.ngrok-free.app/success?checkout_id={CHECKOUT_ID}- 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.