# Gerar token de acesso JWT Source: https://docs.firebanking.dev/api-reference/endpoints/generate-token post /api/auth/token **Requer certificado de cliente no header X-SSL-Client-Cert**. Autentique usando certificado de cliente X.509 + credenciais OAuth 2.0 (clientId/clientSecret) e receba um token JWT. Este endpoint gera um token JWT de acesso válido por **30 minutos** (1800 segundos). Requer certificado X.509 vinculado à conta e credenciais OAuth 2.0. O certificado X.509 deve ser enviado URL-encoded no header `X-SSL-Client-Cert`. O certificado deve estar previamente vinculado à sua conta. # Consultar saldo da conta Source: https://docs.firebanking.dev/api-reference/endpoints/get-balance get /api/balance **Requer token Bearer no header Authorization**. Retorna o saldo bruto, bloqueado e líquido da conta autenticada. # Consultar transação por chave PIX e identificador Source: https://docs.firebanking.dev/api-reference/endpoints/get-transaction-by-pix-key get /api/pix/transactions/pix-key/{pixKey}/{identifier} **Requer token Bearer no header Authorization**. Retorna uma transação específica associada a uma chave PIX, buscando pelo identificador fornecido. **Lógica de resolução do identificador**: O valor informado é comparado simultaneamente contra `endToEndId` (e2eId do PIX), `externalId` e `id` numérico. Na prática não há ambiguidade: o formato de cada tipo é único (e2eId começa com `E` + 32 chars alfanuméricos; id é puramente numérico; externalId é qualquer outra string). # Listar transações por chave PIX Source: https://docs.firebanking.dev/api-reference/endpoints/list-transactions-by-pix-key get /api/pix/transactions/pix-key/{pixKey} **Requer token Bearer no header Authorization**. Retorna transações associadas a uma chave PIX específica com paginação. **Características:** - Valores convertidos para reais (2 decimais) - Status e tipos mapeados para português - Documentos de contraparte mascarados - Intervalo máximo de **31 dias** entre startDate e endDate - Default de `startDate`: últimos **30 dias** - Limite máximo de **1000 resultados** totais **Tipos de chave PIX suportados:** CPF, CNPJ, telefone, e-mail, chave aleatória EVP **Mapeamento de Status:** - `PENDING` → `Pendente` - `CONFIRMED` → `Confirmado` - `ERROR` → `Error` **Mapeamento de Tipos:** - `PAYMENT` → `Pix in` - `WITHDRAW` → `Pix out` - `REFUND_IN` → `Refund in` - `REFUND_OUT` → `Refund out` # Gerar cobrança PIX (Cash-In) Source: https://docs.firebanking.dev/api-reference/endpoints/pix-cash-in post /api/pix/cash-in **Requer token Bearer no header Authorization**. Gera um QR Code dinâmico para recebimento via PIX. # Realizar pagamento PIX (Cash-Out) Source: https://docs.firebanking.dev/api-reference/endpoints/pix-cash-out post /api/pix/cash-out **Requer token Bearer no header Authorization**. Envia um pagamento PIX para uma chave PIX. # Realizar pagamento PIX via QR Code (Cash-Out QR Code) Source: https://docs.firebanking.dev/api-reference/endpoints/pix-cash-out-qrcode post /api/pix/cash-out-qrcode **Requer token Bearer no header Authorization**. Envia um pagamento PIX a partir de um QR Code escaneado ou copiado (copia-e-cola). O QR Code deve seguir o padrão EMV PIX do Banco Central. # Solicitar estorno de pagamento recebido (Refund-In) Source: https://docs.firebanking.dev/api-reference/endpoints/pix-refund-in post /api/pix/refund-in/{id} **Requer token Bearer no header Authorization**. Solicita a devolução de um pagamento PIX recebido. O estorno pode ser parcial ou total, desde que dentro do prazo de 89 dias. # Consultar status de transação Source: https://docs.firebanking.dev/api-reference/endpoints/pix-transaction-poll get /api/pix/transaction/{id} **Requer token Bearer no header Authorization**. Retorna o status atual de uma transação PIX com informações detalhadas sobre valores, contraparte e timestamps. O identificador pode ser: - **ID numérico**: Identificador interno da transação retornado pela Fire Banking - **externalId**: Identificador externo que você forneceu na criação da transação **Campos retornados:** - Informações básicas (id, externalId, type, status) - Valores (originalAmount, feeAmount, finalAmount) - End-to-end ID do PIX (e2eId) - Dados da contraparte (nome, documento, banco) - Timestamps (createdAt, updatedAt, processedAt) # Buscar transações da conta Source: https://docs.firebanking.dev/api-reference/endpoints/transactions-search get /api/transactions **Requer token Bearer no header Authorization**. Retorna transações da conta autenticada no formato público amigável com paginação. **Características:** - Valores convertidos para reais (2 decimais) - Status e tipos mapeados para português - Documentos de contraparte mascarados - Intervalo máximo de 31 dias entre startDate e endDate **Mapeamento de Status:** - `PENDING` → `Pendente` - `CONFIRMED` → `Confirmado` - `ERROR` → `Error` **Mapeamento de Tipos:** - `PAYMENT` → `Pix in` - `WITHDRAW` → `Pix out` - `REFUND_IN` → `Refund in` - `REFUND_OUT` → `Refund out` **Tipo de Movimento:** - `Pix in`, `Refund out` → `CREDIT` - `Pix out`, `Refund in` → `DEBIT` # Reenviar webhook de transação Source: https://docs.firebanking.dev/api-reference/endpoints/webhook-resend post /api/resend-webhook/{transactionIdentifier} **Requer token Bearer no header Authorization**. Reenvia o webhook de uma transação específica para a URL configurada ou para uma URL temporária (override). O identificador da transação pode ser: - **ID numérico da transação**: O ID retornado pela Fire Banking (campo `transactionId` nos webhooks) - **Seu ID de referência**: O identificador que você forneceu ao criar a transação (externalId) - **End-to-End ID do PIX**: O e2eId retornado nos webhooks (formato: E/D + 32 chars) **Webhook por Tipo de Operação**: - Cada tipo de operação (cash_in, cash_out, refund_in, refund_out) pode ter uma URL de webhook diferente - O sistema identifica automaticamente o tipo da transação e busca a URL correspondente - Se não houver webhook configurado para o tipo específico, retorna erro 400 **Comportamento de URL**: - Se `url` for fornecido no body, usa essa URL temporariamente (não persiste) - Se `url` não for fornecido, usa a URL configurada no webhook da conta para o tipo da operação - Se nenhuma URL estiver disponível, retorna erro 400 **Rate Limiting**: 60 requisições por minuto por conta. # Listar webhooks da conta Source: https://docs.firebanking.dev/api-reference/endpoints/webhooks-list get /api/webhooks **Requer token Bearer no header Authorization**. Retorna todos os webhooks configurados para a conta autenticada. Cada webhook inclui: - **id**: Identificador único do webhook - **type**: Tipo do evento (cash_in, cash_out, refund_in, refund_out) - **url**: URL do endpoint configurado - **headers**: Headers customizados - **isActive**: Status do webhook - **createdAt**: Data de criação # Configurar webhook da conta Source: https://docs.firebanking.dev/api-reference/endpoints/webhooks-setup post /api/webhooks **Requer token Bearer no header Authorization**. Configura ou atualiza a URL de webhook para um tipo de evento específico. Se já existir um webhook configurado para o mesmo tipo de evento, ele será atualizado (comportamento de upsert). **Eventos disponíveis:** - `cash_in` - PIX recebido - `cash_out` - PIX enviado - `refund_in` - Estorno de recebimento (devolução solicitada) - `refund_out` - Devolução recebida **Headers personalizados:** Você pode configurar até 5 headers customizados para autenticação do seu endpoint. Headers bloqueados (não permitidos): host, content-length, connection, transfer-encoding, content-type, user-agent. **Invalidação de cache:** Ao configurar um webhook, o cache é invalidado automaticamente no serviço de notificações. Transações subsequentes usarão a nova configuração imediatamente. # Autenticação Source: https://docs.firebanking.dev/api-reference/guides/authentication Como autenticar e obter tokens de acesso na API Fire Banking ## Visão Geral A API Fire Banking utiliza autenticação baseada em **OAuth 2.0** com **certificados X.509 (mTLS)**. Este modelo de segurança em camadas garante que apenas clientes autorizados com certificados válidos possam acessar a API. ### Por que mTLS? O mTLS (mutual TLS) oferece segurança superior comparado a tokens simples: * **Autenticação mútua**: Tanto o cliente quanto o servidor se autenticam * **Não-repúdio**: Certificados vinculados à conta garantem rastreabilidade * **Proteção contra roubo de credenciais**: Mesmo com clientId/clientSecret vazados, o atacante precisaria do certificado ## Pré-requisitos Antes de iniciar, você precisará: Obtenha seu certificado cliente através do portal Fire Banking. O certificado deve estar no formato PEM e será **vinculado à sua conta**. Solicite suas credenciais (`clientId` e `clientSecret`) no painel administrativo. Configure seu ambiente para enviar o certificado no header `X-SSL-Client-Cert`. O certificado X.509 deve estar **vinculado à sua conta** antes de ser utilizado. Certificados não vinculados serão rejeitados mesmo que válidos. ## Endpoint de Autenticação ### POST /api/auth/token Gera um token JWT de acesso válido por **30 minutos** (1800 segundos). O certificado X.509 deve ser enviado **URL-encoded** no header `X-SSL-Client-Cert`. O sistema valida o fingerprint SHA256 do certificado contra os registros vinculados à conta. #### Request ```bash theme={null} curl -X POST https://api.public.firebanking.com.br/api/auth/token \ -H "Content-Type: application/json" \ -H "X-SSL-Client-Cert: -----BEGIN%20CERTIFICATE-----%0AMIIB..." \ -d '{ "clientId": "account-93-550e8400", "clientSecret": "a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6" }' ``` #### Response (201 Created) ```json theme={null} { "access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...", "token_type": "Bearer", "expires_in": 1800 } ``` ## Exemplo Prático: Node.js ### Instalação ```bash theme={null} npm install axios ``` ### Código Completo ```javascript theme={null} const axios = require('axios'); const fs = require('fs'); // Carregar certificado X.509 const certificate = fs.readFileSync('./client-cert.pem', 'utf8'); const encodedCert = encodeURIComponent(certificate); // Configuração da requisição const config = { method: 'post', url: 'https://api.public.firebanking.com.br/api/auth/token', headers: { 'Content-Type': 'application/json', 'X-SSL-Client-Cert': encodedCert }, data: { clientId: process.env.FIREBANKING_CLIENT_ID, clientSecret: process.env.FIREBANKING_CLIENT_SECRET } }; // Fazer requisição async function getToken() { try { const response = await axios(config); console.log('Token obtido com sucesso!'); console.log('Expira em:', response.data.expires_in, 'segundos'); return response.data.access_token; } catch (error) { console.error('Erro ao obter token:', error.response?.data || error.message); throw error; } } getToken(); ``` ## Exemplo Prático: Python ### Instalação ```bash theme={null} pip install requests ``` ### Código Completo ```python theme={null} import os import requests import urllib.parse # Carregar e codificar certificado with open('client-cert.pem', 'r') as f: certificate = f.read() encoded_cert = urllib.parse.quote(certificate) # Configuração da requisição url = 'https://api.public.firebanking.com.br/api/auth/token' headers = { 'Content-Type': 'application/json', 'X-SSL-Client-Cert': encoded_cert } payload = { 'clientId': os.environ.get('FIREBANKING_CLIENT_ID'), 'clientSecret': os.environ.get('FIREBANKING_CLIENT_SECRET') } # Fazer requisição try: response = requests.post(url, json=payload, headers=headers) response.raise_for_status() data = response.json() print('Token obtido com sucesso!') print(f"Expira em: {data['expires_in']} segundos") except requests.exceptions.RequestException as e: print(f'Erro ao obter token: {e}') if hasattr(e.response, 'text'): print(f'Resposta: {e.response.text}') ``` ## Utilizando o Token Após obter o token, inclua-o no header `Authorization` de todas as requisições: ```bash theme={null} curl -X GET https://api.public.firebanking.com.br/api/balance \ -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..." ``` ## Renovação de Token Tokens expiram após **30 minutos**. Implemente lógica de renovação automática em sua aplicação para evitar interrupções. ### Estratégia Recomendada ```javascript theme={null} class TokenManager { constructor(clientId, clientSecret, certificatePath) { this.clientId = clientId; this.clientSecret = clientSecret; this.certificatePath = certificatePath; this.token = null; this.expiresAt = null; } async getValidToken() { // Verifica se o token ainda é válido (com margem de 30 segundos) if (this.token && this.expiresAt && Date.now() < this.expiresAt - 30000) { return this.token; } // Renova o token return await this.refreshToken(); } async refreshToken() { const response = await this.requestNewToken(); this.token = response.access_token; this.expiresAt = Date.now() + (response.expires_in * 1000); return this.token; } async requestNewToken() { const fs = require('fs'); const axios = require('axios'); const certificate = fs.readFileSync(this.certificatePath, 'utf8'); const encodedCert = encodeURIComponent(certificate); const response = await axios.post('https://api.public.firebanking.com.br/api/auth/token', { clientId: this.clientId, clientSecret: this.clientSecret }, { headers: { 'Content-Type': 'application/json', 'X-SSL-Client-Cert': encodedCert } }); return response.data; } } // Uso const tokenManager = new TokenManager( process.env.FIREBANKING_CLIENT_ID, process.env.FIREBANKING_CLIENT_SECRET, './client-cert.pem' ); // Em qualquer requisição const token = await tokenManager.getValidToken(); ``` ## Validação do Certificado O sistema realiza as seguintes validações no certificado: 1. **Formato PEM válido**: O certificado deve estar no formato PEM e URL-encoded 2. **Vinculação à conta**: O fingerprint SHA256 do certificado deve estar registrado e vinculado à sua conta 3. **Correspondência de credenciais**: O certificado deve pertencer à mesma conta das credenciais OAuth Certificados não vinculados ou vinculados a outra conta serão rejeitados, mesmo que tecnicamente válidos. ## Erros Comuns ### 400 Bad Request **Causa:** Certificado ausente ou mal formatado ```json theme={null} { "statusCode": 400, "message": "Certificado ausente no header X-SSL-Client-Cert" } ``` **Solução:** Verifique se: * O certificado está no formato PEM * O certificado está URL-encoded (use `encodeURIComponent()`) * O header `X-SSL-Client-Cert` está presente na requisição *** ### 401 Unauthorized **Causa:** Credenciais inválidas ou certificado não autorizado ```json theme={null} { "statusCode": 401, "message": "Credenciais inválidas ou certificado inválido" } ``` **Solução:** Verifique se: * O `clientId` e `clientSecret` estão corretos * O certificado está **vinculado** à sua conta no portal Fire Banking * O certificado corresponde às credenciais OAuth utilizadas *** ### 403 Forbidden **Causa:** Certificado não vinculado à conta ```json theme={null} { "statusCode": 403, "message": "Certificado não vinculado à conta" } ``` **Solução:** Entre em contato com o suporte Fire Banking para vincular o certificado à sua conta. ## Boas Práticas Nunca exponha suas credenciais em código-fonte. Use variáveis de ambiente ou cofres de segredos (AWS Secrets Manager, Azure Key Vault, etc.). ```javascript theme={null} const clientId = process.env.FIREBANKING_CLIENT_ID; const clientSecret = process.env.FIREBANKING_CLIENT_SECRET; ``` Evite requisições desnecessárias armazenando tokens válidos em cache (Redis, memória, etc.). Configure alertas para detectar falhas de autenticação recorrentes, que podem indicar problemas com renovação de tokens. Sempre utilize conexões HTTPS para proteger credenciais em trânsito. ## Próximos Passos Aprenda a consultar o saldo da conta Crie cobranças PIX dinâmicas # Consulta de Saldo Source: https://docs.firebanking.dev/api-reference/guides/balance Como consultar o saldo da conta em tempo real ## Visão Geral O endpoint de consulta de saldo permite que você obtenha informações detalhadas sobre o saldo da conta autenticada em tempo real. A resposta inclui três tipos de saldo: * **Saldo Bruto (Gross Balance)**: Valor total disponível na conta * **Saldo Bloqueado (Blocked Balance)**: Valores reservados para operações pendentes * **Saldo Líquido (Net Balance)**: Valor disponível para uso imediato Este endpoint requer um token Bearer válido. Verifique a [documentação de autenticação](/api-reference/guides/authentication) para mais detalhes. ## Endpoint ### GET /api/balance Retorna o saldo atual da conta autenticada. #### Headers Obrigatórios ``` Authorization: Bearer {token} ``` #### Request ```bash theme={null} curl -X GET https://api.public.firebanking.com.br/api/balance \ -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..." ``` #### Response (200 OK) ```json theme={null} { "grossBalance": 48734.90, "blockedBalance": 0.00, "netBalance": 48734.90, "consultedAt": "2025-11-19T20:18:29.384Z" } ``` ## Estrutura da Resposta Saldo bruto total da conta (saldo líquido + saldo bloqueado) **Exemplo:** `48734.90` Valor bloqueado por operações pendentes (pagamentos em processamento, cobranças aguardando confirmação) **Exemplo:** `0.00` Saldo líquido disponível para uso imediato (grossBalance - blockedBalance) **Exemplo:** `48734.90` Data e hora da consulta no formato ISO 8601 (UTC) **Exemplo:** `2025-11-19T20:18:29.384Z` ## Exemplos de Implementação ### Node.js / TypeScript ```typescript theme={null} import axios from 'axios'; interface BalanceResponse { grossBalance: number; blockedBalance: number; netBalance: number; consultedAt: string; } async function getBalance(token: string): Promise { try { const response = await axios.get( 'https://api.public.firebanking.com.br/api/balance', { headers: { 'Authorization': `Bearer ${token}` } } ); console.log('=== Saldo da Conta ==='); console.log(`Saldo Bruto: R$ ${response.data.grossBalance.toFixed(2)}`); console.log(`Saldo Bloqueado: R$ ${response.data.blockedBalance.toFixed(2)}`); console.log(`Saldo Líquido: R$ ${response.data.netBalance.toFixed(2)}`); console.log(`Consultado em: ${new Date(response.data.consultedAt).toLocaleString('pt-BR')}`); return response.data; } catch (error) { if (axios.isAxiosError(error)) { console.error('Erro ao consultar saldo:', error.response?.data); throw new Error(error.response?.data?.message || 'Erro ao consultar saldo'); } throw error; } } // Uso const token = 'seu_token_aqui'; getBalance(token); ``` ### Python ```python theme={null} import requests from datetime import datetime from typing import Dict def get_balance(token: str) -> Dict: """ Consulta o saldo da conta Args: token: Token Bearer válido Returns: Dicionário com informações de saldo """ url = 'https://api.public.firebanking.com.br/api/balance' headers = { 'Authorization': f'Bearer {token}' } try: response = requests.get(url, headers=headers) response.raise_for_status() data = response.json() print('=== Saldo da Conta ===') print(f"Saldo Bruto: R$ {data['grossBalance']:.2f}") print(f"Saldo Bloqueado: R$ {data['blockedBalance']:.2f}") print(f"Saldo Líquido: R$ {data['netBalance']:.2f}") consulted_at = datetime.fromisoformat(data['consultedAt'].replace('Z', '+00:00')) print(f"Consultado em: {consulted_at.strftime('%d/%m/%Y %H:%M:%S')}") return data except requests.exceptions.RequestException as e: print(f'Erro ao consultar saldo: {e}') if hasattr(e.response, 'json'): print(f'Detalhes: {e.response.json()}') raise # Uso token = 'seu_token_aqui' balance = get_balance(token) ``` ### PHP ```php theme={null} { const balance = await getBalance(token); // Atualizar UI document.getElementById('gross-balance').textContent = `R$ ${balance.grossBalance.toLocaleString('pt-BR', { minimumFractionDigits: 2 })}`; document.getElementById('net-balance').textContent = `R$ ${balance.netBalance.toLocaleString('pt-BR', { minimumFractionDigits: 2 })}`; // Alerta de saldo baixo if (balance.netBalance < 1000) { showLowBalanceAlert(); } }, 30000); ``` ### 2. Validação Antes de Pagamento Verifique se há saldo suficiente antes de realizar um pagamento: ```javascript theme={null} async function processPayment(amount: number, token: string) { // Consultar saldo atual const balance = await getBalance(token); // Validar saldo disponível if (balance.netBalance < amount) { throw new Error( `Saldo insuficiente. Disponível: R$ ${balance.netBalance.toFixed(2)} | ` + `Necessário: R$ ${amount.toFixed(2)}` ); } // Prosseguir com pagamento return await createPixPayment(amount, token); } ``` ### 3. Relatório de Conciliação Gere relatórios de conciliação com saldo atual: ```python theme={null} def generate_reconciliation_report(token: str): """Gera relatório de conciliação""" balance = get_balance(token) report = { 'report_date': datetime.now().isoformat(), 'balance_snapshot': balance, 'status': 'OK' if balance['netBalance'] > 0 else 'ALERT', 'blocked_percentage': ( balance['blockedBalance'] / balance['grossBalance'] * 100 if balance['grossBalance'] > 0 else 0 ) } # Salvar relatório with open(f"reconciliation_{datetime.now().strftime('%Y%m%d')}.json", 'w') as f: json.dump(report, f, indent=2) return report ``` ## Entendendo Saldo Bloqueado O **saldo bloqueado** representa valores temporariamente indisponíveis devido a: Quando você envia um pagamento PIX (seja por chave PIX ou via QR Code), o valor é imediatamente bloqueado até a confirmação da transação. **Exemplo:** * Saldo inicial: R\$ 1.000,00 * Pagamento de R\$ 250,00 iniciado * Saldo líquido: R\$ 750,00 * Saldo bloqueado: R\$ 250,00 Cobranças PIX geradas mas ainda não pagas pelos clientes não afetam o saldo bloqueado. O bloqueio ocorre apenas após o pagamento, durante o processamento. Solicitações de estorno podem bloquear temporariamente o valor até a conclusão da análise. ## Códigos de Resposta | Código | Descrição | Significado | | ------ | -------------- | ----------------------------------------- | | `200` | Sucesso | Saldo consultado com sucesso | | `401` | Token Inválido | Token não fornecido, expirado ou inválido | Consulte a [Referência da API](/api-reference/endpoints/get-balance) para detalhes completos dos campos de resposta. ## Considerações O saldo retornado reflete o estado no momento exato da consulta. Para operações críticas, sempre consulte o saldo imediatamente antes da transação. * **Cache:** Não recomendamos cache de saldo por mais de 1 minuto * **Precisão:** Valores são retornados com 2 casas decimais * **Moeda:** Todos os valores são em Reais (BRL) ## Monitoramento e Alertas ### Implementando Alertas de Saldo ```javascript theme={null} class BalanceMonitor { constructor(token, thresholds) { this.token = token; this.thresholds = { critical: thresholds.critical || 500, warning: thresholds.warning || 2000, high: thresholds.high || 10000 }; } async checkAndAlert() { const balance = await getBalance(this.token); const netBalance = balance.netBalance; if (netBalance < this.thresholds.critical) { this.sendAlert('CRITICAL', `Saldo crítico: R$ ${netBalance.toFixed(2)}`); } else if (netBalance < this.thresholds.warning) { this.sendAlert('WARNING', `Saldo baixo: R$ ${netBalance.toFixed(2)}`); } // Alerta de saldo bloqueado alto const blockedPercentage = (balance.blockedBalance / balance.grossBalance) * 100; if (blockedPercentage > 50) { this.sendAlert('WARNING', `${blockedPercentage.toFixed(1)}% do saldo está bloqueado`); } return balance; } sendAlert(level, message) { console.log(`[${level}] ${message}`); // Integrar com sistema de alertas (email, SMS, Slack, etc.) } } // Uso const monitor = new BalanceMonitor(token, { critical: 1000, warning: 5000 }); // Executar a cada 5 minutos setInterval(() => monitor.checkAndAlert(), 5 * 60 * 1000); ``` ## Próximos Passos Envie pagamentos para qualquer chave PIX Crie cobranças para receber pagamentos # PIX Cash-In (Recebimento) Source: https://docs.firebanking.dev/api-reference/guides/pix-cash-in Como gerar cobranças PIX e receber pagamentos ## Visão Geral O endpoint **PIX Cash-In** permite que você gere cobranças PIX dinâmicas para receber pagamentos. Cada cobrança gera um QR Code único e um código PIX (Pix Copia e Cola) que seus clientes podem usar para efetuar o pagamento. Este endpoint requer um token Bearer válido. Verifique a [documentação de autenticação](/api-reference/guides/authentication) para mais detalhes. ## Características * Geração de QR Code dinâmico * Código PIX no formato EMV (Copia e Cola) * Configuração de prazo de expiração (5 minutos a 7 dias) * Identificação única por `externalId` * Informações adicionais personalizáveis * Validação automática de CPF/CNPJ ## Endpoint ### POST /api/pix/cash-in Gera uma nova cobrança PIX. #### Headers Obrigatórios ``` Authorization: Bearer {token} Content-Type: application/json ``` #### Request Body ```json theme={null} { "transaction": { "value": 150.00, "description": "Pagamento de pedido #12345", "expirationTime": 86400, "externalId": "ORDER-12345-20240119", "generateQrCode": true }, "payer": { "fullName": "Carlos Oliveira", "document": "12345678901" }, "additionalInfo": { "orderId": "12345", "storeName": "Tech Solutions", "productCategory": "Eletrônicos" } } ``` #### Request ```bash theme={null} curl -X POST https://api.public.firebanking.com.br/api/pix/cash-in \ -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..." \ -H "Content-Type: application/json" \ -d '{ "transaction": { "value": 150.00, "description": "Pagamento de pedido #12345", "expirationTime": 86400, "externalId": "ORDER-12345-20240119", "generateQrCode": true }, "payer": { "fullName": "Carlos Oliveira", "document": "12345678901" }, "additionalInfo": { "orderId": "12345" } }' ``` #### Response (201 Created) ```json theme={null} { "transactionId": "7845", "correlationId": "550e8400-e29b-41d4-a716-446655440000", "externalId": "ORDER-12345-20240119", "status": "PENDING", "pixCode": "00020126580014br.gov.bcb.pix0136550e8400-e29b-41d4-a716-4466554400005204000053039865802BR5916Tech Solutions Ltda6009SAO PAULO62070503***63041D3D", "generateTime": "2024-01-19T14:30:00.000Z", "expirationDate": "2024-01-20T14:30:00.000Z", "qrCodeImage": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAASwAAAEsCAYAAAB5fY51..." } ``` O campo `qrCodeImage` é retornado apenas quando `generateQrCode: true` é enviado na requisição. O valor é uma imagem PNG do QR Code codificada em Base64 no formato Data URL. ## Parâmetros da Requisição ### Transaction Object Valor da transação em reais (BRL). Deve ter no máximo 2 casas decimais. **Mínimo:** `0.01` **Exemplo:** `150.00` Descrição da transação que aparecerá no extrato do pagador. **Máximo:** 140 caracteres **Exemplo:** `"Pagamento de pedido #12345"` Tempo de expiração em segundos. **Mínimo:** `300` (5 minutos) **Máximo:** `604800` (7 dias) **Padrão:** `86400` (24 horas) Identificador único externo da transação. Use para correlacionar com seu sistema. **Máximo:** 255 caracteres **Recomendação:** Use um formato que inclua data/hora para garantir unicidade **Exemplo:** `"ORDER-12345-20240119-143000"` Define se deve gerar o QR Code em Base64. **Padrão:** `false` **Recomendação:** Use `true` para exibir QR Code ao usuário ### Payer Object Nome completo do pagador. **Exemplo:** `"Carlos Oliveira"` CPF ou CNPJ do pagador (apenas números). **CPF:** 11 dígitos **CNPJ:** 14 dígitos **Exemplo:** `"12345678901"` ou `"12345678000199"` ### Additional Info Object Informações adicionais em formato chave-valor (string:string). **Máximo:** 10 chaves **Exemplo:** ```json theme={null} { "orderId": "12345", "customerId": "67890", "storeName": "Tech Solutions" } ``` ## Estrutura da Resposta ID interno da transação gerada pela Fire Banking. **Exemplo:** `"7845"` UUID para rastreamento e correlação da transação. **Exemplo:** `"550e8400-e29b-41d4-a716-446655440000"` ID externo fornecido na requisição (mesmo valor do input). **Exemplo:** `"ORDER-12345-20240119"` Status atual da transação. **Valores possíveis:** * `PENDING`: Aguardando pagamento * `CONFIRMED`: Pagamento confirmado * `ERROR`: Erro no processamento **Exemplo:** `"PENDING"` Código PIX no formato EMV (Pix Copia e Cola). **Exemplo:** `"00020126580014br.gov.bcb.pix..."` Data e hora de geração da cobrança (ISO 8601 UTC). **Exemplo:** `"2024-01-19T14:30:00.000Z"` Data e hora de expiração da cobrança (ISO 8601 UTC). **Exemplo:** `"2024-01-20T14:30:00.000Z"` QR Code em Base64 no formato Data URL. Retornado apenas quando `generateQrCode: true` na requisição. **Formato:** `data:image/png;base64,{base64_encoded_image}` **Exemplo:** `"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAASwAAAEsCAYAAAB5fY51..."` **Uso:** Pode ser exibido diretamente em uma tag `` no HTML ou decodificado para salvar como arquivo. ## Exemplos de Implementação ### Node.js / TypeScript ```typescript theme={null} import axios from 'axios'; interface CashInRequest { transaction: { value: number; description: string; expirationTime?: number; externalId: string; generateQrCode?: boolean; }; payer: { fullName: string; document: string; }; additionalInfo?: Record; } interface CashInResponse { transactionId: string; correlationId: string; externalId: string; status: 'PENDING' | 'CONFIRMED' | 'ERROR'; pixCode: string; generateTime: string; expirationDate: string; qrCodeImage?: string; // Presente apenas quando generateQrCode: true } async function createPixCharge( token: string, orderId: string, amount: number, customerName: string, customerDocument: string ): Promise { const payload: CashInRequest = { transaction: { value: amount, description: `Pagamento do pedido ${orderId}`, expirationTime: 3600, // 1 hora externalId: `ORDER-${orderId}-${Date.now()}`, generateQrCode: true }, payer: { fullName: customerName, document: customerDocument }, additionalInfo: { orderId: orderId, timestamp: new Date().toISOString() } }; try { const response = await axios.post( 'https://api.public.firebanking.com.br/api/pix/cash-in', payload, { headers: { 'Authorization': `Bearer ${token}`, 'Content-Type': 'application/json' } } ); console.log('Cobrança PIX gerada com sucesso!'); console.log(`ID da Transação: ${response.data.transactionId}`); console.log(`Código PIX: ${response.data.pixCode}`); console.log(`Expira em: ${new Date(response.data.expirationDate).toLocaleString('pt-BR')}`); if (response.data.qrCodeImage) { console.log('QR Code Image disponível para exibição'); } return response.data; } catch (error) { if (axios.isAxiosError(error)) { console.error('Erro ao gerar cobrança:', error.response?.data); throw new Error(error.response?.data?.message || 'Erro ao gerar cobrança PIX'); } throw error; } } // Uso const token = 'seu_token_aqui'; createPixCharge(token, '12345', 150.00, 'Carlos Oliveira', '12345678901'); ``` ### Python ```python theme={null} import requests from datetime import datetime, timedelta from typing import Dict, Optional def create_pix_charge( token: str, order_id: str, amount: float, customer_name: str, customer_document: str, expiration_hours: int = 1, additional_info: Optional[Dict[str, str]] = None ) -> Dict: """ Gera uma cobrança PIX Args: token: Token Bearer válido order_id: ID do pedido amount: Valor em reais customer_name: Nome do cliente customer_document: CPF ou CNPJ (apenas números) expiration_hours: Horas até expiração (padrão: 1) additional_info: Informações adicionais Returns: Dados da cobrança gerada """ url = 'https://api.public.firebanking.com.br/api/pix/cash-in' payload = { 'transaction': { 'value': round(amount, 2), 'description': f'Pagamento do pedido {order_id}', 'expirationTime': expiration_hours * 3600, 'externalId': f'ORDER-{order_id}-{int(datetime.now().timestamp())}', 'generateQrCode': True }, 'payer': { 'fullName': customer_name, 'document': customer_document }, 'additionalInfo': additional_info or {} } headers = { 'Authorization': f'Bearer {token}', 'Content-Type': 'application/json' } try: response = requests.post(url, json=payload, headers=headers) response.raise_for_status() data = response.json() print('Cobrança PIX gerada com sucesso!') print(f"ID da Transação: {data['transactionId']}") print(f"Código PIX: {data['pixCode']}") print(f"Status: {data['status']}") expiration = datetime.fromisoformat(data['expirationDate'].replace('Z', '+00:00')) print(f"Expira em: {expiration.strftime('%d/%m/%Y %H:%M:%S')}") if 'qrCodeImage' in data: print('QR Code Image disponível para exibição') return data except requests.exceptions.RequestException as e: print(f'Erro ao gerar cobrança: {e}') if hasattr(e.response, 'json'): print(f'Detalhes: {e.response.json()}') raise # Uso token = 'seu_token_aqui' charge = create_pix_charge( token=token, order_id='12345', amount=150.00, customer_name='Carlos Oliveira', customer_document='12345678901', expiration_hours=24, additional_info={ 'storeName': 'Tech Solutions', 'productCategory': 'Eletrônicos' } ) ``` ### PHP ```php theme={null} [ 'value' => round($amount, 2), 'description' => "Pagamento do pedido $orderId", 'expirationTime' => $expirationHours * 3600, 'externalId' => "ORDER-$orderId-" . time(), 'generateQrCode' => true ], 'payer' => [ 'fullName' => $customerName, 'document' => $customerDocument ], 'additionalInfo' => [ 'orderId' => $orderId ] ]; $ch = curl_init($url); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); curl_setopt($ch, CURLOPT_POST, true); curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($payload)); curl_setopt($ch, CURLOPT_HTTPHEADER, [ 'Authorization: Bearer ' . $token, 'Content-Type: application/json' ]); $response = curl_exec($ch); $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); curl_close($ch); if ($httpCode !== 201) { throw new Exception("Erro ao gerar cobrança: HTTP $httpCode - $response"); } $data = json_decode($response, true); echo "Cobrança PIX gerada com sucesso!" . PHP_EOL; echo "ID da Transação: {$data['transactionId']}" . PHP_EOL; echo "Código PIX: {$data['pixCode']}" . PHP_EOL; echo "Status: {$data['status']}" . PHP_EOL; if (isset($data['qrCodeImage'])) { echo "QR Code Image disponível para exibição" . PHP_EOL; } return $data; } // Uso $token = 'seu_token_aqui'; $charge = createPixCharge( $token, '12345', 150.00, 'Carlos Oliveira', '12345678901', 24 ); ``` ## Casos de Uso ### 1. E-commerce - Checkout com PIX ```javascript theme={null} // Integração em checkout de e-commerce class PixCheckout { constructor(token) { this.token = token; } async generatePayment(order) { const charge = await createPixCharge( this.token, order.id, order.total, order.customer.name, order.customer.document ); // Exibir QR Code na página (usando imagem da API ou gerando localmente) this.displayQrCode(charge); // Iniciar polling para verificar pagamento this.startPaymentPolling(charge.transactionId); return charge; } displayQrCode(charge) { const qrCanvas = document.getElementById('qr-canvas'); const qrImage = document.getElementById('qr-image'); // Usar imagem Base64 da API (preferível - evita processamento no cliente) if (charge.qrCodeImage) { qrImage.src = charge.qrCodeImage; qrImage.style.display = 'block'; qrCanvas.style.display = 'none'; } else { // Fallback: gerar QR Code localmente usando biblioteca (ex: qrcode.js) QRCode.toCanvas(qrCanvas, charge.pixCode, { width: 300, margin: 2 }); qrCanvas.style.display = 'block'; qrImage.style.display = 'none'; } // Mostrar também o código Pix Copia e Cola document.getElementById('pix-code').textContent = charge.pixCode; } startPaymentPolling(transactionId) { // Verificar status a cada 3 segundos const interval = setInterval(async () => { const status = await this.checkPaymentStatus(transactionId); if (status === 'CONFIRMED') { clearInterval(interval); this.onPaymentConfirmed(); } }, 3000); // Parar após 10 minutos setTimeout(() => clearInterval(interval), 10 * 60 * 1000); } onPaymentConfirmed() { // Redirecionar para página de sucesso window.location.href = '/payment/success'; } } ``` ### 2. PDV (Ponto de Venda) ```python theme={null} class PixPDV: """Sistema de PDV com cobrança PIX""" def __init__(self, token: str): self.token = token def process_sale(self, items: list, customer: dict) -> dict: """Processar venda e gerar cobrança PIX""" # Calcular total total = sum(item['price'] * item['quantity'] for item in items) # Gerar descrição description = self.generate_sale_description(items) # Criar cobrança PIX (expira em 15 minutos) charge = create_pix_charge( token=self.token, order_id=self.generate_sale_id(), amount=total, customer_name=customer['name'], customer_document=customer['document'], expiration_hours=0.25, # 15 minutos additional_info={ 'items_count': str(len(items)), 'cashier_id': self.get_cashier_id() } ) # Imprimir comprovante com QR Code self.print_receipt(charge, items, total) return charge def generate_sale_description(self, items: list) -> str: """Gerar descrição resumida da venda""" if len(items) == 1: return f"{items[0]['name']}" else: return f"{len(items)} itens - {items[0]['name']} e mais" def print_receipt(self, charge: dict, items: list, total: float): """Imprimir comprovante com QR Code""" # Implementar impressão térmica ou gerar PDF print("\n" + "="*50) print("COMPROVANTE DE COBRANÇA PIX") print("="*50) for item in items: print(f"{item['name']}: R$ {item['price']:.2f}") print("-"*50) print(f"TOTAL: R$ {total:.2f}") print(f"\nID da Transação: {charge['transactionId']}") print(f"Código PIX:\n{charge['pixCode']}") print("="*50 + "\n") ``` ### 3. SaaS - Cobrança de Assinatura ```typescript theme={null} class SubscriptionBilling { constructor(private token: string) {} async chargeMonthlySubscription( subscriptionId: string, userId: string, planValue: number ) { // Buscar dados do usuário const user = await this.getUserData(userId); // Gerar cobrança com expiração de 3 dias const charge = await createPixCharge( this.token, `SUB-${subscriptionId}-${new Date().getMonth() + 1}`, planValue, user.name, user.document ); // Enviar email com link de pagamento await this.sendPaymentEmail(user.email, charge); // Agendar lembrete 1 dia antes de expirar await this.scheduleReminder(user, charge, 24); return charge; } async sendPaymentEmail(email: string, charge: CashInResponse) { // Implementar envio de email const paymentLink = `https://app.exemplo.com/payment/${charge.transactionId}`; await sendEmail({ to: email, subject: 'Fatura disponível - Pague com PIX', html: `

Sua fatura está disponível

Valor: R$ ${charge.value}

Vencimento: ${new Date(charge.expirationDate).toLocaleDateString('pt-BR')}

Clique aqui para pagar com PIX

` }); } } ``` ## Monitoramento de Pagamentos Para ser notificado quando um pagamento for confirmado, você pode: Configure webhooks para receber notificações automáticas quando o status mudar. ```javascript theme={null} // Endpoint webhook em seu servidor app.post('/webhooks/pix', (req, res) => { const { transactionId, status, externalId } = req.body; if (status === 'CONFIRMED') { // Processar pagamento confirmado processPaymentConfirmation(externalId); } res.sendStatus(200); }); ``` Consulte periodicamente o status da transação. ```javascript theme={null} async function monitorPayment(transactionId, maxAttempts = 200) { for (let i = 0; i < maxAttempts; i++) { const status = await checkTransactionStatus(transactionId); if (status === 'CONFIRMED') { return true; } // Aguardar 3 segundos antes de tentar novamente await new Promise(resolve => setTimeout(resolve, 3000)); } return false; // Timeout } ``` ## Códigos de Resposta | Código | Descrição | Significado | | ------ | --------------- | ------------------------------------------- | | `201` | Cobrança Criada | Cobrança PIX gerada com sucesso | | `400` | Dados Inválidos | Verifique os campos obrigatórios e formatos | | `401` | Token Inválido | Token não fornecido, expirado ou inválido | Consulte a [Referência da API](/api-reference/endpoints/pix-cash-in) para detalhes completos dos campos de resposta. ## Boas Práticas Inclua informações que facilitem a identificação: `ORDER-{orderId}-{timestamp}` ou `INV-{invoiceId}-{date}` * **E-commerce:** 15-30 minutos * **Boletos/Faturas:** 3-7 dias * **PDV:** 5-15 minutos Implemente validação local para evitar erros 400. ```javascript theme={null} function isValidCPF(cpf: string): boolean { cpf = cpf.replace(/\D/g, ''); if (cpf.length !== 11) return false; // Adicionar lógica de validação de CPF return true; } ``` Use bibliotecas de precisão decimal para evitar erros de arredondamento. ```javascript theme={null} import Decimal from 'decimal.js'; const total = new Decimal(price).times(quantity).toNumber(); ``` ## Observações Importantes Cobranças expiradas não podem ser reativadas. Gere uma nova cobrança se necessário. * **Valor mínimo:** R\$ 0,01 * **Expiração mínima:** 5 minutos (300 segundos) * **Expiração máxima:** 7 dias (604800 segundos) ## Próximos Passos Aprenda a estornar pagamentos recebidos Envie pagamentos PIX Realize pagamentos via QR Code PIX # PIX Cash-Out (Pagamento) Source: https://docs.firebanking.dev/api-reference/guides/pix-cash-out Como realizar pagamentos PIX para qualquer chave ## Visão Geral O endpoint **PIX Cash-Out** permite que você realize pagamentos PIX instantâneos para qualquer chave PIX válida (CPF, CNPJ, telefone, email ou chave aleatória). O pagamento é processado em tempo real e o valor é debitado da sua conta imediatamente. Para pagamentos via QR Code PIX (escaneamento ou copia-e-cola), utilize o endpoint dedicado [Cash-Out via QR Code](/api-reference/guides/pix-cash-out-qrcode). Este endpoint é exclusivo para pagamentos por chave PIX. Este endpoint requer um token Bearer válido. Verifique a [documentação de autenticação](/api-reference/guides/authentication) para mais detalhes. ## Características * Pagamentos instantâneos 24/7 * Suporte a todos os tipos de chave PIX * Validação automática de dados do destinatário * Identificação única por `externalId` * Descrição personalizável para o destinatário * Verificação de saldo automática ## Endpoint ### POST /api/pix/cash-out Realiza um pagamento PIX. #### Headers Obrigatórios ``` Authorization: Bearer {token} Content-Type: application/json ``` #### Request Body ```json theme={null} { "value": 250.50, "details": { "key": "12345678901", "keyType": "DOCUMENT", "name": "Ana Costa", "document": "12345678901" }, "externalId": "PAYMENT-987654-20240119", "description": "Pagamento de fornecedor" } ``` #### Request ```bash theme={null} curl -X POST https://api.public.firebanking.com.br/api/pix/cash-out \ -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..." \ -H "Content-Type: application/json" \ -d '{ "value": 250.50, "details": { "key": "12345678901", "keyType": "DOCUMENT", "name": "Ana Costa", "document": "12345678901" }, "externalId": "PAYMENT-987654-20240119", "description": "Pagamento de fornecedor" }' ``` #### Response (201 Created) ```json theme={null} { "transactionId": "9876", "externalId": "PAYMENT-987654-20240119", "status": "PENDING", "generateTime": "2024-01-19T15:45:00.000Z" } ``` ## Parâmetros da Requisição Valor do pagamento em reais (BRL). Deve ter no máximo 2 casas decimais. **Mínimo:** `0.01` **Exemplo:** `250.50` Informações da chave PIX de destino. Chave PIX de destino. **Formatos aceitos:** * CPF: `12345678901` (11 dígitos) * CNPJ: `12345678000199` (14 dígitos) * Email: `usuario@exemplo.com` * Telefone: `5511999999999` (com DDI e DDD) * Chave aleatória: UUID formato `xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx` Tipo da chave PIX. **Valores aceitos:** * `DOCUMENT` - CPF ou CNPJ * `EMAIL` - Endereço de email * `PHONE` - Número de telefone * `RANDOM` - Chave aleatória (UUID) **Exemplo:** `"DOCUMENT"` Nome completo do titular da chave PIX de destino. **Validação:** O nome deve corresponder ao cadastrado na chave PIX **Exemplo:** `"Ana Costa"` CPF ou CNPJ do titular (apenas números). **CPF:** 11 dígitos **CNPJ:** 14 dígitos **Validação:** O documento deve corresponder ao cadastrado na chave PIX **Exemplo:** `"12345678901"` Identificador único externo da transação. **Máximo:** 255 caracteres **Recomendação:** Use um formato que garanta unicidade **Exemplo:** `"PAYMENT-987654-20240119-154500"` Descrição do pagamento que aparecerá no extrato do destinatário. **Máximo:** 140 caracteres **Padrão:** Vazio **Exemplo:** `"Pagamento de fornecedor - Nota Fiscal 12345"` ## Estrutura da Resposta ID interno da transação gerada pela Fire Banking. **Exemplo:** `"9876"` ID externo fornecido na requisição (mesmo valor do input). **Exemplo:** `"PAYMENT-987654-20240119"` Status atual da transação. **Valores possíveis:** * `PENDING`: Pagamento em processamento * `CONFIRMED`: Pagamento confirmado e finalizado * `ERROR`: Erro no processamento **Exemplo:** `"PENDING"` **Nota:** A maioria dos pagamentos PIX é confirmada em poucos segundos Data e hora de criação do pagamento (ISO 8601 UTC). **Exemplo:** `"2024-01-19T15:45:00.000Z"` ## Exemplos de Implementação ### Node.js / TypeScript ```typescript theme={null} import axios from 'axios'; interface CashOutRequest { value: number; details: { key: string; keyType: 'DOCUMENT' | 'EMAIL' | 'PHONE' | 'RANDOM'; name: string; document: string; }; externalId: string; description?: string; } interface CashOutResponse { transactionId: string; externalId: string; status: 'PENDING' | 'CONFIRMED' | 'ERROR'; generateTime: string; } async function sendPixPayment( token: string, recipientKey: string, recipientKeyType: 'DOCUMENT' | 'EMAIL' | 'PHONE' | 'RANDOM', recipientName: string, recipientDocument: string, amount: number, description?: string ): Promise { const payload: CashOutRequest = { value: amount, details: { key: recipientKey, keyType: recipientKeyType, name: recipientName, document: recipientDocument }, externalId: `PAY-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`, description: description || `Pagamento PIX de R$ ${amount.toFixed(2)}` }; try { const response = await axios.post( 'https://api.public.firebanking.com.br/api/pix/cash-out', payload, { headers: { 'Authorization': `Bearer ${token}`, 'Content-Type': 'application/json' } } ); console.log('Pagamento PIX iniciado com sucesso!'); console.log(`ID da Transação: ${response.data.transactionId}`); console.log(`Status: ${response.data.status}`); console.log(`Valor: R$ ${amount.toFixed(2)}`); console.log(`Destinatário: ${recipientName}`); return response.data; } catch (error) { if (axios.isAxiosError(error)) { const errorData = error.response?.data; console.error('Erro ao realizar pagamento:', errorData); // Tratar erros específicos if (error.response?.status === 400) { if (errorData?.message?.includes('saldo insuficiente')) { throw new Error('Saldo insuficiente para realizar o pagamento'); } throw new Error('Dados inválidos: ' + errorData?.message); } throw new Error(errorData?.message || 'Erro ao realizar pagamento PIX'); } throw error; } } // Uso - Pagamento por CPF sendPixPayment( 'seu_token_aqui', '12345678901', 'DOCUMENT', 'Ana Costa', '12345678901', 250.50, 'Pagamento de fornecedor' ); // Uso - Pagamento por Email sendPixPayment( 'seu_token_aqui', 'ana.costa@email.com', 'EMAIL', 'Ana Costa', '12345678901', 100.00, 'Reembolso' ); // Uso - Pagamento por Telefone sendPixPayment( 'seu_token_aqui', '5511999999999', 'PHONE', 'Ana Costa', '12345678901', 50.00 ); ``` ### Python ```python theme={null} import requests from datetime import datetime from typing import Dict, Optional import uuid def send_pix_payment( token: str, recipient_key: str, recipient_key_type: str, recipient_name: str, recipient_document: str, amount: float, description: Optional[str] = None ) -> Dict: """ Envia um pagamento PIX Args: token: Token Bearer válido recipient_key: Chave PIX do destinatário recipient_key_type: Tipo da chave (DOCUMENT, EMAIL, PHONE, RANDOM) recipient_name: Nome do destinatário recipient_document: CPF ou CNPJ do destinatário amount: Valor em reais description: Descrição do pagamento (opcional) Returns: Dados do pagamento iniciado """ url = 'https://api.public.firebanking.com.br/api/pix/cash-out' payload = { 'value': round(amount, 2), 'details': { 'key': recipient_key, 'keyType': recipient_key_type, 'name': recipient_name, 'document': recipient_document }, 'externalId': f'PAY-{int(datetime.now().timestamp())}-{uuid.uuid4().hex[:8]}', 'description': description or f'Pagamento PIX de R$ {amount:.2f}' } headers = { 'Authorization': f'Bearer {token}', 'Content-Type': 'application/json' } try: response = requests.post(url, json=payload, headers=headers) response.raise_for_status() data = response.json() print('Pagamento PIX iniciado com sucesso!') print(f"ID da Transação: {data['transactionId']}") print(f"Status: {data['status']}") print(f"Valor: R$ {amount:.2f}") print(f"Destinatário: {recipient_name}") return data except requests.exceptions.HTTPError as e: error_data = e.response.json() if e.response else {} # Tratar erros específicos if e.response.status_code == 400: if 'saldo insuficiente' in error_data.get('message', '').lower(): raise Exception('Saldo insuficiente para realizar o pagamento') raise Exception(f"Dados inválidos: {error_data.get('message')}") raise Exception(f"Erro ao realizar pagamento: {error_data.get('message', str(e))}") # Uso token = 'seu_token_aqui' # Pagamento por CPF payment = send_pix_payment( token=token, recipient_key='12345678901', recipient_key_type='DOCUMENT', recipient_name='Ana Costa', recipient_document='12345678901', amount=250.50, description='Pagamento de fornecedor' ) ``` ### PHP ```php theme={null} round($amount, 2), 'details' => [ 'key' => $recipientKey, 'keyType' => $recipientKeyType, 'name' => $recipientName, 'document' => $recipientDocument ], 'externalId' => 'PAY-' . time() . '-' . bin2hex(random_bytes(4)), 'description' => $description ?? "Pagamento PIX de R$ " . number_format($amount, 2, ',', '.') ]; $ch = curl_init($url); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); curl_setopt($ch, CURLOPT_POST, true); curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($payload)); curl_setopt($ch, CURLOPT_HTTPHEADER, [ 'Authorization: Bearer ' . $token, 'Content-Type: application/json' ]); $response = curl_exec($ch); $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); curl_close($ch); if ($httpCode !== 201) { $errorData = json_decode($response, true); $errorMessage = $errorData['message'] ?? "HTTP $httpCode"; if ($httpCode === 400 && stripos($errorMessage, 'saldo insuficiente') !== false) { throw new Exception('Saldo insuficiente para realizar o pagamento'); } throw new Exception("Erro ao realizar pagamento: $errorMessage"); } $data = json_decode($response, true); echo "Pagamento PIX iniciado com sucesso!" . PHP_EOL; echo "ID da Transação: {$data['transactionId']}" . PHP_EOL; echo "Status: {$data['status']}" . PHP_EOL; echo "Valor: R$ " . number_format($amount, 2, ',', '.') . PHP_EOL; echo "Destinatário: $recipientName" . PHP_EOL; return $data; } // Uso $token = 'seu_token_aqui'; $payment = sendPixPayment( $token, '12345678901', 'DOCUMENT', 'Ana Costa', '12345678901', 250.50, 'Pagamento de fornecedor' ); ``` ## Casos de Uso ### 1. Folha de Pagamento ```typescript theme={null} class PayrollProcessor { constructor(private token: string) {} async processPayroll(employees: Employee[]) { const results = { successful: [], failed: [] }; for (const employee of employees) { try { // Verificar saldo antes de cada pagamento const balance = await getBalance(this.token); if (balance.netBalance < employee.salary) { throw new Error('Saldo insuficiente'); } // Realizar pagamento const payment = await sendPixPayment( this.token, employee.pixKey, employee.pixKeyType, employee.fullName, employee.document, employee.salary, `Salário ${new Date().toLocaleDateString('pt-BR', { month: 'long', year: 'numeric' })}` ); results.successful.push({ employee: employee.fullName, amount: employee.salary, transactionId: payment.transactionId }); // Aguardar 1 segundo entre pagamentos await this.sleep(1000); } catch (error) { results.failed.push({ employee: employee.fullName, error: error.message }); } } return results; } private sleep(ms: number): Promise { return new Promise(resolve => setTimeout(resolve, ms)); } } // Uso interface Employee { fullName: string; document: string; pixKey: string; pixKeyType: 'DOCUMENT' | 'EMAIL' | 'PHONE' | 'RANDOM'; salary: number; } const payroll = new PayrollProcessor('seu_token_aqui'); const employees: Employee[] = [ { fullName: 'Pedro Santos', document: '12345678901', pixKey: '12345678901', pixKeyType: 'DOCUMENT', salary: 3500.00 }, // ... mais funcionários ]; const results = await payroll.processPayroll(employees); console.log(`Pagamentos bem-sucedidos: ${results.successful.length}`); console.log(`Pagamentos com erro: ${results.failed.length}`); ``` ### 2. Marketplace - Repasse para Vendedores ```python theme={null} class MarketplacePayouts: """Processa repasses para vendedores de marketplace""" def __init__(self, token: str): self.token = token def process_seller_payouts(self, sales_data: list) -> dict: """Processa repasses baseados em vendas""" results = {'successful': [], 'failed': []} # Agrupar vendas por vendedor seller_totals = self.group_sales_by_seller(sales_data) for seller_id, total_amount in seller_totals.items(): try: # Buscar dados do vendedor seller = self.get_seller_data(seller_id) # Calcular valor após comissão commission = total_amount * 0.10 # 10% de comissão payout_amount = total_amount - commission # Realizar pagamento payment = send_pix_payment( token=self.token, recipient_key=seller['pix_key'], recipient_key_type=seller['pix_key_type'], recipient_name=seller['name'], recipient_document=seller['document'], amount=payout_amount, description=f'Repasse de vendas - {len(sales_data)} transações' ) results['successful'].append({ 'seller': seller['name'], 'gross_amount': total_amount, 'commission': commission, 'net_amount': payout_amount, 'transaction_id': payment['transactionId'] }) # Registrar repasse no banco de dados self.record_payout(seller_id, payment) except Exception as e: results['failed'].append({ 'seller_id': seller_id, 'error': str(e) }) return results def group_sales_by_seller(self, sales_data: list) -> dict: """Agrupa vendas por vendedor""" totals = {} for sale in sales_data: seller_id = sale['seller_id'] totals[seller_id] = totals.get(seller_id, 0) + sale['amount'] return totals ``` ### 3. Sistema de Reembolso ```javascript theme={null} class RefundSystem { constructor(token) { this.token = token; } async processRefund(orderId, refundReason) { // Buscar dados do pedido const order = await this.getOrderData(orderId); // Validar se reembolso é permitido if (!this.canRefund(order)) { throw new Error('Reembolso não permitido para este pedido'); } // Realizar pagamento de volta ao cliente const refund = await sendPixPayment( this.token, order.customer.pixKey, order.customer.pixKeyType, order.customer.name, order.customer.document, order.amount, `Reembolso - Pedido ${orderId} - ${refundReason}` ); // Atualizar status do pedido await this.updateOrderStatus(orderId, 'REFUNDED', refund.transactionId); // Enviar notificação ao cliente await this.notifyCustomer(order.customer.email, refund); return refund; } canRefund(order) { // Verificar se pedido foi pago e ainda está dentro do prazo const daysSincePurchase = (Date.now() - new Date(order.paidAt)) / (1000 * 60 * 60 * 24); return order.status === 'PAID' && daysSincePurchase <= 7; } } ``` ## Validação de Chave PIX Antes de enviar um pagamento, valide o formato da chave PIX: ```typescript theme={null} function validatePixKey(key: string, keyType: string): boolean { switch (keyType) { case 'DOCUMENT': // CPF: 11 dígitos ou CNPJ: 14 dígitos return /^\d{11}$|^\d{14}$/.test(key); case 'EMAIL': return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(key); case 'PHONE': // Formato: +5511999999999 (DDI + DDD + número) return /^55\d{10,11}$/.test(key); case 'RANDOM': // UUID formato: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx return /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(key); default: return false; } } ``` ## Verificação de Saldo Sempre verifique o saldo antes de realizar pagamentos para evitar erros 400. ```typescript theme={null} async function safePayment( token: string, amount: number, recipient: RecipientData ) { // Consultar saldo const balance = await getBalance(token); // Verificar se há saldo suficiente if (balance.netBalance < amount) { throw new Error( `Saldo insuficiente. Disponível: R$ ${balance.netBalance.toFixed(2)} | ` + `Necessário: R$ ${amount.toFixed(2)}` ); } // Prosseguir com pagamento return await sendPixPayment(token, ...recipient, amount); } ``` ## Monitoramento de Status Para acompanhar a confirmação do pagamento: ```javascript theme={null} async function monitorPaymentStatus(transactionId, timeout = 60000) { const startTime = Date.now(); while (Date.now() - startTime < timeout) { const status = await checkTransactionStatus(transactionId); if (status === 'CONFIRMED') { console.log('Pagamento confirmado!'); return true; } if (status === 'ERROR') { throw new Error('Pagamento falhou'); } // Aguardar 2 segundos antes de verificar novamente await new Promise(resolve => setTimeout(resolve, 2000)); } throw new Error('Timeout: Pagamento não confirmado no tempo esperado'); } ``` ## Códigos de Resposta | Código | Descrição | Significado | | ------ | ------------------ | -------------------------------------------- | | `201` | Pagamento Iniciado | Transferência PIX iniciada com sucesso | | `400` | Saldo Insuficiente | Saldo insuficiente para realizar a transação | | `400` | Dados Inválidos | Verifique os campos obrigatórios e formatos | | `401` | Token Inválido | Token não fornecido, expirado ou inválido | Consulte a [Referência da API](/api-reference/endpoints/pix-cash-out) para detalhes completos dos campos de resposta. ## Boas Práticas Consulte o saldo disponível antes de realizar pagamentos para evitar erros. Facilita a conciliação e o rastreamento de pagamentos: `PAY-{timestamp}-{uuid}` Implemente validação local de chaves PIX e documentos antes de enviar a requisição. Em caso de falhas temporárias, implemente lógica de retry com backoff exponencial. ```javascript theme={null} async function retryPayment(paymentFn, maxRetries = 3) { for (let i = 0; i < maxRetries; i++) { try { return await paymentFn(); } catch (error) { if (i === maxRetries - 1) throw error; await new Promise(resolve => setTimeout(resolve, 1000 * Math.pow(2, i))); } } } ``` Mantenha um log completo de todas as tentativas de pagamento para auditoria. ## Observações Importantes * **Valor mínimo:** R\$ 0,01 ## Próximos Passos Realize pagamentos escaneando QR Codes PIX Verifique o saldo antes de realizar pagamentos Receba pagamentos via PIX # PIX Cash-Out via QR Code Source: https://docs.firebanking.dev/api-reference/guides/pix-cash-out-qrcode Como realizar pagamentos PIX escaneando QR Codes ## Visão Geral O endpoint **PIX Cash-Out via QR Code** permite que você realize pagamentos PIX a partir de um QR Code escaneado ou copiado (copia-e-cola). O QR Code deve seguir o padrão EMV do Banco Central do Brasil. Os dados do destinatário são extraídos automaticamente do QR Code, simplificando o processo de pagamento. Este endpoint requer um token Bearer válido. Verifique a [documentação de autenticação](/api-reference/guides/authentication) para mais detalhes. ## Chave PIX vs QR Code: Qual endpoint usar? A API Fire Banking oferece dois endpoints para enviar pagamentos PIX. Escolha o mais adequado para o seu caso de uso: | Critério | Cash-Out por Chave PIX | Cash-Out via QR Code | | -------------------------- | ------------------------------------------- | ---------------------------------------------------- | | **Endpoint** | `POST /api/pix/cash-out` | `POST /api/pix/cash-out-qrcode` | | **Quando usar** | Você conhece a chave PIX do destinatário | Você tem o QR Code gerado pelo recebedor | | **Dados do destinatário** | Obrigatórios (chave, tipo, nome, documento) | Embutidos no QR Code (opcionais no request) | | **Validação de valor** | Apenas saldo e limites | Saldo, limites + valor do QR Code vs valor informado | | **Tipos de chave** | CPF, CNPJ, email, telefone, aleatória | N/A (informação dentro do QR Code) | | **Webhook de confirmação** | Evento `CashOut` | Mesmo evento `CashOut` | | **Resposta** | Mesma estrutura | Mesma estrutura | * **Use Cash-Out por Chave** quando sua aplicação já tem os dados do destinatário (ex: folha de pagamento, repasses programáticos) * **Use Cash-Out via QR Code** quando o pagamento é iniciado a partir de um QR Code escaneado (ex: PDV, pagamento de conta, copia-e-cola) Ambos os endpoints retornam a mesma estrutura de resposta e disparam o mesmo webhook `CashOut` quando confirmados. ## Características * Pagamento via QR Code estático ou dinâmico * Validação automática de valor embutido no QR Code * Verificação de saldo automática antes do envio * Identificação única por `externalId` (idempotência) * Cálculo automático de taxas ## Endpoint ### POST /api/pix/cash-out-qrcode Realiza um pagamento PIX a partir de um QR Code. #### Headers Obrigatórios ``` Authorization: Bearer {token} Content-Type: application/json ``` #### Request Body ```json theme={null} { "value": 15.50, "qrCode": "00020126580014br.gov.bcb.pix0136a1b2c3d4-e5f6-7890-abcd-ef1234567890520400005303986540515.505802BR5925DESTINATARIO LTDA6009SAO PAULO62070503***6304ABCD", "externalId": "QRPAY-987654-20240119", "description": "Pagamento fornecedor XYZ via QR Code", "name": "Destinatario Ltda", "document": "12345678000190" } ``` #### Request ```bash theme={null} curl -X POST https://api.public.firebanking.com.br/api/pix/cash-out-qrcode \ -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..." \ -H "Content-Type: application/json" \ -d '{ "value": 15.50, "qrCode": "00020126580014br.gov.bcb.pix0136a1b2c3d4-e5f6-7890-abcd-ef1234567890520400005303986540515.505802BR5925DESTINATARIO LTDA6009SAO PAULO62070503***6304ABCD", "externalId": "QRPAY-987654-20240119", "description": "Pagamento fornecedor XYZ via QR Code", "name": "Destinatario Ltda", "document": "12345678000190" }' ``` #### Response (201 Created) ```json theme={null} { "transactionId": "456", "externalId": "QRPAY-987654-20240119", "status": "PENDING", "generateTime": "2024-01-19T14:30:00.000Z" } ``` ## Parâmetros da Requisição Valor do pagamento em reais (BRL). Deve ter no máximo 2 casas decimais. Se o QR Code contiver um valor embutido, o valor informado deve corresponder (tolerância de 1 centavo). **Mínimo:** `0.01` **Exemplo:** `15.50` Conteúdo do QR Code PIX (string EMV). Pode ser obtido via escaneamento de câmera ou pelo campo copia-e-cola. **Mínimo:** 50 caracteres **Máximo:** 500 caracteres **Formato:** Deve iniciar com `000201` (padrão EMV PIX do Banco Central) **Exemplo:** `"00020126580014br.gov.bcb.pix0136a1b2c3d4-e5f6-7890-abcd-ef1234567890520400005303986540515.505802BR5925DESTINATARIO LTDA6009SAO PAULO62070503***6304ABCD"` Identificador externo único da transação. Garante idempotência — enviar o mesmo `externalId` duas vezes resulta em erro 409. **Máximo:** 255 caracteres **Recomendação:** Use um formato que garanta unicidade **Exemplo:** `"QRPAY-987654-20240119"` Descrição do pagamento que aparecerá no extrato do destinatário. **Máximo:** 140 caracteres **Padrão:** Vazio **Exemplo:** `"Pagamento fornecedor XYZ via QR Code"` Nome do destinatário. Opcional — quando omitido, os dados do QR Code são utilizados. **Exemplo:** `"Destinatario Ltda"` CPF ou CNPJ do destinatário (apenas números). Opcional — quando omitido, os dados do QR Code são utilizados. **CPF:** 11 dígitos **CNPJ:** 14 dígitos **Exemplo:** `"12345678000190"` ## Estrutura da Resposta ID interno da transação gerada pela Fire Banking. **Exemplo:** `"456"` ID externo fornecido na requisição (mesmo valor do input). **Exemplo:** `"QRPAY-987654-20240119"` Status atual da transação. **Valores possíveis:** * `PENDING`: Pagamento em processamento * `CONFIRMED`: Pagamento confirmado e finalizado * `ERROR`: Erro no processamento **Exemplo:** `"PENDING"` **Nota:** A maioria dos pagamentos PIX é confirmada em poucos segundos Data e hora de criação do pagamento (ISO 8601 UTC). **Exemplo:** `"2024-01-19T14:30:00.000Z"` ## Exemplos de Implementação ### Node.js / TypeScript ```typescript theme={null} import axios from 'axios'; interface CashOutQrCodeRequest { value: number; qrCode: string; externalId: string; description?: string; name?: string; document?: string; } interface CashOutQrCodeResponse { transactionId: string; externalId: string; status: 'PENDING' | 'CONFIRMED' | 'ERROR'; generateTime: string; } async function payWithQrCode( token: string, qrCode: string, amount: number, description?: string ): Promise { const payload: CashOutQrCodeRequest = { value: amount, qrCode, externalId: `QRPAY-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`, description: description || 'Pagamento via QR Code PIX' }; try { const response = await axios.post( 'https://api.public.firebanking.com.br/api/pix/cash-out-qrcode', payload, { headers: { 'Authorization': `Bearer ${token}`, 'Content-Type': 'application/json' } } ); console.log('Pagamento via QR Code iniciado!'); console.log(`ID da Transação: ${response.data.transactionId}`); console.log(`Status: ${response.data.status}`); console.log(`Valor: R$ ${amount.toFixed(2)}`); return response.data; } catch (error) { if (axios.isAxiosError(error)) { const errorData = error.response?.data; console.error('Erro ao realizar pagamento:', errorData); if (error.response?.status === 400) { if (errorData?.code === 'INVALID_QR_CODE') { throw new Error('QR Code inválido ou malformado'); } if (errorData?.code === 'QR_CODE_VALUE_MISMATCH') { throw new Error('Valor informado diverge do valor no QR Code'); } if (errorData?.code === 'INSUFFICIENT_BALANCE') { throw new Error('Saldo insuficiente para realizar o pagamento'); } throw new Error('Dados inválidos: ' + errorData?.message); } if (error.response?.status === 409) { throw new Error('externalId já utilizado em outra transação'); } throw new Error(errorData?.message || 'Erro ao realizar pagamento via QR Code'); } throw error; } } // Uso - Pagamento via QR Code escaneado const qrCodeContent = '00020126580014br.gov.bcb.pix0136a1b2c3d4...6304ABCD'; payWithQrCode('seu_token_aqui', qrCodeContent, 15.50, 'Pagamento fornecedor'); ``` ### Python ```python theme={null} import requests from datetime import datetime from typing import Dict, Optional import uuid def pay_with_qr_code( token: str, qr_code: str, amount: float, description: Optional[str] = None ) -> Dict: """ Envia um pagamento PIX via QR Code Args: token: Token Bearer válido qr_code: Conteúdo do QR Code PIX (string EMV) amount: Valor em reais description: Descrição do pagamento (opcional) Returns: Dados do pagamento iniciado """ url = 'https://api.public.firebanking.com.br/api/pix/cash-out-qrcode' payload = { 'value': round(amount, 2), 'qrCode': qr_code, 'externalId': f'QRPAY-{int(datetime.now().timestamp())}-{uuid.uuid4().hex[:8]}', 'description': description or 'Pagamento via QR Code PIX' } headers = { 'Authorization': f'Bearer {token}', 'Content-Type': 'application/json' } try: response = requests.post(url, json=payload, headers=headers) response.raise_for_status() data = response.json() print('Pagamento via QR Code iniciado!') print(f"ID da Transação: {data['transactionId']}") print(f"Status: {data['status']}") print(f"Valor: R$ {amount:.2f}") return data except requests.exceptions.HTTPError as e: error_data = e.response.json() if e.response else {} if e.response.status_code == 400: code = error_data.get('code', '') if code == 'INVALID_QR_CODE': raise Exception('QR Code inválido ou malformado') if code == 'QR_CODE_VALUE_MISMATCH': raise Exception('Valor informado diverge do valor no QR Code') if code == 'INSUFFICIENT_BALANCE': raise Exception('Saldo insuficiente para realizar o pagamento') raise Exception(f"Dados inválidos: {error_data.get('message')}") if e.response.status_code == 409: raise Exception('externalId já utilizado em outra transação') raise Exception(f"Erro ao realizar pagamento: {error_data.get('message', str(e))}") # Uso token = 'seu_token_aqui' qr_code = '00020126580014br.gov.bcb.pix0136a1b2c3d4...6304ABCD' payment = pay_with_qr_code( token=token, qr_code=qr_code, amount=15.50, description='Pagamento fornecedor XYZ via QR Code' ) ``` ### PHP ```php theme={null} round($amount, 2), 'qrCode' => $qrCode, 'externalId' => 'QRPAY-' . time() . '-' . bin2hex(random_bytes(4)), 'description' => $description ?? 'Pagamento via QR Code PIX' ]; $ch = curl_init($url); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); curl_setopt($ch, CURLOPT_POST, true); curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($payload)); curl_setopt($ch, CURLOPT_HTTPHEADER, [ 'Authorization: Bearer ' . $token, 'Content-Type: application/json' ]); $response = curl_exec($ch); $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); curl_close($ch); if ($httpCode !== 201) { $errorData = json_decode($response, true); $errorCode = $errorData['code'] ?? ''; $errorMessage = $errorData['message'] ?? "HTTP $httpCode"; if ($httpCode === 400) { if ($errorCode === 'INVALID_QR_CODE') { throw new Exception('QR Code inválido ou malformado'); } if ($errorCode === 'QR_CODE_VALUE_MISMATCH') { throw new Exception('Valor informado diverge do valor no QR Code'); } if ($errorCode === 'INSUFFICIENT_BALANCE') { throw new Exception('Saldo insuficiente para realizar o pagamento'); } } if ($httpCode === 409) { throw new Exception('externalId já utilizado em outra transação'); } throw new Exception("Erro ao realizar pagamento: $errorMessage"); } $data = json_decode($response, true); echo "Pagamento via QR Code iniciado!" . PHP_EOL; echo "ID da Transação: {$data['transactionId']}" . PHP_EOL; echo "Status: {$data['status']}" . PHP_EOL; echo "Valor: R$ " . number_format($amount, 2, ',', '.') . PHP_EOL; return $data; } // Uso $token = 'seu_token_aqui'; $qrCode = '00020126580014br.gov.bcb.pix0136a1b2c3d4...6304ABCD'; $payment = payWithQrCode($token, $qrCode, 15.50, 'Pagamento fornecedor XYZ'); ``` ## Casos de Uso ### 1. PDV — Pagar via QR Code no Caixa ```typescript theme={null} class PointOfSalePayment { constructor(private token: string) {} async payFromScannedQrCode(qrCodeContent: string, amount: number) { // Validar QR Code localmente antes de enviar if (!this.isValidPixQrCode(qrCodeContent)) { throw new Error('QR Code inválido. Verifique e tente novamente.'); } const payment = await payWithQrCode( this.token, qrCodeContent, amount, `Pagamento PDV - ${new Date().toLocaleDateString('pt-BR')}` ); console.log(`Pagamento iniciado: ${payment.transactionId}`); return payment; } private isValidPixQrCode(qrCode: string): boolean { return qrCode.length >= 50 && qrCode.startsWith('000201'); } } ``` ### 2. Pagamento de Fornecedor via QR Code ```python theme={null} class SupplierPayment: """Processa pagamentos a fornecedores via QR Code""" def __init__(self, token: str): self.token = token def pay_supplier_invoice(self, qr_code: str, invoice_amount: float, invoice_id: str): """Paga fatura de fornecedor via QR Code""" # Verificar saldo antes balance = get_balance(self.token) if balance['netBalance'] < invoice_amount: raise Exception(f"Saldo insuficiente. Disponível: R$ {balance['netBalance']:.2f}") payment = pay_with_qr_code( token=self.token, qr_code=qr_code, amount=invoice_amount, description=f'Pagamento fatura #{invoice_id}' ) return { 'invoice_id': invoice_id, 'transaction_id': payment['transactionId'], 'status': payment['status'], 'amount': invoice_amount } ``` ### 3. Automação de Pagamentos Recorrentes ```javascript theme={null} class RecurringQrCodePayment { constructor(token) { this.token = token; } async processPaymentBatch(payments) { const results = { successful: [], failed: [] }; for (const payment of payments) { try { const result = await payWithQrCode( this.token, payment.qrCode, payment.amount, payment.description ); results.successful.push({ reference: payment.reference, transactionId: result.transactionId, amount: payment.amount }); // Aguardar entre pagamentos await new Promise(resolve => setTimeout(resolve, 1000)); } catch (error) { results.failed.push({ reference: payment.reference, error: error.message }); } } return results; } } ``` ## Validação do QR Code O QR Code PIX segue o padrão **EMV (Europay, Mastercard, Visa)** definido pelo Banco Central do Brasil. Antes de enviar à API, você pode validar localmente: ```typescript theme={null} function isValidPixQrCode(qrCode: string): boolean { // Verificar tamanho mínimo e máximo if (qrCode.length < 50 || qrCode.length > 500) { return false; } // Verificar prefixo EMV obrigatório if (!qrCode.startsWith('000201')) { return false; } return true; } ``` **Estrutura do QR Code EMV PIX:** * `000201` — Payload Format Indicator (obrigatório) * `0102XX` — Point of Initiation Method (`11` = estático, `12` = dinâmico) * Campos com dados do recebedor, valor, cidade, etc. * `6304XXXX` — CRC16 (checksum de validação) A validação completa do QR Code (decodificação EMV, verificação de CRC e extração de dados) é feita automaticamente pela API. A validação local serve apenas para filtrar QR Codes claramente inválidos. ## Códigos de Resposta | Código | Erro | Descrição | | ------ | ------------------------ | ---------------------------------------------------- | | `201` | — | Pagamento PIX via QR Code iniciado com sucesso | | `400` | `INVALID_QR_CODE` | QR Code inválido ou malformado | | `400` | `QR_CODE_VALUE_MISMATCH` | Valor informado diverge do valor embutido no QR Code | | `400` | `INSUFFICIENT_BALANCE` | Saldo insuficiente para realizar a transação | | `401` | — | Token não fornecido, expirado ou inválido | | `409` | `DUPLICATE_EXTERNAL_ID` | `externalId` já utilizado em outra transação | Consulte a [Referência da API](/api-reference/endpoints/pix-cash-out-qrcode) para detalhes completos dos campos de resposta. ## Boas Práticas Verifique se a string começa com `000201` e tem pelo menos 50 caracteres. Isso evita chamadas desnecessárias à API para QR Codes claramente inválidos. Consulte o saldo disponível antes de enviar o pagamento para evitar erros 400 de saldo insuficiente. ```typescript theme={null} const balance = await getBalance(token); if (balance.netBalance < amount) { throw new Error('Saldo insuficiente'); } ``` Garante idempotência e facilita a conciliação: `QRPAY-{timestamp}-{uuid}` Se enviar o mesmo `externalId` duas vezes, receberá erro 409 — evitando pagamentos duplicados. QR Codes dinâmicos podem não conter valor embutido. Nesse caso, o campo `value` define o valor do pagamento. QR Codes estáticos com valor embutido exigem que o `value` informado corresponda ao valor do QR Code. ## Observações Importantes * **Valor mínimo:** R\$ 0,01 * **Formato do QR Code:** Deve iniciar com `000201` e ter entre 50 e 500 caracteres * **QR Codes dinâmicos:** QR Codes sem valor embutido são aceitos — o campo `value` define o valor do pagamento * **QR Codes estáticos com valor:** O valor informado em `value` deve corresponder ao valor embutido no QR Code (tolerância de 1 centavo) ## Próximos Passos Realize pagamentos informando a chave PIX do destinatário Receba notificações quando o pagamento for confirmado Verifique o saldo antes de realizar pagamentos # PIX Refund-In (Estorno) Source: https://docs.firebanking.dev/api-reference/guides/pix-refund-in Como estornar pagamentos PIX recebidos ## Visão Geral O endpoint **PIX Refund-In** permite que você estorne (devolva) pagamentos PIX recebidos através de cobranças geradas via Cash-In. Os estornos podem ser **totais** ou **parciais** e devem ser solicitados dentro do prazo de **89 dias** após o recebimento. Este endpoint requer um token Bearer válido. Verifique a [documentação de autenticação](/api-reference/guides/authentication) para mais detalhes. ## Características * Estornos totais ou parciais * Múltiplos estornos parciais da mesma transação * Prazo de até 89 dias * Processamento instantâneo * Rastreamento por motivo do estorno ## Quando Usar Estornos Devolve 100% do valor recebido ao pagador original. **Casos de uso:** * Cancelamento completo do pedido * Produto não enviado * Duplicação de pagamento * Erro no valor cobrado Devolve apenas parte do valor recebido. **Casos de uso:** * Devolução de itens específicos * Compensação por problemas no produto/serviço * Ajuste de valores * Desconto retroativo ## Endpoint ### POST /api/pix/refund-in/ Solicita o estorno de um pagamento recebido. #### Headers Obrigatórios ``` Authorization: Bearer {token} Content-Type: application/json ``` #### Path Parameters ID da transação original (Cash-In) a ser estornada. **Exemplo:** `"7845"` #### Request Body ```json theme={null} { "refundValue": 75.00, "reason": "Cliente solicitou devolução de 1 item do pedido" } ``` #### Request ```bash theme={null} curl -X POST https://api.public.firebanking.com.br/api/pix/refund-in/7845 \ -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..." \ -H "Content-Type: application/json" \ -d '{ "refundValue": 75.00, "reason": "Cliente solicitou devolução de 1 item do pedido" }' ``` #### Response (201 Created) ```json theme={null} { "transactionId": "7846", "externalId": "D123456789", "status": "PENDING", "refundValue": 75.00, "providerTransactionId": "7ef4fc3f-a187-495e-857c-e84d70612761", "generateTime": "2024-01-19T16:30:00.000Z" } ``` ## Parâmetros da Requisição Valor a ser estornado em reais (BRL). Deve ter no máximo 2 casas decimais. **Validações:** * Deve ser maior ou igual a 0.01 * Não pode exceder o valor disponível para estorno * Soma de todos os estornos não pode exceder o valor original **Exemplo:** `75.00` Motivo do estorno (opcional, mas recomendado). **Máximo:** 255 caracteres **Exemplo:** `"Cliente solicitou devolução de 1 item do pedido"` **Recomendação:** Sempre forneça um motivo claro para fins de auditoria ID externo para identificação da devolução (opcional). Na API BACEN, corresponde ao parâmetro 'id' da URL. **Exemplo:** `"D123456789"` ## Estrutura da Resposta ID da nova transação de estorno gerada. **Exemplo:** `"7846"` **Nota:** Este é um ID diferente da transação original ID externo da transação de estorno. **Exemplo:** `"D123456789"` Status atual da transação de estorno. **Valores possíveis:** * `PENDING`: Estorno em processamento * `CONFIRMED`: Estorno confirmado e finalizado * `ERROR`: Erro no processamento **Exemplo:** `"PENDING"` Valor do estorno em reais. **Exemplo:** `75.00` ID da transação no provedor (usado para correlação com webhooks). **Exemplo:** `"7ef4fc3f-a187-495e-857c-e84d70612761"` Data e hora de geração do estorno (ISO 8601 UTC). **Exemplo:** `"2024-01-19T16:30:00.000Z"` ## Exemplos de Implementação ### Node.js / TypeScript ```typescript theme={null} import axios from 'axios'; interface RefundRequest { refundValue: number; reason?: string; externalId?: string; } interface RefundResponse { transactionId: string; externalId: string; status: 'PENDING' | 'CONFIRMED' | 'ERROR'; refundValue: number; providerTransactionId: string; generateTime: string; } async function refundPixPayment( token: string, originalTransactionId: string, refundAmount: number, reason?: string ): Promise { const payload: RefundRequest = { refundValue: refundAmount, reason: reason || 'Estorno solicitado pelo cliente' }; try { const response = await axios.post( `https://api.public.firebanking.com.br/api/pix/refund-in/${originalTransactionId}`, payload, { headers: { 'Authorization': `Bearer ${token}`, 'Content-Type': 'application/json' } } ); console.log('Estorno PIX iniciado com sucesso!'); console.log(`ID da Transação de Estorno: ${response.data.transactionId}`); console.log(`ID Externo Original: ${response.data.externalId}`); console.log(`Valor do Estorno: R$ ${response.data.refundValue.toFixed(2)}`); console.log(`Status: ${response.data.status}`); return response.data; } catch (error) { if (axios.isAxiosError(error)) { const errorData = error.response?.data; console.error('Erro ao processar estorno:', errorData); // Tratar erros específicos if (error.response?.status === 400) { if (errorData?.message?.includes('prazo excedido')) { throw new Error('Prazo de 89 dias para estorno foi excedido'); } if (errorData?.message?.includes('valor inválido')) { throw new Error('Valor do estorno excede o disponível para estorno'); } } if (error.response?.status === 404) { throw new Error('Transação original não encontrada'); } throw new Error(errorData?.message || 'Erro ao processar estorno'); } throw error; } } // Uso - Estorno Total async function fullRefund(token: string, transactionId: string, originalValue: number) { return await refundPixPayment( token, transactionId, originalValue, 'Cancelamento total do pedido' ); } // Uso - Estorno Parcial async function partialRefund(token: string, transactionId: string, itemValue: number) { return await refundPixPayment( token, transactionId, itemValue, 'Devolução de 1 item do pedido' ); } // Exemplo prático const token = 'seu_token_aqui'; const transactionId = '7845'; // Estornar R$ 75,00 de uma transação de R$ 150,00 refundPixPayment(token, transactionId, 75.00, 'Cliente solicitou devolução parcial'); ``` ### Python ```python theme={null} import requests from datetime import datetime from typing import Dict, Optional def refund_pix_payment( token: str, original_transaction_id: str, refund_amount: float, reason: Optional[str] = None ) -> Dict: """ Estorna um pagamento PIX recebido Args: token: Token Bearer válido original_transaction_id: ID da transação original (Cash-In) refund_amount: Valor a ser estornado reason: Motivo do estorno (opcional) Returns: Dados do estorno criado """ url = f'https://api.public.firebanking.com.br/api/pix/refund-in/{original_transaction_id}' payload = { 'refundValue': round(refund_amount, 2), 'reason': reason or 'Estorno solicitado pelo cliente' } headers = { 'Authorization': f'Bearer {token}', 'Content-Type': 'application/json' } try: response = requests.post(url, json=payload, headers=headers) response.raise_for_status() data = response.json() print('Estorno PIX iniciado com sucesso!') print(f"ID da Transação de Estorno: {data['transactionId']}") print(f"ID Externo Original: {data['externalId']}") print(f"Valor do Estorno: R$ {data['refundValue']:.2f}") print(f"Status: {data['status']}") return data except requests.exceptions.HTTPError as e: error_data = e.response.json() if e.response else {} error_message = error_data.get('message', str(e)) # Tratar erros específicos if e.response.status_code == 400: if 'prazo excedido' in error_message: raise Exception('Prazo de 89 dias para estorno foi excedido') if 'valor inválido' in error_message: raise Exception('Valor do estorno excede o disponível para estorno') raise Exception(f'Dados inválidos: {error_message}') if e.response.status_code == 404: raise Exception('Transação original não encontrada') raise Exception(f'Erro ao processar estorno: {error_message}') # Uso token = 'seu_token_aqui' transaction_id = '7845' # Estorno parcial refund = refund_pix_payment( token=token, original_transaction_id=transaction_id, refund_amount=75.00, reason='Cliente solicitou devolução de 1 item do pedido' ) # Estorno total def full_refund(token: str, transaction_id: str, original_value: float): """Realiza estorno total""" return refund_pix_payment( token=token, original_transaction_id=transaction_id, refund_amount=original_value, reason='Cancelamento total do pedido' ) ``` ### PHP ```php theme={null} round($refundAmount, 2), 'reason' => $reason ?? 'Estorno solicitado pelo cliente' ]; $ch = curl_init($url); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); curl_setopt($ch, CURLOPT_POST, true); curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($payload)); curl_setopt($ch, CURLOPT_HTTPHEADER, [ 'Authorization: Bearer ' . $token, 'Content-Type: application/json' ]); $response = curl_exec($ch); $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); curl_close($ch); if ($httpCode !== 201) { $errorData = json_decode($response, true); $errorMessage = $errorData['message'] ?? "HTTP $httpCode"; if ($httpCode === 400) { if (stripos($errorMessage, 'prazo excedido') !== false) { throw new Exception('Prazo de 89 dias para estorno foi excedido'); } if (stripos($errorMessage, 'valor inválido') !== false) { throw new Exception('Valor do estorno excede o disponível para estorno'); } } if ($httpCode === 404) { throw new Exception('Transação original não encontrada'); } throw new Exception("Erro ao processar estorno: $errorMessage"); } $data = json_decode($response, true); echo "Estorno PIX iniciado com sucesso!" . PHP_EOL; echo "ID da Transação de Estorno: {$data['transactionId']}" . PHP_EOL; echo "ID Externo Original: {$data['externalId']}" . PHP_EOL; echo "Valor do Estorno: R$ " . number_format($data['refundValue'], 2, ',', '.') . PHP_EOL; echo "Status: {$data['status']}" . PHP_EOL; return $data; } // Uso $token = 'seu_token_aqui'; $transactionId = '7845'; // Estorno parcial $refund = refundPixPayment( $token, $transactionId, 75.00, 'Cliente solicitou devolução de 1 item do pedido' ); ``` ## Casos de Uso ### 1. E-commerce - Devolução de Produtos ```typescript theme={null} class OrderRefundSystem { constructor(private token: string) {} async processItemReturn(orderId: string, returnedItems: OrderItem[]) { // Buscar transação original do pedido const originalTransaction = await this.getTransactionByOrderId(orderId); // Calcular valor total a estornar const refundAmount = returnedItems.reduce( (sum, item) => sum + (item.price * item.quantity), 0 ); // Verificar se não excede o valor da transação original const availableForRefund = await this.getAvailableRefundAmount( originalTransaction.id ); if (refundAmount > availableForRefund) { throw new Error( `Valor solicitado (R$ ${refundAmount.toFixed(2)}) excede o disponível ` + `para estorno (R$ ${availableForRefund.toFixed(2)})` ); } // Gerar descrição do estorno const itemsDescription = returnedItems .map(item => `${item.name} (${item.quantity}x)`) .join(', '); // Realizar estorno const refund = await refundPixPayment( this.token, originalTransaction.id, refundAmount, `Devolução de itens: ${itemsDescription}` ); // Atualizar status do pedido await this.updateOrderStatus(orderId, 'PARTIALLY_REFUNDED', refund); // Notificar cliente await this.notifyCustomerRefund(orderId, refundAmount); return refund; } async getAvailableRefundAmount(transactionId: string): Promise { // Buscar transação original e todos os estornos já realizados const transaction = await this.getTransaction(transactionId); const existingRefunds = await this.getTransactionRefunds(transactionId); const totalRefunded = existingRefunds.reduce( (sum, refund) => sum + refund.value, 0 ); return transaction.value - totalRefunded; } } // Uso interface OrderItem { name: string; price: number; quantity: number; } const refundSystem = new OrderRefundSystem('seu_token_aqui'); const returnedItems: OrderItem[] = [ { name: 'Camiseta Azul', price: 49.90, quantity: 1 } ]; await refundSystem.processItemReturn('ORDER-12345', returnedItems); ``` ### 2. SaaS - Reembolso Proporcional ```python theme={null} from datetime import datetime, timedelta from decimal import Decimal class SubscriptionRefundManager: """Gerencia reembolsos proporcionais de assinaturas""" def __init__(self, token: str): self.token = token def calculate_prorated_refund( self, payment_date: datetime, cancellation_date: datetime, monthly_value: float ) -> float: """Calcula reembolso proporcional baseado em dias não utilizados""" # Calcular dias da mensalidade (30 dias) billing_period_days = 30 # Calcular dias utilizados days_used = (cancellation_date - payment_date).days # Calcular dias não utilizados days_unused = billing_period_days - days_used if days_unused <= 0: return 0.0 # Calcular valor proporcional daily_rate = Decimal(str(monthly_value)) / Decimal(str(billing_period_days)) refund_amount = float(daily_rate * Decimal(str(days_unused))) return round(refund_amount, 2) def process_subscription_cancellation( self, subscription_id: str, transaction_id: str ) -> dict: """Processa cancelamento com reembolso proporcional""" # Buscar dados da assinatura subscription = self.get_subscription(subscription_id) # Calcular reembolso proporcional refund_amount = self.calculate_prorated_refund( payment_date=subscription['last_payment_date'], cancellation_date=datetime.now(), monthly_value=subscription['monthly_value'] ) if refund_amount <= 0: return {'refund': None, 'message': 'Sem valor a reembolsar'} # Realizar estorno refund = refund_pix_payment( token=self.token, original_transaction_id=transaction_id, refund_amount=refund_amount, reason=f'Cancelamento de assinatura - Reembolso proporcional' ) # Atualizar status da assinatura self.update_subscription_status(subscription_id, 'CANCELLED') return refund # Uso manager = SubscriptionRefundManager('seu_token_aqui') # Cliente pagou R$ 99,00 no dia 01/01 e cancelou no dia 15/01 # Reembolso proporcional: 15 dias não utilizados refund = manager.process_subscription_cancellation( subscription_id='SUB-12345', transaction_id='7845' ) ``` ### 3. Marketplace - Compensação por Problemas ```javascript theme={null} class MarketplaceCompensation { constructor(token) { this.token = token; } async compensateForIssue(orderId, issueType) { const order = await this.getOrder(orderId); const compensationRules = this.getCompensationRules(); // Definir valor da compensação baseado no tipo de problema const compensationPercent = compensationRules[issueType] || 0; const compensationAmount = order.value * (compensationPercent / 100); if (compensationAmount === 0) { throw new Error('Tipo de problema não elegível para compensação'); } // Realizar estorno parcial como compensação const refund = await refundPixPayment( this.token, order.transactionId, compensationAmount, `Compensação por ${issueType} - ${compensationPercent}% de desconto` ); // Registrar compensação await this.recordCompensation(orderId, issueType, compensationAmount); return refund; } getCompensationRules() { return { 'ATRASO_ENTREGA': 10, // 10% de compensação 'PRODUTO_AVARIADO': 20, // 20% de compensação 'ITEM_FALTANTE': 15, // 15% de compensação 'QUALIDADE_INFERIOR': 25 // 25% de compensação }; } } // Uso const compensation = new MarketplaceCompensation('seu_token_aqui'); // Produto chegou avariado - compensar com 20% await compensation.compensateForIssue('ORDER-12345', 'PRODUTO_AVARIADO'); ``` ## Validações e Regras de Negócio ### Verificar Valor Disponível para Estorno ```typescript theme={null} async function validateRefundAmount( transactionId: string, requestedAmount: number ): Promise { // Buscar transação original const transaction = await getTransaction(transactionId); // Buscar todos os estornos já realizados const refunds = await getRefundsByTransaction(transactionId); // Calcular total já estornado const totalRefunded = refunds.reduce((sum, refund) => sum + refund.value, 0); // Calcular valor disponível const availableForRefund = transaction.value - totalRefunded; // Validar if (requestedAmount > availableForRefund) { throw new Error( `Valor solicitado (R$ ${requestedAmount.toFixed(2)}) excede o disponível ` + `para estorno (R$ ${availableForRefund.toFixed(2)}). ` + `Total já estornado: R$ ${totalRefunded.toFixed(2)}` ); } return true; } ``` ### Verificar Prazo de Estorno ```python theme={null} from datetime import datetime, timedelta def can_refund_transaction(transaction_date: datetime) -> bool: """Verifica se a transação ainda está dentro do prazo de estorno""" max_refund_days = 89 cutoff_date = datetime.now() - timedelta(days=max_refund_days) if transaction_date < cutoff_date: days_passed = (datetime.now() - transaction_date).days raise Exception( f'Prazo para estorno excedido. ' f'Transação realizada há {days_passed} dias. ' f'Prazo máximo: {max_refund_days} dias.' ) return True # Uso try: can_refund_transaction(datetime(2024, 1, 1)) print('Transação pode ser estornada') except Exception as e: print(f'Erro: {e}') ``` ## Monitoramento de Estornos ```typescript theme={null} class RefundMonitor { async monitorRefundStatus(refundTransactionId: string, timeout = 60000) { const startTime = Date.now(); while (Date.now() - startTime < timeout) { const status = await this.checkRefundStatus(refundTransactionId); if (status === 'CONFIRMED') { console.log('Estorno confirmado!'); await this.onRefundConfirmed(refundTransactionId); return true; } if (status === 'ERROR') { await this.onRefundFailed(refundTransactionId); throw new Error('Estorno falhou'); } // Aguardar 3 segundos antes de verificar novamente await new Promise(resolve => setTimeout(resolve, 3000)); } throw new Error('Timeout: Estorno não confirmado no tempo esperado'); } async onRefundConfirmed(refundTransactionId: string) { // Atualizar banco de dados // Notificar cliente // Registrar log } async onRefundFailed(refundTransactionId: string) { // Notificar equipe de suporte // Registrar incidente // Criar ticket para análise manual } } ``` ## Códigos de Resposta | Código | Descrição | Significado | | ------ | ------------------------ | ------------------------------------------ | | `201` | Estorno Criado | Estorno PIX iniciado com sucesso | | `400` | Valor Inválido | Valor do estorno excede o disponível | | `400` | Prazo Excedido | Prazo de 89 dias para estorno foi excedido | | `401` | Token Inválido | Token não fornecido, expirado ou inválido | | `404` | Transação Não Encontrada | Transação pai não encontrada | Consulte a [Referência da API](/api-reference/endpoints/pix-refund-in) para detalhes completos dos campos de resposta. ## Boas Práticas O motivo do estorno é útil para auditoria e análise de métricas. ```javascript theme={null} // Bom reason: 'Cliente solicitou cancelamento - produto não atendeu expectativas' // Ruim reason: 'Cancelado' ``` Consulte a transação original e estornos anteriores para evitar erros. Mantenha registro de todos os estornos para evitar ultrapassar o valor original. Envie email/SMS informando sobre o estorno e prazo para crédito (geralmente instantâneo). Mantenha um log completo com data, valor, motivo e usuário que solicitou o estorno. ## Observações Importantes Estornos não podem ser cancelados após iniciados. Certifique-se dos valores antes de processar. * **Prazo máximo:** 89 dias após o recebimento * **Valor mínimo:** R\$ 0,01 * **Múltiplos estornos:** Permitidos, desde que a soma não exceda o valor original ## Próximos Passos Crie cobranças para receber pagamentos Verifique o saldo após estornos # Quickstart Source: https://docs.firebanking.dev/api-reference/guides/quickstart Integre com a API Fire Banking em 5 minutos ## Pré-requisitos Antes de começar, você precisa ter: Certificado X.509 (arquivo `.pem`) vinculado à sua conta Credenciais OAuth (`clientId` e `clientSecret`) Solicite suas credenciais e certificado através do [Painel Fire Banking](https://dashboard.firebanking.com.br). ## 1. Configurar Ambiente Crie um arquivo `.env` com suas credenciais: ```bash theme={null} FIREBANKING_CLIENT_ID=account-93-seu-id FIREBANKING_CLIENT_SECRET=sua-senha-secreta FIREBANKING_API_URL=https://api.public.firebanking.com.br ``` Salve seu certificado como `client-cert.pem` no diretório do projeto. ## 2. Instalar Dependências ```bash Node.js theme={null} npm install axios dotenv ``` ```bash Python theme={null} pip install requests python-dotenv ``` ## 3. Código Completo O exemplo abaixo autentica, consulta saldo e cria uma cobrança PIX: ```javascript Node.js theme={null} require('dotenv').config(); const axios = require('axios'); const fs = require('fs'); const API_URL = process.env.FIREBANKING_API_URL; const certificate = fs.readFileSync('./client-cert.pem', 'utf8'); const encodedCert = encodeURIComponent(certificate); // 1. Obter token async function getToken() { const response = await axios.post(`${API_URL}/api/auth/token`, { clientId: process.env.FIREBANKING_CLIENT_ID, clientSecret: process.env.FIREBANKING_CLIENT_SECRET }, { headers: { 'Content-Type': 'application/json', 'X-SSL-Client-Cert': encodedCert } }); return response.data.access_token; } // 2. Consultar saldo async function getBalance(token) { const response = await axios.get(`${API_URL}/api/balance`, { headers: { 'Authorization': `Bearer ${token}` } }); return response.data; } // 3. Criar cobrança PIX async function createPixCharge(token, value, description, externalId, payer) { const response = await axios.post(`${API_URL}/api/pix/cash-in`, { transaction: { value, description, externalId, expirationTime: 3600, // 1 hora generateQrCode: true }, payer: { fullName: payer.name, document: payer.document } }, { headers: { 'Authorization': `Bearer ${token}`, 'Content-Type': 'application/json' } }); return response.data; } // Executar async function main() { try { // Autenticar console.log('Autenticando...'); const token = await getToken(); console.log('Token obtido com sucesso!'); // Consultar saldo console.log('\nConsultando saldo...'); const balance = await getBalance(token); console.log(`Saldo disponível: R$ ${balance.netBalance.toFixed(2)}`); // Criar cobrança console.log('\nCriando cobrança PIX...'); const charge = await createPixCharge(token, 100.00, 'Teste de integração', 'ORDER-001', { name: 'João da Silva', document: '12345678901' }); console.log(`\nCobrança criada!`); console.log(`ID: ${charge.transactionId}`); console.log(`Status: ${charge.status}`); console.log(`PIX Copia e Cola: ${charge.pixCode}`); console.log(`Expira em: ${charge.expirationDate}`); } catch (error) { console.error('Erro:', error.response?.data || error.message); } } main(); ``` ```python Python theme={null} import os import urllib.parse import requests from dotenv import load_dotenv load_dotenv() API_URL = os.getenv('FIREBANKING_API_URL') # Carregar certificado with open('client-cert.pem', 'r') as f: certificate = f.read() encoded_cert = urllib.parse.quote(certificate) # 1. Obter token def get_token(): response = requests.post(f'{API_URL}/api/auth/token', json={ 'clientId': os.getenv('FIREBANKING_CLIENT_ID'), 'clientSecret': os.getenv('FIREBANKING_CLIENT_SECRET') }, headers={ 'Content-Type': 'application/json', 'X-SSL-Client-Cert': encoded_cert } ) response.raise_for_status() return response.json()['access_token'] # 2. Consultar saldo def get_balance(token): response = requests.get(f'{API_URL}/api/balance', headers={'Authorization': f'Bearer {token}'} ) response.raise_for_status() return response.json() # 3. Criar cobrança PIX def create_pix_charge(token, value, description, external_id, payer_name, payer_document): response = requests.post(f'{API_URL}/api/pix/cash-in', json={ 'transaction': { 'value': value, 'description': description, 'externalId': external_id, 'expirationTime': 3600, 'generateQrCode': True }, 'payer': { 'fullName': payer_name, 'document': payer_document } }, headers={ 'Authorization': f'Bearer {token}', 'Content-Type': 'application/json' } ) response.raise_for_status() return response.json() # Executar def main(): try: # Autenticar print('Autenticando...') token = get_token() print('Token obtido com sucesso!') # Consultar saldo print('\nConsultando saldo...') balance = get_balance(token) print(f"Saldo disponível: R$ {balance['netBalance']:.2f}") # Criar cobrança print('\nCriando cobrança PIX...') charge = create_pix_charge( token, 100.00, 'Teste de integração', 'ORDER-001', 'João da Silva', '12345678901' ) print(f"\nCobrança criada!") print(f"ID: {charge['transactionId']}") print(f"Status: {charge['status']}") print(f"PIX Copia e Cola: {charge['pixCode']}") print(f"Expira em: {charge['expirationDate']}") except requests.exceptions.RequestException as e: print(f'Erro: {e.response.json() if e.response else e}') if __name__ == '__main__': main() ``` ## 4. Executar ```bash Node.js theme={null} node quickstart.js ``` ```bash Python theme={null} python quickstart.py ``` **Saída esperada:** ``` Autenticando... Token obtido com sucesso! Consultando saldo... Saldo disponível: R$ 48734.90 Criando cobrança PIX... Cobrança criada! ID: 7845 Status: PENDING PIX Copia e Cola: 00020126580014br.gov.bcb.pix... Expira em: 2024-01-20T14:30:00.000Z ``` ## 5. Receber Notificações (Webhook) Configure um endpoint para receber notificações quando o pagamento for confirmado: ```javascript theme={null} // Express.js app.post('/webhook/firebanking', (req, res) => { const { event, transactionId, status, finalAmount } = req.body; if (event === 'CashIn' && status === 'CONFIRMED') { console.log(`Pagamento ${transactionId} confirmado: R$ ${finalAmount}`); // Atualizar pedido no seu sistema } res.status(200).send('OK'); }); ``` Configure a URL do webhook no [Painel Fire Banking](https://dashboard.firebanking.com.br). Veja o [Guia de Webhooks](/api-reference/guides/webhooks) para mais detalhes. ## Próximos Passos Entenda o fluxo de autenticação em detalhes Explore todas as opções de cobrança PIX Envie pagamentos PIX Pague via QR Code PIX Configure notificações em tempo real ## Troubleshooting Verifique se: * O arquivo `client-cert.pem` existe no diretório * O certificado está sendo enviado URL-encoded * O header `X-SSL-Client-Cert` está presente Verifique se: * As variáveis de ambiente estão configuradas corretamente * O `clientId` e `clientSecret` estão corretos * O certificado está vinculado à sua conta Entre em contato com o suporte Fire Banking para vincular o certificado à sua conta. O token expira em 30 minutos. Implemente renovação automática: ```javascript theme={null} // Renovar token antes de expirar if (tokenExpiresAt < Date.now() + 30000) { token = await getToken(); } ``` # Transações por Chave PIX Source: https://docs.firebanking.dev/api-reference/guides/transactions-by-pix-key Consulte e filtre transações associadas a uma chave PIX específica com paginação ## Visão Geral Os endpoints de transações por chave PIX permitem consultar o histórico de transações associadas a uma chave PIX específica (CPF, CNPJ, telefone, e-mail ou chave aleatória EVP). São ideais para cenários como: * **Reconciliação por chave**: verificar todos os recebimentos de um CNPJ ou CPF específico * **Auditoria**: auditar movimentações de uma chave PIX em um período * **Monitoramento de recebimentos**: acompanhar pagamentos recebidos via uma chave específica Comparado ao endpoint geral `/api/transactions`, a diferença principal é o filtro obrigatório por chave PIX e o `size` máximo maior: até **1000 registros por página** (vs. 100 no endpoint geral). A consulta retorna no máximo 1000 resultados no total — se a chave tiver mais transações no período, use filtros de `type`, `status` ou períodos menores para refinar. ## Autenticação Este endpoint requer um token Bearer válido no header `Authorization`: ```bash theme={null} Authorization: Bearer ``` O token deve ser obtido através do endpoint [Gerar Token](/api-reference/endpoints/generate-token). ## Tipos de Chave PIX Suportados | Tipo | Formato | Exemplo | | --------------------- | ----------------------------------------- | -------------------------------------- | | CPF | Apenas números (11 dígitos) | `12345678900` | | CNPJ | Apenas números (14 dígitos) | `12345678000190` | | Telefone | Formato E.164 com `+` encodado como `%2B` | `%2B5511999999999` | | E-mail | Endereço de e-mail válido | `joao@example.com` | | Chave aleatória (EVP) | UUID v4 | `550e8400-e29b-41d4-a716-446655440000` | Ao usar chaves com caracteres especiais na URL (como `+` no telefone), sempre aplique URL encoding. Nos exemplos de código, `encodeURIComponent` (JavaScript) e `quote(..., safe="")` (Python) fazem isso automaticamente. *** ## Endpoint 1: Listar Transações por Chave PIX ``` GET /api/pix/transactions/pix-key/{pixKey} ``` ### Parâmetros | Parâmetro | Tipo | Obrigatório | Padrão | Descrição | | ----------- | ------- | ----------- | ------------------- | -------------------------------------------------- | | `pixKey` | string | ✓ (path) | — | Chave PIX (URL-encoded) | | `page` | integer | — | `1` | Número da página | | `size` | integer | — | `20` | Registros por página (máx. **1000**) | | `status` | string | — | — | `PENDING`, `CONFIRMED` ou `ERROR` | | `type` | string | — | — | `PAYMENT`, `WITHDRAW`, `REFUND_IN` ou `REFUND_OUT` | | `startDate` | date | — | Últimos **30 dias** | Data inicial (ISO 8601) | | `endDate` | date | — | Hoje | Data final (ISO 8601) | O intervalo entre `startDate` e `endDate` não pode exceder **31 dias**. A consulta retorna no máximo **1000 resultados** — se houver mais transações no período, use filtros adicionais (`type`, `status`, ou períodos menores) para refinar. ### Chave sem resultados Se a `pixKey` informada não tiver transações no período consultado, a API retorna HTTP 200 com `data` vazio: ```json theme={null} { "data": [], "metadata": { "page": 1, "size": 20, "total": 0, "totalPages": 0, "hasNext": false, "hasPrevious": false } } ``` ### Exemplos ```bash cURL theme={null} curl -X GET "https://api.public.firebanking.com.br/api/pix/transactions/pix-key/joao%40example.com?status=CONFIRMED&page=1&size=50" \ -H "Authorization: Bearer seu_token_aqui" ``` ```javascript Node.js theme={null} const pixKey = 'joao@example.com'; const params = new URLSearchParams({ status: 'CONFIRMED', page: '1', size: '50' }); const response = await fetch( `https://api.public.firebanking.com.br/api/pix/transactions/pix-key/${encodeURIComponent(pixKey)}?${params}`, { headers: { 'Authorization': 'Bearer seu_token_aqui' } } ); const data = await response.json(); console.log(data); ``` ```python Python theme={null} import requests from urllib.parse import quote pix_key = 'joao@example.com' response = requests.get( f'https://api.public.firebanking.com.br/api/pix/transactions/pix-key/{quote(pix_key, safe="")}', params={ 'status': 'CONFIRMED', 'page': 1, 'size': 50 }, headers={ 'Authorization': 'Bearer seu_token_aqui' } ) data = response.json() print(data) ``` ### Exemplo de Resposta ```json theme={null} { "data": [ { "transactionId": "12345", "externalId": "order-abc123", "status": "Confirmado", "operationType": "Pix in", "movementType": "CREDIT", "originalAmount": 100.00, "feeAmount": 1.00, "finalAmount": 99.00, "endToEndId": "E00416968202501151030VX5Sx8fIpkY", "createdAt": "2025-01-15T10:30:00.000Z", "processedAt": "2025-01-15T10:30:05.000Z", "counterpart": { "name": "João Silva", "document": "***.456.789-**", "bank": { "bankISPB": "00000000", "bankName": "Banco do Brasil", "bankCode": "001", "accountBranch": "0001", "accountNumber": "123456-7" } } } ], "metadata": { "page": 1, "size": 50, "total": 320, "totalPages": 7, "hasNext": true, "hasPrevious": false } } ``` ### Paginação | Campo | Descrição | | ------------- | ------------------------------------------ | | `page` | Página atual | | `size` | Quantidade de registros por página | | `total` | Total de registros encontrados (máx. 1000) | | `totalPages` | Total de páginas disponíveis | | `hasNext` | Indica se existe próxima página | | `hasPrevious` | Indica se existe página anterior | Para obter todos os resultados de uma vez, use `size=1000`. Para processar em lotes, navegue pelas páginas enquanto `hasNext` for `true`: ```javascript theme={null} async function getAllTransactions(pixKey, filters = {}) { const results = []; let page = 1; do { const params = new URLSearchParams({ ...filters, page, size: 100 }); const response = await fetch( `https://api.public.firebanking.com.br/api/pix/transactions/pix-key/${encodeURIComponent(pixKey)}?${params}`, { headers: { 'Authorization': 'Bearer seu_token_aqui' } } ); const { data, metadata } = await response.json(); results.push(...data); if (!metadata.hasNext) break; page++; } while (true); return results; } ``` *** ## Endpoint 2: Consultar Transação por Chave PIX e Identificador ``` GET /api/pix/transactions/pix-key/{pixKey}/{identifier} ``` ### Parâmetros | Parâmetro | Tipo | Obrigatório | Descrição | | ------------ | ------ | ----------- | ---------------------------------------- | | `pixKey` | string | ✓ | Chave PIX (URL-encoded) | | `identifier` | string | ✓ | Identificador da transação (URL-encoded) | O `identifier` é comparado simultaneamente contra três campos da transação: * **`endToEndId`** — End-to-End ID do PIX (ex: `E00416968202501151030VX5Sx8fIpkY`) * **`externalId`** — Identificador externo fornecido na criação da transação * **`id` numérico** — ID interno da transação na Fire Banking (apenas se o valor for puramente numérico) Na prática não há ambiguidade: o formato de cada tipo de identificador é único (e2eId começa com `E` seguido de 32 caracteres alfanuméricos; id é puramente numérico; externalId é qualquer string). ### Exemplos ```bash cURL (endToEndId) theme={null} curl -X GET "https://api.public.firebanking.com.br/api/pix/transactions/pix-key/joao%40example.com/E00416968202501151030VX5Sx8fIpkY" \ -H "Authorization: Bearer seu_token_aqui" ``` ```bash cURL (externalId) theme={null} curl -X GET "https://api.public.firebanking.com.br/api/pix/transactions/pix-key/joao%40example.com/order-abc123" \ -H "Authorization: Bearer seu_token_aqui" ``` ```javascript Node.js theme={null} const pixKey = 'joao@example.com'; const identifier = 'E00416968202501151030VX5Sx8fIpkY'; const response = await fetch( `https://api.public.firebanking.com.br/api/pix/transactions/pix-key/${encodeURIComponent(pixKey)}/${encodeURIComponent(identifier)}`, { headers: { 'Authorization': 'Bearer seu_token_aqui' } } ); if (response.status === 404) { console.log('Transação não encontrada'); } else { const transaction = await response.json(); console.log(transaction); } ``` ```python Python theme={null} import requests from urllib.parse import quote pix_key = 'joao@example.com' identifier = 'E00416968202501151030VX5Sx8fIpkY' response = requests.get( f'https://api.public.firebanking.com.br/api/pix/transactions/pix-key/{quote(pix_key, safe="")}/{quote(identifier, safe="")}', headers={ 'Authorization': 'Bearer seu_token_aqui' } ) if response.status_code == 404: print('Transação não encontrada') else: transaction = response.json() print(transaction) ``` ### Resposta 200 — Transação Encontrada ```json theme={null} { "transactionId": "12345", "externalId": "order-abc123", "status": "Confirmado", "operationType": "Pix in", "movementType": "CREDIT", "originalAmount": 100.00, "feeAmount": 1.00, "finalAmount": 99.00, "endToEndId": "E00416968202501151030VX5Sx8fIpkY", "createdAt": "2025-01-15T10:30:00.000Z", "processedAt": "2025-01-15T10:30:05.000Z", "counterpart": { "name": "João Silva", "document": "***.456.789-**", "bank": { "bankISPB": "00000000", "bankName": "Banco do Brasil", "bankCode": "001", "accountBranch": "0001", "accountNumber": "123456-7" } } } ``` ### Resposta 404 — Não Encontrada Retornado quando nenhuma transação corresponde ao `identifier` informado para a `pixKey` especificada. ```json theme={null} { "statusCode": 404, "message": "Transação não encontrada" } ``` *** ## Mapeamento de Campos ### Status | Valor Interno | Valor Retornado | | ------------- | --------------- | | `PENDING` | `Pendente` | | `CONFIRMED` | `Confirmado` | | `ERROR` | `Error` | ### Tipo de Operação | Valor Interno | Valor Retornado | Descrição | | ------------- | --------------- | ---------------------------- | | `PAYMENT` | `Pix in` | Recebimento via PIX | | `WITHDRAW` | `Pix out` | Pagamento via PIX | | `REFUND_IN` | `Refund in` | Estorno solicitado (débito) | | `REFUND_OUT` | `Refund out` | Devolução recebida (crédito) | ### Tipo de Movimento | Tipo de Operação | Movimento | | ---------------- | --------- | | `Pix in` | `CREDIT` | | `Pix out` | `DEBIT` | | `Refund in` | `DEBIT` | | `Refund out` | `CREDIT` | *** ## Diferenças em Relação a `/api/transactions` | Aspecto | `/api/transactions` | `/api/pix/transactions/pix-key/{pixKey}` | | ---------------------------------- | -------------------------- | ---------------------------------------- | | Filtro principal | Toda a conta | Por chave PIX específica | | Máx. `size` por página | 100 | **1000** | | Máx. resultados totais | Sem limite | **1000** | | Default `startDate` | Últimos 31 dias | Últimos **30 dias** | | Chave inexistente / sem resultados | 200 com lista vazia | 200 com lista vazia | | Busca por `externalId` | Query param `?externalId=` | Path param `/{identifier}` | | Busca por `endToEndId` | Query param `?endToEndId=` | Path param `/{identifier}` | *** ## Casos de Uso Verificar todos os recebimentos confirmados de um CNPJ em um período: ```bash theme={null} curl -X GET "https://api.public.firebanking.com.br/api/pix/transactions/pix-key/12345678000190?type=PAYMENT&status=CONFIRMED&startDate=2025-01-01&endDate=2025-01-31&size=1000" \ -H "Authorization: Bearer seu_token" ``` Confirmar se um pagamento PIX específico foi recebido pela chave: ```bash theme={null} curl -X GET "https://api.public.firebanking.com.br/api/pix/transactions/pix-key/joao%40example.com/E00416968202501151030VX5Sx8fIpkY" \ -H "Authorization: Bearer seu_token" ``` Buscar todas as movimentações de um CNPJ nos últimos 30 dias (padrão): ```bash theme={null} curl -X GET "https://api.public.firebanking.com.br/api/pix/transactions/pix-key/12345678000190?size=1000" \ -H "Authorization: Bearer seu_token" ``` Monitorar recebimentos de uma chave de telefone em uma semana específica: ```bash theme={null} curl -X GET "https://api.public.firebanking.com.br/api/pix/transactions/pix-key/%2B5511999999999?type=PAYMENT&startDate=2025-01-13&endDate=2025-01-19" \ -H "Authorization: Bearer seu_token" ``` *** ## Códigos de Erro | Código | Descrição | | ------ | ---------------------------------------------------------------------- | | `400` | Parâmetros inválidos, datas invertidas, ou intervalo excede 31 dias | | `401` | Token não fornecido ou inválido | | `404` | Transação não encontrada (apenas no endpoint `/{pixKey}/{identifier}`) | *** ## Próximos Passos Consulte transações gerais da conta com filtros avançados Verifique o status detalhado de uma transação específica Receba notificações automáticas quando transações chegarem # Buscar Transações Source: https://docs.firebanking.dev/api-reference/guides/transactions-search Consulte e filtre transações PIX da sua conta com paginação ## Visão Geral O endpoint de busca de transações permite consultar o histórico de transações PIX da sua conta com diversos filtros e paginação. Os dados são retornados em um formato amigável, com status e tipos traduzidos para português e valores em reais. ## Autenticação Este endpoint requer um token Bearer válido no header `Authorization`: ```bash theme={null} Authorization: Bearer ``` O token deve ser obtido através do endpoint [Gerar Token](/api-reference/endpoints/generate-token). ## Parâmetros de Consulta | Parâmetro | Tipo | Descrição | | ------------ | ------- | ----------------------------------------------------------------- | | `page` | integer | Número da página (padrão: 1) | | `size` | integer | Registros por página (padrão: 20, máximo: 100) | | `status` | string | Filtro por status: `PENDING`, `CONFIRMED`, `ERROR` | | `type` | string | Filtro por tipo: `PAYMENT`, `WITHDRAW`, `REFUND_IN`, `REFUND_OUT` | | `startDate` | date | Data inicial (ISO 8601). Padrão: últimos 31 dias | | `endDate` | date | Data final (ISO 8601). Padrão: hoje | | `externalId` | string | Filtro por seu identificador externo | | `endToEndId` | string | Filtro por End-to-End ID do PIX | O intervalo entre `startDate` e `endDate` não pode exceder **31 dias**. ## Mapeamento de Campos ### Status Os status internos são traduzidos para o formato público: | Valor Interno | Valor Retornado | | ------------- | --------------- | | `PENDING` | `Pendente` | | `CONFIRMED` | `Confirmado` | | `ERROR` | `Error` | ### Tipo de Operação Os tipos de transação são traduzidos para português: | Valor Interno | Valor Retornado | Descrição | | ------------- | --------------- | ---------------------------- | | `PAYMENT` | `Pix in` | Recebimento via PIX | | `WITHDRAW` | `Pix out` | Pagamento via PIX | | `REFUND_IN` | `Refund in` | Estorno solicitado (débito) | | `REFUND_OUT` | `Refund out` | Devolução recebida (crédito) | ### Tipo de Movimento Indica se a transação é entrada ou saída na conta: | Tipo de Operação | Movimento | | ---------------- | --------- | | `Pix in` | `CREDIT` | | `Pix out` | `DEBIT` | | `Refund in` | `DEBIT` | | `Refund out` | `CREDIT` | ### Valores Todos os valores monetários são retornados em **reais** com 2 casas decimais: * `originalAmount`: Valor original da transação * `feeAmount`: Taxa aplicada * `finalAmount`: Valor final (original ± taxa) ### Mascaramento de Documento Por segurança, documentos de contrapartes são mascarados: * **CPF**: `123.456.789-00` → `***.456.789-**` * **CNPJ**: `12.345.678/0001-90` → `**.345.678/****-**` ## Exemplo de Uso ### Buscar todas as transações confirmadas ```bash cURL theme={null} curl -X GET "https://api.public.firebanking.com.br/api/transactions?status=CONFIRMED&page=1&size=10" \ -H "Authorization: Bearer seu_token_aqui" ``` ```javascript Node.js theme={null} const response = await fetch( 'https://api.public.firebanking.com.br/api/transactions?status=CONFIRMED&page=1&size=10', { headers: { 'Authorization': 'Bearer seu_token_aqui' } } ); const data = await response.json(); console.log(data); ``` ```python Python theme={null} import requests response = requests.get( 'https://api.public.firebanking.com.br/api/transactions', params={ 'status': 'CONFIRMED', 'page': 1, 'size': 10 }, headers={ 'Authorization': 'Bearer seu_token_aqui' } ) data = response.json() print(data) ``` ### Buscar transações por período ```bash cURL theme={null} curl -X GET "https://api.public.firebanking.com.br/api/transactions?startDate=2025-01-01&endDate=2025-01-15&type=PAYMENT" \ -H "Authorization: Bearer seu_token_aqui" ``` ```javascript Node.js theme={null} const params = new URLSearchParams({ startDate: '2025-01-01', endDate: '2025-01-15', type: 'PAYMENT' }); const response = await fetch( `https://api.public.firebanking.com.br/api/transactions?${params}`, { headers: { 'Authorization': 'Bearer seu_token_aqui' } } ); ``` ### Buscar por identificador específico ```bash theme={null} # Por externalId (seu identificador) curl -X GET "https://api.public.firebanking.com.br/api/transactions?externalId=order-12345" \ -H "Authorization: Bearer seu_token_aqui" # Por endToEndId (ID do PIX) curl -X GET "https://api.public.firebanking.com.br/api/transactions?endToEndId=E12345678901234567890123456789012" \ -H "Authorization: Bearer seu_token_aqui" ``` ## Exemplo de Resposta ```json theme={null} { "data": [ { "transactionId": "12345", "externalId": "order-abc123", "status": "Confirmado", "operationType": "Pix in", "movementType": "CREDIT", "originalAmount": 100.00, "feeAmount": 1.00, "finalAmount": 99.00, "endToEndId": "E12345678901234567890123456789012", "createdAt": "2025-01-15T10:30:00.000Z", "processedAt": "2025-01-15T10:30:05.000Z", "counterpart": { "name": "João Silva", "document": "***.456.789-**", "bank": { "bankISPB": "00000000", "bankName": "Banco do Brasil", "bankCode": "001", "accountBranch": "0001", "accountNumber": "123456-7" } } } ], "metadata": { "page": 1, "size": 20, "total": 150, "totalPages": 8, "hasNext": true, "hasPrevious": false } } ``` ## Paginação A resposta inclui metadados de paginação para facilitar a navegação: | Campo | Descrição | | ------------- | ---------------------------------- | | `page` | Página atual | | `size` | Quantidade de registros por página | | `total` | Total de registros encontrados | | `totalPages` | Total de páginas disponíveis | | `hasNext` | Indica se existe próxima página | | `hasPrevious` | Indica se existe página anterior | ### Navegação entre páginas ```javascript theme={null} // Primeira página const page1 = await fetchTransactions({ page: 1, size: 20 }); if (page1.metadata.hasNext) { // Próxima página const page2 = await fetchTransactions({ page: 2, size: 20 }); } ``` ## Casos de Uso Comuns ```bash theme={null} curl -X GET "https://api.public.firebanking.com.br/api/transactions?type=PAYMENT&status=CONFIRMED&startDate=2025-01-15&endDate=2025-01-15" \ -H "Authorization: Bearer seu_token" ``` ```bash theme={null} curl -X GET "https://api.public.firebanking.com.br/api/transactions?status=PENDING" \ -H "Authorization: Bearer seu_token" ``` ```bash theme={null} curl -X GET "https://api.public.firebanking.com.br/api/transactions?type=REFUND_IN" \ -H "Authorization: Bearer seu_token" ``` ```bash theme={null} curl -X GET "https://api.public.firebanking.com.br/api/transactions?externalId=MEU-PEDIDO-123" \ -H "Authorization: Bearer seu_token" ``` ## Códigos de Erro | Código | Descrição | | ------ | --------------------------------------------------------- | | `400` | Parâmetros inválidos ou intervalo de datas excede 31 dias | | `401` | Token não fornecido ou inválido | ## Próximos Passos Consulte o status detalhado de uma transação específica Reenvie notificações de transações para seu sistema Filtre transações por chave PIX específica com paginação # Reenvio de Webhooks Source: https://docs.firebanking.dev/api-reference/guides/webhook-resend Reenvie webhooks de transações manualmente quando necessário ## Visão Geral O endpoint de **Reenvio de Webhook** permite que você solicite o reenvio manual de notificações de transações específicas. Isso é útil em cenários onde: * Seu servidor estava indisponível quando o webhook original foi enviado * Você precisa reprocessar uma transação específica * Deseja testar a integração com uma URL diferente temporariamente Este endpoint não altera a configuração de webhook da sua conta. A URL fornecida é usada apenas para o reenvio específico. *** ## Funcionamento ### Identificação da Transação O endpoint aceita três tipos de identificadores: | Tipo | Descrição | Escopo | | ----------------- | ------------------------------------------------------------------------ | ------------------- | | **ID Numérico** | ID interno da transação (campo `transactionId` nos webhooks) | Global | | **ID Externo** | Identificador fornecido por você na criação (campo `externalId`) | Único por conta | | **End-to-End ID** | Identificador PIX do BACEN (campo `endToEndId`, formato: E/D + 32 chars) | Único por transação | O sistema busca simultaneamente por todos os tipos de identificador na sua conta. Na prática não há ambiguidade: id numérico é puramente dígitos; e2eId começa com 'E' ou 'D' seguido de 32 caracteres alfanuméricos; externalId é qualquer string fornecida por você. **Qual identificador usar?** Use o `transactionId` numérico retornado pela Fire Banking, o `externalId` que você forneceu na criação da transação, ou o `endToEndId` do PIX recebido nos webhooks. Todos são igualmente válidos. ### Processamento Síncrono O reenvio de webhook é processado de forma **síncrona**. Isso significa que: * A requisição aguarda o envio do webhook ser concluído * O resultado é comunicado via HTTP status code (200, 502, 504) * O tempo de resposta depende da latência do seu servidor (timeout: 10s) ```mermaid theme={null} flowchart TD A[Requisição de Reenvio] --> B{URL fornecida?} B -->|Sim| C[Usa URL temporária] B -->|Não| D{Webhook configurado?} D -->|Sim| E[Usa URL configurada] D -->|Não| F[Erro 400: Sem URL] C --> G[Envia Webhook HTTP] E --> G G --> H{Resposta do servidor} H -->|2xx| I[HTTP 200: Sucesso] H -->|4xx/5xx| J[HTTP 502: Bad Gateway] H -->|Timeout| K[HTTP 504: Gateway Timeout] I --> L[Registra log de auditoria] J --> L K --> L L --> M[Retorna resultado] ``` Diferentemente dos webhooks automáticos (que utilizam filas com retry), o reenvio manual é executado imediatamente e retorna o resultado na mesma requisição. *** ## Casos de Uso ### 1. Reenvio para URL Configurada Se você já tem um webhook configurado na sua conta, basta chamar o endpoint sem body: ```bash theme={null} curl -X POST https://api.public.firebanking.com.br/api/resend-webhook/external-teste-001 \ -H "Authorization: Bearer SEU_TOKEN" \ -H "Content-Type: application/json" ``` ### 2. Reenvio com URL Temporária Para testar com uma URL diferente ou reenviar para um endpoint de contingência: ```bash theme={null} curl -X POST https://api.public.firebanking.com.br/api/resend-webhook/external-teste-001 \ -H "Authorization: Bearer SEU_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "url": "https://meu-servidor-backup.com/webhooks/firebanking" }' ``` A URL temporária **não é persistida**. O próximo webhook automático será enviado para a URL configurada na conta. *** ## Resposta ### Sucesso (200) Webhook enviado com sucesso para a URL de destino. ```json theme={null} { "message": "Webhook resent successfully", "webhookLogId": 12345, "sentAt": "2024-01-15T10:30:00.000Z", "statusCode": 200 } ``` ### Erro: Sem URL Configurada (400) ```json theme={null} { "statusCode": 400, "message": "No webhook configured and no override URL provided", "error": "Bad Request" } ``` ### Erro: Transação Não Encontrada (404) ```json theme={null} { "statusCode": 404, "message": "Transaction not found", "error": "Not Found" } ``` ### Erro: Destino Retornou Erro (502) O servidor de destino retornou um erro (4xx ou 5xx) ou houve falha de conexão. ```json theme={null} { "statusCode": 502, "message": "Webhook failed with status 500", "webhookLogId": 12345, "sentAt": "2024-01-15T10:30:00.000Z" } ``` Mesmo em caso de erro, o webhook é registrado no log de auditoria. Use o `webhookLogId` para rastreamento. ### Erro: Timeout (504) O servidor de destino não respondeu dentro do tempo limite (10 segundos). ```json theme={null} { "statusCode": 504, "message": "Timeout after 10000ms", "webhookLogId": 12345, "sentAt": "2024-01-15T10:30:00.000Z" } ``` Se você está recebendo timeouts frequentes, verifique se seu servidor está respondendo em menos de 10 segundos. *** ## Rate Limiting Este endpoint possui rate limiting de **60 requisições por minuto** por conta para evitar abusos. Se o limite for excedido, você receberá um erro `429 Too Many Requests`: ```json theme={null} { "statusCode": 429, "message": "Too Many Requests" } ``` *** ## Auditoria Todos os reenvios manuais são registrados para fins de auditoria e rastreabilidade: | Informação | Descrição | | ------------- | --------------------------------------------------- | | Tipo de envio | Marcado como reenvio manual | | URL utilizada | Registra se foi usada URL temporária ou configurada | | Resultado | Status HTTP e tempo de resposta | | Identificador | ID único do log para rastreamento | Use o `webhookLogId` retornado na resposta para correlacionar com logs de suporte se necessário. *** ## Exemplos de Integração ```javascript Node.js theme={null} const axios = require('axios'); async function resendWebhook(transactionId, overrideUrl = null) { const config = { headers: { 'Authorization': `Bearer ${process.env.FIREBANKING_TOKEN}`, 'Content-Type': 'application/json' } }; const body = overrideUrl ? { url: overrideUrl } : {}; try { const response = await axios.post( `https://api.public.firebanking.com.br/api/resend-webhook/${transactionId}`, body, config ); console.log('Webhook reenviado:', response.data); return response.data; } catch (error) { console.error('Erro ao reenviar webhook:', error.response?.data); throw error; } } // Uso resendWebhook('external-teste-001'); resendWebhook('external-teste-001', 'https://backup.meusite.com/webhook'); // Com URL temporária ``` ```python Python theme={null} import requests import os def resend_webhook(transaction_id: str, override_url: str = None): headers = { 'Authorization': f'Bearer {os.environ["FIREBANKING_TOKEN"]}', 'Content-Type': 'application/json' } body = {'url': override_url} if override_url else {} response = requests.post( f'https://api.public.firebanking.com.br/api/resend-webhook/{transaction_id}', json=body, headers=headers ) response.raise_for_status() return response.json() # Uso result = resend_webhook('external-teste-001') print(f"Webhook reenviado: {result}") # Com URL temporária result = resend_webhook('external-teste-001', 'https://backup.meusite.com/webhook') ``` ```csharp C# theme={null} using System.Net.Http; using System.Text; using System.Text.Json; public class FireBankingClient { private readonly HttpClient _client; private readonly string _token; public FireBankingClient(string token) { _client = new HttpClient(); _token = token; _client.DefaultRequestHeaders.Add("Authorization", $"Bearer {_token}"); } public async Task ResendWebhookAsync( string transactionId, string overrideUrl = null) { var url = $"https://api.public.firebanking.com.br/api/resend-webhook/{transactionId}"; var body = overrideUrl != null ? JsonSerializer.Serialize(new { url = overrideUrl }) : "{}"; var content = new StringContent(body, Encoding.UTF8, "application/json"); var response = await _client.PostAsync(url, content); response.EnsureSuccessStatusCode(); var json = await response.Content.ReadAsStringAsync(); return JsonSerializer.Deserialize(json); } } ``` *** ## Próximos Passos Entenda como os webhooks funcionam na Fire Banking Guia completo de implementação de webhooks # CashIn Source: https://docs.firebanking.dev/api-reference/guides/webhooks/cash-in Evento de recebimento PIX confirmado ## Visão Geral O evento **CashIn** é enviado quando um pagamento PIX é **recebido** com sucesso na sua conta. Este é o evento mais comum e indica que o dinheiro está disponível. O `movementType` para CashIn é sempre `CREDIT`, indicando entrada de recursos na conta. | Campo | Valor | | -------------- | ---------------------------- | | `event` | `CashIn` | | `movementType` | `CREDIT` | | Significado | Dinheiro entrou na sua conta | *** ## Payload Completo ```json theme={null} { "event": "CashIn", "status": "CONFIRMED", "transactionType": "PIX", "movementType": "CREDIT", "transactionId": "12345", "externalId": "PIX-5482123298-EJUYFSMU1UU", "endToEndId": "E00416968202512111942rjzxxzSSTD9", "pixKey": "1ff6ce09-4244-44d5-aa8f-1fe69f8986a9", "feeAmount": 0.01, "originalAmount": 0.5, "finalAmount": 0.49, "processingDate": "2025-12-11T19:42:04.080Z", "errorCode": null, "errorMessage": null, "counterpart": { "name": "Carlos Oliveira", "document": "*.345.678-**", "bank": { "bankISPB": null, "bankName": null, "bankCode": null, "accountBranch": null, "accountNumber": null } }, "metadata": {} } ``` *** ## Campos Específicos do CashIn O CashIn inclui o objeto `counterpart` com dados do **pagador** (quem enviou o PIX). Dados do **pagador** (quem enviou o PIX para você). Nome completo do pagador conforme cadastrado no banco de origem. CPF/CNPJ do pagador (parcialmente mascarado por questões de privacidade). **Exemplo:** `"*.345.678-**"` Dados bancários do pagador. Código ISPB do banco do pagador (identificador único no Sistema de Pagamentos Brasileiro). Nome do banco do pagador. Código COMPE do banco (ex: "001" para Banco do Brasil, "260" para Nubank). Agência do pagador (quando disponível). Número da conta do pagador (quando disponível). *** ## Cálculo do Valor Final Para eventos de `CREDIT` (entrada), o valor final é calculado como: ``` finalAmount = originalAmount - feeAmount ``` A taxa (`feeAmount`) é descontada do valor original. Se o pagador enviou R$ 100,00 e a taxa é R$ 0,50, você receberá R\$ 99,50. *** ## Casos de Uso ### 1. Pagamento de Pedido ```javascript theme={null} async function handleCashIn(payload) { // Usar externalId para correlacionar com o pedido const orderId = payload.externalId.replace('PIX-', ''); await orderService.markAsPaid({ orderId, transactionId: payload.transactionId, amount: payload.finalAmount, paidAt: payload.processingDate }); // Notificar cliente await notificationService.sendPaymentConfirmation(orderId); } ``` ### 2. Recarga de Saldo ```javascript theme={null} async function handleCashIn(payload) { await walletService.credit({ userId: payload.metadata.userId, amount: payload.finalAmount, reference: payload.transactionId }); } ``` *** ## Fluxo Típico ```mermaid theme={null} sequenceDiagram participant Pagador participant Fire Banking participant SeuSistema Pagador->>Fire Banking: Envia PIX Fire Banking->>Fire Banking: Processa transação Fire Banking->>SeuSistema: Webhook CashIn SeuSistema->>SeuSistema: Valida autenticação SeuSistema->>SeuSistema: Verifica idempotência SeuSistema-->>Fire Banking: HTTP 200 OK SeuSistema->>SeuSistema: Processa pagamento ``` *** ## Próximos Passos Aprenda a gerar cobranças PIX Entenda o evento de estorno # CashInReversal Source: https://docs.firebanking.dev/api-reference/guides/webhooks/cash-in-reversal Evento de estorno de recebimento PIX ## Visão Geral O evento **CashInReversal** é enviado quando você inicia um **estorno** de um PIX recebido anteriormente, devolvendo o valor ao pagador original. Este evento ocorre quando você chama a API de Refund-In. O `movementType` para CashInReversal é `DEBIT`, pois você está devolvendo dinheiro que havia entrado na sua conta. | Campo | Valor | | -------------- | ----------------------------------------- | | `event` | `CashInReversal` | | `movementType` | `DEBIT` | | Significado | Você devolveu dinheiro que havia recebido | *** ## Payload Completo ```json theme={null} { "event": "CashInReversal", "status": "CONFIRMED", "transactionType": "PIX", "movementType": "DEBIT", "transactionId": "11111", "externalId": "refund-678689ca-3e16-4f5e-a08f-09a984a97781", "endToEndId": "D07136847202512112011O5222ZRBI5A", "pixKey": null, "feeAmount": 0.01, "originalAmount": 0.3, "finalAmount": 0.31, "processingDate": "2025-12-11T20:11:13.289Z", "errorCode": null, "errorMessage": null, "metadata": {}, "parentTransaction": { "transactionId": "12345", "externalId": "PIX-5482123298-EJUYFSMU1UU", "endToEndId": "E00416968202512111942rjzxxzSSTD9", "processingDate": "2025-12-11T19:42:04.080Z", "wasTotalRefunded": false, "remainingAmountForRefund": 0.2, "metadata": {}, "counterpart": { "name": "Carlos Oliveira", "document": "*.345.678-**", "bank": { "bankISPB": null, "bankName": null, "bankCode": null, "accountBranch": null, "accountNumber": null } } } } ``` *** ## Campos Específicos do CashInReversal O CashInReversal inclui o objeto `parentTransaction` com dados da transação original que está sendo estornada. ### parentTransaction Dados da transação **PIX In original** que está sendo estornada. ID numérico da transação PIX In original (retornado como string). ID externo da transação original. ID End-to-End da transação original. Data de processamento da transação original. Indica se o valor total da transação original foi estornado. * `true`: Estorno total (não pode estornar mais) * `false`: Estorno parcial (ainda pode estornar o restante) Valor restante que ainda pode ser estornado (em reais). **Exemplo:** `0.2` (ainda pode estornar R\$ 0,20) Dados do pagador original que receberá o estorno. *** ## Estorno Total vs Parcial ### Estorno Total Quando você devolve 100% do valor recebido: ```json theme={null} { "parentTransaction": { "wasTotalRefunded": true, "remainingAmountForRefund": 0 } } ``` ### Estorno Parcial Quando você devolve apenas parte do valor: ```json theme={null} { "parentTransaction": { "wasTotalRefunded": false, "remainingAmountForRefund": 0.2 } } ``` Você pode fazer múltiplos estornos parciais até que `wasTotalRefunded` seja `true`. *** ## Casos de Uso ### 1. Devolução de Pagamento Duplicado ```javascript theme={null} async function handleCashInReversal(payload) { const refundId = payload.transactionId; const originalOrderId = payload.parentTransaction.externalId; await refundService.markAsCompleted({ refundId, originalOrderId, amount: payload.originalAmount, completedAt: payload.processingDate }); // Notificar cliente sobre a devolução await notificationService.sendRefundConfirmation({ orderId: originalOrderId, amount: payload.originalAmount }); } ``` ### 2. Controle de Estornos Parciais ```javascript theme={null} async function handleCashInReversal(payload) { const { parentTransaction } = payload; await refundService.updateStatus({ originalTransactionId: parentTransaction.transactionId, totalRefunded: !parentTransaction.wasTotalRefunded ? false : true, remainingAmount: parentTransaction.remainingAmountForRefund }); if (parentTransaction.wasTotalRefunded) { console.log('Transação totalmente estornada'); } else { console.log(`Ainda disponível para estorno: R$ ${parentTransaction.remainingAmountForRefund}`); } } ``` *** ## Fluxo Típico ```mermaid theme={null} sequenceDiagram participant SeuSistema participant Fire Banking participant PagadorOriginal Note over SeuSistema,PagadorOriginal: Transação original (CashIn) PagadorOriginal->>Fire Banking: PIX recebido anteriormente Fire Banking->>SeuSistema: Webhook CashIn Note over SeuSistema,PagadorOriginal: Estorno (CashInReversal) SeuSistema->>Fire Banking: POST /api/pix/refund-in/{id} Fire Banking->>PagadorOriginal: Devolve PIX Fire Banking->>SeuSistema: Webhook CashInReversal SeuSistema-->>Fire Banking: HTTP 200 OK ``` *** ## Próximos Passos Aprenda a estornar recebimentos Entenda o evento de recebimento # CashOut Source: https://docs.firebanking.dev/api-reference/guides/webhooks/cash-out Evento de envio PIX confirmado ## Visão Geral O evento **CashOut** é enviado quando um pagamento PIX é **enviado** com sucesso da sua conta para outra conta. Indica que a transferência foi completada. Este evento é disparado tanto para pagamentos via chave PIX (`/api/pix/cash-out`) quanto para pagamentos via QR Code (`/api/pix/cash-out-qrcode`). O `movementType` para CashOut é sempre `DEBIT`, indicando saída de recursos da conta. | Campo | Valor | | -------------- | -------------------------- | | `event` | `CashOut` | | `movementType` | `DEBIT` | | Significado | Dinheiro saiu da sua conta | *** ## Payload Completo ```json theme={null} { "event": "CashOut", "status": "CONFIRMED", "transactionType": "PIX", "movementType": "DEBIT", "transactionId": "67890", "externalId": "PIX-OUT-5483571657-OWUJDUDVDO", "endToEndId": "E071368472025121120065P1T3N1CS1A", "pixKey": "07646173380", "feeAmount": 0.01, "originalAmount": 0.30, "finalAmount": 0.31, "processingDate": "2025-12-11T20:06:12.117Z", "errorCode": null, "errorMessage": null, "counterpart": { "name": "Ana Costa", "document": "*.765.432-**", "bank": { "bankISPB": null, "bankName": null, "bankCode": "260", "accountBranch": null, "accountNumber": null } }, "metadata": {} } ``` *** ## Campos Específicos do CashOut O CashOut inclui o objeto `counterpart` com dados do **recebedor** (quem recebeu o PIX). Dados do **recebedor** (quem recebeu o PIX que você enviou). Nome completo do recebedor conforme cadastrado no banco de destino. CPF/CNPJ do recebedor (parcialmente mascarado por questões de privacidade). **Exemplo:** `"*.765.432-**"` Dados bancários do recebedor. Código COMPE do banco do recebedor. **Exemplo:** `"260"` (Nubank) Código ISPB do banco do recebedor. Nome do banco do recebedor. *** ## Cálculo do Valor Final Para eventos de `DEBIT` (saída), o valor final é calculado como: ``` finalAmount = originalAmount + feeAmount ``` A taxa (`feeAmount`) é somada ao valor original. Se você enviou R$ 100,00 e a taxa é R$ 0,50, o débito total na sua conta será R\$ 100,50. *** ## Casos de Uso ### 1. Pagamento a Fornecedor ```javascript theme={null} async function handleCashOut(payload) { const paymentId = payload.externalId.replace('PIX-OUT-', ''); await paymentService.markAsCompleted({ paymentId, transactionId: payload.transactionId, endToEndId: payload.endToEndId, completedAt: payload.processingDate }); // Notificar equipe financeira await notificationService.sendPaymentCompleted(paymentId); } ``` ### 2. Saque de Cliente ```javascript theme={null} async function handleCashOut(payload) { await withdrawalService.confirm({ withdrawalId: payload.externalId, transactionId: payload.transactionId, amount: payload.originalAmount, fee: payload.feeAmount }); } ``` *** ## Fluxo Típico ```mermaid theme={null} sequenceDiagram participant SeuSistema participant Fire Banking participant Recebedor SeuSistema->>Fire Banking: POST /api/pix/payment Fire Banking->>Fire Banking: Valida e processa Fire Banking->>Recebedor: Transfere PIX Recebedor-->>Fire Banking: Confirmação Fire Banking->>SeuSistema: Webhook CashOut SeuSistema-->>Fire Banking: HTTP 200 OK ``` *** ## Tratamento de Erros Quando um CashOut falha, você receberá o webhook com `status: "ERROR"`: ```json theme={null} { "event": "CashOut", "status": "ERROR", "errorCode": "INVALID_PIX_KEY", "errorMessage": "Chave PIX não encontrada ou inválida", ... } ``` Quando `status` é `ERROR`, o valor **não foi debitado** da sua conta. Trate o erro e informe o usuário. *** ## Próximos Passos Aprenda a enviar pagamentos PIX Pague via QR Code PIX Entenda o evento de devolução # CashOutReversal Source: https://docs.firebanking.dev/api-reference/guides/webhooks/cash-out-reversal Evento de devolução de PIX enviado ## Visão Geral O evento **CashOutReversal** é enviado quando você **recebe uma devolução** de um PIX que enviou anteriormente. Isso pode ocorrer quando: * O recebedor devolve o valor voluntariamente * Há um problema com a transação original (dados inválidos, conta encerrada, etc.) * O banco destino rejeita a transação O `movementType` para CashOutReversal é `CREDIT`, pois você está recebendo de volta dinheiro que havia saído da sua conta. | Campo | Valor | | -------------- | ------------------------------------------------ | | `event` | `CashOutReversal` | | `movementType` | `CREDIT` | | Significado | Você recebeu de volta dinheiro que havia enviado | *** ## Payload Completo ```json theme={null} { "event": "CashOutReversal", "status": "CONFIRMED", "transactionType": "PIX", "movementType": "CREDIT", "transactionId": "22222", "externalId": null, "endToEndId": "D18236120202512112009s0018351d9f", "pixKey": "07646173380", "feeAmount": 0.01, "originalAmount": 0.08, "finalAmount": 0.07, "processingDate": "2025-12-11T20:09:27.786Z", "errorCode": null, "errorMessage": null, "metadata": { "refund": { "value": 8, "originalValue": 31000, "referenceTransactionId": 917561 }, "provider": "hyperwallet", "counterpart": { "bankCode": "260", "bankIspb": "18236120", "bankName": "NU PAGAMENTOS S.A. - INSTITUIÇÃO DE PAGAMENTO" }, "webhookEvent": "PixOutReversalExternal", "originatedFrom": "WEBHOOK_DIRECT" }, "parentTransaction": { "transactionId": "67890", "externalId": "PIX-OUT-5483571657-OWUJDUDVDO", "endToEndId": "E071368472025121120065P1T3N1CS1A", "processingDate": "2025-12-11T20:06:12.117Z", "wasTotalRefunded": false, "remainingAmountForRefund": 0.22, "metadata": {}, "counterpart": { "name": "Ana Costa", "document": "*.765.432-**", "bank": { "bankISPB": null, "bankName": null, "bankCode": "260", "accountBranch": null, "accountNumber": null } } } } ``` *** ## Campos Específicos do CashOutReversal O CashOutReversal inclui campos adicionais no `metadata` e o objeto `parentTransaction`. ### metadata.refund Detalhes da devolução recebida. Valor devolvido **em centavos**. **Exemplo:** `8` (R\$ 0,08) Valor original da transação **em centavos**. **Exemplo:** `31000` (R\$ 310,00) ID interno de referência da transação original no provedor. ### parentTransaction Dados da transação **PIX Out original** que foi devolvida. ID numérico da transação PIX Out original (retornado como string). ID externo que você forneceu ao criar o PIX Out. Indica se o valor total foi devolvido. * `true`: Devolução total * `false`: Devolução parcial Valor restante que ainda pode ser devolvido (em reais). Dados do recebedor original que devolveu o PIX. *** ## Diferença: CashInReversal vs CashOutReversal | Aspecto | CashInReversal | CashOutReversal | | ----------------- | ------------------------ | ------------------------------ | | **Quem inicia** | Você (via API Refund-In) | O recebedor ou o banco destino | | **Direção** | Você → Pagador original | Recebedor → Você | | **movementType** | `DEBIT` (saída) | `CREDIT` (entrada) | | **Quando ocorre** | Você decide devolver | Você recebe de volta | *** ## Casos de Uso ### 1. Devolução Recebida ```javascript theme={null} async function handleCashOutReversal(payload) { const { parentTransaction, metadata } = payload; // Creditar o valor devolvido no saldo await balanceService.credit({ amount: payload.finalAmount, reference: payload.transactionId, originalPaymentId: parentTransaction.transactionId }); // Atualizar status do pagamento original await paymentService.markAsRefunded({ paymentId: parentTransaction.externalId, refundAmount: payload.originalAmount, wasFullRefund: parentTransaction.wasTotalRefunded }); // Notificar equipe financeira await notificationService.sendRefundReceived({ originalAmount: metadata.refund.originalValue / 100, refundAmount: metadata.refund.value / 100 }); } ``` ### 2. Tratamento de Rejeição ```javascript theme={null} async function handleCashOutReversal(payload) { // Se o PIX foi devolvido, pode ser rejeição do banco if (payload.metadata.originatedFrom === 'WEBHOOK_DIRECT') { console.log('PIX rejeitado pelo banco destino'); await transferService.markAsFailed({ transferId: payload.parentTransaction.externalId, reason: 'Devolvido pelo banco destino' }); // Notificar usuário para verificar dados await notificationService.sendTransferFailed(); } } ``` *** ## Fluxo Típico ```mermaid theme={null} sequenceDiagram participant SeuSistema participant Fire Banking participant Recebedor Note over SeuSistema,Recebedor: Transação original (CashOut) SeuSistema->>Fire Banking: POST /api/pix/payment Fire Banking->>Recebedor: PIX enviado Fire Banking->>SeuSistema: Webhook CashOut Note over SeuSistema,Recebedor: Devolução (CashOutReversal) Recebedor->>Fire Banking: Devolve PIX Fire Banking->>Fire Banking: Processa devolução Fire Banking->>SeuSistema: Webhook CashOutReversal SeuSistema-->>Fire Banking: HTTP 200 OK SeuSistema->>SeuSistema: Credita saldo ``` *** ## Próximos Passos Aprenda a enviar pagamentos PIX Entenda o evento de envio # Implementação Source: https://docs.firebanking.dev/api-reference/guides/webhooks/implementation Exemplos de código e boas práticas para implementar webhooks ## Exemplos Completos ```typescript theme={null} import express from 'express'; interface PixWebhookPayload { event: 'CashIn' | 'CashOut' | 'CashInReversal' | 'CashOutReversal'; status: 'PENDING' | 'CONFIRMED' | 'ERROR'; transactionType: 'PIX'; movementType: 'CREDIT' | 'DEBIT'; transactionId: string; externalId: string | null; endToEndId: string; pixKey: string | null; feeAmount: number; originalAmount: number; finalAmount: number; processingDate: string; errorCode: string | null; errorMessage: string | null; counterpart?: Counterpart; parentTransaction?: ParentTransaction; metadata: Record; } interface Counterpart { name: string; document: string; bank: { bankISPB: string | null; bankName: string | null; bankCode: string | null; accountBranch: string | null; accountNumber: string | null; }; } interface ParentTransaction { transactionId: string; externalId: string; endToEndId: string; processingDate: string; wasTotalRefunded: boolean; remainingAmountForRefund: number; metadata: Record; counterpart: Counterpart; } const app = express(); app.use(express.json()); // Middleware de autenticação Basic Auth function validateBasicAuth( req: express.Request, res: express.Response, next: express.NextFunction ) { const authHeader = req.headers.authorization; if (!authHeader || !authHeader.startsWith('Basic ')) { return res.status(401).json({ error: 'Unauthorized' }); } const base64Credentials = authHeader.split(' ')[1]; const credentials = Buffer.from(base64Credentials, 'base64').toString('ascii'); const [username, password] = credentials.split(':'); if ( username !== process.env.WEBHOOK_USER || password !== process.env.WEBHOOK_PASS ) { return res.status(401).json({ error: 'Invalid credentials' }); } next(); } // Set para controle de idempotência const processedTransactions = new Set(); app.post('/webhooks/pix', validateBasicAuth, async (req, res) => { const payload: PixWebhookPayload = req.body; // Responder rapidamente (webhook exige resposta em até 10s) res.status(200).json({ acknowledged: true }); // Verificar idempotência if (processedTransactions.has(payload.transactionId)) { console.log(`Transação ${payload.transactionId} já processada`); return; } // Marcar como processada processedTransactions.add(payload.transactionId); // Processar assincronamente try { switch (payload.event) { case 'CashIn': await handleCashIn(payload); break; case 'CashOut': await handleCashOut(payload); break; case 'CashInReversal': await handleCashInReversal(payload); break; case 'CashOutReversal': await handleCashOutReversal(payload); break; } } catch (error) { console.error(`Erro ao processar ${payload.event}:`, error); processedTransactions.delete(payload.transactionId); } }); async function handleCashIn(payload: PixWebhookPayload) { console.log(`[CashIn] Recebido: R$ ${payload.finalAmount}`); // await orderService.markAsPaid(payload.externalId); } async function handleCashOut(payload: PixWebhookPayload) { console.log(`[CashOut] Enviado: R$ ${payload.originalAmount}`); // await transferService.markAsCompleted(payload.transactionId); } async function handleCashInReversal(payload: PixWebhookPayload) { console.log(`[CashInReversal] Estornado: R$ ${payload.originalAmount}`); // await refundService.markAsCompleted(payload.transactionId); } async function handleCashOutReversal(payload: PixWebhookPayload) { console.log(`[CashOutReversal] Devolvido: R$ ${payload.finalAmount}`); // await balanceService.credit(payload.finalAmount); } app.listen(3000); ``` ```python theme={null} from flask import Flask, request, jsonify from functools import wraps import base64 import os from typing import Dict, Any, Optional from dataclasses import dataclass app = Flask(__name__) processed_transactions: set = set() @dataclass class PixWebhookPayload: event: str status: str transaction_id: str external_id: Optional[str] end_to_end_id: str fee_amount: float original_amount: float final_amount: float counterpart: Optional[Dict[str, Any]] parent_transaction: Optional[Dict[str, Any]] @classmethod def from_dict(cls, data: Dict[str, Any]) -> 'PixWebhookPayload': return cls( event=data.get('event'), status=data.get('status'), transaction_id=data.get('transactionId'), external_id=data.get('externalId'), end_to_end_id=data.get('endToEndId'), fee_amount=data.get('feeAmount', 0), original_amount=data.get('originalAmount', 0), final_amount=data.get('finalAmount', 0), counterpart=data.get('counterpart'), parent_transaction=data.get('parentTransaction'), ) def require_basic_auth(f): @wraps(f) def decorated(*args, **kwargs): auth_header = request.headers.get('Authorization') if not auth_header or not auth_header.startswith('Basic '): return jsonify({'error': 'Unauthorized'}), 401 try: credentials = base64.b64decode( auth_header.split(' ')[1] ).decode('utf-8') username, password = credentials.split(':') if ( username != os.environ.get('WEBHOOK_USER') or password != os.environ.get('WEBHOOK_PASS') ): return jsonify({'error': 'Invalid credentials'}), 401 except Exception: return jsonify({'error': 'Invalid auth header'}), 401 return f(*args, **kwargs) return decorated @app.route('/webhooks/pix', methods=['POST']) @require_basic_auth def handle_pix_webhook(): data = request.get_json() payload = PixWebhookPayload.from_dict(data) # Idempotência if payload.transaction_id in processed_transactions: return jsonify({'acknowledged': True}), 200 processed_transactions.add(payload.transaction_id) # Processar if payload.event == 'CashIn': print(f"[CashIn] R$ {payload.final_amount:.2f}") elif payload.event == 'CashOut': print(f"[CashOut] R$ {payload.original_amount:.2f}") elif payload.event == 'CashInReversal': print(f"[CashInReversal] R$ {payload.original_amount:.2f}") elif payload.event == 'CashOutReversal': print(f"[CashOutReversal] R$ {payload.final_amount:.2f}") return jsonify({'acknowledged': True}), 200 if __name__ == '__main__': app.run(host='0.0.0.0', port=3000) ``` ```php theme={null} 'Unauthorized']); exit; } $payload = json_decode(file_get_contents('php://input'), true); // Responder rapidamente http_response_code(200); header('Content-Type: application/json'); echo json_encode(['acknowledged' => true]); if (function_exists('fastcgi_finish_request')) { fastcgi_finish_request(); } // Idempotência if (isProcessed($payload['transactionId'])) { exit; } markProcessed($payload['transactionId']); // Processar switch ($payload['event']) { case 'CashIn': error_log("[CashIn] R$ " . $payload['finalAmount']); break; case 'CashOut': error_log("[CashOut] R$ " . $payload['originalAmount']); break; case 'CashInReversal': error_log("[CashInReversal] R$ " . $payload['originalAmount']); break; case 'CashOutReversal': error_log("[CashOutReversal] R$ " . $payload['finalAmount']); break; } ``` *** ## Idempotência Webhooks podem ser enviados mais de uma vez (em caso de retentativas). Implemente tratamento de idempotência para evitar processamento duplicado. Use o campo `transactionId` como chave única: ```typescript theme={null} // Verificar se já processou const isProcessed = await redis.get(`webhook:${payload.transactionId}`); if (isProcessed) { console.log('Webhook já processado, ignorando'); return; } // Marcar como processado ANTES de processar await redis.set(`webhook:${payload.transactionId}`, '1', 'EX', 86400); // Processar webhook await processWebhook(payload); ``` * **Performance**: Verificação em memória é extremamente rápida * **Distribuído**: Funciona com múltiplas instâncias do servidor * **TTL automático**: Limpeza automática de registros antigos ```sql theme={null} CREATE TABLE processed_webhooks ( transaction_id VARCHAR PRIMARY KEY, processed_at TIMESTAMP DEFAULT NOW() ); INSERT INTO processed_webhooks (transaction_id) VALUES ($1) ON CONFLICT (transaction_id) DO NOTHING RETURNING transaction_id; ``` *** ## Boas Práticas O sistema espera resposta em até 10 segundos. Responda imediatamente e processe de forma assíncrona para evitar timeouts. ```javascript theme={null} app.post('/webhooks/pix', (req, res) => { res.status(200).json({ acknowledged: true }); processWebhookAsync(req.body).catch(console.error); }); ``` Configure seu endpoint apenas com HTTPS para garantir transmissão segura. Sempre valide o header `Authorization` com Basic Auth. ```javascript theme={null} console.log({ timestamp: new Date().toISOString(), event: payload.event, transactionId: payload.transactionId, amount: payload.finalAmount }); ``` O campo `externalId` contém o identificador enviado na criação. Use-o para correlacionar com seus registros. *** ## Retentativas Se seu endpoint não responder com HTTP 200 em até 10 segundos: | Tentativa | Intervalo | Tempo acumulado | | ------------- | ---------- | --------------- | | 1ª | Imediato | 0 min | | 2ª (1º retry) | 5 minutos | 5 min | | 3ª (2º retry) | 5 minutos | 10 min | | 4ª (3º retry) | 15 minutos | 25 min | Após 4 tentativas sem sucesso (tempo total \~25 minutos), o webhook é movido para uma fila de falhas (DLQ). Implemente consulta periódica como fallback para garantir que nenhuma transação seja perdida. A estratégia de retry diferencia erros temporários (network, timeout, 5xx) de erros permanentes (validação, formato inválido). Erros permanentes não são retentados. *** ## Códigos de Resposta Seu endpoint deve retornar um código HTTP apropriado: | Código | Descrição | Ação do Sistema | | ------ | ----------------------------- | ---------------------------------------- | | `2xx` | Sucesso (200, 201, 204, etc.) | ✅ Webhook confirmado, não será retentado | | `3xx` | Redirecionamento | ⚠️ Considerado falha, será retentado | | `4xx` | Erro do cliente | ⚠️ Considerado falha, será retentado | | `5xx` | Erro do servidor | ⚠️ Considerado falha, será retentado | O sistema valida **apenas o código HTTP**. Qualquer resposta 2xx (200-299) é considerada sucesso, independente do conteúdo do body. Você pode retornar body vazio, `"OK"`, ou qualquer JSON. *** ## Próximos Passos Aprenda a gerar cobranças PIX Aprenda a enviar pagamentos PIX Aprenda a estornar recebimentos Configure a autenticação da API # Visão Geral Source: https://docs.firebanking.dev/api-reference/guides/webhooks/overview Receba notificações automáticas sobre o status das suas transações PIX ## O que são Webhooks? Os **Webhooks PIX** permitem que você receba notificações em tempo real quando o status de uma transação PIX muda. Em vez de fazer polling constantemente na API, seu sistema é notificado automaticamente quando eventos importantes ocorrem. Webhooks são a forma recomendada de acompanhar o status das transações. Eles reduzem a latência e o consumo de recursos comparado ao polling. ### Características * Notificações em tempo real * Suporte a 4 tipos de eventos (Cash In, Cash Out, Refund In, Refund Out) * Retentativas automáticas em caso de falha * Autenticação via Basic Auth * Payload padronizado em JSON *** ## Eventos Disponíveis Recebimento PIX confirmado (CREDIT) Envio PIX confirmado (DEBIT) Estorno de recebimento (DEBIT) Devolução de envio recebida (CREDIT) | Evento | `event` | `movementType` | Descrição | | ---------- | ----------------- | -------------- | ---------------------------------------------------- | | PIX In | `CashIn` | `CREDIT` | Recebimento PIX confirmado | | PIX Out | `CashOut` | `DEBIT` | Envio PIX confirmado | | Refund In | `CashInReversal` | `DEBIT` | Estorno de recebimento (devolução iniciada por você) | | Refund Out | `CashOutReversal` | `CREDIT` | Devolução de envio (devolução recebida) | *** ## Configuração do Endpoint Para receber webhooks, você precisa: Use a [API de Configuração de Webhooks](/api-reference/guides/webhooks/setup) para definir a URL do seu endpoint programaticamente. Crie um endpoint HTTPS que aceite requisições POST e retorne HTTP 200 rapidamente. Configure a validação do header de autenticação Basic Auth. ### Requisitos Técnicos | Requisito | Descrição | | ------------ | ---------------------------- | | Protocolo | HTTPS obrigatório | | Método | POST | | Timeout | Responder em até 10 segundos | | Response | HTTP 200 OK | | Content-Type | application/json | Se seu endpoint não responder com HTTP 200 dentro de 10 segundos, o webhook será considerado como falha e será retentado. *** ## Autenticação Basic Auth Os webhooks são enviados com autenticação **Basic Auth** no header: ``` Authorization: Basic base64(username:password) ``` ```javascript theme={null} // Node.js/Express - Validação app.post('/webhooks/pix', (req, res) => { const authHeader = req.headers.authorization; if (!authHeader || !authHeader.startsWith('Basic ')) { return res.status(401).send('Unauthorized'); } const base64Credentials = authHeader.split(' ')[1]; const credentials = Buffer.from(base64Credentials, 'base64').toString('ascii'); const [username, password] = credentials.split(':'); if (username !== process.env.WEBHOOK_USER || password !== process.env.WEBHOOK_PASS) { return res.status(401).send('Unauthorized'); } // Processar webhook... res.status(200).json({ acknowledged: true }); }); ``` *** ## Estrutura Base do Payload Todos os webhooks compartilham uma estrutura base comum: ```json theme={null} { "event": "CashIn", "status": "CONFIRMED", "transactionType": "PIX", "movementType": "CREDIT", "transactionId": "12345", "externalId": "PIX-5482123298-EJUYFSMU1UU", "endToEndId": "E00416968202512111942rjzxxzSSTD9", "pixKey": "1ff6ce09-4244-44d5-aa8f-1fe69f8986a9", "feeAmount": 0.01, "originalAmount": 0.5, "finalAmount": 0.49, "processingDate": "2025-12-11T19:42:04.080Z", "errorCode": null, "errorMessage": null, "metadata": {} } ``` Tipo do evento. **Valores possíveis:** `CashIn`, `CashOut`, `CashInReversal`, `CashOutReversal` Status da transação. **Valores possíveis:** `PENDING`, `CONFIRMED`, `ERROR` Tipo de transação. Sempre `PIX` para webhooks PIX. Tipo de movimento na conta. * `CREDIT`: Entrada de recursos (recebimento ou devolução recebida) * `DEBIT`: Saída de recursos (envio ou estorno) ID numérico da transação na Fire Banking (retornado como string). **Exemplo:** `"12345"` ID End-to-End gerado pelo Banco Central para rastreamento. **Exemplo:** `"E00416968202512111942rjzxxzSSTD9"` Data e hora do processamento (ISO 8601 UTC). **Exemplo:** `"2025-12-11T19:42:04.080Z"` Taxa cobrada pela transação em reais (BRL). **Exemplo:** `0.01` Valor original da transação em reais (BRL). **Exemplo:** `0.50` Valor final após aplicação de taxas. * Para `CREDIT`: `originalAmount - feeAmount` * Para `DEBIT`: `originalAmount + feeAmount` ID externo fornecido na criação da transação. **Exemplo:** `"PIX-5482123298-EJUYFSMU1UU"` Chave PIX utilizada na transação (CPF, CNPJ, email, telefone ou chave aleatória). Código de erro quando `status` é `ERROR`. Nulo se sucesso. Mensagem de erro descritiva. Nulo se sucesso. Metadados adicionais específicos do evento. *** ## Próximos Passos Configure URLs de webhook via API Detalhes do evento de recebimento Detalhes do evento de envio Detalhes do evento de estorno Detalhes do evento de devolução # Configurar Webhooks via API Source: https://docs.firebanking.dev/api-reference/guides/webhooks/setup Configure URLs de webhook programaticamente para receber notificações de eventos PIX ## Visão Geral A API de configuração de webhooks permite que você defina programaticamente onde sua aplicação receberá notificações de eventos PIX. Isso elimina a necessidade de contato com o suporte para configurar webhooks. Mudanças na configuração de webhooks são aplicadas **imediatamente**. Transações subsequentes usarão a nova URL configurada. ## Endpoint **POST** `/api/webhooks` ## Autenticação Requer token Bearer da conta (Account Token) no header Authorization. ```bash theme={null} Authorization: Bearer {account_token} ``` O token deve ser obtido através do endpoint de autenticação usando seu certificado de cliente. ## Parâmetros URL HTTPS do seu endpoint de webhook. **Requisitos:** * Deve usar protocolo HTTPS (HTTP não é aceito) * Deve ser uma URL válida e acessível **Exemplo:** `https://api.example.com/webhooks/pix` Tipo de evento para receber notificações. **Valores possíveis:** * `cash_in` - PIX recebido * `cash_out` - PIX enviado * `refund_in` - Estorno de recebimento (devolução solicitada) * `refund_out` - Devolução recebida Headers customizados para autenticação do seu endpoint (máximo 5). Cada item deve ter: * `key`: Nome do header * `value`: Valor do header **Headers bloqueados (nao permitidos):** * host * content-length * connection * transfer-encoding * content-type * user-agent ## Exemplo de Request ```bash cURL theme={null} curl -X POST https://api.public.firebanking.com.br/api/webhooks \ -H "Authorization: Bearer {account_token}" \ -H "Content-Type: application/json" \ -d '{ "url": "https://api.example.com/webhooks/pix", "eventType": "cash_in", "headers": [ { "key": "Authorization", "value": "Bearer my-secret-token" }, { "key": "X-Webhook-Secret", "value": "abc123" } ] }' ``` ```javascript Node.js theme={null} const response = await fetch('https://api.public.firebanking.com.br/api/webhooks', { method: 'POST', headers: { 'Authorization': `Bearer ${accountToken}`, 'Content-Type': 'application/json', }, body: JSON.stringify({ url: 'https://api.example.com/webhooks/pix', eventType: 'cash_in', headers: [ { key: 'Authorization', value: 'Bearer my-secret-token' }, { key: 'X-Webhook-Secret', value: 'abc123' }, ], }), }); const data = await response.json(); console.log(data); ``` ```python Python theme={null} import requests response = requests.post( 'https://api.public.firebanking.com.br/api/webhooks', headers={ 'Authorization': f'Bearer {account_token}', 'Content-Type': 'application/json', }, json={ 'url': 'https://api.example.com/webhooks/pix', 'eventType': 'cash_in', 'headers': [ {'key': 'Authorization', 'value': 'Bearer my-secret-token'}, {'key': 'X-Webhook-Secret', 'value': 'abc123'}, ], }, ) print(response.json()) ``` ## Exemplo de Response ```json theme={null} { "success": true, "message": "Webhook configurado com sucesso" } ``` ## Comportamento de Upsert Se já existir um webhook configurado para o mesmo `eventType`, ele será **atualizado** com a nova URL e headers. Não é criado um webhook duplicado. Ao atualizar um webhook existente, os headers anteriores são **substituídos** pelos novos. Se você não enviar headers, os headers anteriores serão removidos. ## Códigos de Erro | Código | Descrição | | ------ | ------------------------------------------------------------------------- | | 400 | URL inválida (não é HTTPS), tipo de evento inválido, ou mais de 5 headers | | 401 | Token não fornecido ou inválido | | 404 | Conta não encontrada | | 500 | Erro interno ao configurar webhook | ## Configurando Múltiplos Eventos Para receber notificações de múltiplos tipos de eventos, faça uma chamada para cada tipo: ```javascript theme={null} const eventTypes = ['cash_in', 'cash_out', 'refund_in', 'refund_out']; for (const eventType of eventTypes) { await fetch('https://api.public.firebanking.com.br/api/webhooks', { method: 'POST', headers: { 'Authorization': `Bearer ${accountToken}`, 'Content-Type': 'application/json', }, body: JSON.stringify({ url: 'https://api.example.com/webhooks/pix', eventType, headers: [ { key: 'X-Webhook-Secret', value: 'abc123' }, ], }), }); } ``` Você pode usar a mesma URL para todos os tipos de evento e diferenciar pelo campo `type` no payload do webhook. ## Próximos Passos Entenda a estrutura dos webhooks recebidos Exemplos de codigo para processar webhooks Reenvie webhooks perdidos ou para testes Detalhes do webhook de PIX recebido # Introdução à API Source: https://docs.firebanking.dev/api-reference/introduction Bem-vindo à documentação da API Pública Fire Banking ## Visão Geral A **API Pública Fire Banking** é uma plataforma completa para integração com serviços de pagamento PIX e gestão de contas. Nossa API permite que você: * Gere cobranças PIX dinâmicas para recebimento * Realize pagamentos PIX para qualquer chave * Realize pagamentos PIX via QR Code * Consulte saldos em tempo real * Gerencie estornos de transações * Integre pagamentos instantâneos em sua aplicação ## Ambiente ``` https://api.public.firebanking.com.br ``` ## Autenticação Todos os endpoints da API (exceto o de geração de token) requerem autenticação via Bearer token. O processo de autenticação segue o padrão OAuth 2.0 com certificado X.509: Solicite seu `clientId` e `clientSecret` através do portal Fire Banking Instale o certificado cliente fornecido em seu ambiente Use o endpoint `/api/auth/token` com suas credenciais e certificado para gerar um token Bearer Inclua o token no header `Authorization: Bearer {token}` em todas as requisições O token gerado tem validade de **30 minutos** e deve ser renovado após esse período. ## Códigos de Status HTTP A API utiliza códigos de status HTTP padrão para indicar o sucesso ou falha de uma requisição: | Código | Significado | Descrição | | ------ | --------------------- | ----------------------------------- | | `200` | OK | Requisição bem-sucedida (GET) | | `201` | Created | Recurso criado com sucesso (POST) | | `400` | Bad Request | Dados inválidos na requisição | | `401` | Unauthorized | Token ausente, inválido ou expirado | | `404` | Not Found | Recurso não encontrado | | `500` | Internal Server Error | Erro interno do servidor | ## Formato de Datas Todas as datas na API seguem o padrão **ISO 8601** com timezone UTC: ``` 2024-01-15T10:30:00.000Z ``` ## Suporte Para questões técnicas ou suporte, entre em contato: * **Email:** [suporte@firebanking.com.br](mailto:suporte@firebanking.com.br) * **Documentação:** [https://docs.firebanking.com.br](https://docs.firebanking.com.br) * **Status da API:** [https://status.firebanking.io](https://status.firebanking.io) # Ativação Source: https://docs.firebanking.dev/pix-bacen/activation Como ativar o modo PIX Bacen e Webhooks V2 na sua conta ## Visão Geral O modo PIX Bacen inclui duas funcionalidades que precisam ser ativadas: 1. **Endpoints BACEN**: Acesso aos endpoints compatíveis com a especificação do Banco Central 2. **Webhooks V2**: Novo formato de notificações com envelope `{type, data}` A ativação do modo PIX Bacen é uma **breaking change**. Os webhooks passam a usar um formato completamente diferente. Certifique-se de atualizar sua integração antes de solicitar a ativação. ## Como Solicitar Ativação ### 1. Entre em contato com o suporte Envie um email para **[suporte@firebanking.com.br](mailto:suporte@firebanking.com.br)** com: * Nome da empresa * CNPJ * Client ID da aplicação * Confirmação de que já implementou suporte ao Webhook V2 ### 2. Aguarde a configuração Nossa equipe irá: 1. Ativar o modo PIX Bacen na sua conta 2. Habilitar os endpoints e o formato de webhook V2 3. Confirmar a ativação por email ### 3. Teste a integração Após a ativação: 1. Faça uma cobrança teste via `PUT /cob/:txid` 2. Verifique se o webhook V2 chegou corretamente 3. Confirme que sua aplicação processou o novo formato ## O que muda com a ativação? ### Endpoints Você passa a ter acesso aos endpoints BACEN: | Antes | Depois | | ---------------------- | ------------------------------- | | `POST /pix/cash-in` | `PUT /cob/:txid` | | `POST /pix/cash-out` | `POST /dict/pix` | | `POST /pix/:id/refund` | `PUT /pix/:e2eid/devolucao/:id` | | `GET /balance` | `GET /accounts/balances` | Os endpoints antigos continuam funcionando. Você pode usar ambas as APIs simultaneamente. ### Webhooks O formato de webhook muda completamente: ```json theme={null} { "event": "CashIn", "status": "CONFIRMED", "transactionId": "12345", "movementType": "CREDIT", "originalAmount": 100.00, "finalAmount": 100.00, "counterpart": { "name": "João Silva", "document": "123.xxx.xxx-xx" } } ``` ```json theme={null} { "type": "RECEIVE", "data": { "id": 123, "txId": "abc123", "status": "LIQUIDATED", "payment": { "amount": "100.00", "currency": "BRL" }, "creditDebitType": "CREDIT", "debtorAccount": { "name": "João Silva", "document": "123.xxx.xxx-xx" }, "creditorAccount": {...} } } ``` ### Principais diferenças nos Webhooks | Aspecto | V1 | V2 | | -------------- | ----------------- | ----------------------------------- | | Estrutura | Campos na raiz | Envelope `{type, data}` | | Tipo de evento | `event: "CashIn"` | `type: "RECEIVE"` | | Status sucesso | `CONFIRMED` | `LIQUIDATED` | | Status refund | `CONFIRMED` | `REFUNDED` | | Valores | `number` (100.00) | `string` ("100.00") | | Contraparte | `counterpart` | `debtorAccount` / `creditorAccount` | ## Preparando sua integração ### 1. Atualize o handler de webhooks ```typescript theme={null} // ANTES (V1) function handleWebhookV1(payload: any) { if (payload.event === 'CashIn' && payload.status === 'CONFIRMED') { processPayment(payload.transactionId, payload.finalAmount); } } // DEPOIS (V2) function handleWebhookV2(payload: any) { if (payload.type === 'RECEIVE' && payload.data.status === 'LIQUIDATED') { const amount = parseFloat(payload.data.payment.amount); processPayment(payload.data.id, amount); } } ``` ### 2. Atualize os tipos/interfaces ```typescript theme={null} // V2 Types interface WebhookV2Payload { type: 'RECEIVE' | 'TRANSFER' | 'REFUND'; data: WebhookV2Data; } interface WebhookV2Data { id: number; txId: string | null; status: 'PENDING' | 'LIQUIDATED' | 'REFUNDED' | 'ERROR'; payment: { amount: string; // Note: string, não number! currency: string; }; creditDebitType: 'CREDIT' | 'DEBIT'; debtorAccount: AccountInfo; creditorAccount: AccountInfo; endToEndId: string | null; refunds: RefundInfo[]; // ... outros campos } ``` ### 3. Teste em ambiente de desenvolvimento Antes de solicitar a ativação em produção: 1. Solicite ativação no ambiente de sandbox 2. Execute testes completos de Cash-In, Cash-Out e Refund 3. Valide que todos os webhooks são processados corretamente ## Rollback Após a ativação, **não é possível voltar para V1** automaticamente. Se precisar reverter, entre em contato com o suporte. Recomendamos manter suporte a ambas as versões durante a transição: ```typescript theme={null} function handleWebhook(payload: any) { // Detecta versão pelo formato if (payload.type && payload.data) { return handleWebhookV2(payload); } else if (payload.event) { return handleWebhookV1(payload); } throw new Error('Formato de webhook desconhecido'); } ``` ## Checklist de Ativação Atualize seu código para processar o formato envelope `{type, data}` Solicite ativação em sandbox e execute testes completos Teste: RECEIVE, TRANSFER, REFUND com status LIQUIDATED, REFUNDED e ERROR Envie email para [suporte@firebanking.com.br](mailto:suporte@firebanking.com.br) com as informações necessárias Acompanhe as primeiras transações após a ativação para garantir funcionamento ## Dúvidas Frequentes Os **endpoints** podem ser usados simultaneamente (ex: `POST /pix/cash-in` e `PUT /cob/:txid`). Os **webhooks** são sempre na versão configurada na conta. Não é possível receber V1 e V2 ao mesmo tempo. Transações criadas antes da ativação continuarão enviando webhooks no formato antigo até serem concluídas. Novas transações usarão o formato V2. Não. A URL permanece a mesma. Apenas o formato do payload muda. ## Próximos Passos Entenda o novo formato de webhooks Use o endpoint BACEN para criar cobranças # Autenticação Source: https://docs.firebanking.dev/pix-bacen/authentication Como autenticar suas requisições na API PIX Bacen ## Visão Geral A API PIX Bacen utiliza o mesmo sistema de autenticação da API padrão Fire Banking. Todas as requisições devem incluir um token Bearer válido no header `Authorization`. A autenticação é idêntica à [API padrão](/api-reference/guides/authentication). Se você já possui credenciais, pode usá-las diretamente. ## Obtendo o Token ### Endpoint ``` POST /oauth/token ``` ### Request ```bash cURL theme={null} curl -X POST https://api.public.firebanking.com.br/oauth/token \ -H "Content-Type: application/json" \ -d '{ "clientId": "seu-client-id", "clientSecret": "seu-client-secret" }' ``` ```typescript Node.js theme={null} const response = await fetch('https://api.public.firebanking.com.br/oauth/token', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ clientId: 'seu-client-id', clientSecret: 'seu-client-secret', }), }); const { access_token } = await response.json(); ``` ```python Python theme={null} import requests response = requests.post( 'https://api.public.firebanking.com.br/oauth/token', json={ 'clientId': 'seu-client-id', 'clientSecret': 'seu-client-secret' } ) access_token = response.json()['access_token'] ``` ### Response ```json theme={null} { "access_token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...", "token_type": "Bearer", "expires_in": 3600, "scope": "pix:read pix:write balance:read" } ``` ## Usando o Token Inclua o token em todas as requisições da API PIX Bacen: ```bash theme={null} curl -X PUT https://api.public.firebanking.com.br/cob/abc123 \ -H "Authorization: Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9..." \ -H "Content-Type: application/json" \ -d '{...}' ``` ## Parâmetros de Autenticação Identificador único da sua aplicação. Fornecido durante o cadastro. Chave secreta da sua aplicação. Deve ter entre 8 e 64 caracteres. Nunca exponha o `clientSecret` em código frontend ou repositórios públicos. ## Campos da Resposta Token JWT para autenticação nas requisições. Tipo do token. Sempre `"Bearer"`. Tempo de vida do token em segundos. Padrão: 3600 (1 hora). Escopos de permissão do token. ## Renovação do Token O token expira após `expires_in` segundos. Implemente renovação automática: ```typescript theme={null} class TokenManager { private token: string | null = null; private expiresAt: number = 0; async getToken(): Promise { // Renovar 5 minutos antes de expirar if (!this.token || Date.now() >= this.expiresAt - 300000) { await this.refreshToken(); } return this.token!; } private async refreshToken(): Promise { const response = await fetch('https://api.public.firebanking.com.br/oauth/token', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ clientId: process.env.CLIENT_ID, clientSecret: process.env.CLIENT_SECRET, }), }); const data = await response.json(); this.token = data.access_token; this.expiresAt = Date.now() + (data.expires_in * 1000); } } ``` ## Erros de Autenticação | Código | Descrição | Solução | | ------ | ------------------- | ----------------------------------------------- | | 401 | Token não fornecido | Inclua o header `Authorization: Bearer ` | | 401 | Token inválido | Verifique se o token está correto e não expirou | | 401 | Token expirado | Obtenha um novo token via `/oauth/token` | | 403 | Permissão negada | Verifique os escopos do token | ## Boas Práticas * Use variáveis de ambiente * Nunca commite credenciais no código * Use secret managers em produção (AWS Secrets Manager, HashiCorp Vault) * Cache o token até próximo da expiração * Renove alguns minutos antes de expirar * Evite requisições desnecessárias ao endpoint de token * Todas as requisições devem usar HTTPS * Verifique certificados SSL/TLS * Configure timeouts apropriados ## Próximos Passos Ative o modo PIX Bacen na sua conta Faça sua primeira cobrança # Consultar Saldo Source: https://docs.firebanking.dev/pix-bacen/endpoints/balance Consulte o saldo da conta autenticada no formato BACEN ## Visão Geral O endpoint `GET /accounts/balances` retorna o saldo da conta autenticada no formato compatível com a especificação do Banco Central. ## Endpoint ``` GET /accounts/balances ``` ## Autenticação Token Bearer obtido via `/oauth/token`. ## Request ```bash cURL theme={null} curl -X GET https://api.public.firebanking.com.br/accounts/balances \ -H "Authorization: Bearer " ``` ```typescript Node.js theme={null} const response = await fetch('https://api.public.firebanking.com.br/accounts/balances', { method: 'GET', headers: { 'Authorization': `Bearer ${token}`, }, }); const balance = await response.json(); ``` ```python Python theme={null} import requests response = requests.get( 'https://api.public.firebanking.com.br/accounts/balances', headers={ 'Authorization': f'Bearer {token}', } ) balance = response.json() ``` ## Response ```json theme={null} { "data": [ { "eventDate": "2025-01-15T10:30:00.000Z", "balanceAmount": { "available": 48734.90, "blocked": 1500.00, "overdraft": 0 } } ] } ``` ```json theme={null} { "statusCode": 401, "message": "Token não fornecido ou inválido", "error": "Unauthorized" } ``` ## Campos da Resposta Lista de saldos. Atualmente retorna apenas um item. Data e hora da consulta (ISO 8601). Valores do saldo. Saldo disponível para uso imediato. Já desconta valores bloqueados. Saldo bloqueado. Valores reservados para operações pendentes (ex: PIX Out em processamento). Limite de crédito (cheque especial). Atualmente sempre `0`. ## Tipos de Saldo | Tipo | Descrição | | ------------- | ---------------------------------------------------------- | | **available** | Saldo que pode ser usado imediatamente para transferências | | **blocked** | Valores reservados para operações em processamento | | **overdraft** | Limite de crédito adicional (não implementado) | ### Cálculo do Saldo Total ``` saldoTotal = available + blocked ``` O saldo `available` já desconta os valores `blocked`. ## Comparação com API Padrão | API Padrão (`GET /balance`) | API BACEN (`GET /accounts/balances`) | | --------------------------- | ------------------------------------ | | `grossBalance` | `available + blocked` | | `blockedBalance` | `blocked` | | `netBalance` | `available` | | `consultedAt` | `eventDate` | ### Exemplo de Equivalência ```json theme={null} // API Padrão { "grossBalance": 50234.90, "blockedBalance": 1500.00, "netBalance": 48734.90, "consultedAt": "2025-01-15T10:30:00.000Z" } // API BACEN (equivalente) { "data": [{ "eventDate": "2025-01-15T10:30:00.000Z", "balanceAmount": { "available": 48734.90, // = netBalance "blocked": 1500.00, // = blockedBalance "overdraft": 0 } }] } ``` ## Fluxo de Saldo em Operações ### PIX Out (Transferência) ```mermaid theme={null} sequenceDiagram participant App participant API participant Banco Note over App,Banco: Estado inicial: available=1000, blocked=0 App->>API: POST /dict/pix (R$ 100) API-->>App: { type: "PENDING" } Note over App,Banco: Estado: available=900, blocked=100 Banco->>API: Confirmação API->>App: Webhook TRANSFER (LIQUIDATED) Note over App,Banco: Estado final: available=900, blocked=0 ``` ### PIX In (Recebimento) ```mermaid theme={null} sequenceDiagram participant Pagador participant API participant App Note over Pagador,App: Estado inicial: available=1000 Pagador->>API: Paga QR Code API->>App: Webhook RECEIVE (LIQUIDATED) Note over Pagador,App: Estado final: available=1100 ``` ## Boas Práticas Sempre verifique o saldo disponível antes de iniciar uma transferência para evitar erros de saldo insuficiente. ```typescript theme={null} async function transferir(valor: number) { const balance = await getBalance(); const disponivel = balance.data[0].balanceAmount.available; if (valor > disponivel) { throw new Error(`Saldo insuficiente. Disponível: ${disponivel}`); } return await createTransfer(valor); } ``` O saldo `blocked` representa operações em andamento. Em caso de falha, esse valor retorna para `available`. Se precisar cachear o saldo, use TTL curto (ex: 5-10 segundos) para manter valores atualizados. ## Erros Comuns | Código | Erro | Solução | | ------ | ------------------- | --------------------------------------------- | | 401 | Token não fornecido | Inclua header `Authorization: Bearer ` | | 401 | Token inválido | Verifique se o token está correto | | 401 | Token expirado | Obtenha novo token via `/oauth/token` | ## Próximos Passos Gere um QR Code para receber PIX Envie um PIX para outra conta # Criar Cobrança Source: https://docs.firebanking.dev/pix-bacen/endpoints/cob Crie uma cobrança PIX imediata (QR Code) seguindo a especificação BACEN ## Visão Geral O endpoint `PUT /cob/:txid` cria uma cobrança imediata (cob) associada ao identificador de transação (txid) informado. Este endpoint segue a especificação oficial do Banco Central do Brasil para cobranças PIX. O `txid` é um identificador único gerado pelo seu sistema. Deve ter entre 26 e 35 caracteres alfanuméricos. ## Endpoint ``` PUT /cob/{txid} ``` ## Autenticação Token Bearer obtido via `/oauth/token`. Exemplo: `Bearer eyJhbGciOiJSUzI1NiIs...` ## Parâmetros de URL Identificador da transação. Deve ser único e conter entre 26 e 35 caracteres alfanuméricos `[a-zA-Z0-9]`. Exemplo: `7978c0c97ea847e78e8849634473c1f1` ## Request Body Informações de controle de tempo da cobrança. Tempo de vida da cobrança em segundos. Padrão: 86400 (24 horas). Dados do devedor (pagador). Pode ser Pessoa Física (CPF) ou Jurídica (CNPJ). CPF do devedor. Apenas números, 11 dígitos. Nome completo do devedor. Máximo 200 caracteres. CNPJ do devedor. Apenas números, 14 dígitos. Razão social do devedor. Máximo 200 caracteres. Valores monetários da cobrança. Valor original da cobrança. **String** no formato decimal com 2 casas. Exemplo: `"123.45"` Modalidade de alteração do valor: * `0`: Valor fixo (não pode ser alterado pelo pagador) * `1`: Valor alterável (pagador pode modificar) Chave PIX do recebedor. Pode ser telefone, e-mail, CPF/CNPJ ou EVP (chave aleatória). Máximo 77 caracteres. Texto livre para o pagador. Máximo 140 caracteres. Lista de informações adicionais ao pagador. Nome do campo. Máximo 50 caracteres. Valor do campo. Máximo 200 caracteres. ## Request ```bash cURL theme={null} curl -X PUT https://api.public.firebanking.com.br/cob/7978c0c97ea847e78e8849634473c1f1 \ -H "Authorization: Bearer " \ -H "Content-Type: application/json" \ -d '{ "calendario": { "expiracao": 3600 }, "devedor": { "cpf": "12345678909", "nome": "Carlos Oliveira" }, "valor": { "original": "123.45", "modalidadeAlteracao": 0 }, "chave": "7d9f0335-8dcc-4054-9bf9-0dbd61d36906", "solicitacaoPagador": "Serviço realizado.", "infoAdicionais": [ { "nome": "Pedido", "valor": "#12345" } ] }' ``` ```typescript Node.js theme={null} const response = await fetch( 'https://api.public.firebanking.com.br/cob/7978c0c97ea847e78e8849634473c1f1', { method: 'PUT', headers: { 'Authorization': `Bearer ${token}`, 'Content-Type': 'application/json', }, body: JSON.stringify({ calendario: { expiracao: 3600, }, devedor: { cpf: '12345678909', nome: 'Carlos Oliveira', }, valor: { original: '123.45', modalidadeAlteracao: 0, }, chave: '7d9f0335-8dcc-4054-9bf9-0dbd61d36906', solicitacaoPagador: 'Serviço realizado.', infoAdicionais: [ { nome: 'Pedido', valor: '#12345' }, ], }), } ); const cobranca = await response.json(); ``` ```python Python theme={null} import requests response = requests.put( 'https://api.public.firebanking.com.br/cob/7978c0c97ea847e78e8849634473c1f1', headers={ 'Authorization': f'Bearer {token}', 'Content-Type': 'application/json', }, json={ 'calendario': { 'expiracao': 3600, }, 'devedor': { 'cpf': '12345678909', 'nome': 'Carlos Oliveira', }, 'valor': { 'original': '123.45', 'modalidadeAlteracao': 0, }, 'chave': '7d9f0335-8dcc-4054-9bf9-0dbd61d36906', 'solicitacaoPagador': 'Serviço realizado.', 'infoAdicionais': [ {'nome': 'Pedido', 'valor': '#12345'}, ], } ) cobranca = response.json() ``` ## Response ```json theme={null} { "calendario": { "criacao": "2024-01-15T10:30:00.358Z", "expiracao": 3600 }, "txid": "7978c0c97ea847e78e8849634473c1f1", "revisao": 0, "loc": { "id": 12345, "location": "00020126580014br.gov.bcb.pix0136a629532e-7693-4846-852d-1bbff817b5a8520400005303986540512.345802BR5916Tech Solutions Ltda6009Sao Paulo62070503***6304ABCD", "tipoCob": "cob" }, "location": "00020126580014br.gov.bcb.pix0136a629532e-7693-4846-852d-1bbff817b5a8520400005303986540512.345802BR5916Tech Solutions Ltda6009Sao Paulo62070503***6304ABCD", "status": "ATIVA", "devedor": { "cpf": "12345678909", "nome": "Carlos Oliveira" }, "valor": { "original": "123.45", "modalidadeAlteracao": 0 }, "chave": "7d9f0335-8dcc-4054-9bf9-0dbd61d36906", "solicitacaoPagador": "Serviço realizado.", "infoAdicionais": [ { "nome": "Pedido", "valor": "#12345" } ] } ``` ```json theme={null} { "statusCode": 400, "message": "CPF deve conter exatamente 11 dígitos numéricos", "error": "Bad Request" } ``` ```json theme={null} { "statusCode": 409, "message": "Cobrança com este txid já existe", "error": "Conflict" } ``` ## Campos da Resposta Data e hora de criação da cobrança (ISO 8601). Tempo de expiração em segundos. Identificador da transação informado na requisição. Número da revisão da cobrança. Sempre `0` na criação. Informações do payload PIX. Identificador da transação. Código PIX copia-e-cola (EMV). Use este valor para gerar o QR Code ou permitir que o pagador copie e cole no aplicativo do banco. Tipo de cobrança. Sempre `"cob"` para cobranças imediatas. Código PIX copia-e-cola (mesmo valor de `loc.location`). String no formato EMV que pode ser usada para pagamento. Status da cobrança: * `ATIVA`: Cobrança ativa, aguardando pagamento * `CONCLUIDA`: Pagamento recebido * `REMOVIDA_PELO_USUARIO_RECEBEDOR`: Cancelada pelo recebedor * `REMOVIDA_PELO_PSP`: Removida pelo PSP ## Status da Cobrança ```mermaid theme={null} stateDiagram-v2 [*] --> ATIVA: Cobrança criada ATIVA --> CONCLUIDA: Pagamento recebido ATIVA --> REMOVIDA_PELO_USUARIO_RECEBEDOR: Cancelada ATIVA --> REMOVIDA_PELO_PSP: Expirada/Removida CONCLUIDA --> [*] REMOVIDA_PELO_USUARIO_RECEBEDOR --> [*] REMOVIDA_PELO_PSP --> [*] ``` ## Webhook de Pagamento Quando o pagamento for confirmado, você receberá um webhook V2 do tipo `RECEIVE`: ```json theme={null} { "type": "RECEIVE", "data": { "id": 123, "txId": "7978c0c97ea847e78e8849634473c1f1", "status": "LIQUIDATED", "payment": { "amount": "123.45", "currency": "BRL" }, "endToEndId": "E12345678901234567890123456789012", "debtorAccount": { "name": "Carlos Oliveira", "document": "123.xxx.xxx-xx" } } } ``` Veja a documentação completa do webhook RECEIVE ## Erros Comuns | Código | Erro | Solução | | ------ | ------------------- | -------------------------------------------- | | 400 | txid fora do padrão | Use 26-35 caracteres alfanuméricos | | 400 | CPF/CNPJ inválido | Verifique formato (apenas números) | | 400 | Valor inválido | Use formato "123.45" (string com 2 decimais) | | 401 | Token inválido | Renove o token de acesso | | 409 | txid já existe | Use um txid diferente | ## Próximos Passos Devolva um PIX recebido Processe notificações de pagamento # Solicitar Devolução Source: https://docs.firebanking.dev/pix-bacen/endpoints/devolucao Solicite a devolução de um PIX recebido seguindo a especificação BACEN ## Visão Geral O endpoint `PUT /pix/:e2eid/devolucao/:id` solicita a devolução de um PIX recebido. Utiliza o End to End ID (e2eid) da transação original e um identificador de devolução gerado pelo cliente. A devolução pode ser **total** ou **parcial**. A soma de todas as devoluções não pode ultrapassar o valor original da transação. ## Endpoint ``` PUT /pix/{e2eid}/devolucao/{id} ``` ## Autenticação Token Bearer obtido via `/oauth/token`. ## Parâmetros de URL End to End ID - identificador único da transação PIX original. Contém exatamente 32 caracteres alfanuméricos. Exemplo: `E12345678901234567890123456789012` Identificação gerada pelo cliente para representar a devolução. Entre 1 e 35 caracteres. Exemplo: `D123456789` ## Request Body Valor solicitado para devolução. **String** no formato decimal com 2 casas. A soma dos valores de todas as devoluções não pode ultrapassar o valor total do PIX original. Exemplo: `"7.89"` Indica a natureza da devolução solicitada: * `ORIGINAL`: Devolução de PIX comum ou valor da compra em PIX Troco * `RETIRADA`: Devolução de PIX Saque ou valor do troco em PIX Troco Texto a ser apresentado ao pagador contendo informações sobre a devolução. Máximo: 140 caracteres. ## Request ```bash cURL theme={null} curl -X PUT https://api.public.firebanking.com.br/pix/E12345678901234567890123456789012/devolucao/D123456789 \ -H "Authorization: Bearer " \ -H "Content-Type: application/json" \ -d '{ "valor": "7.89", "natureza": "ORIGINAL", "descricao": "Devolução solicitada pelo recebedor" }' ``` ```typescript Node.js theme={null} const e2eid = 'E12345678901234567890123456789012'; const devolucaoId = 'D123456789'; const response = await fetch( `https://api.public.firebanking.com.br/pix/${e2eid}/devolucao/${devolucaoId}`, { method: 'PUT', headers: { 'Authorization': `Bearer ${token}`, 'Content-Type': 'application/json', }, body: JSON.stringify({ valor: '7.89', natureza: 'ORIGINAL', descricao: 'Devolução solicitada pelo recebedor', }), } ); const devolucao = await response.json(); ``` ```python Python theme={null} import requests e2eid = 'E12345678901234567890123456789012' devolucao_id = 'D123456789' response = requests.put( f'https://api.public.firebanking.com.br/pix/{e2eid}/devolucao/{devolucao_id}', headers={ 'Authorization': f'Bearer {token}', 'Content-Type': 'application/json', }, json={ 'valor': '7.89', 'natureza': 'ORIGINAL', 'descricao': 'Devolução solicitada pelo recebedor', } ) devolucao = response.json() ``` ## Response ```json theme={null} { "id": "D123456789", "rtrId": "D12345678901234567890123456789012", "valor": "7.89", "natureza": "ORIGINAL", "descricao": "Devolução solicitada pelo recebedor", "horario": { "solicitacao": "2024-01-15T10:30:00.000Z" }, "status": "EM_PROCESSAMENTO" } ``` ```json theme={null} { "statusCode": 400, "message": "Valor deve estar no formato decimal com 2 casas (ex: 7.89)", "error": "Bad Request" } ``` ```json theme={null} { "statusCode": 404, "message": "Transação original não encontrada", "error": "Not Found" } ``` ## Campos da Resposta Identificação gerada pelo cliente para representar a devolução (mesmo valor enviado na URL). Identificador único da transação de devolução. Contém 32 caracteres. Valor da devolução no formato string com 2 casas decimais. Natureza da devolução: * `ORIGINAL`: Devolução comum * `RETIRADA`: Devolução de saque * `MED_OPERACIONAL`: Devolução MED por falha operacional * `MED_FRAUDE`: Devolução MED por suspeita de fraude Mensagem ao pagador relativa à devolução. Horário no qual a devolução foi solicitada (ISO 8601). Horário no qual a devolução foi liquidada (ISO 8601). Preenchido apenas quando `status = DEVOLVIDO`. Status da devolução: * `EM_PROCESSAMENTO`: Devolução em processamento * `DEVOLVIDO`: Devolução realizada com sucesso * `NAO_REALIZADO`: Devolução não realizada (falha) Campo opcional com detalhes sobre o motivo do status atual. Preenchido principalmente em caso de falha. ## Status da Devolução ```mermaid theme={null} stateDiagram-v2 [*] --> EM_PROCESSAMENTO: Solicitação enviada EM_PROCESSAMENTO --> DEVOLVIDO: Sucesso EM_PROCESSAMENTO --> NAO_REALIZADO: Falha DEVOLVIDO --> [*] NAO_REALIZADO --> [*] ``` ## Webhook de Devolução Quando a devolução for processada, você receberá um webhook V2 do tipo `REFUND`: ```json theme={null} { "type": "REFUND", "data": { "id": 123, "txId": "original-txid", "status": "REFUNDED", "payment": { "amount": "100.00", "currency": "BRL" }, "refunds": [ { "status": "LIQUIDATED", "payment": { "amount": 7.89, "currency": "BRL" }, "endToEndId": "D12345678901234567890123456789012", "eventDate": "2024-01-15T10:30:00.000Z", "information": "Devolução solicitada pelo recebedor" } ], "endToEndId": "E12345678901234567890123456789012", "creditDebitType": "DEBIT" } } ``` Veja a documentação completa do webhook REFUND ## Natureza da Devolução | Natureza | Descrição | | ----------------- | ------------------------------- | | `ORIGINAL` | Devolução de PIX comum | | `RETIRADA` | Devolução de PIX Saque ou Troco | | `MED_OPERACIONAL` | MED por falha operacional | | `MED_FRAUDE` | MED por suspeita de fraude | Os valores `MED_OPERACIONAL` e `MED_FRAUDE` são retornados apenas na resposta, não podem ser enviados na requisição. São utilizados em casos específicos de Mecanismo Especial de Devolução (MED). ## Prazo para Devolução Devoluções podem ser solicitadas em até **89 dias** após o recebimento do PIX original, conforme regulamentação do Banco Central. ## Devoluções Parciais Você pode solicitar múltiplas devoluções parciais: ```typescript theme={null} // Transação original: R$ 100,00 // Primeira devolução: R$ 30,00 await solicitarDevolucao(e2eid, 'DEV001', '30.00'); // Saldo disponível para devolução: R$ 70,00 // Segunda devolução: R$ 50,00 await solicitarDevolucao(e2eid, 'DEV002', '50.00'); // Saldo disponível para devolução: R$ 20,00 // Terceira devolução: R$ 25,00 - ERRO! await solicitarDevolucao(e2eid, 'DEV003', '25.00'); // Falha: valor excede saldo disponível (R$ 20,00) ``` ## Erros Comuns | Código | Erro | Solução | | ------ | ----------------------------------- | ------------------------------------------ | | 400 | Valor inválido | Use formato "7.89" (string com 2 decimais) | | 400 | Valor excede disponível | Verifique saldo disponível para devolução | | 404 | Transação não encontrada | Verifique o e2eid informado | | 404 | Transação não é do tipo recebimento | Devoluções só para PIX recebidos | | 422 | Prazo expirado | Devoluções só até 89 dias após recebimento | ## Próximos Passos Envie um PIX para outra conta Processe notificações de devolução # Transferência PIX Source: https://docs.firebanking.dev/pix-bacen/endpoints/dict-pix Inicie uma transferência PIX para uma chave DICT ## Visão Geral O endpoint `POST /dict/pix` inicia uma transferência PIX para a chave informada. A chave pode ser CPF, CNPJ, e-mail, telefone ou EVP (chave aleatória). Este endpoint segue a especificação do Banco Central para transferências PIX via DICT (Diretório de Identificadores de Contas Transacionais). ## Endpoint ``` POST /dict/pix ``` ## Autenticação Token Bearer obtido via `/oauth/token`. Identificador único da requisição para suporte a idempotência. Deve ser um UUID v4. Exemplo: `550e8400-e29b-41d4-a716-446655440000` ## Request Body Chave PIX de destino. Pode ser: * **CPF**: 11 dígitos numéricos * **CNPJ**: 14 dígitos numéricos * **E-mail**: endereço de e-mail válido * **Telefone**: +55DDDNUMERO (ex: +5511999999999) * **EVP**: Chave aleatória (UUID) Documento do credor (CPF ou CNPJ). Obrigatório quando `priority = HIGH` para validação instantânea. Prioridade do processamento: * `HIGH`: Processado instantaneamente (requer `creditorDocument`) * `NORM`: Processamento normal na fila Mensagem que acompanha a transferência PIX. Será exibida para o destinatário. Fluxo de pagamento (opcional). Utilizado para categorização interna. Tempo máximo em segundos que a operação pode permanecer na fila antes de ser cancelada. * Mínimo: 1 segundo * Máximo: 10800 segundos (3 horas) * Padrão: 600 segundos (10 minutos) Dados do pagamento. Moeda da transação. Atualmente apenas `BRL` é suportado. Valor da transferência. **Número** com até 2 casas decimais. Exemplo: `100.50` Diferente do endpoint `/cob`, aqui o valor é **number**, não string. Lista de códigos ISPB para os quais pagamentos não serão permitidos. Útil para bloquear transferências para instituições específicas. Exemplo: `["12345678", "87654321"]` ## Request ```bash cURL theme={null} curl -X POST https://api.public.firebanking.com.br/dict/pix \ -H "Authorization: Bearer " \ -H "x-idempotency-key: 550e8400-e29b-41d4-a716-446655440000" \ -H "Content-Type: application/json" \ -d '{ "pixKey": "12345678909", "creditorDocument": "12345678909", "priority": "NORM", "description": "Pagamento referente a NF 12345", "expiration": 600, "payment": { "currency": "BRL", "amount": 100.50 } }' ``` ```typescript Node.js theme={null} const response = await fetch('https://api.public.firebanking.com.br/dict/pix', { method: 'POST', headers: { 'Authorization': `Bearer ${token}`, 'x-idempotency-key': crypto.randomUUID(), 'Content-Type': 'application/json', }, body: JSON.stringify({ pixKey: '12345678909', creditorDocument: '12345678909', priority: 'NORM', description: 'Pagamento referente a NF 12345', expiration: 600, payment: { currency: 'BRL', amount: 100.50, }, }), }); const transfer = await response.json(); ``` ```python Python theme={null} import requests import uuid response = requests.post( 'https://api.public.firebanking.com.br/dict/pix', headers={ 'Authorization': f'Bearer {token}', 'x-idempotency-key': str(uuid.uuid4()), 'Content-Type': 'application/json', }, json={ 'pixKey': '12345678909', 'creditorDocument': '12345678909', 'priority': 'NORM', 'description': 'Pagamento referente a NF 12345', 'expiration': 600, 'payment': { 'currency': 'BRL', 'amount': 100.50, }, } ) transfer = response.json() ``` ## Response ```json theme={null} { "endToEndId": "550e8400-e29b-41d4-a716-446655440000", "eventDate": "2024-01-15T10:30:00.000Z", "id": 12345, "payment": { "amount": 100.50 }, "type": "PENDING" } ``` ```json theme={null} { "statusCode": 400, "message": "Chave PIX inválida", "error": "Bad Request" } ``` ```json theme={null} { "statusCode": 422, "message": "Chave PIX não encontrada no DICT", "error": "Unprocessable Entity" } ``` ## Campos da Resposta Identificador único da transação PIX. O valor real do E2E será enviado no webhook quando a transação for liquidada. Data e hora do evento (ISO 8601). Identificador único da transação. Valor da transferência. Tipo/status da transação: * `PENDING`: Transferência em processamento * `COMPLETED`: Transferência concluída * `ERROR`: Falha na transferência ## Idempotência O header `x-idempotency-key` garante que a mesma requisição não seja processada mais de uma vez: ```typescript theme={null} // Mesma idempotency key = mesma resposta const key = '550e8400-e29b-41d4-a716-446655440000'; // Primeira chamada - cria a transferência const res1 = await createTransfer(key, { amount: 100 }); // { id: 123, type: 'PENDING' } // Segunda chamada com mesma key - retorna a mesma transferência const res2 = await createTransfer(key, { amount: 100 }); // { id: 123, type: 'PENDING' } (não cria nova) ``` O `x-idempotency-key` é **obrigatório**. Requisições sem este header serão rejeitadas. ## Webhook de Transferência Quando a transferência for processada, você receberá um webhook V2 do tipo `TRANSFER`: ```json theme={null} { "type": "TRANSFER", "data": { "id": 12345, "txId": null, "pixKey": "12345678909", "status": "LIQUIDATED", "payment": { "amount": "100.50", "currency": "BRL" }, "endToEndId": "E12345678901234567890123456789012", "creditDebitType": "DEBIT", "idempotencyKey": "550e8400-e29b-41d4-a716-446655440000", "creditorAccount": { "name": "João Silva", "document": "123.xxx.xxx-xx", "ispb": "18236120" }, "remittanceInformation": "Pagamento referente a NF 12345" } } ``` Veja a documentação completa do webhook TRANSFER ## Tipos de Chave PIX | Tipo | Formato | Exemplo | | -------- | ------------ | -------------------------------------- | | CPF | 11 dígitos | `12345678909` | | CNPJ | 14 dígitos | `12345678000195` | | E-mail | email válido | `joao@email.com` | | Telefone | +55DDDNUMERO | `+5511999999999` | | EVP | UUID | `7d9f0335-8dcc-4054-9bf9-0dbd61d36906` | ## Prioridade de Processamento * Processamento padrão na fila * Menor custo * Tempo de processamento variável * `creditorDocument` opcional * Processamento instantâneo * Validação imediata do destinatário * `creditorDocument` **obrigatório** * Ideal para pagamentos críticos ## Bloqueio de ISPBs Use `ispbDeny` para bloquear transferências para instituições específicas: ```json theme={null} { "pixKey": "joao@email.com", "payment": { "amount": 100.00 }, "ispbDeny": [ "12345678", // Bloquear banco X "87654321" // Bloquear banco Y ] } ``` Se a chave PIX pertencer a uma instituição bloqueada, a transferência será rejeitada. ## Erros Comuns | Código | Erro | Solução | | ------ | ------------------------ | -------------------------------------- | | 400 | Chave PIX inválida | Verifique o formato da chave | | 400 | Valor inválido | Use número com até 2 casas decimais | | 400 | Idempotency key faltando | Inclua header `x-idempotency-key` | | 401 | Token inválido | Renove o token de acesso | | 422 | Chave não encontrada | A chave não existe no DICT | | 422 | Saldo insuficiente | Verifique o saldo disponível | | 422 | ISPB bloqueado | A instituição está na lista `ispbDeny` | ## Próximos Passos Verifique o saldo disponível Processe notificações de transferência # Introdução Source: https://docs.firebanking.dev/pix-bacen/introduction API PIX compatível com a especificação do Banco Central do Brasil ## O que é PIX Bacen? A **API PIX Bacen** é uma versão da API Fire Banking que segue a especificação oficial do Banco Central do Brasil para o sistema de pagamentos instantâneos PIX. Esta versão foi desenvolvida para atender integradores que precisam de compatibilidade com o formato padrão BACEN. Esta API é uma alternativa à [API padrão Fire Banking](/api-reference/introduction). Ambas oferecem as mesmas funcionalidades, mas com formatos de requisição e resposta diferentes. ## Quando usar a API PIX Bacen? Use esta API quando: * Seu sistema já está integrado com outros PSPs que seguem a especificação BACEN * Você precisa manter compatibilidade com múltiplos provedores PIX * Sua aplicação foi construída seguindo a documentação oficial do Banco Central * Você prefere trabalhar com o formato de webhook V2 (envelope `{type, data}`) ## Principais diferenças Valores monetários são **strings** com 2 casas decimais (ex: `"123.45"`) ao invés de números. Webhooks usam formato envelope `{type, data}` com status `LIQUIDATED` ao invés de `CONFIRMED`. Usa `txid` para cobranças e `e2eid` para devoluções, seguindo nomenclatura BACEN. Contraparte dividida em `debtorAccount` (pagador) e `creditorAccount` (recebedor). ## Endpoints disponíveis | Endpoint | Método | Descrição | | --------------------------- | ------ | -------------------------------------- | | `/cob/:txid` | PUT | Criar cobrança imediata (QR Code PIX) | | `/pix/:e2eid/devolucao/:id` | PUT | Solicitar devolução de um PIX recebido | | `/dict/pix` | POST | Iniciar transferência PIX (Cash-Out) | | `/accounts/balances` | GET | Consultar saldo da conta | ## Comparação com API padrão | Operação | API Padrão | API PIX Bacen | | -------- | ---------------------- | ------------------------------- | | Cash-In | `POST /pix/cash-in` | `PUT /cob/:txid` | | Cash-Out | `POST /pix/cash-out` | `POST /dict/pix` | | Refund | `POST /pix/:id/refund` | `PUT /pix/:e2eid/devolucao/:id` | | Balance | `GET /balance` | `GET /accounts/balances` | ## Fluxo de integração ```mermaid theme={null} sequenceDiagram participant Cliente participant Fire Banking participant BACEN Note over Cliente,BACEN: 1. Autenticação Cliente->>Fire Banking: POST /oauth/token Fire Banking-->>Cliente: access_token Note over Cliente,BACEN: 2. Criar Cobrança Cliente->>Fire Banking: PUT /cob/{txid} Fire Banking->>BACEN: Registra cobrança Fire Banking-->>Cliente: QR Code + dados Note over Cliente,BACEN: 3. Pagamento (via app bancário) BACEN->>Fire Banking: Webhook pagamento Fire Banking->>Cliente: Webhook V2 (type: RECEIVE) ``` ## Próximos passos Configure a autenticação para acessar a API Saiba como ativar o modo PIX Bacen na sua conta Gere sua primeira cobrança PIX Entenda o formato de notificações V2 # Visão Geral Source: https://docs.firebanking.dev/pix-bacen/webhooks/overview Entenda o formato de webhooks V2 usado na API PIX Bacen ## O que são Webhooks V2? Webhooks V2 são notificações enviadas para sua aplicação quando eventos importantes ocorrem em suas transações PIX. O formato V2 usa uma estrutura de envelope `{type, data}` que facilita o processamento e oferece mais detalhes sobre cada evento. Para receber webhooks V2, sua conta deve estar com a versão de webhook configurada para V2. Veja [como ativar](/pix-bacen/activation). ## Estrutura Base Todos os webhooks V2 seguem esta estrutura: ```json theme={null} { "type": "RECEIVE" | "TRANSFER" | "REFUND", "data": { // Dados específicos do evento } } ``` ## Tipos de Evento | Type | Descrição | Equivalente V1 | | ---------- | ---------------------- | ------------------------------------ | | `RECEIVE` | PIX recebido (Cash-In) | `CashIn` | | `TRANSFER` | PIX enviado (Cash-Out) | `CashOut` | | `REFUND` | Devolução (In ou Out) | `CashInReversal` / `CashOutReversal` | ## Estrutura Completa do Data ```typescript theme={null} interface WebhookV2Data { // Identificadores id: number; // ID da transação txId: string | null; // Identificador da cobrança (txid) endToEndId: string | null; // End to End ID da transação PIX // Chave PIX pixKey: string | null; // Chave PIX utilizada // Status status: 'PENDING' | 'LIQUIDATED' | 'REFUNDED' | 'ERROR'; // Pagamento payment: { amount: string; // Valor (string com 2 decimais) currency: string; // Moeda (BRL) }; // Devoluções refunds: RefundInfo[]; // Lista de devoluções (vazio se não houver) // Datas createdAt: string; // Data de criação (ISO 8601) // Erro errorCode: string | null; // Código de erro (se houver) // Tipo de operação webhookType: 'RECEIVE' | 'TRANSFER' | 'REFUND'; creditDebitType: 'CREDIT' | 'DEBIT'; transactionType: 'PIX'; localInstrument: 'DICT'; // Contas debtorAccount: AccountInfo; // Pagador/Remetente creditorAccount: AccountInfo; // Recebedor/Destinatário // Idempotência idempotencyKey: string | null; // Dados adicionais ticketData: object; remittanceInformation: string | null; // Descrição da transação } interface AccountInfo { ispb: string | null; // Código ISPB do banco name: string | null; // Nome do banco issuer: string | null; // Código do banco number: string | null; // Número da conta document: string | null; // CPF/CNPJ (mascarado) accountType: string | null; // Tipo de conta } interface RefundInfo { status: 'PENDING' | 'LIQUIDATED' | 'ERROR'; payment: { amount: number; // Valor do refund (number!) currency: string; }; errorCode: string | null; eventDate: string; // Data do refund endToEndId: string | null; // E2E ID do refund information: string | null; // Descrição do refund } ``` ## Diferenças V1 vs V2 | Aspecto | V1 | V2 | | ----------- | ----------------- | ----------------------- | | Formato | Campos na raiz | Envelope `{type, data}` | | Tipo evento | `event: "CashIn"` | `type: "RECEIVE"` | | Aspecto | V1 | V2 | | -------------- | ----------- | ------------ | | Sucesso PIX | `CONFIRMED` | `LIQUIDATED` | | Sucesso Refund | `CONFIRMED` | `REFUNDED` | | Erro | `ERROR` | `ERROR` | | Aspecto | V1 | V2 | | ------- | --------------- | ----------------------------------- | | Campo | `counterpart` | `debtorAccount` / `creditorAccount` | | Banco | `bank.bankName` | `name` | | ISPB | `bank.bankISPB` | `ispb` | | Aspecto | V1 | V2 | | ------- | -------- | ---------- | | Tipo | `number` | `string` | | Formato | `100.00` | `"100.00"` | ## Mapeamento de Contas ### PIX Recebido (RECEIVE) ``` debtorAccount = Quem pagou (contraparte) creditorAccount = Sua conta (recebedor) creditDebitType = CREDIT ``` ### PIX Enviado (TRANSFER) ``` debtorAccount = Sua conta (pagador) creditorAccount = Quem recebeu (contraparte) creditDebitType = DEBIT ``` ### Devolução de Recebimento (REFUND - CashInReversal) ``` debtorAccount = Sua conta (devolvendo) creditorAccount = Quem vai receber de volta (contraparte) creditDebitType = DEBIT ``` ### Devolução de Envio (REFUND - CashOutReversal) ``` debtorAccount = Quem está devolvendo (contraparte) creditorAccount = Sua conta (recebendo de volta) creditDebitType = CREDIT ``` ## Configuração do Endpoint ### Requisitos * URL HTTPS obrigatória * Timeout máximo: 10 segundos * Resposta esperada: HTTP 2xx ### Autenticação Os webhooks são enviados com Basic Auth: ``` Authorization: Basic base64(username:password) ``` Configure as credenciais no painel ou entre em contato com o suporte. ## Exemplo de Handler ```typescript theme={null} import express from 'express'; const app = express(); app.use(express.json()); interface WebhookV2 { type: 'RECEIVE' | 'TRANSFER' | 'REFUND'; data: WebhookV2Data; } // Set para idempotência const processedIds = new Set(); app.post('/webhooks/pix', (req, res) => { const webhook: WebhookV2 = req.body; // Responder rapidamente res.status(200).json({ acknowledged: true }); // Verificar idempotência if (processedIds.has(webhook.data.id)) { console.log(`Webhook ${webhook.data.id} já processado`); return; } processedIds.add(webhook.data.id); // Processar por tipo switch (webhook.type) { case 'RECEIVE': handleReceive(webhook.data); break; case 'TRANSFER': handleTransfer(webhook.data); break; case 'REFUND': handleRefund(webhook.data); break; } }); function handleReceive(data: WebhookV2Data) { if (data.status === 'LIQUIDATED') { const amount = parseFloat(data.payment.amount); console.log(`PIX recebido: R$ ${amount}`); // Creditar no sistema } } function handleTransfer(data: WebhookV2Data) { if (data.status === 'LIQUIDATED') { console.log(`PIX enviado: ${data.endToEndId}`); // Confirmar transferência } else if (data.status === 'ERROR') { console.log(`PIX falhou: ${data.errorCode}`); // Reverter operação } } function handleRefund(data: WebhookV2Data) { if (data.status === 'REFUNDED') { const refund = data.refunds[0]; console.log(`Devolução: R$ ${refund.payment.amount}`); // Processar devolução } } ``` ## Retentativas Se seu endpoint não responder com HTTP 2xx em 10 segundos: | Tentativa | Intervalo | Acumulado | | --------- | ---------- | --------- | | 1ª | Imediato | 0 min | | 2ª | 5 minutos | 5 min | | 3ª | 5 minutos | 10 min | | 4ª | 15 minutos | 25 min | Após 4 tentativas sem sucesso, o webhook não será mais reenviado automaticamente. Implemente consulta periódica como fallback para garantir que nenhuma transação seja perdida. ## Próximos Passos PIX recebido PIX enviado Devolução # RECEIVE Source: https://docs.firebanking.dev/pix-bacen/webhooks/receive Webhook enviado quando um PIX é recebido (Cash-In) ## Visão Geral O webhook `RECEIVE` é enviado quando um PIX é recebido na sua conta. Este evento indica que alguém pagou um QR Code gerado pela sua aplicação ou fez uma transferência direta para sua chave PIX. ## Quando é enviado * Pagamento de QR Code (cobrança) confirmado * Transferência direta para chave PIX da conta ## Estrutura do Payload ```json theme={null} { "type": "RECEIVE", "data": { "id": 123, "txId": "7978c0c97ea847e78e8849634473c1f1", "pixKey": "7d9f0335-8dcc-4054-9bf9-0dbd61d36906", "status": "LIQUIDATED", "payment": { "amount": "100.00", "currency": "BRL" }, "refunds": [], "createdAt": "2024-01-15T10:30:00.000Z", "errorCode": null, "endToEndId": "E12345678901234567890123456789012", "ticketData": {}, "webhookType": "RECEIVE", "debtorAccount": { "ispb": "18236120", "name": "NU PAGAMENTOS S.A.", "issuer": "260", "number": "12345-6", "document": "123.xxx.xxx-xx", "accountType": null }, "idempotencyKey": null, "creditDebitType": "CREDIT", "creditorAccount": { "ispb": null, "name": null, "issuer": null, "number": null, "document": null, "accountType": null }, "localInstrument": "DICT", "transactionType": "PIX", "remittanceInformation": "Pagamento pedido #12345" } } ``` ## Campos Importantes Sempre `"RECEIVE"` para PIX recebido. ID da transação. Use para idempotência. Identificador da cobrança (txid do endpoint `/cob`). Pode ser `null` para transferências diretas. End to End ID - identificador único da transação PIX no Banco Central. Status da transação: * `LIQUIDATED`: Pagamento confirmado (sucesso) * `ERROR`: Falha no processamento Valor recebido. **String** com 2 casas decimais. Moeda. Sempre `"BRL"`. Dados de quem pagou (o pagador/remetente). Código ISPB do banco do pagador. Nome do banco do pagador. Código do banco (ex: "260" para Nubank). Número da conta do pagador. CPF/CNPJ do pagador (mascarado). Sempre `"CREDIT"` para recebimentos. Lista de devoluções. Vazio para transações sem devolução. Descrição da transferência (se informada pelo pagador). ## Processando o Webhook ### Exemplo Node.js ```typescript theme={null} interface ReceiveWebhook { type: 'RECEIVE'; data: { id: number; txId: string | null; status: 'LIQUIDATED' | 'ERROR'; payment: { amount: string; currency: string; }; endToEndId: string; debtorAccount: { name: string | null; document: string | null; }; remittanceInformation: string | null; }; } async function handleReceive(webhook: ReceiveWebhook) { const { data } = webhook; if (data.status !== 'LIQUIDATED') { console.log(`PIX não confirmado: ${data.status}`); return; } // Converter valor de string para number const amount = parseFloat(data.payment.amount); // Encontrar pedido pelo txId (se for cobrança) if (data.txId) { const order = await findOrderByTxId(data.txId); if (order) { await markOrderAsPaid(order.id, { amount, endToEndId: data.endToEndId, payer: data.debtorAccount.name, }); return; } } // Recebimento sem cobrança associada await createGenericCredit({ amount, endToEndId: data.endToEndId, payer: data.debtorAccount.name, description: data.remittanceInformation, }); } ``` ### Exemplo Python ```python theme={null} from decimal import Decimal def handle_receive(webhook: dict): data = webhook['data'] if data['status'] != 'LIQUIDATED': print(f"PIX não confirmado: {data['status']}") return # Converter valor amount = Decimal(data['payment']['amount']) # Processar por txId se existir if data.get('txId'): order = find_order_by_txid(data['txId']) if order: mark_order_as_paid( order_id=order.id, amount=amount, e2e_id=data['endToEndId'], payer=data['debtorAccount'].get('name') ) return # Crédito genérico create_generic_credit( amount=amount, e2e_id=data['endToEndId'], payer=data['debtorAccount'].get('name'), description=data.get('remittanceInformation') ) ``` ## Correlação com Cobrança Se o PIX foi pago via QR Code gerado pelo endpoint `/cob/:txid`, o campo `txId` conterá o identificador: ```json theme={null} { "type": "RECEIVE", "data": { "txId": "7978c0c97ea847e78e8849634473c1f1", // Mesmo txid do PUT /cob // ... } } ``` Use este campo para correlacionar com seus registros internos: ```typescript theme={null} // Criar cobrança const cobranca = await createCob('meu-txid-123', { valor: '100.00' }); // Salvar associação await saveOrder({ orderId: 'pedido-456', txId: 'meu-txid-123', status: 'PENDING' }); // No webhook RECEIVE if (webhook.data.txId === 'meu-txid-123') { await updateOrder('pedido-456', { status: 'PAID' }); } ``` ## Tratamento de Erros Se `status === 'ERROR'`, verifique o campo `errorCode`: ```typescript theme={null} if (data.status === 'ERROR') { console.error(`Erro no PIX: ${data.errorCode}`); // Notificar sobre falha await notifyPaymentError({ txId: data.txId, errorCode: data.errorCode, }); } ``` ## Idempotência Use `data.id` para evitar processamento duplicado: ```typescript theme={null} const PROCESSED_KEY = 'processed_webhooks'; async function handleWebhook(webhook: ReceiveWebhook) { const webhookId = `receive:${webhook.data.id}`; // Verificar se já processou const isProcessed = await redis.sismember(PROCESSED_KEY, webhookId); if (isProcessed) { console.log(`Webhook ${webhookId} já processado`); return; } // Marcar como processado ANTES de processar await redis.sadd(PROCESSED_KEY, webhookId); // Processar await handleReceive(webhook); } ``` ## Boas Práticas Retorne HTTP 200 imediatamente e processe de forma assíncrona. ```typescript theme={null} app.post('/webhook', (req, res) => { res.status(200).send(); // Responder primeiro handleWebhook(req.body) // Processar depois .catch(console.error); }); ``` Sempre verifique se `status === 'LIQUIDATED'` antes de creditar. Se você criou a cobrança via `/cob`, use o `txId` para encontrar o pedido correspondente. ```typescript theme={null} console.log({ event: 'PIX_RECEIVED', id: data.id, txId: data.txId, amount: data.payment.amount, e2eId: data.endToEndId, payer: data.debtorAccount.name, }); ``` ## Próximos Passos PIX enviado Devolução # REFUND Source: https://docs.firebanking.dev/pix-bacen/webhooks/refund Webhook enviado quando uma devolução PIX é processada ## Visão Geral O webhook `REFUND` é enviado quando uma devolução PIX é processada. Existem dois cenários: 1. **CashInReversal**: Você devolveu um PIX recebido (via `/pix/:e2eid/devolucao/:id`) 2. **CashOutReversal**: Alguém devolveu um PIX que você enviou ## Quando é enviado * Devolução de PIX recebido confirmada (você devolvendo) * Devolução de PIX enviado recebida (alguém devolvendo para você) ## Estrutura do Payload ```json theme={null} { "type": "REFUND", "data": { "id": 123, "txId": "7978c0c97ea847e78e8849634473c1f1", "pixKey": "7d9f0335-8dcc-4054-9bf9-0dbd61d36906", "status": "REFUNDED", "payment": { "amount": "100.00", "currency": "BRL" }, "refunds": [ { "status": "LIQUIDATED", "payment": { "amount": 50.00, "currency": "BRL" }, "errorCode": null, "eventDate": "2024-01-15T10:30:00.000Z", "endToEndId": "D12345678901234567890123456789012", "information": "Devolução solicitada pelo recebedor" } ], "createdAt": "2024-01-15T09:00:00.000Z", "errorCode": null, "endToEndId": "E12345678901234567890123456789012", "ticketData": {}, "webhookType": "REFUND", "debtorAccount": { "ispb": null, "name": null, "issuer": null, "number": null, "document": null, "accountType": null }, "idempotencyKey": "7978c0c97ea847e78e8849634473c1f1", "creditDebitType": "DEBIT", "creditorAccount": { "ispb": "18236120", "name": "NU PAGAMENTOS S.A.", "issuer": "260", "number": "12345-6", "document": "123.xxx.xxx-xx", "accountType": null }, "localInstrument": "DICT", "transactionType": "PIX", "remittanceInformation": "Devolução parcial" } } ``` ## Diferença entre CashInReversal e CashOutReversal **Você devolveu** um PIX recebido. ``` creditDebitType = DEBIT (saindo da sua conta) debtorAccount = Sua conta creditorAccount = Quem vai receber de volta ``` **Exemplo**: Você recebeu R$ 100, depois devolveu R$ 50. **Alguém devolveu** um PIX que você enviou. ``` creditDebitType = CREDIT (entrando na sua conta) debtorAccount = Quem está devolvendo creditorAccount = Sua conta ``` **Exemplo**: Você enviou R$ 100, o destinatário devolveu R$ 30. ## Campos Importantes Sempre `"REFUND"` para devoluções. ID da transação **original** (não da devolução). Status da transação original após a devolução: * `REFUNDED`: Devolução processada * `ERROR`: Falha na devolução Valor da transação **original**, não da devolução. Valor original. **String** com 2 casas decimais. Moeda. Sempre `"BRL"`. Lista de devoluções realizadas. Contém detalhes de cada devolução. Status da devolução: `LIQUIDATED` ou `ERROR`. Valor da devolução. **Atenção**: É `number`, não `string`! E2E ID da devolução (diferente do E2E da transação original). Data/hora da devolução. Descrição da devolução. Direção do dinheiro: * `DEBIT`: Saindo da sua conta (CashInReversal) * `CREDIT`: Entrando na sua conta (CashOutReversal) E2E ID da transação **original**. ## Processando o Webhook ### Exemplo Node.js ```typescript theme={null} interface RefundWebhook { type: 'REFUND'; data: { id: number; txId: string | null; status: 'REFUNDED' | 'ERROR'; payment: { amount: string; currency: string; }; refunds: Array<{ status: 'LIQUIDATED' | 'ERROR'; payment: { amount: number; // number, não string! currency: string; }; endToEndId: string; eventDate: string; information: string | null; }>; endToEndId: string; creditDebitType: 'CREDIT' | 'DEBIT'; }; } async function handleRefund(webhook: RefundWebhook) { const { data } = webhook; // Identificar tipo de devolução const isCashInReversal = data.creditDebitType === 'DEBIT'; if (isCashInReversal) { // Você devolveu um PIX recebido await handleCashInReversal(data); } else { // Alguém devolveu um PIX que você enviou await handleCashOutReversal(data); } } async function handleCashInReversal(data: RefundWebhook['data']) { // Encontrar transação original const original = await findTransactionByE2eId(data.endToEndId); // Processar cada devolução for (const refund of data.refunds) { if (refund.status === 'LIQUIDATED') { // Devolução confirmada - debitar do saldo await processRefundOut({ originalId: original.id, refundAmount: refund.payment.amount, // já é number refundE2eId: refund.endToEndId, }); console.log(`Devolvido R$ ${refund.payment.amount} do PIX ${original.id}`); } } } async function handleCashOutReversal(data: RefundWebhook['data']) { // Encontrar transferência original const original = await findTransferByE2eId(data.endToEndId); // Processar cada devolução recebida for (const refund of data.refunds) { if (refund.status === 'LIQUIDATED') { // Devolução recebida - creditar no saldo await processRefundIn({ originalId: original.id, refundAmount: refund.payment.amount, refundE2eId: refund.endToEndId, }); console.log(`Recebido R$ ${refund.payment.amount} de devolução`); } } } ``` ### Exemplo Python ```python theme={null} from decimal import Decimal def handle_refund(webhook: dict): data = webhook['data'] # Identificar tipo is_cash_in_reversal = data['creditDebitType'] == 'DEBIT' if is_cash_in_reversal: handle_cash_in_reversal(data) else: handle_cash_out_reversal(data) def handle_cash_in_reversal(data: dict): """Você devolveu um PIX recebido""" original = find_transaction_by_e2e(data['endToEndId']) for refund in data['refunds']: if refund['status'] == 'LIQUIDATED': # Já é number, converter para Decimal amount = Decimal(str(refund['payment']['amount'])) process_refund_out( original_id=original.id, refund_amount=amount, refund_e2e=refund['endToEndId'] ) def handle_cash_out_reversal(data: dict): """Alguém devolveu um PIX que você enviou""" original = find_transfer_by_e2e(data['endToEndId']) for refund in data['refunds']: if refund['status'] == 'LIQUIDATED': amount = Decimal(str(refund['payment']['amount'])) process_refund_in( original_id=original.id, refund_amount=amount, refund_e2e=refund['endToEndId'] ) ``` ## Devoluções Parciais Uma transação pode ter múltiplas devoluções parciais. O array `refunds` contém todas: ```json theme={null} { "type": "REFUND", "data": { "payment": { "amount": "100.00" }, // Valor original: R$ 100 "refunds": [ { "payment": { "amount": 30.00 }, // 1ª devolução: R$ 30 "eventDate": "2024-01-15T10:00:00Z" }, { "payment": { "amount": 50.00 }, // 2ª devolução: R$ 50 "eventDate": "2024-01-15T11:00:00Z" } ] } } ``` **Cálculo do saldo de devolução:** ```typescript theme={null} const valorOriginal = parseFloat(data.payment.amount); // 100.00 const totalDevolvido = data.refunds .filter(r => r.status === 'LIQUIDATED') .reduce((sum, r) => sum + r.payment.amount, 0); // 80.00 const saldoDisponivel = valorOriginal - totalDevolvido; // 20.00 ``` ## Atenção: amount é number em refunds Dentro do array `refunds`, o campo `payment.amount` é **number**, não **string**! ```typescript theme={null} // data.payment.amount → string "100.00" // data.refunds[0].payment.amount → number 50.00 // CORRETO const refundAmount = data.refunds[0].payment.amount; // 50.00 (number) // ERRADO - não precisa de parseFloat const refundAmount = parseFloat(data.refunds[0].payment.amount); ``` ## Idempotência Use uma combinação de `data.id` e `refunds[].endToEndId` para idempotência: ```typescript theme={null} async function handleWebhook(webhook: RefundWebhook) { for (const refund of webhook.data.refunds) { const key = `refund:${webhook.data.id}:${refund.endToEndId}`; const isProcessed = await redis.sismember('processed', key); if (isProcessed) { continue; // Já processado } await redis.sadd('processed', key); await processRefund(webhook.data, refund); } } ``` ## Tratamento de Erros Se `refund.status === 'ERROR'`, a devolução falhou: ```typescript theme={null} for (const refund of data.refunds) { if (refund.status === 'ERROR') { console.error(`Devolução falhou: ${refund.errorCode}`); // Notificar sobre falha await notifyRefundFailed({ originalE2eId: data.endToEndId, refundE2eId: refund.endToEndId, errorCode: refund.errorCode, }); } } ``` ## Boas Práticas Use `creditDebitType` para determinar se é CashInReversal (DEBIT) ou CashOutReversal (CREDIT). O array `refunds` pode conter múltiplas devoluções parciais. Itere por todas. * `data.payment.amount` é **string** * `data.refunds[].payment.amount` é **number** * CashInReversal: Debita do seu saldo * CashOutReversal: Credita no seu saldo ## Próximos Passos PIX recebido PIX enviado # TRANSFER Source: https://docs.firebanking.dev/pix-bacen/webhooks/transfer Webhook enviado quando um PIX é enviado (Cash-Out) ## Visão Geral O webhook `TRANSFER` é enviado quando uma transferência PIX iniciada pela sua aplicação é processada. Este evento indica o resultado (sucesso ou falha) de uma chamada ao endpoint `/dict/pix`. ## Quando é enviado * Transferência PIX processada com sucesso (`LIQUIDATED`) * Transferência PIX falhou (`ERROR`) ## Estrutura do Payload ```json theme={null} { "type": "TRANSFER", "data": { "id": 456, "txId": null, "pixKey": "destino@email.com", "status": "LIQUIDATED", "payment": { "amount": "100.50", "currency": "BRL" }, "refunds": [], "createdAt": "2024-01-15T10:30:00.000Z", "errorCode": null, "endToEndId": "E12345678901234567890123456789012", "ticketData": {}, "webhookType": "TRANSFER", "debtorAccount": { "ispb": null, "name": null, "issuer": null, "number": null, "document": null, "accountType": null }, "idempotencyKey": "550e8400-e29b-41d4-a716-446655440000", "creditDebitType": "DEBIT", "creditorAccount": { "ispb": "18236120", "name": "NU PAGAMENTOS S.A.", "issuer": "260", "number": "12345-6", "document": "123.xxx.xxx-xx", "accountType": null }, "localInstrument": "DICT", "transactionType": "PIX", "remittanceInformation": "Pagamento NF 12345" } } ``` ## Campos Importantes Sempre `"TRANSFER"` para PIX enviado. ID da transação. Mesmo valor retornado no `POST /dict/pix`. End to End ID - identificador único da transação PIX no Banco Central. Status da transferência: * `LIQUIDATED`: Transferência confirmada (sucesso) * `ERROR`: Falha na transferência Valor transferido. **String** com 2 casas decimais. Moeda. Sempre `"BRL"`. Chave de idempotência enviada no header `x-idempotency-key` da requisição original. Dados de quem recebeu (o destinatário). Código ISPB do banco do destinatário. Nome do banco do destinatário. Código do banco (ex: "260" para Nubank). Número da conta do destinatário. CPF/CNPJ do destinatário (mascarado). Sempre `"DEBIT"` para transferências enviadas. Código de erro quando `status === 'ERROR'`. Pode ser `null` em caso de sucesso. Descrição da transferência (campo `description` enviado na requisição). ## Processando o Webhook ### Exemplo Node.js ```typescript theme={null} interface TransferWebhook { type: 'TRANSFER'; data: { id: number; status: 'LIQUIDATED' | 'ERROR'; payment: { amount: string; currency: string; }; endToEndId: string; idempotencyKey: string; creditorAccount: { name: string | null; document: string | null; }; errorCode: string | null; }; } async function handleTransfer(webhook: TransferWebhook) { const { data } = webhook; // Encontrar transferência pelo idempotencyKey const transfer = await findTransferByIdempotencyKey(data.idempotencyKey); if (!transfer) { console.warn(`Transferência não encontrada: ${data.idempotencyKey}`); return; } if (data.status === 'LIQUIDATED') { // Sucesso - confirmar transferência await updateTransfer(transfer.id, { status: 'COMPLETED', endToEndId: data.endToEndId, completedAt: new Date(), }); // Notificar usuário await notifyTransferSuccess({ transferId: transfer.id, amount: parseFloat(data.payment.amount), recipient: data.creditorAccount.name, }); } else if (data.status === 'ERROR') { // Falha - reverter await updateTransfer(transfer.id, { status: 'FAILED', errorCode: data.errorCode, }); // Notificar usuário await notifyTransferFailed({ transferId: transfer.id, errorCode: data.errorCode, }); // Liberar saldo bloqueado await releaseBlockedBalance(transfer.id); } } ``` ### Exemplo Python ```python theme={null} from decimal import Decimal def handle_transfer(webhook: dict): data = webhook['data'] # Encontrar transferência transfer = find_transfer_by_idempotency_key(data['idempotencyKey']) if not transfer: print(f"Transferência não encontrada: {data['idempotencyKey']}") return if data['status'] == 'LIQUIDATED': # Sucesso update_transfer( transfer_id=transfer.id, status='COMPLETED', e2e_id=data['endToEndId'] ) notify_transfer_success( transfer_id=transfer.id, amount=Decimal(data['payment']['amount']), recipient=data['creditorAccount'].get('name') ) elif data['status'] == 'ERROR': # Falha update_transfer( transfer_id=transfer.id, status='FAILED', error_code=data['errorCode'] ) notify_transfer_failed( transfer_id=transfer.id, error_code=data['errorCode'] ) # Liberar saldo release_blocked_balance(transfer.id) ``` ## Correlação com Requisição Use `idempotencyKey` para correlacionar o webhook com sua requisição original: ```typescript theme={null} // 1. Criar transferência const idempotencyKey = crypto.randomUUID(); const transfer = await createTransfer(idempotencyKey, { pixKey: 'destino@email.com', amount: 100.50, }); // 2. Salvar associação await saveTransfer({ id: transfer.id, idempotencyKey, status: 'PENDING', }); // 3. No webhook TRANSFER const savedTransfer = await findByIdempotencyKey(webhook.data.idempotencyKey); // savedTransfer.id corresponde à transferência original ``` ## Tratamento de Erros Códigos de erro comuns: | Código | Descrição | Ação Recomendada | | ---------------------- | ---------------------------- | ----------------------------------- | | `INSUFFICIENT_BALANCE` | Saldo insuficiente | Verificar saldo antes de transferir | | `INVALID_KEY` | Chave PIX inválida | Verificar chave com usuário | | `KEY_NOT_FOUND` | Chave não encontrada no DICT | Solicitar chave válida | | `ACCOUNT_BLOCKED` | Conta bloqueada | Contatar suporte | | `TIMEOUT` | Timeout no processamento | Tentar novamente | ```typescript theme={null} if (data.status === 'ERROR') { switch (data.errorCode) { case 'INSUFFICIENT_BALANCE': // Notificar saldo insuficiente await notifyInsufficientBalance(transfer); break; case 'INVALID_KEY': case 'KEY_NOT_FOUND': // Solicitar nova chave ao usuário await requestNewPixKey(transfer); break; case 'TIMEOUT': // Pode tentar novamente com nova idempotency key await retryTransfer(transfer); break; default: // Erro genérico await notifyGenericError(transfer, data.errorCode); } } ``` ## Fluxo de Saldo ```mermaid theme={null} sequenceDiagram participant App participant API participant Banco Note over App,Banco: Saldo: available=1000, blocked=0 App->>API: POST /dict/pix (R$ 100) API-->>App: { type: PENDING } Note over App,Banco: Saldo: available=900, blocked=100 alt Sucesso Banco->>API: Confirmação API->>App: Webhook TRANSFER (LIQUIDATED) Note over App,Banco: Saldo: available=900, blocked=0 else Falha Banco->>API: Erro API->>App: Webhook TRANSFER (ERROR) Note over App,Banco: Saldo: available=1000, blocked=0 end ``` ## Idempotência Use `data.id` para evitar processamento duplicado: ```typescript theme={null} async function handleWebhook(webhook: TransferWebhook) { const webhookId = `transfer:${webhook.data.id}`; const isProcessed = await redis.sismember('processed', webhookId); if (isProcessed) { return; // Já processado } await redis.sadd('processed', webhookId); await handleTransfer(webhook); } ``` ## Boas Práticas Salve o `idempotencyKey` junto com a transferência para facilitar a correlação no webhook. Implemente tratamento tanto para `LIQUIDATED` quanto para `ERROR`. Se a transferência falhar, o saldo bloqueado deve ser liberado. Certifique-se de atualizar seu sistema. Informe o usuário sobre o resultado da transferência, especialmente em caso de falha. ## Próximos Passos PIX recebido Devolução