Как избежать двойного оформления заказа при нестабильном интернете и повторных запросах?
Что проверяют
Понимаете ли вы, что повторный запрос неизбежен: пользователь жмёт кнопку дважды, URLSession получает таймаут уже после того, как сервер записал заказ, приложение уходит в фон посреди отправки. Защита от дубля строится на ключе идемпотентности и работает только в паре с сервером. Следом обычно спрашивают, что показать пользователю, если ответ так и не пришёл.
Как отвечать
Каркас из нескольких уровней:
- ключ идемпотентности: при открытии экрана оформления клиент генерирует
UUID()и отправляет его в заголовке, напримерIdempotency-Key. Ключ живёт, пока пользователь не изменил корзину, и одинаков для всех повторов одной попытки; - сервер: хранит ключ под уникальным ограничением вместе с результатом. Повтор с тем же ключом не создаёт заказ, а возвращает уже созданный. Без этого никакие клиентские ухищрения дубль не исключат;
- состояние на клиенте: кнопка блокируется на время запроса, во вью-модели флаг или конечный автомат
idle / sending / done / unknown. Ключ и черновик сохраняются на диск, чтобы после выгрузки приложения повтор ушёл с тем же ключом; - неизвестный исход: таймаут не означает, что заказ не создан. Правильно повторить запрос с тем же ключом или спросить статус по ключу, а не отправлять новую попытку с чистого листа;
- повторы: ограниченное число попыток с экспоненциальной задержкой, только на сетевые ошибки и 5xx, но не на 4xx.
NWPathMonitorподскажет, когда сеть вернулась.
Упомяните, что POST по спецификации HTTP не идемпотентен, поэтому гарантию приходится строить явно, а не надеяться на транспорт.
Где спотыкаются
Ограничиваются блокировкой кнопки. От двойного тапа это спасёт, от повтора после таймаута нет: первый запрос мог дойти, а потерялся только ответ.
Генерируют новый UUID на каждую попытку. Тогда сервер видит два разных запроса и честно создаёт два заказа.
Считают таймаут ошибкой и пишут пользователю «не удалось оформить». Человек оформляет заново, а деньги за первый заказ уже списаны.