Pular para o conteúdo

Integracao Stripe (checkout + webhooks + portal de billing)

Stripe e o provider de pagamento padrao para B2B internacional/cartao do Studeia. Checkout self-service, webhooks idempotentes para subscription lifecycle, portal Stripe self-manage, dual currency BRL + USD.

2026-05-24 6 min
Resposta curta

Stripe e provider de pagamento B2B internacional do Studeia. Checkout self-service via Stripe Checkout Session, webhooks idempotentes (HMAC SHA-256 + ordering guard via lastEventAt) para subscription lifecycle, portal Stripe self-manage para admin trocar plano/cartao, dual currency BRL + USD detectado por geolocation. Para PIX/boleto Brasil: Asaas (integration paralela).

Setup

1. Stripe Dashboard

  1. https://dashboard.stripe.com > Developers > API Keys > copie 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. Copie Webhook signing secret

2. Criar produtos + prices

Para cada plano B2B do Studeia (mini, growth, pro_100):

  1. Products > Add product > nome (ex: "Studeia Mini")
  2. Pricing: Recurring monthly + BRL R$ 250.00
  3. Copie price_id (ex: price_1TZk...)
  4. Repita para USD se quiser suportar clientes internacionais

3. Env vars

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_...

Fluxo de checkout

Admin clica "Contratar Mini" em /institution/billing
  ↓
POST /api/institution/billing/checkout
  Body: { planSlug: "mini" }
  ↓
Studeia createCheckoutSession() (lib/billing/create-checkout.ts):
  1. resolveCustomerIdForProvider() — pega/cria stripeCustomerId
  2. Detect currency via getCurrencyFromHeaders() (server-side, anti-fraud)
  3. Stripe Checkout Session com line_items=[price_id correto]
  4. Retorna { url } do Stripe Checkout
  ↓
Frontend redireciona: window.location.href = data.url
  ↓
Admin paga no Stripe Checkout
  ↓
Stripe envia webhook checkout.session.completed
  ↓
Studeia applyWebhookEvent():
  1. Valida HMAC signature
  2. stripe.subscriptions.retrieve(subscriptionId) — sempre re-fetch (nao confia em metadata)
  3. Valida price_id contra allowlist
  4. Cross-check tenantId entre session.metadata e subscription.metadata
  5. Cria/atualiza TenantSubscription com currentPeriodEnd REAL
  6. Promove Tenant.plan
  7. Cria PaymentLog (idempotente via [provider, externalEventId])

Hardening (regras 129-139)

  • Webhook retorna 5xx em falha: Stripe re-tenta 3 dias
  • Ordering guard: lastEventAt + lastEventId em TenantSubscription. Eventos fora de ordem sao pulados com warning + PaymentLog
  • Idempotencia: unique [provider, externalEventId] em PaymentLog
  • Schema Zod: z.enum(PAID_PLAN_SLUGS) no body do checkout (anti-typo)
  • PII redact: PaymentLog.rawPayload passa por redactPaymentPayload() (remove email/name/address/billing_details/cpfCnpj/card.last4 antes de persistir)
  • Past_due gate: isAccessBlocked(status) em layouts (substitui status === "suspended"). Cron /api/cron/billing-grace-expire transiciona apos grace 7d

Portal Stripe self-manage

POST /api/institution/billing/portal retorna URL temporaria do Stripe portal:

  • Admin troca plano (upgrade/downgrade)
  • Atualiza cartao
  • Ve invoices
  • Cancela subscription
  • Sem passar pelo suporte Studeia

Multi-currency

AspectoBRLUSD
DetectionDefaultHeader x-vercel-ip-country / cf-ipcountry
Price IDsSTRIPE_PRICE_MINI, _GROWTH, _PRO_100STRIPE_PRICE_MINI_USD, _GROWTH_USD, _PRO_100_USD
Asaas fallbackSim (PIX/boleto)NAO (Asaas e Brasil only)
WebhookMesmo endpointMesmo endpoint

Veja tambem

FAQ

Stripe e como Studeia recebe pagamento por mensalidade?

Sim, para B2B internacional/cartao. Plano e selecionado em /institution/billing > Studeia chama POST /api/institution/billing/checkout que cria Stripe Checkout Session > admin paga > webhook subscription.activated promove tenant.plan automaticamente. Para PIX/boleto Brasil: use Asaas (integration paralela).

Webhooks Stripe sao confiaveis?

Sim, mas Studeia trata defensivamente. (1) Verifica HMAC SHA-256 via stripe-signature header com STRIPE_WEBHOOK_SECRET. (2) Stripe re-tenta 3 dias com backoff em case de 5xx. (3) Idempotencia via unique [provider, externalEventId] em PaymentLog. (4) Ordering guard via lastEventAt/lastEventId — eventos fora de ordem sao pulados com warning. (5) checkout.session.completed sempre faz stripe.subscriptions.retrieve() pra validar (nao confia em metadata).

Studeia suporta moeda USD para clientes fora do Brasil?

Sim. STRIPE_PRICE_IDS (BRL) + STRIPE_PRICE_IDS_USD (USD) configuraveis via env vars. Detecta moeda server-side via x-vercel-ip-country / cf-ipcountry header. Cliente fora do BR ve preco em USD automaticamente.

Posso usar Stripe Customer Portal para self-service?

Sim. POST /api/institution/billing/portal retorna URL do Stripe portal — admin troca plano, atualiza cartao, ve invoices, cancela. Sem precisar passar pelo suporte.

Veja tambem

Integracao Stripe (checkout + webhooks + portal de billing) | Studeia Docs