Documentación

Pagos (Polar)

Facturación Polar en Motoko Base — configuración, checkout, portal, webhooks y entitlements.

Abrir enChatGPT (se abre en una pestaña nueva)Claude (se abre en una pestaña nueva)Cursor (se abre en una pestaña nueva)Facturación Polar en Motoko Base — configuración, checkout, portal, webhooks y entitlements.

Motoko Base usa Polar para suscripciones en v1. La facturación está envuelta en una API agnóstica al proveedor — el código de feature nunca importa Polar directamente.

Official docs: Polar documentation

Architecture

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

Polar es la fuente de verdad. Motoko Base no refleja las suscripciones en Postgres. El estado del plan se obtiene del Polar Customer State y se cachea brevemente (60s). Los webhooks invalidan la caché cuando cambian las suscripciones.

Better Auth user id = Polar externalId. Se crea un cliente Polar en el sign-up cuando la facturación está configurada.

Setup

  1. Crea una organización Polar — empieza en sandbox
  2. Crea un producto de suscripción Pro → copia el product ID
  3. Crea un Organization Access Token
  4. Añade un webhook → {APP_URL}/api/billing/webhooks/polar
  5. Define las env vars en .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

Sin las env vars de Polar la app permanece en Free — sin crash, checkout deshabilitado.

Checkout & portal

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

Checkout redirige a Polar Checkout. El portal abre Polar Customer Portal (cancelar, actualizar método de pago, facturas).

Webhooks

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

Polar envía eventos de suscripción → se verifica la firma → se invalida la caché de billing → se captura un evento de analytics. Sin ledger local de suscripciones.

Suscríbete a los eventos de suscripción + customer.state_changed en el dashboard de Polar. Los tipos de evento no manejados se ignoran de forma segura.

Entitlements

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

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

El mapeo feature→plan vive en src/lib/billing/config.ts. Las features demo no están gated en v1 — llama a canAccess cuando añadas gates de pago.

Etiquetas de plan en la UI: src/lib/billing/config/plans.ts.

Sandbox testing

  1. Define POLAR_SERVER=sandbox y el token/product ID de sandbox
  2. Usa las tarjetas de prueba de Polar en checkout — consulta Polar sandbox docs
  3. Confirma la entrega de webhooks en el dashboard de Polar (usa ngrok en local si hace falta)
  4. Tras el checkout, abre /dashboard/settings/administration/billing?checkout=success — el plan debería actualizarse

Cambia a POLAR_SERVER=production con credenciales de producción para facturación en 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-*

No importes @polar-sh/* desde features o UI — usa solo @/lib/billing.


Environment Variables — todas las env vars de Polar.