Projetando um Sistema de Pagamentos em Escala: Guia Completo de System Design
Resumo: Um guia de nível produção para desenhar sistemas de pagamento robustos, cobrindo autorização, captura, ledger de dupla entrada, idempotência, antifraude, conciliação, multi-moeda, subscriptions, compliance e confiabilidade operacional.
Publicado: Fevereiro 2026
Tempo de leitura: 110 minutos
Palavras-chave: #SystemDesign #PaymentSystem #Ledger #Idempotency #FraudDetection #Reconciliation #PCI #DistributedSystems #TechInterview
Pagamentos são o ponto onde software encontra dinheiro real. Quando um sistema de feed falha, o usuário vê conteúdo errado. Quando um sistema de pagamento falha, você perde dinheiro, gera disputa, cria risco regulatório e destrói confiança.
Um checkout simples parece um clique. Por trás, você precisa coordenar:
- validação de pedido,
- tokenização de meio de pagamento,
- autorização,
- captura,
- registro contábil,
- webhooks assíncronos,
- conciliação com provedores,
- tratamento de reembolso e chargeback.
Tudo isso sob retries, timeouts, duplicação de mensagens e falhas parciais.
Este guia organiza essa complexidade em um design defensável e operável.
Sumário
- Análise de Requisitos
- Cálculos de Envelope
- Arquitetura de Alto Nível
- Design de API
- Modelagem de Dados
- Fluxo de Processamento de Pagamento (Core 1)
- Ledger de Dupla Entrada (Core 2)
- Idempotência e Exactly-Once de Negócio (Core 3)
- Integração com Gateways e Adquirentes
- Detecção de Fraude
- Assinaturas e Recorrência
- Suporte a Multi-Moeda
- Conciliação Financeira
- Segurança e Compliance
- Arquitetura de Banco de Dados
- Arquitetura Event-Driven
- Confiabilidade e Tolerância a Falhas
- Observabilidade e SLOs
- Dicas para Entrevista
- Anti-Patterns
- Conclusão
- Referências
- Referência Rápida
Análise de Requisitos
Requisitos Funcionais
- Criar intents de pagamento.
- Autorizar pagamento em gateway externo.
- Capturar pagamento (imediata ou tardia).
- Registrar todas as transações em ledger de dupla entrada.
- Reembolsar total/parcial.
- Tratar chargebacks/disputas.
- Suportar webhooks assíncronos idempotentes.
- Gerar extratos e relatórios de settlement.
- Suportar pagamentos recorrentes.
- Suportar várias moedas.
Requisitos Não-Funcionais
Requisito
Meta
Motivo
Disponibilidade API de pagamento
99,99%
impacto direto em receita
Latência auth
< 500ms p99 (fora lat. gateway)
UX de checkout
Durabilidade transacional
perda 0 após ack
confiança e auditoria
Integridade contábil
soma débitos = soma créditos
exigência financeira
Idempotência
retries seguros
rede e provedores são imperfeitos
Auditabilidade
trilha completa e imutável
compliance e disputas
Perguntas de Clarificação
- Escopo inclui apenas cartão ou métodos alternativos também?
- Captura imediata ou delayed capture?
- Subscription está em escopo?
- Qual volume de transações por dia?
- Precisamos de ledger completo ou apenas registro simplificado?
Premissas do guia:
- integração com gateways externos,
- ledger completo,
- captura flexível,
- webhooks assíncronos,
- antifraude e conciliação obrigatórios.
Cálculos de Envelope
Escala Assumida
Transações/dia: 250 milhõesValor médio: USD 32Pico de transações/s (eventos): 25.000Webhooks/dia: 500 milhõesReembolsos/dia: 5 milhõesChargebacks/dia: 300 mil
Throughput
QPS médio tx = 250.000.000 / 86.400 ~= 2.893Pico (8x) ~= 23.144Webhooks/s médio ~= 5.787Pico (6x) ~= 34.722
Armazenamento
Assumindo:
- payment txn row: 1KB
- ledger entry row: 500B
- 4 lançamentos por transação em média
payment rows/dia ~= 250GBledger rows/dia ~= 250M * 4 * 500B ~= 500GBcom réplicas e índices => vários TB/dia
Insight
No sistema de pagamento, o gargalo principal não é CPU de API. É integridade de estado entre:
- sistema interno,
- gateway externo,
- ledger,
- settlement.
Arquitetura de Alto Nível
flowchart TB subgraph Clientes APP["Checkout Clients"] MER["Merchant Backoffice"] end subgraph Borda API["Payment API Gateway"] AUTH["Auth + Rate Limit"] end subgraph Serviços Core ORCH["Payment Orchestrator"] RISK["Risk/Fraud Engine"] TOKEN["Token Service"] GW["Gateway Connector"] LEDGER["Ledger Service"] REFUND["Refund Service"] SUBS["Subscription Service"] RECON["Reconciliation Service"] WEBHOOK["Webhook Handler"] NOTI["Notification Service"] end subgraph Dados e Plataforma SQL[("Transactional SQL")] LDB[("Ledger Store")] REDIS[("Redis")] KAFKA[("Kafka")] OLAP[("Analytics/Warehouse")] end APP --> API --> AUTH --> ORCH MER --> API ORCH --> RISK ORCH --> TOKEN ORCH --> GW ORCH --> LEDGER GW --> WEBHOOK WEBHOOK --> ORCH WEBHOOK --> LEDGER ORCH --> REFUND ORCH --> SUBS ORCH --> NOTI ORCH --> RECON ORCH --> SQL LEDGER --> LDB RISK --> REDIS ORCH --> KAFKA LEDGER --> KAFKA WEBHOOK --> KAFKA KAFKA --> OLAP
Princípios
- Ledger separado de estado de orquestração.
- Idempotência de ponta a ponta.
- Eventos para desacoplamento e auditoria.
- Reconciliação contínua com fontes externas.
Design de API
Endpoints
POST /v1/payment-intentsPOST /v1/payment-intents/{id}/confirmPOST /v1/payment-intents/{id}/capturePOST /v1/payment-intents/{id}/cancelPOST /v1/refundsGET /v1/payments/{payment_id}GET /v1/ledger/accounts/{account_id}/entriesPOST /v1/webhooks/{provider}
Criar Payment Intent
{ "idempotency_key": "9b2f8d8a-78ae-4722-a8c5-88e8ed8f2ca5", "merchant_id": "m_123", "order_id": "ord_901", "amount": { "currency": "USD", "value": 129.99 }, "payment_method": { "type": "CARD_TOKEN", "token": "tok_abc" }, "capture_mode": "MANUAL", "metadata": { "channel": "web" }}
Response:
{ "payment_intent_id": "pi_887", "status": "REQUIRES_CONFIRMATION", "created_at": "2026-02-22T22:10:00Z"}
Confirmar Payment Intent
{ "idempotency_key": "f9d02bc1-61be-4288-a8cd-6d9c8b4a4df0", "payment_intent_id": "pi_887"}
Response:
{ "payment_intent_id": "pi_887", "payment_id": "pay_001", "status": "AUTHORIZED", "authorized_amount": { "currency": "USD", "value": 129.99 }}
Modelagem de Dados
Entidades Principais
- PaymentIntent
- Payment
- PaymentAttempt
- LedgerAccount
- LedgerEntry
- Refund
- Dispute
- SettlementBatch
- ReconciliationRun
Tabelas Core
CREATE TABLE payment_intents ( payment_intent_id VARCHAR(64) PRIMARY KEY, merchant_id BIGINT NOT NULL, order_id VARCHAR(64) NOT NULL, amount_value NUMERIC(20,6) NOT NULL, amount_currency CHAR(3) NOT NULL, status VARCHAR(32) NOT NULL, capture_mode VARCHAR(16) NOT NULL, created_at TIMESTAMP NOT NULL, updated_at TIMESTAMP NOT NULL, UNIQUE (merchant_id, order_id));CREATE TABLE payment_attempts ( payment_attempt_id VARCHAR(64) PRIMARY KEY, payment_intent_id VARCHAR(64) NOT NULL, provider VARCHAR(32) NOT NULL, provider_txn_id VARCHAR(128), status VARCHAR(32) NOT NULL, response_code VARCHAR(32), response_payload JSONB, created_at TIMESTAMP NOT NULL, updated_at TIMESTAMP NOT NULL, UNIQUE (provider, provider_txn_id));
Ledger Schema
CREATE TABLE ledger_accounts ( account_id BIGSERIAL PRIMARY KEY, account_type VARCHAR(32) NOT NULL, currency CHAR(3) NOT NULL, owner_ref VARCHAR(64), status VARCHAR(16) NOT NULL, created_at TIMESTAMP NOT NULL);CREATE TABLE ledger_entries ( entry_id BIGSERIAL PRIMARY KEY, tx_id VARCHAR(64) NOT NULL, account_id BIGINT NOT NULL REFERENCES ledger_accounts(account_id), direction VARCHAR(6) NOT NULL CHECK (direction IN ('DEBIT','CREDIT')), amount NUMERIC(20,6) NOT NULL, currency CHAR(3) NOT NULL, created_at TIMESTAMP NOT NULL);
Regra de Integridade
Para cada tx_id:
SUM(DEBIT) == SUM(CREDIT)
Fluxo de Processamento de Pagamento (Core 1)
Etapas
- Validar request e idempotência.
- Avaliar risco.
- Enviar auth ao gateway.
- Persistir resultado local.
- Escrever lançamentos no ledger.
- Retornar status ao cliente.
Sequência Auth
sequenceDiagram participant C as Client participant P as Payment API participant O as Orchestrator participant R as Risk Engine participant G as Gateway participant L as Ledger C->>P: confirm payment intent P->>O: process request O->>R: risk score R-->>O: allow O->>G: authorize G-->>O: auth approved O->>L: post ledger entries L-->>O: committed O-->>P: AUTHORIZED P-->>C: response
Estados Típicos
REQUIRES_CONFIRMATIONPROCESSINGAUTHORIZEDCAPTUREDFAILEDCANCELEDREFUNDED
Ledger de Dupla Entrada (Core 2)
Ledger é o coração de confiabilidade financeira.
Exemplo de Transação de Captura
Para captura de USD 100 com fee USD 3:
- Débito: clearing account +100
- Crédito: merchant payable +97
- Crédito: platform fee revenue +3
Características do Ledger
- Append-only.
- Imutável (sem update/delete de entry).
- Reversão por lançamentos compensatórios.
- Índice por tx_id e account_id.
Exemplo de Postagem
{ "tx_id": "tx_445", "entries": [ { "account": "clearing_usd", "direction": "DEBIT", "amount": "100.00", "currency": "USD" }, { "account": "merchant_payable_m123_usd", "direction": "CREDIT", "amount": "97.00", "currency": "USD" }, { "account": "platform_fee_revenue_usd", "direction": "CREDIT", "amount": "3.00", "currency": "USD" } ]}
Reversão
Reembolso não apaga transação anterior. Cria nova transação de sinal oposto.
Idempotência e Exactly-Once de Negócio (Core 3)
Rede e provedores vão duplicar requests. Idempotência é obrigatória.
Tabela
CREATE TABLE idempotency_records ( idempotency_key VARCHAR(64) NOT NULL, merchant_id BIGINT NOT NULL, endpoint VARCHAR(128) NOT NULL, request_hash CHAR(64) NOT NULL, response_code INT NOT NULL, response_body JSONB NOT NULL, created_at TIMESTAMP NOT NULL, expires_at TIMESTAMP NOT NULL, PRIMARY KEY (idempotency_key, merchant_id, endpoint));
Regras
- Mesmo key + mesmo hash => replay response.
- Mesmo key + hash diferente => 409.
- TTL adequado por endpoint (24h–72h).
Webhook Dedupe
async function processWebhook(provider: string, eventId: string, payload: unknown) { const key = `${provider}:${eventId}`; if (await dedupe.exists(key)) return; await db.transaction(async (tx) => { await tx.webhook_events.insert({ provider, event_id: eventId, payload }); await tx.dedupe.insert({ key, ttlHours: 72 }); });}
Integração com Gateways e Adquirentes
Adapter Pattern
Cada provedor tem contrato próprio. Crie camada adaptadora uniforme.
Interface Interna
type GatewayAuthorizeRequest = { amountValue: string; currency: string; token: string; merchantRef: string; idempotencyKey: string;};type GatewayAuthorizeResponse = { approved: boolean; providerTxnId?: string; responseCode: string; requiresAction?: boolean;};
Routing de Gateway
- roteamento por país/moeda,
- fallback para segundo gateway,
- smart retry com limites.
Timeout Policy
- request timeout estrito,
- retries idempotentes,
- classificação de erro (transiente x definitivo).
Detecção de Fraude
Sinais
- device fingerprint,
- velocity por cartão/conta/IP,
- anomalia geográfica,
- histórico de chargeback,
- score de comerciante.
Decisão
- allow,
- challenge,
- decline,
- manual review.
Arquitetura
flowchart LR P["Payment Request"] --> F["Feature Fetch"] F --> S["Fraud Scorer"] S --> D["Decision Engine"] D --> A["ALLOW"] D --> C["CHALLENGE"] D --> X["DECLINE"]
Latência
Fraud no caminho crítico precisa budget de milissegundos baixos.
Assinaturas e Recorrência
Requisitos
- billing cycles,
- retry strategy para falhas,
- grace period,
- dunning workflow,
- cancelamento e prorata.
Ciclo
- scheduler gera renewal intent,
- tenta charge,
- falhou → retry policy,
- sucesso → ledger + invoice,
- expirou retries → suspende.
Retry Plan Exemplo
T+0, T+1d, T+3d, T+7d
Dados
CREATE TABLE subscriptions ( subscription_id VARCHAR(64) PRIMARY KEY, customer_id BIGINT NOT NULL, plan_id VARCHAR(64) NOT NULL, status VARCHAR(32) NOT NULL, next_billing_at TIMESTAMP NOT NULL, retry_count INT NOT NULL DEFAULT 0, created_at TIMESTAMP NOT NULL, updated_at TIMESTAMP NOT NULL);
Suporte a Multi-Moeda
Desafios
- arredondamento,
- taxa de câmbio,
- ledger por moeda,
- settlement cross-border.
Regras
- Nunca misturar moedas no mesmo lançamento.
- Armazenar valores em decimal exato.
- Guardar FX rate usado na conversão.
- Separar contas contábeis por moeda.
Exemplo de Conversão
{ "source": { "currency": "EUR", "value": "10.00" }, "target": { "currency": "USD", "value": "10.83" }, "fx_rate": "1.0830", "fx_timestamp": "2026-02-22T21:00:00Z"}
Conciliação Financeira
Conciliação é o mecanismo que detecta divergência entre seu estado e o do provedor.
Tipos
- intraday automática,
- diária completa,
- conciliação de chargeback/refund,
- conciliação de payout.
Pipeline
- importar arquivo/API de settlement,
- normalizar para schema interno,
- comparar com pagamentos/ledger,
- classificar discrepâncias,
- abrir casos de remediação.
Resultado de Conciliação
{ "run_id": "recon_2026_02_22", "provider": "gateway_x", "matched": 24900123, "missing_internal": 123, "missing_provider": 77, "amount_mismatch": 44, "status": "COMPLETED"}
SLA
Sem conciliação, incidente financeiro pode ficar oculto por dias.
Segurança e Compliance
Segurança
- TLS e mTLS interno em domínios sensíveis.
- tokenização de cartão.
- HSM/KMS para chaves criptográficas.
- segregação de ambientes e segredos.
- auditoria de acesso privilegiado.
PCI DSS
Objetivo prático:
- minimizar escopo PCI,
- evitar armazenar PAN bruto,
- usar provedores/token vault certificados.
Privacidade
- minimização de PII,
- retenção por requisito legal,
- anonimização quando possível,
- trilha de consentimento.
Arquitetura de Banco de Dados
Mapeamento por Domínios
Domínio
Store
intents/payments
SQL transacional
ledger
SQL append-only ou store contábil dedicado
idempotency/dedupe
SQL ou KV forte
risk cache
Redis
analytics
OLAP/Warehouse
Sharding
- por merchant_id para isolamento,
- ou hash(payment_id) para distribuição uniforme,
- com índices secundários para consultas operacionais.
Evolução de Schema
- adicionar campo opcional,
- backfill,
- dual-read,
- switch writer,
- remover legado.
Arquitetura Event-Driven
Eventos Core
PaymentIntentCreatedPaymentAuthorizedPaymentCapturedPaymentFailedRefundIssuedChargebackOpenedChargebackResolvedSettlementImportedReconciliationCompleted
Diagrama
flowchart TB ORCH["Orchestrator"] --> BUS["Kafka"] LED["Ledger"] --> BUS WEB["Webhook Handler"] --> BUS REF["Refund Service"] --> BUS BUS --> REC["Reconciliation"] BUS --> NOTI["Notifications"] BUS --> DWH["Analytics"] BUS --> AUD["Audit Pipeline"]
Outbox
Garantir publish confiável junto da transação local.
CREATE TABLE outbox_events ( event_id UUID PRIMARY KEY, aggregate_type VARCHAR(32) NOT NULL, aggregate_id VARCHAR(64) NOT NULL, event_type VARCHAR(64) NOT NULL, payload JSONB NOT NULL, created_at TIMESTAMP NOT NULL, published_at TIMESTAMP);
Confiabilidade e Tolerância a Falhas
Falhas Clássicas
- timeout no gateway com status desconhecido,
- webhook duplicado/atrasado,
- ledger indisponível,
- fila com lag,
- erro de conciliação.
Estratégias
- idempotência em todas as mutações,
- timeouts + retries com jitter,
- circuit breakers,
- fallback para estado
PENDING_EXTERNAL_CONFIRMATION, - replay de eventos.
Estado "Desconhecido"
Quando timeout no gateway sem resposta conclusiva:
- marcar tentativa como
UNKNOWN, - consultar status assíncrono,
- bloquear duplicidade via idempotency,
- atualizar estado final após confirmação.
SLA de Recovery
- caminhos de autorização/captura devem recuperar rápido,
- processos de conciliação podem ter janelas maiores,
- sem perder audit trail.
Observabilidade e SLOs
SLOs
Serviço
SLI
SLO
Payment confirm
p99 latência
< 500ms (sem rede externa)
Auth success
taxa de sucesso
dentro da baseline por método
Webhook processing
atraso p95
< 30s
Ledger posting
durabilidade
perda 0
Reconciliation
execução diária
100% runs concluídas
Golden Signals
Orchestrator:
- success/failure rate,
- timeout externo,
- idempotency replay,
- latency p95/p99.
Ledger:
- post failures,
- imbalance detector,
- write latency.
Webhook:
- dedupe hit rate,
- processing lag,
- invalid signature rate.
Correlação
x-request-idx-payment-intent-idx-payment-attempt-idx-ledger-tx-idx-provider-txn-id
Dicas para Entrevista
Estrutura de Resposta
- Defina escopo (auth/capture/refund/ledger).
- Faça estimativa de volume.
- Desenhe arquitetura macro.
- Deep dive em idempotência e ledger.
- Explique webhooks e conciliação.
- Feche com compliance e SLOs.
Erros Comuns
- ignorar ledger de dupla entrada,
- tratar retry sem idempotência,
- sem plano para status desconhecido,
- sem conciliação,
- sem separação entre estado de negócio e estado externo.
Tabela de Trade-offs
Decisão
Opção A
Opção B
Recomendado
registro financeiro
saldo mutável
ledger append-only
append-only
retries
sem chave
idempotência forte
idempotência forte
webhook
best effort
dedupe + assinatura + replay
dedupe + replay
gateway
único provedor
multi-provider
multi-provider (complexidade controlada)
conciliação
manual eventual
pipeline automatizada
automatizada
Anti-Patterns
1) Atualizar saldo direto sem ledger
Problema: sem trilha auditável e alta chance de inconsistência.
Correção: dupla entrada append-only.
2) Retry sem idempotency key
Problema: cobrança duplicada.
Correção: chave obrigatória por mutação.
3) Confiar cegamente no status síncrono
Problema: timeout gera incerteza e duplicidade.
Correção: estado pendente + confirmação assíncrona.
4) Ignorar webhooks duplicados
Problema: transições repetidas e estado corrompido.
Correção: dedupe por provider event id.
