Webhooks (notificação à loja)
A HibouPay envia notificações de eventos da sessão para a URL de callback da sua loja
(platformCallbackUrl na criação da sessão). Schema completo: WebhookEvent na
referência Redoc.
Eventos
event |
outcome |
Quando dispara |
|---|---|---|
payment.approved |
APPROVED |
Somente depois que o pagamento foi liquidado. Nunca antecipado. authorizationId presente (referência do desembolso). |
payment.denied |
DENIED |
Pagamento negado (ex.: margem insuficiente, recusa de proposta/assinatura). |
session.cancelled |
CANCELLED |
A sessão não segue: cancelamento pela loja (POST /v1/sessions/{id}/cancel), valor fora da faixa permitida, ou indisponibilidade temporária do fluxo de margem. O envelope é o mesmo (event=session.cancelled); use a consulta de status se precisar do detalhe do status da sessão. |
session.expired |
EXPIRED |
O tempo de vida da sessão esgotou. |
Envelope (WebhookEvent):
{
"event": "payment.approved",
"sessionId": "22222222-2222-4222-8222-222222222222",
"platformPaymentId": "order-42",
"outcome": "APPROVED",
"authorizationId": "auth-xyz",
"occurredAt": "2026-07-14T17:12:34.000Z"
}
authorizationId e reason são opcionais — authorizationId aparece em payment.approved;
reason pode acompanhar payment.denied / session.cancelled.
Entrega: at-least-once, retry, dedupe
A entrega é at-least-once: em resposta não-2xx (ou falha de rede), a HibouPay tenta de novo com backoff. O mesmo evento pode chegar mais de uma vez — sua rota deve ser idempotente.
- Ack: responda
200(ou qualquer 2xx) assim que tiver persistido/processado o evento (mesmo que o processamento downstream seja assíncrono). Qualquer status não-2xx dispara retry. - Dedupe: cada tentativa carrega
X-HibouPay-Delivery-Id— use esse header como chave de deduplicação (ex.:INSERT ... ON CONFLICT DO NOTHING) antes de agir sobre o evento.
Verificação de assinatura
Headers em toda entrega:
X-HibouPay-Signature: t=<unix_seconds>,v1=<hmac_hex>X-HibouPay-Timestamp: <unix_seconds>
A assinatura é HMAC-SHA256, calculada sobre "{t}.{rawBody}" — os bytes exatos do corpo
recebido, concatenados com o timestamp por um ponto. t é o mesmo valor do componente t= do
header de assinatura (e de X-HibouPay-Timestamp).
Crítico: se o framework faz parse do JSON antes do handler (ex.:
express.json()) e você tenta verificar re-serializando o objeto, a assinatura sempre falha — o corpo re-serializado nunca é idêntico byte a byte. Você precisa do corpo cru.
Com o SDK (@hiboupay/api-client) — Express
import express from 'express';
import { constructEvent, WebhookSignatureError } from '@hiboupay/api-client';
const app = express();
app.post(
'/webhooks/hiboupay',
// express.raw preserva o Buffer original — NÃO use express.json() nesta rota.
express.raw({ type: 'application/json' }),
(req, res) => {
try {
const event = constructEvent(
req.body,
req.headers,
process.env.HIBOUPAY_WEBHOOK_SECRET!,
);
// dedupe por req.headers['x-hiboupay-delivery-id'] antes de agir
switch (event.event) {
case 'payment.approved':
// somente depois que o pagamento foi liquidado
break;
case 'payment.denied':
case 'session.cancelled':
case 'session.expired':
break;
}
res.status(200).end();
} catch (err) {
if (err instanceof WebhookSignatureError) {
res.status(400).send('invalid signature');
return;
}
throw err;
}
},
);
constructEvent verifica a assinatura e faz o JSON.parse do corpo cru, devolvendo o
WebhookEvent tipado — ou lança WebhookSignatureError.
Alternativa: verifyWebhookSignature(rawBody, signatureHeader, secret, opts?) retorna boolean.
Verificação manual (sem SDK)
1. Extraia t e v1 de X-HibouPay-Signature (formato "t=<t>,v1=<hex>").
2. payload = `${t}.${rawBody}` // bytes exatos recebidos, sem parse/re-serialização
3. expected = hex(HMAC-SHA256(secret, payload))
4. Compare expected com v1 em tempo constante.
5. Rejeite se |now - t| exceder a tolerância anti-replay (default do SDK: 300s).
Anti-replay
Assinaturas com t fora da janela de tolerância (mais de 300 segundos no passado ou futuro, por
default no SDK) são rejeitadas.
Configurar o segredo e o callback
Configure a URL de callback e o segredo HMAC no painel HibouPay (ou no onboarding comercial). Sem segredo configurado, a HibouPay não envia webhook não assinado — a entrega falha por design.
O platformCallbackUrl informado na criação da sessão indica para onde notificar; o segredo
permanece no painel/onboarding.
Segurança
- Nunca logue o segredo do webhook nem a API key em texto claro.
- Sempre valide a assinatura antes de agir — nunca confie em
X-HibouPay-Delivery-Idou no conteúdo sem verificar o HMAC. - Use HTTPS no callback — a HibouPay não garante confidencialidade em trânsito para HTTP puro.