# Documentação da API — EYT PIX

## Base URL

| Ambiente | URL |
|----------|-----|
| Produção | `https://api.eyt.com.br` |
| Sandbox | `https://sandbox.eyt.com.br` |

Formato das requisições: `application/json`.

## Autenticação

Cada requisição leva a API key da sua Client. Guarde essa chave em segredo. Não
coloque ela em código de frontend nem em repositórios abertos.

Cada Client tem **apenas uma API key ativa por vez**. Não existem chaves paralelas.

### Headers obrigatórios

| Header | Valor |
|--------|-------|
| `Authorization` | `Bearer eyt_live_xxx...` |
| `Content-Type` | `application/json` |

Você recebe a chave no onboarding da sua Client, fora da API, por canal seguro.

### Rotação de chave — 3 passos

1. Gere uma nova chave no painel. O valor completo aparece **uma única vez**.
2. Atualize sua aplicação para usar a nova chave.
3. Revogue a chave antiga no painel.

A nova chave vale a partir do passo 2; a antiga só vale até o passo 3. Se você perder
a chave, refaça os três passos.

## Cobranças (receber)

Cria um PIX Cobrança e devolve o QR Code para pagamento.

### Criar cobrança

```
POST /v1/pix/charges
```

```json
{
  "amountMinor": 1500,
  "expiresInSeconds": 3600,
  "description": "Pedido #1234"
}
```

`amountMinor` é o valor em centavos (`R$ 10,00` = `1000`). `expiresInSeconds` aceita
60 a 86400.

Resposta `201`:

```json
{
  "id": "5a1f...-...-...-...-...",
  "amountMinor": 1500,
  "status": "pending",
  "brCode": "00020126580014BR.GOV.BCB.PIX...",
  "txid": "EYT...",
  "expiresAt": "2026-08-20T19:00:00Z"
}
```

Mostre o `brCode` ao cliente final. Bibliotecas de QR Code aceitam esse string
diretamente.

### Listar cobranças

```
GET /v1/pix/charges?page=1&pageSize=20&status=pending
```

`page` começa em 1, `pageSize` vai de 1 a 100. `status` aceita `pending`, `paid`,
`expired` ou `cancelled`.

### Consultar uma cobrança

```
GET /v1/pix/charges/{id}
```

### Status possíveis

| Status | Significado |
|--------|-------------|
| `pending` | criada, aguardando pagamento |
| `paid` | sistema confirmou o pagamento |
| `expired` | passou de `expiresAt` sem pagamento |
| `cancelled` | cancelada |

### Idempotência

Toda criação de cobrança exige o header `Idempotency-Key`. Mesma chave + mesmo body
devolve a mesma resposta. Mesma chave + body diferente devolve `422`. Use um ID
determinístico do seu lado (ex.: `pedido-{id}-cobranca`).

## Pagamentos (enviar)

Envia um PIX para uma chave qualquer. O fluxo é em duas etapas (criar + aprovar) para
permitir aprovação humana.

### Validar chave do destinatário (opcional, recomendado)

```
GET /v1/pix/dict/{key}
```

Substitua `{key}` por CPF, CNPJ, e-mail, telefone ou chave aleatória. Resposta `200`:

```json
{
  "name": "Maria Silva",
  "documentMasked": "***.456.789-**"
}
```

Mostre o nome ao usuário antes de confirmar o envio. `404` significa chave
inexistente.

### Criar pagamento

```
POST /v1/pix/payouts
```

```json
{
  "destinationKey": "maria@example.com",
  "destinationKeyType": "email",
  "amountMinor": 4990,
  "recipientNameConfirmed": true
}
```

Resposta `201` com `status: "pending_approval"` quando o valor exige aprovação.

### Aprovar pagamento

```
POST /v1/pix/payouts/{id}/approve
```

```json
{ "confirmedAmountMinor": 4990 }
```

O valor confirmado tem que bater com o valor criado. O criador do pagamento não pode
aprovar o próprio.

### Rejeitar pagamento

```
POST /v1/pix/payouts/{id}/reject
```

```json
{ "reason": "valor divergente do pedido" }
```

Só funciona em `pending_approval`.

### Status possíveis

| Status | Significado |
|--------|-------------|
| `pending_approval` | criado, aguardando aprovação |
| `approved` | aprovado, prestes a enviar |
| `blocked` | rejeitado manualmente |
| `processing` | enviado, aguardando confirmação |
| `settled` | sistema confirmou |
| `failed` | rejeitado ou timeout |

## Webhooks

A EYT notifica o cliente sobre mudanças de estado que ele não pode inferir sozinho
(confirmação de pagamento, falha, expiração).

### Endpoint e assinatura

A EYT faz `POST` na URL que você cadastrou, autenticando por **assinatura HMAC-SHA256**
(não por API key).

Headers:

| Header | Conteúdo |
|--------|----------|
| `X-EYT-Timestamp` | unix seconds (UTC) |
| `X-EYT-Signature` | hex do HMAC-SHA256 |

Assinatura: `HMAC-SHA256("{timestamp}.{rawBody}", secret)`, em hex.

### Validação (Python)

```python
import hmac, hashlib

def is_valid(payload: bytes, ts: str, sig: str, secret: str, now: int) -> bool:
    try:
        signed_at = int(ts)
    except ValueError:
        return False
    if abs(now - signed_at) > 300:  # 5 minutos
        return False
    msg = ts.encode() + b"." + payload
    expected = hmac.new(secret.encode(), msg, hashlib.sha256).hexdigest()
    return hmac.compare_digest(expected, sig.lower())
```

### Boas práticas

1. Não dependa de webhooks para fechar UX. Tenha fallback por polling em
   `GET /v1/pix/charges/{id}`.
2. Responda `2xx` em menos de 5 segundos. Acima disso, a EYT considera falha e
   reentrega em backoff exponencial.
3. Processe de forma idempotente. Use `endToEndId` ou `charge.id` como chave de
   dedupe.
4. Não exponha logs com o segredo do webhook. É uma credencial.

## Códigos de erro

| Status | Código | Quando |
|--------|--------|--------|
| `400` | — | JSON malformado |
| `401` | — | chave ausente / inválida / revogada |
| `403` | — | chave válida, sem permissão para a operação |
| `404` | `not_found` | recurso inexistente |
| `409` | `idempotency_in_progress` | retry concorrente; reenvie |
| `422` | `validation_error` | JSON válido, viola contrato |
| `422` | `idempotency_key_reused` | mesma chave com body diferente |
| `422` | `daily_limit_exceeded` | payout acima do limite diário |
| `422` | `saldo_insuficiente` | payout sem saldo |
| `422` | `amount_mismatch` | valor aprovado difere do criado |
| `422` | `invalid_state` | transição de estado inválida |
| `422` | `self_approval_not_allowed` | criador tentando aprovar próprio |
| `429` | — | rate-limit; aguarde |
| `503` | — | serviço indisponível; reenvie |

Formato:

```json
{
  "code": "validation_error",
  "message": "Requisição inválida.",
  "details": [
    { "field": "amountMinor", "reason": "must be >= 1" }
  ]
}
```

`code` é estável. `message` pode mudar entre versões.

## Suporte

Para dúvidas ou problemas com a API, fale com o seu contato na EYT.