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

# Cotações de câmbio

> Exiba e confirme o valor final com o comprador antes da autorização quando a empresa repassa o câmbio a ele.

Algumas empresas apresentam preços numa moeda e recebem em outra. Quando a empresa está configurada
para repassar o câmbio ao comprador, ele é cobrado **mais** que o preço comercial — e esse valor final
precisa ser exibido e confirmado **antes** da autorização.

O Checkout hospedado já faz isso sozinho. Em todo o resto — Payment Element, integração direta pela
API, sua própria página de pagamento — a tela é sua, então a garantia é contratual: você cria uma
cotação, exibe, e informa o id dela ao criar a transação.

<Note>
  Esta página só vale para empresas configuradas para repassar o câmbio ao comprador. Se a sua empresa
  absorve o câmbio, nada muda: continue criando transações exatamente como faz hoje.
</Note>

## O fluxo

<Steps>
  <Step title="Crie a cotação">
    Chame `POST /v2/fx/quotes` com o valor comercial, a moeda apresentada ao comprador e o método pelo
    qual você vai cobrar.

    ```bash theme={null}
    curl -X POST https://api.pagou.ai/v2/fx/quotes \
      -H "Authorization: Bearer $PAGOU_SECRET_KEY" \
      -H "Content-Type: application/json" \
      -H "Idempotency-Key: pedido-1029" \
      -d '{ "amount": 1000, "currency": "BRL", "payment_method": "pix" }'
    ```

    ```json theme={null}
    {
      "data": {
        "id": "0198f3a0-1c2d-7e00-9a3b-6f8a2c4d1e5f",
        "presented_currency": "BRL",
        "wallet_currency": "USD",
        "payment_method": "pix",
        "base_rate": "0.2000000000",
        "spread_bps": 500,
        "presented_amount": 1000,
        "spread_amount": 50,
        "buyer_charged_amount": 1050,
        "expires_at": "2026-08-13T12:05:00.000Z"
      }
    }
    ```

    Valores são inteiros na menor unidade da moeda. O `Idempotency-Key` é opcional; repetir a chamada com
    a mesma chave devolve a mesma cotação, em vez de congelar uma segunda taxa.
  </Step>

  <Step title="Mostre ao comprador o que ele vai pagar">
    Antes de o comprador autorizar, sua página precisa exibir:

    * **o valor final** (`buyer_charged_amount`), na moeda cobrada, em destaque — ele tem que ser igual ao
      valor autorizado;
    * o valor comercial (`presented_amount`) e a moeda de origem, quando diferentes;
    * a cotação aplicada (`base_rate`) e a moeda de destino;
    * um rótulo neutro e consistente.

    Nunca:

    * chamar de imposto, IOF, MDR, taxa do cartão ou taxa da bandeira — não é nenhum deles;
    * apresentar como uma escolha do comprador;
    * alterar o valor entre a confirmação e a autorização;
    * fazer o câmbio aparecer só depois de o comprador escolher o método.

    <Warning>
      Os dois últimos não são questão de estilo. Um valor que muda depois da confirmação, ou uma margem que
      só aparece depois da escolha do método, mudam o que a cobrança é juridicamente.
    </Warning>
  </Step>

  <Step title="Crie a transação com a cotação">
    Informe `fx_quote_id` no `POST /v2/transactions`. A cobrança usa a taxa congelada na cotação, não a
    atual, então o comprador é autorizado exatamente pelo valor que confirmou.

    ```json theme={null}
    {
      "amount": 1000,
      "currency": "BRL",
      "method": "pix",
      "fx_quote_id": "0198f3a0-1c2d-7e00-9a3b-6f8a2c4d1e5f",
      "buyer": { "name": "...", "email": "..." }
    }
    ```

    O `amount` continua sendo o valor **comercial** — o mesmo que você cotou. O total do comprador sai da
    cotação; enviá-lo aqui seria cotar um número e cobrar outro.
  </Step>
</Steps>

## Com o Payment Element

O Element tokeniza o cartão e cuida do 3DS; ele nunca renderiza valor. Então a cotação vive no seu
próprio fluxo, em volta do `submit`:

```js theme={null}
const quote = await fetch("/api/fx-quote", { method: "POST" }).then((r) => r.json());

renderTotal(quote); // sua UI, seguindo as regras de exibição acima

const result = await elements.submit({
  createTransaction: ({ token }) =>
    fetch("/api/pay", {
      method: "POST",
      body: JSON.stringify({ token, fx_quote_id: quote.id }),
    }).then((r) => r.json()),
});
```

Seu back-end repassa o `fx_quote_id` ao `POST /v2/transactions`.

## Erros

| código                       | o que aconteceu                                                                                                            |
| ---------------------------- | -------------------------------------------------------------------------------------------------------------------------- |
| `FX_QUOTE_REQUIRED`          | A empresa repassa o câmbio ao comprador e nenhum `fx_quote_id` foi enviado. Crie a cotação, exiba e tente de novo.         |
| `FX_QUOTE_EXPIRED`           | A cotação venceu. Crie outra e confirme o novo total com o comprador — nunca reaproveite o valor antigo.                   |
| `FX_QUOTE_REUSED`            | A cotação já autorizou outra transação. Cotações são de uso único.                                                         |
| `FX_QUOTE_AMOUNT_MISMATCH`   | O valor da transação difere do valor comercial cotado.                                                                     |
| `FX_PASSTHROUGH_NOT_ALLOWED` | Este método e moeda não estão liberados para repassar o câmbio ao comprador.                                               |
| `503`                        | Não há cotação utilizável. A cobrança é recusada em vez de convertida por uma taxa inventada — tente de novo em instantes. |

## Observações

* Cotações são de uso único e expiram junto com a taxa que as gerou; espere criar uma por tentativa de
  checkout.
* Uma cotação pertence a uma empresa. Cotação de outra empresa devolve `404`.
* Moeda apresentada igual à da carteira significa que não há câmbio, e o `POST /v2/fx/quotes` devolve
  `422`.
