Documentação

Pagamentos (Polar)

Billing Polar no Motoko Base — setup, checkout, portal, webhooks e entitlements.

Abrir emChatGPT (abre em uma nova aba)Claude (abre em uma nova aba)Cursor (abre em uma nova aba)Billing Polar no Motoko Base — setup, checkout, portal, webhooks e entitlements.

O Motoko Base usa Polar para assinaturas na v1. O billing é encapsulado em uma API agnóstica ao provedor — o código de feature nunca importa Polar diretamente.

Official docs: Polar documentation

Architecture

Polar (source of truth)
   ↓
polar.customers.getStateExternal(userId)
   ↓
BillingSummary (plan, status, period end)
   ↓
Dashboard UI + hasPlan() / canAccess()

Polar é a fonte da verdade. O Motoko Base não espelha assinaturas no Postgres. O estado do plano é buscado do Polar Customer State e cacheado brevemente (60s). Webhooks invalidam o cache quando as assinaturas mudam.

Better Auth user id = Polar externalId. Um customer Polar é criado no sign-up quando o billing está configurado.

Setup

  1. Crie uma organização Polar — comece no sandbox
  2. Crie um produto de assinatura Pro → copie o product ID
  3. Crie um Organization Access Token
  4. Adicione um webhook → {APP_URL}/api/billing/webhooks/polar
  5. Defina as env vars em .env:
VariablePurpose
BILLING_PROVIDERpolar (default)
POLAR_ACCESS_TOKENOrganization access token
POLAR_WEBHOOK_SECRETWebhook signing secret
POLAR_PRO_MONTHLY_PRODUCT_IDPro monthly product ID
POLAR_PRO_YEARLY_PRODUCT_IDPro yearly product ID (optional)
POLAR_SERVERsandbox or production

Sem as env vars do Polar o app permanece em Free — sem crash, checkout desabilitado.

Checkout & portal

ActionAPIUI
Upgrade to Procheckout(userId, "pro")Billing page → Upgrade
Manage subscriptionopenPortal(userId)Billing page → Manage billing

O checkout redireciona para o Polar Checkout. O portal abre o Polar Customer Portal (cancelar, atualizar método de pagamento, faturas).

Webhooks

Rota: src/app/api/billing/webhooks/polar/

O Polar envia eventos de assinatura → assinatura verificada → cache de billing invalidado → evento de analytics capturado. Sem ledger local de assinaturas.

Assine eventos de assinatura + customer.state_changed no dashboard do Polar. Tipos de evento não tratados são ignorados com segurança.

Entitlements

import { hasPlan, canAccess } from "@/lib/billing";

await hasPlan(userId, "pro");
await canAccess(userId, "ai"); // feature → plan map in billingConfig

O mapeamento feature→plan fica em src/lib/billing/config.ts. Features demo não são gated na v1 — chame canAccess quando adicionar gates pagos.

Labels de plano na UI: src/lib/billing/config/plans.ts.

Sandbox testing

  1. Defina POLAR_SERVER=sandbox e token/product ID de sandbox
  2. Use cartões de teste do Polar no checkout — veja Polar sandbox docs
  3. Confirme a entrega de webhooks no dashboard do Polar (use ngrok localmente se precisar)
  4. Após o checkout, abra /dashboard/settings/administration/billing?checkout=success — o plano deve atualizar

Mude para POLAR_SERVER=production com credenciais de produção para billing ao vivo.

Where to change this

WhatWhere
Public billing APIsrc/lib/billing/index.ts
Plans & feature gatessrc/lib/billing/config.ts
Polar providersrc/lib/billing/providers/polar/
Webhook handlersrc/lib/billing/providers/polar/webhooks.ts
Billing UI & actionssrc/features/dashboard/actions/billing.ts, src/features/dashboard/components/settings/billing-*

Não importe @polar-sh/* de features ou UI — use apenas @/lib/billing.


Environment Variables — todas as env vars do Polar.