Розробка системи фрібетів крипто-казино
Фрібет (free bet) — це безплатна ставка, яку казино надає гравцю. На відміну від депозитного бонусу, фрібет не потребує внесення коштів. Користувач отримує можливість грати безплатно, але при виграшу отримує лише чистий прибуток (net winnings), а не повну суму ставки + прибуток.
Механіка фрібета
Стандартна механіка:
- Гравець отримує фрібет на $10
- Ставить фрібет на подію з коефіцієнтом 2.0
- При виграшу отримує $10 (прибуток), а не $20 (повернення ставки + прибуток)
- При програшу — нічого не втрачає (ставка була безплатною)
Free Spin (слоти) — аналог фрібета для слот-машин. Певна кількість обертань без використання реального балансу.
Модель даних
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] = [] # game IDs
# Для sports bets
min_odds: Optional[Decimal] # мінімальний коефіцієнт
eligible_markets: list[str] = [] # типи ринків
# Результати
bet_id: Optional[str] # ID використаної ставки
winnings: Decimal = Decimal(0) # лише чистий прибуток
# Wagering на виграш
wagering_required: bool = True
wagering_multiplier: int = 1 # 1x для спорту, до 30x для казино
expires_at: datetime
issued_at: datetime
source: str # 'WELCOME', 'PROMO', 'REWARD', 'REFERRAL'
Застосування фрібета до ставки
class FreeBetService:
async def apply_free_bet(
self,
user_id: str,
free_bet_id: str,
bet_params: BetRequest,
) -> BetResult:
free_bet = await self.freebet_repo.get(free_bet_id)
# Валідація
if free_bet.user_id != user_id:
raise AuthorizationError()
if free_bet.status != "AVAILABLE":
raise InvalidStatusError(f"Free bet status: {free_bet.status}")
if datetime.utcnow() > free_bet.expires_at:
raise ExpiredError()
# Перевіряємо eligibility
if free_bet.min_odds and bet_params.odds < free_bet.min_odds:
raise ValidationError(f"Мінімальні коефіцієнти: {free_bet.min_odds}")
# Помічаємо як використаний (атомарно)
async with self.db.transaction():
updated = await self.freebet_repo.claim(free_bet_id, bet_params.bet_id)
if not updated:
raise ConflictError("Фрібет вже використаний")
# Створюємо ставку без списання з реального балансу
bet = await self.bet_service.place_bet(
user_id=user_id,
amount=free_bet.amount,
is_free_bet=True,
free_bet_id=free_bet_id,
**bet_params.dict(),
)
return bet
async def settle_free_bet(self, bet: Bet, outcome: str):
"""Розрахунок виграшу по фрібету"""
free_bet = await self.freebet_repo.get(bet.free_bet_id)
if outcome == "WIN":
# Виграш = прибуток (без повернення суми ставки)
winnings = bet.potential_profit # (bet.amount * odds) - bet.amount
# Застосовуємо wagering якщо потрібно
if free_bet.wagering_required:
# Зачислюємо як заблоковані кошти з wagering requirement
await self.bonus_service.create_winnings_bonus(
user_id=free_bet.user_id,
amount=winnings,
wagering_multiplier=free_bet.wagering_multiplier,
reference=f"FREEBET_WIN:{free_bet.id}",
)
else:
# Пряме зачислення
await self.balance_service.credit(
user_id=free_bet.user_id,
amount=winnings,
reference=f"FREEBET_WIN:{free_bet.id}",
)
await self.freebet_repo.update_status(free_bet.id, "WON", winnings=winnings)
else:
await self.freebet_repo.update_status(free_bet.id, "LOST")
Механіка Free Spins
class FreeSpinService:
async def use_free_spin(self, user_id: str, free_bet_id: str, game_id: str) -> SpinResult:
free_bet = await self.freebet_repo.get(free_bet_id)
if game_id not in free_bet.eligible_games:
raise ValidationError("Гра не підходить для цього фрібета")
if free_bet.spins_used >= free_bet.spin_count:
raise ValidationError("Усі обертання вже використані")
# Атомарно збільшуємо лічильник використаних обертань
remaining = await self.freebet_repo.consume_spin(free_bet_id)
if remaining < 0:
raise ConflictError("Race condition: обертання вже використано")
# Запускаємо ігровий движок без реальних грошей
result = await self.game_engine.spin(
game_id=game_id,
bet_amount=free_bet.amount / free_bet.spin_count,
is_free=True,
)
if result.winnings > 0:
await self.credit_free_spin_winnings(user_id, free_bet, result.winnings)
# Якщо усі обертання використані
if remaining == 0:
await self.freebet_repo.update_status(free_bet_id, "USED")
return result
Маркетингова аналітика
Фрібети — маркетинговий інструмент, тому потрібна аналітика ефективності:
- Conversion rate: % користувачів, які отримали фрібет та зробили реальний депозит
- LTV uplift: порівняння LTV користувачів з фрібетом vs без
- Cost per acquisition: вартість привернення через фрібет vs інші канали
- Abuse rate: % користувачів, які зловживають фрібетами без наміру депонувати







