Aller au contenu
Studeia Docs
AI-assisted translation — last updated 2026-05-24. For original (pt-BR or en-US), use the language switcher.

Intégration Stripe (checkout + webhooks + portail de billing)

Stripe est le provider de paiement B2B de Studeia : checkout self-service, webhooks idempotents pour le cycle de vie des abonnements, portail self-manage et double devise R$ + USD

2026-05-24 6 min
Resposta curta

Stripe est le provider de paiement B2B international de Studeia. Checkout self-service via Stripe Checkout Session, webhooks idempotents (HMAC SHA-256 + garde d'ordonnancement via lastEventAt) pour le cycle de vie des abonnements, portail Stripe self-manage pour que l'admin change de plan/carte, double devise BRL + USD détectée par géolocalisation. Pour PIX/boleto au Brésil : Asaas (intégration parallèle).

Configuration

1. Tableau de bord Stripe

  1. https://dashboard.stripe.com > Developers > API Keys > copiez la Secret Key
  2. Webhooks > Add endpoint > URL : https://[tenant].studeia.com/api/webhooks/stripe
  3. Events to send :
    • checkout.session.completed
    • customer.subscription.created
    • customer.subscription.updated
    • customer.subscription.deleted
    • invoice.payment_succeeded
    • invoice.payment_failed
  4. Copiez le Webhook signing secret

2. Créer les produits + prices

Pour chaque plan B2B de Studeia (mini, growth, pro_100) :

  1. Products > Add product > nom (ex : "Studeia Mini")
  2. Pricing : Recurring monthly + BRL R$ 250,00
  3. Copiez le price_id (ex : price_1TZk...)
  4. Répétez pour USD si vous souhaitez prendre en charge les clients internationaux

3. Variables d'environnement

STRIPE_SECRET_KEY=sk_live_...
STRIPE_WEBHOOK_SECRET=whsec_...
STRIPE_PRICE_MINI=price_...
STRIPE_PRICE_GROWTH=price_...
STRIPE_PRICE_PRO_100=price_...
STRIPE_PRICE_MINI_USD=price_...
STRIPE_PRICE_GROWTH_USD=price_...
STRIPE_PRICE_PRO_100_USD=price_...

Flux de checkout

L'admin clique sur "Souscrire Mini" dans /institution/billing
  ↓
POST /api/institution/billing/checkout
  Body: { planSlug: "mini" }
  ↓
Studeia createCheckoutSession() (lib/billing/create-checkout.ts) :
  1. resolveCustomerIdForProvider() — récupère/crée stripeCustomerId
  2. Détecte la devise via getCurrencyFromHeaders() (côté serveur, anti-fraude)
  3. Stripe Checkout Session avec line_items=[price_id correct]
  4. Retourne { url } du Stripe Checkout
  ↓
Le frontend redirige : window.location.href = data.url
  ↓
L'admin paie sur Stripe Checkout
  ↓
Stripe envoie le webhook checkout.session.completed
  ↓
Studeia applyWebhookEvent() :
  1. Valide la signature HMAC
  2. stripe.subscriptions.retrieve(subscriptionId) — toujours re-fetch (ne fait pas confiance aux métadonnées)
  3. Valide le price_id contre la liste blanche
  4. Vérification croisée du tenantId entre session.metadata et subscription.metadata
  5. Crée/met à jour TenantSubscription avec currentPeriodEnd RÉEL
  6. Promeut Tenant.plan
  7. Crée PaymentLog (idempotent via [provider, externalEventId])

Durcissement (règles 129-139)

  • Le webhook retourne 5xx en cas d'échec : Stripe réessaie pendant 3 jours
  • Garde d'ordonnancement : lastEventAt + lastEventId dans TenantSubscription. Les événements hors ordre sont ignorés avec avertissement + PaymentLog
  • Idempotence : unique [provider, externalEventId] dans PaymentLog
  • Schéma Zod : z.enum(PAID_PLAN_SLUGS) dans le body du checkout (anti-typo)
  • Suppression des PII : PaymentLog.rawPayload passe par redactPaymentPayload() (supprime email/name/address/billing_details/cpfCnpj/card.last4 avant persistance)
  • Blocage past_due : isAccessBlocked(status) dans les layouts (remplace status === "suspended"). Le cron /api/cron/billing-grace-expire effectue la transition après une période de grâce de 7 jours

Portail Stripe self-manage

POST /api/institution/billing/portal retourne l'URL temporaire du portail Stripe :

  • L'admin change de plan (upgrade/downgrade)
  • Met à jour sa carte
  • Consulte les factures
  • Annule l'abonnement
  • Sans passer par le support Studeia

Multi-devise

AspectBRLUSD
DétectionPar défautEn-tête x-vercel-ip-country / cf-ipcountry
Price IDsSTRIPE_PRICE_MINI, _GROWTH, _PRO_100STRIPE_PRICE_MINI_USD, _GROWTH_USD, _PRO_100_USD
Fallback AsaasOui (PIX/boleto)NON (Asaas est Brésil uniquement)
WebhookMême endpointMême endpoint

Voir aussi

FAQ

Stripe est-il le moyen par lequel Studeia reçoit le paiement des abonnements mensuels ?

Oui, pour le B2B international/carte. Le plan est sélectionné dans /institution/billing > Studeia appelle POST /api/institution/billing/checkout qui crée une Stripe Checkout Session > l'admin paie > le webhook subscription.activated promeut tenant.plan automatiquement. Pour PIX/boleto au Brésil : utilisez Asaas (intégration parallèle).

Les webhooks Stripe sont-ils fiables ?

Oui, mais Studeia les traite de manière défensive. (1) Vérifie le HMAC SHA-256 via l'en-tête stripe-signature avec STRIPE_WEBHOOK_SECRET. (2) Stripe réessaie pendant 3 jours avec backoff en cas de 5xx. (3) Idempotence via un [provider, externalEventId] unique dans PaymentLog. (4) Garde d'ordonnancement via lastEventAt/lastEventId — les événements hors ordre sont ignorés avec un avertissement. (5) checkout.session.completed effectue toujours stripe.subscriptions.retrieve() pour valider (ne fait pas confiance aux métadonnées).

Studeia supporte-t-il la devise USD pour les clients hors du Brésil ?

Oui. STRIPE_PRICE_IDS (BRL) + STRIPE_PRICE_IDS_USD (USD) configurables via des variables d'environnement. Détecte la devise côté serveur via l'en-tête x-vercel-ip-country / cf-ipcountry. Les clients hors du Brésil voient automatiquement les prix en USD.

Puis-je utiliser le Stripe Customer Portal pour le self-service ?

Oui. POST /api/institution/billing/portal retourne l'URL du portail Stripe — l'admin change de plan, met à jour sa carte, consulte les factures, annule. Sans avoir à passer par le support.

Veja tambem

Intégration Stripe (checkout + webhooks + portail de billing)