Depois de alguns anos como engenheiro de software em empresas — frontend architecture, 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 pra construir o seu, ou só curte ver decisão técnica real (com o porquê, não só o resultado final), essa série é pra você.
Por que o surfaai
O surfaai nasceu de um problema simples: conectar alunos a providers (instrutores/prestadores de serviço) numa plataforma que cuidasse de agendamento, pagamento e repasse automático — sem que cada provider precisasse resolver seu próprio checkout, emissão, taxas e 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 pro aluno e nenhum controle real sobre a operação. Foi esse o problema que me fez decidir construir a própria camada de pagamento da plataforma, em vez de terceirizar isso pra fora do produto.
Sou o desenvolvedor principal do projeto — arquitetura, backend, frontend e pagamentos —, e este é o primeiro de uma série de artigos técnicos sobre as decisões que tomamos construindo isso em produção.
A stack
- Next.js + React + TypeScript no front e nas rotas de API
- Tailwind pra UI
- Supabase (Postgres + Auth + RLS) como backend
- Stripe Connect (Express) para pagamentos e repasse a providers
Cada peça dessa stack foi escolhida pensando em um time pequeno mantendo um produto com dinheiro real passando por ele — ou seja, produtividade de desenvolvimento sem abrir mão de segurança e corretude financeira.
O desafio central: marketplace de verdade precisa de split de pagamento
A parte mais delicada de um marketplace não é o cadastro ou 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 race conditions.
Isso significou usar Stripe Connect com destination charges: o PaymentIntent é criado na plataforma, com uma taxa de aplicação (application_fee_amount) e o destino do repasse (transfer_data.destination) apontando pra conta Connect do provider. O Stripe cuida da transferência líquida automaticamente assim que o pagamento é confirmado.
Vou detalhar essa arquitetura no próximo artigo da série.
Uma decisão que valeu a pena: créditos só nascem no webhook
Desde o início, decidimos que créditos de compra nunca são criados no client-side — só no evento payment_intent.succeeded do webhook do Stripe. Isso evita duplicação de crédito por race condition (por exemplo, usuário atualizando a página de confirmação duas vezes) e torna o sistema auditável: toda criação de crédito tem uma origem única e rastreável.
Migração de gateway em produção
O surfaai não nasceu no Stripe — começou com Asaas e migramos para Stripe já com usuários ativos. Foi uma das decisões técnicas mais arriscadas do projeto, e vou contar como fizemos isso sem quebrar nada em outro artigo desta série.
O que vem a seguir
Nos próximos posts vou destrinchar:
- Como funciona destination charges na prática (com código)
- Por que idempotência em webhook de pagamento não é opcional
- Como migramos de gateway de pagamento sem downtime
Se você constrói produtos com pagamento embutido, ou só curte ver decisões reais de arquitetura (com os trade-offs e os erros no caminho), me segue aqui e no GitHub.
Top comments (0)