Onboarding
1. Summary
Goal: Гейтинг доступа к приложению через проверку подписок на Telegram бота и канал. Обеспечивает контролируемую воронку входа пользователей.
User Value: Быстрый и понятный старт с пошаговым гайдом. Пользователь чётко видит, что нужно сделать для получения доступа.
2. Business Logic
Activation Flow
Subscription Checks
- Bot Subscription
- Channel Subscription
Проверка: User.botStatus
Допустимые значения:
ACTIVE— пользователь написал/startботуREACTIVATED— пользователь разблокировал бота после блокировки
Недопустимые:
NEW_USER— пользователь создан в TMA, но не писал ботуBLOCKED— пользователь заблокировал бота
Как обновляется: При получении Telegram webhook от бота.
Проверка: TelegramSubscriptionService.checkChannelSubscription()
API вызов: Bot.api.getChatMember(channelId, userId)
Допустимые статусы: member, administrator, creator
Недопустимые: left, kicked, restricted (без can_send_messages)
Канал: Настраивается через environment variable.
Onboarding Steps Interface
interface OnboardingStep {
name: 'bot_subscription' | 'channel_subscription';
description: string; // Локализованное описание
completed: boolean; // Выполнен ли шаг
required: boolean; // Обязателен для активации (всегда true)
}
interface OnboardingStatus {
isComplete: boolean; // Все шаги выполнены
canActivate: boolean; // Можно завершить онбординг
nextStep: string | null; // Следующий необходимый шаг
steps: OnboardingStep[]; // Список всех шагов
}
Core Logic
getOnboardingStatus(telegramId, forceRefresh):
- Найти пользователя по
telegramId - Проверить
botStatus(ACTIVE или REACTIVATED) - Проверить подписку на канал через Telegram API
- Сформировать список шагов с их статусами
- Определить
canActivate = allRequired.every(completed)
completeOnboarding(telegramId):
- Получить актуальный статус онбординга
- Проверить
canActivate === true - Если да — вернуть welcome message
- Если нет — вернуть ошибку с недостающими шагами
OnboardingService НЕ активирует InviteSession. Активация выполняется через ActivationRewardService в Telegram bot flow. Онбординг только проверяет подписки. См. Activation.
Protection
| Действие | Rate Limit | Auth | Validation |
|---|---|---|---|
| Get status | — | none (public) | GetOnboardingStatusSchema |
| Complete | — | none (public) | CompleteOnboardingSchema |
| Refresh subscriptions | — | none (public) | RefreshSubscriptionsSchema |
Эндпоинты публичные, потому что пользователь ещё не аутентифицирован на этом этапе. Аутентификация происходит после успешного онбординга.
Edge Cases
| Ситуация | Поведение | UI |
|---|---|---|
| Прямой вход в TMA без invite link | Всё равно проверяем подписки | Показываем шаги как обычно |
Force refresh (?force=true) | Bypass кэша, актуальная проверка | Используется кнопкой "Обновить" |
| Telegram API недоступен | Graceful fallback, allow access | Пропускаем пользователя |
| Bot status = BLOCKED | Шаг bot_subscription = incomplete | "Разблокируйте бота" |
| Канал не существует | Ошибка при проверке | Логируем, не блокируем |
| Бэкенд недоступен (сетевая ошибка) | onboardingState = 'server_unavailable' | ServerUnavailableScreen с retry |
| Бэкенд не отвечает (TCP timeout >15с) | Timeout safety net → server_unavailable | Тот же ServerUnavailableScreen |
| Сессия Telegram устарела | onboardingState = 'session_expired' | SessionExpiredScreen — перезапуск Mini App |
Gate Screens (LoadingOrchestrator)
LoadingOrchestrator управляет инициализацией и может показать блокирующий экран вместо приложения:
| Экран | Условие | Кнопки |
|---|---|---|
BrandedLoadingScreen | Идёт инициализация | — (fake progress bar) |
ServerUnavailableScreen | Оба API-запроса (maintenance + season) упали с network error, либо timeout 15с | "Повторить попытку", "Закрыть приложение" |
SessionExpiredScreen | initData Telegram отсутствует или невалиден | "Перезапустить приложение" |
BannedScreen | Пользователь забанен | "Закрыть приложение" |
| Bot/Channel activation | Подписки не выполнены | Ссылки на бота/канал |
Детекция backend unavailability происходит в initialize() через networkError: boolean флаг в return type обоих check-функций (checkMaintenanceStatus, checkSeasonStatus). Если хотя бы одна вернула networkError: true и при этом maintenance не активен — показывается ServerUnavailableScreen. Retry полностью сбрасывает state machine и запускает инициализацию заново.
3. ADR (Architectural Decisions)
Почему OnboardingService НЕ активирует сессии?
Проблема: Изначально OnboardingService и активировал подписки, и активировал InviteSession. Это нарушало Single Responsibility.
Решение: Разделение ответственности:
OnboardingService— только проверка подписокActivationRewardService— активация сессий и распределение наград
Альтернативы (отклонены):
- Всё в одном сервисе — сложнее тестировать, нарушает SRP
Последствия:
- Чистая архитектура
- Проще unit-тесты
- POST /onboarding/complete не имеет side effects кроме возврата сообщения
Почему проверки для ВСЕХ пользователей?
Проблема: Пользователи могут войти в TMA напрямую (без invite link), минуя создание PENDING InviteSession.
Решение: Проверять подписки для всех, независимо от наличия PENDING сессии.
Последствия:
- Консистентный гейтинг для всех способов входа
- Нет "обходного пути" через direct access
Почему публичные эндпоинты без auth?
Проблема: Как аутентифицировать пользователя, который ещё не прошёл онбординг?
Решение: Эндпоинты /onboarding/* публичные, используют только telegramId из query/body.
Риски и митигация:
- Риск: Спам запросов → Митигация: Rate limit на уровне IP (implicit)
- Риск: Утечка данных → Митигация: Эндпоинты возвращают только boolean статусы, никаких sensitive данных
Последствия:
- Простой flow для нового пользователя
- Нет chicken-and-egg проблемы с auth
4. Architecture
Service Dependencies
Key Components
| Компонент | Путь | Описание |
|---|---|---|
| OnboardingService | backend/src/domains/users/services/onboarding.service.ts | Проверка подписок |
| OnboardingController | backend/src/domains/users/controllers/onboarding.controller.ts | HTTP handlers |
| TelegramSubscriptionService | backend/src/domains/telegram/services/telegram-subscription.service.ts | Telegram API интеграция |
| Routes | backend/src/domains/users/routes/onboarding.routes.ts | API endpoints |
| Schemas | backend/src/domains/users/schemas/onboarding.schemas.ts | Request/Response validation |
Method Details
OnboardingService Methods
getOnboardingStatus(telegramId: number, forceRefresh?: boolean)
- Возвращает:
OnboardingStatus - Кэширование: Нет (проверки реал-тайм)
- Force refresh: Bypass any implicit caching
completeOnboarding(telegramId: number)
- Возвращает:
{ wasActivated: boolean, message: string } - Ошибки:
ONBOARDING_NOT_COMPLETEесли canActivate = false
checkBotSubscription(telegramId: number)
- Возвращает:
boolean - Проверяет:
user.botStatus in ['ACTIVE', 'REACTIVATED']
checkChannelSubscription(telegramId: number)
- Возвращает:
boolean - Использует:
TelegramSubscriptionService
refreshSubscriptionStatus(telegramId: number)
- Возвращает:
{ refreshed: boolean, subscriptions: SubscriptionStatus[] } - Для: Troubleshooting и testing
5. Database Schema
Related Models
| Модель | Описание | Ключевые поля |
|---|---|---|
| User | Содержит botStatus | botStatus (BotRegistrationState enum) |
| InviteSession | Трекинг перехода по ссылке | state, telegramId, metadata, expiresAt |
BotRegistrationState Enum
enum BotRegistrationState {
NEW_USER // Создан в TMA, никогда не писал боту
ACTIVE // Написал /start боту
REACTIVATED // Разблокировал бота после блокировки
BLOCKED // Заблокировал бота
}
InviteSession States
Relationships
6. API Endpoints
Public API
| Метод | Эндпоинт | Описание | Docs |
|---|---|---|---|
| GET | /api/onboarding/status | Проверка статуса онбординга | → |
| POST | /api/onboarding/complete | Завершение онбординга | → |
| POST | /api/onboarding/refresh-subscriptions | Принудительное обновление статуса | → |
Request/Response Examples
GET /api/onboarding/status?telegramId=123456789
// Response 200
{
"success": true,
"data": {
"isComplete": false,
"canActivate": false,
"nextStep": "channel_subscription",
"steps": [
{
"name": "bot_subscription",
"description": "Подпишитесь на бота",
"completed": true,
"required": true
},
{
"name": "channel_subscription",
"description": "Подпишитесь на канал",
"completed": false,
"required": true
}
]
}
}
POST /api/onboarding/complete
// Request Body
{
"telegramId": 123456789
}
// Response 200 (success)
{
"success": true,
"data": {
"wasActivated": true,
"message": "Добро пожаловать в GOLOOT!"
}
}
// Response 400 (not ready)
{
"success": false,
"error": "Onboarding not complete",
"data": {
"missingSteps": ["channel_subscription"]
}
}
POST /api/onboarding/refresh-subscriptions
// Request Body
{
"telegramId": 123456789
}
// Response 200
{
"success": true,
"data": {
"refreshed": true,
"subscriptions": [
{ "type": "bot", "status": true },
{ "type": "channel", "status": false }
]
}
}
Stub Endpoints (не реализованы)
| Метод | Эндпоинт | Описание | Статус |
|---|---|---|---|
| GET | /api/onboarding/notifications | Получение уведомлений | Stub (empty array) |
| POST | /api/onboarding/notifications/mark-read | Пометить прочитанными | Stub (always success) |
7. Interactive Guide
Добавлена система интерактивного тура для новых пользователей после успешной активации. Использует react-joyride для пошагового обучения.
Goal
После прохождения activation flow (подписки на бота/канал), новым пользователям показывается интерактивный тур по приложению. Цель — познакомить с основными возможностями за ~1 минуту.
Tour System Architecture
Components
| Компонент | Путь | Описание |
|---|---|---|
| OnboardingGuide | frontend/src/components/onboarding/OnboardingGuide.tsx | React-joyride wrapper, управление туром |
| TourPromptModal | frontend/src/components/onboarding/TourPromptModal.tsx | Модалка "Хочешь пройти тур?" |
| ConfirmSkipModal | frontend/src/components/onboarding/ConfirmSkipModal.tsx | Подтверждение пропуска тура |
| onboardingStore | frontend/src/stores/onboardingStore.ts | Zustand store для UI состояния |
| tourSteps | frontend/src/config/tourSteps.ts | Конфигурация 12 шагов тура |
Tour Steps (12 шагов)
Шаги нацелены на основные UI элементы через data-tour атрибуты:
| # | Target | Описание |
|---|---|---|
| 1 | balance-scrap | Баланс Scrap — игровая валюта |
| 2 | balance-xp | XP — опыт для прокачки уровня |
| 3 | daily-case | Ежедневный бесплатный кейс |
| 4 | quests-section | Квесты — задания для наград |
| 5 | quest-filters | Фильтры квестов по типам |
| 6 | nav-spin | Навигация: Рулетка |
| 7 | nav-cases | Навигация: Кейсы |
| 8 | nav-profile | Навигация: Профиль |
| 9 | inventory-button | Кнопка инвентаря (скины) |
| 10 | steam-trade-url | Steam Trade URL для вывода |
| 11 | settings-button | Кнопка настроек |
| 12 | faq-button | FAQ для помощи |
User Flow
Новый пользователь:
- Проходит activation (подписки)
- После успешной активации →
onboardingGuideCompleted = false(default) - App.tsx показывает TourPromptModal
- Пользователь выбирает:
- "Начать тур" → Joyride показывает 12 шагов
- "Не сейчас" → ConfirmSkipModal → подтверждение → сохранение флага в БД
Повторный запуск:
- Settings → "Пройти тур заново"
setOnboardingGuideCompleted(false)→showPrompt()- TourPromptModal появляется снова
Backend Integration
Поле: UserSettings.onboardingGuideCompleted (Boolean, default: false)
Тип синхронизации: Backend DB only (НЕ сохраняется в localStorage)
Обновление:
- При завершении тура:
PUT /api/users/settings { onboardingGuideCompleted: true } - При пропуске тура: то же самое
- При "Пройти заново": локальный
setOnboardingGuideCompleted(false)→ модалка появляется
onboardingGuideCompleted НЕ сохраняется в localStorage (в отличие от soundEnabled). Это предотвращает bypass тура через очистку localStorage.
Edge Cases
| Ситуация | Поведение |
|---|---|
| Пользователь закрывает модалку (X) | Тур не засчитывается, модалка появится при следующем запуске |
| Пользователь нажимает ESC во время тура | Joyride останавливается, тур не засчитывается |
| Target элемент не найден | Joyride пропускает шаг (event: TARGET_NOT_FOUND) |
| Пользователь переключил таб во время тура | Тур останавливается (элементы скрыты) |
8. Related
- Activation — активация InviteSession и награды
- Profile — профиль после активации
- Settings — настройки пользователя (включая onboardingGuideCompleted)
- Referrals — реферальная система
- Telegram Bot — интеграция с Telegram
- UTM Tracking — трекинг источников при регистрации
- Security Matrix — обзор защит