Confiabilidade
Idempotência
Repita requisições POST com segurança. Envie o cabeçalho Idempotency-Key e a KrokoPay executa a operação uma única vez, respondendo cada repetição com a resposta original.
Nesta página
A rede falha no pior momento: depois que a KrokoPay criou a cobrança, mas antes de o seu servidor ler a resposta. Sem proteção, a solução natural, tentar de novo, cria uma segunda cobrança. Uma chave de idempotência avisa a KrokoPay que várias requisições são a mesma operação, e ela é executada uma vez só, não importa quantas vezes você a envie.
O cabeçalho Idempotency-Key
Adicione Idempotency-Key a qualquer requisição POST. A primeira requisição com uma chave é processada normalmente; as seguintes, com a mesma chave e o mesmo corpo, recebem o resultado guardado.
curl -X POST https://api.krokopay.app/v1/charges \ -H "Authorization: Bearer kp_test_sk_4f9a2c7e1b8d4a6f9c3e5d7b" \ -H "Idempotency-Key: 6f1c2b8e-3d4a-4e5f-9a7b-1c2d3e4f5a6b" \ -H "Content-Type: application/json" \ -d '{ "amount": 49700, "currency": "BRL", "description": "Curso Identidade Visual do Zero", "customer_id": "cus_8Hq2LmZr4TxN1a", "methods": [ "pix", "card" ] }'| Método | Idempotency-Key |
|---|---|
POST | Recomendado em toda requisição que cria ou altera algo: cobranças, reembolsos, clientes, links de pagamento, assinaturas, pausa e retomada. |
GET | Desnecessário. Uma leitura não tem efeitos colaterais e sempre pode ser repetida. |
DELETE | Desnecessário. Excluir o mesmo objeto duas vezes deixa tudo no mesmo estado. |
Gerando chaves
- Use um UUID v4 (
crypto.randomUUID()) ou um valor derivado do seu próprio registro, único por operação, comoorder_1042ourefund_1042_1. - Gere a chave uma vez por operação e guarde junto com o registro. Toda nova tentativa dessa operação deve reutilizá-la.
- Use uma chave nova para uma operação nova: um segundo reembolso da mesma cobrança é outra operação e precisa de outra chave.
- Nunca reutilize uma chave em requisições sem relação entre si e nunca coloque dados pessoais (e-mails, documentos) nela.
- O tamanho máximo da chave ainda será definido. Um UUID (36 caracteres) é uma escolha segura.
// Generate the key once per operation and store it with the order,// so every retry of that operation reuses it.const idempotencyKey = crypto.randomUUID(); // e.g. "6f1c2b8e-3d4a-4e5f-9a7b-1c2d3e4f5a6b"await db.orders.update(order.id, { idempotencyKey });O que acontece na repetição
Passo 1: Primeira requisição
Processada normalmente. O código de status e o corpo ficam guardados com a chave.
Passo 2: Mesma chave, mesmo corpo
Não é processada de novo. Você recebe o status e o corpo guardados, idênticos à primeira resposta, mesmo que ela tenha sido um erro, como um
422de validação.Passo 3: Mesma chave, corpo diferente
Rejeitada. Veja Conflitos.
Sucesso: Repetições são seguras
Pela proposta, as chaves valem por conta e pelo ambiente da chave de API usada, então requisições de teste nunca colidem com as de produção. O que acontece quando uma segunda requisição chega enquanto a primeira ainda está sendo processada ainda será definido; até lá, evite enviar a mesma chave em paralelo.
Conflitos
Reutilizar uma chave com outro corpo (outro valor, outro cliente) quase sempre indica um bug: duas operações dividindo a mesma chave. A KrokoPay recusa a requisição em vez de tentar adivinhar qual delas você queria.
curl -X POST https://api.krokopay.app/v1/charges \ -H "Authorization: Bearer kp_test_sk_4f9a2c7e1b8d4a6f9c3e5d7b" \ -H "Idempotency-Key: 6f1c2b8e-3d4a-4e5f-9a7b-1c2d3e4f5a6b" \ -H "Content-Type: application/json" \ -d '{ "amount": 59700, "currency": "BRL", "description": "Curso Identidade Visual do Zero", "customer_id": "cus_8Hq2LmZr4TxN1a", "methods": [ "pix", "card" ] }'HTTP/1.1 409 ConflictContent-Type: application/json{ "error": { "type": "idempotency_error", "code": "idempotency_key_reused", "message": "This Idempotency-Key was already used with a different request body." }}Informação: Proposta
409 e o código idempotency_key_reused são uma proposta e podem mudar. O formato geral dos erros está em Erros.Por quanto tempo as chaves ficam guardadas
Os resultados ficam guardados por tempo limitado; depois disso, a chave pode ser usada de novo como se fosse nova. Esse prazo de retenção ainda será definido. Não conte com chaves antigas para eliminar duplicados: repita a requisição logo e, em fluxos longos, confira antes o estado do seu próprio registro (por exemplo, se o pedido já tem um id de cobrança).
Um laço de novas tentativas com fetch
Tente de novo só quando o resultado for desconhecido ou temporário: erros de rede, timeouts e respostas 5xx. Um 4xx significa que a própria requisição precisa mudar; repeti-la igual devolve a mesma resposta.
const API_URL = "https://api.krokopay.app/v1";const SECRET_KEY = process.env.KROKOPAY_SECRET_KEY ?? ""; // kp_test_sk_…const sleep = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms));/** * POSTs with the same Idempotency-Key on every attempt. Retries network * failures and 5xx answers only; any 4xx is returned to the caller as is. */export async function postWithRetry( path: string, body: Record<string, unknown>, idempotencyKey: string, maxAttempts = 3,): Promise<Response> { for (let attempt = 1; ; attempt++) { try { const response = await fetch(`${API_URL}${path}`, { method: "POST", headers: { Authorization: `Bearer ${SECRET_KEY}`, "Idempotency-Key": idempotencyKey, "Content-Type": "application/json", }, body: JSON.stringify(body), // identical on every attempt }); if (response.status < 500 || attempt >= maxAttempts) return response; } catch (error) { // Timeout or dropped connection: the charge may or may not exist yet. if (attempt >= maxAttempts) throw error; } // Exponential backoff with jitter before the next attempt. await sleep(2 ** attempt * 250 + Math.random() * 250); }}// Usage: the key comes from the order, not from the attempt.const response = await postWithRetry( "/charges", { amount: 49700, currency: "BRL", description: "Order 1042", methods: ["pix", "card"] }, order.idempotencyKey,);const charge = await response.json();| Resultado | Tentar de novo? |
|---|---|
| Erro de rede ou timeout | Sim, com a mesma chave. |
5xx | Sim, com a mesma chave e um intervalo crescente. |
Conflito 409 | Não. Corrija a chave ou o corpo. |
Outros 4xx (401, 404, 422) | Não. Corrija a requisição; veja Erros. |
2xx | Não. Pronto. |
Veja também
- Criando cobranças: onde a idempotência mais importa.
- Webhooks: eliminando eventos duplicados do seu lado.
- Erros: códigos de status e o formato dos erros.