Webhooks
Cadastre uma URL sua para receber um POST assim que o status de uma cobrança ou saque mudar sem precisar ficar consultando a API.
Cadastrar um endpoint
| Campo | Tipo | Descrição | |
|---|---|---|---|
url | string | obrigatório | HTTPS obrigatório. |
label | string | opcional | Nome para identificar o endpoint no painel. |
events | array | opcional | Lista de eventos (veja abaixo). Omitido = recebe todos. |
O secret só aparece nesta resposta, na criação. Guarde-o é o que assina os eventos recebidos, e não há como recuperá-lo depois.
{
"object": "webhook_endpoint",
"id": "whe_1",
"url": "https://minhaloja.com/webhooks/stacepay",
"events": null,
"active": true,
"last_delivery_at": null,
"last_status_code": null,
"consecutive_failures": 0,
"created_at": "2026-08-11T21:25:50-03:00",
"secret": "052742b9296fda91caa59228e6939bbc1e651fc"
}Listar, remover e ver entregas
Eventos disponíveis
| Evento | Quando dispara |
|---|---|
charge.paid | Cobrança confirmada. |
charge.authorized | Cartão autorizado (antes da captura). |
charge.processing | Em confirmação com o parceiro. |
charge.refused | Recusada. |
charge.failed | Falhou antes do parceiro. |
charge.refunded | Estornada. |
charge.chargeback | Contestada pelo pagador. |
charge.disputed | Em disputa. |
charge.blocked | Bloqueada por análise de risco. |
withdrawal.paid | Saque concluído. |
withdrawal.processing | Enviado ao parceiro. |
withdrawal.approved | Aprovado, aguardando envio. |
withdrawal.rejected | Recusado. |
withdrawal.failed | Falhou no envio. |
withdrawal.refunded | Valor devolvido ao saldo. |
withdrawal.canceled | Cancelado. |
withdrawal.blocked | Bloqueado por análise. |
Formato do evento
{
"id": "evt_9e2f...",
"object": "event",
"type": "charge.paid",
"created_at": "2026-08-11T21:40:02-03:00",
"data": { "object": "charge", "id": "sp1_d9c51fe9", "status": "paid", "...": "..." }
}Verificando a assinatura
Todo envio traz o cabeçalho Novex-Signature: t=<timestamp>,v1=<hmac>. O HMAC-SHA256 é calculado sobre a string "{timestamp}.{corpo cru}" usando o secret do endpoint.
const crypto = require('crypto');
function verificar(header, body, secret) {
const [tPart, vPart] = header.split(',');
const timestamp = tPart.split('=')[1];
const assinaturaRecebida = vPart.split('=')[1];
const esperada = crypto
.createHmac('sha256', secret)
.update(`${timestamp}.${body}`)
.digest('hex');
return crypto.timingSafeEqual(Buffer.from(assinaturaRecebida), Buffer.from(esperada));
}function verificar(string $header, string $rawBody, string $secret): bool {
[$tPart, $vPart] = explode(',', $header, 2);
$timestamp = substr($tPart, 2);
$recebida = substr($vPart, 3);
$esperada = hash_hmac('sha256', $timestamp . '.' . $rawBody, $secret);
return hash_equals($esperada, $recebida);
}Sempre valide contra o corpo cru da requisição, antes de decodificar o JSON reserializar altera espaçamento e ordem de chaves, e a assinatura deixa de bater.
Reenvio
Uma resposta fora da faixa 200–299 conta como falha. Reenviamos com backoff crescente (~1min, 5min, 30min, 2h, 6h) por até 6 tentativas. Depois de 20 falhas seguidas, o endpoint é desativado automaticamente reative cadastrando de novo.