Pular para o conteúdo

Webhooks

Um webhook é uma requisição HTTP que o Invoise envia ao seu servidor quando um pagamento muda. Você pode começar com verificações de status por GET e adicionar webhooks quando precisar de atualizações automáticas.

Janela do terminal
curl -X POST 'https://platform.invoise.me/api/v1/shops/{shop_id}/webhooks' \
-H 'Authorization: Bearer ivk_...' \
-H 'Idempotency-Key: <saved-unique-key>' \
-H 'Content-Type: application/json' \
-d '{"url":"https://example.com/invoise","filters":["transfer","invoice","payout"]}'

Use sua própria URL HTTPS pública. Endereços locais e privados são rejeitados. Salve o secret retornado quando o endpoint é criado: você precisa dele para verificar as requisições.

Uma loja pode ter até 10 endpoints. A URL de um endpoint pode ser alterada, então reutilize um em vez de adicionar outro. Veja Limites.

Evento Significado
transfer.observed Uma transferência foi vista; ainda não está confirmada.
transfer.confirmed Uma transferência recebida tem confirmações suficientes. Pode ser apenas um pagamento parcial da fatura.
transfer.reverted Um evento de transferência anterior foi revertido.
invoice.closed A fatura se fechou na blockchain.
invoice.cancelled A página de pagamento foi cancelada ou a fatura expirou; isso não é um reembolso.
payout.sent O repasse foi transmitido ou sua transferência de saída foi vista.
payout.confirmed A contabilização completa da liquidação está disponível.
payout.reverted Uma confirmação de repasse anterior foi revertida.
payout.failed Uma falha terminal de repasse precisa de intervenção.

Para invoice.cancelled, data.reason diz o motivo: expired quando a vida útil da fatura terminou, merchant_blocked quando a equipe do Invoise bloqueou o lojista. Um cancelamento pelo lojista não tem reason.

Assine por grupo: transfer, invoice, payout. Transferências recebidas e repasses de saída são eventos diferentes. Um atraso temporário de RPC ou gas não é payout.failed.

gas.wait e sweep.failed são aceitos como filtros, mas o Invoise não os envia ao seu endpoint. São sinais internos: gas.wait significa que um repasse está aguardando fundos de taxa de rede e continua por conta própria; sweep.failed significa que uma tentativa de mover os fundos ao seu destinatário falhou. Você não precisa agir em nenhum dos dois casos. Um repasse que não consegue se completar chega até você como payout.failed.

Em uma loja sandbox você pode simular os dois por POST /shops/{shop_id}/sandbox/simulate para verificar que um repasse atrasado não quebra seu fluxo. Eles alteram apenas o estado do repasse e não enviam webhook.

Invoise-Signature tem o formato t=<timestamp>,v1=<hex>. A assinatura é HMAC-SHA256 de timestamp + "." + raw_body, usando o segredo do seu webhook.

O exemplo em Node.js a seguir também rejeita timestamps com mais de cinco minutos de diferença do relógio do servidor. Mantenha esse relógio sincronizado e escolha sua tolerância deliberadamente.

import { createHmac, timingSafeEqual } from 'node:crypto';
export function verify(rawBody, header, secret) {
const match = /^t=(\d+),v1=([a-f0-9]{64})$/.exec(header ?? '');
if (!match) return false;
const [, timestamp, signature] = match;
const age = Math.abs(Date.now() / 1000 - Number(timestamp));
if (!Number.isFinite(age) || age > 300) return false;
const expected = createHmac('sha256', secret)
.update(timestamp + '.')
.update(rawBody)
.digest();
return timingSafeEqual(Buffer.from(signature, 'hex'), expected);
}

Passe os bytes originais da requisição como rawBody. Não faça parse do JSON e stringify antes de verificar a assinatura. Rejeite assinaturas inválidas antes de armazenar ou agir sobre o conteúdo.

Um payload resumido se parece com isto:

{
"id": "<event-id>",
"type": "transfer.confirmed",
"created_at": "2026-09-19T10:00:00Z",
"data": {
"issuance_id": "<issuance-id>",
"shop_id": "<shop-id>",
"external_id": "order-123",
"type": "invoice",
"invoice_cancelled": false
}
}
Caso Tratamento
Duplicatas Deduplique usando Invoise-Event-ID, que corresponde ao id do envelope. Salve o ID e a atualização de negócio atomicamente.
Aceitar um evento Salve eventos aceitos de forma durável, retorne 2xx prontamente e processe o trabalho enfileirado depois.
Repetições e reenvios Repetições e reenvios manuais reutilizam o ID do evento. Invoise-Attempt-ID muda e não é uma chave de deduplicação.
Ordem dos eventos Os eventos podem chegar fora de ordem. Use seu conteúdo e reconcilie com o status atual quando necessário.

Para reversões, chain_event.reference_event_id aponta para o evento anterior na blockchain. Os payloads de repasse incluem um payout_id estável, hash de transação, valores e detalhes de taxa; valores desconhecidos são null. A lista de IDs de transferências recebidas deles é limitada a 100 e sinaliza truncamento.

Eventos armazenados antigos podem usar project_id; leia shop_id primeiro e use-o como alternativa se necessário. Reenvios mantêm os bytes originais.

Liste com GET /api/v1/shops/{shop_id}/deliveries. Reenvie uma entrega com POST /api/v1/shops/{shop_id}/deliveries/{id}/replay e uma chave de idempotência salva. Reenviar não cria um novo evento de negócio.