O que são Webhooks?
Webhooks são callbacks HTTP que a KillB envia para seu servidor quando eventos ocorrem. Eles habilitam notificações em tempo real sobre ramps, usuários, contas e transações sem polling constante.
Webhooks são a forma recomendada de monitorar status de transações e manter sua aplicação sincronizada com a KillB.
Como os Webhooks Funcionam
Estrutura do Evento Webhook
Todos os eventos webhook seguem esta estrutura:
{
id : string , // UUID - ID único do evento
event : string , // Tipo de evento: RAMP | USER | TRANSACTION | ACCOUNT | CUSTODIAL_ACCOUNT
action : string , // Ação: CREATE | UPDATE | DELETE
data : object , // Dados específicos do evento
createdAt : string , // Timestamp ISO 8601
updatedAt : string , // Timestamp ISO 8601
attempts : number // Contagem de tentativas de entrega (0 a 5)
}
Eventos de Webhook
A KillB suporta os seguintes tipos de eventos:
Eventos de transação ramp Tipo de Evento: RAMPAções:
CREATE - Novo ramp criado
UPDATE - Status do ramp mudou
DELETE - Ramp cancelado (raro)
Valores de Status Comuns:
QUOTE_CREATED - Cotação criada
PENDING_PAYMENT - Aguardando pagamento
CASH_IN_PROCESSING - Pagamento sendo processado
CASH_IN_COMPLETED - Pagamento confirmado
PROCESSING - Convertendo moeda
CASH_OUT_PROCESSING - Enviando fundos
COMPLETED - Ramp concluído
FAILED - Ramp falhou
CANCELED - Ramp cancelado
Eventos de usuário e KYC Tipo de Evento: USERAções:
CREATE - Novo usuário registrado
UPDATE - Informações do usuário ou status KYC mudou
DELETE - Usuário excluído (raro)
Valores de Status:
PENDING - Aguardando verificação
ACTIVE - Verificado e ativo
REJECTED - KYC rejeitado
SUSPENDED - Conta suspensa
Níveis de Acesso:
L0 - Não verificado
L1 - Verificação básica
L2 - Verificação padrão
L3 - Verificação aprimorada
L4 - Verificação premium
Eventos de verificação de conta Tipo de Evento: ACCOUNTAções:
CREATE - Nova conta adicionada
UPDATE - Status ou detalhes da conta mudaram
DELETE - Conta excluída
Tipos de Conta:
PSE - Conta bancária colombiana
SPEI - Conta bancária mexicana
ACH - Conta bancária dos EUA
WIRE - Conta de transferência wire
WALLET - Carteira cripto
PIX - Conta PIX brasileira
TRANSFIYA - Carteira móvel colombiana
Valores de Status:
PENDING - Aguardando verificação
ACTIVE - Verificado e ativo
REJECTED - Verificação falhou
INACTIVE - Desabilitado
Configurando Webhooks
Criar Configuração de Webhook
Requisição:
{
"url" : "https://api.seuapp.com/webhooks/killb" ,
"secret" : "sua-chave-secreta-min-32-caracteres" ,
"events" : [ "RAMP" , "USER" , "ACCOUNT" ]
}
Resposta:
{
"id" : "webhook-id" ,
"customerId" : "customer-id" ,
"url" : "https://api.seuapp.com/webhooks/killb" ,
"events" : [ "RAMP" , "USER" , "ACCOUNT" ],
"active" : true ,
"createdAt" : "2024-01-15T10:30:00.000Z"
}
const setupWebhook = async () => {
const response = await fetch ( 'https://teste-94u93qnn.uc.gateway.dev/api/v2/webhooks' , {
method: 'POST' ,
headers: {
'Authorization' : `Bearer ${ token } ` ,
'Content-Type' : 'application/json'
},
body: JSON . stringify ({
url: 'https://api.seuapp.com/webhooks/killb' ,
secret: process . env . WEBHOOK_SECRET ,
events: [ 'RAMP' , 'USER' , 'ACCOUNT' , 'TRANSACTION' ]
})
});
return await response . json ();
};
Segurança de Webhook
Verificação de Assinatura
A KillB assina todas as requisições webhook com HMAC SHA-256. Sempre verifique assinaturas para garantir autenticidade.
Cabeçalho: x-signature-sha256
const crypto = require ( 'crypto' );
const express = require ( 'express' );
app . post ( '/webhooks/killb' , express . raw ({ type: 'application/json' }), ( req , res ) => {
const signature = req . headers [ 'x-signature-sha256' ];
const payload = req . body . toString ();
// Calcular assinatura esperada
const expectedSignature = crypto
. createHmac ( 'sha256' , process . env . WEBHOOK_SECRET )
. update ( payload )
. digest ( 'hex' );
// Verificar assinatura
if ( signature !== expectedSignature ) {
console . error ( 'Assinatura de webhook inválida' );
return res . status ( 401 ). json ({ error: 'Assinatura inválida' });
}
// Analisar e processar evento
const event = JSON . parse ( payload );
processWebhookEvent ( event );
res . status ( 200 ). json ({ received: true });
});
Nunca pule a verificação de assinatura! Webhooks não verificados podem ser falsificados por atacantes.
Requisitos de Webhook
Seu endpoint de webhook deve:
Retornar 200 OK dentro de 5 segundos
Processar eventos assincronamente
Não esperar por operações lentas
Usar filas de jobs para processamento pesado
Manter >99% de uptime
Usar apenas HTTPS
Ter certificado SSL válido
Responder com códigos de status adequados
Sempre verificar x-signature-sha256
Usar comparação de tempo constante
Rejeitar assinaturas inválidas
Registrar falhas de verificação
Lógica de Retry
A KillB automaticamente tenta novamente entregas de webhook falhadas com backoff exponencial:
Agenda de Retry:
Tentativa 0: Entrega imediata
Tentativa 1: Após 1 minuto
Tentativa 2: Após 5 minutos
Tentativa 3: Após 15 minutos
Tentativa 4: Após 1 hora
Tentativa 5: Após 6 horas (tentativa final)
Critérios de Falha:
Código de resposta não-2xx
Timeout de conexão (> 5 segundos)
Erro de rede
O campo attempts no payload do webhook indica a tentativa de entrega atual (0 a 5).
Após a tentativa 5 falhar, o webhook é marcado como permanentemente falhado e requer intervenção manual. Verifique seus logs de webhook e corrija quaisquer problemas.
Testando Webhooks
# Instalar ngrok
npm install -g ngrok
# Iniciar seu servidor local
node server.js
# Expor para internet
ngrok http 3000
# Usar URL ngrok na configuração de webhook
# https://abc123.ngrok.io/webhooks/killb
Teste Manual
Acione eventos de teste no sandbox:
// Criar ramp de teste
const ramp = await createRamp ( quoteId , userId , accountId );
// Simular conclusão (aciona webhooks)
await fetch ( '/api/v2/faker/cash-in' , {
method: 'POST' ,
headers: { 'Authorization' : `Bearer ${ token } ` },
body: JSON . stringify ({ rampId: ramp . id })
});
// Seu webhook deve receber evento ramp.cash_in_completed
Processamento de Eventos
Exemplo de Handler
const processWebhookEvent = ( webhookEvent ) => {
const { id , event , action , data , attempts } = webhookEvent ;
console . log ( `Processando ${ event } . ${ action } (tentativa ${ attempts } )` );
// Rotear baseado no tipo de evento e ação
switch ( event ) {
case 'RAMP' :
handleRampEvent ( action , data );
break ;
case 'USER' :
handleUserEvent ( action , data );
break ;
case 'ACCOUNT' :
handleAccountEvent ( action , data );
break ;
case 'TRANSACTION' :
handleTransactionEvent ( action , data );
break ;
default :
console . log ( `Tipo de evento não tratado: ${ event } ` );
}
};
const handleRampEvent = async ( action , data ) => {
if ( action === 'UPDATE' && data . status === 'COMPLETED' ) {
// Atualizar banco de dados
await db . ramps . update ( data . id , {
status: data . status ,
updatedAt: data . updatedAt ,
transferProof: data . transferProof
});
// Notificar usuário
await sendEmail ( data . userId , 'Transação Concluída' , {
amount: data . toAmount ,
currency: data . toCurrency ,
txHash: data . transferProof
});
}
};
Idempotência
Lide com entregas de webhook duplicadas usando o ID do evento:
const handleWebhook = async ( webhookEvent ) => {
const { id , event , action , data } = webhookEvent ;
// Verificar se já processado (usando ID único do evento)
const existing = await db . webhookEvents . findOne ({ eventId: id });
if ( existing ) {
console . log ( `Evento duplicado ${ id } , pulando` );
return { received: true , duplicate: true };
}
// Armazenar evento imediatamente para prevenir processamento duplicado
await db . webhookEvents . create ({
eventId: id ,
eventType: event ,
action: action ,
payload: data ,
receivedAt: new Date (),
processed: false
});
try {
// Processar evento
await processWebhookEvent ( webhookEvent );
// Marcar como processado
await db . webhookEvents . update (
{ eventId: id },
{ processed: true , processedAt: new Date () }
);
return { received: true };
} catch ( error ) {
// Registrar erro mas ainda marcar como recebido
await db . webhookEvents . update (
{ eventId: id },
{ error: error . message , failedAt: new Date () }
);
throw error ;
}
};
Melhores Práticas
app . post ( '/webhooks/killb' , async ( req , res ) => {
// Verificar assinatura
verifySignature ( req );
// Reconhecer imediatamente
res . status ( 200 ). json ({ received: true });
// Processar assincronamente
processAsync ( req . body );
});
Retornar 200 OK imediatamente
Processar eventos em background
Usar filas de jobs (Bull, Celery, etc.)
Não esperar por serviços externos
Lidar com Falhas Graciosamente
Armazenar Histórico de Eventos
await db . webhookLogs . create ({
eventId: event . id ,
eventType: event . event ,
payload: event . data ,
receivedAt: new Date (),
processed: true
});
Manter trilha de auditoria
Habilitar replay se necessário
Depurar problemas facilmente
Atender requisitos de compliance
Próximos Passos