# Реферальный модуль ThaiForTravel

## Структура папки

```text
telegram-referral/
├── src/       # агентский webhook, tracking, кампании и атрибуция
├── tests/     # модульные и HTTP-тесты
├── scripts/   # безопасная регистрация webhook в Telegram
├── web/       # адаптивный Mini App и desktop-кабинет агента
├── .env.example
└── README.md
```

Модуль компилируется и запускается общим backend-процессом, поскольку использует
тот же клиентский Telegram webhook, OkoCRM и PostgreSQL. Общая Prisma-схема и
исполняемая миграция по правилам Prisma находятся соответственно в
`../prisma/schema.prisma` и
`../prisma/migrations/20260804143000_referral_agents/migration.sql`.

## Этап 1: tracking-контур без новых таблиц

Первый срез реализует путь от агентской ссылки до запуска клиентского бота:

```text
GET /r/<signed-token>
  -> REFERRAL_VISIT
  -> 302 https://t.me/<customer-bot>?start=<signed-token>
  -> /start <signed-token>
  -> REFERRAL_BOT_STARTED
```

Токен содержит публичный код будущей записи `ReferralAsset` и тип размещения
`LINK` или `QR`. Он подписан HMAC и укладывается в ограничение Telegram start
parameter — 64 символа. Изменённый вручную токен не принимается.

До создания реферальных таблиц события выводятся в структурированный backend
log через `ReferralEventSink`. Маршруты и Telegram webhook зависят от интерфейса
sink, поэтому на следующем этапе логирующая реализация заменяется Prisma-
реализацией без изменения публичных ссылок.

Для приблизительного подсчёта уникальных переходов используется суточный HMAC
от IP и User-Agent. Сырые IP, полный referer и query string не сохраняются.
Известные link-preview роботы помечаются `PREVIEW_BOT` и не должны учитываться
как человеческие переходы в будущих отчётах.

## Переменные окружения

`REFERRAL_TOKEN_SECRET` — отдельный секрет подписи токенов, минимум 16 символов.
Пока он не настроен, backend выводит отдельный ключ из
`TELEGRAM_WEBHOOK_SECRET` через SHA-256 для обратной совместимости. Перед
production-запуском рефералок нужно обязательно задать отдельный случайный
секрет. Уже выданные ссылки зависят от него, поэтому секрет нельзя менять без
механизма ротации.

Ошибка временного хранилища или логирования не блокирует переход клиента:
валидная ссылка всё равно перенаправляет его в Telegram.

## Этап 2: агентский бот и постоянное хранилище

Добавлен отдельный webhook `/webhook/telegram-agents`. Он включается, когда в
окружении одновременно заданы:

```env
AGENT_TELEGRAM_BOT_TOKEN=...
AGENT_TELEGRAM_BOT_USERNAME=...
AGENT_TELEGRAM_MINIAPP_URL=https://api.thaifortravel.ru/partners/
```

`AGENT_TELEGRAM_WEBHOOK_SECRET` можно не задавать: backend и скрипт настройки
Telegram одинаково выводят его из токена бота через SHA-256. При желании можно
задать отдельное значение из букв, цифр, `_` и `-`.

Webhook регистрируется после deployment командой:

```bash
npm run telegram:configure-agent
```

Бот реализует регистрацию агента через собственный Telegram-контакт, выбор
площадки, название кампании, отдельные assets для ссылки и QR, отправку PNG и
отчёт по переходам, запускам, клиентам и заявкам. Доступны YouTube, ВКонтакте,
Rutube, Telegram, Instagram, TikTok, сайт, офлайн-размещения, печать и «Другое».
Тексты и кнопки явно показывают, что одна кампания создаёт сразу ссылку и QR.

## Кабинет агента

Адаптивный кабинет опубликован по `/partners/`. Один интерфейс работает как
Telegram Mini App на телефоне, в Telegram Desktop и как обычный desktop-сайт.
Внутри Telegram вход проверяется по подписанному `initData` агентского бота.
В браузере backend создаёт одноразовую ссылку: агент подтверждает её командой
`/start web_<code>` в этом же боте, после чего вкладка получает подписанную
HttpOnly-сессию на восемь часов. Отчёты и QR нельзя получить без одной из этих
авторизаций.

API кабинета находится под `/partners/api`:

- `GET /config` — площадки и публичное имя бота;
- `POST /web-auth`, `GET /web-auth/:code` — одноразовый browser-login;
- `GET /me` — профиль зарегистрированного агента;
- `GET /campaigns` — комплекты и статистика;
- `POST /campaigns` — одновременное создание ссылки и QR;
- `GET /campaigns/:id/qr` — защищённая загрузка PNG.

Скрипт `npm run telegram:configure-agent` регистрирует webhook, команды и кнопку
«Кабинет агента» в меню Telegram.

Постоянное хранилище описано миграцией
`20260804143000_referral_agents`. Площадки соцсетей расширяются отдельной
безопасной миграцией `20260804210000_referral_social_platforms`, поэтому
существующие кампании не изменяются. Одноразовые desktop-входы хранятся в
`ReferralWebLogin` из миграции `20260804213000_referral_web_login`.

Планируемые таблицы:

- `ReferralAgent` — регистрация и статус агента;
- `ReferralCampaign` — название и платформа кампании;
- `ReferralAsset` — отдельные коды для ссылки и QR;
- `ReferralVisit` — переходы до Telegram;
- `ReferralStart` — идентифицированные запуски бота;
- `ClientAttribution` — неизменяемое закрепление нового клиента.

Prisma sink записывает переходы и запуски идемпотентно на уровне пары
`asset + Telegram user`. Первый новый пользователь получает статус
`PENDING_IDENTITY`. После передачи телефона worker сначала ищет контакт в OkoCRM:

- найденный контакт получает статус `EXISTING_CRM` без агента;
- только созданный этой проверкой контакт получает неизменяемую
  `ClientAttribution`;
- второй переход уже известного локального пользователя получает
  `EXISTING_LOCAL`;
- заявки сохраняют ссылку на `ClientAttribution` и попадают в отчёт кампании.

Запись значения в конкретное пользовательское поле «Агенты» OkoCRM подключается
после получения ID и типа этого поля. До этого данные агента, кампании, платформы
и формата уже добавляются в примечание создаваемой сделки.
