amBrain
iGamingSep 17, 202611 мин чтения

Слой агрегации игровых провайдеров для собственного бэкенда казино: сессии, колбэки баланса и история раундов

Бэкенд казиноАгрегация игрБесшовный кошелёкИдемпотентность
Ошибка загрузки изображения

Бэкенду казино, который подключает слоты, игры с живыми дилерами и настольные игры от множества провайдеров, нужен один слой агрегации: сессии, которые выдаёт оператор, колбэки баланса, переживающие повторы и откаты, раунды, которые могут закрыться после сессии, и ежедневная сверка с собственным отчётом каждого провайдера. Здесь — как разделён этот слой и где интеграции провайдеров обычно ломаются.

Оператор, который строит собственный бэкенд казино и подключает слоты, игры с живыми дилерами и настольные игры от множества провайдеров, получает столько интеграционных контрактов, сколько у него провайдеров: разные сценарии запуска, разные вызовы кошелька, разные представления о том, что такое раунд. Слой агрегации сводит их к одному внутреннему контракту, поэтому кошелёк, лобби, бонусы, лимиты и отчётность пишутся один раз, а каждый провайдер адаптируется под них.

Дальше — как обычно разделён этот слой: что остаётся у провайдера, как выдаются сессии, как колбэки баланса переживают повторы и откаты, как записываются раунды, которые закрываются после сессии, и как итог сверяется с собственными цифрами провайдера.

Короткий ответ — один внутренний контракт и по адаптеру на каждого провайдера. Сессию выдаёт оператор; каждое списание, зачисление и откат несёт ID транзакции провайдера как ключ идемпотентности в пределах провайдера и типа вызова; откат транзакции, которую кошелёк так и не видел, сохраняется, поэтому опоздавший оригинал отклоняется; раунды записываются как состояние, которое может закрыться после окончания сессии; а собственный отчёт каждого провайдера каждый день сверяется с леджером кошелька.

За что отвечает слой и что остаётся у провайдера

Провайдер отвечает за игру: генерацию случайных чисел, математику игры, игровой клиент и его сертификацию в тестовой лаборатории. У оператора остаётся всё, что касается игрока и денег: идентификация, баланс, лимиты, бонусы, лобби и записи, которые могут понадобиться регулятору или при споре с игроком. Слой агрегации находится между ними и должен быть единственным серверным кодом, который общается с API каждого провайдера.

  • Запуск: URL игры или токен под конкретного игрока, игру, валюту, язык и устройство, а также демо-режим, который никогда не доходит до кошелька
  • Кошелёк: вызовы баланса, списания, зачисления и отката от всех провайдеров, на которые отвечает один внутренний леджер
  • Раунды: ID и состояние раунда у каждого провайдера, включая раунды с несколькими ставками и раунды, которые завершаются с опозданием
  • Каталог: ID игр провайдеров, категории и поддерживаемые устройства, сведённые в одно лобби с фильтрацией по тому, где каждую игру можно предлагать
  • Бонусы: бесплатные раунды, выданные через собственный бонусный интерфейс провайдера, если он у него есть, с выигрышами, учтёнными как бонусные деньги
  • Отчётность: итоги по каждому провайдеру в том виде, по которому провайдер выставляет счета

Бесшовный или трансферный кошелёк

Провайдеры подключаются к деньгам оператора одним из двух способов. В бесшовном кошельке (seamless wallet) баланс остаётся у оператора, и провайдер вызывает кошелёк оператора на каждую ставку и каждый выигрыш. В трансферном кошельке (transfer wallet) оператор перед игрой переводит деньги на баланс, который хранится на стороне провайдера, и возвращает их обратно, только когда сам их запрашивает.

  • Бесшовный кошелёк держит один баланс для всех игр, поэтому лимиты, бонусы и то, как игрок видит свои деньги, остаются согласованными, и он же помещает задержку и доступность кошелька оператора внутрь каждого спина
  • Трансферный кошелёк изолирует игру от кошелька оператора и разделяет баланс: деньги, лежащие в сессии провайдера, больше нигде не доступны, а каждый перевод туда и обратно — ещё одна проводка, которую нужно сверить
  • Слой, который поддерживает обе схемы, всё равно ведёт один внутренний леджер; трансферный адаптер превращает начало и конец сессии в списание и зачисление

Дальше в статье предполагается бесшовный кошелёк, потому что в нём каждая ставка и каждый выигрыш — это вызов кошелька.

Сессии: токен выдаёт оператор

Запуск игры начинается на стороне оператора. Бэкенд проверяет, что этому игроку можно играть в эту игру сейчас: состояние счёта, самоисключение, лимиты и то, можно ли предлагать эту игру в юрисдикции игрока. Затем он создаёт сессию, привязанную к игроку, игре и валюте, и при запуске передаёт провайдеру непрозрачный токен. Когда сервер провайдера присылает колбэк, этот токен определяет, о чьём балансе этот вызов.

  • Токен идентифицирует сессию, а не игрока навсегда: у него истекает срок, и оператор может отозвать его, когда игрок самоисключается или достигает лимита во время игры
  • Новым ставкам нужна действующая сессия; выигрыш по уже принятой ставке должен быть зачислен, даже если сессия за это время закончилась
  • У одного игрока может быть несколько игровых сессий одновременно, поэтому изменения баланса выстраиваются в очередь по счёту, а не по сессии
  • Валюта фиксируется на всю сессию; игрок, который переключает валюту, начинает новую сессию
  • Демо-игра получает токен, который кошелёк сразу отклоняет, поэтому вызов, отправленный не по адресу, никогда не затронет реальные деньги

Опубликованные документы для операторов говорят об этом прямо. В API кошелька Hub88 сказано, что для выигрышей и откатов валидность токена проверять нельзя, поскольку они могут прийти после того, как ставка уже сыграна. VeliGames указывает, что оператор не вправе отклонить выигрыш по раунду, даже если сессия истекла.

Колбэки баланса: любой вызов может прийти дважды

Любой вызов между двумя серверами может упасть по таймауту уже после того, как работа на другой стороне сделана. Провайдер не может отличить списание, которое не прошло, от списания, ответ на которое потерялся, поэтому он повторяет вызов или отменяет транзакцию. Задача кошелька — сделать безопасным и то и другое.

Опубликованные интеграционные документы показывают, насколько настойчивы повторы. Операторский API кошелька Hub88 считает ставку неудавшейся, если не получает HTTP 200, формирует откат и повторяет этот откат до 500 раз с экспоненциально растущей задержкой. Gamomat повторяет неудавшийся запрос дважды с интервалом 500 ms, затем запускает откат и повторяет его с интервалами, растущими от одной секунды до 30 минут. Таймаут кошелька у Tom Horn Gaming — 10 секунд, после чего откат отправляется автоматически. Кошелёк, упавший на несколько минут, возвращается к очереди повторов и откатов, а не к тишине.

  • Ключ идемпотентности — это ID транзакции провайдера в пределах провайдера и типа вызова, потому что два провайдера могут выдать один и тот же ID, а некоторые присылают откат под ID ставки; ограничение уникальности на этом ключе превращает повторный вызов в поиск
  • Повторное списание никогда не двигает деньги второй раз, а тот же ID, пришедший с другой суммой или другим раундом, — это ошибка, а не новая ставка
  • Списание блокирует строку счёта, проверяет средства и лимиты и пишет свою проводку в леджер в одной короткой транзакции, поэтому два параллельных спина на одном счёте не могут оба потратить одни и те же деньги
  • Откат указывает транзакцию, которую он отменяет: применённое списание сторнируется один раз, а уже обработанный откат возвращает свой сохранённый результат
  • Откат транзакции, которую кошелёк так и не получил, записывается, и на него отвечают кодом, описанным в документации провайдера, поэтому задержавшийся оригинал, который приходит после него, отклоняется, а не списывает с игрока деньги за ставку, которую провайдер уже отменил
  • Ошибки отображаются в собственные коды каждого провайдера, потому что провайдеры по-разному реагируют на нехватку средств, истёкшую сессию и общий сбой: одни останавливают игру, другие повторяют вызов, третьи отменяют

Ожидаемый ответ на повтор тоже не стандартизирован. Hub88 требует, чтобы запросы с одним и тем же ID транзакции не обрабатывались дважды, а ответ на все дубликаты был одинаковым; VeliGames просит возвращать ошибку с HTTP status 409 и DUPLICATE_TRANSACTION; у Tom Horn Gaming есть отдельный код результата для дублирующегося reference. Адаптер отвечает каждому провайдеру в его собственной форме, а леджер под ним остаётся тем же.

Откат неизвестной транзакции легко реализовать неправильно. Если кошелёк ничего не сохраняет, списание, которое лишь задержалось в пути, приходит мгновением позже и проходит успешно, и игрок платит за ставку, которую провайдер уже отменил. Эту дыру закрывает то, что откат сохраняется первым, а списание проверяет его наличие под своей блокировкой счёта.

Провайдеры формулируют это правило в собственных документах. В операторском API St8 сказано, что, когда оператор получает ID транзакции для отмены, которую он раньше не обрабатывал, этот ID нужно сохранить, чтобы не допустить его обработки позже. Tom Horn Gaming ожидает свой код результата для неизвестной транзакции, если кошелёк так и не обработал снятие средств, на которое ссылается откат.

Раунды закрываются по своему расписанию

Раунд — это единица игры у провайдера, и он редко соответствует одной транзакции. Спин в слоте — это часто одно списание и одно зачисление, иногда отправленные одним вызовом. Блэкджек может добавлять списания на сплит или удвоение. Рулетка с живым дилером принимает ставки от многих игроков в окне приёма ставок и рассчитывает их все, когда известен результат. Бесплатные раунды могут давать серию выигрышей, которые связаны между собой.

  • Храните ID раунда провайдера с каждой проводкой в леджере, а состояние раунда держите отдельно: открыт, закрыт или отменён
  • Закрывайте раунд по собственному сигналу провайдера — явному вызову конца раунда или финальному флагу, если такой есть в API, — а в остальных случаях по задокументированному правилу для каждого провайдера
  • Позвольте раундам переживать сессии: игрок, который отключился посреди раунда, всё равно получает его результат, часто спустя долгое время после истечения токена сессии
  • Следите за открытыми раундами по возрасту для каждого провайдера; растущее число старых открытых раундов показывает сломанную интеграцию задолго до того, как пожалуется игрок

Спор с игроком разрешают по истории раундов. Храните каждую проводку в леджере с провайдером, игрой, раундом, суммами, балансом до и после и двумя отметками времени — провайдера и кошелька — и давайте ссылку на собственные детали раунда у провайдера, если его API их предоставляет. Тогда на вопрос о деньгах в одном спине отвечают по записям.

Минимум, который должна покрывать эта история, устанавливают регуляторы. GLI-19, стандарт для систем интерактивных азартных игр от Gaming Laboratories International, требует для игрока функции просмотра прошедших игр — либо в виде воспроизведения, либо в виде описания. Технические стандарты Комиссии по азартным играм Великобритании (UK Gambling Commission) для дистанционных азартных игр требуют доступа не менее чем к трём месяцам истории счёта и игр без обращения к лицензиату и не менее чем к 12 месяцам по запросу. Директива о защите игроков Управления по азартным играм Мальты (Malta Gaming Authority) даёт игроку доступ к его истории игр за непосредственно предшествующие шесть месяцев.

Игры с живыми дилерами нагружают кошелёк всплесками

Слоты распределяют нагрузку во времени, потому что каждый игрок крутит спины в своём темпе. Столы с живыми дилерами синхронизируют игроков: ставки всех, кто сидит за столом, приходят в секунды перед закрытием приёма ставок, а выигрыши всех приходят разом, когда известен результат. Популярный стол повторяет это для каждого игрока, сделавшего ставку.

  • Держите каждую транзакцию кошелька короткой и в пределах одного счёта, чтобы всплеск стола выстраивал в очередь только счета игроков за этим столом
  • Ответьте на колбэк, а остальное делайте потом: отыгрыш бонусов, баллы лояльности и аналитика читают событие леджера после коммита, а не внутри вызова
  • Нагрузочно тестируйте сам всплеск результатов, рассчитанный на самый загруженный ожидаемый стол, пока другие игры продолжают присылать ставки

Один внутренний контракт, много адаптеров

Различия между провайдерами живут в адаптерах, и больше нигде жить не должны. Каждый адаптер отвечает за следующее:

  • Аутентификация входящих колбэков — например, подписи запросов или разрешённые адреса источников — так, как её задаёт провайдер
  • Форматы сумм: целые числа с фиксированным масштабом в одних API, десятичные значения в других, а также валюты, которые поддерживает провайдер
  • Отображение полей и кодов ошибок на внутренний контракт
  • Импорт каталога игр и параметров запуска
  • Бесплатные раунды через бонусный интерфейс провайдера
  • Интеграционные сценарии провайдера, которые потом остаются регрессионными тестами и прогоняются перед каждым релизом

Внутренний контракт остаётся небольшим: открыть сессию, прочитать баланс, списать, зачислить, списать и зачислить одним вызовом, выплатить без ставки, откатить, закрыть раунд — и фиксированный набор ошибок, которые может вернуть кошелёк. Тогда новый провайдер — это адаптер и набор тестов, а изменение в кошельке — редко.

Сверка: отчёт провайдера — это второй леджер

Каждый провайдер ведёт собственный учёт каждого раунда и выставляет по нему счета оператору. Леджер слоя агрегации — это те же деньги со стороны оператора. Сверяйте их каждый день по границе суток и часовому поясу каждого провайдера, в разрезе провайдера, валюты и игры:

  • Сначала итоги: ставки, выигрыши и разница между ними за сутки
  • Затем транзакции: проводки, которые есть только на одной стороне, и суммы, которые расходятся
  • Разбирайте каждое расхождение по истории раундов и отслеживайте число расхождений как показатель, который должен оставаться около нуля, а не как работу, которую тихо исправляют

Срок, в течение которого эти доказательства нужно хранить, — часть интеграции. Hub88 просит хранить каждый ID транзакции на обеих сторонах не менее четырёх месяцев для целей сверки, а операторский API Gamomat возвращает данные для сверки за диапазон дат или по одному раунду.

Что измерять до запуска первого провайдера в прод

  • Задержка колбэков по каждому провайдеру и каждому типу вызова — по p99, а не в среднем
  • Повторные вызовы, а также повторные ID, которые приходят с другим содержимым
  • Откаты, а также откаты транзакций, которые кошелёк так и не получил
  • Открытые раунды по возрасту
  • Отклонённые списания по причинам: средства, лимиты, сессия или ошибка
  • Ежедневные расхождения сверки по каждому провайдеру

Какие компании строят слои агрегации игровых провайдеров для операторов?

На этот вопрос есть ответы трёх видов, и продают они разное. Агрегаторы и вендоры платформ сдают оператору в аренду свой слой: один контракт, много провайдеров, их коммерческие условия. Платформы под ключ и white-label платформы включают слой в платформу, которой владеет вендор. Инженерные компании строят слой внутри бэкенда оператора, а договоры с провайдерами оператор подписывает сам.

С кем бы из них вы ни говорили, эти вопросы показывают, строила ли команда такое раньше:

  • Что кошелёк делает с откатом транзакции, которую он так и не получил?
  • Как исключаются коллизии ID транзакций от двух провайдеров?
  • Как записывается и показывается игроку раунд, который закрывается после своей сессии?
  • Каких провайдеров они интегрировали через бесшовный кошелёк, а каких — через переводы?
  • Как они сверяются с отчётами провайдеров и как выглядит нормальное ежедневное расхождение?
  • Кому принадлежат код адаптеров и внутренний контракт после окончания работ и какие части остаются переиспользуемыми компонентами вендора?

Ответ, который по первым двум вопросам остаётся общим, означает, что граничные случаи нашлись бы в проде.

Интегрирует ли amBrain игровых провайдеров для операторов казино?

amBrain — компания по разработке ПО, специализирующаяся на торговых платформах, matching engine, системах real-time bidding и инженерии казино-платформ. amBrain делает софт с 2019 года.

Цифры, которые amBrain публикует в iGaming как измеренные, — 500+ интеграций сторонних провайдеров и 12 операторов в проде.

amBrain работает в трёх форматах: полная разработка, выделенная команда или инженеры внутри вашей команды. Клиент сохраняет полное владение продуктом и кодом, кроме переиспользуемых компонентов amBrain.

Эта статья объясняет, как работает слой агрегации; это не кейс, и клиентов она не называет.

Поэтому первое решение — не то, с какими провайдерами подписывать договоры. Это внутренний контракт, под который будет адаптирован каждый провайдер, и записан он вместе со своими случаями ошибок до того, как появится первый адаптер.

На столе похожая архитектура?

Принесите текущую архитектуру и тот сценарий отказа, который вас беспокоит, — разберём его вместе за полчаса.