Webhook duplicado processado sem idempotência

Esse erro ocorre quando a integração recebe o mesmo webhook mais de uma vez e executa novamente a regra de negócio sem verificar se o evento já foi processado.

O recebimento repetido de um evento não significa que a ação deve ser executada novamente. A integração precisa reconhecer duplicidades e responder com sucesso sem repetir seus efeitos.

Diferentemente do erro Webhook recebido, mas não persistido, neste caso o problema está na ausência de controle sobre eventos já registrados ou processados.

Como identificar

  • o mesmo evento aparece mais de uma vez nos logs;
  • a mesma ação é executada repetidamente para uma única operação;
  • notificações, liberações ou atualizações são duplicadas;
  • existem múltiplos processamentos com o mesmo identificador de evento;
  • o sistema não verifica se o evento já foi recebido;
  • não existe status interno para diferenciar eventos recebidos e processados;
  • dois processos podem executar o mesmo evento simultaneamente;
  • o problema ocorre após uma nova tentativa de entrega do webhook.

Como corrigir

  1. Identifique os eventos que foram processados mais de uma vez.
  2. Confirme quais ações foram duplicadas.
  3. Corrija os efeitos indevidos, quando aplicável.
  4. Persista o identificador do evento antes de iniciar o processamento.
  5. Utilize uma chave única para controlar eventos já recebidos.
  6. Consulte essa chave antes de executar a regra de negócio.
  7. Quando o evento já estiver processado, não execute a ação novamente.
  8. Quando o processamento anterior estiver pendente ou com erro, retome-o de forma segura.
  9. Impeça que processos concorrentes executem o mesmo evento.
  10. Registre o resultado de cada processamento.

Fluxo recomendado: receber → identificar o evento → consultar o registro → persistir se for novo → processar uma única vez → atualizar o status → responder.

Fluxo de diagnóstico

1. Identificação e tratamento da duplicidade

%%{init: {"flowchart": {"nodeSpacing": 18,"rankSpacing": 24,"diagramPadding": 4,"padding": 7
}}}%%
flowchart TD
A["Webhook recebido<br/>pelo endpoint"] --> B{"Existe registro com o mesmo<br/>identificador do evento?"}

B --> BSim(("Sim"))
B --> BNao(("Não"))

BSim --> C{"O evento já foi<br/>processado?"}

BNao --> D["Persistir o evento<br/>com status recebido"]

C --> CSim(("Sim"))
C --> CNao(("Não"))

CSim --> E["Não executar novamente<br/>a regra de negócio"]

CNao --> F["Continuar o processamento<br/>pendente"]

D --> G["Processar o evento<br/>uma única vez"]
F --> G

E --> H["Registrar a entrega<br/>duplicada"]

G --> I["Atualizar o status<br/>para processado"]

H --> J["Responder sucesso sem<br/>repetir a ação"]
I --> J

classDef inicio fill:#DBEAFE,stroke:#2563EB,color:#1E3A8A,stroke-width:3px
classDef decisao fill:#FEF3C7,stroke:#D97706,color:#78350F,stroke-width:3px
classDef correcao fill:#FFEDD5,stroke:#EA580C,color:#7C2D12,stroke-width:2px
classDef validacao fill:#E0F2FE,stroke:#0284C7,color:#0C4A6E,stroke-width:2px
classDef sucesso fill:#DCFCE7,stroke:#16A34A,color:#14532D,stroke-width:3px
classDef analise fill:#FEE2E2,stroke:#DC2626,color:#7F1D1D,stroke-width:3px

classDef respostaSim fill:#22C55E,stroke:#15803D,color:#FFFFFF,stroke-width:3px
classDef respostaNao fill:#EF4444,stroke:#B91C1C,color:#FFFFFF,stroke-width:3px
classDef respostaDuvida fill:#8B5CF6,stroke:#6D28D9,color:#FFFFFF,stroke-width:3px

class A inicio
class B,C decisao
class D,F,G,I correcao
class H validacao
class J sucesso
class E analise

class BSim,CSim respostaSim
class BNao,CNao respostaNao

linkStyle default stroke:#94A3B8,stroke-width:2px
linkStyle 1,5 stroke:#22C55E,stroke-width:4px
linkStyle 2,6 stroke:#EF4444,stroke-width:4px

2. Processamento com controle de idempotência

%%{init: {"flowchart": {"nodeSpacing": 18,"rankSpacing": 24,"diagramPadding": 4,"padding": 7
}}}%%
flowchart TD
A["Antes de processar<br/>o webhook"] --> B{"Existe uma chave única<br/>para o evento?"}

B --> BSim(("Sim"))
B --> BNao(("Não"))

BNao --> C["Definir uma chave<br/>idempotente"]

BSim --> D["Consultar o armazenamento<br/>de eventos"]
C --> D

D --> E{"A chave já está<br/>registrada?"}

E --> ESim(("Sim"))
E --> ENao(("Não"))

ESim --> F{"O status é<br/>processado?"}

ENao --> G["Registrar o evento<br/>com status recebido"]

F --> FSim(("Sim"))
F --> FNao(("Não"))

FSim --> H["Responder sucesso sem<br/>repetir o processamento"]

FNao --> I["Bloquear a execução<br/>concorrente"]
G --> I

I --> J["Executar a regra de negócio<br/>uma única vez"]

J --> K{"O processamento<br/>foi concluído?"}

K --> KSim(("Sim"))
K --> KNao(("Não"))

KSim --> L["Atualizar o status<br/>para processado"]

KNao --> M["Registrar o erro e manter<br/>para reprocessamento"]

L --> N["Responder sucesso"]
H --> N

M --> O["Reprocessar utilizando<br/>a mesma chave"]
O --> I

classDef inicio fill:#DBEAFE,stroke:#2563EB,color:#1E3A8A,stroke-width:3px
classDef decisao fill:#FEF3C7,stroke:#D97706,color:#78350F,stroke-width:3px
classDef correcao fill:#FFEDD5,stroke:#EA580C,color:#7C2D12,stroke-width:2px
classDef validacao fill:#E0F2FE,stroke:#0284C7,color:#0C4A6E,stroke-width:2px
classDef sucesso fill:#DCFCE7,stroke:#16A34A,color:#14532D,stroke-width:3px
classDef analise fill:#FEE2E2,stroke:#DC2626,color:#7F1D1D,stroke-width:3px

classDef respostaSim fill:#22C55E,stroke:#15803D,color:#FFFFFF,stroke-width:3px
classDef respostaNao fill:#EF4444,stroke:#B91C1C,color:#FFFFFF,stroke-width:3px
classDef respostaDuvida fill:#8B5CF6,stroke:#6D28D9,color:#FFFFFF,stroke-width:3px

class A inicio
class B,E,F,K decisao
class C,G,I,J,L,O correcao
class D validacao
class N sucesso
class H,M analise

class BSim,ESim,FSim,KSim respostaSim
class BNao,ENao,FNao,KNao respostaNao

linkStyle default stroke:#94A3B8,stroke-width:2px
linkStyle 1,7,11,18 stroke:#22C55E,stroke-width:4px
linkStyle 2,8,12,19 stroke:#EF4444,stroke-width:4px

O primeiro fluxo orienta como identificar e tratar um evento recebido novamente, enquanto o segundo mostra como impedir que a regra de negócio seja executada mais de uma vez.


Como prevenir

Persista todos os eventos recebidos e utilize uma chave única para controlar o processamento. Mantenha status internos, como recebido, processado e erro, e consulte esse registro antes de executar a regra de negócio.

Utilize restrições de unicidade, controle de concorrência e operações idempotentes. Teste o fluxo enviando o mesmo evento mais de uma vez e confirme que seus efeitos ocorrem somente uma vez.

O mesmo webhook pode ser recebido novamente. A regra de negócio deve ser executada apenas uma vez.



Did this page help you?