Como receber webhooks com segurança: assinatura, idempotência e reprocessamento
Receber um webhook parece simples: o gateway manda um POST e você processa. Receber webhooks com segurança é outra história. O problema aparece em produção, quando o mesmo evento chega duas vezes, chega fora de ordem ou chega de quem não deveria. Estas são as práticas que eu aplico em todo projeto que mexe com pagamento.
1. Valide quem está mandando
O endereço do webhook não é segredo forte. Se alguém descobrir, pode mandar um "pagamento aprovado" falso. Por isso:
- Confira a assinatura, quando o gateway assina a mensagem. Normalmente é um HMAC do corpo da requisição com uma chave secreta, enviado num cabeçalho.
- Ou confira um token que o gateway envia em cada chamada.
- Na dúvida, consulte a API do gateway para confirmar o status do pedido antes de liberar algo importante.
const assinatura = crypto
.createHmac('sha256', process.env.WEBHOOK_SECRET)
.update(corpoBruto)
.digest('hex');
if (!crypto.timingSafeEqual(Buffer.from(assinatura), Buffer.from(req.headers['x-signature']))) {
return res.status(401).end();
}Repare que a assinatura é calculada sobre o corpo bruto, antes de converter para JSON.
2. Responda rápido e processe depois
O gateway espera uma resposta em poucos segundos. Se o seu código liberar acesso, mandar e-mail e chamar três APIs antes de responder, ele vai estourar o tempo, e o gateway vai considerar falha e reenviar.
O caminho seguro:
- validar a requisição;
- salvar o evento;
- responder
200; - processar numa fila, em segundo plano.
3. Seja idempotente
Gateways reenviam webhooks. Isso é normal e é bom, porque garante a entrega. Mas o seu sistema precisa aceitar o mesmo evento várias vezes sem efeito duplicado.
- Guarde o identificador do evento (ou pedido + status) e ignore o que já foi processado.
- Use operações que podem rodar de novo sem estragar nada: "deixar o acesso liberado", e não "somar mais um acesso".
4. Não confie na ordem de chegada
Às vezes o "aprovado" chega antes do "pendente", ou o "reembolsado" chega antes do "aprovado" ser processado. Para não regredir o status:
- guarde a data do evento que veio do gateway;
- defina quais transições são válidas (pendente → aprovado → reembolsado);
- ignore um evento mais antigo do que o último que você aplicou.
Extra: registre e reprocesse
Salve todos os webhooks recebidos, com corpo, cabeçalhos e resultado. Quando um cliente disser "paguei e não recebi", você abre o registro e sabe em segundos o que aconteceu. Se algo falhou, dá para reprocessar o evento sem pedir nada ao gateway.
Checklist para receber webhooks com segurança
- Assinatura ou token validados
- Resposta em menos de 5 segundos
- Processamento em fila
- Idempotência por evento ou pedido
- Transições de status controladas
- Registro de todos os eventos
- Alerta quando os webhooks param de chegar
Parece muito, mas é o que separa uma oferta que escala tranquila de uma que trava no dia do lançamento.
Quer isso funcionando na sua oferta?
Me conta o seu cenário em poucas linhas. Eu leio pessoalmente e te respondo pelo WhatsApp.