Documentation
Paiements (Polar)
Facturation Polar dans Motoko Base — configuration, checkout, portail, webhooks et entitlements.
Motoko Base utilise Polar pour les abonnements en v1. La facturation est encapsulée dans une API indépendante du fournisseur — le code de feature n'importe jamais Polar directement.
Official docs: Polar documentation
Architecture
Polar (source of truth)
↓
polar.customers.getStateExternal(userId)
↓
BillingSummary (plan, status, period end)
↓
Dashboard UI + hasPlan() / canAccess()Polar est la source de vérité. Motoko Base ne reflète pas les abonnements dans Postgres. L'état du plan est récupéré depuis Polar Customer State et mis en cache brièvement (60s). Les webhooks invalident le cache lorsque les abonnements changent.
Better Auth user id = Polar externalId. Un client Polar est créé à l'inscription lorsque la facturation est configurée.
Setup
- Créez une organisation Polar — commencez en sandbox
- Créez un produit d'abonnement Pro → copiez le product ID
- Créez un Organization Access Token
- Ajoutez un webhook →
{APP_URL}/api/billing/webhooks/polar - Définissez les variables d'environnement dans
.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 |
Sans les variables Polar, l'app reste sur Free — pas de crash, checkout désactivé.
Checkout & portal
| Action | API | UI |
|---|---|---|
| Upgrade to Pro | checkout(userId, "pro") | Billing page → Upgrade |
| Manage subscription | openPortal(userId) | Billing page → Manage billing |
Le checkout redirige vers Polar Checkout. Le portail ouvre Polar Customer Portal (annuler, mettre à jour le moyen de paiement, factures).
Webhooks
Route : src/app/api/billing/webhooks/polar/
Polar envoie des événements d'abonnement → signature vérifiée → cache billing invalidé → événement analytics capturé. Pas de ledger local d'abonnements.
Abonnez-vous aux événements d'abonnement + customer.state_changed dans le dashboard Polar. Les types d'événements non gérés sont ignorés en toute sécurité.
Entitlements
import { hasPlan, canAccess } from "@/lib/billing";
await hasPlan(userId, "pro");
await canAccess(userId, "ai"); // feature → plan map in billingConfigLe mapping feature→plan se trouve dans src/lib/billing/config.ts. Les features demo ne sont pas gated en v1 — appelez canAccess lorsque vous ajoutez des portes payantes.
Libellés de plan dans l'UI : src/lib/billing/config/plans.ts.
Sandbox testing
- Définissez
POLAR_SERVER=sandboxet le token/product ID sandbox - Utilisez les cartes de test Polar au checkout — voir Polar sandbox docs
- Confirmez la livraison des webhooks dans le dashboard Polar (utilisez ngrok en local si besoin)
- Après le checkout, ouvrez
/dashboard/settings/administration/billing?checkout=success— le plan devrait se mettre à jour
Passez à POLAR_SERVER=production avec des credentials de production pour la facturation live.
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'importez pas @polar-sh/* depuis les features ou l'UI — utilisez uniquement @/lib/billing.
Environment Variables — toutes les variables Polar.