Referência
Retries e checklist
Como responder às entregas, lidar com reenvios, garantir idempotência e ir para produção com segurança.
Resposta esperada
| HTTP | Efeito |
|---|---|
| 2xx (200–299) | Entrega considerada sucesso |
| Outro / timeout / rede | Falha → retry |
Resposta ideal:
HTTP/1.1 200 OK
Content-Type: application/json
{"received":true}Retries
A HyzePay tenta de novo automaticamente com backoff:
| Tentativa | Atraso |
|---|---|
| 1 | imediato |
| 2 | ~2 s |
| 3 | ~8 s |
Timeout por tentativa: 15 segundos. Cada tentativa gera registro de auditoria no painel.
Boas práticas
- Responda rápido(< 2 s se possível)
- Processamento pesado: grave o evento e processe em background
- Não retorne 5xx se o evento já foi processado — retorne 200 (idempotência)
Idempotência
Use como chave única, nesta ordem:
event.id(v2) /event_id(v1)- Ou o par
(payment.id, type)
Guarde em uma tabela e ignore duplicatas — evita liberar o produto duas vezes se houver retry.
CREATE TABLE processed_webhook_events (
event_id TEXT PRIMARY KEY,
processed_at TIMESTAMPTZ NOT NULL DEFAULT now()
);Exemplo completo mínimo (Node.js)
import express from "express";
import { createHmac, timingSafeEqual } from "crypto";
const SECRET = process.env.HYZEPAY_WEBHOOK_SECRET!;
const seen = new Set<string>(); // use Redis/DB em produção
function verify(raw: string, header: string) {
// … igual seção Verificação e Segurança …
}
const app = express();
app.post("/webhooks/hyzepay", express.raw({ type: "application/json" }), async (req, res) => {
const raw = req.body.toString("utf8");
if (!verify(raw, req.header("x-hyzepay-signature") || "")) {
return res.status(401).end();
}
const event = JSON.parse(raw);
const eventId = event.id || event.event_id;
if (seen.has(eventId)) return res.json({ ok: true, duplicate: true });
seen.add(eventId);
const type = event.type || event.event;
const obj = event.data?.object ?? event.data;
if (type === "payment.paid" && obj?.status === "paid") {
await fulfillOrder(obj.external_id || obj.id, obj);
}
return res.json({ received: true });
});Para teste real use o botão Enviar teste no painel. Assinatura inválida deve ser rejeitada pelo seu código.
Checklist de produção
- URL HTTPS pública (não localhost em produção)
- Secret whsec_… no env (HYZEPAY_WEBHOOK_SECRET) — nunca no frontend
- Validação de assinatura + tolerância de timestamp
- Handler de payment.paid com checagem de status e valor
- Idempotência por event.id
- Resposta 2xx rápida
- Logs de X-HyzePay-Event-Id e X-HyzePay-Delivery
- Evento test ok no painel
- Webhook inscrito em payment.paid (e outros que precisar)
- Ambiente de homologação com secret separado
Erros comuns
| Problema | Causa | Solução |
|---|---|---|
| Assinatura sempre inválida | Body re-serializado | Use o raw body original |
| Assinatura inválida | Secret errado / espaços | Confira o whsec_… na criação |
| Timestamp fora | Clock do servidor errado | NTP; tolerância só se necessário |
| Não recebe eventos | HTTP, firewall, webhook pausado | HTTPS + endpoint público + ativo |
| Duplicou liberação | Sem idempotência | Persista event.id |
| Timeout / retries | Handler lento | 200 rápido + fila |
| payment.paid sem external_id | Não enviou na criação | Passe external_id no POST /api/v1/payments |
Referência rápida
Painel: Integração → Webhook
Método: POST
Auth: HMAC (X-HyzePay-Signature)
Versão: v2 recomendada
Evento-chave: payment.paid
Secret: whsec_… (só na criação)