Webhooks
Receba notificações em tempo real sobre eventos na sua conta Kirvex.
Como funciona
Quando eventos acontecem na sua conta (pagamento confirmado, reembolso processado, etc.), a Kirvex envia um POST HTTP para as URLs que você configurar.
Eventos disponíveis
| Evento | Descrição |
|---|---|
charge.succeeded | Pagamento confirmado pelo adquirente |
charge.failed | Pagamento recusado |
charge.refunded | Reembolso total processado |
charge.disputed | Contestação/chargeback aberto ou atualizado |
payout.succeeded | Saque creditado na conta destino |
payout.failed | Saque falhou |
Configuração
Via SDK (TypeScript)
const ep = await ap.webhookEndpoints.create({
url: 'https://seuapp.com/webhooks/kirvex',
event_types: ['charge.succeeded', 'charge.failed', 'charge.refunded'],
});
// Guarde o secret — ele só é mostrado uma vez
console.log(ep.secret); // whsec_...
Via SDK (PHP)
$ep = $ap->webhookEndpoints->create([
'url' => 'https://seuapp.com/webhooks/kirvex',
'event_types' => ['charge.succeeded', 'charge.failed'],
]);
Via SDK (Python)
ep = ap.webhook_endpoints.create({
"url": "https://seuapp.com/webhooks/kirvex",
"event_types": ["charge.succeeded", "charge.failed"],
})
Verificação de assinatura
Toda requisição webhook inclui o header X-Kirvex-Signature no formato:
t=1714432800,v1=5d41402abc4b2a76b9719d911017c592
t— timestamp Unix (segundos) do enviov1— HMAC-SHA256 de{timestamp}.{body}com o secret do endpoint
TypeScript
import { createHmac, timingSafeEqual } from 'node:crypto';
function verifyWebhook(rawBody: string, sigHeader: string, secret: string): boolean {
const parts = Object.fromEntries(
sigHeader.split(',').map(p => p.split('=', 2) as [string, string])
);
const expected = createHmac('sha256', secret)
.update(`${parts.t}.${rawBody}`)
.digest('hex');
return timingSafeEqual(Buffer.from(expected), Buffer.from(parts.v1));
}
PHP
use Kirvex\Sdk\WebhookHelper;
$event = WebhookHelper::verify(
file_get_contents('php://input'),
$_SERVER['HTTP_X_KIRVEX_SIGNATURE'],
'whsec_...'
);
Python
from kirvex import verify_webhook
event = verify_webhook(
raw_body=request.body,
signature=request.headers["x-kirvex-signature"],
secret="whsec_...",
)
Retries
Se o seu endpoint retornar status ≥ 500 ou não responder em 5s, a Kirvex reenvia com backoff exponencial:
| Tentativa | Delay |
|---|---|
| 1 | Imediata |
| 2 | 5 minutos |
| 3 | 30 minutos |
| 4 | 2 horas |
| 5 | 12 horas |
Após 5 falhas, o evento vai para a DLQ (dead letter queue) e um alerta é emitido no dashboard.
Idempotência
Cada evento tem um id único. Seu handler deve ser idempotente — o mesmo evento pode ser entregue mais de uma vez (retry ou replay manual).
// Exemplo: use o event.id como chave de idempotência
const alreadyProcessed = await db.processedEvents.findUnique({ where: { id: event.id } });
if (alreadyProcessed) return res.status(200).end();
Payload
{
"id": "evt_01J...",
"type": "charge.succeeded",
"created": "2026-04-29T12:00:00.000Z",
"livemode": true,
"data": {
"charge_id": "ch_01J...",
"status": "succeeded",
"amount": { "amount": "9990", "currency": "BRL" },
"vendor": "woovi",
"vendor_ref": "..."
}
}
livemode diz de qual modo é o evento: false para uma cobrança criada com
uma chave sk_test_ (sandbox, sem dinheiro real), true para uma cobrança
real. Os mesmos endpoints recebem os dois — filtre por livemode antes de
liberar um pedido. Eventos de saque são sempre livemode: true.
Test Send
Envie um evento de teste para validar sua integração:
await ap.webhookEndpoints.testSend('we_...');