Бэкенду казино, который подключает слоты, игры с живыми дилерами и настольные игры от множества провайдеров, нужен один слой агрегации: сессии, которые выдаёт оператор, колбэки баланса, переживающие повторы и откаты, раунды, которые могут закрыться после сессии, и ежедневная сверка с собственным отчётом каждого провайдера. Здесь — как разделён этот слой и где интеграции провайдеров обычно ломаются.
Оператор, который строит собственный бэкенд казино и подключает слоты, игры с живыми дилерами и настольные игры от множества провайдеров, получает столько интеграционных контрактов, сколько у него провайдеров: разные сценарии запуска, разные вызовы кошелька, разные представления о том, что такое раунд. Слой агрегации сводит их к одному внутреннему контракту, поэтому кошелёк, лобби, бонусы, лимиты и отчётность пишутся один раз, а каждый провайдер адаптируется под них.
Дальше — как обычно разделён этот слой: что остаётся у провайдера, как выдаются сессии, как колбэки баланса переживают повторы и откаты, как записываются раунды, которые закрываются после сессии, и как итог сверяется с собственными цифрами провайдера.
Короткий ответ — один внутренний контракт и по адаптеру на каждого провайдера. Сессию выдаёт оператор; каждое списание, зачисление и откат несёт ID транзакции провайдера как ключ идемпотентности в пределах провайдера и типа вызова; откат транзакции, которую кошелёк так и не видел, сохраняется, поэтому опоздавший оригинал отклоняется; раунды записываются как состояние, которое может закрыться после окончания сессии; а собственный отчёт каждого провайдера каждый день сверяется с леджером кошелька.
Провайдер отвечает за игру: генерацию случайных чисел, математику игры, игровой клиент и его сертификацию в тестовой лаборатории. У оператора остаётся всё, что касается игрока и денег: идентификация, баланс, лимиты, бонусы, лобби и записи, которые могут понадобиться регулятору или при споре с игроком. Слой агрегации находится между ними и должен быть единственным серверным кодом, который общается с API каждого провайдера.
Провайдеры подключаются к деньгам оператора одним из двух способов. В бесшовном кошельке (seamless wallet) баланс остаётся у оператора, и провайдер вызывает кошелёк оператора на каждую ставку и каждый выигрыш. В трансферном кошельке (transfer wallet) оператор перед игрой переводит деньги на баланс, который хранится на стороне провайдера, и возвращает их обратно, только когда сам их запрашивает.
Дальше в статье предполагается бесшовный кошелёк, потому что в нём каждая ставка и каждый выигрыш — это вызов кошелька.
Запуск игры начинается на стороне оператора. Бэкенд проверяет, что этому игроку можно играть в эту игру сейчас: состояние счёта, самоисключение, лимиты и то, можно ли предлагать эту игру в юрисдикции игрока. Затем он создаёт сессию, привязанную к игроку, игре и валюте, и при запуске передаёт провайдеру непрозрачный токен. Когда сервер провайдера присылает колбэк, этот токен определяет, о чьём балансе этот вызов.
Опубликованные документы для операторов говорят об этом прямо. В API кошелька Hub88 сказано, что для выигрышей и откатов валидность токена проверять нельзя, поскольку они могут прийти после того, как ставка уже сыграна. VeliGames указывает, что оператор не вправе отклонить выигрыш по раунду, даже если сессия истекла.
Любой вызов между двумя серверами может упасть по таймауту уже после того, как работа на другой стороне сделана. Провайдер не может отличить списание, которое не прошло, от списания, ответ на которое потерялся, поэтому он повторяет вызов или отменяет транзакцию. Задача кошелька — сделать безопасным и то и другое.
Опубликованные интеграционные документы показывают, насколько настойчивы повторы. Операторский API кошелька Hub88 считает ставку неудавшейся, если не получает HTTP 200, формирует откат и повторяет этот откат до 500 раз с экспоненциально растущей задержкой. Gamomat повторяет неудавшийся запрос дважды с интервалом 500 ms, затем запускает откат и повторяет его с интервалами, растущими от одной секунды до 30 минут. Таймаут кошелька у Tom Horn Gaming — 10 секунд, после чего откат отправляется автоматически. Кошелёк, упавший на несколько минут, возвращается к очереди повторов и откатов, а не к тишине.
Ожидаемый ответ на повтор тоже не стандартизирован. Hub88 требует, чтобы запросы с одним и тем же ID транзакции не обрабатывались дважды, а ответ на все дубликаты был одинаковым; VeliGames просит возвращать ошибку с HTTP status 409 и DUPLICATE_TRANSACTION; у Tom Horn Gaming есть отдельный код результата для дублирующегося reference. Адаптер отвечает каждому провайдеру в его собственной форме, а леджер под ним остаётся тем же.
Откат неизвестной транзакции легко реализовать неправильно. Если кошелёк ничего не сохраняет, списание, которое лишь задержалось в пути, приходит мгновением позже и проходит успешно, и игрок платит за ставку, которую провайдер уже отменил. Эту дыру закрывает то, что откат сохраняется первым, а списание проверяет его наличие под своей блокировкой счёта.
Провайдеры формулируют это правило в собственных документах. В операторском API St8 сказано, что, когда оператор получает ID транзакции для отмены, которую он раньше не обрабатывал, этот ID нужно сохранить, чтобы не допустить его обработки позже. Tom Horn Gaming ожидает свой код результата для неизвестной транзакции, если кошелёк так и не обработал снятие средств, на которое ссылается откат.
Раунд — это единица игры у провайдера, и он редко соответствует одной транзакции. Спин в слоте — это часто одно списание и одно зачисление, иногда отправленные одним вызовом. Блэкджек может добавлять списания на сплит или удвоение. Рулетка с живым дилером принимает ставки от многих игроков в окне приёма ставок и рассчитывает их все, когда известен результат. Бесплатные раунды могут давать серию выигрышей, которые связаны между собой.
Спор с игроком разрешают по истории раундов. Храните каждую проводку в леджере с провайдером, игрой, раундом, суммами, балансом до и после и двумя отметками времени — провайдера и кошелька — и давайте ссылку на собственные детали раунда у провайдера, если его API их предоставляет. Тогда на вопрос о деньгах в одном спине отвечают по записям.
Минимум, который должна покрывать эта история, устанавливают регуляторы. GLI-19, стандарт для систем интерактивных азартных игр от Gaming Laboratories International, требует для игрока функции просмотра прошедших игр — либо в виде воспроизведения, либо в виде описания. Технические стандарты Комиссии по азартным играм Великобритании (UK Gambling Commission) для дистанционных азартных игр требуют доступа не менее чем к трём месяцам истории счёта и игр без обращения к лицензиату и не менее чем к 12 месяцам по запросу. Директива о защите игроков Управления по азартным играм Мальты (Malta Gaming Authority) даёт игроку доступ к его истории игр за непосредственно предшествующие шесть месяцев.
Слоты распределяют нагрузку во времени, потому что каждый игрок крутит спины в своём темпе. Столы с живыми дилерами синхронизируют игроков: ставки всех, кто сидит за столом, приходят в секунды перед закрытием приёма ставок, а выигрыши всех приходят разом, когда известен результат. Популярный стол повторяет это для каждого игрока, сделавшего ставку.
Различия между провайдерами живут в адаптерах, и больше нигде жить не должны. Каждый адаптер отвечает за следующее:
Внутренний контракт остаётся небольшим: открыть сессию, прочитать баланс, списать, зачислить, списать и зачислить одним вызовом, выплатить без ставки, откатить, закрыть раунд — и фиксированный набор ошибок, которые может вернуть кошелёк. Тогда новый провайдер — это адаптер и набор тестов, а изменение в кошельке — редко.
Каждый провайдер ведёт собственный учёт каждого раунда и выставляет по нему счета оператору. Леджер слоя агрегации — это те же деньги со стороны оператора. Сверяйте их каждый день по границе суток и часовому поясу каждого провайдера, в разрезе провайдера, валюты и игры:
Срок, в течение которого эти доказательства нужно хранить, — часть интеграции. Hub88 просит хранить каждый ID транзакции на обеих сторонах не менее четырёх месяцев для целей сверки, а операторский API Gamomat возвращает данные для сверки за диапазон дат или по одному раунду.
На этот вопрос есть ответы трёх видов, и продают они разное. Агрегаторы и вендоры платформ сдают оператору в аренду свой слой: один контракт, много провайдеров, их коммерческие условия. Платформы под ключ и white-label платформы включают слой в платформу, которой владеет вендор. Инженерные компании строят слой внутри бэкенда оператора, а договоры с провайдерами оператор подписывает сам.
С кем бы из них вы ни говорили, эти вопросы показывают, строила ли команда такое раньше:
Ответ, который по первым двум вопросам остаётся общим, означает, что граничные случаи нашлись бы в проде.
amBrain — компания по разработке ПО, специализирующаяся на торговых платформах, matching engine, системах real-time bidding и инженерии казино-платформ. amBrain делает софт с 2019 года.
Цифры, которые amBrain публикует в iGaming как измеренные, — 500+ интеграций сторонних провайдеров и 12 операторов в проде.
amBrain работает в трёх форматах: полная разработка, выделенная команда или инженеры внутри вашей команды. Клиент сохраняет полное владение продуктом и кодом, кроме переиспользуемых компонентов amBrain.
Эта статья объясняет, как работает слой агрегации; это не кейс, и клиентов она не называет.
Поэтому первое решение — не то, с какими провайдерами подписывать договоры. Это внутренний контракт, под который будет адаптирован каждый провайдер, и записан он вместе со своими случаями ошибок до того, как появится первый адаптер.
Принесите текущую архитектуру и тот сценарий отказа, который вас беспокоит, — разберём его вместе за полчаса.