# 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-in
```

## Headers

| 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](/es/hmac)) |
| `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 **3–86400** (3 s 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` |
| `include_image` | Boolean | No | Si `false`, la respuesta retorna `qr_code_image` vacío (`""`) y se omite la generación de la imagen. Use cuando solo necesita el código copia y pega (`qr_code`). Default: `true`. | `false` |
| `expected_payer_document` | String | No | CPF (11 digitos) o CNPJ (14 digitos) del pagador esperado. Entrada formateada (puntos/guiones/barra) es aceptada y normalizada a digitos; longitud/formato invalido retorna `400`. Vincula el cobro a un pagador esperado - usado por un control **opt-in** (vea abajo). Alias camelCase: `expectedPayerDocument`. | `"12345678901"` |

::: danger 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 como `30` centavos = 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:

```js
const valorEmReais = 30.0;
const amount = Math.round(valorEmReais * 100); // 3000
```

En Python: `amount = round(valor_en_reales * 100)`. En Go: `amount := int(math.Round(valorEnReales * 100))`.
:::

::: danger 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.
:::

::: warning `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.
:::

::: info Control opt-in de pagador esperado - ningun impacto por default
`expected_payer_document` alimenta un control **opcional, por cuenta, desactivado por default**. Ninguna integracion existente es afectada a menos que el equipo de su cuenta active el control de "verificacion de pagador esperado".

- **Con el control activado:** un PIX recibido para este cobro cuyo documento del pagador **no corresponda** (o este ausente) es **rechazado y devuelto al pagador** (no acreditado).
- **Con el control desactivado (default):** el valor es solo almacenado y no tiene efecto de bloqueo.
- **Si el campo es omitido:** nada cambia.

Detalles en [Controles de Cuenta](/es/account-controls).
:::

## Ejemplo

```bash
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.monetarie.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)

```json
{
  "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.monetarie.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. También vacío cuando el request envía `include_image: false`. |
| `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](#ciclo-de-vida-del-qr-code)) |
| `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.monetarie.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 |

::: info 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)

```json
{
  "worked": false,
  "detail": "O campo amount é obrigatório"
}
```

## Respuesta de Error (401)

```json
{
  "error": {
    "status": 401,
    "message": "Missing API key credentials. Use Authorization: ApiKey <client_id>:<client_secret>"
  }
}
```

## Respuesta de Error (422)

```json
{
  "worked": false,
  "detail": "Invalid HMAC signature"
}
```

## Flujo Recomendado

1. Genere el cobro con este endpoint
2. Muestre el QR Code (`qr_code_image`) o el codigo copia-y-pega (`qr_code`) al pagador
3. Reciba la confirmacion via [Webhook](/es/webhooks) cuando el pago sea efectuado
4. O consulte el status: [por ID](/es/pix-cashin-status), [por E2E](/es/pix-cashin-e2e), [por Tag](/es/pix-cashin-tag)

::: warning 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 3–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](/es/pix-cashin-status).
:::

## 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` |

::: info 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](/es/pix-cashin-status#status-posibles) para la tabla completa.
:::
