Diego
← Voltar para projetos

Byro

E-commerce de eBooks — API Laravel + React, com checkout Mercado Pago.

Back-end (API)

  • PHP 8.3
  • Laravel 11
  • PostgreSQL 16
  • Sanctum
  • HMAC-SHA256
  • Mercado Pago SDK
  • Scramble (OpenAPI 3.1)
  • Pest 3

Front-end

  • React 18
  • Vite 6
  • React Router v7
  • Tailwind v3
  • Zustand
  • Axios
  • date-fns
  • Mercado Pago SDK React

Infra & Deploy

  • Docker
  • Docker Compose
  • Nginx
  • Caddy
  • Hetzner VPS
  • Vercel

Full-screen para maior qualidade.

Contexto

Byro é um e-commerce de eBooks. A plataforma cobre cadastro, autenticação por token, catálogo, carrinho, checkout via Mercado Pago e entrega do PDF por URL assinada.

O back-end trata cenários comuns em integrações com gateways de pagamento: recebimento de webhooks fora de ordem, coexistência entre IPN legado e Webhooks v2, validação de assinatura HMAC sujeita ao parser nativo do PHP, e liberação idempotente do produto frente a múltiplas notificações para o mesmo pagamento.

O front-end implementa integração com gateway (webhook, retorno e os três estados do Mercado Pago).

Decisões técnicas

API · Validação HMAC com defesa em profundidade

O Mercado Pago assina cada webhook com HMAC-SHA256 sobre um manifest no formato id:{paymentId};request-id:{xRequestId};ts:{ts};. O id precisa vir da query string crua: o parse_str() do PHP renomeia data.id para data_id automaticamente, o que quebra a assinatura. Parse manual do QUERY_STRING preserva o nome do parâmetro.

O formato do manifest varia entre topics e versões da API. A validação compara as variações possíveis com a assinatura recebida usando hash_equals (comparação timing-safe). Se nenhuma bate, um modo controlado por config registra o caso e confirma o pagamento direto na API do Mercado Pago via access_token — defesa em profundidade: o produto não é liberado sem confirmação, e uma variação de formato não prevista não derruba a venda.

API · Signed URLs em vez de download autenticado direto

Servir o PDF direto de uma rota autenticada acopla o download ao token do Sanctum e quebra quando o usuário troca de aba, abre em outro dispositivo ou usa um gerenciador de download. A solução é uma URL assinada de 30 minutos (URL::signedRoute), que embute o ebookId e o e-mail do usuário e é validada pelo middleware signed do Laravel.

Resultado: nenhum estado para gerenciar, expiração nativa e nome de arquivo personalizado gerado a partir do e-mail embutido na URL.

API · Scramble no lugar de l5-swagger

Documentar a API em OpenAPI com darkaonline/l5-swagger exige anotações #[OA\Post(...)] e #[OA\Property(...)] em cada endpoint. Essa informação já vive no routes/api.php, no FormRequest e no return type — repetir tudo em atributos duplicaria a mesma definição em vários lugares e deixaria a documentação fácil de desatualizar.

dedoc/scramble lê rotas, FormRequests e return types automaticamente e gera o OpenAPI 3.1 sem anotação. PHPDoc curto por endpoint cobre o resumo e os status menos óbvios (404/409). A documentação fica sempre sincronizada com o código, na mesma UI Swagger em /docs/api.

Front · Zustand + persist para o carrinho

Para um catálogo dinâmico, o carrinho precisou de estado global. Zustand resolve com store pequena (~25 linhas), seleção granular por hook (useCartStore(s => s.items.length) só re-renderiza quando contagem muda), e middleware persist para sincronização com localStorage.

Carrinho sobrevive a reload e logout — comportamento esperado para visitante não autenticado que encheu carrinho antes de criar conta.