# PIX Cash-In -- Gerar QR Code

Gera uma cobrança PIX com QR Code para recebimento de valores na conta associada à sua API Key.

## Endpoint

```
POST /api/external/pix/cash-in
```

## Headers

| Header | Tipo | Obrigatório | Descrição |
|--------|------|-------------|-----------|
| `Authorization` | String | Sim | `ApiKey {client_id}:{client_secret}` |
| `Content-Type` | String | Sim | `application/json` |
| `hmac` | String | Sim | Assinatura HMAC-SHA512 do body ([saiba mais](/hmac)) |
| `Idempotency-Key` | String | Não | Chave única para evitar processamento duplicado (max 256 chars) |
| `X-Key-Case` | String | Não | Defina como `camelCase` para receber os campos da resposta em camelCase (padrão é snake_case) |

## Request Body

| Campo | Tipo | Obrigatório | Descrição | Exemplo |
|-------|------|-------------|-----------|---------|
| `amount` | Integer | Sim | Valor em **centavos** (R$ 30,00 = `3000`) | `3000` |
| `description` | String | Não | Descrição da cobrança. Se omitido, o padrão é `"Cobranca PIX"`. | `"Pedido #1234"` |
| `external_id` | String | Não | Identificador do seu sistema para rastreamento. Max 128 chars. Apenas `a-zA-Z0-9._:-` (valores fora do padrão são silenciosamente descartados e o campo volta como `null` -- veja aviso abaixo). Retornado em respostas e webhooks. | `"order-9876"` |
| `pix_key` | String | Não | Chave PIX **ativa e vinculada à conta** para gerar o QR Code. Se omitida, usa a chave ativa mais recente da conta (ordem por data de criação DESC). Se não houver chave ativa, retorna `422`; a API nunca presume CPF/CNPJ como chave. | `"12345678901"` |
| `city` | String | Não | Cidade do recebedor no QR Code. Padrão: `SAO PAULO`. Truncada automaticamente para 15 caracteres. | `"RIO DE JANEIRO"` |
| `expiration_seconds` | Integer | Não | Validade do QR Code em segundos. Faixa **3–86400** (3 s a 24h); fora da faixa retorna `400`. Se omitido, usa o padrão do sistema (15 min). Use para limitar a exposição de QRs dinâmicos (ex.: `300` = 5 min, antifraude). | `300` |
| `include_image` | Boolean | Não | Se `false`, a resposta vem com `qr_code_image` vazio (`""`) e a geração da imagem é pulada. Use quando você só precisa do copia-e-cola (`qr_code`). Padrão: `true`. | `false` |
| `expected_payer_document` | String | Não | CPF (11 dígitos) ou CNPJ (14 dígitos) do pagador esperado. Entrada formatada (pontos/traços/barra) é aceita e normalizada para dígitos; comprimento/formato inválido retorna `400`. Vincula a cobrança a um pagador esperado - usado por um controle **opt-in** (veja abaixo). Alias camelCase: `expectedPayerDocument`. | `"12345678901"` |

::: danger Envie `amount` SEMPRE como inteiro (em centavos)
O campo `amount` **DEVE** ser um **inteiro em centavos**. **NUNCA** envie valores `float`/`decimal`:

- ✅ `"amount": 3000` → R$ 30,00
- ❌ `"amount": 30.00` → interpretado como `30` centavos = R$ 0,30 (cobrança **100× menor** do que pretendido)
- ❌ `"amount": 30` → R$ 0,30 (também incorreto)

Em JavaScript, converta sempre com `Math.round` antes de enviar:

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

Em Python: `amount = round(valor_em_reais * 100)`. Em Go: `amount := int(math.Round(valorEmReais * 100))`.
:::

::: danger Valores monetários (entrada × resposta)
Valores de **entrada** são em **centavos** (R$ 1,00 = `100`). Valores de **resposta** são em **unidades base** (R$ 1,00 = `10000`). Para converter a resposta para reais, **divida por 10.000**. Nunca use ponto flutuante em nenhum sentido.
:::

::: warning `external_id` inválido = descarte silencioso
Se o `external_id` contiver caracteres fora de `a-zA-Z0-9._:-`, ultrapassar 128 bytes, ou for apenas espaços em branco, o servidor **descarta silenciosamente** o valor e a resposta volta com `"external_id": null` (nenhum erro é retornado). **Sempre confira** o `external_id` presente na resposta antes de usá-lo para conciliação -- se veio `null`, seu identificador foi rejeitado pela validação.
:::

::: info Controle opt-in de pagador esperado - nenhum impacto por padrão
`expected_payer_document` alimenta um controle **opcional, por conta, desativado por padrão**. Nenhuma integração existente é afetada a menos que o time da sua conta ative o controle de "verificação de pagador esperado".

- **Com o controle ativado:** um PIX recebido para esta cobrança cujo documento do pagador **não corresponda** (ou esteja ausente) é **rejeitado e devolvido ao pagador** (não creditado).
- **Com o controle desativado (padrão):** o valor é apenas armazenado e não tem efeito de bloqueio.
- **Se o campo for omitido:** nada muda.

Detalhes em [Controles de Conta](/account-controls).
:::

## Exemplo

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

## Resposta de Sucesso (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 | Descrição |
|-------|------|-----------|
| `worked` | Boolean | `true` indica sucesso na operação |
| `transaction_id` | String \| null | Identificador único da cobrança (tx_id do QR Code, alfanumérico máx. 35 chars per spec BACEN). `null` apenas em falhas internas raras de persistência |
| `qr_code` | String \| null | Código EMV copia-e-cola para pagamento. `null` (valor JSON literal, **não** a string `"null"`) apenas em falhas internas raras de persistência |
| `qr_code_image` | String | Imagem do QR Code codificada em base64 (PNG, prefixo `data:image/png;base64,`). String vazia (`""`) em falhas internas raras. Também vazia quando o request envia `include_image: false`. |
| `external_id` | String \| null | Seu identificador, retornado tal como enviado. `null` se não informado, se inválido (ver aviso acima), ou se descartado pela sanitização |
| `amount` | Integer | Valor da cobrança em **unidades base** (÷ 10.000 para reais). `300000` = R$ 30,00 |
| `status` | String | Status inicial sempre `active` (QR Code ativo para pagamento). A evolução posterior (`paid`/`expired`/`cancelled`) é visível em consultas e webhooks (veja [ciclo de vida do QR](#ciclo-de-vida-do-qr-code)) |
| `type` | String \| null | Tipo do QR Code: `dynamic` (padrão desde abril/2026) ou `static`. Dinâmico requer que o banco do pagador consulte `location_url` e valide o JWS |
| `location_url` | String \| null | URL de consulta do payload dinâmico do BACEN (ex: `https://qrcode-h.monetarie.com/pix/v2/{uuid}`). **Não é o JWS em si** -- o banco do pagador faz um `GET` neste endpoint para receber o payload ISO 20022 assinado (JWS PS256). Presente apenas em QR dinâmico; `null` em estático |
| `expires_at` | String \| null | Data/hora de expiração do QR Code em **ISO 8601 UTC com sufixo `Z`** (ex: `2026-03-07T16:30:00Z`). `null` se QR sem expiração |

::: info QR dinâmico é o padrão
Todas as contas em produção usam **QR dinâmico** por padrão desde abril/2026. O QR dinâmico inclui uma URL (`location_url`) que o banco do pagador consulta para obter o payload assinado via JWS -- compatibilidade maximizada com bancos BACEN-strict.
:::

## Resposta de Erro (400)

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

## Resposta de Erro (401)

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

## Resposta de Erro (422)

```json
{
  "worked": false,
  "detail": "pix_key deve ser uma chave PIX ativa vinculada à conta"
}
```

## Resposta de Erro (502)

```json
{
  "worked": false,
  "detail": "serviço de chaves PIX temporariamente indisponível"
}
```

O `502` também é usado quando o serviço de geração de QR Code não devolve um
BR Code válido. Nesses casos, a API não retorna sucesso nem fabrica dados.

## Fluxo Recomendado

1. Gere a cobrança com este endpoint
2. Exiba o QR Code (`qr_code_image`) ou o código copia-e-cola (`qr_code`) ao pagador
3. Receba a confirmação via [Webhook](/webhooks) quando o pagamento for efetuado
4. Ou consulte o status: [por ID](/pix-cashin-status), [por E2E](/pix-cashin-e2e), [por Tag](/pix-cashin-tag)

::: warning Validade do QR Code
Por padrão, o QR Code tem validade de **15 minutos**. Você pode sobrescrever isso **por requisição** com `expiration_seconds` (faixa 3–86400s). Precedência: `expiration_seconds` (requisição) > padrão do sistema. Verifique sempre o `expires_at` retornado na resposta para a data/hora exata.

Após o vencimento, o status muda para `expired` automaticamente (via worker interno, rotina a cada 5 minutos). Para cobranças canceladas manualmente pelo merchant, o status é `cancelled`. Veja a lista completa de status em [Consultar Cash-In por ID](/pix-cashin-status).
:::

## Ciclo de Vida do QR Code

O QR Code passa por estes estados após a criação:

| Estado | Origem | Descrição |
|--------|--------|-----------|
| `active` | Inicial | QR gerado com sucesso, pronto para ser escaneado/pago |
| `paid` | Pagamento recebido | Pagamento BACEN confirmado e linkado ao QR (via worker interno após ACCC) |
| `expired` | Worker `QrExpirationChecker` | TTL atingiu `expires_at`. Worker roda a cada 5 min marcando QRs `active` expirados e disparando webhook `pix.charge.expired` |
| `cancelled` | Manual ou em massa | Cancelado pelo merchant (via portal) ou em operações administrativas (ex: migração estático→dinâmico em abril/2026 cancelou QRs estáticos ativos com valor) |
| `used` | Legado | Estado intermediário transitório do pipeline antigo. Clientes novos devem tratar como equivalente a `paid` |

::: info Em consultas, `paid` pode aparecer como `settled`
Os endpoints `GET /transactions/:id` e equivalentes retornam o campo `status` do ponto de vista da transação (não do QR). Um QR pago aparece como `status: "settled"` na consulta, com `type: "pix"` (ou `type: "pix_qrcode"` em janela curta pós-settlement enquanto a linha final em `transactions` ainda é persistida). Veja [Consultar Cash-In por ID](/pix-cashin-status#status-possíveis) para a tabela completa.
:::
