PIX Cash-Out vía QR Code
Descripción General
El endpoint de PIX Cash-Out vía QR Code le permite realizar pagos PIX a partir de un QR Code escaneado o copiado (copiar y pegar). El QR Code debe seguir el estándar EMV del Banco Central de Brasil. Los datos del destinatario se extraen automáticamente del QR Code, simplificando el proceso de pago.
Este endpoint requiere un Bearer token válido. Consulte la documentación de autenticación para más detalles.
Clave PIX vs QR Code: ¿Qué endpoint usar?
La API de Avista ofrece dos endpoints para enviar pagos PIX. Elija el más adecuado para su caso de uso:
| Criterio | Cash-Out por Clave PIX | Cash-Out vía QR Code |
|---|---|---|
| Endpoint | POST /api/pix/cash-out | POST /api/pix/cash-out-qrcode |
| Cuándo usar | Conoce la clave PIX del destinatario | Tiene el QR Code generado por el receptor |
| Datos del destinatario | Requeridos (clave, tipo, nombre, documento) | Incrustados en el QR Code (opcionales en la solicitud) |
| Validación de valor | Solo saldo y límites | Saldo, límites + valor del QR Code vs valor proporcionado |
| Tipos de clave | CPF, CNPJ, email, teléfono, aleatoria | N/A (información dentro del QR Code) |
| Webhook de confirmación | Evento CashOut | Mismo evento CashOut |
| Respuesta | Misma estructura | Misma estructura |
- Use Cash-Out por Clave cuando su aplicación ya tiene los datos del destinatario (ej.: nómina, pagos programáticos)
- Use Cash-Out vía QR Code cuando el pago se inicia desde un QR Code escaneado (ej.: PDV, pago de facturas, copiar y pegar)
Ambos endpoints retornan la misma estructura de respuesta y disparan el mismo webhook CashOut al confirmar.
Características
- Pago vía QR Code estático o dinámico
- Validación automática del valor incrustado en el QR Code
- Verificación automática de saldo antes del envío
- Identificación única vía
externalId(idempotencia) - Cálculo automático de tarifas
Endpoint
POST /api/pix/cash-out-qrcode
Realiza un pago PIX a partir de un QR Code.
Encabezados Requeridos
Authorization: Bearer {token}
Content-Type: application/jsonCuerpo de la Solicitud
{
"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"
}Solicitud
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"
}'Respuesta (201 Created)
{
"transactionId": "456",
"externalId": "QRPAY-987654-20240119",
"status": "PENDING",
"generateTime": "2024-01-19T14:30:00.000Z"
}Parámetros de la Solicitud
valuenumberobrigatorioMonto del pago en BRL (Reales brasileños). Debe tener como máximo 2 decimales. Si el QR Code contiene un valor incrustado, el valor proporcionado debe coincidir (tolerancia de 1 centavo).
Mínimo: 0.01
Ejemplo: 15.50
qrCodestringobrigatorioContenido del QR Code PIX (cadena EMV). Puede obtenerse mediante escaneo con cámara o del campo copiar y pegar.
Mínimo: 50 caracteres
Máximo: 500 caracteres
Formato: Debe comenzar con 000201 (estándar EMV PIX del Banco Central)
Ejemplo: "00020126580014br.gov.bcb.pix0136a1b2c3d4-e5f6-7890-abcd-ef1234567890520400005303986540515.505802BR5925DESTINATARIO LTDA6009SAO PAULO62070503***6304ABCD"
externalIdstringobrigatorioIdentificador externo único de la transacción. Garantiza idempotencia -- enviar el mismo externalId dos veces resulta en un error 409.
Máximo: 255 caracteres
Recomendación: Use un formato que garantice unicidad
Ejemplo: "QRPAY-987654-20240119"
descriptionstringDescripción del pago que aparecerá en el extracto del destinatario.
Máximo: 140 caracteres
Predeterminado: Vacío
Ejemplo: "Pagamento fornecedor XYZ via QR Code"
namestringNombre del destinatario. Opcional -- cuando se omite, se usan los datos del QR Code.
Ejemplo: "Destinatario Ltda"
documentstringCPF o CNPJ del destinatario (solo números). Opcional -- cuando se omite, se usan los datos del QR Code.
CPF: 11 dígitos
CNPJ: 14 dígitos
Ejemplo: "12345678000190"
Estructura de la Respuesta
transactionIdstringsempre presenteID interno de la transacción generado por Avista.
Ejemplo: "456"
externalIdstringsempre presenteID externo proporcionado en la solicitud (mismo valor que la entrada).
Ejemplo: "QRPAY-987654-20240119"
statusstringsempre presenteEstado actual de la transacción.
Valores posibles:
PENDING: Pago en procesoCONFIRMED: Pago confirmado y completadoERROR: Error de procesamiento
Ejemplo: "PENDING"
Nota: La mayoría de los pagos PIX se confirman en pocos segundos
generateTimestringsempre presenteFecha y hora de creación del pago (ISO 8601 UTC).
Ejemplo: "2024-01-19T14:30:00.000Z"
Ejemplos de Implementación
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('QR Code payment initiated!');
console.log(`Transaction ID: ${response.data.transactionId}`);
console.log(`Status: ${response.data.status}`);
console.log(`Amount: R$ ${amount.toFixed(2)}`);
return response.data;
} catch (error) {
if (axios.isAxiosError(error)) {
const errorData = error.response?.data;
console.error('Error making payment:', errorData);
if (error.response?.status === 400) {
if (errorData?.code === 'INVALID_QR_CODE') {
throw new Error('Invalid or malformed QR Code');
}
if (errorData?.code === 'QR_CODE_VALUE_MISMATCH') {
throw new Error('Provided value differs from the QR Code value');
}
if (errorData?.code === 'INSUFFICIENT_BALANCE') {
throw new Error('Insufficient balance to make the payment');
}
throw new Error('Invalid data: ' + errorData?.message);
}
if (error.response?.status === 409) {
throw new Error('externalId already used in another transaction');
}
throw new Error(errorData?.message || 'Error making QR Code payment');
}
throw error;
}
}
// Usage - Payment via scanned QR Code
const qrCodeContent = '00020126580014br.gov.bcb.pix0136a1b2c3d4...6304ABCD';
payWithQrCode('your_token_here', 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:
"""
Send a PIX payment via QR Code
Args:
token: Valid Bearer token
qr_code: PIX QR Code content (EMV string)
amount: Amount in BRL
description: Payment description (optional)
Returns:
Initiated payment data
"""
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('QR Code payment initiated!')
print(f"Transaction ID: {data['transactionId']}")
print(f"Status: {data['status']}")
print(f"Amount: 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('Invalid or malformed QR Code')
if code == 'QR_CODE_VALUE_MISMATCH':
raise Exception('Provided value differs from the QR Code value')
if code == 'INSUFFICIENT_BALANCE':
raise Exception('Insufficient balance to make the payment')
raise Exception(f"Invalid data: {error_data.get('message')}")
if e.response.status_code == 409:
raise Exception('externalId already used in another transaction')
raise Exception(f"Error making payment: {error_data.get('message', str(e))}")
# Usage
token = 'your_token_here'
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('Invalid or malformed QR Code');
}
if ($errorCode === 'QR_CODE_VALUE_MISMATCH') {
throw new Exception('Provided value differs from the QR Code value');
}
if ($errorCode === 'INSUFFICIENT_BALANCE') {
throw new Exception('Insufficient balance to make the payment');
}
}
if ($httpCode === 409) {
throw new Exception('externalId already used in another transaction');
}
throw new Exception("Error making payment: $errorMessage");
}
$data = json_decode($response, true);
echo "QR Code payment initiated!" . PHP_EOL;
echo "Transaction ID: {$data['transactionId']}" . PHP_EOL;
echo "Status: {$data['status']}" . PHP_EOL;
echo "Amount: R$ " . number_format($amount, 2, ',', '.') . PHP_EOL;
return $data;
}
// Usage
$token = 'your_token_here';
$qrCode = '00020126580014br.gov.bcb.pix0136a1b2c3d4...6304ABCD';
$payment = payWithQrCode($token, $qrCode, 15.50, 'Pagamento fornecedor XYZ');Validación de QR Code
El QR Code PIX sigue el estándar EMV (Europay, Mastercard, Visa) definido por el Banco Central de Brasil. Antes de enviarlo a la API, puede validarlo localmente:
function isValidPixQrCode(qrCode: string): boolean {
// Check minimum and maximum length
if (qrCode.length < 50 || qrCode.length > 500) {
return false;
}
// Check mandatory EMV prefix
if (!qrCode.startsWith('000201')) {
return false;
}
return true;
}Estructura del QR Code EMV PIX:
000201-- Payload Format Indicator (obligatorio)0102XX-- Point of Initiation Method (11= estático,12= dinámico)- Campos con datos del receptor, valor, ciudad, etc.
6304XXXX-- CRC16 (checksum de validación)
La validación completa del QR Code (decodificación EMV, verificación CRC y extracción de datos) se realiza automáticamente por la API. La validación local sirve solo para filtrar QR Codes claramente inválidos.
Códigos de Respuesta
| Código | Error | Descripción |
|---|---|---|
201 | -- | Pago PIX vía QR Code iniciado exitosamente |
400 | INVALID_QR_CODE | QR Code inválido o malformado |
400 | QR_CODE_VALUE_MISMATCH | El valor proporcionado difiere del valor incrustado en el QR Code |
400 | INSUFFICIENT_BALANCE | Saldo insuficiente para completar la transacción |
401 | -- | Token no proporcionado, expirado o inválido |
409 | DUPLICATE_EXTERNAL_ID | externalId ya utilizado en otra transacción |
Consulte la Referencia de la API para detalles completos de los campos de respuesta.
Mejores Prácticas
Notas Importantes
- Monto mínimo: R$ 0.01
- Formato del QR Code: Debe comenzar con
000201y tener entre 50 y 500 caracteres - QR Codes dinámicos: Se aceptan QR Codes sin valor incrustado -- el campo
valuedefine el monto del pago - QR Codes estáticos con valor: El valor proporcionado en
valuedebe coincidir con el valor incrustado en el QR Code (tolerancia de 1 centavo)