Pular para o conteúdo principal

Visão Geral

Os webhooks de PIX Cash Out notificam seu sistema em tempo real sobre o status das transferências PIX realizadas, permitindo que você acompanhe e processe as operações de saque com eficiência.
Os webhooks de cash out seguem o mesmo padrão de segurança dos webhooks de cash in.

Status dos Webhooks

WITHDRAW_REQUEST

Transferência PIX foi iniciada e está sendo processada pelo provedor.

WITHDRAW_PAID

Transferência PIX foi executada com sucesso. Valor debitado da conta.

REFUND_IN

Estorno de PIX enviado. Valor previamente cobrado foi devolvido para conta de origem.

ERROR

Transferência PIX falhou durante o processamento. Valor não foi debitado.

Payload dos Webhooks

Transferência Concluída (Status: WITHDRAW_PAID)

{
  "transactionId": "withdraw-abc123-def456-ghi789",
  "businessTransactionId": "transfer-789012",
  "status": "WITHDRAW_PAID",
  "value": 25050,
  "movementType": "DEBIT",
  "endToEndId": "E00416968202401151530150123456789",
  "pixKey": "[email protected]",
  "createdDate": "2024-01-15T15:30:15.123Z",
  "ReceiverBankAccount": "123456",
  "ReceiverToBankAccountDigit": "7",
  "ReceiverBankBranch": "0001",
  "ReceiverBankCode": "077",
  "ReceiverDocumentNumber": "12345678901",
  "ReceiverBankName": "BANCO INTER S.A.",
  "ReceiverBankISPB": "00416968",
  "ReceiverName": "ANA SILVA SANTOS",
  "PayerBankAccount": "567890",
  "PayerBankAccountDigit": "1",
  "PayerBankBranch": "0001",
  "PayerBankCode": "077",
  "PayerDocumentNumber": "45438750000188",
  "PayerBankName": "BANCO INTER S.A.",
  "PayerBankISPB": "00416968",
  "PayerName": "MINHA EMPRESA LTDA",
  "VoucherUrl": "https://voucher.firebanking.com/withdraw/withdraw-abc123-def456-ghi789.pdf"
}

Transferência com Erro (Status: ERROR)

{
  "transactionId": "error-withdraw-123456789",
  "businessTransactionId": "transfer-erro-789012",
  "status": "ERROR",
  "value": 25050,
  "movementType": null,
  "endToEndId": null,
  "pixKey": "[email protected]",
  "createdDate": "2024-01-15T15:30:15.123Z",
  "ReceiverBankAccount": null,
  "ReceiverToBankAccountDigit": null,
  "ReceiverBankBranch": null,
  "ReceiverBankCode": null,
  "ReceiverDocumentNumber": null,
  "ReceiverBankName": null,
  "ReceiverBankISPB": null,
  "ReceiverName": null,
  "PayerBankAccount": null,
  "PayerBankAccountDigit": null,
  "PayerBankBranch": null,
  "PayerBankCode": null,
  "PayerDocumentNumber": null,
  "PayerBankName": null,
  "PayerBankISPB": null,
  "PayerName": null,
  "VoucherUrl": null
}

Estorno Enviado (Status: REFUND_IN)

{
  "transactionId": "8f7e6d5c-4b3a-2918-7654-3210fedcba98",
  "businessTransactionId": "estorno-pedido-12345",
  "status": "REFUND_IN",
  "value": 15000,
  "movementType": "DEBIT",
  "endToEndId": "E00000000202401151700300987654321",
  "pixKey": "[email protected]",
  "createdDate": "2024-01-15T17:00:30.123Z",
  "ReceiverBankAccount": "123456",
  "ReceiverToBankAccountDigit": "7",
  "ReceiverBankBranch": "0001",
  "ReceiverBankCode": "001",
  "ReceiverDocumentNumber": "12345678901",
  "ReceiverBankName": "BANCO DO BRASIL S.A.",
  "ReceiverBankISPB": "00000000",
  "ReceiverName": "JOAO COSTA SILVA",
  "PayerBankAccount": "567890",
  "PayerBankAccountDigit": "1",
  "PayerBankBranch": "0001",
  "PayerBankCode": "077",
  "PayerDocumentNumber": "45438750000188",
  "PayerBankName": "BANCO INTER S.A.",
  "PayerBankISPB": "00416968",
  "PayerName": "MINHA EMPRESA LTDA",
  "VoucherUrl": "https://voucher.firebanking.com/refund/8f7e6d5c-4b3a-2918-7654-3210fedcba98.pdf"
}

Transferência Solicitada (Status: WITHDRAW_REQUEST)

{
  "transactionId": "pending-qr-789012345",
  "businessTransactionId": "qr-transfer-001",
  "status": "WITHDRAW_REQUEST",
  "value": 15000,
  "movementType": "DEBIT",
  "endToEndId": null,
  "pixKey": "00020101021226830014br.gov.bcb.pix2584",
  "createdDate": "2024-01-15T16:45:05.123Z",
  "ReceiverBankAccount": "456789",
  "ReceiverToBankAccountDigit": "0",
  "ReceiverBankBranch": "0001",
  "ReceiverBankCode": "001",
  "ReceiverDocumentNumber": "98765432100",
  "ReceiverBankName": "BANCO DO BRASIL S.A.",
  "ReceiverBankISPB": "00000000",
  "ReceiverName": "MARIA SANTOS",
  "PayerBankAccount": "567890",
  "PayerBankAccountDigit": "1",
  "PayerBankBranch": "0001",
  "PayerBankCode": "077",
  "PayerDocumentNumber": "45438750000188",
  "PayerBankName": "BANCO INTER S.A.",
  "PayerBankISPB": "00416968",
  "PayerName": "MINHA EMPRESA LTDA",
  "VoucherUrl": null
}

Implementação do Webhook

app.post('/webhooks/pix-cashout', (req, res) => {
  const signature = req.headers['x-firebanking-signature'];
  const payload = req.body;
  
  if (!verifySignature(payload, signature)) {
    return res.status(401).send('Unauthorized');
  }
  
  const webhook = JSON.parse(payload);
  
  switch (webhook.operation.status) {
    case 'WITHDRAW_REQUEST':
      handleWithdrawRequested(webhook);
      break;

    case 'WITHDRAW_PAID':
      handleWithdrawCompleted(webhook);
      break;

    case 'REFUND_IN':
      handleRefundSent(webhook);
      break;

    case 'ERROR':
      handleWithdrawFailed(webhook);
      break;
  }
  
  res.status(200).send('OK');
});

function handleWithdrawCompleted(transaction) {
  const valueBrl = transaction.operation.value / 100;
  const feeBrl = transaction.operation.fee / 100;
  const resultBrl = transaction.operation.result / 100;

  console.log(`Transferência PIX concluída: ${transaction.operation.externalID}`);
  console.log(`Beneficiário: ${transaction.receiver.fullName}`);
  console.log(`Valor: R$ ${valueBrl} (Taxa: R$ ${feeBrl}, Líquido: R$ ${resultBrl})`);
  console.log(`End-to-End ID: ${transaction.operation.additionalInfo.endToEndId}`);

  // Atualizar sistema interno
  updateTransferStatus(transaction.operation.externalID, 'completed');

  // Notificar usuário
  sendNotification({
    type: 'transfer_success',
    message: `Transferência PIX de R$ ${valueBrl} realizada com sucesso`,
    transaction_id: transaction.operation.externalID,
    endToEndId: transaction.operation.additionalInfo.endToEndId
  });
}

function handleWithdrawFailed(transaction) {
  const error = transaction.operation.error?.[0];
  const amountReturned = transaction.operation.additionalInfo?.amountReturned;

  console.log(`Transferência PIX falhou: ${transaction.operation.externalID}`);
  console.log(`Código do erro: ${error?.code}`);
  console.log(`Mensagem: ${error?.message}`);
  console.log(`Valor retornado: ${amountReturned ? 'Sim' : 'Não'}`);

  // Atualizar sistema interno
  updateTransferStatus(transaction.operation.externalID, 'failed', error);

  // Notificar usuário do erro
  sendNotification({
    type: 'transfer_error',
    message: `Erro na transferência PIX: ${error?.message}`,
    transaction_id: transaction.operation.externalID,
    error_code: error?.code,
    amount_returned: amountReturned
  });

  // Log para auditoria
  logTransferFailure({
    transactionId: transaction._id,
    businessId: transaction.businessId,
    errorCode: error?.code,
    errorMessage: error?.message
  });
}

Categorias de Erro

VALIDATION_ERROR

  • Descrição: Erro de validação (chave PIX inválida, dados incorretos)
  • Ação: Corrigir dados e tentar novamente
  • Valor retornado: Sim

INSUFFICIENT_BALANCE

  • Descrição: Saldo insuficiente na conta
  • Ação: Adicionar saldo e tentar novamente
  • Valor retornado: Sim

LIMIT_EXCEEDED

  • Descrição: Limite de transferência excedido
  • Ação: Aguardar próximo período ou ajustar limites
  • Valor retornado: Sim

TEMPORARY_ERROR

  • Descrição: Erro temporário do sistema PIX
  • Ação: Tentar novamente em alguns minutos
  • Valor retornado: Sim

CRITICAL_ERROR

  • Descrição: Erro crítico que requer intervenção manual
  • Ação: Contatar suporte
  • Valor retornado: Varia

Monitoramento de Transferências

Métricas Importantes

// Acompanhar performance das transferências
function trackWithdrawMetrics(data) {
  const metrics = {
    // Taxa de sucesso
    success_rate: calculateSuccessRate(),
    
    // Tempo médio de processamento
    avg_processing_time: calculateAvgProcessingTime(data),
    
    // Bancos mais utilizados
    top_banks: getTopBanks(data.beneficiary.bank),
    
    // Valores médios
    avg_amount: calculateAvgAmount(data.transaction.amount),
    
    // Tipos de erro mais comuns
    common_errors: getCommonErrors()
  };
  
  sendMetrics(metrics);
}

Dashboard de Transferências

  • Volume diário: Total de transferências por dia
  • Taxa de sucesso: % de transferências bem-sucedidas
  • Tempo médio: Tempo de processamento
  • Principais erros: Tipos de erro mais frequentes
  • Distribuição por banco: Transferências por instituição

Casos de Uso

Marketplace - Pagamento de Vendedores

function handleWithdrawCompleted(data) {
  const vendorId = extractVendorId(data.metadata);
  const amount = data.transaction.amount;
  
  // Atualizar saldo do vendedor
  updateVendorBalance(vendorId, amount);
  
  // Notificar vendedor
  notifyVendor(vendorId, {
    type: 'payment_received',
    amount: amount,
    transaction_id: data.transaction.id
  });
  
  // Registrar para relatórios
  recordVendorPayment(vendorId, amount, data.transaction.processed_at);
}

Sistema de Folha de Pagamento

function handleWithdrawCompleted(data) {
  const employeeId = data.metadata.employee_id;
  const salaryAmount = data.transaction.amount;
  
  // Marcar salário como pago
  markSalaryAsPaid(employeeId, data.transaction.external_id);
  
  // Gerar comprovante de pagamento
  generateSalaryReceipt(employeeId, {
    amount: salaryAmount,
    date: data.transaction.processed_at,
    reference: data.transaction.external_id
  });
  
  // Enviar por email
  emailSalaryReceipt(employeeId);
}

Plataforma de Serviços

function handleWithdrawCompleted(data) {
  const serviceId = data.metadata.service_id;
  const providerId = data.metadata.provider_id;
  
  // Marcar serviço como pago
  updateServiceStatus(serviceId, 'paid');
  
  // Notificar prestador de serviço
  notifyServiceProvider(providerId, {
    service_id: serviceId,
    payment_amount: data.transaction.amount,
    payment_date: data.transaction.processed_at
  });
  
  // Atualizar rating/reputação
  updateProviderRating(providerId, serviceId);
}

Recuperação de Falhas

Retry Automático

function handleWithdrawFailed(data) {
  const error = data.error;
  const transactionId = data.transaction.external_id;
  
  // Determinar se deve tentar novamente
  if (shouldRetry(error.category)) {
    const retryDelay = calculateRetryDelay(error.category);
    
    setTimeout(() => {
      retryTransfer(transactionId, {
        reason: error.message,
        attempt: getAttemptCount(transactionId) + 1
      });
    }, retryDelay);
  } else {
    // Erro permanente - notificar usuário
    notifyPermanentFailure(transactionId, error);
  }
}

function shouldRetry(errorCategory) {
  const retryableErrors = [
    'TEMPORARY_ERROR',
    'SYSTEM_UNAVAILABLE',
    'TIMEOUT_ERROR'
  ];
  return retryableErrors.includes(errorCategory);
}

Boas Práticas

Tratamento de Erros

  • Classifique erros: Separe erros temporários de permanentes
  • Retry inteligente: Apenas para erros recuperáveis
  • Alerta proativo: Monitore taxa de erro
  • Backup manual: Tenha processo manual para casos críticos

Auditoria e Compliance

  • Log completo: Registre todas as transferências
  • Rastrebilidade: Mantenha End-to-End ID
  • Relatórios: Gere relatórios regulares
  • Conciliação: Compare com extratos bancários

Performance

  • Processamento assíncrono: Não bloqueie o webhook
  • Filas de trabalho: Use filas para tarefas pesadas
  • Cache de dados: Cache informações frequentes
  • Monitoramento: Acompanhe tempo de resposta

Próximos Passos