PagamentosWebhooks

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:

  1. Acesse isolutionpay.com/dashboard/api-keys
  2. Abra a API Key que deseja configurar
  3. Em Webhook, informe a URL do seu servidor (precisa ser https:// em produção)
  4. Escolha os eventos que deseja receber: Cashout, Pagamento confirmado e/ou Venda criada
  5. Salve — o Webhook Secret aparece logo abaixo e você vai usá-lo pra validar as requisições

Eventos disponíveis

Pagamentos

EventoQuando é disparado
payment.paidPagamento confirmado (PIX ou cartão)
payment.expiredPIX expirou sem pagamento
payment.canceledCobrança cancelada
payment.deniedCartão negado ou reprovado por antifraude
payment.refundedEstorno realizado
payment.chargedbackChargeback recebido
payment.failedFalha no processamento

Cashout

EventoQuando é disparado
cashout.processedPIX do cashout foi liquidado com sucesso
cashout.failedCashout 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"
}
CampoDescrição
eventNome do evento
payment.idUUID interno da transação
payment.transaction_idMesmo valor que id — mantido por compatibilidade
payment.statusStatus interno: CONCLUIDA, REFUNDED, CHARGEDBACK, etc.
payment.methodpix, credit_card ou debit_card
payment.amount_centsValor bruto em centavos
payment.installmentsNúmero de parcelas (sempre 1 pra PIX)
payment.paid_atPresente apenas no evento payment.paid
customerDados 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_at está presente apenas no evento cashout.processed. No evento cashout.failed o 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: 1723042800

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