Webhooks
Configure um endpoint no seu servidor e a ISolutionPay envia um POST automático toda vez que o status de um pagamento mudar — sem precisar fazer polling.
Configurando
O webhook é configurado diretamente na sua API Key:
- Acesse isolutionpay.com/dashboard/api-keys
- Abra a API Key que deseja configurar
- Em Webhook, informe a URL do seu servidor (precisa ser
https://em produção) - Escolha os eventos que deseja receber: Cashout, Pagamento confirmado e/ou Venda criada
- Salve — o Webhook Secret aparece logo abaixo e você vai usá-lo pra validar as requisições
Eventos disponíveis
Pagamentos
| Evento | Quando é disparado |
|---|---|
payment.paid | Pagamento confirmado (PIX ou cartão) |
payment.expired | PIX expirou sem pagamento |
payment.canceled | Cobrança cancelada |
payment.denied | Cartão negado ou reprovado por antifraude |
payment.refunded | Estorno realizado |
payment.chargedback | Chargeback recebido |
payment.failed | Falha no processamento |
Cashout
| Evento | Quando é disparado |
|---|---|
cashout.processed | PIX do cashout foi liquidado com sucesso |
cashout.failed | Cashout falhou — saldo estornado automaticamente |
Estrutura do payload
Todos os eventos seguem o mesmo formato:
{
"event": "payment.paid",
"payment": {
"id": "a1b2c3d4-...",
"transaction_id": "a1b2c3d4-...",
"status": "CONCLUIDA",
"method": "pix",
"amount_cents": 9990,
"installments": 1,
"created_at": "2025-08-07T14:00:00.000Z",
"paid_at": "2025-08-07T14:05:32.000Z"
},
"customer": {
"name": "João Silva",
"email": "joao@email.com",
"cpf": "12345678900"
},
"timestamp": "2025-08-07T14:05:32.000Z"
}| Campo | Descrição |
|---|---|
event | Nome do evento |
payment.id | UUID interno da transação |
payment.transaction_id | Mesmo valor que id — mantido por compatibilidade |
payment.status | Status interno: CONCLUIDA, REFUNDED, CHARGEDBACK, etc. |
payment.method | pix, credit_card ou debit_card |
payment.amount_cents | Valor bruto em centavos |
payment.installments | Número de parcelas (sempre 1 pra PIX) |
payment.paid_at | Presente apenas no evento payment.paid |
customer | Dados do pagador informados na criação da cobrança |
Payload — Cashout
Eventos cashout.processed e cashout.failed têm estrutura própria:
{
"event": "cashout.processed",
"cashout": {
"id": "31b972ce-4275-4dbb-a177-2dba6b8d9f37",
"merchant_transaction_id": "6ea30d8f-9835-49bb-8086-1d2d93bcb046",
"amount": 100,
"status": "processed",
"pix_key": "+5511999999999",
"pix_key_type": "phone",
"end_to_end_id": "E60746948202608112206abcdef12345",
"date_created": "2026-08-11T22:06:21.000Z",
"processed_at": "2026-08-11T22:07:05.000Z"
},
"timestamp": "2026-08-11T22:07:05.000Z"
}
processed_atestá presente apenas no eventocashout.processed. No eventocashout.failedo campo não aparece e o saldo já foi estornado automaticamente.
Validando a assinatura
Toda requisição enviada pela ISolutionPay inclui dois headers de segurança:
X-CasperPay-Signature: t=1723042800,v1=a3f9c12e...
X-CasperPay-Timestamp: 1723042800A assinatura é um HMAC-SHA256 calculado sobre {timestamp}.{corpo_json} usando seu Webhook Secret.
Como verificar
import { createHmac } from "crypto"
function verificarWebhook(signatureHeader, rawBody, secret) {
const parts = Object.fromEntries(
signatureHeader.split(",").map((s) => s.split("="))
)
const t = parseInt(parts.t, 10)
const v1 = parts.v1
// Rejeita requisições mais velhas que 5 minutos
if (Math.abs(Date.now() - t * 1000) > 300_000) {
throw new Error("Webhook expirado")
}
const expected = createHmac("sha256", secret)
.update(`${t}.${rawBody}`)
.digest("hex")
if (v1 !== expected) {
throw new Error("Assinatura inválida")
}
}import hmac
import hashlib
import time
def verificar_webhook(signature_header, raw_body, secret):
parts = dict(s.split("=", 1) for s in signature_header.split(","))
t = int(parts["t"])
v1 = parts["v1"]
# Rejeita requisições mais velhas que 5 minutos
if abs(time.time() - t) > 300:
raise Exception("Webhook expirado")
expected = hmac.new(
secret.encode(), f"{t}.{raw_body}".encode(), hashlib.sha256
).hexdigest()
if not hmac.compare_digest(v1, expected):
raise Exception("Assinatura inválida")⚠️ Leia o corpo da requisição como string bruta antes de parsear o JSON. Qualquer reformatação invalida a assinatura.
Respondendo ao webhook
Responda com HTTP 2xx assim que receber — não espere terminar o processamento. Respostas fora de 2xx ou ausência de resposta em 5 segundos são registradas como falha nos logs do painel.
app.post("/webhook", express.raw({ type: "application/json" }), (req, res) => {
const rawBody = req.body.toString("utf8") // express.raw() entrega Buffer
verificarWebhook(req.headers["x-casperpay-signature"], rawBody, process.env.WEBHOOK_SECRET)
res.status(200).json({ ok: true }) // responda rápido
// processe de forma assíncrona depois
const evento = JSON.parse(rawBody)
if (evento.event === "payment.paid") {
liberarAcesso(evento.payment.id)
}
})Logs
Acesse isolutionpay.com/dashboard/api-keys e abra a sua API Key para ver o histórico de disparos, código HTTP retornado e corpo da resposta do seu servidor.