Skip to main content
Esta página detalha todos os eventos disponíveis na API Avantti Finance que podem disparar webhooks, incluindo estrutura de dados e quando cada evento é enviado.

🔹 Eventos de Transação (PIX IN)

transaction_created

Quando é enviado: QR Code PIX criado com sucesso
{
  "id": "wh_64f8a2b1c3d4e5f6g7h8i9j0",
  "type": "transaction",
  "event": "transaction_created",
  "scope": "user",
  "transaction": {
    "id": "clm8x9y0z1234567890abcdef",
    "amount": 29990,
    "status": "pending",
    "description": "Assinatura Premium",
    "customer": {
      "name": "Maria Silva Santos",
      "email": "[email protected]",
      "document": "12345678901"
    },
    "createdAt": "2024-01-20T10:30:00.000Z",
    "expiresAt": "2024-01-20T22:30:00.000Z"
  }
}
Uso típico: Confirmar que QR Code foi gerado, iniciar timer de expiração

transaction_paid

Quando é enviado: PIX recebido e confirmado pelo sistema
{
  "id": "wh_64f8a2b1c3d4e5f6g7h8i9j0",
  "type": "transaction",
  "event": "transaction_paid",
  "scope": "user",
  "transaction": {
    "id": "clm8x9y0z1234567890abcdef",
    "amount": 29990,
    "status": "paid",
    "netAmount": 29891,
    "fees": 99,
    "pix": {
      "endToEndId": "E12345678202412011030567890AB123C",
      "payerInfo": {
        "name": "Maria Silva Santos",
        "document": "12345678901"
      }
    },
    "paidAt": "2024-01-20T10:35:22.000Z"
  }
}
Uso típico: Liberar produto/serviço, enviar email de confirmação, atualizar estoque

transaction_refunded

Quando é enviado: Estorno processado com sucesso
{
  "id": "wh_64f8a2b1c3d4e5f6g7h8i9j0",
  "type": "transaction",
  "event": "transaction_refunded",
  "scope": "user",
  "transaction": {
    "id": "clm8x9y0z1234567890abcdef",
    "amount": 29990,
    "status": "refunded",
    "refund": {
      "amount": 29990,
      "reason": "Solicitação do cliente",
      "refundedAt": "2024-01-20T14:20:00.000Z"
    }
  }
}
Uso típico: Cancelar entrega, notificar cliente, restaurar estoque

transaction_infraction

Quando é enviado: Problemas ou infrações detectadas pelo Banco Central
{
  "id": "wh_64f8a2b1c3d4e5f6g7h8i9j0",
  "type": "transaction",
  "event": "transaction_infraction",
  "scope": "user",
  "transaction": {
    "id": "clm8x9y0z1234567890abcdef",
    "amount": 29990,
    "status": "infraction",
    "infraction": {
      "type": "fraud_suspected",
      "description": "Suspeita de fraude detectada",
      "reportedAt": "2024-01-20T15:10:00.000Z"
    }
  }
}
Uso típico: Pausar entrega, revisar transação, contatar suporte

🔹 Eventos de Transferência (PIX OUT)

transfer_created

Quando é enviado: Transferência PIX iniciada
{
  "id": "wh_75g9b3c2d4e5f6g7h8i9j0k1",
  "type": "transfer",
  "event": "transfer_created",
  "scope": "user",
  "transfer": {
    "id": "cln1a2b3c4567890defghijk",
    "amount": 150000,
    "status": "created",
    "pixKey": "[email protected]",
    "description": "Pagamento fornecedor NF 12345",
    "createdAt": "2024-01-20T15:30:00.000Z"
  }
}
Uso típico: Registrar tentativa de transferência, notificar solicitante

transfer_completed

Quando é enviado: Transferência PIX concluída com sucesso
{
  "id": "wh_75g9b3c2d4e5f6g7h8i9j0k1",
  "type": "transfer",
  "event": "transfer_completed",
  "scope": "user",
  "transfer": {
    "id": "cln1a2b3c4567890defghijk",
    "amount": 150000,
    "status": "completed",
    "netAmount": 149700,
    "fees": 300,
    "pix": {
      "endToEndId": "E87654321202412011145543210ZY987X",
      "creditorAccount": {
        "bank": "341",
        "branch": "1234",
        "account": "567890"
      }
    },
    "completedAt": "2024-01-20T15:30:08.000Z"
  }
}
Uso típico: Confirmar pagamento, atualizar sistema financeiro, notificar destinatário

transfer_canceled

Quando é enviado: Transferência PIX cancelada
{
  "id": "wh_75g9b3c2d4e5f6g7h8i9j0k1",
  "type": "transfer",
  "event": "transfer_canceled",
  "scope": "user",
  "transfer": {
    "id": "cln1a2b3c4567890defghijk",
    "amount": 150000,
    "status": "canceled",
    "cancellation": {
      "reason": "Chave PIX inválida",
      "canceledAt": "2024-01-20T15:30:05.000Z"
    }
  }
}
Uso típico: Reverter operação, notificar erro, tentar novamente se apropriado

transfer_updated

Quando é enviado: Status da transferência foi atualizado
{
  "id": "wh_75g9b3c2d4e5f6g7h8i9j0k1",
  "type": "transfer",
  "event": "transfer_updated",
  "scope": "user",
  "transfer": {
    "id": "cln1a2b3c4567890defghijk",
    "amount": 150000,
    "status": "processing",
    "updatedAt": "2024-01-20T15:30:03.000Z",
    "previousStatus": "created"
  }
}
Uso típico: Atualizar status em tempo real, mostrar progresso ao usuário

🔄 Implementação por Evento

Processamento de Pagamentos Recebidos

function handleTransactionPaid(transaction) {
  console.log(`💰 Pagamento recebido: R$ ${transaction.amount / 100}`)
  
  // 1. Liberar produto/serviço
  await releaseProduct(transaction.id)
  
  // 2. Enviar email de confirmação
  await sendConfirmationEmail(transaction.customer.email, {
    amount: transaction.amount,
    transactionId: transaction.id,
    paidAt: transaction.paidAt
  })
  
  // 3. Atualizar sistema interno
  await updateInternalStatus(transaction.id, 'paid')
  
  // 4. Atualizar estoque (se aplicável)
  if (transaction.items) {
    await updateInventory(transaction.items)
  }
  
  console.log(`✅ Processamento concluído para transação ${transaction.id}`)
}

Processamento de Transferências

function handleTransferCompleted(transfer) {
  console.log(`🚀 Transferência concluída: R$ ${transfer.amount / 100}`)
  
  // 1. Atualizar registro financeiro
  await updateFinancialRecord(transfer.id, {
    status: 'completed',
    endToEndId: transfer.pix.endToEndId,
    completedAt: transfer.completedAt,
    fees: transfer.fees
  })
  
  // 2. Notificar solicitante
  await notifyRequester(transfer.id, 'completed')
  
  // 3. Registrar para auditoria
  await logTransferCompletion(transfer)
  
  console.log(`✅ Transferência ${transfer.id} processada`)
}

Tratamento de Estornos

function handleTransactionRefunded(transaction) {
  console.log(`↩️ Estorno processado: R$ ${transaction.refund.amount / 100}`)
  
  // 1. Cancelar entrega se ainda não enviada
  await cancelDeliveryIfPending(transaction.id)
  
  // 2. Restaurar estoque
  if (transaction.items) {
    await restoreInventory(transaction.items)
  }
  
  // 3. Notificar cliente
  await sendRefundNotification(transaction.customer.email, {
    refundAmount: transaction.refund.amount,
    reason: transaction.refund.reason,
    refundedAt: transaction.refund.refundedAt
  })
  
  // 4. Atualizar sistemas internos
  await updateInternalStatus(transaction.id, 'refunded')
  
  console.log(`✅ Estorno ${transaction.id} processado`)
}

📊 Implementação Robusta

Processador de Eventos Centralizado

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 handleTransferCompleted(event) {
    const transfer = event.transfer
    
    await Promise.all([
      this.updateFinancialRecord(transfer),
      this.notifyTransferCompletion(transfer),
      this.updateAnalytics('transfer_completed', 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/avantti-pagamentos', async (req, res) => {
  try {
    // Verificar assinatura
    if (!verifySignature(req.body, req.headers['avantti-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')
  }
})

🛡️ Boas Práticas

1. Idempotência

// Sempre verificar se evento já foi processado
async function processEventIdempotent(event) {
  const eventKey = `event_${event.id}`
  
  if (await cache.get(eventKey)) {
    console.log('Evento já processado')
    return
  }
  
  try {
    await processEvent(event)
    await cache.set(eventKey, 'processed', 86400) // 24h TTL
  } catch (error) {
    // Não marcar como processado em caso de erro
    throw error
  }
}

2. Retry e Recuperação

async function processEventWithRetry(event, maxRetries = 3) {
  for (let attempt = 1; attempt <= maxRetries; attempt++) {
    try {
      await processEvent(event)
      return
    } catch (error) {
      console.error(`Tentativa ${attempt} falhou:`, error.message)
      
      if (attempt === maxRetries) {
        // Enviar para dead letter queue
        await deadLetterQueue.send(event)
        throw error
      }
      
      // Aguardar antes de tentar novamente
      await new Promise(resolve => setTimeout(resolve, 1000 * attempt))
    }
  }
}

3. Monitoramento

class WebhookMonitor {
  constructor() {
    this.metrics = {
      eventsReceived: 0,
      eventsProcessed: 0,
      eventsFailed: 0,
      processingTimes: []
    }
  }

  async recordEventProcessing(eventType, processingTime, success) {
    this.metrics.eventsReceived++
    
    if (success) {
      this.metrics.eventsProcessed++
    } else {
      this.metrics.eventsFailed++
    }
    
    this.metrics.processingTimes.push(processingTime)
    
    // Enviar métricas para sistema de monitoramento
    await this.sendMetrics(eventType, {
      processingTime,
      success,
      timestamp: Date.now()
    })
  }

  getStatistics() {
    const avgProcessingTime = this.metrics.processingTimes.reduce((a, b) => a + b, 0) / this.metrics.processingTimes.length
    
    return {
      eventsReceived: this.metrics.eventsReceived,
      eventsProcessed: this.metrics.eventsProcessed,
      eventsFailed: this.metrics.eventsFailed,
      successRate: (this.metrics.eventsProcessed / this.metrics.eventsReceived) * 100,
      averageProcessingTime: avgProcessingTime
    }
  }
}

🎯 Próximos Passos