Payments · modules/billing

Production-ready adapter

Polar,
end to end.

Polar is the most complete of the three billing adapters: it runs on @polar-sh/sdk with checkout, a hosted customer portal, signed-webhook verification, and plan-catalog sync all implemented against the same IPaymentProvider port.

Checkout and webhooks, implemented

One checkouts.create call handles both one-time and subscription products — Polar reads the cadence from the product price, so the mode arg is informational. Incoming webhooks are verified with the SDK helper (Standard Webhooks spec); a bad signature maps to a ValidationError so the HTTP layer returns 400.

polar-payment-provider.ts
 1  const checkout = await this.client.checkouts.create({
 2    products: [productId],
 3    externalCustomerId: input.customer.internalId,
 4    successUrl: input.successUrl,
 5  });
 6  
 7  // Standard-Webhooks signature verification via the SDK:
 8  payload = validateEvent(req.rawBody, req.headers, this.cfg.webhookSecret);

Everything wired

Lazy customer mapping

externalCustomerId keys everything by your own user/org id; Polar lazily creates its customer on first checkout — no pre-provisioning step.

Hosted portal

createCustomerPortalLink calls customerSessions.create and returns a hosted portal URL — no billing UI to build.

Event mapping

parseWebhook maps subscription.created/updated/active/canceled/revoked and order.paid onto the normalized BillingEvent.

Plan sync

listPlans paginates products.list({ isArchived: false }) and reads metadata.features / metadata.highlight into the Plan DTO.

Sandbox isolation

POLAR_SERVER (sandbox | production) picks the API host. It defaults to sandbox in the env schema so a fresh clone never hits live by accident.

Same port as the rest

Polar returns the identical Result types as Stripe and MercadoPago, so switching to it is a one-file change.

Configuration reference

Environment variables

  • PAYMENT_PROVIDERrequired

    Set to polar

  • POLAR_ACCESS_TOKENrequired
  • POLAR_WEBHOOK_SECRETrequired
  • POLAR_SERVERoptional

    sandbox | production — env default: sandbox

Where it lives

  • apps/server/src/modules/billing/infrastructure/providers/polar-payment-provider.ts
  • apps/server/src/modules/billing/infrastructure/providers/index.ts

Ship merchant-of-record billing today.

Polar is fully wired — checkout, portal, webhooks and plan sync — behind the same contract as Stripe and MercadoPago.