> ## Documentation Index
> Fetch the complete documentation index at: https://docs.usevext.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Estorno parcial

> Como devolver parte de uma venda, a conta que o vendedor vê depois e o webhook que chega em seguida.

O comprador levou dois itens e devolveu um. Você precisa mandar de volta parte da venda, e não ela
inteira.

<Note>
  Os valores aqui são **inteiros em centavos**: R\$ 100,00 se envia como `10000`. Decimais são
  recusados com `422`, e o porquê está em [Valores em centavos](/essenciais/valores-em-centavos).
</Note>

## O pedido

<CodeGroup>
  ```bash cURL theme={null}
  curl -X POST https://api.usevext.com/v1/charges/ch_01k1y6r6m6q2x0p3d9v4t7c8n2/refund \
    -H "Authorization: Bearer sk_live_..." \
    -H "Idempotency-Key: estorno-pedido-1042-item-2" \
    -H "Content-Type: application/json" \
    -d '{"amount": 4000, "reason": "Devolução de um item"}'
  ```

  ```php PHP theme={null}
  $resposta = Http::withToken(config('services.vext.key'))
      ->withHeaders(['Idempotency-Key' => "estorno-{$devolucao->id}"])
      ->post("https://api.usevext.com/v1/charges/{$pedido->cobranca_id}/refund", [
          'amount' => $devolucao->valor_em_centavos,   // omita para devolver todo o restante
          'reason' => 'Devolução de um item',
      ]);
  ```

  ```javascript JavaScript theme={null}
  const resposta = await fetch(
    `https://api.usevext.com/v1/charges/${pedido.cobrancaId}/refund`,
    {
      method: 'POST',
      headers: {
        Authorization: `Bearer ${process.env.VEXT_API_KEY}`,
        'Idempotency-Key': `estorno-${devolucao.id}`,
        'Content-Type': 'application/json',
      },
      body: JSON.stringify({
        amount: devolucao.valorEmCentavos, // omita para devolver todo o restante
        reason: 'Devolução de um item',
      }),
    },
  );
  ```

  ```python Python theme={null}
  resposta = requests.post(
      f"https://api.usevext.com/v1/charges/{pedido.cobranca_id}/refund",
      headers={
          "Authorization": f"Bearer {os.environ['VEXT_API_KEY']}",
          "Idempotency-Key": f"estorno-{devolucao.id}",
      },
      json={
          "amount": devolucao.valor_em_centavos,  # omita para devolver todo o restante
          "reason": "Devolução de um item",
      },
  )
  ```
</CodeGroup>

<Warning>
  Envie `amount` sempre que o estorno for parcial. Sem ele, devolvemos **todo o saldo estornável**
  da cobrança — numa venda de R\$ 100,00 intacta, isso são R\$ 100,00, e não os R\$ 40,00 que você
  queria.
</Warning>

## A conta

```json 201 Created theme={null}
{
  "object": "refund",
  "id": "rf_01k1y7c3n8b5w2q9r4t6y8u1i3",
  "charge": "ch_01k1y6r6m6q2x0p3d9v4t7c8n2",
  "status": "succeeded",
  "amount": 4000,
  "currency": "BRL",
  "fee_returned": 280,
  "seller_debit": 3720,
  "reason": "Devolução de um item",
  "failed_reason": null,
  "created_at": "2026-08-05T10:12:00+00:00",
  "processed_at": "2026-08-05T10:12:02+00:00"
}
```

O comprador recebe `amount` — R\$ 40,00. Do saldo do vendedor saem `seller_debit` — R\$ 37,20. A
diferença, R\$ 2,80, é a parte da nossa taxa que devolvemos, em `fee_returned`.

Sempre `amount = seller_debit + fee_returned`. É a igualdade que responde a pergunta "por que saíram
R\$ 37,20 se eu estornei R\$ 40,00?" antes de ela virar chamado.

## O que muda na cobrança

Depois deste estorno, a cobrança fica assim:

| Campo             | Antes  | Depois                      |
| ----------------- | ------ | --------------------------- |
| `status`          | `paid` | `partially_refunded`        |
| `refunded_amount` | `0`    | `4000`                      |
| `refunded_at`     | `null` | `2026-08-05T10:12:02+00:00` |

<Note>
  `partially_refunded` é estado próprio, e não um `refunded` com asterisco. Tratar esta venda como
  estornada faria o seu relatório descontar os R\$ 100,00 inteiros, quando só R\$ 40,00 voltaram.
</Note>

Ainda cabem R\$ 60,00 de estorno (`amount - refunded_amount`). Pedir mais que isso devolve
`422 exceeds_remaining`. Um segundo estorno de R\$ 60,00 leva a cobrança para `refunded`.

## O webhook que chega depois

`charge.refunded` traz a cobrança **e** o estorno, porque as duas perguntas aparecem juntas do outro
lado: quanto voltou, e se sobrou algo.

```json charge.refunded theme={null}
{
  "id": "evt_01k1y7c4a1b2c3d4e5f6g7h8i9",
  "type": "charge.refunded",
  "created_at": "2026-08-05T10:12:03+00:00",
  "data": {
    "charge": { "status": "partially_refunded", "amount": 10000, "refunded_amount": 4000 },
    "refund": { "amount": 4000, "fee_returned": 280, "seller_debit": 3720 }
  }
}
```

Ele chega também quando o estorno é feito por nós ou pelo provedor, fora do seu fluxo — então trate
o evento mesmo que a sua aplicação nunca chame a API de estorno.

## Se o saldo não cobrir

O estorno acontece de qualquer forma. O dinheiro é do comprador e precisa voltar; se o vendedor já
sacou, o saldo dele fica negativo, as vendas seguintes cobrem automaticamente, e só o saque fica
bloqueado até lá. Ver [Saldo do vendedor](/essenciais/saldo).

## Veja também

<CardGroup cols={2}>
  <Card title="Estornos" icon="rotate-ccw" href="/essenciais/estornos">
    Todas as regras, incluindo o prazo de 90 dias.
  </Card>

  <Card title="charge.refunded" icon="webhook" href="/api-reference/webhooks/charge-refunded">
    O evento completo, campo a campo.
  </Card>
</CardGroup>

***

Ficou algo de fora? Escreva para [suporte@usevext.com](mailto:suporte@usevext.com). Se o assunto for
uma chamada específica, informe o horário dela; se for uma entrega de webhook, informe o
`X-Vext-Delivery` — é por ele que localizamos a tentativa, a resposta do seu servidor e o horário.
