Проблема: один пользователь, несколько запросов
Кнопка может быть нажата дважды, сеть может повторить запрос, а webhook может прийти несколько раз.
Idempotency означает, что повтор одного и того же намерения не меняет результат повторно. В платежах это особенно важно: повторный запрос не должен создавать второй заказ, второй платеж или второй чек.
Типовой пример: frontend отправил запрос создания оплаты, но не получил ответ из-за таймаута. Пользователь нажимает кнопку еще раз. Без idempotency backend может создать две платежные попытки с одной и той же суммой.
- повтор запроса создания платежа возвращает уже созданный payment id;
- повтор webhook не меняет финальный статус повторно;
- повтор фискальной задачи не создает второй чек;
- ошибки retries видны в журнале.
Где хранить idempotency key
Ключ должен жить на backend и быть связан с бизнес-намерением, а не только с HTTP-запросом.
Idempotency key можно генерировать на backend или принимать от доверенного клиента, но проверять его должен backend. Ключ полезно связывать с типом операции: create payment, deliver webhook, create fiscal receipt.
Для защиты от случайного переиспользования стоит хранить request hash. Если тот же ключ пришел с другой суммой или другим order id, это должно быть ошибкой, а не новой операцией.
- key + operation type как уникальное ограничение;
- request hash для проверки неизменности запроса;
- сохраненный response для безопасного повтора;
- expiration policy для очистки старых ключей.
Как idempotency связан со статусами
Idempotency не заменяет state machine, а работает вместе с правилами допустимых переходов.
Если платеж уже находится в финальном статусе paid, повторный pending webhook не должен откатить заказ назад. Idempotency защищает от дублей, а state machine защищает от неправильных переходов.
Обе защиты нужны одновременно. Ключ помогает понять, что операция уже выполнялась, а правила переходов определяют, можно ли менять бизнес-статус.
- финальный статус нельзя перезаписывать старым промежуточным;
- refund является новым событием, а не failed;
- дубли событий должны логироваться как повторная доставка;
- ручные изменения должны иметь отдельный audit trail.
FAQ
Idempotency key должен быть одинаковым для заказа и платежа?
Не обязательно. Обычно лучше разделять ключи по типу операции: создание платежа, webhook delivery, фискальная задача.
Что делать, если тот же ключ пришел с другой суммой?
Считать это конфликтом и не создавать новую операцию. Для этого хранится request hash или проверяются ключевые поля.
Idempotency нужен только для POST-запросов?
Чаще всего он нужен для команд, которые создают или меняют состояние: платеж, чек, доставку события, возврат.