0

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

Requisitos Funcionais

  1. Criar intents de pagamento.
  2. Autorizar pagamento em gateway externo.
  3. Capturar pagamento (imediata ou tardia).
  4. Registrar todas as transações em ledger de dupla entrada.
  5. Reembolsar total/parcial.
  6. Tratar chargebacks/disputas.
  7. Suportar webhooks assíncronos idempotentes.
  8. Gerar extratos e relatórios de settlement.
  9. Suportar pagamentos recorrentes.
  10. Suportar várias moedas.

Requisitos Não-Funcionais

RequisitoMetaMotivo
Disponibilidade API de pagamento99,99%impacto direto em receita
Latência auth< 500ms p99 (fora lat. gateway)UX de checkout
Durabilidade transacionalperda 0 após ackconfiança e auditoria
Integridade contábilsoma débitos = soma créditosexigência financeira
Idempotênciaretries segurosrede e provedores são imperfeitos
Auditabilidadetrilha completa e imutávelcompliance e disputas

Perguntas de Clarificação

  1. Escopo inclui apenas cartão ou métodos alternativos também?
  2. Captura imediata ou delayed capture?
  3. Subscription está em escopo?
  4. Qual volume de transações por dia?
  5. 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ões
Valor médio: USD 32
Pico de transações/s (eventos): 25.000
Webhooks/dia: 500 milhões
Reembolsos/dia: 5 milhões
Chargebacks/dia: 300 mil

Throughput

QPS médio tx = 250.000.000 / 86.400 ~= 2.893
Pico (8x) ~= 23.144

Webhooks/s médio ~= 5.787
Pico (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 ~= 250GB
ledger rows/dia ~= 250M * 4 * 500B ~= 500GB
com 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

  1. Ledger separado de estado de orquestração.
  2. Idempotência de ponta a ponta.
  3. Eventos para desacoplamento e auditoria.
  4. Reconciliação contínua com fontes externas.

Design de API

Endpoints

POST /v1/payment-intents
POST /v1/payment-intents/{id}/confirm
POST /v1/payment-intents/{id}/capture
POST /v1/payment-intents/{id}/cancel
POST /v1/refunds
GET /v1/payments/{payment_id}
GET /v1/ledger/accounts/{account_id}/entries
POST /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

  1. PaymentIntent
  2. Payment
  3. PaymentAttempt
  4. LedgerAccount
  5. LedgerEntry
  6. Refund
  7. Dispute
  8. SettlementBatch
  9. 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

  1. Validar request e idempotência.
  2. Avaliar risco.
  3. Enviar auth ao gateway.
  4. Persistir resultado local.
  5. Escrever lançamentos no ledger.
  6. 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_CONFIRMATION
  • PROCESSING
  • AUTHORIZED
  • CAPTURED
  • FAILED
  • CANCELED
  • REFUNDED

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

  1. Append-only.
  2. Imutável (sem update/delete de entry).
  3. Reversão por lançamentos compensatórios.
  4. Í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

  1. Mesmo key + mesmo hash => replay response.
  2. Mesmo key + hash diferente => 409.
  3. 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

  1. device fingerprint,
  2. velocity por cartão/conta/IP,
  3. anomalia geográfica,
  4. histórico de chargeback,
  5. 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

  1. billing cycles,
  2. retry strategy para falhas,
  3. grace period,
  4. dunning workflow,
  5. cancelamento e prorata.

Ciclo

  1. scheduler gera renewal intent,
  2. tenta charge,
  3. falhou → retry policy,
  4. sucesso → ledger + invoice,
  5. 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

  1. arredondamento,
  2. taxa de câmbio,
  3. ledger por moeda,
  4. settlement cross-border.

Regras

  1. Nunca misturar moedas no mesmo lançamento.
  2. Armazenar valores em decimal exato.
  3. Guardar FX rate usado na conversão.
  4. 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

  1. intraday automática,
  2. diária completa,
  3. conciliação de chargeback/refund,
  4. conciliação de payout.

Pipeline

  1. importar arquivo/API de settlement,
  2. normalizar para schema interno,
  3. comparar com pagamentos/ledger,
  4. classificar discrepâncias,
  5. 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

  1. TLS e mTLS interno em domínios sensíveis.
  2. tokenização de cartão.
  3. HSM/KMS para chaves criptográficas.
  4. segregação de ambientes e segredos.
  5. 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ínioStore
intents/paymentsSQL transacional
ledgerSQL append-only ou store contábil dedicado
idempotency/dedupeSQL ou KV forte
risk cacheRedis
analyticsOLAP/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

  1. adicionar campo opcional,
  2. backfill,
  3. dual-read,
  4. switch writer,
  5. remover legado.

Arquitetura Event-Driven

Eventos Core

  • PaymentIntentCreated
  • PaymentAuthorized
  • PaymentCaptured
  • PaymentFailed
  • RefundIssued
  • ChargebackOpened
  • ChargebackResolved
  • SettlementImported
  • ReconciliationCompleted

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

  1. timeout no gateway com status desconhecido,
  2. webhook duplicado/atrasado,
  3. ledger indisponível,
  4. fila com lag,
  5. erro de conciliação.

Estratégias

  1. idempotência em todas as mutações,
  2. timeouts + retries com jitter,
  3. circuit breakers,
  4. fallback para estado PENDING_EXTERNAL_CONFIRMATION,
  5. 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çoSLISLO
Payment confirmp99 latência< 500ms (sem rede externa)
Auth successtaxa de sucessodentro da baseline por método
Webhook processingatraso p95< 30s
Ledger postingdurabilidadeperda 0
Reconciliationexecução diária100% 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-id
x-payment-intent-id
x-payment-attempt-id
x-ledger-tx-id
x-provider-txn-id

Dicas para Entrevista

Estrutura de Resposta

  1. Defina escopo (auth/capture/refund/ledger).
  2. Faça estimativa de volume.
  3. Desenhe arquitetura macro.
  4. Deep dive em idempotência e ledger.
  5. Explique webhooks e conciliação.
  6. Feche com compliance e SLOs.

Erros Comuns

  1. ignorar ledger de dupla entrada,
  2. tratar retry sem idempotência,
  3. sem plano para status desconhecido,
  4. sem conciliação,
  5. sem separação entre estado de negócio e estado externo.

Tabela de Trade-offs

DecisãoOpção AOpção BRecomendado
registro financeirosaldo mutávelledger append-onlyappend-only
retriessem chaveidempotência forteidempotência forte
webhookbest effortdedupe + assinatura + replaydedupe + replay
gatewayúnico provedormulti-providermulti-provider (complexidade controlada)
conciliaçãomanual eventualpipeline automatizadaautomatizada

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.

5) Sem conciliação diária

Carregando publicação patrocinada...