# Lottery Application - Complete Overview

## Table of Contents
1. [Application Overview](#application-overview)
2. [Architecture](#architecture)
3. [Core Features](#core-features)
4. [Game Mechanics](#game-mechanics)
5. [Referral System](#referral-system)
6. [Tasks System](#tasks-system)
7. [Payment System](#payment-system)
8. [User Management](#user-management)
9. [Feature Switches](#feature-switches)
10. [Lottery Bot System](#lottery-bot-system)
11. [Database Schema](#database-schema)
12. [API Endpoints](#api-endpoints)

---

## Application Overview

The Lottery Application is a Telegram Mini App that provides a real-time lottery game experience. Users can participate in lottery rounds across multiple game rooms, complete tasks to earn rewards, refer friends to earn commissions, and manage their balance through Telegram Stars payments.

**Technology Stack:**
- **Backend:** Java 17, Spring Boot 3.2.0, MySQL, WebSocket (STOMP)
- **Frontend:** React, Vite, WebSocket client
- **Authentication:** Telegram Mini App initData validation, JWT for admin panel
- **Payment:** Crypto deposits (external API), optional Telegram Stars (legacy); feature switch for deposits
- **Payout:** Crypto withdrawals (external API), STARS/GIFT (Telegram); feature switch for withdrawals
- **Infrastructure:** Docker, Nginx, VPS deployment

**Domain:** `win-spin.live`

---

## Architecture

### Backend Architecture
- **Spring Boot REST API** with WebSocket support for real-time game updates
- **Dual Authentication System:**
  - Telegram initData validation for app users (via `AuthInterceptor`)
  - JWT-based authentication for admin panel (via Spring Security)
- **Session Management:** Bearer token-based sessions stored in `sessions` table
- **Real-time Communication:** WebSocket (STOMP) for game state broadcasts

### Frontend Architecture
- **Main App (lottery-fe):** React SPA (Vite) deployed at `win-spin.live`. Path `/` shows a home/landing screen; `/auth` (or other paths) loads the authenticated app. Screens: Main (game), Game History, FAQ, Support, Support Chat, Referral, Transaction History, Store, Payment Options/Confirmation/Error, Tasks, Payout, Stars Payout Confirmation, Daily Bonus. Balance and user state from `GET /api/users/current`; game join and room state via WebSocket. i18n and Telegram WebApp integration.
- **Admin Panel (lottery-admin):** React SPA (Vite, React Router, TanStack Query) at `win-spin.live/{secret-path}/`. Routes: Login, Dashboard (stats), Rooms (list + RoomDetail), Feature Switchers, Configurations, Bots, Users (list + UserDetail), Payments, Payouts, Support Tickets (list + TicketDetail, Quick Answers), Masters, Analytics (User, Game, Financial, Referral). Role-based visibility: ROLE_ADMIN (all), ROLE_PAYOUT_SUPPORT (payouts), ROLE_TICKETS_SUPPORT (tickets + quick answers).
- **WebSocket Client (lottery-fe):** Real-time game state via STOMP; subscribe to `/topic/room/{roomNumber}/state`, send join via `/app/game/join`.

### Database
- **MySQL** with Flyway migrations
- **Normalized Schema:** User data split across multiple tables (A, B, D)
- **Performance:** Indexed for high concurrency (1000+ concurrent users)

---

## Core Features

### 1. Real-Time Lottery Game
- **3 Game Rooms** with different bet limits:
  - Room 1: 1-50 tickets
  - Room 2: 10-500 tickets
  - Room 3: 100-5000 tickets
- **Game Phases:**
  - `WAITING`: Waiting for at least 2 players
  - `COUNTDOWN`: 5-second countdown after 2nd player joins
  - `SPINNING`: 5-second spinning animation
  - `RESOLUTION`: Winner announcement
- **Winner Selection:** Random selection based on ticket distribution (weighted lottery)
- **House Commission:** 20% of total pool (80% distributed to winner)

### 2. User Balance System
- **Balance A:** Main balance (used for betting, can be withdrawn)
- **Balance B:** Secondary balance (bonuses, rewards)
- **Currency:** Internal "tickets" system (1 ticket = 1,000,000 in bigint format)
- **Deposit Conversion:** 1 USD = 1,000 tickets (crypto deposits); legacy Telegram Stars conversion no longer used for new deposits
- **Withdrawal Limit:** Only winnings since last deposit can be withdrawn; tracked in `total_win_after_deposit` (reset on deposit, reduced when payout created)

### 3. Telegram Integration
- **Telegram Mini App:** Full integration with Telegram WebView
- **Telegram Stars Payments:** Deposit via Telegram Stars
- **Telegram Bot:** Handles webhooks, channel membership verification
- **Avatar Management:** Downloads and stores user avatars from Telegram

### 4. Multi-Language Support
- **i18n:** Full internationalization support
- **Languages:** Multiple language codes stored per user
- **Localization Service:** Dynamic message translation

### 5. Support System
- **Support Tickets:** Users can create tickets
- **Support Messages:** Threaded conversations per ticket
- **Status Tracking:** OPENED/CLOSED status

### 6. Admin Panel
- **Secret Path Access:** Accessible via `win-spin.live/{secret-path}/`
- **Role-Based Access:** `ROLE_ADMIN` (full access), `ROLE_PAYOUT_SUPPORT` (payouts only), `ROLE_TICKETS_SUPPORT` (support tickets and quick answers). Admins table stores `role` per account.
- **Dashboard:** Stats overview (total/new users, active players 24h/7d/30d, revenue and payouts in Stars and crypto USD, net revenue, rounds count, open support tickets). Served by `GET /api/admin/dashboard/stats`.
- **Rooms:** List all game rooms; room detail by room number (participants, phase, totals); repair round for stuck rooms (`POST /api/admin/rooms/{roomNumber}/repair`).
- **Configurations:** App configuration view for admins (`GET /api/admin/configurations`).
- **Bots:** Lottery bot configs management—create/update/delete bot configs (linked to real users), shuffle bot order. Scheduler (when `lottery_bot_scheduler_enabled` is on) auto-joins bots into joinable rounds per time window and persona.
- **User Management:** Paginated users list; user detail (profile, balance, transactions, game rounds, tasks); ban user; manual balance adjustment.
- **Feature Switchers:** Runtime toggles for remote_bet_enabled, payment_enabled, payout_enabled, lottery_bot_scheduler_enabled.
- **Payments:** List payments with filters (userId, status); crypto and legacy records.
- **Payouts:** List payouts with filters (userId, status, type); manual complete/cancel for PROCESSING or WAITING (CRYPTO status synced by cron).
- **Support Tickets:** List tickets, ticket detail with messages; close ticket. **Quick Answers:** Per-admin templates for fast replies (CRUD); access for ROLE_ADMIN and ROLE_TICKETS_SUPPORT.
- **Masters:** Referral masters view.
- **Analytics:** Revenue time series (crypto USD) and activity metrics (new users, rounds, revenue by period) for charts; range 7d/30d/90d/1y/all.
- **JWT Authentication:** Separate admin authentication system.
- **Admin Table:** Separate `admins` table; optional `user_id` linking to `db_users_a` for support messages.

---

## Game Mechanics

### Game Flow

1. **User Joins Room:**
   - User selects a room (1, 2, or 3)
   - User places a bet (within room limits)
   - Balance A is deducted immediately
   - User becomes a participant

2. **Round Start:**
   - When 2nd player joins, countdown starts (5 seconds)
   - During countdown, more players can join
   - After countdown, spinning phase begins (5 seconds)

3. **Winner Selection:**
   - Winner selected randomly based on ticket distribution
   - Probability = (user_tickets / total_tickets)
   - House takes 20% commission
   - Winner receives 80% of total pool

4. **Round Resolution:**
   - Winner's balance credited
   - Referral commissions processed
   - Round marked as completed
   - New round starts automatically

### Bet Limits Per Room

| Room | Min Bet | Max Bet | Max Bet Per User |
|------|---------|---------|------------------|
| 1    | 1       | 50      | 50               |
| 2    | 10      | 500     | 500              |
| 3    | 100     | 5000    | 5000             |

**Note:** All amounts stored in bigint format (1 ticket = 1,000,000)

### Game Phases

- **WAITING:** Room waiting for players (minimum 2 required)
- **COUNTDOWN:** 5-second countdown after 2nd player joins
- **SPINNING:** 5-second spinning animation (no new bets allowed)
- **RESOLUTION:** Winner announced, payouts processed

### Real-Time Updates

- **WebSocket Broadcasts:** All room state changes broadcast to connected clients
- **State Synchronization:** Clients receive full room state on connection
- **Participant Updates:** Real-time participant list and bet totals

---

## Referral System

### 3-Tier Referral Structure

The referral system tracks up to 5 levels of referrers, but commissions are paid to the first 3 levels:

**Referral Chain:**
- `referer_id_1`: Direct referrer (Level 1)
- `referer_id_2`: Referrer's referrer (Level 2)
- `referer_id_3`: Referrer's referrer's referrer (Level 3)
- `referer_id_4`: Level 4 (tracked but no commission)
- `referer_id_5`: Level 5 (tracked but no commission)

### Commission Rates

#### When Referral Wins:
- **All Levels:** 1% of referral's net profit
- **Net Profit Calculation:** (total_pool - house_commission) - referral_bet

#### When Referral Loses:
- **Level 1:** 4% of referral's bet
- **Level 2:** 2% of referral's bet
- **Level 3:** 1% of referral's bet

### Referral Statistics

Each user tracks:
- **referals_1 to referals_5:** Count of direct referrals at each level
- **from_referals_1 to from_referals_5:** Total commissions earned from each level
- **to_referer_1 to to_referer_5:** Total commissions paid to referrers

### Third Bet Bonus

- Special bonus given to referrers when their referral makes their 3rd bet
- Uses `rounds_played` column (not transaction count)
- Prevents issues with transaction cleanup cron jobs

---

## Tasks System

### Task Types

1. **Referral Tasks (`referral`):**
   - Invite 1, 3, 7, 15, 30, 50, 100 friends
   - Rewards: 2, 5, 15, 25, 40, 60, 150 Stars respectively
   - One-time rewards (can only claim each tier once)

2. **Follow Task (`follow`):**
   - Follow the news channel
   - Reward: 5 Stars
   - Verified via Telegram Bot API

3. **Deposit Task (`other`):**
   - Top up balance: $5
   - Reward: 100 Stars
   - Based on `deposit_total` field

4. **Daily Bonus (`daily`):**
   - Claim 1 free ticket every 24 hours
   - Reward: 1 ticket
   - Tracked in `user_daily_bonus_claims` table

### Task Completion Logic

- **Referral Tasks:** Counts `referals_1` from `db_users_d`
- **Follow Task:** Verified via Telegram Bot API channel membership check
- **Deposit Task:** Checks `deposit_total >= requirement`
- **Daily Bonus:** Checks if last claim was > 24 hours ago

### Task Rewards

- Rewards credited to **Balance A** (main balance)
- Transaction recorded in `transactions` table with type `TASK_BONUS`
- Task marked as claimed in `user_task_claims` table

---

## Payment System

### Crypto Deposits (Primary)

Deposits are handled via an external crypto API (e.g. spin-passim.tech). The backend syncs deposit methods periodically and provides deposit addresses on demand; balance is credited when the 3rd party sends a completion webhook.

**Deposit Flow:**
1. User enters USD amount on Store screen (min 2, max 10,000 USD; max 2 decimal places).
2. User selects a crypto method on Payment Options (methods from `crypto_deposit_methods`, synced every 10 min from external API).
3. Backend calls external API `POST api/v1/deposit-address` (pid, amountUsd, userData); no payment record is created yet.
4. User is shown wallet address and exact amount in coins to send (Payment Confirmation screen).
5. When the 3rd party detects the deposit, it calls `POST /api/deposit_webhook/{token}` with `user_id` and `usd_amount`.
6. Backend creates a COMPLETED payment record, credits balance (1 USD = 1,000 tickets in DB units: 1 ticket = 1,000,000), updates deposit_total/deposit_count, resets total_win_after_deposit, and creates a DEPOSIT transaction.

**Payment Record (crypto):**
- `usd_amount`: decimal (e.g. 1.25 USD)
- `tickets_amount`: bigint (1 USD = 1,000 tickets → 1,000,000,000 in DB)
- `stars_amount`: 0 for crypto
- `status`: PENDING (if invoice created but not used), COMPLETED, FAILED, CANCELLED

**Feature Switch:** When `payment_enabled` is off, deposit-related endpoints return 503 and the Store shows "deposits temporarily unavailable".

### Telegram Stars (Legacy)

- Creating new payments via Stars (invoice link) is no longer supported; the app uses crypto-only for new deposits.
- Telegram payment webhook is still processed for backward compatibility if a legacy payment is completed.
- Legacy conversion: Stars × 0.9 × 1,000,000 for tickets (no longer used for new flows).

### Payout System

**Payout Types:**
- **CRYPTO:** Primary. User selects withdrawal method (e.g. TON, LTC, TRX), enters wallet and amount in tickets. Backend calls external API `POST api/v1/withdrawal`; on success creates payout with `payment_id`, deducts balance, and a cron job syncs status from `GET api/v1/withdrawals-info/{payment_id}`.
- **STARS:** Convert tickets to Telegram Stars (fixed amounts; admin may process manually).
- **GIFT:** Convert to Telegram gifts (HEART, BEAR, etc.); admin may process manually.

**Crypto Payout Flow:**
1. User opens Payout screen; backend returns withdrawal methods from `crypto_withdrawal_methods` (synced every 30 min from external API).
2. User selects method, enters wallet and amount (tickets). Frontend may call `GET withdrawal-method-details?pid=` for rate_usd and fee to show "You will receive".
3. User submits; backend acquires per-user lock, calls external API POST withdrawal, then creates payout (type CRYPTO) with payment_id, crypto_name, amount_coins, commission_coins, amount_to_send, wallet, status PROCESSING, and deducts balance.
4. Cron job (every minute) fetches PROCESSING/WAITING CRYPTO payouts with payment_id, calls withdrawals-info API, and updates status: -1→PROCESSING, 0→WAITING, 1→COMPLETED (marks completed, updates withdraw_total/count), 2→CANCELLED (refunds balance, creates cancellation transaction).

**Payout Statuses:**
- `PROCESSING`: Created or being processed by external provider
- `WAITING`: Awaiting confirmation (from API status 0)
- `COMPLETED`: Resolved; user withdraw_total/count updated
- `CANCELLED`: Refunded to balance_a

**Withdrawal Rules:**
- At least one deposit required before any withdrawal.
- Withdrawal amount cannot exceed `total_win_after_deposit` (winnings since last deposit).
- Crypto withdrawal amount must have at most 2 decimal places (in ticket units).
- One withdrawal at a time per user (in-memory lock).

**Feature Switch:** When `payout_enabled` is off, withdrawal-related endpoints return 503 and the Payout screen shows "withdrawals temporarily unavailable".

---

## User Management

### User Data Structure

User data is split across 3 tables for normalization:

**`db_users_a` (User Authentication & Profile):**
- Basic user info (Telegram ID, name, language, country)
- Registration and login timestamps
- Ban status
- Avatar URL

**`db_users_b` (User Balance & Financial):**
- Balance A (main balance)
- Balance B (bonus balance)
- Deposit/withdraw totals and counts
- Rounds played counter
- **total_win_after_deposit:** Total winnings since last deposit (bigint). Reset to 0 on each deposit; incremented when user wins a round; reduced when user creates a payout. Used to enforce withdrawal limit (only this amount can be withdrawn).

**`db_users_d` (User Referral Data):**
- Referral chain (5 levels)
- Referral statistics
- Commission tracking

### User Sessions

- **Session Management:** Bearer token-based
- **Multi-Device Support:** Up to 5 active sessions per user
- **Session Expiration:** Configurable TTL
- **Automatic Cleanup:** Cron job removes expired sessions

### User Authentication

- **Telegram initData Validation:** Validates Telegram Mini App authentication
- **Session Creation:** Creates session token on first login
- **Session Validation:** `AuthInterceptor` validates all API requests

- **Current User API:** Returns `paymentEnabled` and `payoutEnabled` from feature switches (used by frontend to show/hide or disable Store and Payout).

---

## Feature Switches

Runtime toggles stored in `feature_switches` table. Changes take effect immediately without restart. Configurable from the admin panel.

| Key | Description |
|-----|-------------|
| `remote_bet_enabled` | When on, 3rd party can register users to rounds via GET `/api/remotebet/{token}?user_id=&room=&amount=`. When off, endpoint returns 503. |
| `payment_enabled` | When on, deposits (Store, Payment Options, deposit-address, create invoice) are allowed. When off, related API endpoints return 503 and Store shows "deposits temporarily unavailable". Default: 1. |
| `payout_enabled` | When on, withdrawals (Payout screen, withdrawal-methods, withdrawal-method-details, crypto-withdrawal) are allowed. When off, related endpoints return 503 and Payout shows "withdrawals temporarily unavailable". Default: 1. |
| `lottery_bot_scheduler_enabled` | When on, the lottery bot scheduler auto-joins configured bots (from `lottery_bot_configs`) into joinable rounds within their time windows and persona. When off, scheduler skips registration. Default: 1. |

---

## Lottery Bot System

Bots are **real users** (from `db_users_a`/`db_users_b`) with a config row in `lottery_bot_configs`. The scheduler runs periodically and, when `lottery_bot_scheduler_enabled` is on, registers these users into active rounds via the same join flow as real players (balance deducted, etc.).

### Bot Config (per user)

- **Rooms:** Flags `room_1`, `room_2`, `room_3` (which rooms the bot can play).
- **Time window (UTC):** `time_utc_start`, `time_utc_end`—bot only plays within this window.
- **Bet range:** `bet_min`, `bet_max` in bigint (1 ticket = 1,000,000).
- **Persona:** `conservative`, `aggressive`, or `balanced`—used by the bet decision service to choose bet size and timing.
- **Active:** Toggle to enable/disable this bot without deleting the config.

One config per user; `user_id` is unique. Admin can create/update/delete configs and shuffle bot order via the Bots page.

### Scheduler

- When the feature switch is on, a scheduled job evaluates active configs, checks time windows and room state, and places bets for bots using the bet decision service (persona-based logic).
- Bots participate in the same game flow as human players (rounds, referral logic, etc.).

---

## Database Schema

### Core Tables

#### `sessions`
User session management for Bearer token authentication.

| Column | Type | Description |
|--------|------|-------------|
| `id` | BIGINT | Primary key, auto-increment |
| `session_id_hash` | VARCHAR(255) | Hashed session ID (unique) |
| `user_id` | INT | Foreign key to `db_users_a.id` |
| `created_at` | TIMESTAMP | Session creation time |
| `expires_at` | TIMESTAMP | Session expiration time |

**Indexes:**
- `idx_session_hash` on `session_id_hash`
- `idx_expires_at` on `expires_at`
- `idx_user_id` on `user_id`
- `idx_user_created` on `(user_id, created_at)`

#### `db_users_a`
User authentication and profile information.

| Column | Type | Description |
|--------|------|-------------|
| `id` | INT | Primary key, auto-increment |
| `screen_name` | VARCHAR(75) | User's screen name |
| `telegram_id` | BIGINT | Telegram user ID (unique) |
| `telegram_name` | VARCHAR(33) | Telegram username |
| `is_premium` | INT | Telegram Premium status (0/1) |
| `language_code` | VARCHAR(2) | User's language code |
| `country_code` | VARCHAR(2) | User's country code |
| `device_code` | VARCHAR(5) | Device type code |
| `ip` | VARBINARY(16) | User's IP address (IPv4/IPv6) |
| `date_reg` | INT | Registration timestamp (Unix) |
| `date_login` | INT | Last login timestamp (Unix) |
| `banned` | INT | Ban status (0/1) |
| `avatar_url` | VARCHAR(500) | Avatar URL with cache busting |
| `last_telegram_file_id` | VARCHAR(255) | Telegram file ID for avatar |

**Indexes:**
- Primary key on `id`
- Unique key on `telegram_id`
- Index on `telegram_name`
- Index on `ip`

#### `db_users_b`
User balance and financial information.

| Column | Type | Description |
|--------|------|-------------|
| `id` | INT | Primary key (matches `db_users_a.id`) |
| `balance_a` | BIGINT UNSIGNED | Main balance (tickets in bigint format) |
| `balance_b` | BIGINT UNSIGNED | Bonus balance (tickets in bigint format) |
| `deposit_total` | BIGINT | Total deposits (in bigint format) |
| `deposit_count` | INT | Number of deposits |
| `withdraw_total` | BIGINT | Total withdrawals (in bigint format) |
| `withdraw_count` | INT | Number of withdrawals |
| `rounds_played` | INT | Number of rounds user participated in |
| `total_win_after_deposit` | BIGINT | Winnings since last deposit (1 ticket = 1,000,000). Reset on deposit; increased on win; decreased when payout created. Withdrawal limit. |

**Indexes:**
- Primary key on `id`

**Note:** `balance_a` and `balance_b` store values in bigint format where 1 ticket = 1,000,000

#### `db_users_d`
User referral data and statistics.

| Column | Type | Description |
|--------|------|-------------|
| `id` | INT | Primary key (matches `db_users_a.id`) |
| `referer_id_1` | INT | Level 1 referrer ID |
| `referer_id_2` | INT | Level 2 referrer ID |
| `referer_id_3` | INT | Level 3 referrer ID |
| `referer_id_4` | INT | Level 4 referrer ID |
| `referer_id_5` | INT | Level 5 referrer ID |
| `master_id` | INT | Master referrer ID |
| `referals_1` | INT | Count of level 1 referrals |
| `referals_2` | INT | Count of level 2 referrals |
| `referals_3` | INT | Count of level 3 referrals |
| `referals_4` | INT | Count of level 4 referrals |
| `referals_5` | INT | Count of level 5 referrals |
| `from_referals_1` | BIGINT | Commissions earned from level 1 |
| `from_referals_2` | BIGINT | Commissions earned from level 2 |
| `from_referals_3` | BIGINT | Commissions earned from level 3 |
| `from_referals_4` | BIGINT | Commissions earned from level 4 |
| `from_referals_5` | BIGINT | Commissions earned from level 5 |
| `to_referer_1` | BIGINT | Commissions paid to level 1 referrer |
| `to_referer_2` | BIGINT | Commissions paid to level 2 referrer |
| `to_referer_3` | BIGINT | Commissions paid to level 3 referrer |
| `to_referer_4` | BIGINT | Commissions paid to level 4 referrer |
| `to_referer_5` | BIGINT | Commissions paid to level 5 referrer |

**Indexes:**
- Primary key on `id`
- Indexes on `referer_id_1` through `referer_id_5`
- Index on `master_id`

### Game Tables

#### `game_rooms`
Active game rooms (Room 1, 2, 3).

| Column | Type | Description |
|--------|------|-------------|
| `id` | INT | Primary key, auto-increment |
| `room_number` | INT | Room number (1, 2, or 3), unique |
| `current_phase` | VARCHAR(20) | Current game phase (WAITING, COUNTDOWN, SPINNING, RESOLUTION) |
| `countdown_end_at` | TIMESTAMP | Countdown end time (NULL if not counting down) |
| `total_bet` | BIGINT UNSIGNED | Total tickets bet in current round |
| `registered_players` | INT | Number of players in current round |
| `created_at` | TIMESTAMP | Room creation time |
| `updated_at` | TIMESTAMP | Last update time |

**Indexes:**
- Primary key on `id`
- Unique key on `room_number`
- Index on `current_phase`
- Index on `countdown_end_at`

#### `game_rounds`
Completed game rounds history.

| Column | Type | Description |
|--------|------|-------------|
| `id` | BIGINT | Primary key, auto-increment |
| `room_id` | INT | Foreign key to `game_rooms.id` |
| `phase` | VARCHAR(20) | Final phase (usually RESOLUTION) |
| `total_bet` | BIGINT UNSIGNED | Total tickets/bet in the round |
| `winner_user_id` | INT | Winner's user ID (NULL if no winner) |
| `winner_bet` | BIGINT UNSIGNED | Winner's ticket/bet count |
| `commission` | BIGINT UNSIGNED | House commission (20% of total) |
| `payout` | BIGINT UNSIGNED | Winner's payout (80% of total) |
| `started_at` | TIMESTAMP | Round start time |
| `countdown_started_at` | TIMESTAMP | Countdown start time |
| `countdown_ended_at` | TIMESTAMP | Countdown end time |
| `resolved_at` | TIMESTAMP | Resolution time |
| `created_at` | TIMESTAMP | Record creation time |

**Indexes:**
- Primary key on `id`
- Index on `room_id`
- Index on `winner_user_id`
- Index on `resolved_at`
- Foreign key to `game_rooms(id)`

#### `game_round_participants`
Participants in each game round.

| Column | Type | Description |
|--------|------|-------------|
| `id` | BIGINT | Primary key, auto-increment |
| `round_id` | BIGINT | Foreign key to `game_rounds.id` |
| `user_id` | INT | Foreign key to `db_users_a.id` |
| `tickets` | BIGINT UNSIGNED | User's total tickets in this round |
| `joined_at` | TIMESTAMP | When user joined the round |

**Indexes:**
- Primary key on `id`
- Index on `round_id`
- Index on `user_id`
- Composite index on `(round_id, user_id)`
- Foreign keys to `game_rounds(id)` and `db_users_a(id)`

### Task Tables

#### `tasks`
Available tasks for users to complete.

| Column | Type | Description |
|--------|------|-------------|
| `id` | INT | Primary key, auto-increment |
| `type` | VARCHAR(20) | Task type: `referral`, `follow`, `other`, `daily` |
| `requirement` | INT | Requirement value (e.g., number of referrals, deposit amount) |
| `reward_amount` | BIGINT | Reward amount in bigint format |
| `reward_type` | VARCHAR(20) | Reward type: `Stars`, `Tickets`, `Power` |
| `display_order` | INT | Display order in UI |
| `title` | VARCHAR(255) | Task title |
| `description` | TEXT | Task description |

**Indexes:**
- Primary key on `id`
- Index on `type`
- Index on `display_order`

**Default Tasks:**
- **Referral:** Invite 1, 3, 7, 15, 30, 50, 100 friends (rewards: 2, 5, 15, 25, 40, 60, 150 Stars)
- **Follow:** Follow news channel (reward: 5 Stars)
- **Deposit:** Top up $5 (reward: 100 Stars)
- **Daily:** Daily bonus (reward: 1 ticket, claimable every 24h)

#### `user_task_claims`
Tracks which tasks users have claimed.

| Column | Type | Description |
|--------|------|-------------|
| `id` | BIGINT | Primary key, auto-increment |
| `user_id` | INT | Foreign key to `db_users_a.id` |
| `task_id` | INT | Foreign key to `tasks.id` |
| `claimed_at` | TIMESTAMP | When task was claimed |

**Indexes:**
- Primary key on `id`
- Unique composite index on `(user_id, task_id)`
- Index on `user_id`
- Index on `task_id`
- Foreign keys to `db_users_a(id)` and `tasks(id)`

#### `user_daily_bonus_claims`
Tracks daily bonus claims (optimized table to avoid JOINs).

| Column | Type | Description |
|--------|------|-------------|
| `id` | BIGINT | Primary key, auto-increment |
| `user_id` | INT | Foreign key to `db_users_a.id` |
| `avatar_url` | VARCHAR(255) | User's avatar URL (denormalized) |
| `screen_name` | VARCHAR(75) | User's screen name (denormalized) |
| `claimed_at` | TIMESTAMP | Claim time |

**Indexes:**
- Primary key on `id`
- Index on `user_id`
- Index on `claimed_at` (DESC)
- Foreign key to `db_users_a(id)`

### Feature Switches Table

#### `feature_switches`
Runtime feature toggles (e.g. remote bet, deposits, withdrawals). Admin can change without restart.

| Column | Type | Description |
|--------|------|-------------|
| `key` | VARCHAR(64) | Primary key (e.g. `remote_bet_enabled`, `payment_enabled`, `payout_enabled`) |
| `enabled` | TINYINT(1) | 0 or 1 |
| `updated_at` | TIMESTAMP | Last update |

### Crypto Deposit Tables

#### `crypto_deposit_config`
Single row (id=1): minimum_deposit (decimal), methods_hash for sync skip when unchanged.

| Column | Type | Description |
|--------|------|-------------|
| `id` | INT | Primary key (1) |
| `methods_hash` | VARCHAR(255) | Hash from external API; skip sync when unchanged |
| `minimum_deposit` | DECIMAL(10,2) | Minimum USD (e.g. 2.50) |
| `updated_at` | TIMESTAMP | Last update |

#### `crypto_deposit_methods`
One row per active deposit method (synced from external API every 10 min). `pid` = icon filename without extension (e.g. 235.png).

| Column | Type | Description |
|--------|------|-------------|
| `id` | BIGINT | Primary key |
| `pid` | INT | External API PID (unique) |
| `name` | VARCHAR(100) | e.g. TON, USDT |
| `network` | VARCHAR(50) | e.g. TON, TRC20 |
| `example` | VARCHAR(255) | Example address |
| `min_deposit_sum` | DECIMAL(10,2) | Min USD for this method |
| `updated_at` | TIMESTAMP | Last update |

### Crypto Withdrawal Tables

#### `crypto_withdrawal_methods`
One row per active withdrawal method (synced from external API every 30 min). `pid` = method id; `icon_id` = icon filename (e.g. 30.png).

| Column | Type | Description |
|--------|------|-------------|
| `id` | BIGINT | Primary key |
| `pid` | INT | Method ID (unique) |
| `name` | VARCHAR(50) | Ticker (e.g. LTC, TON) |
| `network` | VARCHAR(100) | Network name |
| `icon_id` | VARCHAR(20) | Icon filename without extension |
| `min_withdrawal` | DECIMAL(10,2) | Min withdrawal USD |
| `updated_at` | TIMESTAMP | Last update |

### Payment Tables

#### `payments`
Payment records (crypto and legacy Telegram Stars).

| Column | Type | Description |
|--------|------|-------------|
| `id` | BIGINT | Primary key, auto-increment |
| `user_id` | INT | Foreign key to `db_users_a.id` |
| `order_id` | VARCHAR(255) | Unique order ID |
| `stars_amount` | INT | Telegram Stars (0 for crypto) |
| `usd_amount` | DECIMAL(20,2) | USD amount (crypto; e.g. 1.25) |
| `tickets_amount` | BIGINT UNSIGNED | Tickets in bigint (1 USD = 1,000 tickets → 1,000,000,000 per USD) |
| `status` | VARCHAR(20) | `PENDING`, `COMPLETED`, `FAILED`, `CANCELLED` |
| `telegram_payment_charge_id` | VARCHAR(255) | Telegram (legacy) |
| `telegram_provider_payment_charge_id` | VARCHAR(255) | Telegram (legacy) |
| `created_at` | TIMESTAMP | Creation time |
| `completed_at` | TIMESTAMP | Completion time |

**Indexes:**
- Primary key on `id`
- Unique key on `order_id`
- Index on `user_id`
- Index on `status`
- Index on `created_at`
- Composite index on `(user_id, status)`
- Foreign key to `db_users_a(id)`

#### `payouts`
Withdrawal/payout requests (CRYPTO, STARS, GIFT).

| Column | Type | Description |
|--------|------|-------------|
| `id` | BIGINT | Primary key, auto-increment |
| `user_id` | INT | Foreign key to `db_users_a.id` |
| `username` | VARCHAR(255) | User's username (for STARS/GIFT) |
| `wallet` | VARCHAR(120) | Wallet address (CRYPTO) |
| `type` | VARCHAR(20) | `CRYPTO`, `STARS`, `GIFT` |
| `gift_name` | VARCHAR(50) | Gift type for GIFT (HEART, BEAR, etc.) |
| `crypto_name` | VARCHAR(20) | Ticker for CRYPTO (e.g. TRX, TON) |
| `total` | BIGINT UNSIGNED | Tickets in bigint format |
| `stars_amount` | INT | Stars amount (0 for CRYPTO) |
| `usd_amount` | DECIMAL(20,2) | USD equivalent (CRYPTO) |
| `amount_coins` | VARCHAR(50) | Withdrawal amount in coins (from API) |
| `commission_coins` | VARCHAR(50) | Commission in coins (from API) |
| `amount_to_send` | VARCHAR(50) | Final amount to send (from API) |
| `payment_id` | INT | Crypto API payment id (for status sync) |
| `quantity` | INT | Quantity (e.g. gifts); default 1 |
| `status` | VARCHAR(20) | `PROCESSING`, `WAITING`, `COMPLETED`, `CANCELLED` |
| `created_at` | TIMESTAMP | Request time |
| `updated_at` | TIMESTAMP | Last update (touched by cron sync) |
| `resolved_at` | TIMESTAMP | Resolution time |

**Indexes:**
- Primary key on `id`
- Index on `user_id`
- Index on `status`
- Index on `type`
- Index on `created_at`
- Index on `updated_at`
- Composite indexes for CRYPTO status sync
- Foreign key to `db_users_a(id)`

#### `transactions`
Transaction history (all balance changes).

| Column | Type | Description |
|--------|------|-------------|
| `id` | BIGINT | Primary key, auto-increment |
| `user_id` | INT | Foreign key to `db_users_a.id` |
| `amount` | BIGINT | Amount in bigint format (positive=credit, negative=debit) |
| `type` | VARCHAR(50) | Type: `DEPOSIT`, `WITHDRAWAL`, `WIN`, `LOSS`, `TASK_BONUS`, `CANCELLATION_OF_WITHDRAWAL` |
| `task_id` | INT | Task ID for `TASK_BONUS` type (nullable) |
| `round_id` | BIGINT | Round ID for `WIN`/`LOSS` type (nullable) |
| `created_at` | TIMESTAMP | Transaction time |

**Indexes:**
- Primary key on `id`
- Composite index on `(user_id, created_at DESC)`
- Composite index on `(user_id, type)`
- Foreign key to `db_users_a(id)`

**Note:** Transactions older than 30 days are automatically cleaned up by cron job.

### Support Tables

#### `support_tickets`
User support tickets.

| Column | Type | Description |
|--------|------|-------------|
| `id` | BIGINT | Primary key, auto-increment |
| `user_id` | INT | Foreign key to `db_users_a.id` |
| `subject` | VARCHAR(100) | Ticket subject |
| `status` | ENUM | Status: `OPENED`, `CLOSED` |
| `created_at` | TIMESTAMP | Ticket creation time |
| `updated_at` | TIMESTAMP | Last update time |

**Indexes:**
- Primary key on `id`
- Index on `user_id`
- Index on `status`
- Composite index on `(user_id, status)`
- Index on `created_at`
- Foreign key to `db_users_a(id)`

#### `support_messages`
Messages within support tickets.

| Column | Type | Description |
|--------|------|-------------|
| `id` | BIGINT | Primary key, auto-increment |
| `ticket_id` | BIGINT | Foreign key to `support_tickets.id` |
| `user_id` | INT | Foreign key to `db_users_a.id` |
| `message` | VARCHAR(2000) | Message content |
| `created_at` | TIMESTAMP | Message time |

**Indexes:**
- Primary key on `id`
- Index on `ticket_id`
- Index on `user_id`
- Composite index on `(ticket_id, created_at)`
- Foreign keys to `support_tickets(id)` and `db_users_a(id)`

### Admin Tables

#### `admins`
Admin user accounts (separate from regular users).

| Column | Type | Description |
|--------|------|-------------|
| `id` | INT | Primary key, auto-increment |
| `user_id` | INT | Optional FK to `db_users_a.id` (for support messages) |
| `username` | VARCHAR(50) | Admin username (unique) |
| `password_hash` | VARCHAR(255) | BCrypt hashed password |
| `role` | VARCHAR(20) | Role: `ROLE_ADMIN`, `ROLE_PAYOUT_SUPPORT`, `ROLE_TICKETS_SUPPORT` |
| `created_at` | TIMESTAMP | Account creation time |
| `updated_at` | TIMESTAMP | Last update time |

**Indexes:**
- Primary key on `id`
- Unique key on `username`
- Index on `role`
- Index on `user_id`

#### `lottery_bot_configs`
Bot behaviour config: one row per user; user must exist in `db_users_a`.

| Column | Type | Description |
|--------|------|-------------|
| `id` | INT | Primary key, auto-increment |
| `user_id` | INT | FK to `db_users_a.id` (unique) |
| `room_1` | TINYINT(1) | Can play room 1 (0/1) |
| `room_2` | TINYINT(1) | Can play room 2 (0/1) |
| `room_3` | TINYINT(1) | Can play room 3 (0/1) |
| `time_utc_start` | TIME | Start of active window (UTC) |
| `time_utc_end` | TIME | End of active window (UTC) |
| `bet_min` | BIGINT | Min bet in bigint (1 ticket = 1,000,000) |
| `bet_max` | BIGINT | Max bet in bigint |
| `persona` | VARCHAR(20) | `conservative`, `aggressive`, `balanced` |
| `active` | TINYINT(1) | 1 = enabled, 0 = disabled |
| `created_at` | TIMESTAMP | Creation time |
| `updated_at` | TIMESTAMP | Last update time |

**Indexes:**
- Unique key on `user_id`
- Index on `(active, room_1, room_2, room_3)`

#### `quick_answers`
Admin quick-response templates for support (per admin).

| Column | Type | Description |
|--------|------|-------------|
| `id` | INT | Primary key, auto-increment |
| `admin_id` | INT | FK to `admins.id` |
| `text` | TEXT | Template text |
| `created_at` | TIMESTAMP | Creation time |
| `updated_at` | TIMESTAMP | Last update time |

**Indexes:**
- Primary key on `id`
- Index on `admin_id`
- Index on `(admin_id, created_at)`

---

## API Endpoints

### Public Endpoints

- `POST /api/auth/tma/session` - Create session from Telegram initData
- `GET /api/check_user/{token}/{telegramId}` - Check user info (external API). Token is configured via `APP_CHECK_USER_TOKEN` (set on server only, not in repo).
- `POST /api/deposit_webhook/{token}` - 3rd party deposit completion (no auth). Body: `user_id`, `usd_amount`. Token via `APP_DEPOSIT_WEBHOOK_TOKEN`.

### Authenticated Endpoints (Bearer Token)

#### Game (REST)
- `GET /api/game/room/{roomNumber}/completed-rounds` - Last 10 completed rounds for a room
- `GET /api/game/history` - Get game history for current user
- **Join and room state:** Via WebSocket (see WebSocket section below), not REST.

#### User
- `GET /api/users/current` - Get current user info (includes balance, `paymentEnabled`, `payoutEnabled` from feature switches)
- `GET /api/users/referrals` - Get referral info
- `POST /api/users/deposit` - Legacy/optional deposit flow

#### Tasks
- `GET /api/tasks?type=` - Get available tasks (query: `type` = referral, follow, other, etc.)
- `GET /api/tasks/daily-bonus` - Daily bonus status
- `GET /api/tasks/daily-bonus/recent-claims` - Recent daily bonus claims (optional query: `timezone`)
- `POST /api/tasks/claim` - Claim task reward (body: `taskId`)

#### Payments (deposits)
- `GET /api/payments/minimum-deposit` - Min deposit USD from config (503 if payment disabled)
- `GET /api/payments/deposit-methods` - Crypto deposit methods from DB (503 if payment disabled)
- `POST /api/payments/deposit-address` - Get crypto deposit address/amount from external API (pid, usdAmount)
- `POST /api/payments/create` - Create payment invoice (crypto: usdAmount; legacy Stars no longer supported) (503 if payment disabled)
- `POST /api/payments/cancel` - Cancel payment by orderId

#### Payouts (withdrawals)
- `GET /api/payments/withdrawal-methods` - Crypto withdrawal methods from DB (503 if payout disabled)
- `GET /api/payments/withdrawal-method-details?pid=` - Rate/fee for method from external API (503 if payout disabled)
- `POST /api/payments/crypto-withdrawal` - Create crypto withdrawal (pid, wallet, total in tickets); creates payout and deducts balance (503 if payout disabled)
- `POST /api/payouts` - Request STARS or GIFT payout
- `GET /api/payouts/history` - Last 20 payouts for current user

#### Support
- `GET /api/support/tickets` - Get user's tickets
- `POST /api/support/tickets` - Create ticket
- `GET /api/support/tickets/{ticketId}` - Get ticket with messages
- `POST /api/support/tickets/{ticketId}/messages` - Send message
- `POST /api/support/tickets/{ticketId}/close` - Close ticket

#### Transactions
- `GET /api/transactions` - Get current user's transaction history

### Admin Endpoints (JWT Token)

All admin paths are under `/api/admin/`. Role requirements: dashboard, rooms, configurations, bots, users, payments, payouts, feature-switches, analytics require ADMIN; payouts also allow ROLE_PAYOUT_SUPPORT; tickets and quick-answers allow ROLE_TICKETS_SUPPORT.

- `POST /api/admin/login` - Admin login
- `GET /api/admin/dashboard/stats` - Dashboard stats (users, active players, revenue/payouts in Stars and crypto USD, rounds, support tickets)
- `GET /api/admin/rooms` - List all game rooms
- `GET /api/admin/rooms/{roomNumber}` - Room detail (participants, phase, etc.)
- `POST /api/admin/rooms/{roomNumber}/repair` - Repair stuck round
- `GET /api/admin/configurations` - App configurations
- `GET /api/admin/bots` - List lottery bot configs
- `GET /api/admin/bots/{id}` - Get bot config by id
- `POST /api/admin/bots` - Create bot config
- `PUT /api/admin/bots/{id}` - Update bot config
- `DELETE /api/admin/bots/{id}` - Delete bot config
- `POST /api/admin/bots/shuffle?roomNumber=` - Shuffle bot time windows for room (query: `roomNumber` = 2 or 3)
- `GET /api/admin/users` - Paginated users list (50 per page; sort supported)
- `GET /api/admin/users/{id}` - User detail
- `GET /api/admin/users/{id}/transactions` - User transactions
- `GET /api/admin/users/{id}/game-rounds` - User game rounds
- `GET /api/admin/users/{id}/tasks` - User task claims
- `PATCH /api/admin/users/{id}/ban` - Ban/unban user (body: `banned`)
- `POST /api/admin/users/{id}/balance/adjust` - Manual balance adjustment
- `GET /api/admin/feature-switches` - List all feature switches
- `PATCH /api/admin/feature-switches/{key}` - Update feature switch (body: `enabled`)
- `GET /api/admin/payments` - Paginated payments (filter by userId, status)
- `GET /api/admin/payouts` - Paginated payouts (filter by userId, status, type)
- `POST /api/admin/payouts/{id}/complete` - Mark payout COMPLETED
- `POST /api/admin/payouts/{id}/cancel` - Mark payout CANCELLED (refund)
- `GET /api/admin/analytics/revenue` - Revenue/payout time series (query: `range` = 7d, 30d, 90d, 1y, all)
- `GET /api/admin/analytics/activity` - Activity metrics for charts
- `GET /api/admin/quick-answers` - List quick answers for current admin
- `POST /api/admin/quick-answers` - Create quick answer (body: `text`)
- `PUT /api/admin/quick-answers/{id}` - Update quick answer (own only)
- `DELETE /api/admin/quick-answers/{id}` - Delete quick answer (own only)

### WebSocket

- **Endpoint:** `/ws` (STOMP over SockJS or WebSocket)
- **Subscribe:** `/topic/room/{roomNumber}/state` - Room state updates (full state on subscribe; broadcasts on phase/participant changes)
- **Send (join round):** `/app/game/join` - Payload: roomNumber, amount (tickets in bigint). Join is handled via WebSocket, not REST.

---

## Key Business Logic

### Balance Format

All monetary values in balance/transaction/payout totals are stored in **bigint format** with 6 decimal places:
- 1 Ticket = 1,000,000 (in database)
- **Crypto deposits:** 1 USD = 1,000 tickets → 1,000,000,000 in database per USD
- Legacy: 1 Telegram Star = 900,000 (0.9 conversion; no longer used for new deposits)
- Example: 50 tickets = 50,000,000 in database

### Winner Selection Algorithm

1. Calculate total tickets in round
2. Generate random number between 0 and total_tickets
3. Iterate through participants, accumulating ticket counts
4. First participant whose accumulated tickets >= random number wins
5. This creates a weighted lottery (more tickets = higher chance)

### House Commission

- **Rate:** 20% of total pool
- **Calculation:** `commission = total_tickets × 0.2`
- **Winner Payout:** `payout = total_tickets - commission`

### Referral Commission Calculation

**When Referral Wins:**
```
net_profit = (total_pool - house_commission) - referral_bet
commission = net_profit × 0.01  // 1% for all levels
```

**When Referral Loses:**
```
Level 1: commission = referral_bet × 0.04  // 4%
Level 2: commission = referral_bet × 0.02  // 2%
Level 3: commission = referral_bet × 0.01  // 1%
```

### Data Cleanup

- **Transactions:** Cleaned up after 30 days (cron job)
- **Game Rounds:** Old rounds cleaned up periodically
- **Sessions:** Expired sessions cleaned up in batches

### Crypto API Integration

- **Deposits:** External API base URL and API key via `app.crypto-api.base-url` and `app.crypto-api.api-key`. Deposit methods synced every 10 min (cron); deposit address on demand (POST deposit-address). Completion via `POST /api/deposit_webhook/{token}` (token: `app.deposit-webhook.token`).
- **Withdrawals:** Withdrawal methods synced every 30 min (cron). POST withdrawal creates payout; status sync cron every minute calls GET withdrawals-info for PROCESSING/WAITING CRYPTO payouts and updates status (COMPLETED/CANCELLED trigger balance and transaction updates).

---

## Security Features

1. **Telegram Authentication:** Validates Telegram Mini App initData
2. **Session Management:** Secure Bearer token sessions
3. **Rate Limiting:** Per-user and per-endpoint rate limiting
4. **CORS Protection:** Configured for specific origins
5. **Admin Authentication:** Separate JWT-based admin system
6. **Input Validation:** All user inputs validated
7. **SQL Injection Protection:** JPA/Hibernate parameterized queries
8. **XSS Protection:** Content Security Policy headers

---

## Performance Optimizations

1. **Database Indexes:** Comprehensive indexing for fast queries
2. **Connection Pooling:** HikariCP with optimized pool size
3. **WebSocket Keep-Alive:** Efficient real-time communication
4. **Batch Operations:** Bulk cleanup operations
5. **Caching:** In-memory caching for active game rounds
6. **Nginx Caching:** Static asset caching (1 year TTL)
7. **Composite Indexes:** Optimized for common query patterns

---

## Deployment

- **Backend:** Docker container on VPS
- **Frontend:** Static files served by Nginx
- **Admin Panel:** Static files at `win-spin.live/{secret-path}/`
- **Database:** MySQL on VPS
- **SSL:** Let's Encrypt certificates
- **Domain:** `win-spin.live`

---

This document provides a comprehensive overview of the Lottery Application. For specific implementation details, refer to the source code in the respective service classes and repositories.


