← Voltar para o início

ExTrip

Estudo de caso

Uma plataforma para organizar excursões de ponta a ponta: da venda das vagas por um link ao embarque no ônibus por QR Code. Criei do zero, sozinho, e está no ar.

Papel
Tudo: produto, back-end, app, site, painel e infraestrutura
Status
No ar em extrip.com.br, com o app publicado como PWA
Plataformas
Web (PWA), Android e iOS a partir do mesmo código
ExTrip: venda as vagas por um link, receba por Pix ou cartão e embarque todo mundo com QR Code
4
sistemas: API, app, site de vendas e painel
300+
testes automatizados a cada publicação
36
tipos de aviso por e-mail, cada um com a sua chave
5
versões guardadas para voltar em segundos

O problema

Excursão ainda é organizada no improviso.

Um grupo de WhatsApp, uma planilha, comprovante de Pix por print e uma lista de papel na porta do ônibus. Quem organiza passa a semana cobrando, conferindo pagamento à mão e controlando desistências e lista de espera de cabeça.

O ExTrip troca isso por um link. O organizador cria a excursão, divulga onde quiser e a página faz a venda: o passageiro reserva e paga ali mesmo, sem criar conta, e a vaga é confirmada sozinha quando o pagamento cai.

O que o produto faz

Dois públicos, duas experiências.

Para quem organiza

  • Cria a excursão com destino, data, vagas, preço e até 5 pontos de embarque.
  • Recebe por Pix ou cartão em até 12x, direto na própria conta do Mercado Pago.
  • Acompanha quem pagou, quem falta e onde cada um embarca. Lotou? A lista de espera chama o próximo.
  • Importa participantes por planilha, com uma prévia que aponta cada erro antes de gravar.
  • Divide o embarque com uma equipe, que vê só o necessário para embarcar, sem valores nem documentos.
  • Recebe a lista de passageiros em PDF ou planilha, pronta para a empresa de ônibus.

Para quem viaja

  • Compra pelo link da excursão, com Pix ou cartão, sem precisar criar conta.
  • Tem o QR de embarque, o grupo e o ponto de embarque num link só.
  • Cancela, transfere a vaga ou troca o ponto de embarque, conforme as regras da excursão.
  • Avalia a excursão depois da viagem, com fotos.

Como funciona uma venda

Do link ao embarque, sem ninguém conferir nada à mão.

  1. 01

    Cria a excursão

    Destino, data, vagas, preço e pontos de embarque.

  2. 02

    Divulga o link

    No WhatsApp, no Instagram ou onde quiser.

  3. 03

    Passageiro compra

    Informa os dados e paga por Pix ou cartão, sem criar conta.

  4. 04

    Vaga confirmada

    O Mercado Pago avisa o ExTrip e a vaga é confirmada sozinha.

  5. 05

    Minha Vaga

    Um link privado com o QR de embarque e tudo da viagem.

  6. 06

    Embarque

    O organizador ou a equipe lê o QR com a câmera do celular.

Telas

O app no dia a dia.

Tela do ExTrip: Painel do organizador
Painel do organizadorRecebido, a receber, check-ins e a próxima saída.
Tela do ExTrip: Participantes
ParticipantesQuem pagou, quem embarcou e de onde veio a reserva.
Tela do ExTrip: Compra pelo link
Compra pelo linkPreço, vagas livres e pagamento por Pix ou cartão.
Tela do ExTrip: Minha vaga
Minha vagaEmbarque, agenda, organizador e convite para amigos.

Como é construído

Quatro sistemas, uma infraestrutura.

Organizador

app

Passageiro

site

Administrador

painel

Cloudflare

HTTPS, proxy e cache

Servidor Linux

App

Flutter (PWA)

Site de vendas

Razor Pages

API

.NET 8

Painel

Razor Pages

MariaDB

backup diário

Mercado Pago

pagamentos

R2

fotos

Resend

e-mails

Serviços externos na última linha.
API
ASP.NET Core (.NET 8), EF Core e MariaDB. Regras de negócio no domínio, validação com FluentValidation.
App
Flutter, publicado primeiro como PWA. O mesmo código gera o app nativo.
Site de vendas
Razor Pages: página da excursão, compra e a “Minha Vaga” de quem comprou, sem precisar de conta.
Painel
Razor Pages num projeto separado: pessoas, dinheiro, operação e servidor, tudo num lugar só.
Infra
Servidor Linux atrás da Cloudflare, imagens no R2, e-mails pelo Resend, deploy pelo GitHub Actions.
  • .NET 8
  • ASP.NET Core
  • EF Core
  • MariaDB
  • Flutter
  • Razor Pages
  • Mercado Pago
  • Cloudflare R2
  • GitHub Actions
  • xUnit

Por dentro do código

Para quem quer ver como funciona por baixo.

As escolhas de arquitetura, concorrência, segurança e entrega que sustentam o produto.

10
projetos na solução .NET
32
entidades de domínio
108
endpoints na API
54
migrations
~85 mil
linhas de C# e Dart
317
testes automatizados

ExTrip.Api

REST + JWT

PublicWeb

Razor, anônimo

Admin

Razor, restrito

Application

casos de uso, validação, e-mails

Domain

entidades, regras e portas (interfaces)

Infrastructure

EF Core, R2, PDF

MercadoPago

adaptador do gateway

Security

tokens e sessões

As setas mostram a direção das dependências: tudo aponta para o domínio.

Camadas com o domínio no centro

O domínio não depende de nenhum outro projeto. Banco, armazenamento, e-mail e o gateway de pagamento entram por portas definidas nele (como IPaymentGateway e IUnitOfWork) e são implementados nas bordas.

Os três hosts (API, site e painel) compartilham as regras e a infraestrutura, e um projeto de hospedagem comum cuida do que todos precisam para rodar atrás da Cloudflare: IP real, health check e proteção das chaves.

Na prática, quando a cobrança precisou trocar de API do Mercado Pago em produção, a mudança ficou concentrada no adaptador: cinco dos sete arquivos de código alterados estavam nele.

O fluxo de um pagamento

  1. 01

    Pedido sob lock

    O pedido é gravado com a chave de idempotência sob o lock da linha da excursão, que serializa as compras da mesma vaga.

  2. 02

    Provedor fora da transação

    A cobrança vai ao Mercado Pago fora da transação, com X-Idempotency-Key e política de resiliência (retry, circuit breaker e timeout). Segurar o lock durante uma chamada lenta pararia a fila de compradores.

  3. 03

    Resultado e conferência

    O resultado é gravado depois, e na mesma transação entra uma tarefa agendada que confere o pedido se o aviso do provedor não chegar.

  4. 04

    Webhook verificado

    O aviso do Mercado Pago só é aceito com a assinatura HMAC-SHA256 válida. A confirmação é idempotente: aviso repetido não confirma a vaga duas vezes.

Tarefas em segundo plano

  • Tarefas agendadas na mesma transação do evento que as cria: vencer reservas, conferir cobranças, refazer avisos que falharam e enviar e-mails em lote.
  • Um despachante na API acorda na próxima tarefa e trava as que vai executar com SKIP LOCKED, então duas instâncias nunca pegam a mesma.
  • O despachante nunca deixa exceção escapar: um serviço de fundo que morre calado pararia o vencimento de reservas até o próximo deploy.

Segurança em camadas

  • JWT com verificação explícita do algoritmo, refresh token rotacionado a cada renovação e sessões que o próprio usuário pode revogar.
  • No PWA, o refresh token fica num cookie HttpOnly; no app nativo, no Keystore/Keychain.
  • Tokens de terceiros cifrados com AES-256-GCM, com nonce novo a cada gravação. Nenhum token vai para log.
  • Limites de requisição por rota no site público e uma raiz de injeção de dependência estreita para as páginas anônimas.
  • Painel com Content Security Policy restritiva e confirmação de ações sensíveis feita no servidor.

Pipeline de publicação

  1. 01

    Versão fixada

    O commit é fixado ao disparar; o que chegar depois não entra.

  2. 02

    Testes

    xUnit contra um MariaDB descartável e análise estática do app Flutter.

  3. 03

    Build e envio

    Compila no GitHub e envia com uma chave que só pode escrever na entrada.

  4. 04

    Migrations

    Aplicadas por bundle do EF Core antes de o processo novo subir.

  5. 05

    Troca e health check

    Troca a versão e espera o /saude responder.

  6. 06

    Rollback

    Sem resposta em cerca de 60 s, volta sozinho para a versão anterior.

Bibliotecas principais

  • EF Core + Pomelo
  • FluentValidation
  • QuestPDF
  • ClosedXML
  • ImageSharp
  • AWS SDK (R2)
  • Http.Resilience
  • SDK do Mercado Pago
  • QRCoder
  • Swashbuckle
  • xUnit
  • provider
  • go_router
  • dio
  • flutter_secure_storage
  • mobile_scanner

Painel administrativo

A operação inteira, sem abrir um terminal.

  • Aprovação de organizadores, reembolsos, pagamentos online e moderação de avaliações e fotos.
  • Publicação dos quatro serviços com um clique ou agendada, e volta para uma das cinco últimas versões em segundos.
  • Uma chave por tipo de aviso por e-mail, e uma chave geral para emergências.
  • Ações sensíveis confirmadas no servidor e histórico de quem mudou o quê.
  • Monitoramento, tarefas agendadas, backup e configuração do servidor sem abrir um terminal.

Decisões técnicas

O porquê de cada escolha.

01Pagamento dentro da página, sem redirecionar

No celular, cada redirecionamento perde comprador. Usei o checkout transparente do Mercado Pago: o Pix aparece na própria página e o cartão é tokenizado no navegador, então o número nunca passa pelo ExTrip e nenhum cartão é guardado. As mensagens de aprovação, recusa e pendência passaram a ser nossas.

02O dinheiro vai direto para o organizador

Cada organizador conecta a própria conta do Mercado Pago por OAuth, e a comissão do ExTrip sai no mesmo pagamento. Descartei receber tudo e repassar depois: mudaria o enquadramento fiscal do produto e criaria um risco que não precisava existir.

03PWA primeiro, app nativo depois

Publicar como PWA põe o app no iPhone e no Android sem esperar a revisão das lojas. A sessão muda conforme a plataforma: no nativo os tokens ficam no Keystore/Keychain; no navegador, onde não existe armazenamento fora do alcance do JavaScript, o refresh token fica num cookie HttpOnly.

04Preço que não muda depois da venda

A configuração comercial da excursão é versionada e só recebe acréscimos: salvar cria uma versão nova, e cada pedido aponta para a que valia na hora da compra. A taxa é calculada uma vez, truncando os centavos para nunca cobrar mais que o anunciado, e fica gravada no pedido.

05Site público e painel em processos separados

O site de vendas é anônimo e tem um contêiner de injeção de dependência estreito de propósito: o que não está registrado não pode ser chamado por engano. O painel, que ativa contas e mexe com dinheiro, roda em outro projeto, então um erro de rota numa página pública nunca vira acesso administrativo.

06Deploy com volta automática

O GitHub Actions compila, roda os testes e envia a versão. No servidor, as migrations são aplicadas antes de o processo novo subir; se a versão nova não responder ao health check em cerca de um minuto, o serviço volta sozinho para a anterior.

Qualidade e cuidado com dados

Seguro e testado desde o primeiro dia.

  • Mais de 300 testes automatizados (xUnit) rodam a cada publicação. Teste quebrado para o deploy antes de compilar.
  • Os fluxos de dinheiro são testados de ponta a ponta contra um banco descartável e um Mercado Pago simulado, incluindo avisos simultâneos.
  • Trava na linha da excursão: duas compras ao mesmo tempo nunca vendem a mesma vaga.
  • Cartão tokenizado no navegador e tokens do Mercado Pago cifrados no banco com AES-256-GCM.
  • Termos e política de privacidade com aceite versionado, exclusão de conta, log de auditoria e exclusão lógica.
  • Fotos reprocessadas em WebP e sem metadados, para não carregar dados como a localização de quem tirou.
  • Roteiro de resposta a incidentes alinhado à LGPD.

Precisa de um sistema assim?

O mesmo cuidado que coloquei no ExTrip vai para o projeto da sua empresa, do levantamento dos requisitos ao sistema em produção.