# Devoluções — eventos

Cobre devolução de Pix e estorno de cartão. Ambos usam `POST /v1/wallet/refunds` e emitem eventos `refund.*`.

## Solicitando

**Pix** — exige `endToEndId` e `reason`:

```json
{ "endToEndId": "E0036030520260326132336e2f8787c4", "reason": "CUSTOMER_REQUEST" }
```

`reason` aceita: `CUSTOMER_REQUEST`, `FRAUD`, `BANK_ERROR`, `PIX_CHANGE_ERROR`.

**Cartão** — exige `chargeId`:

```json
{ "chargeId": "cha_1774530966959_frgj3ptax" }
```

Consulta: `GET /v1/wallet/refunds?refundId=ref_...`

Scope: `wallet/write` para solicitar, `wallet/read` para consultar.

## Diferença de comportamento

- **Pix** normalmente nasce `PROCESSING` e é concluído de forma assíncrona — aguarde `refund.confirmed`.
- **Cartão** pode retornar já `CONFIRMED` com `success: true` na própria resposta.

Não trate a resposta da solicitação como conclusão da devolução no caso do Pix.

## Eventos

| Evento | `status` no payload | Quando |
|---|---|---|
| `refund.requested` | `PROCESSING` | Devolução registrada e em processamento |
| `refund.confirmed` | `CONFIRMED` | Valor devolvido ao pagador |
| `refund.failed` | **`ERROR`** | Devolução recusada ou falhou |

O `refund.failed` envia `status: "ERROR"` — não `FAILED`. Compare pelo nome do evento, não pelo status.

## Payload

Eventos `refund.*` têm schema próprio — **não** carregam a base de assinatura. O campo da conta é `accountId`, e não `accountNumber` como nos eventos de assinatura.

Campos comuns aos três eventos: `event`, `timestamp`, `accountId`, `refundId`, `chargeId`, `amount`, `customer`.

### `refund.requested`

```json
{
  "event": "refund.requested",
  "timestamp": "2026-08-14T22:33:58.728Z",
  "accountId": "4231833",
  "refundId": "ref_1786746837248_gsoosg37y",
  "chargeId": "cha_1786746721295_f7y0qtzpz",
  "endToEndId": "E18236120202608142232s0290d93572",
  "amount": 9.99,
  "reason": "CUSTOMER_REQUEST",
  "status": "PROCESSING",
  "customer": {
    "customerId": "cus_1786746719497_9px4bs9dh",
    "name": "Lucas Ferreira Valente",
    "email": "lucas@example.com",
    "taxId": "12354358903",
    "phone": "4197424445"
  }
}
```

No **estorno de cartão** o payload não tem `endToEndId`; no lugar vêm `paymentType: "CREDIT_CARD"` e `providerChargeId` com o identificador da transação no adquirente.

O bloco `customer` é resolvido a partir do `chargeId`. Quando a cobrança não tem cliente vinculado, ele chega com **todos os campos `null`** — o objeto existe, mas vazio. Trate esse caso.

### `refund.confirmed`

Acrescenta `returnIdentification` (identificador da devolução no SPI) e **não** repete o `reason`:

```json
{
  "event": "refund.confirmed",
  "timestamp": "2026-08-14T22:33:59.762Z",
  "accountId": "4231833",
  "refundId": "ref_1786746837248_gsoosg37y",
  "chargeId": "cha_1786746721295_f7y0qtzpz",
  "endToEndId": "E18236120202608142232s0290d93572",
  "returnIdentification": "D13935893202608142233X967RIOG07L",
  "amount": 9.99,
  "status": "CONFIRMED",
  "customer": { "...": "..." }
}
```

Quando a cobrança original tinha split, o payload traz também `splitReversals`, com o resultado do estorno em cada participante:

```json
"splitReversals": [
  { "accountNumber": "429131213", "amount": 2.00, "status": "REVERSED" },
  { "accountNumber": "429131214", "amount": 1.00, "status": "PENDING", "debtId": "sdb_1786746837_x9k2" }
]
```

`status: "PENDING"` com `debtId` significa que o participante não tinha saldo para devolver e a diferença virou débito, a ser quitado no próximo crédito da conta.

### `refund.failed`

Mesmos campos do `confirmed`, com `status: "ERROR"` e um campo `error` com o motivo da recusa:

```json
{
  "event": "refund.failed",
  "timestamp": "2026-08-25T22:06:18.242Z",
  "accountId": "4231833",
  "refundId": "ref_1787695576556_4xlxpapgl",
  "chargeId": "cha_1787240610393_0sq80sprv",
  "endToEndId": "E00416968202608201543DFwF1iVynNt",
  "returnIdentification": "D13935893202608252206s2DvmooTPb2",
  "amount": 0.27,
  "status": "ERROR",
  "error": "ERROR",
  "customer": { "...": "..." }
}
```

`error` reflete o motivo informado pelo banco e pode repetir o próprio status quando não há detalhe.

## Devoluções do MED

Devoluções determinadas pelo Banco Central via MED chegam pelos eventos `med.refund.opened` e `med.refund.closed`, e podem vir acompanhadas de `med.balance.blocked`. Veja [Subcontas — eventos](./subcontas-eventos.md).
