PIX Cash-In -- Generar QR Code
Genera un cobro PIX con QR Code para recepcion de valores en la cuenta asociada a su API Key.
Endpoint
POST /api/external/pix/cash-inHeaders
| Header | Tipo | Obligatorio | Descripcion |
|---|---|---|---|
Authorization | String | Si | ApiKey {client_id}:{client_secret} |
Content-Type | String | Si | application/json |
hmac | String | Si | Firma HMAC-SHA512 del body (mas informacion) |
Idempotency-Key | String | No | Clave unica para evitar procesamiento duplicado (max 256 chars) |
X-Key-Case | String | No | Defina como camelCase para recibir los campos de la respuesta en camelCase (default es snake_case) |
Request Body
| Campo | Tipo | Obligatorio | Descripcion | Ejemplo |
|---|---|---|---|---|
amount | Integer | Si | Valor en centavos (R$ 30,00 = 3000) | 3000 |
description | String | No | Descripcion del cobro. Si se omite, el default es "Cobranca PIX". | "Pedido #1234" |
external_id | String | No | Identificador de su sistema para rastreo. Max 128 chars. Solo a-zA-Z0-9._:- (valores fuera del estandar son silenciosamente descartados y el campo vuelve como null -- vea aviso abajo). Retornado en respuestas y webhooks. | "order-9876" |
pix_key | String | No | Clave PIX especifica para generar el QR Code. Si se omite, usa la clave activa mas reciente de la cuenta (orden por inserted_at DESC). | "12345678901" |
city | String | No | Ciudad del receptor en el QR Code. Default: SAO PAULO. Truncada automaticamente a 15 caracteres. | "RIO DE JANEIRO" |
expiration_seconds | Integer | No | Validez del QR Code en segundos. Rango 60–86400 (1 min a 24h); fuera de rango retorna 400. Si se omite, usa el default de la cuenta y, luego, el default del sistema (15 min). Use para limitar la exposicion de QRs dinamicos (ej.: 300 = 5 min, antifraude). | 300 |
Envie amount SIEMPRE como entero (en centavos)
El campo amount DEBE ser un entero en centavos. NUNCA envie valores float/decimal:
- ✅
"amount": 3000→ R$ 30,00 - ❌
"amount": 30.00→ interpretado como30centavos = R$ 0,30 (cobro 100× menor que lo pretendido) - ❌
"amount": 30→ R$ 0,30 (tambien incorrecto)
En JavaScript, convierta siempre con Math.round antes de enviar:
const valorEmReais = 30.0;
const amount = Math.round(valorEmReais * 100); // 3000En Python: amount = round(valor_en_reales * 100). En Go: amount := int(math.Round(valorEnReales * 100)).
Valores monetarios (entrada × respuesta)
Los valores de entrada son en centavos (R$ 1,00 = 100). Los valores de respuesta son en unidades base (R$ 1,00 = 10000). Para convertir la respuesta a reales, divida por 10.000. Nunca use punto flotante en ningun sentido.
external_id invalido = descarte silencioso
Si el external_id contiene caracteres fuera de a-zA-Z0-9._:-, sobrepasa 128 bytes, o es solo espacios en blanco, el servidor descarta silenciosamente el valor y la respuesta vuelve con "external_id": null (ningun error es retornado). Siempre verifique el external_id presente en la respuesta antes de usarlo para conciliacion -- si vino null, su identificador fue rechazado por la validacion.
Ejemplo
BODY='{"amount":3000,"description":"Pedido #1234","external_id":"order-9876"}'
HMAC=$(echo -n "$BODY" | openssl dgst -sha512 -hmac "$CLIENT_SECRET" | awk '{print $2}')
curl -X POST https://api.minhakonta.com/api/external/pix/cash-in \
-H "Authorization: ApiKey $CLIENT_ID:$CLIENT_SECRET" \
-H "Content-Type: application/json" \
-H "hmac: $HMAC" \
-d "$BODY"Respuesta de Exito (200)
{
"worked": true,
"transaction_id": "7popu57v6us7p6pcicgq12345",
"qr_code": "00020126580014br.gov.bcb.pix...",
"qr_code_image": "data:image/png;base64,iVBORw0KGgo...",
"external_id": "order-9876",
"amount": 300000,
"status": "active",
"type": "dynamic",
"location_url": "https://qrcode-h.minhakonta.com/pix/v2/a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d",
"expires_at": "2026-03-07T16:30:00Z"
}| Campo | Tipo | Descripcion |
|---|---|---|
worked | Boolean | true indica exito en la operacion |
transaction_id | String | null | Identificador unico del cobro (tx_id del QR Code, alfanumerico max. 35 chars per spec BACEN). null solo en fallas internas raras de persistencia |
qr_code | String | null | Codigo EMV copia-y-pega para pago. null (valor JSON literal, no el string "null") solo en fallas internas raras de persistencia |
qr_code_image | String | Imagen del QR Code codificada en base64 (PNG, prefijo data:image/png;base64,). String vacio ("") en fallas internas raras |
external_id | String | null | Su identificador, retornado tal como fue enviado. null si no informado, si invalido (vea aviso arriba), o si descartado por la sanitizacion |
amount | Integer | Valor del cobro en unidades base (÷ 10.000 para reales). 300000 = R$ 30,00 |
status | String | Status inicial siempre active (QR Code activo para pago). La evolucion posterior (paid/expired/cancelled) es visible en consultas y webhooks (vea ciclo de vida del QR) |
type | String | null | Tipo del QR Code: dynamic (default desde abril/2026) o static. Dinamico requiere que el banco del pagador consulte location_url y valide el JWS |
location_url | String | null | URL de consulta del payload dinamico del BACEN (ej: https://qrcode-h.minhakonta.com/pix/v2/{uuid}). No es el JWS en si -- el banco del pagador hace un GET en este endpoint para recibir el payload ISO 20022 firmado (JWS PS256). Presente solo en QR dinamico; null en estatico |
expires_at | String | null | Fecha/hora de expiracion del QR Code en ISO 8601 UTC con sufijo Z (ej: 2026-03-07T16:30:00Z). null si QR sin expiracion |
QR dinamico es el default
Todas las cuentas en produccion usan QR dinamico por default desde abril/2026. El QR dinamico incluye una URL (location_url) que el banco del pagador consulta para obtener el payload firmado via JWS -- compatibilidad maximizada con bancos BACEN-strict.
Respuesta de Error (400)
{
"worked": false,
"detail": "O campo amount é obrigatório"
}Respuesta de Error (401)
{
"error": {
"status": 401,
"message": "Missing API key credentials. Use Authorization: ApiKey <client_id>:<client_secret>"
}
}Respuesta de Error (422)
{
"worked": false,
"detail": "Invalid HMAC signature"
}Flujo Recomendado
- Genere el cobro con este endpoint
- Muestre el QR Code (
qr_code_image) o el codigo copia-y-pega (qr_code) al pagador - Reciba la confirmacion via Webhook cuando el pago sea efectuado
- O consulte el status: por ID, por E2E, por Tag
Validez del QR Code
Por default, el QR Code tiene validez de 15 minutos (default del sistema). Puede sobrescribirlo por solicitud con expiration_seconds (rango 60–86400s), o definir un default por cuenta (qrcode_expiration_seconds). Precedencia: expiration_seconds (solicitud) > default de la cuenta > default del sistema. Verifique siempre el expires_at retornado en la respuesta para la fecha/hora exacta.
Despues del vencimiento, el status cambia a expired automaticamente (via worker interno, rutina cada 5 minutos). Para cobros cancelados manualmente por el merchant, el status es cancelled. Vea la lista completa de status en Consultar Cash-In por ID.
Ciclo de Vida del QR Code
El QR Code pasa por estos estados despues de la creacion:
| Estado | Origen | Descripcion |
|---|---|---|
active | Inicial | QR generado con exito, listo para ser escaneado/pagado |
paid | Pago recibido | Pago BACEN confirmado y linkeado al QR (via worker interno despues del ACCC) |
expired | Worker QrExpirationChecker | TTL alcanzo expires_at. Worker rueda cada 5 min marcando QRs active expirados y disparando webhook pix.charge.expired |
cancelled | Manual o en masa | Cancelado por el merchant (via portal) o en operaciones administrativas (ej: migracion estatico→dinamico en abril/2026 cancelo QRs estaticos activos con valor) |
used | Legacy | Estado intermedio transitorio del pipeline antiguo. Clientes nuevos deben tratar como equivalente a paid |
En consultas, paid puede aparecer como settled
Los endpoints GET /transactions/:id y equivalentes retornan el campo status del punto de vista de la transaccion (no del QR). Un QR pagado aparece como status: "settled" en la consulta, con type: "pix" (o type: "pix_qrcode" en ventana corta pos-settlement mientras la linea final en transactions aun es persistida). Vea Consultar Cash-In por ID para la tabla completa.
