A idempotência permite a resubmissão segura de solicitações, eliminando o risco de duplicar operações involuntariamente.
A idempotência garante que fazer a mesma chamada à API múltiplas vezes tem o mesmo efeito que fazê-la uma vez. Isto é crucial quando:
- Ocorrem timeouts de rede
- Não tem certeza se uma solicitação foi processada
- Precisa tentar novamente solicitações falhadas de forma segura
Para fazer uma solicitação idempotente, inclua um cabeçalho Idempotency-Key com um identificador único:
curl -L -X POST 'https://api.test.easypay.pt/2.0/single' \
-H 'AccountId: 2b0f63e2-9fb5-4e52-aca0-b4bf0339bbe6' \
-H 'ApiKey: eae4aa59-8e5b-4ec2-887d-b02768481a92' \
-H 'Idempotency-Key: 435e08a0-e5a9-4216-acb5-44d6b96de612' \
-H 'Content-Type: application/json' \
--data-raw '{
"type": "sale",
"value": 10.00,
"currency": "EUR",
"method": "cc"
}'- Primeira Solicitação: A Easypay processa a solicitação e armazena a resposta com a chave de idempotência
- Solicitações Duplicadas: Solicitações subsequentes com a mesma chave retornam a resposta armazenada sem reprocessar
- Payload Diferente: Se o corpo da solicitação diferir, é retornado um erro para prevenir uso acidental incorreto
Quando uma solicitação é uma repetição, a resposta inclui um cabeçalho Idempotency-Replay:
Idempotency-Replay: trueIsto ajuda-o a distinguir entre solicitações originais e repetidas na lógica da sua aplicação.
Boas Práticas:
- Use UUIDs (v4) ou ULIDs para chaves de idempotência
- Gere uma nova chave para cada operação única
- Comprimento máximo da chave: 50 caracteres
- As chaves devem ter entropia suficiente para prevenir colisões
Exemplos:
// JavaScript (usando UUID v4)
const { v4: uuidv4 } = require('uuid');
const idempotencyKey = uuidv4();# Python (usando UUID v4)
import uuid
idempotency_key = str(uuid.uuid4())// PHP (usando ramsey/uuid)
use Ramsey\Uuid\Uuid;
$idempotencyKey = Uuid::uuid4()->toString();Duas solicitações com a mesma Idempotency-Key mas diferentes cabeçalhos AccountId são tratadas como solicitações diferentes.
Solicitação 1: AccountId: account-1, Idempotency-Key: key-123
Solicitação 2: AccountId: account-2, Idempotency-Key: key-123
Resultado: Ambas as solicitações são processadas independentementeA camada de idempotência compara o corpo da solicitação das solicitações recebidas com o original:
- Mesmo corpo: Retorna a resposta em cache
- Corpo diferente: Retorna um erro para prevenir uso acidental incorreto
{
"status": "error",
"message": "Idempotency Error: Request body differs from original request",
"code": "IDEMPOTENCY_MISMATCH"
}As chaves de idempotência não são armazenadas para erros transitórios, permitindo-lhe tentar novamente com segurança:
429 Too Many Requests502 Bad Gateway503 Service Unavailable
Para estes erros, pode tentar novamente com a mesma chave de idempotência.
As chaves de idempotência são automaticamente removidas do sistema 24 horas após a criação. Após a expiração, a mesma chave pode ser usada para uma nova solicitação.
As chaves de idempotência são suportadas em:
- Solicitações POST
- Solicitações PATCH
- Solicitações GET (inerentemente idempotentes)
- Solicitações DELETE (inerentemente idempotentes)
Essencial:
- Criação de pagamentos
- Processamento de reembolsos
- Qualquer operação que modifique dados financeiros
Recomendado:
- Qualquer solicitação POST ou PATCH
- Operações que criam ou atualizam recursos
- Solicitações que podem ser tentadas novamente
Ao usar chaves de idempotência, siga esta estratégia de retentativa:
async function safeRequest(url, payload, maxRetries = 3) {
const idempotencyKey = generateUUID();
for (let i = 0; i < maxRetries; i++) {
try {
const response = await fetch(url, {
method: 'POST',
headers: {
'AccountId': process.env.EASYPAY_ACCOUNT_ID,
'ApiKey': process.env.EASYPAY_API_KEY,
'Idempotency-Key': idempotencyKey,
'Content-Type': 'application/json'
},
body: JSON.stringify(payload)
});
const shouldRetry = response.headers.get('X-Easypay-Should-Retry');
if (response.ok) {
return await response.json();
}
if (shouldRetry === 'true' && i < maxRetries - 1) {
await sleep(Math.pow(2, i) * 1000); // Backoff exponencial
continue;
}
throw new Error(`Request failed: ${response.status}`);
} catch (error) {
if (i < maxRetries - 1) {
await sleep(Math.pow(2, i) * 1000);
continue;
}
throw error;
}
}
}- Use Sempre para Pagamentos: Inclua chaves de idempotência em todas as solicitações relacionadas com pagamentos
- Gere Chaves Novas: Crie uma nova chave de idempotência para cada operação única
- Armazene Chaves: Guarde chaves de idempotência na sua base de dados para rastrear quais operações foram concluídas
- Verifique o Cabeçalho de Repetição: Use o cabeçalho
Idempotency-Replaypara detetar solicitações repetidas - Respeite a Expiração: Não reutilize chaves após 24 horas
- Trate Incompatibilidades: Se receber um erro de incompatibilidade de idempotência, gere uma nova chave
const axios = require('axios');
const { v4: uuidv4 } = require('uuid');
async function createPayment(paymentData) {
const idempotencyKey = uuidv4();
try {
const response = await axios.post(
'https://api.prod.easypay.pt/2.0/single',
paymentData,
{
headers: {
'AccountId': process.env.EASYPAY_ACCOUNT_ID,
'ApiKey': process.env.EASYPAY_API_KEY,
'Idempotency-Key': idempotencyKey,
'Content-Type': 'application/json'
}
}
);
// Verificar se esta foi uma solicitação repetida
const wasReplayed = response.headers['idempotency-replay'] === 'true';
if (wasReplayed) {
console.log('Este pagamento já foi processado');
}
return response.data;
} catch (error) {
// Verificar se devemos tentar novamente
const shouldRetry = error.response?.headers['x-easypay-should-retry'];
if (shouldRetry === 'true') {
// Tentar novamente com a mesma chave de idempotência
// ... implementar lógica de retentativa
}
throw error;
}
}- Tratamento de Erros - Aprenda sobre respostas de erro e estratégias de retentativa
- Início Rápido - Faça a sua primeira chamada idempotente à API
- Referência da API - Veja quais endpoints suportam idempotência