Skip to main content
Esta página mostra como criar e configurar novos webhooks para receber notificações automáticas sobre eventos em sua conta.

Endpoint

POST /api/v1/webhook

Cria um novo webhook para receber notificações automáticas. URL Completa:
  • Produção: https://api.vexybank.com/api/v1/webhook

Headers obrigatórios

Content-Type: application/json
Authorization: Bearer <seu_token_bearer>

Parâmetros do corpo

Campos Obrigatórios

CampoTipoDescrição
urlstringURL do endpoint que receberá os webhooks
eventsarrayLista de eventos que dispararão o webhook

Campos Opcionais

CampoTipoDescriçãoPadrão
namestringNome descritivo do webhook"Webhook"
isActivebooleanSe o webhook deve estar ativotrue

Eventos disponíveis

Eventos de Transação (PIX IN)

EventoDescrição
transaction_createdQR Code criado com sucesso
transaction_paidPIX recebido e confirmado
transaction_refundedEstorno processado
transaction_infractionProblemas detectados pelo BC

Eventos de Transferência (PIX OUT)

EventoDescrição
transfer_createdTransferência iniciada
transfer_completedPIX enviado com sucesso
transfer_canceledTransferência cancelada
transfer_updatedStatus alterado

Exemplos práticos

Exemplo Básico - Todos os Eventos

curl -X POST 'https://api.vexybank.com/api/v1/webhook' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...' \
  -d '{
    "url": "https://minhaempresa.com/api/webhooks/vexy-bank",
    "name": "Webhook Principal",
    "isActive": true,
    "events": [
      "transaction_created",
      "transaction_paid",
      "transaction_refunded",
      "transfer_created",
      "transfer_completed",
      "transfer_canceled"
    ]
  }'

Exemplo Específico - Apenas Pagamentos

curl -X POST 'https://api.vexybank.com/api/v1/webhook' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...' \
  -d '{
    "url": "https://minhaloja.com/webhooks/pagamentos",
    "name": "Webhook E-commerce - Pagamentos",
    "isActive": true,
    "events": [
      "transaction_paid",
      "transaction_refunded"
    ]
  }'

Exemplo Desenvolvimento - Ambiente de Teste

curl -X POST 'https://api.vexybank.com/api/v1/webhook' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...' \
  -d '{
    "url": "https://ngrok-tunnel.ngrok.io/webhooks/test",
    "name": "Webhook Desenvolvimento",
    "isActive": false,
    "events": [
      "transaction_created",
      "transaction_paid"
    ]
  }'

Resposta de sucesso (201)

{
  "sucess": true,
  "data": {
    "id": 3,
    "name": "Webhook Principal",
    "url": "https://minhaempresa.com/api/webhooks/vexy-bank",
    "isActive": true,
    "events": [
      "transaction_created",
      "transaction_paid", 
      "transaction_refunded",
      "transfer_created",
      "transfer_completed",
      "transfer_canceled"
    ],
    "signatureSecret": "whk_live_x9y8z7w6v5u4t3s2r1q0p9o8n7m6l5k4",
    "createdAt": "2024-01-20T09:15:00.000Z"
  }
}

Campos da Resposta

CampoDescrição
idID único do webhook criado
signatureSecretChave secreta para validação HMAC
createdAtData/hora de criação
Importante: Guarde o signatureSecret em local seguro. É necessário para validar a autenticidade dos webhooks recebidos.

Implementações por linguagem

JavaScript/Node.js

class WebhookCreator {
  constructor(auth) {
    this.auth = auth
  }

  async createWebhook(webhookData) {
    try {
      const response = await this.auth.makeAuthenticatedRequest(
        'POST',
        '/api/v1/webhook',
        webhookData
      )
      
      return response.data.data
    } catch (error) {
      throw new Error(`Erro ao criar webhook: ${error.message}`)
    }
  }

  async createPaymentWebhook(url, name = 'Webhook Pagamentos') {
    return await this.createWebhook({
      url,
      name,
      isActive: true,
      events: [
        'transaction_created',
        'transaction_paid',
        'transaction_refunded'
      ]
    })
  }

  async createTransferWebhook(url, name = 'Webhook Transferências') {
    return await this.createWebhook({
      url,
      name,
      isActive: true,
      events: [
        'transfer_created',
        'transfer_completed',
        'transfer_canceled'
      ]
    })
  }

  async createCompleteWebhook(url, name = 'Webhook Completo') {
    return await this.createWebhook({
      url,
      name,
      isActive: true,
      events: [
        'transaction_created',
        'transaction_paid',
        'transaction_refunded',
        'transaction_infraction',
        'transfer_created',
        'transfer_completed',
        'transfer_canceled',
        'transfer_updated'
      ]
    })
  }

  validateWebhookUrl(url) {
    try {
      const urlObj = new URL(url)
      
      // Deve ser HTTPS em produção
      if (process.env.NODE_ENV === 'production' && urlObj.protocol !== 'https:') {
        throw new Error('URL deve usar HTTPS em produção')
      }
      
      // Não pode ser localhost em produção
      if (process.env.NODE_ENV === 'production' && 
          (urlObj.hostname === 'localhost' || urlObj.hostname === '127.0.0.1')) {
        throw new Error('URL não pode ser localhost em produção')
      }
      
      return true
    } catch (error) {
      throw new Error(`URL inválida: ${error.message}`)
    }
  }
}

// Uso
const webhookCreator = new WebhookCreator(authInstance)

try {
  // Validar URL primeiro
  const webhookUrl = 'https://minhaempresa.com/api/webhooks/vexy-bank'
  webhookCreator.validateWebhookUrl(webhookUrl)

  // Criar webhook completo
  const webhook = await webhookCreator.createCompleteWebhook(
    webhookUrl,
    'Webhook Produção - Todos Eventos'
  )

  console.log('Webhook criado com sucesso!')
  console.log('ID:', webhook.id)
  console.log('Secret:', webhook.signatureSecret)
  
  // Salvar secret de forma segura
  process.env.WEBHOOK_SECRET = webhook.signatureSecret
  
} catch (error) {
  console.error('Erro:', error.message)
}

Python

import os
from urllib.parse import urlparse

class WebhookCreator:
    def __init__(self, auth_instance):
        self.auth = auth_instance

    def create_webhook(self, webhook_data):
        response = self.auth.make_authenticated_request(
            'POST',
            '/api/v1/webhook',
            webhook_data
        )
        
        if response.status_code == 201:
            return response.json()['data']
        else:
            raise Exception(f"Erro ao criar webhook: {response.text}")

    def create_payment_webhook(self, url, name='Webhook Pagamentos'):
        return self.create_webhook({
            'url': url,
            'name': name,
            'isActive': True,
            'events': [
                'transaction_created',
                'transaction_paid',
                'transaction_refunded'
            ]
        })

    def create_transfer_webhook(self, url, name='Webhook Transferências'):
        return self.create_webhook({
            'url': url,
            'name': name,
            'isActive': True,
            'events': [
                'transfer_created',
                'transfer_completed',
                'transfer_canceled'
            ]
        })

    def create_complete_webhook(self, url, name='Webhook Completo'):
        return self.create_webhook({
            'url': url,
            'name': name,
            'isActive': True,
            'events': [
                'transaction_created',
                'transaction_paid',
                'transaction_refunded',
                'transaction_infraction',
                'transfer_created',
                'transfer_completed',
                'transfer_canceled',
                'transfer_updated'
            ]
        })

    def validate_webhook_url(self, url):
        try:
            parsed = urlparse(url)
            
            # Deve ser HTTPS em produção
            if os.getenv('ENVIRONMENT') == 'production' and parsed.scheme != 'https':
                raise ValueError('URL deve usar HTTPS em produção')
            
            # Não pode ser localhost em produção
            if (os.getenv('ENVIRONMENT') == 'production' and 
                parsed.hostname in ['localhost', '127.0.0.1']):
                raise ValueError('URL não pode ser localhost em produção')
            
            return True
        except Exception as error:
            raise ValueError(f"URL inválida: {error}")

# Uso
webhook_creator = WebhookCreator(auth_instance)

try:
    # Validar URL primeiro
    webhook_url = 'https://minhaempresa.com/api/webhooks/vexy-bank'
    webhook_creator.validate_webhook_url(webhook_url)

    # Criar webhook completo
    webhook = webhook_creator.create_complete_webhook(
        webhook_url,
        'Webhook Produção - Todos Eventos'
    )

    print('Webhook criado com sucesso!')
    print(f'ID: {webhook["id"]}')
    print(f'Secret: {webhook["signatureSecret"]}')
    
    # Salvar secret de forma segura
    os.environ['WEBHOOK_SECRET'] = webhook['signatureSecret']
    
except Exception as error:
    print(f'Erro: {error}')

PHP

<?php

class WebhookCreator {
    private $auth;

    public function __construct($authInstance) {
        $this->auth = $authInstance;
    }

    public function createWebhook($webhookData) {
        $response = $this->auth->makeAuthenticatedRequest(
            'POST',
            '/api/v1/webhook',
            $webhookData
        );

        $data = json_decode($response, true);

        if ($data['sucess']) {
            return $data['data'];
        } else {
            throw new Exception("Erro ao criar webhook: " . $response);
        }
    }

    public function createPaymentWebhook($url, $name = 'Webhook Pagamentos') {
        return $this->createWebhook([
            'url' => $url,
            'name' => $name,
            'isActive' => true,
            'events' => [
                'transaction_created',
                'transaction_paid',
                'transaction_refunded'
            ]
        ]);
    }

    public function createTransferWebhook($url, $name = 'Webhook Transferências') {
        return $this->createWebhook([
            'url' => $url,
            'name' => $name,
            'isActive' => true,
            'events' => [
                'transfer_created',
                'transfer_completed',
                'transfer_canceled'
            ]
        ]);
    }

    public function createCompleteWebhook($url, $name = 'Webhook Completo') {
        return $this->createWebhook([
            'url' => $url,
            'name' => $name,
            'isActive' => true,
            'events' => [
                'transaction_created',
                'transaction_paid',
                'transaction_refunded',
                'transaction_infraction',
                'transfer_created',
                'transfer_completed',
                'transfer_canceled',
                'transfer_updated'
            ]
        ]);
    }

    public function validateWebhookUrl($url) {
        $parsed = parse_url($url);
        
        if (!$parsed) {
            throw new InvalidArgumentException('URL inválida');
        }
        
        // Deve ser HTTPS em produção
        if ($_ENV['ENVIRONMENT'] === 'production' && $parsed['scheme'] !== 'https') {
            throw new InvalidArgumentException('URL deve usar HTTPS em produção');
        }
        
        // Não pode ser localhost em produção
        if ($_ENV['ENVIRONMENT'] === 'production' && 
            in_array($parsed['host'], ['localhost', '127.0.0.1'])) {
            throw new InvalidArgumentException('URL não pode ser localhost em produção');
        }
        
        return true;
    }
}

// Uso
$webhookCreator = new WebhookCreator($authInstance);

try {
    // Validar URL primeiro
    $webhookUrl = 'https://minhaempresa.com/api/webhooks/vexy-bank';
    $webhookCreator->validateWebhookUrl($webhookUrl);

    // Criar webhook completo
    $webhook = $webhookCreator->createCompleteWebhook(
        $webhookUrl,
        'Webhook Produção - Todos Eventos'
    );

    echo "Webhook criado com sucesso!\n";
    echo "ID: " . $webhook['id'] . "\n";
    echo "Secret: " . $webhook['signatureSecret'] . "\n";
    
    // Salvar secret de forma segura
    $_ENV['WEBHOOK_SECRET'] = $webhook['signatureSecret'];
    
} catch (Exception $error) {
    echo "Erro: " . $error->getMessage() . "\n";
}
?>

Boas práticas de configuração

1. Validação de URL

Sempre use HTTPS em produção para segurança:
if (url.startsWith('http://') && environment === 'production') {
  throw new Error('Use HTTPS em produção')
}
URLs devem ser acessíveis publicamente:
const forbiddenHosts = ['localhost', '127.0.0.1', '192.168.', '10.']
if (forbiddenHosts.some(host => url.includes(host))) {
  console.warn('URL pode não ser acessível publicamente')
}
Teste se o endpoint responde antes de configurar:
async function testWebhookUrl(url) {
  try {
    const response = await fetch(url, { 
      method: 'HEAD',
      timeout: 5000 
    })
    return response.ok
  } catch (error) {
    return false
  }
}

2. Escolha de Eventos

// Configurações por tipo de negócio

// E-commerce - foco em vendas
const ecommerceEvents = [
  'transaction_created',  // QR Code gerado
  'transaction_paid',     // Pagamento confirmado
  'transaction_refunded'  // Estornos
]

// Marketplace - vendas + repasses
const marketplaceEvents = [
  'transaction_created',
  'transaction_paid',
  'transaction_refunded',
  'transfer_created',     // Repasse iniciado
  'transfer_completed'    // Repasse concluído
]

// Fintech - operações completas
const fintechEvents = [
  'transaction_created',
  'transaction_paid',
  'transaction_refunded',
  'transaction_infraction',
  'transfer_created',
  'transfer_completed',
  'transfer_canceled',
  'transfer_updated'
]

3. Ambiente de Desenvolvimento

// Configuração para desenvolvimento local
async function setupDevWebhook() {
  // Usar ngrok ou similar para expor localhost
  const ngrokUrl = await startNgrokTunnel(3000)
  
  const webhook = await webhookCreator.createWebhook({
    url: `${ngrokUrl}/webhooks/test`,
    name: 'Webhook Desenvolvimento',
    isActive: true,
    events: ['transaction_created', 'transaction_paid']
  })
  
  console.log(`Webhook dev criado: ${webhook.url}`)
  return webhook
}

Configuração do endpoint

Estrutura Básica do Endpoint

const express = require('express')
const crypto = require('crypto')
const app = express()

// Middleware para webhook
app.use('/webhooks/vexy-bank', express.raw({type: 'application/json'}))

app.post('/webhooks/vexy-bank', (req, res) => {
  try {
    // 1. Verificar assinatura
    const signature = req.headers['vexy-signature']
    const payload = req.body
    
    if (!verifySignature(payload, signature, process.env.WEBHOOK_SECRET)) {
      return res.status(401).send('Invalid signature')
    }

    // 2. Parse do evento
    const event = JSON.parse(payload)
    
    // 3. Processar evento
    processWebhookEvent(event)
    
    // 4. Responder rapidamente
    res.status(200).send('OK')
    
  } catch (error) {
    console.error('Webhook error:', error)
    res.status(500).send('Internal server error')
  }
})

function verifySignature(payload, signature, secret) {
  const computedSignature = crypto
    .createHmac('sha256', secret)
    .update(payload)
    .digest('hex')
  
  return signature === computedSignature
}

function processWebhookEvent(event) {
  switch (event.event) {
    case 'transaction_paid':
      handlePaymentReceived(event.transaction)
      break
    case 'transfer_completed':
      handleTransferCompleted(event.transfer)
      break
    default:
      console.log('Evento não tratado:', event.event)
  }
}

Códigos de erro

CódigoErroCausaSolução
400URL inválidaFormato incorreto da URLVerificar formato da URL
400Eventos inválidosEvento não existeUsar eventos válidos
422URL não acessívelEndpoint não respondeVerificar conectividade
422Limite excedidoMuitos webhooksRemover webhooks antigos

Exemplo de Tratamento

async function createWebhookSafely(webhookData) {
  try {
    // Validar antes de criar
    await validateWebhookData(webhookData)
    
    return await webhookCreator.createWebhook(webhookData)
  } catch (error) {
    switch (error.code) {
      case 'INVALID_URL':
        throw new Error('URL inválida. Verifique o formato.')
      case 'URL_NOT_ACCESSIBLE':
        throw new Error('URL não está acessível. Verifique sua conectividade.')
      case 'LIMIT_EXCEEDED':
        throw new Error('Limite de webhooks excedido. Remova webhooks antigos.')
      default:
        throw new Error('Erro interno. Tente novamente.')
    }
  }
}

Próximos passos