Design clássico · Sênior
Sistema de pagamentos
Idempotência ponta a ponta, conciliação e consistência entre ledger e PSP.
Resposta curta
Idempotência ponta a ponta, conciliação e consistência entre ledger e PSP. O exercício clássico pede que um comerciante consiga iniciar uma cobrança de um valor específico contra um cliente e acompanhar o estado dela (pendente, sucesso ou falha). O pagamento em si é feito por um provedor externo, o PSP: o cliente informa os dados no site do comerciante, que…
01 · CONTEXTO
Mover dinheiro não tolera meio-termo
O exercício clássico pede que um comerciante consiga iniciar uma cobrança de um valor específico contra um cliente e acompanhar o estado dela (pendente, sucesso ou falha). O pagamento em si é feito por um provedor externo, o PSP: o cliente informa os dados no site do comerciante, que os repassa ao PSP, e o PSP devolve o resultado.
A carga de referência do exercício é de cerca de 10.000 transações por segundo no pico, e o requisito que mais pesa no desenho não é a vazão: é garantir durabilidade e auditabilidade, sem perder dado de transação nem em caso de falha. Esse requisito explica as três decisões do caso: operações idempotentes para tolerar repetição, filas para absorver a natureza assíncrona do PSP e um registro contábil imutável para provar o que aconteceu.
02 · IDEMPOTÊNCIA
Repetir a chamada sem cobrar duas vezes
O perigo central é descrito sem rodeios por quem opera um PSP: chamar a operação duas vezes por acidente deixaria o cliente cobrado em dobro. E a repetição é inevitável, porque a chamada pode ter sucesso e a conexão cair antes de o servidor avisar o cliente. A saída é tornar a operação segura para repetir, com uma chave de idempotência gerada pelo cliente.
O mecanismo: o cliente reenvia a mesma requisição com o mesmo identificador, o servidor correlaciona esse identificador com o estado da requisição do seu lado e, se a operação já tinha dado certo, responde com o resultado em cache. Na API do Stripe o resultado da primeira execução é guardado e devolvido nas repetições, inclusive quando era um erro de servidor; além disso, a camada de idempotência compara os parâmetros recebidos com os da requisição original e recusa a chamada se forem diferentes, o que evita o uso indevido da mesma chave.
O cliente gera uma chave única para esta intenção de pagamento e a envia junto com a requisição de cobrança.
O serviço de pagamentos consulta o banco: se a chave já existe, devolve o resultado guardado; se não, registra a chave antes de falar com o PSP.
Só então o serviço pede a cobrança ao PSP, repassando a mesma chave para que a repetição também seja segura nesse trecho.
O PSP executa a cobrança, mas a conexão cai antes de a resposta chegar: o serviço não sabe se o dinheiro andou.
O cliente repete com a mesma chave. O serviço reconhece a chave, confere o estado e devolve o resultado, sem criar uma segunda cobrança.
Repetir sem critério também tem custo: se todos os clientes tentam de novo ao mesmo tempo, o servidor recebe uma onda sincronizada. Por isso a espera entre tentativas cresce de forma exponencial e recebe um componente aleatório (jitter) por cliente, para espalhar as repetições no tempo.
03 · ASSÍNCRONO
O PSP avisa depois: webhooks e fila
Parte do desfecho de um pagamento chega depois, por eventos: o banco do cliente confirma a cobrança, o cliente contesta, um pagamento recorrente acontece. O comerciante recebe esses eventos num endpoint de webhook e precisa tratá-los como um fluxo assíncrono, não como a continuação da chamada original.
A documentação do Stripe é explícita sobre três propriedades dessa entrega. Primeiro, o mesmo evento pode chegar mais de uma vez, e a defesa é registrar os identificadores de evento já processados. Segundo, a ordem de entrega não é garantida, então o tratamento não pode depender de uma sequência; quando falta um objeto, a saída é consultá-lo na API. Terceiro, o endpoint deve responder com sucesso rapidamente, antes de qualquer lógica pesada, e processar os eventos por meio de uma fila assíncrona, a uma taxa que o sistema aguente. Se a entrega falha, o PSP tenta de novo por vários dias com espera crescente.
O PSP envia um evento (pagamento confirmado, falho ou contestado) ao endpoint de webhook do comerciante.
O endpoint valida a origem do evento, coloca-o na fila e responde com sucesso de imediato, sem tocar no banco.
A fila desacopla o ritmo do PSP do ritmo do nosso processamento: um pico de eventos vira espera, não queda.
O consumidor confere se o identificador do evento já foi processado; se sim, descarta, se não, aplica o efeito e grava o identificador na mesma transação.
Se o consumidor cai no meio, a mensagem não foi confirmada e continua na fila; ao voltar, ele reprocessa, e a checagem do identificador impede o efeito duplicado.
04 · LEDGER
O registro que ninguém reescreve
O estado do pagamento (pendente, sucesso, falha) responde "onde está esta cobrança". Já a pergunta "para onde foi o dinheiro" pede outro registro: um ledger, uma sequência de lançamentos que só cresce. O Stripe descreve o seu como um log imutável de eventos e sistema de registro de todos os dados financeiros: o que já foi publicado não pode ser apagado nem alterado, e é essa imutabilidade que permite auditar e reproduzir qualquer dado a qualquer momento. Um erro é corrigido com um novo lançamento compensatório, nunca editando o antigo.
A contabilidade por partidas dobradas dá ao ledger uma regra verificável: todo lançamento tira de uma conta e põe em outra pelo mesmo valor, então o conjunto sempre fecha. O mesmo texto do Stripe usa essa construção como base para uma prova matemática de correção. Gravar o lançamento junto com a mudança de estado numa única transação do banco mantém as duas visões coerentes dentro do nosso sistema; entre nós e o PSP não há transação compartilhada, e é aí que entra a conciliação.
05 · CONCILIAÇÃO
Provar que os dois lados concordam
Mesmo com idempotência e fila, dois sistemas independentes podem divergir: um evento perdido, um lançamento atrasado, um estorno que só um dos lados viu. A conciliação é a checagem periódica que compara o nosso ledger com os registros do PSP e levanta toda diferença. O Stripe descreve a mesma ideia internamente: mede qual fração do ledger está devidamente zerada no estado estável e usa verificações explícitas de completude de dados para se proteger de casos em que algo deixa de ser registrado.
O job de conciliação lê os lançamentos do período no nosso ledger.
Em paralelo, ele carrega os registros que o PSP publica sobre o mesmo período.
Cada pagamento é casado pelos identificadores; o que existe só de um lado, ou tem valor diferente, vira uma divergência.
As divergências vão para monitoramento e viram trabalho de correção por lançamento compensatório, nunca por edição do histórico.
| Mecanismo | Problema que resolve | O que ainda deixa passar |
|---|---|---|
| Chave de idempotência | Cobrança duplicada por repetição do cliente | Divergência causada por evento perdido do PSP |
| Fila com deduplicação por id de evento | Eventos duplicados, fora de ordem ou em rajada | Evento que nunca chegou |
| Ledger imutável | Perda de rastro e edição indevida do histórico | Diferença entre o nosso registro e o do PSP |
| Conciliação periódica | Divergências entre os dois lados, achadas depois | Prejuízo no intervalo até a próxima rodada |
Perguntas frequentes
O que é Sistema de pagamentos?
Idempotência ponta a ponta, conciliação e consistência entre ledger e PSP.
O que o design de Sistema de pagamentos cobre?
Ele está organizado nestas seções: Mover dinheiro não tolera meio-termo, Repetir a chamada sem cobrar duas vezes, O PSP avisa depois: webhooks e fila, O registro que ninguém reescreve e Provar que os dois lados concordam.
Quanto tempo leva Sistema de pagamentos e qual é o nível?
Leva cerca de 19 minutos para ler e tem nível Sênior.
Agora projete você mesmo
Rode este caso como uma entrevista na escola de System Design: você toma as decisões, o tutor as questiona e você vê a nota do seu desenho.