Pular para o conteúdo principal

Webhooks

Quando o status de uma cobrança muda, enviamos um POST para o seu endpoint. Informe webhookUrl na criação da cobrança, ou configure um endpoint fixo no dashboard (Webhooks).

Headers

X-PagNow-Event-Type: payment.completed # ou payment.failed
X-PagNow-Delivery-Id: <uuid>
X-PagNow-Signature: sha256=<hmac_hex>
Content-Type: application/json

Corpo

Sempre no formato { event, data }. O nome do evento está em event e o status da transação em data.status.

{
"event": "payment.completed",
"data": {
"transactionId": "a99e3d82-352b-49b2-b10e-4551ca69446e",
"amount": 1990,
"status": "PAID",
"previousStatus": "WAITING_PAYMENT",
"paidWith": "PIX",
"providerFee": 30,
"platformFee": 40,
"netAmount": 1920,
"occurredAt": "2026-05-27T12:34:56.000Z"
}
}

Eventos: payment.completed (status PAID) e payment.failed (status REFUSED). Campos de taxa (providerFee/platformFee/netAmount) e paidWith só aparecem quando aplicáveis.

Verificação de assinatura

A assinatura é o HMAC-SHA256 do corpo cru (bytes recebidos, sem re-serializar) usando o secret do seu endpoint, no formato sha256=<hex>. Sempre valide antes de confiar no payload.

import { createHmac, timingSafeEqual } from 'crypto';

function verify(rawBody, header, secret) {
const expected = 'sha256=' + createHmac('sha256', secret).update(rawBody).digest('hex');
const a = Buffer.from(header ?? '');
const b = Buffer.from(expected);
return a.length === b.length && timingSafeEqual(a, b);
}

// Express — use o corpo CRU (raw), não o JSON já parseado:
app.post('/webhooks/pagnow', express.raw({ type: 'application/json' }), (req, res) => {
if (!verify(req.body, req.headers['x-pagnow-signature'], endpointSecret)) {
return res.status(401).end();
}
const { event, data } = JSON.parse(req.body.toString('utf8'));
if (event === 'payment.completed') {
// data.status === 'PAID' — libere o pedido
}
res.status(200).end();
});

Os SDKs já trazem isso pronto: pagnow.webhooks.verify(rawBody, signature, secret) (Node) e $pagnow->webhooks->verify($raw, $sig, $secret) (PHP). Veja SDKs.

Boas práticas

  • Responda 2xx rápido; reentregamos em caso de falha.
  • Use o corpo cru na verificação (não o JSON re-serializado).
  • Trate entregas duplicadas de forma idempotente (use data.transactionId + X-PagNow-Delivery-Id).