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

# Refund e clawback

> Como reembolsos geram clawback de comissão.

Quando uma cobrança é reembolsada ou sofre chargeback, a Repass reverte a comissão correspondente. Como o saldo de uma Comissão pode já estar pago (ou em vias de ser pago), a reversão nem sempre é uma simples anulação: para comissões já pagas e para reembolsos parciais, a Repass cria uma **comissão de clawback** (um lançamento financeiro de valor negativo) em vez de apagar o registro original. Isso preserva a trilha de auditoria e mantém o saldo do Afiliado consistente, centavo a centavo.

Esta página descreve o fluxo de refund/void de uma Conversão, como ele se desdobra em void ou clawback, o efeito no saldo do Afiliado e nos Payouts, e os Eventos emitidos. Antes de seguir, vale conhecer o modelo de [Comissões](/docs/conceitos/comissoes).

## Refund, void e clawback: o que acontece

Um refund chega à Repass a partir dos eventos de cobrança dos seus gateways de pagamento. A partir do valor reembolsado e da cobrança original, a Repass decide caso a caso o que fazer com cada Comissão daquele ciclo:

| Situação da comissão                       | Tipo do refund | Ação     | Resultado                                                           |
| ------------------------------------------ | -------------- | -------- | ------------------------------------------------------------------- |
| Não paga (sem `payoutId`, status ≠ `paid`) | Total          | Void     | Comissão vira `voided` com `voidReason: refunded` (ou `chargeback`) |
| Paga, ou qualquer comissão                 | Parcial        | Clawback | Nova comissão `clawback` negativa, proporcional                     |
| Paga (com `payoutId` ou `paid`)            | Total          | Clawback | Nova comissão `clawback` negativa, valor cheio                      |

A regra-chave: **se a comissão já saiu da plataforma (foi paga) ou se o estorno é parcial, não dá para "desfazer" o lançamento: entra um débito (clawback) que será compensado no próximo Payout.** Se a comissão ainda está retida (em hold ou aprovada, mas sem Payout) e o refund é total, ela é simplesmente anulada.

<Info>
  **Refund total marca a Conversão como `refunded`.** Apenas o refund total (incluindo chargeback) atualiza a Conversão de origem para `status: refunded`. Refund parcial não altera a Conversão.
</Info>

### Clawback é uma comissão negativa

Um clawback não é um estado de uma comissão existente, é uma **nova** Comissão (`comm_…`) com:

* `type: clawback`
* `status: approved`, nasce aprovado, **sem hold** (débito imediato)
* `amountCents` negativo
* `originalCommissionId` apontando a Comissão revertida
* `calculation.refund` com `refundedAmountCents`, `chargedAmountCents`, `originalCommissionAmountCents` e `chargeback`

Como nasce `approved` e sem hold, o clawback entra imediatamente no cálculo do saldo e no netting do próximo Payout.

### Cálculo do valor do clawback

<AccordionGroup>
  <Accordion title="Refund total → valor cheio">
    O clawback reverte o valor integral da Comissão original. Se a comissão original valia `2000` centavos, o clawback vale `-2000`.
  </Accordion>

  <Accordion title="Refund parcial → proporcional (floor)">
    O clawback é proporcional ao quanto foi reembolsado, arredondado para baixo:

    ```
    clawbackCents = -floor(originalCommissionAmountCents × refundedAmountCents / chargedAmountCents)
    ```

    Exemplo: cobrança de `10000`, comissão original de `2000`, refund parcial de `3000`:
    `floor(2000 × 3000 / 10000) = floor(600) = 600` → clawback de `-600`.

    Refund parcial **sempre** vira clawback, mesmo que a comissão original ainda não tenha sido paga: isso preserva a trilha de auditoria do valor parcial reembolsado.
  </Accordion>

  <Accordion title="Chargeback = refund total + fraude">
    Um chargeback é tratado como refund total. Além de reverter a comissão (void ou clawback de valor cheio), ele marca a Conversão `refunded` **e** grava `fraudDecision: confirmed`.
  </Accordion>
</AccordionGroup>

Os valores são sempre em centavos (números inteiros), evitando erros de arredondamento.

## Fluxo completo

```mermaid theme={null}
sequenceDiagram
    participant GW as Cobrança do gateway
    participant RR as Refund
    participant Comm as Comissões
    participant Conv as Conversões

    GW->>RR: refund (conversionId, refundedAmount,<br/>chargeSourceEventId | billingCycle,<br/>sourceEventId, chargeback?)
    RR->>Comm: este refund já foi processado?
    alt reentrega
        Comm-->>RR: comissões existentes
        RR-->>GW: replayed: true
    else novo
        RR->>Conv: localiza a Conversão
        RR->>Comm: comissões da cobrança<br/>(por chargeSourceEventId ou billingCycle)
        alt nenhuma comissão encontrada
            RR-->>GW: erro: cobrança não encontrada
        end
        Note over RR: total = refundedAmount ≥ chargedAmount
        loop por comissão alvo (ignora voided)
            alt total e NÃO paga
                RR->>RR: void (refunded / chargeback)
            else paga, OU refund parcial
                RR->>RR: cria clawback negativo (approved)
            end
        end
        RR->>Comm: aplica voids + clawbacks + atualização da Conversão
        Comm-->>RR: voided[] + clawbacks[]
        RR-->>GW: resultado
    end
```

Pontos relevantes do fluxo:

* **Identificação da cobrança.** O refund localiza as comissões da cobrança original por `chargeSourceEventId` (preferencial) ou por `billingCycle` (alternativo). Se nenhum dos dois for informado, a operação é recusada; uma cobrança inexistente retorna um erro de cobrança não encontrada.
* **Por afiliado winner.** Uma cobrança pode ter gerado uma Comissão por Afiliado (atribuição multi-touch). O refund percorre todas as comissões daquele ciclo e aplica void ou clawback a cada uma conforme a tabela acima.
* **Operação consistente.** Voids, clawbacks, a atualização da Conversão (`refunded`/`fraudDecision`) e todos os Eventos são aplicados de uma só vez: ou tudo é registrado, ou nada é.

## Idempotência

O refund é idempotente por `sourceEventId`: reentregar o mesmo evento de refund retorna o resultado existente com `replayed: true`, sem duplicar voids ou clawbacks.

<Note>
  O caminho **só-void** do refund é idempotente mesmo com um `sourceEventId` diferente: se a Comissão já foi anulada, não há mais alvo a reverter e a operação retorna `replayed: true`. Veja [Idempotência](/docs/convencoes/idempotencia) para o modelo geral.
</Note>

## Efeito no saldo do afiliado

O saldo do Afiliado (`GET /affiliates/:affiliateId/balance`) agrega as comissões em buckets. O clawback aparece em seu próprio bucket e **reduz a projeção do próximo Payout**:

<ResponseField name="pendingCents" type="integer">
  Soma das comissões `pending` (em hold).
</ResponseField>

<ResponseField name="approvedCents" type="integer">
  Comissões `approved`, sem Payout, fora de revisão, **exceto** clawbacks.
</ResponseField>

<ResponseField name="underReviewCents" type="integer">
  Comissões `approved` congeladas por revisão de ban (`underReview = true`).
</ResponseField>

<ResponseField name="clawbackCents" type="integer">
  Clawbacks não anulados e sem Payout (valor negativo).
</ResponseField>

<ResponseField name="nextPayoutProjectionCents" type="integer">
  Projeção do próximo Payout: `max(0, approvedCents + clawbackCents)`.
</ResponseField>

A regra central do saldo: **a projeção do próximo Payout nunca é negativa.**

```
nextPayoutProjectionCents = max(0, approvedCents + clawbackCents)
```

Se o débito (clawback) exceder o crédito aprovado, o excedente **não vira cobrança ao Afiliado**: ele rola para Payouts futuros, abatendo o crédito que surgir depois.

<Warning>
  A Repass nunca cobra o Afiliado por um saldo negativo. Um clawback maior que o crédito disponível apenas zera a projeção do próximo Payout; a diferença permanece como débito e é compensada nos próximos ciclos.
</Warning>

### Exemplo

<Tabs>
  <Tab title="Cenário">
    O Afiliado tem `5000` centavos em comissões aprovadas. Uma cobrança de outra Conversão (comissão paga de `2000`) é totalmente reembolsada, gerando um clawback de `-2000`.
  </Tab>

  <Tab title="Saldo resultante">
    ```json theme={null}
    {
      "approvedCents": 5000,
      "clawbackCents": -2000,
      "nextPayoutProjectionCents": 3000
    }
    ```

    O próximo Payout projeta `max(0, 5000 + (-2000)) = 3000`.
  </Tab>

  <Tab title="Débito excedente">
    Se o crédito aprovado fosse apenas `1500`:

    ```json theme={null}
    {
      "approvedCents": 1500,
      "clawbackCents": -2000,
      "nextPayoutProjectionCents": 0
    }
    ```

    A projeção zera; o débito de `-500` rola para o próximo ciclo.
  </Tab>
</Tabs>

## Efeito nos payouts

* Clawbacks nascem `approved` e entram no **netting** que o módulo de [Payouts](/docs/conceitos/payouts) aplica ao montar um Payout: comissões positivas e clawbacks negativos do Afiliado são somados na hora de fechar o valor a pagar.
* O bucket `clawbackCents` só conta clawbacks **não anulados e sem `payoutId`**. Uma vez que um clawback é incorporado a um Payout, ele deixa de contar no saldo.
* Uma Comissão que foi **anulada** (void por refund total não pago) sai de qualquer projeção: não há valor a pagar nem a debitar.

## Eventos emitidos

Todos os Eventos abaixo são emitidos quando o refund é processado. Consulte o [catálogo de eventos](/docs/webhooks/catalogo-de-eventos) para o formato de entrega via Webhook.

| Evento                        | Quando                                                        | Payload                                       |
| ----------------------------- | ------------------------------------------------------------- | --------------------------------------------- |
| `commission.voided`           | Refund total de comissão não paga                             | `{ reason, refundedAmountCents, chargeback }` |
| `commission.clawback_created` | Clawback gerado por refund de comissão paga ou refund parcial | `{ commission }` (o clawback negativo)        |
| `conversion.refunded`         | Refund total (inclui chargeback), recurso `conversion`        | atualização de status / fraude da Conversão   |

Um único refund pode emitir múltiplos eventos: por exemplo, um refund total de um ciclo com dois Afiliados winners (um com comissão paga e outro não) emite um `commission.clawback_created`, um `commission.voided` e um `conversion.refunded`.

## Exemplos

O exemplo abaixo ilustra os dados de um refund originado a partir de uma cobrança do seu gateway. Para a forma e os campos exatos, veja a aba Referência da API.

<CodeGroup>
  ```json Refund total theme={null}
  {
    "conversionId": "conv_01HZX9...",
    "chargeSourceEventId": "evt_charge_01HZ...",
    "refundedAmountCents": 9900,
    "chargedAmountCents": 9900,
    "sourceEventId": "evt_refund_01J0...",
    "chargeback": false
  }
  ```

  ```json Refund parcial theme={null}
  {
    "conversionId": "conv_01HZX9...",
    "billingCycle": 1,
    "refundedAmountCents": 3000,
    "chargedAmountCents": 9900,
    "sourceEventId": "evt_refund_01J1...",
    "chargeback": false
  }
  ```

  ```json Chargeback (refund total + fraude) theme={null}
  {
    "conversionId": "conv_01HZX9...",
    "chargeSourceEventId": "evt_charge_01HZ...",
    "refundedAmountCents": 9900,
    "chargedAmountCents": 9900,
    "sourceEventId": "evt_chargeback_01J2...",
    "chargeback": true
  }
  ```
</CodeGroup>

Você pode inspecionar o clawback resultante e seu cálculo aberto listando as comissões por tipo:

```bash theme={null}
curl "https://api.userepass.com/commissions?affiliate_id=aff_01HZX9...&type=clawback" \
  -H "Authorization: Bearer rstr_..."
```

A resposta traz cada clawback com o `calculation.refund` que reconstrói o valor:

```json theme={null}
{
  "data": [
    {
      "id": "comm_01J3...",
      "type": "clawback",
      "status": "approved",
      "amountCents": -600,
      "originalCommissionId": "comm_01HZX9...",
      "calculation": {
        "refund": {
          "refundedAmountCents": 3000,
          "chargedAmountCents": 9900,
          "originalCommissionAmountCents": 1980,
          "chargeback": false
        }
      }
    }
  ],
  "hasMore": false
}
```

Para conferir o impacto no saldo do Afiliado:

```bash theme={null}
curl https://api.userepass.com/affiliates/aff_01HZX9.../balance \
  -H "Authorization: Bearer rstr_..."
```

## Próximos passos

<CardGroup cols={2}>
  <Card title="Comissões" icon="money-bill" href="/docs/conceitos/comissoes">
    O modelo completo de cálculo, ciclos, hold e estados de comissão.
  </Card>

  <Card title="Payouts" icon="building-columns" href="/docs/conceitos/payouts">
    Como o netting de clawbacks entra no fechamento do Payout.
  </Card>

  <Card title="Conversões e fraude" icon="shield-halved" href="/docs/conceitos/conversoes-e-fraude">
    O ciclo de vida da Conversão e o estado `refunded`.
  </Card>

  <Card title="Conversões server-to-server" icon="server" href="/docs/guias/conversoes-server-to-server">
    Como registrar cobranças que alimentam o fluxo de comissão.
  </Card>
</CardGroup>
