Skip to main content
Se um pagamento for criado com webhookUrl, a API enviará notificações HTTP (webhooks) para os seguintes eventos:
  • payment.created — enviado após a criação do pagamento
  • payment.status_changed — enviado quando o status do pagamento muda

Assinatura digital de webhooks

Todos os webhooks enviados pela API pública são assinados digitalmente para garantir:
  • Autenticidade (o webhook foi realmente enviado pela Paguebit)
  • Integridade (o payload não foi alterado)
  • Proteção contra replay attacks

Headers enviados

Cada webhook inclui os seguintes headers HTTP:
HeaderDescrição
X-Paguebit-SignatureAssinatura HMAC-SHA256 do payload
X-Paguebit-TimestampTimestamp Unix (segundos)
X-Paguebit-Event-IdID único do evento
Observação: headers HTTP são case-insensitive. X-Paguebit-Signature e x-paguebit-signature são equivalentes.

Como funciona a assinatura

Ao criar um token de API pública, você recebe um webhookSecret exclusivo. A cada webhook enviado, a API:
  1. Obtém o corpo bruto (raw body) da requisição
  2. Concatena o timestamp no formato:
    {timestamp}.{rawBody}
    
  3. Gera uma assinatura usando HMAC-SHA256
  4. Envia o resultado no header X-Paguebit-Signature (hexadecimal)

Formato matemático

signed_payload = "{timestamp}.{rawBody}"
signature = HMAC_SHA256(webhookSecret, signed_payload)

Validando a assinatura

Para validar um webhook recebido:
  1. Leia os headers:
    • X-Paguebit-Signature
    • X-Paguebit-Timestamp
  2. Obtenha o corpo bruto da requisição
  3. Gere a assinatura localmente usando o mesmo algoritmo
  4. Compare as assinaturas usando comparação segura

Exemplo em Node.js (Express)

import crypto from 'crypto';
import express from 'express';

function validateWebhookSignature(
  rawBody: string,
  timestamp: string,
  signature: string,
  webhookSecret: string,
): boolean {
  const signedPayload = `${timestamp}.${rawBody}`;

  const expectedSignature = crypto
    .createHmac('sha256', webhookSecret)
    .update(signedPayload)
    .digest('hex');

  return crypto.timingSafeEqual(
    Buffer.from(signature, 'hex'),
    Buffer.from(expectedSignature, 'hex'),
  );
}

const app = express();

app.post(
  '/webhook',
  express.raw({ type: 'application/json' }),
  (req, res) => {
    const signature = req.headers['x-paguebit-signature'] as string;
    const timestamp = req.headers['x-paguebit-timestamp'] as string;
    const rawBody = req.body.toString('utf8');

    if (!signature || !timestamp) {
      return res.status(400).send('Missing webhook headers');
    }

    const isValid = validateWebhookSignature(
      rawBody,
      timestamp,
      signature,
      process.env.WEBHOOK_SECRET!,
    );

    if (!isValid) {
      return res.status(401).send('Invalid signature');
    }

    const payload = JSON.parse(rawBody);
    console.log('Webhook válido:', payload);

    res.status(200).send('OK');
  },
);

A validação deve ser feita antes de qualquer processamento do payload.
O rawBody deve ser usado exatamente como recebido.
Não faça JSON.parse + JSON.stringify antes de validar, pois isso altera o conteúdo e invalida a assinatura.

Payload do webhook

Campos enviados em todos os eventos:
CampoTipoDescrição
eventstringTipo do evento
idstringID do pagamento
statusstringStatus atual
amountnumberValor do pagamento (BRL)
storeIdstringID da loja
createdAtstringData ISO de criação
emailstringE-mail do pagamento
observationstring?Observação
qrCodeUrlstring?URL do QR Code
qrCopyPastestring?QR Copia e Cola
isPublicbooleanIndica se o pagamento é público

Campo adicional em payment.status_changed

CampoTipoDescrição
previousStatusstringStatus anterior

Exemplo — payment.created

{
  "event": "payment.created",
  "id": "pay_123",
  "status": "pending",
  "amount": 150,
  "storeId": "store_abc",
  "createdAt": "2025-12-16T15:00:00.000Z",
  "observation": "Pedido #9876",
  "email": "cliente@example.com",
  "qrCodeUrl": "https://.../qrcode.png",
  "qrCopyPaste": "000201010212...",
  "isPublic": true
}

Exemplo — payment.status_changed

{
  "event": "payment.status_changed",
  "id": "pay_123",
  "status": "approved",
  "previousStatus": "pending",
  "amount": 150,
  "storeId": "store_abc",
  "createdAt": "2025-12-16T15:00:00.000Z",
  "observation": "Pedido #9876",
  "email": "cliente@example.com",
  "qrCodeUrl": "https://.../qrcode.png",
  "qrCopyPaste": "000201010212...",
  "isPublic": true
}

Testando webhooks

Ferramentas recomendadas: Essas ferramentas permitem validar rapidamente a assinatura antes de ir para produção.

Resumo rápido

ItemValor
AlgoritmoHMAC-SHA256
Header da assinaturaX-Paguebit-Signature
Header do timestampX-Paguebit-Timestamp
Payload assinado{timestamp}.{rawBody}
EncodingHexadecimal
Proteção contra replay