Webhooks Explicados: Payloads Assinados e Retentativas
Você acabou de integrar um gateway de pagamento usando webhooks. Tudo funciona nos testes, mas em produção, às vezes os eventos se perdem, ou você recebe notificações duplicadas que causam cobranças duplicadas. A causa raiz geralmente está em como você lida com payloads assinados e retentativas. Este artigo explica a mecânica dos webhooks e fornece passos práticos para construir uma integração robusta e segura.
O Que São Webhooks?
Webhooks são callbacks HTTP definidos pelo usuário. Quando um evento ocorre em um sistema de origem (por exemplo, um pagamento é processado), ele envia uma requisição HTTP POST para uma URL que você especifica, contendo os dados do evento. Diferente do polling, webhooks enviam dados quase em tempo real, reduzindo latência e carga no servidor.
No entanto, webhooks introduzem desafios: verificar autenticidade, lidar com falhas e garantir processamento exatamente uma vez. Vamos abordar cada um.
Payloads Assinados: Verificando Autenticidade
Como os endpoints de webhook são publicamente acessíveis, qualquer pessoa poderia enviar requisições falsas. Para evitar isso, os provedores assinam o payload com uma chave secreta. Você verifica a assinatura para garantir que a requisição é genuína.
Como Funcionam as Assinaturas HMAC
A maioria dos provedores usa HMAC (Hash-based Message Authentication Code) com SHA-256. O provedor calcula uma assinatura usando o corpo da requisição e um segredo compartilhado, então a inclui em um cabeçalho (por exemplo, X-Signature). Você recalcula a assinatura do seu lado e compara.
Exemplo em Python:
import hmac
import hashlib
def verify_signature(payload, secret, received_signature):
expected = hmac.new(secret.encode(), payload, hashlib.sha256).hexdigest()
return hmac.compare_digest(expected, received_signature)
Importante: Use o corpo bruto da requisição, não o JSON parseado, pois o parsing pode alterar espaços em branco e quebrar a assinatura.
Armadilhas Comuns
- Usar dados parseados: Sempre verifique contra os bytes brutos.
- Ataques de temporização: Use comparação de tempo constante (por exemplo,
hmac.compare_digest). - Ignorar timestamps: Alguns provedores incluem um timestamp na assinatura para prevenir ataques de replay. Valide que está dentro de uma janela de tolerância (por exemplo, 5 minutos).
Retentativas: Lidando com Falhas com Elegância
Seu endpoint pode estar temporariamente fora do ar, ou problemas de rede podem causar falhas. Sistemas de webhook confiáveis retentam entregas falhas com backoff exponencial.
Estratégias de Retentativa
Provedores tipicamente retentam em respostas não-2xx ou timeouts. Padrões comuns:
| Estratégia | Descrição | Exemplo |
|---|---|---|
| Intervalo fixo | Retentar a cada N segundos | A cada 30s, até 5 vezes |
| Backoff exponencial | Dobrar o atraso a cada tentativa | 1s, 2s, 4s, 8s... |
| Exponencial com jitter | Adicionar aleatoriedade para evitar thundering herd | 1s ± 0.5s, 2s ± 1s... |
Como receptor, você não pode controlar a política de retentativa do remetente, mas pode projetar seu endpoint para ser resiliente.
Melhores Práticas para Receptores
- Responda rapidamente: Retorne 2xx dentro de alguns segundos. Delegue o processamento para um job em background.
- Seja idempotente: Use uma chave de idempotência do payload para evitar processamento duplicado.
- Registre tudo: Armazene webhooks recebidos para depuração e replay.
- Monitore falhas: Configure alertas para falhas repetidas.
Implementando Idempotência
Duplicatas acontecem: o remetente pode retentar porque sua resposta foi lenta, ou você pode acidentalmente processar o mesmo evento duas vezes. Idempotência garante que processar um evento múltiplas vezes tem o mesmo efeito que uma vez.
Use um ID de evento único (frequentemente fornecido no payload ou cabeçalhos) e armazene-o em um banco de dados com uma restrição única. Antes de processar, verifique se o ID existe; se sim, pule.
def process_event(event_id, data):
if EventLog.exists(event_id):
return # já processado
EventLog.create(event_id)
# processar dados...
Para sistemas de alto throughput, use um lock distribuído ou uma transação de banco de dados para evitar condições de corrida.
Considerações de Segurança
Além da verificação de assinatura, considere:
- HTTPS: Sempre use TLS para prevenir interceptação.
- Allowlisting de IP: Se o provedor publica ranges de IP, restrinja requisições recebidas.
- Rate limiting: Proteja seu endpoint contra abuso.
- Validação de payload: Mesmo após verificação de assinatura, valide o schema dos dados para evitar ataques de injeção.
Testando Webhooks Localmente
Durante o desenvolvimento, você precisa de uma URL pública para receber webhooks. Ferramentas como ngrok ou localtunnel expõem seu servidor local. Alternativamente, muitos provedores oferecem uma CLI para encaminhar eventos para seu localhost.
Simule falhas para testar o tratamento de retentativas: retorne erros 500 intencionalmente e observe o padrão de retentativa do provedor.
FAQ
Como verifico uma assinatura de webhook?
Use HMAC com o segredo compartilhado. Calcule o hash do corpo bruto da requisição e compare com o cabeçalho de assinatura usando uma função de comparação de tempo constante.
O que devo fazer se receber webhooks duplicados?
Implemente idempotência armazenando IDs de eventos. Verifique se o evento já foi processado antes de executar a lógica de negócios.
Posso confiar apenas nas retentativas de webhook para confiabilidade?
Não. Retentativas ajudam, mas não podem garantir a entrega. Para eventos críticos, combine webhooks com um fallback de polling ou uma fila de mensagens que persiste eventos.
Ao depurar payloads de webhook, você pode precisar inspecionar dados JSON. Use nosso JSON Formatter para formatar e validar payloads rapidamente.