Overview

aSaaSin uses Polar for subscriptions and one-time purchases. Pricing is displayed from the database, and a webhook grants benefits (Discord role, repo/file access) after successful checkout.

What you get

  • One-click checkout with Polar
  • Monthly & yearly subscriptions
  • One-time purchases (guest checkout)
  • Webhook-based benefit delivery
  • Public, read-only pricing table

Flows at a glance

Subscription flow

  1. Unauthenticated user clicks Subscribe → redirected to sign-up with ?variant_id=<polar-product-id>&redirect_to=/checkout.
  2. After auth, /auth/callback reads params → SubscribeRedirector creates a Polar checkout session server-side.
  3. User completes payment on Polar's hosted page → redirected to POLAR_SUCCESS_URL (must include {CHECKOUT_ID} placeholder).

One-time purchase flow

  1. User clicks Buy → server action creates Polar checkout session → user redirected to Polar.
  2. Polar redirects to POLAR_SUCCESS_URL on completion.
  3. Success page reads checkout_id from query params and finalizes fulfillment.

Webhook setup

Webhooks keep your database in sync with Polar. Configure the endpoint in Polar → Webhooks: https://yourdomain.com/api/webhooks/polar.

Required events:

  • subscription.created — new subscription activated
  • subscription.updated — plan change or renewal
  • subscription.canceled — subscription ended
  • order.created — one-time purchase completed
  • order.refunded — refund issued
// app/api/webhooks/polar/route.ts (simplified)
export async function POST(req: Request) {
  const body = await req.text()
  const signature = req.headers.get('webhook-signature') ?? ''
  const event = await polar.webhooks.constructEvent(body, signature, process.env.POLAR_WEBHOOK_SECRET!)

  if (event.type === 'subscription.created' || event.type === 'subscription.updated') {
    // Map event.data.productId → subscription_plans.id
    // Upsert into subscriptions table
  }
  if (event.type === 'subscription.canceled') {
    // Set subscriptions.status = 'canceled', store canceled_at
  }
}

The handler resolves product_id to an internal subscription_plan_id via the subscription_plans table where polar_product_id matches. Always make webhook handlers idempotent — Polar may deliver the same event more than once.

Environment variables

  • POLAR_API_KEY — API key from the Polar dashboard.
  • POLAR_WEBHOOK_SECRET — used to verify the signature on each webhook request.
  • POLAR_SUCCESS_URL — redirect URL after checkout; must include the {CHECKOUT_ID} placeholder, e.g. https://yourdomain.com/success?checkout_id={CHECKOUT_ID}.

For local development, use ngrok to expose your local server and set the tunnel URL as the webhook endpoint in Polar Sandbox. Update POLAR_SUCCESS_URL in .env to use the ngrok URL too.