Проблема:
HTTP POST не является идемпотентным по определению. Если клиент повторяет запрос из-за таймаута, сервер не может отличить повтор от нового запроса. Это приводит к дублированию операций (списаний, созданий заказов).
Решение – идемпотентный ключ (Idempotency Key):
Клиент генерирует уникальный ключ (например, UUID) и передаёт его в заголовке
Idempotency-Key. Сервер хранит этот ключ вместе с результатом операции в течение определённого времени (например, 24 часа). При повторном запросе с тем же ключом сервер возвращает ранее сохранённый результат, не выполняя операцию повторно.Алгоритм работы сервера:
Получить ключ из заголовка.
Проверить в хранилище (Redis, БД), есть ли уже выполненная операция с этим ключом.
Если есть – вернуть сохранённый ответ.
Если нет – выполнить операцию (списание денег), сохранить ключ и результат, затем вернуть ответ.
Пример кода (Node.js):
javascript
const idempotencyKey = req.headers['idempotency-key'];
const cached = await redis.get(idempotencyKey);
if (cached) return JSON.parse(cached);
const result = await processPayment(req.body);
await redis.setex(idempotencyKey, 86400, JSON.stringify(result));
res.json(result);
Почему не подходят другие варианты:
B (IP-адрес) – не надёжен: несколько клиентов за NAT, динамические IP.
C (rate limiting) – не решает дублирование из-за таймаута, а лишь ограничивает частоту.
D (DELETE) – не подходит для создания ресурса/списания средств.
Реальный пример:
Stripe требует обязательный заголовок
Idempotency-Key для идемпотентных запросов. Это позволяет клиентам безопасно повторять запросы при сетевых сбоях.Что должен зафиксировать аналитик:
«API должен поддерживать заголовок Idempotency-Key для всех небезопасных методов (POST, PUT, PATCH)».
«Ключ должен храниться не менее 24 часов».
«Ответ на повторный запрос с тем же ключом должен быть идентичен первому».
Вывод: Idempotency Key – стандарт защиты от двойной обработки в REST API, обязательный для финансовых и критических операций.