Pular para o conteúdo
Fundamentos

Webhooks

A BOSS Pay avisa o seu servidor, por HTTPS, quando algo acontece. Cada entrega é assinada e retentada até dar certo.

Configurar

  • Cadastre a URL em Painel › Desenvolvedores › Webhooks, escolha os eventos e guarde o segredo do endpoint.
  • Endpoints são por ambiente: eventos do Sandbox só vão para endpoints do Sandbox.
  • Pelo painel dá para enviar um evento de teste, ver cada entrega (status HTTP, latência, tentativas) e reenviar.

Entrega

POST na sua URL
POST /webhooks/bosspay HTTP/1.1
Content-Type: application/json
User-Agent: BOSSPay-Webhooks/1.0
BossPay-Signature: t=1790812345,v1=5f2b…c9
BossPay-Event: payment.approved
BossPay-Delivery: cmv…
BossPay-Environment: PRODUCTION

{
  "id": "cmv…",
  "type": "payment.approved",
  "environment": "PRODUCTION",
  "createdAt": "2026-09-30T18:42:10.512Z",
  "data": { "id": "cmu…", "object": "transaction", "method": "PIX", "status": "APPROVED", "amount": "15990", "externalId": "1042", … }
}
  • Responda com 2xx em até 10 segundos. Processe de forma assíncrona se precisar de mais tempo.
  • Qualquer outro status, timeout ou redirecionamento (3xx não é seguido) conta como falha.
  • Retentativas com backoff exponencial: 30 s, 2 min, 8 min, 32 min, ~2 h e depois a cada 8 h, até 12 tentativas. Esgotadas, a entrega vai para a fila de falhas e você é notificado no painel.
  • A mesma notificação pode chegar mais de uma vez: use o id do evento para descartar duplicadas.
  • Em Produção a URL não pode apontar para IP privado, loopback ou metadados de nuvem.

Verificar a assinatura

Calcule o HMAC-SHA256 de <t>.<corpo bruto> com o segredo do endpoint e compare com v1 em tempo constante. Rejeite se t estiver a mais de 5 minutos do seu relógio.

Node.js
import crypto from "node:crypto";

export function verifyBossPay(rawBody, header, secret, toleranceSec = 300) {
  const parts = Object.fromEntries(header.split(",").map((p) => p.split("=")));
  const t = Number(parts.t);
  if (!t || !parts.v1 || Math.abs(Date.now() / 1000 - t) > toleranceSec) return false;
  const expected = crypto.createHmac("sha256", secret).update(`${t}.${rawBody}`).digest("hex");
  const a = Buffer.from(expected), b = Buffer.from(parts.v1);
  return a.length === b.length && crypto.timingSafeEqual(a, b);
}

// Express: use o corpo BRUTO (express.raw), não o JSON já convertido.
app.post("/webhooks/bosspay", express.raw({ type: "application/json" }), (req, res) => {
  if (!verifyBossPay(req.body.toString("utf8"), req.get("BossPay-Signature") ?? "", process.env.BOSSPAY_WEBHOOK_SECRET)) return res.sendStatus(400);
  const event = JSON.parse(req.body);
  // … trate event.type de forma idempotente (event.id)
  res.sendStatus(200);
});
Python
import hmac, hashlib, time

def verify_bosspay(raw_body: bytes, header: str, secret: str, tolerance=300) -> bool:
    parts = dict(p.split("=", 1) for p in header.split(","))
    t = int(parts.get("t", 0))
    if not t or "v1" not in parts or abs(time.time() - t) > tolerance:
        return False
    expected = hmac.new(secret.encode(), f"{t}.".encode() + raw_body, hashlib.sha256).hexdigest()
    return hmac.compare_digest(expected, parts["v1"])
Nunca confie apenas no webhook para valores: se precisar, confirme consultando GET /v1/transactions/:id.

Eventos

payment.createdCobrança criada (qualquer método).
payment.pendingAguardando pagamento.
payment.approvedPagamento confirmado — libere o pedido aqui.
payment.failedRecusada, falhou ou expirou (veja status e failureReason).
payment.refundedEstorno total ou parcial concluído.
payment.chargebackChargeback recebido em pagamento com cartão.
pix.receivedPIX recebido na conta.
pix.med_opened—
pix.med_closed—
boleto.paidBoleto compensado.
subscription.createdAssinatura criada.
subscription.paidFatura da assinatura paga.
subscription.failedCobrança da fatura falhou (entra na política de retentativa).
settlement.processingLiquidação iniciada.
settlement.completedLiquidação concluída — valor disponível no saldo.
crypto.receivedStablecoin recebida e confirmada na rede.
crypto.expiredCobrança cripto expirou sem pagamento.
crypto.withdrawal_completed—
crypto.withdrawal_failed—
conversion.completedConversão BRL ↔ stablecoin concluída.
conversion.failedConversão não concluída.
transfer.scheduledTransferência agendada.
transfer.pending_approvalTransferência aguardando aprovação de outro usuário.
transfer.completedTransferência concluída.
transfer.failedTransferência falhou.