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.
1. Registre seu endpoint
Seção intitulada “1. Registre seu endpoint”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.
2. Escolha os eventos que você precisa
Seção intitulada “2. Escolha os eventos que você precisa”| 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.
3. Verifique antes de processar
Seção intitulada “3. Verifique antes de processar”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.
4. Processe uma vez
Seção intitulada “4. Processe uma vez”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.
Inspecione entregas com falha
Seção intitulada “Inspecione entregas com falha”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.