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
iddo 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.
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.
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.