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