Pular para o conteúdo principal

Idempotência

Idempotência é a garantia de que reenviar a mesma requisição não gera um segundo pagamento. Em sistemas distribuídos a resposta pode se perder mesmo depois de a operação ter sido efetivada — um timeout de rede, um deploy no meio do caminho — e sem idempotência o retry viraria débito duplicado.

Duas chaves controlam esse comportamento:

CampoOnde vaiPapel
Idempotency-KeyHeader HTTPIdentifica a tentativa. É por ela que recuperamos o pagamento já registrado e devolvemos o mesmo resultado.
identifierCampo do bodyIdentifica a operação no seu sistema. Ecoamos para o liquidante, e ele o devolve em todos os webhooks.

Ambas são opcionais: quando você não envia, geramos um valor aleatório. Isso é importante e costuma passar batido — uma requisição sem essas chaves é sempre tratada como uma operação nova, e não tem proteção nenhuma contra duplicidade. Se o seu retry precisa ser seguro, envie as duas e repita exatamente os mesmos valores.

Recomendamos UUID v4 na Idempotency-Key e, no identifier, a chave que você já usa internamente para a operação (número do lote, id da ordem de pagamento).

O que cada fluxo usa para deduplicar

A chave que vale muda por fluxo. Reenviar com os mesmos valores é sempre seguro; o que difere é qual campo você precisa manter fixo.

FluxoDedupe porReenvio depois de uma falha
PIX out e devolução PIXidentifier + Idempotency-KeyExecuta novamente
Transferência internaidentifier + Idempotency-KeyExecuta novamente
Pagamento de boletoIdempotency-KeyExecuta novamente
TEDidentifierNão executa; devolve o estado da tentativa anterior

Cenários

Os exemplos usam PIX out, mas o raciocínio vale para transferência interna e boleto. A TED tem uma seção própria porque se comporta de forma diferente.

A operação deu certo e você reenvia

Devolvemos 200 com o resultado original — mesmo paymentId, mesmo endToEndId. Nenhum novo débito acontece, e o payload da segunda requisição é ignorado.

{
"paymentId": "pay_9f2c…",
"status": "COMPLETED",
"endToEndId": "E1234567820260803…",
"identifier": "ordem-4471"
}

A operação falhou de forma definitiva e você reenvia

Este é o caso da conta sem saldo, da chave PIX inválida, da recusa por antifraude e do bloqueio por uma policy sua. A resposta é 422 com o motivo:

{
"paymentId": "pay_9f2c…",
"status": "FAILED",
"errorCode": "insufficient_funds",
"errorReason": "PIX não autorizado por falta de saldo na conta"
}

Uma falha definitiva significa que o dinheiro não saiu e não vai sair. Reenviar com a mesma Idempotency-Key executa o pagamento outra vez — é exatamente o que você quer depois de aportar saldo ou ajustar a regra que barrou a operação. O paymentId é reaproveitado, então você continua acompanhando o mesmo registro em vez de precisar rastrear um novo.

Só reenvie quando a causa da recusa tiver sido resolvida. Repetir contra a mesma condição vai devolver a mesma recusa.

A operação ainda está em andamento

Enquanto o pagamento não tem desfecho, reenviar com a mesma Idempotency-Key anexa à execução em curso em vez de abrir uma segunda. A resposta é 202 com status: "PENDING". Nunca existem dois pagamentos concorrentes para a mesma chave.

O desfecho ficou indeterminado (TIMEOUT)

Caso mais delicado da API. O liquidante aceitou a submissão mas não confirmou o resultado dentro da janela de espera: o dinheiro pode ter saído. Devolvemos 202 com status: "TIMEOUT" e um warning.

Aqui o comportamento muda de propósito: reenviar com a mesma Idempotency-Key devolve o estado existente e não submete o pagamento de novo, porque reexecutar sobre um desfecho desconhecido é o cenário clássico de pagamento em duplicidade.

O que fazer:

  1. Consulte GET /v1/accounts/{accountId}/payments/{identifier} ou aguarde o webhook — o desfecho tardio (pix.out.completed ou pix.out.failed) chega quando o liquidante confirmar.
  2. Antes de emitir um pagamento novo, confira o extrato.
  3. Só use uma Idempotency-Key nova se tiver confirmado que o valor não saiu.

Você reenvia com uma Idempotency-Key nova

É uma operação nova, e vai gerar um segundo pagamento. Trocar a chave é a forma de dizer "quero pagar de novo, de propósito". Nunca gere uma chave nova dentro de um laço de retry automático.

Com uma ressalva útil: se você trocar a chave mas repetir o mesmo identifier, o PIX out reconhece a operação e devolve 202 sem criar um segundo pagamento. Vale como rede de proteção adicional, não como substituto — a garantia forte de não duplicar vem de repetir a mesma Idempotency-Key.

Você reenvia a mesma chave com um payload diferente

Não comparamos payloads: quem manda é a chave. Se já existe pagamento para aquela Idempotency-Key, devolvemos o resultado dele e ignoramos os novos valores — não devolvemos erro de conflito. Portanto, jamais reaproveite uma chave para uma operação diferente, ou você vai receber 200 de um pagamento que não é o que você acabou de pedir.

TED

A TED deduplica só pelo identifier, e nunca reexecuta. Qualquer reenvio com um identifier já usado devolve 200 com o estado atual daquela TED, inclusive quando o estado é de falha:

{
"tedId": "ted-ordem-4471",
"status": "FAILED",
"identifier": "ordem-4471",
"note": "TED already exists for this identifier — returning current state (idempotent)"
}

Para tentar de novo depois de uma TED falhada, envie um identifier novo. Isso é deliberado: a TED tem janela de liquidação longa (até 48h) e o identifier é o que amarra o registro aos webhooks do liquidante.

Expiração

As chaves não expiram. O registro do pagamento é permanente, então uma Idempotency-Key usada meses atrás continua devolvendo o resultado daquela operação. Não reaproveite chaves entre operações diferentes.

Boas práticas

  • Gere a Idempotency-Key antes da primeira tentativa e persista junto com a ordem de pagamento. Uma chave gerada dentro do retry não protege nada.
  • Mantenha a mesma chave em todo o backoff exponencial de erros de rede e 5xx.
  • Envie sempre o identifier, com o id que a operação já tem no seu sistema. Ele é a chave de rastreio nos webhooks e no suporte.
  • Trate TIMEOUT como estado próprio, nunca como falha. É o único desfecho em que reemitir pode duplicar dinheiro.
  • Antes de reenviar depois de um 422, resolva a causa. O errorCode diz qual é; a lista completa está em Erros.