Skip to main content
Seu endpoint de webhook é público — qualquer um poderia tentar enviar requisições falsas para ele. Por isso, valide a assinatura HMAC de todo evento antes de processá-lo.

Assinatura HMAC

Cada webhook é assinado com HMAC-SHA256 usando o signatureSecret que você recebeu ao cadastrar o webhook. A assinatura acompanha o evento no formato:
O HMAC é calculado sobre a string `${timestamp}.${corpo_raw}`. Para validar, recalcule a assinatura com o seu segredo e compare — usando uma comparação time-safe.
Use sempre timingSafeEqual / hmac.compare_digest / hmac.Equal ao comparar assinaturas — nunca === ou ==. Comparações diretas são vulneráveis a timing attacks.
Calcule o HMAC sobre o corpo raw (a string exata recebida), antes de qualquer JSON.parse. Re-serializar o JSON pode reordenar campos e invalidar a assinatura.

Proteção contra replay

Compare o timestamp da assinatura com o horário atual e rejeite entregas muito antigas (ex.: mais de 5 minutos). Isso evita que um evento legítimo capturado seja reenviado mais tarde por um atacante.

Idempotência

A mesma entrega pode chegar mais de uma vez (retentativa ou reenvio). Registre o id do evento e descarte duplicatas antes de processar.

Retentativas

Se seu endpoint não responder 2xx, a Ephra reenvia o evento automaticamente:
  • Até 3 retentativas por entrega.
  • Backoff crescente: aproximadamente 8 min, 15 min e 30 min.
  • São retentadas apenas entregas das últimas 48 horas; depois disso, a entrega é marcada como failed.
  • Timeout de 60 segundos por tentativa.
Se o seu processamento for pesado, responda 200 imediatamente e processe em segundo plano (enfileire).

Checklist de segurança

  • Use HTTPS — nunca HTTP em produção.
  • Valide a assinatura HMAC do header com comparação time-safe.
  • Rejeite entregas com timestamp muito antigo (replay).
  • Responda 2xx somente após concluir (ou enfileirar) o processamento.
  • Implemente idempotência usando o id do evento.
  • Não valide o payload inteiro com schemas rígidos — campos novos não devem quebrar seu endpoint.
Para valores altos, confirme o estado real com GET /v1/transactions/{id} antes de liberar o pedido.