Skip to main content

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.

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:
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:
ProvedorMecanismoSuporte
Mercado PagoHeader X-Idempotency-Key✅ Completo
WooviCampo correlationID no body✅ Completo
AbacatePayPIX é 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:
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

// 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.
// ✅ 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.
// 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.
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:
TipoCampo
ClienteCreateInputidempotencyKey?: string
ClienteUpdateInputidempotencyKey?: string (via Partial)
CobrancaCreateInputidempotencyKey?: string
CobrancaUpdateInputidempotencyKey?: string
CobrancaCancelInputidempotencyKey?: string
CheckoutCreateInputidempotencyKey?: string
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.