Валидация купона ставки: что проверяет интерфейс, а что сервер
Быстрые проверки удобнее делать на клиенте, но финальное решение зависит от рынка, аккаунта и текущей цены. Разбираем границу между UX-валидацией, quote и серверным подтверждением операции.

Две скорости одной формы
Betslip должен реагировать мгновенно, но не имеет права самостоятельно решать, что ставка допустима. Локальная проверка нужна для очевидных вещей — пустая сумма, формат, конфликт выбора в одной группе. Все условия, зависящие от рынка, аккаунта или лимитов, подтверждает сервер.
Расчёт перед размещением ставки
Полезная граница — отдельный quote. Клиент отправляет selections и сумму, получает проверенную цену, допустимый диапазон и короткоживущий token. Placement использует этот token и idempotency key. Так UI не пытается повторить весь rule engine, а commit остаётся защищён от двойного клика и сетевого повтора.
Цена дополнительного сетевого обмена
Quote добавляет запрос перед размещением. Зато он делает изменения условий объяснимыми: клиент получает структурированную причину — price changed, market suspended, stake out of range. Без этого ошибки часто превращаются в общий «не удалось оформить», а логика расползается между frontend и backend.
Ошибки как часть программный интерфейс-контракта
Вместо текстовых сообщений API возвращает machine-readable code и данные для интерфейса. Например, PRICE_CHANGED содержит новую цену и требует явного принятия пользователем; MARKET_SUSPENDED блокирует selection; LIMIT_CHANGED возвращает новый диапазон. Текст локализуется на клиенте или BFF, но смысл ошибки остаётся стабильным.
Чего не должно происходить при повторе
Самый неприятный сетевой сценарий — commit прошёл, ответ потерялся, клиент повторил запрос. Idempotency key должен идентифицировать именно попытку размещения, а не сессию. Повтор с тем же ключом возвращает уже известный результат; другой payload с тем же ключом считается конфликтом.
Как разложить проверки по слоям
Клиентская проверка отвечает за мгновенную обратную связь: пустое значение, формат суммы, очевидные ограничения интерфейса. Серверная — за всё, что зависит от текущего состояния рынка, аккаунта, лимитов и бизнес-правил. Между ними полезен quote: он позволяет заранее показать изменившуюся цену или недоступность до финального commit.
Такой подход не убирает дополнительный запрос, зато делает его частью понятного продукта. Пользователь видит, что условия были перепроверены, а команда получает один источник окончательного решения вместо дублирования правил в нескольких клиентах.
- Не копировать сложные лимитные правила во frontend только ради скорости.
- Возвращать машинный код причины и человекочитаемое сообщение отдельно.
- Финальный commit должен повторно проверять критичные условия, даже если quote был успешным.
Матрица проверок вместо общего «форма невалидна»
На практике спор о границе заканчивается быстрее, если разложить проверки по источнику данных. Пустая сумма и формат числа известны клиенту сразу. Доступность рынка, лимит аккаунта, свежая цена и возможность конкретной комбинации зависят от серверного состояния. Значит, первая группа отвечает за мгновенную обратную связь, а вторая — за quote и финальный commit.
Полезно прогнать матрицу на повторе. Если quote вернулся с PRICE_CHANGED, пользователь принимает новую цену и нажимает confirm; финальный placement всё равно обязан перепроверить критичные условия. Между двумя запросами рынок снова мог suspend. Сервер не должен считать старый успешный quote вечным разрешением.
В компоненте Betslip это выражается отдельными состояниями quoting, repriced и placing. А путь betslip → placement полезно держать перед глазами при review: там видно, на каком шаге возможен новый отказ и кто объясняет его пользователю.
Эксплуатация через причины отказа
Мониторинг строится по кодам валидации, а не только по HTTP 4xx. Резкий рост PRICE_CHANGED говорит про лаг market state, QUOTE_EXPIRED — про слишком короткое окно или медленный UI, DUPLICATE_COMMAND — про поведение сети/клиента. Это уже диагностические сигналы, а не просто ошибки.

Связанные разборы по той же архитектурной цепочке
Кнопка коэффициента: доступность, изменение цены и приостановка
Как проектировать кнопку коэффициента: изменение цены, приостановка, фокус, клавиатурное управление и понятные состояния после выбора пользователя.
Перейти к разбору →Букмекерская логикаКак проверять отказоустойчивость букмекерской системы до запуска
Проверка сценариев отказа до запуска: потеря поставщика, переполнение очереди, повтор команды, рассинхронизация и восстановление.
Перейти к разбору →АрхитектураСостояние спортивного события: счёт, фазы и завершение матча
Как связать счёт, таймер и фазы матча в модель состояний, обрабатывать поздние сообщения поставщика и не допускать невозможных переходов.
Перейти к разбору →ИнтерфейсыСтраница спортивного события: как показать много рынков и не потерять контекст
Как устроить страницу спортивного события с большим числом рынков: стабильный контекст, группировка, быстрые обновления и метрики качества интерфейса.
Перейти к разбору →