Integrator

Webhooks

Quando uma cobrança chega a um estado final, enviamos um POST assinado para a URL configurada no painel.

Configuração

  • No painel, em Integração → Webhook, informe uma URL HTTPS pública. Uma URL por conta.
  • Copie o segredo de assinatura (whsec_…). Ele pode ser rotacionado a qualquer momento.
  • Use Enviar teste para receber um evento ping.
  • O painel mostra o log de entregas com status, resposta e botão para reenviar.

Eventos

charge.approvedevento
Pagamento aprovado. É o momento de liberar o pedido.
charge.declinedevento
Recusado pela adquirente ou emissor.
charge.canceledevento
Cancelado via API ou na maquininha.
charge.failedevento
Falha técnica ou interrupção. Veja failure_reason.
charge.expiredevento
A maquininha não buscou a cobrança a tempo.
pingevento
Enviado pelo botão de teste do painel.

Requisição

Cabeçalhos
POST /sua/url HTTP/1.1
Content-Type: application/json
User-Agent: Integrator-Webhooks/1.0
X-Integrator-Event: charge.approved
X-Integrator-Delivery: a3b1c0de-1111-4f00-9c3a-5e2b1d0f0a99
X-Integrator-Signature: t=1792000842,v1=5f2b0c…
Corpo
{
  "id": "a3b1c0de-1111-4f00-9c3a-5e2b1d0f0a99",
  "type": "charge.approved",
  "created_at": "2026-10-13T18:20:42.120Z",
  "data": {
    "id": "0b6f0f7e-5a8f-4f1e-9a52-2b7d3c1e9d10",
    "object": "charge",
    "terminal_id": "6f1c2b8e-2f4a-4c55-9a59-6a1d5b1f0c3e",
    "amount": 4590,
    "currency": "BRL",
    "method": "credit",
    "status": "approved",
    "description": "Pedido #1234",
    "external_reference": "pedido-1234",
    "fee": 46,
    "acquirer": null,
    "nsu": "000123456",
    "authorization_code": "A1B2C3",
    "card_brand": "VISA",
    "acquirer_code": "00",
    "failure_reason": null,
    "failure_message": null,
    "created_at": "2026-10-13T18:20:11.204Z",
    "processing_at": "2026-10-13T18:20:11.851Z",
    "completed_at": "2026-10-13T18:20:42.017Z",
    "expires_at": "2026-10-13T18:22:11.204Z"
  }
}

O campo id identifica o evento e é o mesmo em todas as tentativas. Use-o para ignorar entregas repetidas.

Validar a assinatura

O cabeçalho X-Integrator-Signature traz t (Unix timestamp em segundos) e v1, que é o HMAC-SHA256 em hexadecimal de t + "." + corpo_bruto usando o seu segredo. Calcule sobre os bytes exatos recebidos, antes de fazer o parse do JSON, e rejeite timestamps com mais de 5 minutos de diferença.

import crypto from "node:crypto";
import express from "express";

const app = express();

app.post("/webhooks/integrator", express.raw({ type: "application/json" }), (req, res) => {
  const header = req.get("X-Integrator-Signature") ?? "";
  const parts = Object.fromEntries(header.split(",").map((p) => p.split("=")));
  const expected = crypto
    .createHmac("sha256", process.env.INTEGRATOR_WEBHOOK_SECRET)
    .update(`${parts.t}.${req.body}`)
    .digest("hex");

  const valid =
    parts.v1?.length === expected.length &&
    crypto.timingSafeEqual(Buffer.from(parts.v1), Buffer.from(expected));
  const recent = Math.abs(Date.now() / 1000 - Number(parts.t)) < 300;
  if (!valid || !recent) return res.status(400).send("assinatura inválida");

  const event = JSON.parse(req.body.toString("utf8"));
  if (event.type === "charge.approved") {
    marcarPedidoComoPago(event.data.external_reference, event.data.nsu);
  }
  res.sendStatus(200);
});

Como responder

  • Responda 2xx em até 10 segundos. Qualquer outro status, timeout ou erro de conexão conta como falha.
  • Faça o mínimo na requisição (gravar o evento numa fila, por exemplo) e processe depois.
  • Redirecionamentos não são seguidos.

Novas tentativas

Se a entrega falhar, tentamos de novo com espera crescente:

1ª imediato2ª +1 min3ª +5 min4ª +30 min5ª +2 h6ª +6 h7ª +24 h

Depois da sétima falha a entrega fica como failed no painel, de onde você pode reenviar. Eventos podem chegar fora de ordem e mais de uma vez: use o status atual da cobrança, ou consulte GET /v1/charges/{id}, como fonte da verdade.

Sem webhook configurado