Webhooks
Como o IndicaSaaS recebe os eventos de venda do seu gateway com segurança: assinatura verificada, idempotência, resposta rápida e processamento assíncrono com retentativa.
Endpoints
Dois formatos, conforme o tipo de conexão:
| Conexão | Endpoint | Gateways |
|---|---|---|
| OAuth | POST /webhooks/<gateway> | Stripe, Mercado Pago — endpoint único; o merchant vem no próprio evento |
| API-key | POST /webhooks/<gateway>/<token> | Asaas, AbacatePay — o token na URL identifica o seu SaaS |
Verificação de assinatura
Todo evento é verificado antes de qualquer processamento (HMAC-SHA256 via Web Crypto, comparação em tempo constante). Assinatura inválida → 400.
| Gateway | Header | Como | Secret |
|---|---|---|---|
| Stripe | stripe-signature | HMAC sobre timestamp.corpo; tolerância 10 min (anti-replay) | plataforma |
| Mercado Pago | x-signature + x-request-id | HMAC sobre o manifesto id;request-id;ts; tolerância 5 min | plataforma |
| Asaas | asaas-access-token | token estático comparado em tempo constante | por merchant (gerado no connect) |
| AbacatePay | x-webhook-signature | HMAC sobre o corpo | por merchant (gerado no connect) |
Idempotência
Cada evento é gravado com uma chave única (gateway, id_do_evento). Reentregas do mesmo evento respondem 200 e são descartadas — pode reenviar à vontade sem duplicar comissão.
Resposta rápida + fila
O handler verifica, deduplica e enfileira o evento, respondendo 200 em milissegundos. O trabalho pesado (cálculo de comissão, hidratação, chamadas à API do gateway) roda assíncrono num consumidor de fila — seu gateway nunca espera por isso.
Retentativa & DLQ
Se o processamento falhar, a mensagem é retentada (até 5×). Esgotadas as tentativas, vai para uma dead-letter queue para inspeção. Como o pipeline é idempotente, retentar é seguro.
Respostas HTTP (para depurar)
| Status | Corpo | Significado |
|---|---|---|
| 200 | { ok: true } | aceito e enfileirado |
| 200 | { duplicate: true } | evento já visto (idempotente) |
| 200 | { ignored: true } | tipo de evento não rastreado |
| 200 | { unresolved: true } | merchant não encontrado — gravado p/ inspeção, sem disparar retry |
| 400 | missing_token | endpoint API-key sem token na URL |
| 400 | invalid_signature | assinatura não confere |
| 404 | unknown_merchant | token não corresponde a nenhuma conexão |
| 500 | missing_platform_secret | secret de plataforma ausente (Stripe/MP) |