Skip to content

Resilient Payment Event Processor in Go. Focus on Idempotency, Resiliency (Redis Streams/PostgreSQL) and Clean Architecture. [Demo Version]

Notifications You must be signed in to change notification settings

gabriel-sa-dev/payment-event-processor

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

1 Commit
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

Payment Event Processor πŸ’³ πŸš€

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.


πŸ” Por que este projeto existe? (O Problema)

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:

  1. IdempotΓͺncia: Garante que mesmo que um evento seja recebido 10 vezes, ele serΓ‘ processado apenas uma vez.
  2. ResiliΓͺncia: Se o processamento falhar por um erro temporΓ‘rio, o sistema tenta novamente de forma inteligente (Backoff Exponencial).
  3. Observabilidade: Monitoramento em tempo real de mΓ©tricas, erros e saΓΊde do sistema.

πŸ›  Tech Stack & Ferramentas

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 Accepted instantΓ’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).

🧠 Detalhes de Implementação (Foco em Qualidade)

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.

πŸ‘¨β€πŸ’» Sobre mim & Desejo de Aprender

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:

  1. Antecipa erros (O que acontece se o banco ficar lento?).
  2. Escreve cΓ³digo para humanos (Nomes de variΓ‘veis intuitivos e estrutura limpa).
  3. Γ‰ 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.


πŸš€ Como visualizar a estrutura

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!

πŸ§ͺ Testando Webhooks

Linux/Mac (Bash)

# 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":"[email protected]","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}"

Windows (PowerShell)

# 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":"[email protected]","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 $payload

πŸ“ Estrutura do Projeto

payment-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

βš™οΈ ConfiguraΓ§Γ£o

VariΓ‘veis de Ambiente

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

πŸ›  Comandos Make

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

πŸ”„ Tipos de Evento Suportados

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

πŸ— Arquitetura

                    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
                    β”‚   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) β”‚
             β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜          β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

πŸ“ LicenΓ§a

MIT

About

Resilient Payment Event Processor in Go. Focus on Idempotency, Resiliency (Redis Streams/PostgreSQL) and Clean Architecture. [Demo Version]

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published