22 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
- Aller sur https://dashboard.stripe.com/register
- Créer un compte (utiliser l'email
sales@memento-note.com) - Activer le mode test (toggle en haut à droite)
2.2 Créer les produits et prix
Dans le Stripe Dashboard → Produits :
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 Dashboard → Développeurs → Clé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 Dashboard → Produits → 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 Dashboard → Développeurs → Webhooks :
- Cliquer Ajouter un endpoint
- URL :
https://memento-note.com/api/billing/webhook - Événements à écouter :
checkout.session.completedcustomer.subscription.createdcustomer.subscription.updatedcustomer.subscription.deletedinvoice.payment_failed
- Copier la signature secrète →
STRIPE_WEBHOOK_SECRET(commence parwhsec_...)
2.5 Configurer le Customer Portal
Dans Stripe Dashboard → Paramètres → Customer Portal :
- Activer le portal
- Configurer les fonctions autorisées :
- Mettre à jour le moyen de paiement
- Voir les factures
- Annuler l'abonnement
- Changer de plan (optionnel)
- 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 | Où | 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
- Lancer le serveur :
npm run dev - Aller sur
/settings/billing - Cliquer "Passer au Plan Pro"
- Utiliser la carte test : 4242 4242 4242 4242
- Date d'expiration : n'importe quelle date future
- CVC : n'importe quels 3 chiffres
- Le webhook se déclenche →
Subscriptionupserté 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
.envmises à 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_KEYouSTRIPE_WEBHOOK_SECRETcô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
stripeSubscriptionIdgarantit 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).
11. Bons de réduction & Codes de promotion (Coupons & Promo Codes)
Momento intègre le support natif et sécurisé de Stripe pour les codes promotionnels lors du paiement en Embedded Checkout via l'attribut allow_promotion_codes: true dans /api/billing/create-checkout/route.ts.
11.1 Concepts Clés : Bon de réduction (Coupon) vs Code de promotion (Promo Code)
Dans Stripe, la gestion des remises se fait en deux niveaux :
- Le Bon de réduction (Coupon) : C'est la règle financière sous-jacente (ex.
-20 % sur l'abonnement pendant 6 moisou-10 € à vie). Un coupon n'est pas vu par le client final sous cette forme. - Le Code de promotion (Promo Code) : C'est la chaîne de caractères réelle saisie par le client (ex.
WELCOME20,LAUNCH50). Un code promo est obligatoirement rattaché à un Coupon. Vous pouvez créer plusieurs codes promos pour un seul et même coupon (ex.INFLUENCEUR1etINFLUENCEUR2qui appliquent tous les deux la même réduction de -10%).
11.2 Comment créer un Code Promo sur Stripe (Dashboard)
- Connectez-vous à votre Stripe Dashboard (en mode test ou live selon votre cible).
- Rendez-vous dans Produits (Products) → Bons de réduction (Coupons) → bouton + Nouveau.
- Remplissez les informations du Coupon :
- Nom : Le nom interne pour vous y retrouver (ex:
Lancement 50%). - Type de réduction : Pourcentage (ex:
50%) ou Montant fixe (ex:10 €). - Durée :
- Une seule fois (appliqué uniquement sur la première facture).
- Plusieurs mois (Spécifier le nombre de mois, ex: 3 mois).
- Pour toujours (appliqué à vie sur toutes les factures de l'abonnement).
- Nom : Le nom interne pour vous y retrouver (ex:
- Sous le bloc Codes de promotion, cochez "Créer un code de promotion orienté client" :
- Code : Saisissez le code en majuscules (ex:
LAUNCH50). - Limites d'utilisation (optionnel) :
- Nombre d'utilisations max (ex: limité aux 100 premiers utilisateurs).
- Date limite de validité (ex: valable uniquement jusqu'au 31 décembre).
- Limiter aux clients n'ayant jamais payé.
- Restriction à des plans spécifiques (ex: restreindre ce code promo uniquement au produit
Momento Pro).
- Code : Saisissez le code en majuscules (ex:
- Cliquez sur Créer le bon de réduction.
11.3 Comment désactiver ou supprimer un Code Promo
- Allez dans Produits → Bons de réduction → Codes de promotion.
- Cliquez sur les
...à côté du code promo concerné. - Sélectionnez Désactiver (Deactivate) : le code ne sera plus utilisable par aucun client, mais les clients en ayant déjà bénéficié conserveront leur réduction active selon la durée définie (ex. s'ils ont déjà eu les 3 mois, Stripe continue de l'appliquer jusqu'à la fin de la période).
- Si vous souhaitez supprimer définitivement un coupon global et annuler la réduction pour tout le monde (y compris les abonnés actuels), allez dans Bons de Réduction, cliquez sur le coupon puis sur Supprimer.
11.4 Mode Test vs Production
- En local / Test : Créez vos codes de promotion dans Stripe en ayant activé le commutateur Mode Test (Test Mode). Les codes créés ici ne fonctionneront qu'avec vos clés d'API de test (
sk_test_...). - En production / Live : Désactivez le Mode Test sur Stripe pour passer en mode réel, et créez les coupons dans la section Live. Ils ne fonctionneront qu'avec vos clés d'API réelles (
sk_live_...).