Covers architecture, configuration steps, user flows, API routes, webhooks, pricing, testing with Stripe CLI, production checklist, and troubleshooting.
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
- 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).