Gambling — это одна из немногих индустрий, где баг в проде — это не просто «пользователи немного расстроятся». Это деньги. Реальные. Иногда чужие.
Я работаю QA Lead в платформе с казино, спортивным беттингом, крипто-выплатами и 21 микросервисом. Тестирование игровых платформ и платёжных систем — это отдельная история по сравнению с обычным вебом: здесь конкурентные сценарии, финансовые транзакции и compliance требования пересекаются в одном продукте. Ниже — пять случаев, которые я хорошо помню. Не потому что они технически сложные, а потому что они показывают, как именно ломается gambling, когда о конкретных вещах не думают заранее.
Race condition при выводе средств
Пользователь нажал «вывести 500 USDT». Кнопка визуально не отреагировала — интернет лагал. Нажал ещё раз. Оба запроса ушли на сервер с разницей в 300 миллисекунд.
Дальше — классика. Сервис вывода и сервис баланса жили отдельно и общались по API. Оба запроса пришли почти одновременно. Оба прочитали баланс: 500. Оба прошли проверку «достаточно средств». Оба создали транзакцию. Баланс стал -500.
Race condition в тестировании платёжных систем — это то, о чём все знают в теории и что регулярно пропускают на практике. Особенно когда сервис вывода и сервис баланса разнесены по разным микросервисам. Каждый из них в изоляции тестируется нормально. А вот поведение при одновременных запросах — нет.
В gambling это критично, потому что вектор атаки существует: пользователь может воспроизвести двойное нажатие намеренно, а не только случайно. Фикс на уровне UI (задизейблить кнопку после клика) не помогает — запрос можно отправить напрямую.
Тест простой: два параллельных запроса на вывод с одного аккаунта. Если оба проходят, идемпотентности нет. Правильное решение на бэкенде — уникальный idempotency key на каждый запрос вывода, оптимистичная блокировка на уровне записи баланса или distributed lock. Без одного из этих трёх — система уязвима.
В любом QA-чеклисте для gambling-платформы этот тест должен стоять первым в разделе платёжных сценариев. Всегда.
Рассогласованность баланса в event-driven системе
Игровая сессия завершилась, событие ушло в Kafka, кошелёк должен был обновиться. Пользователь открыл кошелёк через секунду и увидел старый баланс.
Сначала казалось — просто лаг, бывает. Потом оказалось, что Kafka-консьюмер лежал несколько минут и накопил очередь. Сервис кошелька читал из кэша. Кэш не инвалидировался, пока событие не обработается. Событие ещё не обработалось. Пользователь уже делал новую ставку, опираясь на старые цифры.
Event-driven архитектура в casino и беттинговых платформах предполагает, что данные временно рассогласованы — это нормально и это заложено в дизайн. Ненормально, когда система это никак не обозначает и не защищает пользователя от действий на основе устаревшего состояния. Особенно в gambling, где устаревший баланс напрямую влияет на ставки.
Самая опасная комбинация: consumer lag + кэш без TTL + быстрый пользователь. Все три условия возникают под нагрузкой — именно тогда, когда это наиболее болезненно.
Тестировать нужно не только happy path, но и поведение при задержке. Искусственно притормози консьюмер — минимальные ресурсы в тестовой среде или explicit sleep. Потом проверяй: что может сделать пользователь, пока состояние не синхронизировалось? Может ли он поставить ставку? Видит ли он какой-то индикатор загрузки или устаревших данных?
Если за 10+ секунд система показывает старый баланс без единого намёка на то, что данные обновляются — это баг UX, даже если технически всё сходится. В тестировании казино-платформ это регулярно пропускают, потому что в счастливом сценарии всё работает мгновенно.
Зависшая игровая сессия и замороженный баланс
Игрок был в слоте. Интернет пропал. Браузер закрылся без нормального disconnect.
Сервер не получил событие close — WebSocket так работает при резком обрыве соединения. Сессия осталась открытой. Деньги для текущего раунда зарезервированы и заморожены. Пользователь вернулся через 10 минут, зашёл снова — и увидел «у вас уже есть активная сессия».
Поддержка, тикет, ручное закрытие сессии разработчиком, недовольный пользователь. Всё по классике.
Проблема воспроизводится на двух сценариях. Первый — резкий обрыв: закрытый ноутбук, потеря Wi-Fi, убийство процесса браузера. Второй — мобильный: приложение уходит в фон, ОС убивает соединение. В обоих случаях сервер не получает close.
Если нет heartbeat-механизма и таймаута для «мёртвых» сессий — они накапливаются. Средства заморожены. Со временем это становится проблемой не только UX, но и бухгалтерии: зарезервированные, но незавершённые транзакции.
Симулируется прямо в DevTools: открываешь игровую сессию → Network → Offline → ждёшь конфигурированный таймаут → проверяешь, освободился ли баланс и закрылась ли сессия автоматически. Если нет — нашёл. Дополнительно: вход с другого устройства должен инвалидировать старую сессию, а не открывать две параллельно.
Коэффициент изменился — ставка уже принята
Пользователь поставил на матч при коэффициенте 3.2. Ставка принята. Матч начался, коэффициент съехал до 1.8. При выигрыше выплатили по 1.8.
Технически система ничего не нарушила — она использовала актуальный коэффициент на момент обработки. Но пользователь ставил при 3.2 и именно такой выплаты ожидал. Формально он был прав.
В тестировании спортивного беттинга это один из самых частых пропусков. Live-коэффициенты меняются постоянно — иногда несколько раз в секунду. Между кликом пользователя и обработкой на сервере всегда есть окно. Под нагрузкой оно растягивается. Если в этом окне коэффициент изменился — система должна либо зафиксировать тот, при котором ставка принималась, либо явно отклонить и попросить подтверждение с новым значением.
Ни то ни другое не было реализовано: ставка всегда принималась, расчёт шёл по текущему коэффициенту на момент записи в базу.
Тест: один поток агрессивно меняет коэффициент через admin API, второй одновременно делает ставки через клиентский API. Потом смотришь, по какому значению считается выплата. Если не по зафиксированному в момент принятия — это баг. Тест занимает полчаса на написание, воспроизводимость 100%, делают его редко.
Двойной KYC и проблемы с идемпотентностью
Пользователь загружал документы для верификации. Нажал «отправить», кнопка зависла под медленным интернетом — нажал ещё раз. Полсекунды разницы.
Оба запроса дошли до сервиса верификации. Оба увидели статус «не верифицирован». Оба создали заявку. В базе оказались две KYC-записи на одного пользователя: одна pending, вторая approved. Сервис начал вести себя непредсказуемо при проверке статуса — он не ожидал двух записей.
В QA gambling-продуктов KYC — это не просто пользовательский флоу. Это compliance. В большинстве юрисдикций операторы обязаны хранить верификационные данные в определённом формате и структуре. Дублирующиеся записи с разными статусами — это потенциальная проблема при регуляторном аудите, не только технический баг.
Сервис верификации не был идемпотентным. Два одинаковых запроса — два разных результата в базе. Исправление требовало уникального ограничения на уровне базы данных плюс логику проверки существующей заявки перед созданием новой.
Проверяется так же, как двойной вывод: два параллельных запроса с одним userId. Смотришь, сколько записей создалось. Правильный ответ — одна. Если две — идемпотентность не реализована. Отдельным тест-кейсом стоит проверить повторную отправку документов после уже успешной верификации. Система должна вернуть чистый отказ, не создавать новую заявку.
Тестирование казино-платформ и беттинговых сервисов выглядело бы проще, если бы все эти баги были редкими случаями. Но они воспроизводятся в разных командах и разных продуктах — потому что конкурентные сценарии редко попадают в первоначальный тест-план. Happy path тестируют все. Параллельные запросы, обрывы соединений, задержки между сервисами — нет.
Если хоть один из этих пяти кейсов ты ещё не проверял в своём продукте — это хорошее место, чтобы начать. Дальше — чеклист по тестированию платёжных систем в казино, тестирование крипто-выплат и что проверять при интеграции game providers.