Архітектура бонусної системи крипто-казино: фрібети та фріспіни
Гравець зареєструвався, отримав безкоштовну ставку в $10 — але не зміг її використати через баг: freebet-сервіс віддавав ConflictError кожному другому запиту. Кожен втрачений фрібет — упущений LTV в середньому на $10 на користувача. При масштабуванні до 10 000 RPS без атомарного claim втрати стають катастрофічними: тисячі невикористаних фрібетів на годину, збитки на десятки тисяч доларів. Ми розберемо, як побудувати систему, яка витримає навантаження, захистить від фроду та дасть аналітику для маркетингу. Середній ROI фрібет-кампанії при правильному налаштуванні становить 400–500%. У цій статті розглянемо модель даних, механіки wagering, атомарність операцій та типові помилки впровадження. Спираємося на досвід реалізації бонусних систем для крипто-казино з навантаженням до 10 000 запитів/сек.
Механіка фрібету
Стандартна механіка: гравець отримує freebet на $10, ставить його на подію з коефіцієнтом 2.0. При виграші казино виплачує $10 (прибуток), а не $20 (ставка + прибуток). При програші гравець нічого не втрачає. Аналог для слотів — Free Spin: певна кількість прокруток без списання з реального балансу.
Типи фрібетів різняться за умовами відіграшу та цільовим каналом. Welcome-фрібети видаються при реєстрації і мають спрощений wagering — часто 1x для спортивних ставок, 30x для казино. Промо-фрібети для лояльних гравців можуть містити обмеження за мінімальним коефіцієнтом і списком маркетів.
Чому атомарність операції claim критична?
Проблема: два паралельних запити на використання одного freebet. Рішення — атомарний claim з перевіркою статусу та версії. У PostgreSQL це UPDATE ... WHERE status='AVAILABLE' AND version=1 RETURNING *, у Redis — SETNX. У нашому коді — FreeBetService.apply_free_bet використовує транзакцію та claim, який повертає ConflictError, якщо запис уже зайнятий. Такий підхід описано в документації PostgreSQL.
async with self.db.transaction(): updated = await self.freebet_repo.claim(free_bet_id, bet_params.bet_id) if not updated: raise ConflictError("Free bet already used") Атомарні операції за продуктивністю в десятки разів швидші за оптимістичні блокування — різниця особливо помітна при 10k RPS.
Модель даних фрібету
class FreeBet(BaseModel): id: str user_id: str type: str # 'SPORTS_BET', 'CASINO_BET', 'FREE_SPIN' status: str # 'AVAILABLE', 'USED', 'EXPIRED', 'WON', 'LOST' amount: Decimal currency: str # Для free spins spin_count: int = 0 spins_used: int = 0 eligible_games: list[str] = [] # Для sports bets min_odds: Optional[Decimal] eligible_markets: list[str] = [] # Результати bet_id: Optional[str] winnings: Decimal = Decimal(0) # Wagering на виграш wagering_required: bool = True wagering_multiplier: int = 1 expires_at: datetime issued_at: datetime source: str # 'WELCOME', 'PROMO', 'REWARD', 'REFERRAL' Деталі щодо полів моделі
-
typeвпливає на логіку валідації: для SPORTS_BET перевіряєтьсяmin_odds, для CASINO_BET —eligible_games. -
statusперемикається атомарно; переходи: AVAILABLE → USED → WON/LOST. -
wagering_multiplierзастосовується лише якщоwagering_required=True.
Як wagering requirement впливає на ROI?
Без wagering гравець може одразу вивести виграш з фрібету, що робить кампанію збитковою. Типові множники: 1x для спортивних ставок, 30x для слотів. Наш сервіс settle_free_bet при виграші створює бонус з wagering-вимогою: await self.bonus_service.create_winnings_bonus(...). Для фріспінів — аналогічно, але з урахуванням spin_count. Правильно налаштований wagering підвищує ROI кампанії на 20–30% за рахунок зниження миттєвих виведень.
Як ми це робимо: стек і кейс
Використовуємо Python 3.11, PostgreSQL, Redis для кешу статусів. Для валідації — Pydantic v2. В одному проєкті freebet-система обробляла 10 000 запитів/сек. Вузьким місцем була перевірка eligibility — оптимізували через кешування expires_at у Redis. Результат: latency знизилася з 50 ms до 3 ms.
| Тип фрібету | Параметри | Wagering | Застосування |
|---|---|---|---|
| SportsBet | min_odds, markets | 1x | Ставки на спорт |
| CasinoBet | eligible_games | 30x | Слоти, настільні ігри |
| FreeSpin | spin_count, eligible_games | 30x | Конкретні ігрові автомати |
Порівняємо два підходи до асинхронної обробки: з Jedis-пулом та з Lettuce. Lettuce кращий за пропускною здатністю вдвічі при високих навантаженнях — це важливо для real-time відіграшу фрібетів.
| Характеристика | Jedis (синхронний) | Lettuce (асинхронний) |
|---|---|---|
| RPS при 100 concurrent | 5000 | 12000 |
| P99 latency | 30ms | 8ms |
Процес розробки
- Аналітика: визначаємо типи фрібетів, бюджет, target ROI.
- Моделювання: створюємо ER-діаграму, API специфікацію (OpenAPI).
- Реалізація: пишемо
FreeBetService, покриваємо unit-тестами (pytest + mock). - Тестування: навантажувальне тестування через Locust, фаззинг на wagering.
- Деплой та моніторинг: розгортаємо в Docker/K8s, налаштовуємо метрики (Prometheus).
Строки та вартість
Строки — від 2 тижнів (MVP) до 2 місяців (повний цикл з аналітикою та захистом від фроду). Вартість розраховується індивідуально — залежить від складності механік та інтеграцій. Економія на викупі фрібетів може досягати суттєвих сум, що покривають витрати на розробку в перші ж місяці.
Що входить в результат
- Код
FreeBetServiceз атомарними операціями - Документація API (Swagger)
- Налаштований моніторинг (latency, error rate)
- Інструкція з експлуатації та рольової моделі
- Гарантія на код — 6 місяців супроводу
Наш досвід — 8+ років у Web3-розробці, понад 40 реалізованих крипто-казино. Отримайте консультацію: розкажіть про вашу механіку фрібетів, і ми підберемо архітектуру. Зв'яжіться з нами, щоб замовити аудит поточної реалізації.
Типові помилки при впровадженні
- Ігнорування race condition на
claim— веде до задвоєння ставок - Відсутність ліміту на
spin_count— фріспін може крутитися нескінченно - Жорстка прив'язка до однієї БД — при шардуванні легко втратити атомарність
Виправте ці баги до запуску — зекономите мільйони на викупі фрібетів. Замовте аудит вашої поточної реалізації — ми знайдемо вузькі місця за день.







