Saltar al contenido principal

Finances & Abonnements

La section Finances repond a une question essentielle pour la perennite de BeePass : est-ce que la plateforme est economiquement viable ? Elle permet de suivre les revenus des abonnements, de configurer les plans tarifaires et de mettre en regard les couts d'infrastructure -- le tout depuis une interface unique.

Elle regroupe trois volets : le suivi des commandes Stripe, la configuration des plans d'abonnement et le suivi des couts d'infrastructure.

Architecture Stripe

BeePass delegue entierement le paiement a Stripe. Concretement, aucune donnee de carte bancaire ne transite par nos serveurs -- l'eleveur est redirige vers une page Stripe securisee pour payer, puis renvoye sur BeePass. Ce choix simplifie drastiquement la conformite PCI (les normes de securite des paiements par carte).

BeePass utilise Stripe Checkout (mode redirect) + Customer Portal pour la gestion des paiements. Aucune donnee de carte n'est stockee cote serveur (conformite PCI 100% Stripe).

[Eleveur] -> BillsTab (onglet Facturation)
|
|-- GET /api/billing/subscription -> Stripe API + profiles DB
|-- POST /api/billing/checkout -> Stripe Checkout Session -> redirect
|-- POST /api/billing/portal -> Stripe Customer Portal -> redirect
|-- GET /api/billing/invoices -> Stripe invoices
|
[Public] -> GET /api/plans -> stripe_plans DB (cache 5min, no auth)
|
[Admin] -> GET /api/admin/plans -> stripe_plans DB (tous champs + Stripe IDs)
|-- PUT /api/admin/plans -> update config + Stripe Price create/archive
|-- POST /api/admin/plans/sync -> migration env vars -> DB
|-- GET /api/admin/invoices -> factures Stripe + customer details
|-- GET /api/admin/revenue -> revenus mensuels agreges
|
[Stripe] -> POST /api/billing/webhook -> profiles DB (service_role, bypass RLS)

Commandes Stripe

Tableau des factures

Le tableau (/backoffice/orders) centralise toutes les factures emises par Stripe. C'est l'endroit ou aller quand un eleveur demande "Ou est ma facture ?" ou pour verifier qu'un paiement a bien ete traite.

Il affiche l'ensemble des factures via un datatable TanStack React Table :

ColonneDescription
N. factureIdentifiant Stripe de la facture
ClientNom de l'utilisateur associe (enrichi via priceToPlanAsync())
PlanNom du plan d'abonnement facture (resolu depuis stripe_plans DB)
MontantMontant en EUR (TTC)
Statutpaid, open, draft, void
PeriodeMensuel ou annuel
DateDate d'emission de la facture

Filtres et actions

FonctionnaliteDescription
Filtre par statutSelection rapide : Payees, En attente, Brouillon, Annulees
RecherchePar nom de client
Export CSVTelechargement du tableau filtre
Lien StripeClic sur une facture pour l'ouvrir dans le Stripe Dashboard
Telecharger PDFTelechargement de la facture PDF via l'API Stripe

Indicateurs de revenus

Deux cartes synthetisent les revenus en haut de page pour un apercu immediat :

IndicateurDescriptionSource
Revenus du moisTotal des factures payees sur le mois en cours/api/admin/revenue
Montant moyenMontant moyen par facture payee/api/admin/revenue
Synchronisation Stripe

Les donnees financieres sont recuperees depuis l'API Stripe avec un polling de 5 minutes (useAutoRefresh avec interval: 300000 + Realtime sur profiles). Le webhook Stripe met a jour les colonnes billing dans profiles en temps reel, ce qui signifie que le plan d'un eleveur change instantanement apres paiement.

Gestion des plans

Pourquoi configurer les plans en base de donnees plutot qu'en dur dans le code ? Pour pouvoir ajuster les prix, les periodes d'essai ou les fonctionnalites sans avoir a redeployer l'application. Tout est modifiable depuis l'interface admin.

BeePass propose 4 plans d'abonnement configures dans la table stripe_plans (DB-driven) :

PlanSlugMensuelAnnuelEssaiPopulaireCheckout
Decouvertediscovery0 EUR0 EUR--Non--
Eleveurbreeder12 EUR115 EUR30 joursNonStripe
Selectionneurselector39 EUR374 EUR30 joursOuiStripe
Groupe de selectionprogramSur devisSur devis--NonContact
  • Reduction annuelle : -20% (Eleveur 12 EUR/mois -> 115 EUR/an, Selectionneur 39 EUR/mois -> 374 EUR/an)
  • Le plan program n'a pas de checkout Stripe -- le CTA redirige vers le chat pour un contact commercial
  • Le plan discovery est gratuit pour toujours, sans checkout

Schema DB stripe_plans

ColonneTypeDescription
idTEXT PKSlug plan : discovery, breeder, selector, program
is_activeBOOLEAN NOT NULLPlan visible sur Pricing et BillsTab (defaut TRUE)
is_popularBOOLEAN NOT NULLBadge "Recommande" -- exclusif (un seul plan)
monthly_price_centsINTEGERPrix mensuel en centimes (NULL = gratuit/devis)
yearly_price_centsINTEGERPrix annuel en centimes (NULL = gratuit/devis)
trial_daysINTEGER NOT NULLDuree essai gratuit en jours (defaut 0)
stripe_product_idTEXTStripe Product ID (cree au 1er sync ou changement prix)
monthly_stripe_price_idTEXTStripe Price ID actif mensuel
yearly_stripe_price_idTEXTStripe Price ID actif annuel
featuresJSONBArray cles i18n des fonctionnalites du plan
feature_labelsJSONBObject cle -> texte custom (features admin)
sort_orderINTEGER NOT NULLOrdre d'affichage (defaut 0)
updated_atTIMESTAMPTZ NOT NULLAuto via trigger
created_atTIMESTAMPTZ NOT NULLDate de creation

RLS : SELECT ouvert a tous (pricing page publique). Pas de policy INSERT/UPDATE/DELETE -- service_role uniquement (seuls les admins via l'API serveur peuvent modifier les plans).

Configuration par plan

Pour chaque plan, l'administrateur peut modifier depuis /backoffice/plans :

ParametreDescriptionEditable
Prix mensuelMontant en EUR facture chaque moisBreeder, Selector
Prix annuelMontant en EUR facture chaque anneeBreeder, Selector
Jours d'essaiDuree de la periode d'essai gratuit (0 = pas d'essai)Breeder, Selector
ActifPlan visible sur la page tarifs publiqueTous
PopulariteBadge "Recommande" -- exclusif (un seul plan a la fois)Tous
FeaturesListe des fonctionnalites affichees (cles i18n)Tous

Les prix des plans Discovery et Program ne sont pas editables (gratuit / sur devis).

Immutabilite des prix Stripe

Les prix Stripe sont immutables -- c'est une contrainte imposee par Stripe. Une fois cree, un prix ne peut pas etre modifie. Chaque modification de prix declanche : creation d'un nouveau Stripe Price -> archivage de l'ancien. Les abonnements existants conservent leur tarif jusqu'au prochain renouvellement, ce qui signifie qu'un changement de prix n'affecte pas retroactivement les clients deja abonnes.

Flux de modification de prix

Ce schema montre ce qui se passe en coulisses quand un admin change un prix. L'operation est transparente pour l'admin, mais implique plusieurs appels API Stripe.

1. Admin modifie le prix mensuel Eleveur : 12 EUR -> 15 EUR
2. PUT /api/admin/plans { id: "breeder", monthly_price_cents: 1500 }
3. stripe.products.create() si pas de stripe_product_id (lazy)
4. stripe.prices.create({ unit_amount: 1500, currency: 'eur', recurring: { interval: 'month' } })
5. stripe.prices.update(old_id, { active: false }) -- archive
6. UPDATE stripe_plans SET monthly_stripe_price_id = new_id, monthly_price_cents = 1500
7. invalidatePlanCache() -- cache serveur invalide
8. Page Pricing publique affiche le nouveau prix au prochain fetch

Statistiques par plan

Chaque plan affiche dans l'interface admin des informations pour evaluer sa performance :

  • Nombre d'abonnes actifs (badge compteur)
  • MRR (Monthly Recurring Revenue, c'est-a-dire le revenu recurrent mensuel) pour ce plan
  • Stripe Price IDs (copiables dans le clipboard, utiles pour le debugging Stripe)
  • Features editables en inline
  • Historique des modifications (table stripe_plan_history)

Table stripe_plan_history

Chaque modification de plan est enregistree pour garder une trace complete. Cela permet de repondre a des questions comme "Quand le prix du plan Eleveur a-t-il ete augmente ?"

ColonneTypeDescription
plan_idTEXT FKReference vers stripe_plans(id) CASCADE
change_typeTEXTprice_change, trial_change, status_change, popular_change, features_change
old_valuesJSONBValeurs avant modification
new_valuesJSONBValeurs apres modification
created_atTIMESTAMPTZHorodatage

Webhook Stripe

Le webhook est le mecanisme par lequel Stripe informe BeePass qu'un evenement s'est produit (paiement reussi, abonnement annule, etc.). Sans webhook, BeePass ne saurait pas quand mettre a jour le plan d'un utilisateur.

Le webhook (POST /api/billing/webhook) gere 4 types d'evenements :

Event StripeActionNotification
checkout.session.completedRetrieve subscription -> update profiles (plan, status, period_end, trial_end)Email admin Brevo
customer.subscription.updatedSync plan (via priceId), status, period_end, trial_endEmail admin Brevo
customer.subscription.deletedReset a discovery + status canceled + clear stripe IDsEmail admin Brevo
invoice.payment_failedSet status past_due (lookup par stripe_customer_id)Email admin Brevo

Securite webhook :

  • Verification signature Stripe (stripe-signature header + STRIPE_WEBHOOK_SECRET) -- garantit que la requete vient bien de Stripe et n'a pas ete falsifiee
  • Runtime nodejs (pas Edge -- besoin du raw body pour verification signature)
  • Body lu via request.text() (pas .json())
  • DB writes via service_role client (bypass RLS)

Resolution des plans : Le webhook utilise priceToPlanAsync() pour resoudre le plan a partir du price ID. Cette fonction verifie d'abord la table stripe_plans (DB), puis les variables d'environnement en fallback pour les anciens price IDs qui existaient avant la migration vers la table DB.

Notifications admin : Chaque event Stripe declenche un email Brevo aux admins via notifyAdmins('subscription_changes') (fire-and-forget). Template Brevo ID=4. Filtre par notification_prefs.subscription_changes -- les admins peuvent choisir de ne pas recevoir ces notifications.

Emails transactionnels Stripe (v2.7.0)

Depuis EMAIL v2.7.0, les webhooks Stripe declenchent aussi des emails transactionnels aux eleveurs :

  • checkout.session.completed → email confirmation de paiement (template multilingue Brevo)
  • invoice.payment_failed → email echec de paiement (template multilingue Brevo)

Ces emails sont envoyes en parallele avec la notification admin (notifyAdmins('subscription_changes')).

Flux Checkout complet

Voici le parcours complet d'un eleveur qui s'abonne, du clic sur "Passer a ce plan" jusqu'a la confirmation :

1. [BillsTab] Toggle monthly/yearly + Clic "Passer a ce plan"
2. [Frontend] POST /api/billing/checkout { plan: "breeder", billing_period: "yearly" }
3. [API] loadStripePlans() -> verifie plan actif + prix configure
4. [API] getPlanPriceId("breeder", "yearly") -> Stripe Price ID depuis DB
5. [API] Get/create Stripe Customer -> store stripe_customer_id
6. [API] Create Checkout Session (subscription, trial_days dynamique)
7. [API] Return { url: "https://checkout.stripe.com/..." }
8. [Frontend] window.location.href = url (redirect Stripe)
9. [Stripe] Utilisateur saisit carte -> paiement/trial
10. [Stripe] Webhook -> POST /api/billing/webhook (checkout.session.completed)
11. [Webhook] Update profiles: plan, status, period_end, trial_end
12. [Stripe] Redirect -> /account-settings?checkout=success
13. [BillsTab] Toast succes + fetchSubscription() -> affiche nouveau plan

Couts d'infrastructure

Connaitre ses couts d'infrastructure est indispensable pour evaluer la rentabilite de la plateforme. Cette page permet de maintenir un registre a jour de toutes les depenses, et de les comparer aux revenus affiches sur le tableau de bord.

Cette page (/backoffice/services) permet de suivre les depenses recurrentes et ponctuelles liees a l'hebergement et aux services tiers. Les donnees sont stockees dans la table admin_services.

Schema DB admin_services

ColonneTypeDescription
nameTEXT NOT NULLNom du service
amount_centsINTEGER NOT NULLMontant mensuel en centimes (>= 0)
is_recurringBOOLEANtrue = recurrent mensuel, false = ponctuel
notesTEXTNotes complementaires
created_atTIMESTAMPTZDate de creation

Services suivis

ServiceTypeFrequence
Hetzner CCX23Serveur principal (4 vCPU / 16 GB)Mensuel
Hetzner CX53Serveur production (16 vCPU / 32 GB)Mensuel
SupabaseBase de donnees + AuthMensuel
CloudflareCDN + DNS + WAFMensuel
BrevoEmails transactionnels + CRMMensuel
Domaine beepass.ioNom de domaineAnnuel
Certificats SSLVia Cloudflare Origin Cert (inclus, 15 ans)--

Gestion des entrees

ActionDescription
AjouterNouveau service avec nom, montant, frequence, notes
ModifierMise a jour du montant ou des notes
SupprimerRetrait d'un service obsolete

Calcul des totaux

La page calcule automatiquement les couts en ramenant tout a une base comparable :

TotalCalcul
Cout mensuelSomme des services mensuels + (services annuels / 12)
Cout annuelSomme des services annuels + (services mensuels x 12)
Marge operationnelle

En croisant les couts d'infrastructure avec les revenus Stripe du tableau de bord, vous pouvez suivre la marge operationnelle de la plateforme en temps reel. Les widgets Revenue Chart et Monthly Earning affichent les revenus bruts Stripe, tandis que cette page affiche les couts -- la difference donne la marge.

Variables d'environnement

VariableScopeDescription
STRIPE_SECRET_KEYServer onlyCle secrete Stripe (sk_live_... ou sk_test_...)
STRIPE_WEBHOOK_SECRETServer onlySecret webhook Stripe (whsec_...) -- utilise pour verifier la signature des webhooks
NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEYClientCle publique (reserve futur Stripe Elements)
STRIPE_PRICE_BREEDERServer onlyPrice ID legacy Eleveur (fallback pour les abonnements crees avant la migration DB)
STRIPE_PRICE_SELECTORServer onlyPrice ID legacy Selectionneur (fallback)

API Routes

RouteMethodeAuthDescription
/api/billing/subscriptionGETSupabase SSRPlan actuel + moyen de paiement
/api/billing/checkoutPOSTSupabase SSRCreer Checkout Session (monthly/yearly)
/api/billing/portalPOSTSupabase SSRCreer Customer Portal session
/api/billing/webhookPOSTSignature StripeWebhook (4 events)
/api/billing/invoicesGETSupabase SSRFactures eleveur
/api/plansGETAucune (public)Plans actifs sans Stripe IDs (cache 5min)
/api/admin/plansGETCookie HMACTous les plans avec Stripe IDs
/api/admin/plansPUTCookie HMACModifier plan + Stripe Price create/archive
/api/admin/plans/syncPOSTCookie HMACMigration env vars -> DB + creation Stripe Prices manquants
/api/admin/plans/statsGETCookie HMACCompteurs abonnes + MRR par plan
/api/admin/plans/historyGETCookie HMACHistorique modifications pagine
/api/admin/invoicesGETCookie HMACFactures Stripe + customer details
/api/admin/revenueGETCookie HMACRevenus mensuels agreges

Voir aussi : Tableau de bord | Gestion des utilisateurs | Vue d'ensemble