class WebhookEventProcessor {
constructor() {
this.handlers = new Map()
this.setupHandlers()
}
setupHandlers() {
// Eventos de transação
this.handlers.set('transaction_created', this.handleTransactionCreated)
this.handlers.set('transaction_paid', this.handleTransactionPaid)
this.handlers.set('transaction_refunded', this.handleTransactionRefunded)
this.handlers.set('transaction_infraction', this.handleTransactionInfraction)
// Eventos de transferência
this.handlers.set('transfer_created', this.handleTransferCreated)
this.handlers.set('transfer_completed', this.handleTransferCompleted)
this.handlers.set('transfer_canceled', this.handleTransferCanceled)
this.handlers.set('transfer_updated', this.handleTransferUpdated)
}
async processEvent(event) {
const handler = this.handlers.get(event.event)
if (!handler) {
console.warn(`⚠️ Evento não tratado: ${event.event}`)
return
}
try {
console.log(`Processando evento: ${event.event}`)
// Verificar se já foi processado (idempotência)
if (await this.isEventProcessed(event.id)) {
console.log(`ℹ️ Evento já processado: ${event.id}`)
return
}
// Processar evento
await handler.call(this, event)
// Marcar como processado
await this.markEventProcessed(event.id)
console.log(`Evento processado: ${event.event} - ${event.id}`)
} catch (error) {
console.error(`❌ Erro ao processar evento ${event.event}:`, error)
// Registrar erro para retry
await this.logEventError(event.id, error)
throw error
}
}
async handleTransactionCreated(event) {
const transaction = event.transaction
// Iniciar timer de expiração
await this.scheduleExpirationCheck(transaction.id, transaction.expiresAt)
// Notificar sistemas internos
await this.notifyTransactionCreated(transaction)
}
async handleTransactionPaid(event) {
const transaction = event.transaction
// Executar ações em paralelo quando possível
await Promise.all([
this.releaseProduct(transaction.id),
this.sendConfirmationEmail(transaction),
this.updateInventory(transaction.items),
this.updateAnalytics('payment_received', transaction)
])
}
async handleTransferCreated(event) {
const transfer = event.transfer
// Registrar transferência criada
await this.logTransferAttempt(transfer)
await this.notifyTransferCreated(transfer)
}
async handleTransferUpdated(event) {
const transfer = event.transfer
// Atualizar status da transferência
await this.updateTransferStatus(transfer.id, transfer.status)
}
async handleTransferCompleted(event) {
const transfer = event.transfer
await Promise.all([
this.updateFinancialRecord(transfer),
this.notifyTransferCompletion(transfer),
this.updateAnalytics('transfer_completed', transfer)
])
}
async handleTransferCanceled(event) {
const transfer = event.transfer
// Reverter e notificar cancelamento
await this.revertTransfer(transfer.id)
await this.notifyTransferCanceled(transfer)
}
// Métodos utilitários
async isEventProcessed(eventId) {
// Verificar se evento já foi processado (implementar conforme sua arquitetura)
return await redis.exists(`processed_event:${eventId}`)
}
async markEventProcessed(eventId) {
// Marcar evento como processado com TTL
await redis.setex(`processed_event:${eventId}`, 86400, 'true') // 24 horas
}
async logEventError(eventId, error) {
// Registrar erro para possível retry
await errorLogger.log({
eventId,
error: error.message,
stack: error.stack,
timestamp: new Date().toISOString()
})
}
}
// Uso no endpoint webhook
const eventProcessor = new WebhookEventProcessor()
app.post('/webhooks/vexy-bank', async (req, res) => {
try {
// Verificar assinatura
if (!verifySignature(req.body, req.headers['vexy-signature'], WEBHOOK_SECRET)) {
return res.status(401).send('Invalid signature')
}
const event = JSON.parse(req.body)
// Processar evento
await eventProcessor.processEvent(event)
res.status(200).send('OK')
} catch (error) {
console.error('Webhook processing error:', error)
res.status(500).send('Internal server error')
}
})