Webhooks de cobrança
Com callbackUrl configurada, a YumePay envia um POST a cada evento de cobrança.
{
"event": "charge.denied",
"charge": {
"id": 791,
"subscription_id": 12,
"value": 2990,
"status": "DENIED",
"due_date": "2026-10-09",
"denial_code": "05",
"denial_message": "Not Authorized",
"attempts": 1,
"next_retry_at": null
}
}
| Campo | Descrição |
|---|---|
status | Status atual da cobrança no momento da entrega: "PENDING", "CONFIRMED", "DENIED" ou "REFUNDED" |
denial_code / denial_message | Motivo da recusa. Preenchidos só em cobrança negada |
attempts | Tentativas feitas até agora |
next_retry_at | Próxima retentativa, YYYY-MM-DD, ou null |
| Evento | O que dispara | Quando |
|---|---|---|
charge.created | Cobrança gerada no vencimento | Registrado antes da chamada ao adquirente |
charge.confirmed | Pagamento aprovado — na primeira tentativa ou numa retentativa | Logo após a aprovação |
charge.retry_scheduled | Tentativa negada com nova tentativa agendada. Não é negação definitiva. Só com retentativa ligada | Logo após a recusa |
charge.denied | Pagamento negado (definitivo, com retentativa ligada) | Logo após a recusa |
charge.refunded | Cobrança estornada pela seção Estornar uma cobrança. Status REFUNDED | Logo após o estorno |
event diz o que aconteceu; charge mostra a cobrança como ela está agoraO objeto charge é lido no momento da entrega, então pode já estar à frente do evento: um charge.created que chega depois da aprovação vem com status: "CONFIRMED", e um charge.confirmed entregue depois de um estorno vem com status: "REFUNDED". Decida pelo event e use charge.status como o estado mais recente.
Qual evento libera ou bloqueia o serviço? charge.confirmed libera. charge.denied é o sinal para agir sobre a inadimplência. charge.retry_scheduled é informativo. charge.refunded indica que o valor voltou ao pagador.
Reenvio: enquanto o seu endpoint não responder 2xx, a YumePay reenvia até 10 vezes, com intervalo crescente a partir de 30 s (30 s, 1 min, 2 min, 4 min… limitado a 1 h entre tentativas).
Como testar a entrega em homologação
Os webhooks de homologação estão ativos para todos os lojistas. Para recebê-los sem expor um servidor seu:
- Gere uma URL pública temporária, por exemplo em webhook.site ou com um túnel como ngrok.
- Crie uma assinatura com essa URL em
callbackUrlenextDueDateigual a hoje — a primeira cobrança sai na hora e disparacharge.createdecharge.confirmed(oucharge.retry_scheduled/charge.denied) em menos de um minuto. - Estorne essa cobrança (Estornar uma cobrança) para receber
charge.refunded. - Para PIX, crie uma venda PIX com
callbackUrl(Cobrança PIX) e pague o QR Code.
| Cartão | Resultado |
|---|---|
4111111111111111 | Tokeniza e a cobrança é aprovada |
4111111111111202 (válido, terminado em 2) | Tokeniza e a cobrança volta negada com código 05 — use para testar recusa e retentativa |
4111111111111112 (dígito verificador errado) | 400 Número do cartão inválido, sem chamar o adquirente |
Validade 01/2020 | 400 Cartão vencido |
Validade 13/2030 | 400 Data de validade inválida (use MM/AAAA) |
O 422 CARD_REJECTED na tokenização não tem cartão de teste: o sandbox do adquirente tokeniza qualquer número que passe pelas validações acima.
Lembre: venda com cartão (pré-captura, captura e cancelamento, em Pagamentos avulsos) não gera webhook. Uma observação que só usou cartão avulso não recebe nada — é o esperado.
Validando a assinatura do webhook
Vale para todos os webhooks deste guia: cobrança de assinatura, venda PIX e link de pagamento. Cada requisição vai com o cabeçalho:
X-Yumepay-Signature: sha256=<hex do HMAC-SHA256>
O HMAC é calculado sobre o corpo bruto da requisição, byte a byte. A chave secreta é entregue junto das credenciais de acesso, e é diferente entre homologação e produção.
Exemplo em Python
import hmac, hashlib
def assinatura_confere(corpo_bruto: bytes, cabecalho: str, segredo: str) -> bool:
esperado = "sha256=" + hmac.new(segredo.encode(), corpo_bruto, hashlib.sha256).hexdigest()
return hmac.compare_digest(esperado, cabecalho)
Exemplo em Node
const crypto = require("crypto");
function assinaturaConfere(corpoBruto, cabecalho, segredo) {
const esperado = "sha256=" + crypto.createHmac("sha256", segredo).update(corpoBruto).digest("hex");
return crypto.timingSafeEqual(Buffer.from(esperado), Buffer.from(cabecalho));
}
Regras do endpoint
Valem para todos os webhooks deste guia: venda PIX (Webhook de venda PIX), link de pagamento (Webhook de link de pagamento) e cobrança de assinatura.
URL do endpoint
A URL precisa ser https:// e apontar para um host público. São recusados:
http://;- usuário e senha na URL (
https://user:pass@...); localhoste*.localhost;- host que não resolve no DNS;
- host que resolve para endereço interno (privado, loopback, link-local ou reservado).
A regra é conferida ao gravar a URL (criação e edição) e de novo a cada entrega: se o host passar a resolver para um endereço interno, o envio falha e segue a política de reenvio.
URL fora da regra devolve 400, com a mensagem da rota:
| Webhook | Rota | Mensagem |
|---|---|---|
| Venda PIX | POST /api/v1/online/sales/pre-capture/ | Webhook URL must be a safe public HTTPS URL |
| Link de pagamento | POST /api/v1/payment-links/ | {"webhookUrl": ["Enter a safe public HTTPS URL."]} |
| Assinatura | Criação, edição e PUT .../callback | callbackUrl must be a safe public HTTPS URL |
Para testar, use uma URL pública temporária, como as do webhook.site ou de um túnel ngrok (ver Como testar a entrega em homologação, acima).
Ao implementar o seu endpoint
- Responda
2xxpara confirmar o recebimento. Qualquer outro status, inclusive3xx, é tratado como falha. - Responda rápido (até 10 s) e processe de forma assíncrona.
- Seja idempotente pelo par
charge.id+event: a mesma entrega pode chegar mais de uma vez, e a mesma cobrança gera vários eventos (retry_scheduled→confirmed→refunded, por exemplo). - Não assuma ordem de entrega. Em caso de dúvida, consulte a cobrança (Listar cobranças de uma assinatura).
- No Express, use
express.raw({ type: "application/json" })nessa rota. No Django, leiarequest.bodyantes de qualquer parsing.