Files
Momento/docs/stripe-billing-guide.md
Antigravity bb75b2e763
All checks were successful
Deploy to Production / Build and Deploy (push) Successful in 4s
docs: add comprehensive Stripe billing guide
Covers architecture, configuration steps, user flows, API routes,
webhooks, pricing, testing with Stripe CLI, production checklist,
and troubleshooting.
2026-05-16 21:10:26 +00:00

19 KiB

Momento — Guide Stripe : Configuration, Architecture et Utilisation

Vue d'ensemble

Momento utilise Stripe pour la gestion des abonnements payants (Pro, Business, Enterprise). Le système repose sur :

  • Stripe Embedded Checkout (modal dans l'app, sans redirection)
  • Webhooks pour synchroniser l'état des abonnements en temps réel
  • Customer Portal pour que les utilisateurs gèrent leur carte/facturation
  • Un feature flag pour activer/désactiver le billing sans déploiement

1. Architecture

┌─────────────────────────────────────────────────────────┐
│                    Frontend (React)                      │
│                                                         │
│  /settings/billing                                      │
│  └─ billing-plans.tsx                                   │
│     ├─ Cartes Free / Pro / Business / Enterprise        │
│     ├─ Toggle mensuel/annuel                            │
│     ├─ Embedded Checkout (modal Stripe)                 │
│     └─ Lien vers Customer Portal                       │
│                                                         │
│  /settings → SettingsNav → onglet "Facturation"        │
│  Sidebar → UsageMeter → lien vers /settings/billing    │
└───────────────────────┬─────────────────────────────────┘
                        │
           POST /api/billing/create-checkout
           POST /api/billing/portal
           GET  /api/billing/status
                        │
┌───────────────────────▼─────────────────────────────────┐
│                  Backend (Next.js API)                   │
│                                                         │
│  lib/stripe.ts           → Client Stripe singleton      │
│  lib/billing/            → Logique métier billing       │
│    ├─ stripe-prices.ts   → Mapping priceId ↔ tier       │
│    └─ sync-subscription-from-stripe.ts                   │
│          → Upsert Prisma Subscription depuis webhook    │
│                                                         │
│  POST /api/billing/webhook  → Reception événements      │
│                                Stripe (raw body + sig)   │
└───────────────────────┬─────────────────────────────────┘
                        │
           ┌────────────▼────────────┐
           │    PostgreSQL (Prisma)   │
           │                         │
           │  Subscription {         │
           │    userId               │
           │    tier (BASIC/PRO/     │
           │      BUSINESS/ENTERP.)  │
           │    status (ACTIVE/      │
           │      CANCELED/...)      │
           │    stripeCustomerId     │
           │    stripeSubscriptionId │
           │    stripePriceId        │
           │    currentPeriodStart   │
           │    currentPeriodEnd     │
           │    cancelAtPeriodEnd    │
           │  }                      │
           └────────────┬────────────┘
                        │
           ┌────────────▼────────────┐
           │    Redis                │
           │                         │
           │  usage:{userId}:        │
           │    {feature}:{period}   │
           │  → Compteurs mensuels   │
           └─────────────────────────┘

2. Configuration pas à pas

2.1 Créer un compte Stripe

  1. Aller sur https://dashboard.stripe.com/register
  2. Créer un compte (utiliser l'email sales@memento-note.com)
  3. Activer le mode test (toggle en haut à droite)

2.2 Créer les produits et prix

Dans le Stripe DashboardProduits :

Produit 1 : Momento Pro

Champ Valeur
Nom Momento Pro
Description Pour les consultants et créateurs exigeants

Créer 2 prix :

Prix Montant Récurrent
Pro Mensuel 9,90 EUR Tous les mois
Pro Annuel 99 EUR Tous les ans

Produit 2 : Momento Business

Champ Valeur
Nom Momento Business
Description Pour les équipes et chefs de produit

Créer 2 prix :

Prix Montant Récurrent
Business Mensuel 29,90 EUR Tous les mois
Business Annuel 299 EUR Tous les ans

Enterprise n'a pas de prix Stripe — c'est un contact manuel (sales@memento-note.com).

2.3 Récupérer les clés et price IDs

Dans Stripe DashboardDéveloppeursClés API :

Variable Où la trouver
STRIPE_SECRET_KEY Clés API → Clé secrète (sk_test_...)
NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY Clés API → Clé publiable (pk_test_...)

Dans Stripe DashboardProduits → cliquer sur chaque prix :

Variable Source
STRIPE_PRICE_PRO_MONTHLY Produit Pro → Prix mensuel → price_1xxx...
STRIPE_PRICE_PRO_ANNUAL Produit Pro → Prix annuel → price_1yyy...
STRIPE_PRICE_BUSINESS_MONTHLY Produit Business → Prix mensuel → price_1zzz...
STRIPE_PRICE_BUSINESS_ANNUAL Produit Business → Prix annuel → price_1www...

2.4 Configurer le webhook

Dans Stripe DashboardDéveloppeursWebhooks :

  1. Cliquer Ajouter un endpoint
  2. URL : https://memento-note.com/api/billing/webhook
  3. Événements à écouter :
    • checkout.session.completed
    • customer.subscription.created
    • customer.subscription.updated
    • customer.subscription.deleted
    • invoice.payment_failed
  4. Copier la signature secrèteSTRIPE_WEBHOOK_SECRET (commence par whsec_...)

2.5 Configurer le Customer Portal

Dans Stripe DashboardParamètresCustomer Portal :

  1. Activer le portal
  2. Configurer les fonctions autorisées :
    • Mettre à jour le moyen de paiement
    • Voir les factures
    • Annuler l'abonnement
    • Changer de plan (optionnel)
  3. URL de retour : https://memento-note.com/settings/billing

2.6 Remplir le fichier .env

Ajouter dans memento-note/.env :

# Stripe — Server-side (NE JAMAIS exposer côté client)
STRIPE_SECRET_KEY="sk_test_VOTRE_CLE_SECRETE"
STRIPE_WEBHOOK_SECRET="whsec_VOTRE_SIGNATURE_WEBHOOK"

# Stripe — Price IDs
STRIPE_PRICE_PRO_MONTHLY="price_ID_PRO_MENSUEL"
STRIPE_PRICE_PRO_ANNUAL="price_ID_PRO_ANNUEL"
STRIPE_PRICE_BUSINESS_MONTHLY="price_ID_BUSINESS_MENSUEL"
STRIPE_PRICE_BUSINESS_ANNUAL="price_ID_BUSINESS_ANNUEL"

# Stripe — Client-side (sûr à exposer)
NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY="pk_test_VOTRE_CLE_PUBLIABLE"

# Activer l'interface de facturation
NEXT_PUBLIC_FEATURE_BILLING_ENABLED="true"

Important

: Relancer le serveur après modification du .env.

2.7 Résumé des variables

Variable Obligatoire
STRIPE_SECRET_KEY .env (server) Oui
STRIPE_WEBHOOK_SECRET .env (server) Oui
STRIPE_PRICE_PRO_MONTHLY .env (server) Oui
STRIPE_PRICE_PRO_ANNUAL .env (server) Oui
STRIPE_PRICE_BUSINESS_MONTHLY .env (server) Oui
STRIPE_PRICE_BUSINESS_ANNUAL .env (server) Oui
NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY .env (client+server) Oui
NEXT_PUBLIC_FEATURE_BILLING_ENABLED .env (client+server) Oui

3. Flux utilisateur

3.1 Checkout (achat d'un plan)

Utilisateur                    Frontend                    Backend                  Stripe
    │                            │                          │                        │
    │  Clique "Passer Pro"       │                          │                        │
    │───────────────────────────>│                          │                        │
    │                            │  POST /api/billing/      │                        │
    │                            │       create-checkout    │                        │
    │                            │  {tier: "PRO",           │                        │
    │                            │   interval: "month"}     │                        │
    │                            │─────────────────────────>│                        │
    │                            │                          │  Crée/respire          │
    │                            │                          │  Customer + Session    │
    │                            │                          │───────────────────────>│
    │                            │                          │                        │
    │                            │                          │  {clientSecret}        │
    │                            │                          │<───────────────────────│
    │                            │                          │                        │
    │                            │  Modal Embedded          │                        │
    │                            │  Checkout Stripe         │                        │
    │                            │<─────────────────────────│                        │
    │                            │                          │                        │
    │  Remplit carte 4242...     │                          │                        │
    │───────────────────────────>│                          │                        │
    │                            │  Paiement Stripe         │                        │
    │                            │─────────────────────────────────────────────────>│
    │                            │                          │                        │
    │                            │                          │  Webhook:              │
    │                            │                          │  checkout.session.     │
    │                            │                          │  completed             │
    │                            │                          │<───────────────────────│
    │                            │                          │                        │
    │                            │                          │  Upsert Subscription   │
    │                            │                          │  tier=PRO status=ACTIVE│
    │                            │                          │                        │
    │  Toast "Bienvenue Pro !"   │                          │                        │
    │  UsageMeter rafraîchi      │                          │                        │
    │<───────────────────────────│                          │                        │

3.2 Webhook — Cycle de vie des abonnements

Événement Stripe Action Momento Statut Prisma
checkout.session.completed Upsert subscription avec tier/periode ACTIVE
customer.subscription.created Upsert (nouvelle souscription) Selon Stripe
customer.subscription.updated Upsert (changement de plan, etc.) Selon Stripe
customer.subscription.deleted Marquer annulé CANCELED
invoice.payment_failed Sync subscription (passage en PAST_DUE) PAST_DUE

3.3 Mapping statuts Stripe → Prisma

Stripe Prisma Comportement getEffectiveTier()
active ACTIVE Retourne le tier payé (PRO/BUSINESS)
trialing TRIALING Retourne le tier (accès complet pendant l'essai)
past_due PAST_DUE Garde le tier jusqu'à currentPeriodEnd
canceled / unpaid CANCELED Garde le tier jusqu'à currentPeriodEnd, puis BASIC
incomplete_expired INACTIVE Retourne BASIC

4. Pages et composants

4.1 Accès utilisateur

URL Description
/settings/billing Page principale : plans, usage, facturation
/settings → onglet "Facturation" Navigation via SettingsNav
UsageMeter (sidebar) → "Upgrade" Lien direct vers /settings/billing

4.2 Composants clés

Fichier Rôle
components/settings/billing-plans.tsx Cartes de plans, checkout modal, usage grid, portal
components/settings/SettingsNav.tsx Onglet "Facturation" avec icône CreditCard
components/settings/billing-history.tsx Lien vers le portal Stripe pour factures
components/usage-meter.tsx Meter sidebar → lien vers billing quand quota dépassé

4.3 API Routes

Route Méthode Auth Description
/api/billing/create-checkout POST Session Crée une session checkout embedded
/api/billing/portal POST Session Ouvre le portal client Stripe
/api/billing/status GET Session Retourne tier, status, periodEnd
/api/billing/webhook POST Signature Stripe Reçoit les événements lifecycle

4.4 Librairies backend

Fichier Rôle
lib/stripe.ts Client Stripe singleton (getStripe())
lib/billing/stripe-prices.ts resolvePriceId() et priceIdToTier()
lib/billing/sync-subscription-from-stripe.ts Upsert Prisma depuis webhook Stripe
lib/entitlements.ts getEffectiveTier(), TIER_LIMITS, getUserQuotas()

5. Quotas par tier

Feature BASIC PRO BUSINESS ENTERPRISE
Recherche sémantique 30 100 1000
Tags automatiques 20 200 1000
Titres automatiques 10 200 1000
Reformulation 50 500
Chat IA 100 1000
Brainstorm (création) 1 30 200
Brainstorm (expansion) 10 100 500
Brainstorm (enrichissement) 20 200 1000

Les compteurs sont stockés dans Redis avec un TTL de 90 jours. Le tier détermine les limites, pas les compteurs.


6. Tests

Tests unitaires (22 tests, 100% passent)

Fichier Tests Description
tests/unit/billing-price-map.test.ts 10 Mapping priceId → tier, erreurs si env manquant
tests/unit/billing-sync.test.ts 12 Upsert Prisma, mapping statuts Stripe→Prisma, métadonnées

Lancer les tests

cd memento-note
npx vitest run tests/unit/billing-price-map.test.ts tests/unit/billing-sync.test.ts

7. Test local avec Stripe CLI

7.1 Installer Stripe CLI

# macOS
brew install stripe/stripe-cli/stripe

# Linux
curl -s https://packages.stripe.dev/api/security/keypair/stripe-cli-gpg/public/download.gpg | sudo tee /usr/share/keyrings/stripe.gpg
echo "deb [signed-by=/usr/share/keyrings/stripe.gpg] https://packages.stripe.dev/stripe-cli-debian-local stable main" | sudo tee -a /etc/apt/sources.list.d/stripe.list
sudo apt update && sudo apt install stripe

7.2 Forward des webhooks en local

stripe listen --forward-to localhost:3000/api/billing/webhook

Cette commande affiche la signature webhook (whsec_...) à mettre dans STRIPE_WEBHOOK_SECRET.

7.3 Tester un paiement

  1. Lancer le serveur : npm run dev
  2. Aller sur /settings/billing
  3. Cliquer "Passer au Plan Pro"
  4. Utiliser la carte test : 4242 4242 4242 4242
  5. Date d'expiration : n'importe quelle date future
  6. CVC : n'importe quels 3 chiffres
  7. Le webhook se déclenche → Subscription upserté dans la DB

7.4 Vérifier en base

SELECT tier, status, "currentPeriodStart", "currentPeriodEnd"
FROM "Subscription"
WHERE "userId" = 'VOTRE_USER_ID';

7.5 Vérifier via l'API

curl http://localhost:3000/api/billing/status -H "Cookie: next-auth.session-token=VOTRE_TOKEN"

Devrait retourner :

{
  "tier": "PRO",
  "effectiveTier": "PRO",
  "status": "ACTIVE",
  "currentPeriodEnd": "2026-06-16T...",
  "cancelAtPeriodEnd": false,
  "hasStripeSubscription": true
}

8. Passage en production

8.1 Checklist

  • Compte Stripe vérifié (KYC complété)
  • Produits et prix créés en mode live
  • Webhook configuré en mode live avec l'URL de production
  • Variables .env mises à jour avec les clés live (sk_live_..., pk_live_...)
  • NEXT_PUBLIC_FEATURE_BILLING_ENABLED="true"
  • Customer Portal configuré pour le mode live
  • Tester un paiement réel avec une vraie carte

8.2 Sécurité

  • Jamais de STRIPE_SECRET_KEY ou STRIPE_WEBHOOK_SECRET côté client
  • Les price IDs sont côté serveur uniquement
  • Le webhook vérifie la signature Stripe-Signature sur chaque requête
  • L'upsert par stripeSubscriptionId garantit l'idempotence

9. Dépannage

Les cartes de plans ne s'affichent pas

→ Vérifier NEXT_PUBLIC_FEATURE_BILLING_ENABLED="true" dans .env et relancer le serveur.

Le checkout ne s'ouvre pas

→ Vérifier NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY est défini. Regarder la console navigateur.

Le webhook ne se déclenche pas

→ En local : utiliser stripe listen --forward-to localhost:3000/api/billing/webhook → En production : vérifier l'URL du webhook dans le Stripe Dashboard.

L'abonnement reste BASIC après paiement

→ Vérifier les logs du webhook ([billing/webhook]). Le userId doit être dans les metadata de la session.

getStripe() throw "STRIPE_SECRET_KEY is required"

→ Ajouter STRIPE_SECRET_KEY dans .env.


10. Tarification

Tier Mensuel Annuel Économie annuelle
Gratuit 0 €
Pro 9,90 € 99 € ~17%
Business 29,90 € 299 € ~17%
Enterprise Sur devis Sur devis

Devise : EUR (configurable dans Stripe Dashboard pour multi-devises).