surfaai · parte 1 de 8

Construindo o surfaai do zero: stack, decisões e o que eu faria diferente

30 de setembro de 20264 min de leituraCesar Eduardo Sturmer

Depois de alguns anos como engenheiro de software em empresas — arquitetura de frontend, sistemas distribuídos, produtos que outras pessoas definiam — decidi construir algo do zero, sozinho: do primeiro commit até estar rodando em produção, com usuários reais e dinheiro de verdade passando pelo sistema.

Esse algo é o surfaai.

Esta é a primeira de uma série de artigos técnicos sobre as decisões que tomei construindo o surfaai. Não é um tutorial de "como fazer X" — é um relato real de arquitetura: os trade-offs que pesei, os erros que cometi, e o que eu faria diferente se começasse hoje. Se você já pensou em sair de só integrar sistemas dos outros para construir o seu, ou só curte ver decisão técnica real — com o porquê, não só o resultado final —, essa série é para você.

Por que o surfaai

O surfaai nasceu de um problema simples: conectar alunos a providers numa plataforma que cuidasse de agendamento, pagamento e repasse automático — sem que cada provider precisasse resolver seu próprio checkout, suas taxas e seu split.

Toda plataforma que conecta duas pontas com dinheiro no meio esbarra cedo ou tarde na mesma pergunta difícil: quem fica responsável pela parte financeira? Deixar cada provider resolver isso por conta própria significa fricção de onboarding, inconsistência de experiência para o aluno e nenhum controle real sobre a operação. Foi esse problema que me fez construir a camada de pagamento dentro do produto, em vez de empurrá-la para fora dele.

A stack

  • Next.js + React + TypeScript no front e nas rotas de API
  • Tailwind para a UI
  • Supabase (Postgres + Auth + RLS) como backend
  • Stripe Connect Express para pagamentos e repasse aos providers

Cada peça foi escolhida pensando em um time pequeno mantendo um produto com dinheiro real passando por ele — produtividade de desenvolvimento sem abrir mão de segurança e corretude financeira.

O desafio central: marketplace de verdade precisa de split

A parte mais delicada de um marketplace não é o cadastro nem o agendamento — é garantir que o dinheiro do aluno chegue certo ao provider, com a taxa da plataforma descontada corretamente, de forma auditável e sem condições de corrida.

Isso significou usar Stripe Connect com destination charges. O PaymentIntent é criado na plataforma, com a taxa de aplicação e o destino do repasse apontando para a conta Connect do provider:

const paymentIntent = await stripe.paymentIntents.create({
  amount: calculation.amountInCents,
  currency: "brl",
  application_fee_amount: calculation.applicationFeeAmount,
  transfer_data: {
    destination: stripeAccountId,
  },
  // ...
});

São duas linhas que carregam muita coisa: o Stripe faz a transferência do líquido para o provider automaticamente assim que o pagamento confirma, e a plataforma nunca custodia o dinheiro de terceiros. Detalho essa arquitetura — e por que não fiz o split na mão — nos próximos dois artigos.

Uma decisão que valeu a pena: créditos só nascem no webhook

Desde cedo, créditos de compra nunca são criados no client-side — só a partir do evento payment_intent.succeeded do webhook do Stripe.

Isso resolve dois problemas de uma vez. O primeiro é duplicação por condição de corrida: o usuário atualiza a página de confirmação duas vezes e ganha crédito dobrado. O segundo é mais sutil — métodos assíncronos como PIX e boleto simplesmente não têm um "momento no front" em que o pagamento confirma. O webhook é o único lugar onde cartão, PIX e boleto convergem para o mesmo caminho.

Esse é o assunto dos artigos 4 e 5 da série: o incidente real que me ensinou isso, e o padrão que saiu dele.

Migração de gateway em produção

O surfaai não nasceu no Stripe. Começou no Asaas, e migrei com usuários ativos e compras acontecendo. Foi a decisão mais arriscada do projeto, e o artigo 7 conta como foi feito sem interromper o fluxo de compra de quem estava usando.

O que vem a seguir

Nos próximos artigos:

  • por que Stripe Connect Express em vez de split manual
  • destination charges na prática, com o código e a matemática das taxas
  • o bug de crédito duplicado: um postmortem
  • idempotência em webhook de pagamento, o padrão completo
  • migração de gateway com a plataforma em produção

Se você constrói produtos com pagamento embutido, ou só curte ver decisão real de arquitetura com os trade-offs e os erros no caminho, essa série é para você.

← Todos os artigos