Skip to main content

Activation

1. Summary

Goal: Трекинг и активация пользователей, пришедших по приглашающим ссылкам (UTM или Referral). Связывание источника привлечения с зарегистрированным пользователем.

User Value: Прозрачная система вознаграждений за приглашение друзей. Награды (XP для referrer, Scrap для referred) начисляются отложенно через claim modal.


2. Business Logic

Session Types

Описание: Сессия для пользователей, пришедших по UTM-ссылке (блогеры, рекламные кампании).

Metadata:

  • campaignId — ID UTM-кампании
  • source, medium, campaign — UTM параметры
  • adType, influencer — дополнительные параметры

При активации:

  • Обновляется счётчик конверсий кампании
  • Награды выдаются только через промокоды (если кампания привязана к промокоду)

Session Lifecycle

Core Mechanics

1. Создание сессии (при клике)

  • Проверка на USER_RESET (блокирует создание)
  • Проверка на существующую PENDING сессию (дедупликация)
  • Создание InviteSession + инкремент clicksCount в транзакции

2. Активация сессии (после онбординга)

  • Поиск всех PENDING сессий по telegramId
  • Активация каждой (UTM → конверсия, REFERRAL → награды)
  • Обновление state → ACTIVATED
Дедупликация

Если у пользователя уже есть PENDING сессия любого типа, новая сессия НЕ создаётся. Возвращается существующая.

USER_RESET блокировка

Если у пользователя была сессия со статусом USER_RESET (сброс аккаунта), создание новых сессий заблокировано. Это защита от абьюза наград через пересоздание аккаунта.

Deduplication Logic

СостояниеПри попытке создать UTMПри попытке создать Referral
USER_RESET существуетВозврат USER_RESETВозврат USER_RESET
PENDING UTM существуетВозврат существующейВозврат существующей
PENDING REFERRAL существуетВозврат существующейВозврат существующей
ACTIVATED существует (same type)Возврат существующейВозврат существующей (нет повторного referral)
EXPIRED существуетСоздание новойОшибка unique constraint (EXPIRED referral не обработан явно)
Нет сессийСоздание новойСоздание новой
Cross-type дедупликация

Кросс-типовая дедупликация (UTM блокирует Referral и наоборот) работает только для PENDING сессий. Если у пользователя есть ACTIVATED сессия одного типа, создание сессии другого типа не будет заблокировано.

Edge Cases

СитуацияПоведениеКод
USER_RESET блокировкаВозврат существующей сессии
PENDING дедупликацияВозврат существующей сессии
Referral creation failsСессия активируется, награды не начисляются
Quest/Achievement errorNon-blocking, сессия активируется
Missing referrerUserIdСессия активируется, Referral не создаётся

3. ADR (Architectural Decisions)

Почему разделены InviteSessionService и ActivationRewardService?

Проблема: Изначально всё было в одном сервисе (583 строк). Нарушался SRP: создание сессий смешано с логикой наград.

Решение: Разделение на два сервиса:

  • InviteSessionService — создание и поиск сессий (CRUD)
  • ActivationRewardService — активация и распределение наград

Альтернативы (отклонены):

  • Один большой сервис — сложнее тестировать, нарушает SRP

Последствия:

  • Чистая архитектура
  • Проще unit-тесты (26 тестов для двух сервисов)
  • Каждый сервис < 320 строк

Почему дедупликация кросс-типов?

Проблема: Пользователь может кликнуть сначала по UTM, потом по Referral. Какую сессию считать основной?

Решение: Первая PENDING сессия побеждает. Если пользователь пришёл по UTM, а потом по Referral — UTM сессия сохраняется.

Последствия:

  • Честная атрибуция (first touch wins)
  • Защита от манипуляций с referral после UTM

Почему non-blocking errors для quest/achievement?

Проблема: Если обновление прогресса квестов падает, должна ли активация отменяться?

Решение: Non-blocking. Активация критична, прогресс — нет. Ошибки логируются, но не прерывают flow.

Последствия:

  • Надёжная активация
  • Возможна рассинхронизация прогресса (редко, исправляется ретраем)

Почему UTM не даёт автоматический scrap-бонус?

Проблема: Ранее UTM-переход автоматически начислял 200 scrap (BONUS_SCRAP_UTM). Это было заменено на промокодный flow — маркетолог привязывает промокод к UTM-кампании, пользователь вводит его после онбординга.

Решение: UTM-активация фиксирует только конверсию (PENDING → ACTIVATED). Награды выдаются исключительно через промокоды (/api/promo-codes/redeem).

Последствия:

  • Полный контроль маркетолога над наградами через промокоды
  • Нет скрытого начисления scrap при переходе по ссылке
  • UTM-аналитика конверсий работает независимо от наград

4. Architecture

Services Overview

Key Components

КомпонентПутьОписание
InviteSessionServicebackend/src/domains/activation/services/invite-session.service.tsСоздание и поиск сессий
ActivationRewardServicebackend/src/domains/activation/services/activation-reward.service.tsАктивация и награды
InviteSessionAdapterbackend/src/domains/activation/utils/invite-session.adapter.tsФорматирование для аналитики
Constantsbackend/src/domains/activation/constants/activation.constants.tsTTL, таймауты, конфигурация
Typesbackend/src/domains/activation/types/activation.types.tsТипы домена

Method Details

InviteSessionService Methods

createUTMSession(data: UTMSessionData)

  • Создаёт UTM сессию при клике по ссылке
  • Транзакция: InviteSession + UTMCampaign.clicksCount++
  • Дедупликация: возврат существующей при USER_RESET или PENDING

createReferralSession(data: ReferralSessionData)

  • Создаёт Referral сессию при клике по ссылке
  • Транзакция: InviteSession + ReferralCode.appOpensCount++ + ReferralClickAnalytics
  • Дедупликация: возврат существующей при USER_RESET, PENDING, или ACTIVATED referral

findByTelegramId(telegramId: string)

  • Поиск PENDING сессии для активации
  • Возвращает null при ошибке (graceful)

getAnalyticsForChart(filters)

  • Данные для графиков аналитики
  • Лимит 5000 записей для производительности
ActivationRewardService Methods

activateSession(telegramId: string, userId: string)

  • Находит все PENDING сессии
  • Активирует UTM: обновляет state, считает конверсию
  • Активирует REFERRAL: создаёт Referral, начисляет награды
  • Non-blocking errors для quest/achievement progress
  • Возвращает: { utmActivated?, referralActivated? }

5. Database Schema

Models

МодельОписаниеКлючевые поля
InviteSessionСессия приглашенияtype, state, telegramId, metadata, expiresAt
ReferralClickAnalyticsАналитика кликов по referralreferralCodeId, telegramHash

InviteSession Fields

ПолеТипОписание
typeInviteTypeUTM или REFERRAL
stateInviteSessionStatePENDING, ACTIVATED, EXPIRED, USER_RESET
telegramIdStringTelegram ID пользователя
userIdString?User ID после активации
metadataJsonUTMMetadata или ReferralMetadata
expiresAtDateTimeВремя истечения (24 часа)
activatedAtDateTime?Время активации
activationSourceInviteActivationSource?Источник активации (REF_DEEPLINK, UTM_DEEPLINK, PROMO_CODE)

Relationships


6. API Endpoints

Internal Domain

Activation домен не имеет собственных HTTP endpoints. Вся логика вызывается через Telegram Bot Routes при онбординге.


  • Onboarding — проверка подписок перед активацией
  • Referrals — реферальная система
  • UTM Tracking — UTM кампании и аналитика
  • Telegram Bot — бот, вызывающий активацию
  • Promo Codes — промокоды для UTM-кампаний