Processamento de pagamentos resiliente, idempotente e escalável.
Este projeto é um Processador de Eventos de Pagamento de alta performance, projetado para resolver um dos problemas mais críticos em sistemas financeiros modernos: a confiabilidade no recebimento de Webhooks.
Imagine que o Stripe (ou qualquer gateway) envia uma notificação de "Pagamento Confirmado", mas seu servidor cai no meio do processamento. Ou pior, ele envia a mesma notificação duas vezes e você libera o produto em dobro para o cliente. Este microserviço foi construído com foco obsessivo em Garantia de Entrega e Consistência de Dados.
Webhooks são comunicações assíncronas entre servidores que podem falhar por diversos motivos: instabilidade na rede, picos de tráfego ou erros temporários no banco de dados. Um sistema financeiro não pode simplesmente "perder" um evento.
Este projeto implementa:
- Idempotência: Garante que mesmo que um evento seja recebido 10 vezes, ele será processado apenas uma vez.
- Resiliência: Se o processamento falhar por um erro temporário, o sistema tenta novamente de forma inteligente (Backoff Exponencial).
- Observabilidade: Monitoramento em tempo real de métricas, erros e saúde do sistema.
Escolhi cada tecnologia com o objetivo de equilibrar performance, tipagem forte e confiabilidade:
- Linguagem: Go (Golang) - Escolhida pela sua concorrência nativa (Goroutines) e baixo consumo de memória.
- Banco de Dados: PostgreSQL - Para garantir transações ACID e persistência robusta dos eventos.
- Fila/Mensageria: Redis Streams - Utilizado para processamento em background, garantindo que o Gateway receba um
202 Acceptedinstantâneo enquanto o Worker processa a lógica pesada. - Infraestrutura: Docker & Docker Compose - Para abstração de ambiente e facilidade de deploy.
- Padrões: Clean Architecture, Repository Pattern e Middleware para segurança (HMAC-SHA256).
Para este projeto, fiz questão de aplicar conceitos que demonstram meu compromisso com a excelência técnica:
- Segurança com HMAC: Cada requisição é validada usando assinaturas criptográficas para garantir que os dados realmente vieram do provedor de pagamentos.
- Tratamento de Erros Semântico: Diferencio erros "Transientes" (conectividade) de "Permanentes" (dados inválidos), evitando retries inúteis em dados corrompidos.
- Dead Letter Queue (DLQ): Eventos que falham após todas as tentativas não são descartados; eles vão para uma fila de análise manual.
- Padrão de Tabelas Cruas (Raw Events): Salvo o payload original antes de qualquer processamento, permitindo auditoria e "replay" de eventos se as regras de negócio mudarem.
Este projeto é um reflexo da minha jornada no desenvolvimento de software. Acredito que um bom programador não é aquele que apenas escreve código que funciona, mas aquele que:
- Antecipa erros (O que acontece se o banco ficar lento?).
- Escreve código para humanos (Nomes de variáveis intuitivos e estrutura limpa).
- É obcecado por testes (Este projeto possui testes unitários e de integração).
Estou em constante evolução, explorando padrões de alta disponibilidade e sistemas distribuídos. Este projeto foi uma oportunidade incrível para aprofundar meu conhecimento em Go e sistemas orientados a eventos.
Explore as pastas para entender a organização:
internal/domain: O coração do projeto, onde as regras de negócio vivem.internal/middleware: Camada de segurança e validação.internal/worker: Lógica asíncrona de consumo de fila.
Este é um projeto de portfólio. Para ver o código completo ou discutir oportunidades, sinta-se à vontade para entrar em contato!
# Variáveis
TIMESTAMP=$(date +%s)
SECRET="whsec_test_secret_key_for_development"
PAYLOAD='{"id":"evt_test_123","type":"payment_succeeded","created":1704672000,"data":{"object":{"id":"in_abc","subscription":"sub_xyz","customer_email":"test@example.com","amount":9900,"currency":"brl","plan_id":"plan_pro"}}}'
# Gerar assinatura
SIGNATURE=$(echo -n "${TIMESTAMP}.${PAYLOAD}" | openssl dgst -sha256 -hmac "${SECRET}" | cut -d' ' -f2)
# Enviar webhook
curl -X POST http://localhost:8080/webhooks/payments \
-H "Content-Type: application/json" \
-H "X-Webhook-Signature: sha256=${SIGNATURE}" \
-H "X-Webhook-Timestamp: ${TIMESTAMP}" \
-d "${PAYLOAD}"# Executar script de teste
.\scripts\test-webhook.ps1 -EventType "payment_succeeded"
# Ou manualmente
$timestamp = [int][double]::Parse((Get-Date -UFormat %s))
$secret = "whsec_test_secret_key_for_development"
$payload = '{"id":"evt_test_123","type":"payment_succeeded","created":1704672000,"data":{"object":{"id":"in_abc","subscription":"sub_xyz","customer_email":"test@example.com","amount":9900,"currency":"brl"}}}'
$signedPayload = "$timestamp.$payload"
$hmac = New-Object System.Security.Cryptography.HMACSHA256
$hmac.Key = [System.Text.Encoding]::UTF8.GetBytes($secret)
$hash = $hmac.ComputeHash([System.Text.Encoding]::UTF8.GetBytes($signedPayload))
$signature = [BitConverter]::ToString($hash).Replace("-","").ToLower()
Invoke-RestMethod -Uri "http://localhost:8080/webhooks/payments" `
-Method Post `
-Headers @{
"Content-Type" = "application/json"
"X-Webhook-Signature" = "sha256=$signature"
"X-Webhook-Timestamp" = "$timestamp"
} `
-Body $payloadpayment-event-processor/
├── cmd/
│ ├── api/
│ │ └── main.go # HTTP API server
│ └── worker/
│ └── main.go # Queue consumer worker
├── internal/
│ ├── config/ # Configuração via env vars
│ ├── database/ # Conexão PostgreSQL
│ ├── domain/ # Entidades de domínio
│ ├── handler/ # HTTP handlers
│ ├── middleware/ # Auth, rate limit, logging
│ ├── queue/ # Redis Streams
│ ├── repository/ # Acesso a dados
│ └── worker/ # Processamento de eventos
├── migrations/ # SQL migrations
├── examples/ # Payloads de exemplo
├── scripts/ # Scripts de teste
├── docker-compose.yml
├── Dockerfile
├── Makefile
└── go.mod
| Variável | Descrição | Default |
|---|---|---|
PORT |
Porta do servidor HTTP | 8080 |
ENV |
Ambiente (development/production) | development |
DATABASE_URL |
URL de conexão PostgreSQL | - |
DATABASE_MAX_CONNS |
Conexões máximas do pool | 10 |
REDIS_URL |
URL de conexão Redis | - |
WEBHOOK_SECRET |
Secret HMAC para verificação | - |
WEBHOOK_TOLERANCE_SECONDS |
Tolerância de timestamp | 300 |
RATE_LIMIT_REQUESTS |
Requests por janela | 100 |
RATE_LIMIT_WINDOW_SECONDS |
Janela de rate limit | 60 |
WORKER_CONCURRENCY |
Número de workers | 5 |
WORKER_MAX_RETRIES |
Retries antes do DLQ | 3 |
WORKER_RETRY_BASE_DELAY_MS |
Delay base para retry | 1000 |
make up # Iniciar todos os serviços
make down # Parar e remover containers
make build # Build dos binários
make test # Rodar testes
make test-coverage # Testes com coverage
make logs # Ver logs de todos serviços
make logs-api # Ver logs da API
make logs-worker # Ver logs do Worker
make migrate # Rodar migrations
make seed # Popular dados iniciais
make health # Verificar health
make metrics # Ver métricas
make help # Ver todos comandos| Tipo | Ação |
|---|---|
payment_succeeded |
Marca invoice como paid, subscription como active, incrementa receita |
payment_failed |
Marca invoice como failed, subscription como past_due |
charge_refunded |
Marca invoice como refunded, incrementa reembolsos |
┌──────────────┐
│ Stripe │
└──────┬───────┘
│
▼
┌──────────────────────────────────────────────┐
│ API Server │
│ ┌────────────┐ ┌────────────┐ ┌─────────┐ │
│ │ Signature │ │ Rate Limit │ │ Logging │ │
│ └─────┬──────┘ └─────┬──────┘ └────┬────┘ │
│ └───────────────┼───────────────┘ │
│ ▼ │
│ ┌─────────────────┐ │
│ │ Webhook Handler │ │
│ └────────┬────────┘ │
└───────────────────────┼───────────────────────┘
│
┌────────────┴────────────┐
▼ ▼
┌─────────────┐ ┌─────────────┐
│ PostgreSQL │ │ Redis │
│ (raw_events)│ │ (Streams) │
└─────────────┘ └──────┬──────┘
│
▼
┌───────────────────────────┐
│ Workers │
│ ┌─────────────────────┐ │
│ │ payment_succeeded │ │
│ │ payment_failed │ │
│ │ charge_refunded │ │
│ └─────────┬───────────┘ │
└────────────┼──────────────┘
│
┌────────────┴────────────┐
▼ ▼
┌─────────────┐ ┌─────────────┐
│ PostgreSQL │ │ DLQ │
│ (updates) │ │ (failures) │
└─────────────┘ └─────────────┘
MIT