O que construí
Date Checklist, um SaaS pra casais montarem a lista de dates juntos:
checklist em tempo real, sortear o próximo date, e uma linha do tempo com as fotos dos
que já rolaram. A feature de fotos é paga: R$ 9,90 vitalício por sala.
Multi-tenant de verdade: cada casal tem uma sala isolada, com pagamento processado
pelo Mercado Pago.
O nicho é o de menos. O ponto deste post é que isso é um produto pago, completo e no ar, não um protótipo. E a parte difícil de um SaaS não é a tela bonita: é cobrar de um estranho na internet sem furar segurança nem lei.
As decisões que couberam nos dois dias
Velocidade não veio de digitar rápido. Veio de cortar o caminho certo nas decisões de arquitetura, e de recusar as que dariam retrabalho depois.
- Clone limpo, não migração. Era um app pessoal sem multi-tenancy. Migrar o banco pra um modelo de salas seria mais lento e arriscado que começar do zero com o schema certo. O app pessoal ficou intacto; o SaaS nasceu com Supabase próprio.
-
RLS real, sem brigar com recursão. Isolamento por sala exige Row
Level Security. Mas uma policy que consulta a tabela de membros, que tem policy que
consulta a si mesma, entra em recursão. A saída foi encapsular o "sou membro desta
sala?" numa função
security definer, que roda como dono e quebra o ciclo. -
is_premiumimutável pelo cliente. O que o pagamento destrava nunca pode ser ligado pelo front. Só o webhook (com a chave de serviço, que ignora RLS) muda. Column grant bloqueia o resto. -
Cloudflare, não Vercel. O plano grátis da Vercel proíbe uso
comercial nos termos. Vender exigiria o plano pago. Cloudflare Pages grátis
permite. Migrei as functions pro formato Web/
fetch+ Web Crypto. - SMTP próprio, não pagar pelo nativo. O e-mail nativo do Supabase tem rate limit baixo (é "só pra teste"), e pagar o plano não resolve isso. SMTP externo (Brevo, 300/dia grátis) com o domínio autenticado resolveu, e os links de login pararam de cair em spam.
Padrão: cada "atalho" foi na verdade uma decisão pra não pagar dívida técnica depois. Rápido e descartável são coisas diferentes.
O que quebrou e o que cada bug ensinou
Lançar de verdade é onde os detalhes aparecem. Quatro que custaram tempo e viraram regra reutilizável:
"Uma das partes é de teste"
O pagamento recusava mesmo com a credencial de produção (APP_USR-). O
detalhe: o token existe, mas as credenciais de produção do Mercado Pago só passam a
valer depois de ativadas (preencher ramo + site). Antes disso, ele
opera como teste.
A sessão sumia ao voltar do pagamento
Pagava, voltava pro site e estava deslogado. Causa: localStorage é
por origem, e o retorno do gateway apontava pra um domínio diferente
do que o usuário tinha acessado. Regra: as URLs de retorno (e o redirect do login)
têm que voltar pro mesmo host de onde veio a pessoa.
O convite caía na sala errada
Dois bugs juntos: o modelo N:N permitia múltiplas salas e o app pegava a "mais antiga"; e uma corrida de tempo no carregamento fazia a tela de onboarding montar e consumir o convite antes da sala real carregar. Solução: uma sala por pessoa (limpa vínculos antigos ao entrar) + segurar o "carregando" até o fetch real terminar.
Texto fantasma no iOS
Um botão trocava de texto e o WebKit não repintava: ficava o texto antigo
"queimado" por baixo do novo. Só no iOS. Fix: forçar o React a recriar o elemento
com uma key por estado, em vez de reaproveitar o nó.
Conformidade não foi opcional
Cobrar de estranho na internet sobe o modelo de ameaça, e é aqui que a maioria dos "fiz em um fim de semana" para. Eu não pulei:
- Auditoria legal dos termos e da política de privacidade (revisão multi-perspectiva + um advogado de verdade revisando).
- LGPD/CDC na prática: confirmação de maioridade e consentimento explícito no cadastro, reembolso de 7 dias, e exclusão de conta automatizada: um endpoint que apaga os dados, as fotos no storage e o login de verdade, não "manda um e-mail que a gente resolve".
- Dado pessoal do dono fora do site público, e cláusulas de incidente e transferência internacional escritas (não copiadas de template genérico).
O que dá pra levar disso
- A velocidade está nas decisões, não na digitação. As horas que eu não gastei foram as que recusei: migração, host errado, e-mail caro, RLS recursiva.
- Cortar caminho ≠ pular o que importa. Segurança, pagamento e legal foram feitos direito. É justamente o que separa um SaaS de um protótipo.
- Bug em produção é dado. Cada um dos quatro virou uma regra que eu carrego pro próximo projeto.
- "Completo" inclui a parte chata. Legal, conformidade e exclusão de conta não são polimento, são o produto sendo vendável.
Dois dias, do zero ao cobrando. O produto está no ar em datechecklist.com.br. Se você precisa de um MVP ou SaaS feito assim, rápido mas sem pular a parte que importa, é o tipo de coisa que eu faço.