Documentation

Facturation (admin + user)

Pages billing user et admin — refunds, comp days, vue MRR/ARR/churn, factures Stripe.

Deux pages distinctes :

  • User/account/settings → Facturation : son plan, ses factures, son code coupon actif
  • Admin/admin/billing : KPI revenu + liste des subs avec actions par row

User-side

API GET /api/billing/me agrège en un payload :

  • subscription (status / period end / canceledAt) depuis la DB
  • plan (name / prix / yearly?) en matchant stripePriceId
  • coupon actif (code / kind / appliedUntil)
  • invoices (6 derniers via Stripe API : number, amount, status, pdfUrl)

UI affiche tout. Boutons :

  • Stripe Portal (POST /api/billing/portal) — modifier carte
  • Changer de plan/account/upgrade

Si pas de sub : "Plan gratuit" + CTA upgrade.

Résiliation

Quand l'utilisateur clique sur le plan gratuit depuis sa page billing, un dialog de confirmation s'affiche (pas de redirection Stripe). Il conserve son accès jusqu'à currentPeriodEnd.

POST /api/billing/cancel
  • Appelle stripe.subscriptions.update({ cancel_at_period_end: true })
  • Met à jour canceledAt en DB immédiatement (sans attendre le webhook)
  • Audit subscription.canceled

L'UI bascule le bouton sur "Résiliation en cours…" (désactivé) dès que subscription.canceledAt est renseigné.

Réactivation

Si l'abonnement est en cours de résiliation (canceledAt renseigné mais subscription encore active), le bouton du plan actuel devient "Réactiver l'abonnement" (vert).

POST /api/billing/reactivate
  • Appelle stripe.subscriptions.update({ cancel_at_period_end: false })
  • Vide canceledAt en DB immédiatement
  • Audit subscription.reactivated

Le webhook customer.subscription.updated confirme les deux opérations (idempotent).

Admin-side

/admin/billing :

  • KPI cards : MRR, ARR, abonnés payants, churn 30j
  • Liste paginée des Subscription avec user inclus
  • Filtres : recherche email + status
  • Actions par row (menu) :
ActionEffet
Ouvrir dans Stripelien direct vers le customer Stripe
Rembourser le dernier paiementrefund Stripe API + audit
Offrir N jours d'accèsétend stripeCurrentPeriodEnd (DB only)
Annuler à la fin de périodecancel_at_period_end Stripe

API : POST /api/admin/billing/[subscriptionId] avec { action: "refund_last" | "comp_days" | "cancel_at_period_end" }.

Comp days vs Stripe

L'action comp_days est volontairement DB only — on étend la date de fin de période sans toucher à Stripe. Si l'user a un abonnement actif, Stripe re-charge à la prochaine date Stripe (qui peut être avant). Pour un comp "propre", utilise plutôt :

  • Un coupon FREE_MONTHS Stripe (auto-créé via coupons)
  • Ou LIFETIME si c'est un partenariat permanent

Refunds

refund_last cherche la dernière invoice en status paid du customer et call stripe.refunds.create({ payment_intent, reason }). Reasons acceptés : duplicate, fraudulent, requested_by_customer.

Tracé dans AuditLog (billing.refund avec metadata.refundId + amount).

Packs de crédits

Les credit packs sont des achats one-shot (hors abonnement). Chaque pack est lié à un Stripe Product + Price créé automatiquement.

Sync Stripe automatique

Opération adminEffet Stripe
Créer un packCrée un Product + Price → stocke stripePriceId
Modifier prix ou deviseArchive l'ancien Price, crée un nouveau
Modifier le nomMet à jour le Product.name
Désactiver (active: false)Archive le Price Stripe
RéactiverCrée un nouveau Price actif
SupprimerArchive Price + Product, puis supprime en DB

La sync est best-effort : si Stripe est indisponible lors de la création, le pack est créé en DB sans stripePriceId. Au checkout, si stripePriceId est absent, un price_data inline est utilisé comme fallback.

POST /api/admin/credit-packs          → crée pack + Stripe Product/Price
PATCH /api/admin/credit-packs/[id]    → sync Stripe sur changements
DELETE /api/admin/credit-packs/[id]   → archive Stripe + supprime DB

Checkout

POST /api/billing/credit-pack-checkout   { packId }

Génère une session Stripe Checkout one-time. Le webhook checkout.session.completed (metadata type: "credit_pack") crédite totalCredits = credits + bonusCredits sur le compte user et log un audit credits.purchased.

Allez plus loin

  • Setup Stripe — créer ses clés API, restricted key, Customer Portal
  • Plans — config des plans Stripe
  • Coupons — codes promo (PERCENT / FREE_MONTHS / LIFETIME)
  • Webhooks Stripe — sync DB sur events Stripe

On this page