Documentação
Pagamentos (Polar)
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
- Crie uma organização Polar — comece no sandbox
- Crie um produto de assinatura Pro → copie o product ID
- Crie um Organization Access Token
- Adicione um webhook →
{APP_URL}/api/billing/webhooks/polar - Defina as env vars em
.env:
| Variable | Purpose |
|---|---|
BILLING_PROVIDER | polar (default) |
POLAR_ACCESS_TOKEN | Organization access token |
POLAR_WEBHOOK_SECRET | Webhook signing secret |
POLAR_PRO_MONTHLY_PRODUCT_ID | Pro monthly product ID |
POLAR_PRO_YEARLY_PRODUCT_ID | Pro yearly product ID (optional) |
POLAR_SERVER | sandbox or production |
Sem as env vars do Polar o app permanece em Free — sem crash, checkout desabilitado.
Checkout & portal
| Action | API | UI |
|---|---|---|
| Upgrade to Pro | checkout(userId, "pro") | Billing page → Upgrade |
| Manage subscription | openPortal(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 billingConfigO 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
- Defina
POLAR_SERVER=sandboxe token/product ID de sandbox - Use cartões de teste do Polar no checkout — veja Polar sandbox docs
- Confirme a entrega de webhooks no dashboard do Polar (use ngrok localmente se precisar)
- 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
| What | Where |
|---|---|
| Public billing API | src/lib/billing/index.ts |
| Plans & feature gates | src/lib/billing/config.ts |
| Polar provider | src/lib/billing/providers/polar/ |
| Webhook handler | src/lib/billing/providers/polar/webhooks.ts |
| Billing UI & actions | src/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.