Досрочный расчёт ставки без гонок и двойного подтверждения
Кнопка cashout выглядит простой, а под ней проходит короткая распределённая операция. Рассматриваем quote, повторную проверку состояния, блокировку и идемпотентный commit без ложного ощущения мгновенного результата.

Не обещать пользователю то, чего ещё нет
Кнопка cashout выглядит как простое действие, но значение предложения зависит от текущего состояния ставки и рынков. Поэтому в интерфейсе надо различать offer и result. Показанная сумма — quote с временем жизни, а не уже гарантированный расчёт.
Оркестрация из трёх шагов
Quote читает состояние ставки и pricing input, формирует offer token. Commit повторно проверяет token, ставит логическую блокировку на bet и выполняет изменение состояния вместе с финансовой записью. UI получает финальный result, а не угадывает успех по исчезновению кнопки.
Блокировка не должна превращаться в глобальную блокировку
Блокировать весь аккаунт на время cashout слишком дорого и создаёт ненужную связанность. Достаточно узкой блокировки конкретной ставки/позиции. Если продукт поддерживает несколько связанных операций, конфликт должен возвращаться как бизнес-состояние, а не как случайный 500.
Идемпотентность и повтор ответа
Commit получает idempotency key. После успешной фиксации повтор возвращает тот же cashout result. Если зависимость упала до изменения состояния, команда может быть безопасно повторена. Если состояние уже изменилось, оркестратор не запускает расчёт заново.
Частичный отказ нельзя прятать в цикл повторов
Если pricing ответил, но bet store недоступен, quote просто истекает. Если bet state изменён, а внешняя нотификация не отправилась, это уже отдельная вторичная задача. Оркестратор должен иметь ясную точку commit и не смешивать её с необязательными side effects.
Что пользователь должен видеть во время операции
Cashout лучше проектировать как короткий процесс, а не как мгновенную кнопку. Сначала система получает предложение, затем фиксирует допустимое состояние и только после этого выполняет commit. Пока операция не завершена, интерфейс должен отличать «рассчитываем», «подтверждаем» и «результат получен».
Это особенно важно при сетевой задержке. Повторное нажатие не должно создавать вторую операцию, а исчезновение кнопки без объяснения не должно выглядеть как ошибка интерфейса. Идемпотентный ключ, серверный статус операции и ясный текст позволяют пережить повтор запроса без двойного результата.
- Привязать offer к короткому сроку жизни и конкретной версии состояния.
- Не держать блокировку дольше, чем реально нужен commit.
- После timeout запрашивать статус операции, а не автоматически создавать новую.
Самая неприятная проверка — повторное подтверждение
Представим медленную сеть: пользователь запросил cashout, увидел сумму, подтвердил, но spinner висит дольше обычного. Он нажимает ещё раз или закрывает экран и возвращается. Система должна отличить повтор той же команды от новой попытки, а UI — не обещать, что отсутствие ответа означает отсутствие операции.
Идемпотентный command id решает только часть задачи. Нужен ещё способ получить уже известный результат и восстановить экран после reconnect. Если сервер отвечает «не знаю», клиент сначала сверяет состояние позиции, а не запускает новую денежную операцию. Для этого сценария полезно применять тот же принцип, что в idempotent command: повтор безопасно возвращает прежний outcome.
На продуктовой стороне quote должен иметь видимый срок жизни. Когда время вышло, кнопка подтверждения не остаётся активной «по инерции»: пользователь получает новый quote и понимает, почему сумма изменилась.
Что объясняет сбой досрочный расчёт
Нужны раздельные причины: offer unavailable, quote expired, lock conflict, commit dependency failed. Одна метрика «cashout error rate» ничего не расскажет команде поддержки. Для каждой попытки сохраняется correlation id, но без чувствительных данных в технических логах.

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