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