Pular para o conteúdo principal

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 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érioCash-Out por Chave PIXCash-Out via QR Code
EndpointPOST /api/pix/cash-outPOST /api/pix/cash-out-qrcode
Quando usarVocê conhece a chave PIX do destinatárioVocê tem o QR Code gerado pelo recebedor
Dados do destinatárioObrigatórios (chave, tipo, nome, documento)Embutidos no QR Code (opcionais no request)
Validação de valorApenas saldo e limitesSaldo, limites + valor do QR Code vs valor informado
Tipos de chaveCPF, CNPJ, email, telefone, aleatóriaN/A (informação dentro do QR Code)
Webhook de confirmaçãoEvento CashOutMesmo evento CashOut
RespostaMesma estruturaMesma 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

{
  "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

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)

{
  "transactionId": "456",
  "externalId": "QRPAY-987654-20240119",
  "status": "PENDING",
  "generateTime": "2024-01-19T14:30:00.000Z"
}

Parâmetros da Requisição

value
number
obrigatório
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.01Exemplo: 15.50
qrCode
string
obrigatório
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 caracteresMáximo: 500 caracteresFormato: Deve iniciar com 000201 (padrão EMV PIX do Banco Central)Exemplo: "00020126580014br.gov.bcb.pix0136a1b2c3d4-e5f6-7890-abcd-ef1234567890520400005303986540515.505802BR5925DESTINATARIO LTDA6009SAO PAULO62070503***6304ABCD"
externalId
string
obrigatório
Identificador externo único da transação. Garante idempotência — enviar o mesmo externalId duas vezes resulta em erro 409.Máximo: 255 caracteresRecomendação: Use um formato que garanta unicidadeExemplo: "QRPAY-987654-20240119"
description
string
Descrição do pagamento que aparecerá no extrato do destinatário.Máximo: 140 caracteresPadrão: VazioExemplo: "Pagamento fornecedor XYZ via QR Code"
name
string
Nome do destinatário. Opcional — quando omitido, os dados do QR Code são utilizados.Exemplo: "Destinatario Ltda"
document
string
CPF ou CNPJ do destinatário (apenas números). Opcional — quando omitido, os dados do QR Code são utilizados.CPF: 11 dígitosCNPJ: 14 dígitosExemplo: "12345678000190"

Estrutura da Resposta

transactionId
string
obrigatório
ID interno da transação gerada pela Fire Banking.Exemplo: "456"
externalId
string
obrigatório
ID externo fornecido na requisição (mesmo valor do input).Exemplo: "QRPAY-987654-20240119"
status
string
obrigatório
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
generateTime
string
obrigatório
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

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<CashOutQrCodeResponse> {
  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<CashOutQrCodeResponse>(
      '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

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

function payWithQrCode(
    string $token,
    string $qrCode,
    float $amount,
    ?string $description = null
): array {
    $url = 'https://api.public.firebanking.com.br/api/pix/cash-out-qrcode';

    $payload = [
        'value' => 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

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

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

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:
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ódigoErroDescrição
201Pagamento PIX via QR Code iniciado com sucesso
400INVALID_QR_CODEQR Code inválido ou malformado
400QR_CODE_VALUE_MISMATCHValor informado diverge do valor embutido no QR Code
400INSUFFICIENT_BALANCESaldo insuficiente para realizar a transação
401Token não fornecido, expirado ou inválido
409DUPLICATE_EXTERNAL_IDexternalId já utilizado em outra transação
Consulte a Referência da API 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.
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