Idempotência em pagamento: por que sua integração cobra o cliente duas vezes
O cliente clicou uma vez e foi cobrado duas. Quase sempre a causa não é bug no seu botão, é uma resposta que se perdeu no caminho. O que é idempotência e como usar a chave que resolve isso.
Cobrança duplicada raramente vem de código malfeito. Vem de uma coisa mais chata: você não tem como saber se a requisição que deu timeout chegou ou não.
O problema em três passos
- Seu sistema chama a API pra criar uma cobrança.
- A cobrança é criada do lado de lá, normalmente.
- A resposta se perde. Timeout, rede instável, container reiniciado.
Agora você tem um problema sem saída boa. Se tentar de novo, corre o risco de criar a segunda cobrança. Se não tentar, corre o risco de o cliente ficar sem nada.
Repare que nada aí foi erro seu. É a natureza de qualquer chamada em rede, e é por isso que existe uma solução padronizada.
O que é idempotência
Uma operação é idempotente quando executá-la várias vezes tem o mesmo efeito de executá-la uma vez. Consultar um saldo é idempotente por natureza. Criar uma cobrança não é: chamou duas vezes, criou duas.
A chave de idempotência é o mecanismo que transforma a segunda em idempotente. Você gera um identificador único pra aquela intenção de cobrança e manda junto na requisição. Se a mesma chave chegar de novo, o servidor não cria nada: ele devolve o resultado da primeira.
Como usar direito
Manda um cabeçalho de idempotência com um identificador único por tentativa de compra. Um UUID serve.
POST /v1/transactions
Idempotency-Key: 9f2c1a44-6f5c-4a1e-9a71-2f0a5c8d3b12
Content-Type: application/json
{ "amount_cents": 19700, "method": "PIX" }
Repetiu a chamada com a mesma chave depois de um timeout? Volta a mesma cobrança, com o mesmo identificador. Uma só existe.
Os três erros de quem usa
Gerar a chave no lugar errado. Se você gera a chave dentro da função que faz a chamada, cada retentativa gera uma chave nova e a proteção some. A chave tem que nascer junto com a intenção do cliente, antes da primeira tentativa, e sobreviver às retentativas.
Usar o id do pedido como chave sempre. Funciona pra impedir duplicata do mesmo pedido, mas trava o caso legítimo de o cliente tentar pagar de novo depois de a primeira cobrança expirar. Uma saída é combinar id do pedido com a tentativa.
Achar que idempotência é eterna. A janela costuma ser de horas ou poucos dias. Depois disso, a mesma chave cria uma cobrança nova. Não use como controle de duplicidade de longo prazo.
A outra metade do problema
Idempotência resolve a duplicata na criação. Ela não resolve a duplicata no aviso. Webhook é reenviado por natureza: quem manda repete até ter certeza de que você recebeu, então o mesmo evento de pagamento pode chegar duas ou três vezes.
Se o seu código credita saldo toda vez que recebe o aviso, você vai creditar em dobro em algum momento. A defesa é a mesma ideia, aplicada na entrada: antes de processar, verifica se aquela venda já está no estado que o evento pede. Já está paga? Responde sucesso e não faz nada.
Como o Trilho trata
A API pública aceita chave de idempotência na criação de cobrança. Do lado do recebimento, cada aviso de pagamento é conferido contra o estado atual da venda antes de qualquer crédito, então o mesmo evento chegando três vezes credita uma.
Toda entrega de webhook fica registrada com o que foi recebido e como foi tratado, o que torna possível auditar depois em vez de depender de log de aplicação.
Resumo
Gere a chave junto com a intenção de compra, não dentro da função de chamada. Mande em toda criação de cobrança. E trate o webhook como se ele fosse chegar repetido, porque vai.
Coloque em prática
Testa o Trilho grátis, sem cartão de crédito
Cria a conta em 2 minutos e gera seu primeiro PIX ainda hoje.
Criar conta grátis →Leia também
Como integrar PIX no seu site em 2026: guia técnico completo
Tutorial passo a passo pra integrar PIX no seu site, com código de exemplo, tratamento de webhook, expiração e recuperação. Do zero à primeira venda em 30 minutos.
Melhor API de pagamento pra desenvolvedores brasileiros em 2026
O que separa uma API de pagamento boa de uma ruim na prática: documentação, versionamento, webhook assinado, sandbox de verdade e tempo real até a primeira cobrança.
Order bump: quanto ele soma e o erro que faz você cobrar a menos
Order bump aumenta ticket sem custo de aquisição. Mas existe uma falha de integração comum que mostra o valor certo na tela e cobra só o produto principal, e ela não gera erro nenhum.