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

# Idempotência

> Idempotência como cidadão de primeira classe no **pagamentos.dev**

## O que é idempotência?

Idempotência garante que uma operação produza o mesmo resultado independentemente
de quantas vezes seja executada, desde que os mesmos parâmetros sejam usados. Em
pagamentos, isso é essencial para evitar cobranças duplicadas em cenários como:

* **Timeouts** — a requisição foi enviada mas a resposta não chegou
* **Retries automáticos** — o SDK ou seu cliente tentou novamente após um erro
* **Falhas de rede** — a conexão caiu antes de receber a confirmação
* **Double-submit** — o usuário clicou duas vezes no botão "Pagar"

## `idempotencyKey` como cidadão de primeira classe

No **pagamentos.dev**, a `idempotencyKey` é um campo obrigatório em **todas as
operações de mutação** — criar, atualizar e cancelar. Ela é passada diretamente
no payload da requisição, junto com `valor` e `metodoPagamento`:

```ts theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
const cobranca = await pg.cobrancas.create({
  idempotencyKey: crypto.randomUUID(), // ← sempre uma chave única!
  valor: 1000,
  metodoPagamento: 'pix',
  cliente: {
    nome: 'João Silva',
    documento: '123.456.789-00'
  }
})
```

A `idempotencyKey` é repassada ao provedor de pagamento de acordo com o
mecanismo suportado por cada um:

| Provedor         | Mecanismo                          | Suporte        |
| ---------------- | ---------------------------------- | -------------- |
| **Mercado Pago** | Header `X-Idempotency-Key`         | ✅ Completo     |
| **Woovi**        | Campo `correlationID` no body      | ✅ Completo     |
| **AbacatePay**   | PIX é assíncrono, chave é ignorada | ⚠️ Sem suporte |

## Como funciona na prática

### Gere a chave antes de chamar a API

A chave deve ser gerada no **cliente** (frontend ou serviço que inicia a requisição)
e armazenada para uso em retentativas:

```ts theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
import { randomUUID } from 'node:crypto'

async function criarCobranca(valor: number) {
  // 1. Gere e armazene a chave ANTES de chamar a API
  const idempotencyKey = randomUUID()

  try {
    const cobranca = await pg.cobrancas.create({
      idempotencyKey,
      valor,
      metodoPagamento: 'pix',
      cliente: {
        nome: 'João Silva',
        email: 'joao@email.com'
      }
    })

    // 2. Salve a chave junto com o resultado
    await salvarTransacao({ cobranca, idempotencyKey })
    return cobranca

  } catch (error) {
    // 3. Em caso de erro, retente com a MESMA chave
    //    O provedor reconhecerá a chave e retornará o
    //    resultado original, sem criar duplicatas.
    const cobranca = await pg.cobrancas.create({
      idempotencyKey, // ← mesma chave!
      valor,
      metodoPagamento: 'pix',
      cliente: {
        nome: 'João Silva',
        email: 'joao@email.com'
      }
    })

    await salvarTransacao({ cobranca, idempotencyKey })
    return cobranca
  }
}
```

### Em operações de update e cancel

```ts theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
// Update com idempotência
await pg.cobrancas.update('cob-123', {
  idempotencyKey: randomUUID(),
  valor: 2000
})

// Cancel com idempotência
await pg.cobrancas.cancel('cob-123', {
  idempotencyKey: randomUUID()
})
```

## Boas práticas

### 1. Sempre gere uma chave única por operação

Use `crypto.randomUUID()` (disponível em Node.js, Bun e navegadores modernos)
para gerar chaves. Nunca reutilize a mesma chave para operações diferentes.

```ts theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
// ✅ Correto
const cobranca = await pg.cobrancas.create({
  idempotencyKey: crypto.randomUUID(),
  valor: 1000,
  metodoPagamento: 'pix'
})

// ❌ Errado — mesma chave para operações diferentes
const chaveRepetida = 'chave-fixa'
await pg.cobrancas.create({ idempotencyKey: chaveRepetida, ... })
await pg.clientes.create({ idempotencyKey: chaveRepetida, ... })
```

### 2. Armazene a chave para retentativas

Salve a `idempotencyKey` no seu banco de dados junto com a transação. Se a
requisição falhar por timeout, você pode retentar com a mesma chave com
segurança.

```ts theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
// Salve junto com a transação
await db.transacoes.insert({
  idempotencyKey,
  cobrancaId: cobranca.id,
  status: cobranca.status,
  criadoEm: new Date()
})

// Consulte antes de criar (se você mesmo gerencia idempotência)
const existente = await db.transacoes.findByKey(idempotencyKey)
if (existente) {
  return existente.cobrancaId // já processamos esta operação
}
```

### 3. Trate erros de forma idempotente

Em caso de timeout ou erro de rede, retente a operação com a **mesma chave**.
O provedor (Woovi/Mercado Pago) detectará a chave repetida e retornará o
resultado original.

```ts theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
async function criarComRetry(dados: CreateInput, maxRetries = 3) {
  const idempotencyKey = randomUUID()

  for (let tentativa = 1; tentativa <= maxRetries; tentativa++) {
    try {
      return await pg.cobrancas.create({ ...dados, idempotencyKey })
    } catch (error) {
      if (tentativa === maxRetries) throw error
      if (!isErroRetryavel(error)) throw error
      await sleep(1000 * tentativa) // backoff exponencial
    }
  }
}
```

## Referência

A `idempotencyKey` está disponível nos seguintes tipos de input:

| Tipo                  | Campo                                     |
| --------------------- | ----------------------------------------- |
| `ClienteCreateInput`  | `idempotencyKey?: string`                 |
| `ClienteUpdateInput`  | `idempotencyKey?: string` (via `Partial`) |
| `CobrancaCreateInput` | `idempotencyKey?: string`                 |
| `CobrancaUpdateInput` | `idempotencyKey?: string`                 |
| `CobrancaCancelInput` | `idempotencyKey?: string`                 |
| `CheckoutCreateInput` | `idempotencyKey?: string`                 |

<Note>
  A `idempotencyKey` é opcional em todos os tipos, mas **fortemente recomendada**
  para operações de criação e atualização. Sem ela, retentativas podem gerar
  duplicatas.
</Note>
