> ## Documentation Index
> Fetch the complete documentation index at: https://docs.bedolagam.ru/llms.txt
> Use this file to discover all available pages before exploring further.

# Подписки и тарифы

> Режимы продаж, ценообразование, докупка трафика и скидки

## Режимы продаж

Бот поддерживает два режима продажи подписок. Переключение — через переменную `SALES_MODE` в `.env` или в админ-панели: **Настройки → Подписки**.

| Режим       | `SALES_MODE` | Описание                                                      |
| ----------- | ------------ | ------------------------------------------------------------- |
| Конструктор | `classic`    | Пользователь сам выбирает период, трафик, серверы, устройства |
| Тарифы      | `tariffs`    | Готовые тарифные планы с фиксированными параметрами           |

<Info>
  Для одновременного использования нескольких тарифов (мультиподписки) см. [Мультитарифный режим](/bot/multi-tariff).
</Info>

***

## Конструктор (`classic`)

Режим по умолчанию. Пользователь самостоятельно собирает подписку из компонентов:

1. **Период** — выбор из `AVAILABLE_SUBSCRIPTION_PERIODS`
2. **Трафик** — пакет из `TRAFFIC_PACKAGES_CONFIG` или фиксированный лимит
3. **Серверы** — один или несколько (автопропуск при одном сервере)
4. **Устройства** — от `DEFAULT_DEVICE_LIMIT` до `MAX_DEVICES_LIMIT`

### Настройка периодов и цен

```env theme={null}
# Доступные периоды (дни, через запятую)
AVAILABLE_SUBSCRIPTION_PERIODS=30,90,180

# Доступные периоды для продления (могут отличаться)
AVAILABLE_RENEWAL_PERIODS=30,90,180

# Цены за каждый период (в копейках)
PRICE_14_DAYS=7000
PRICE_30_DAYS=10000
PRICE_60_DAYS=25900
PRICE_90_DAYS=36900
PRICE_180_DAYS=69900
PRICE_360_DAYS=109900

# Базовая надбавка к цене (в копейках, по умолчанию 0)
BASE_SUBSCRIPTION_PRICE=0
```

### Формула расчёта цены

```
Итого = Цена периода + Стоимость трафика × месяцев + Доп. устройства × месяцев + Доп. серверы × месяцев
```

Количество месяцев рассчитывается как `round(период_дней / 30)`, минимум 1.

**Пример для 180 дней (6 месяцев):**

| Компонент                           | Расчёт              | Итого         |
| ----------------------------------- | ------------------- | ------------- |
| Базовый период                      | `PRICE_180_DAYS`    | 699 руб       |
| Трафик безлимит                     | 200 руб/мес × 6 мес | 1 200 руб     |
| 4 устройства (3 бесплатно + 1 доп.) | 100 руб/мес × 6 мес | 600 руб       |
| 2 доп. сервера                      | 100 руб/мес × 6 мес | 1 200 руб     |
| **Итого**                           |                     | **3 699 руб** |

***

## Тарифы (`tariffs`)

Готовые тарифные планы — пользователь выбирает тариф из списка и период оплаты.

```env theme={null}
SALES_MODE=tariffs
```

Тарифы создаются и управляются в админ-панели: **Админ → Тарифы**.

### Что входит в тариф

Каждый тариф включает фиксированные параметры:

| Параметр            | Описание                                           |
| ------------------- | -------------------------------------------------- |
| Название            | Отображаемое имя (например, «Premium», «Basic»)    |
| Описание            | Текстовое описание тарифа                          |
| Трафик              | Фиксированный лимит в ГБ (0 = безлимит)            |
| Устройства          | Базовое количество устройств                       |
| Серверы             | Список доступных серверов (сквадов)                |
| Цены за периоды     | JSON: `{"30": 50000, "90": 120000, "180": 200000}` |
| Порядок отображения | Сортировка в списке (`display_order`)              |
| Уровень (tier)      | Для визуального оформления (1 = базовый)           |

### Расширенные возможности тарифов

Тарифы поддерживают дополнительные настройки, которых нет в классическом режиме:

| Возможность            | Описание                                                                                                  |
| ---------------------- | --------------------------------------------------------------------------------------------------------- |
| Докупка устройств      | `device_price_kopeks` — цена за доп. устройство, `max_device_limit` — максимум                            |
| Докупка трафика        | `traffic_topup_enabled` — разрешение, `traffic_topup_packages` — пакеты `{"5": 5000, "10": 9000}`         |
| Суточный тариф         | `is_daily` + `daily_price_kopeks` — ежедневное списание с баланса                                         |
| Произвольные дни       | `custom_days_enabled` + `price_per_day_kopeks` — покупка на любое количество дней                         |
| Произвольный трафик    | `custom_traffic_enabled` + `traffic_price_per_gb_kopeks` — покупка произвольного объёма                   |
| Режим сброса трафика   | `traffic_reset_mode` — `DAY` / `WEEK` / `MONTH` / `MONTH_ROLLING` / `NO_RESET` (или глобальная настройка) |
| Доступ по промогруппам | Ограничение тарифа для определённых промогрупп                                                            |
| Триал на тарифе        | `is_trial_available` — можно ли взять триал на этом тарифе                                                |

### Триал в режиме тарифов

```env theme={null}
# ID тарифа для триала (0 = стандартные настройки триала)
TRIAL_TARIFF_ID=0
```

Если указан ID тарифа, параметры триала (трафик, устройства, серверы) берутся из тарифа. Длительность триала всё равно определяется `TRIAL_DURATION_DAYS`.

***

## Сравнение режимов

| Возможность         |                    Конструктор                   |                Тарифы               |
| ------------------- | :----------------------------------------------: | :---------------------------------: |
| Выбор периода       |        Из `AVAILABLE_SUBSCRIPTION_PERIODS`       |      Из `period_prices` тарифа      |
| Выбор трафика       |             Пакеты или фиксированный             |         Фиксирован в тарифе         |
| Выбор серверов      |                   Из доступных                   |         Фиксированы в тарифе        |
| Выбор устройств     | От `DEFAULT_DEVICE_LIMIT` до `MAX_DEVICES_LIMIT` | Фиксированы + докупка (опционально) |
| Суточное списание   |                        Нет                       |           Да (`is_daily`)           |
| Произвольные дни    |                        Нет                       |      Да (`custom_days_enabled`)     |
| Произвольный трафик |                        Нет                       |    Да (`custom_traffic_enabled`)    |
| Скидки промогрупп   |      На период, серверы, трафик, устройства      |     На период (% от цены тарифа)    |
| Простая покупка     |                        Да                        |      Нет (и так упрощённый UX)      |

***

## Режимы продажи трафика

Применяются только в режиме конструктора (`classic`).

### Выбираемые пакеты (`selectable`)

Пользователь выбирает пакет трафика из списка.

```env theme={null}
TRAFFIC_SELECTION_MODE=selectable
TRAFFIC_PACKAGES_CONFIG="5:2000:false,10:3500:false,50:11000:true,100:15000:true,0:20000:true"
```

Формат каждого пакета: `ГБ:цена_копейки:показать_безлимит`. Значение `0` ГБ означает безлимит.

Цена безлимитного трафика также задаётся отдельно:

```env theme={null}
PRICE_TRAFFIC_UNLIMITED=20000
```

### Фиксированный лимит (`fixed`)

Все подписки создаются с одинаковым объёмом трафика.

```env theme={null}
TRAFFIC_SELECTION_MODE=fixed
FIXED_TRAFFIC_LIMIT_GB=100
```

`0` означает безлимит.

***

## Докупка трафика

Позволяет пользователю докупить трафик к существующей подписке. Трафик докупается **пакетами на 1 месяц** — при следующем сбросе трафика докупленный объём обнуляется вместе с основным.

```env theme={null}
# Включить докупку трафика
TRAFFIC_TOPUP_ENABLED=true

# Показывать кнопку в меню
BUY_TRAFFIC_BUTTON_VISIBLE=true

# Пакеты для докупки (формат: "гб:цена_копейки:enabled")
# Пустая строка = использовать TRAFFIC_PACKAGES_CONFIG
TRAFFIC_TOPUP_PACKAGES_CONFIG=10:5000:true,25:10000:true,50:15000:true,100:25000:true
```

<Note>
  Докупленный трафик действует до следующего сброса (по стратегии `traffic_reset_mode`). Это не разовая прибавка навсегда, а месячный пакет.
</Note>

<Note>
  В режиме тарифов докупка трафика настраивается индивидуально для каждого тарифа: `traffic_topup_enabled`, `traffic_topup_packages`, `max_topup_traffic_gb`.
</Note>

***

## Сброс трафика

Пользователь может сбросить счётчик использованного трафика за плату.

```env theme={null}
# Режим расчёта цены сброса
TRAFFIC_RESET_PRICE_MODE=traffic_with_purchased
```

| Режим                    | Описание                                                             |
| ------------------------ | -------------------------------------------------------------------- |
| `period`                 | Фиксированная цена = стоимость 30-дневного периода (`PRICE_30_DAYS`) |
| `traffic`                | Цена = стоимость текущего пакета трафика подписки                    |
| `traffic_with_purchased` | Цена = стоимость базового + докупленного трафика **(рекомендуется)** |

```env theme={null}
# Базовая цена сброса в копейках (0 = использовать PRICE_30_DAYS)
TRAFFIC_RESET_BASE_PRICE=0

# Глобальная стратегия сброса трафика
DEFAULT_TRAFFIC_RESET_STRATEGY=MONTH

# Сбрасывать трафик при каждой оплате подписки
RESET_TRAFFIC_ON_PAYMENT=false
```

| Стратегия       | Описание                       |
| --------------- | ------------------------------ |
| `DAY`           | Ежедневно                      |
| `WEEK`          | Еженедельно                    |
| `MONTH`         | Ежемесячно (календарный)       |
| `MONTH_ROLLING` | Ежемесячно (скользящий период) |
| `NO_RESET`      | Без сброса                     |

<Warning>
  Режим `period` может быть использован для абьюза — пользователь с дорогим трафиком будет сбрасывать его по цене 30-дневного периода. Рекомендуется `traffic_with_purchased`.
</Warning>

***

## Сброс трафика при смене тарифа

При переключении пользователя на другой тариф можно автоматически сбросить накопленный трафик:

```env theme={null}
RESET_TRAFFIC_ON_TARIFF_SWITCH=true
```

| Значение              | Описание                                                         |
| --------------------- | ---------------------------------------------------------------- |
| `true` (по умолчанию) | Счётчик трафика и докупленные пакеты обнуляются при смене тарифа |
| `false`               | Трафик сохраняется при смене тарифа                              |

Настройка управляется через **админ-панель** (Настройки → Подписки) или через `.env`. Применяется ко всем путям смены тарифа: Cabinet, Telegram-бот, MiniApp, админ-действия.

***

## Управление устройствами

### В режиме конструктора

```env theme={null}
# Базовое количество устройств (бесплатно)
DEFAULT_DEVICE_LIMIT=3

# Максимум устройств для покупки (0 = без лимита)
MAX_DEVICES_LIMIT=15

# Цена за каждое дополнительное устройство (копейки/месяц)
PRICE_PER_DEVICE=10000

# Включить выбор количества устройств
DEVICES_SELECTION_ENABLED=true

# Количество устройств, если выбор отключён (0 = не назначать)
DEVICES_SELECTION_DISABLED_AMOUNT=0
```

Если `DEVICES_SELECTION_ENABLED=false`, пользователь не видит шаг выбора устройств. Всем подпискам назначается `DEVICES_SELECTION_DISABLED_AMOUNT` устройств (или `DEFAULT_DEVICE_LIMIT`, если `0`).

### В режиме тарифов

Базовое количество устройств задаётся в тарифе (`device_limit`). Если `device_price_kopeks` указан — пользователь может докупить устройства до `max_device_limit`.

<Note>
  При уменьшении количества устройств минимум всегда равен 1. Поле `device_limit` тарифа определяет количество устройств, включённых при покупке, но не нижнюю границу уменьшения.
</Note>

<Note>
  Списки устройств пользователя из панели теперь полностью пагинированы (`get_user_devices_all`), что гарантирует корректное удаление всех лишних устройств при уменьшении лимита.
</Note>

***

## Простая покупка подписки

Упрощённый режим — кнопка «Купить подписку» без пошагового конструктора. Все параметры фиксированы.

```env theme={null}
SIMPLE_SUBSCRIPTION_ENABLED=true

# Фиксированный период (должен быть в AVAILABLE_SUBSCRIPTION_PERIODS)
SIMPLE_SUBSCRIPTION_PERIOD_DAYS=30

# Фиксированное количество устройств
SIMPLE_SUBSCRIPTION_DEVICE_LIMIT=1

# Фиксированный трафик в ГБ (0 = безлимит)
SIMPLE_SUBSCRIPTION_TRAFFIC_GB=0

# UUID сквада (пустой = серверы по умолчанию)
# SIMPLE_SUBSCRIPTION_SQUAD_UUID=
```

<Note>
  Простая покупка доступна только в режиме конструктора (`classic`). В режиме тарифов UX и так упрощён — пользователь просто выбирает тариф и период.
</Note>

***

## Тестовая подписка (триал)

```env theme={null}
TRIAL_DURATION_DAYS=3
TRIAL_TRAFFIC_LIMIT_GB=10
TRIAL_DEVICE_LIMIT=1

# Платный триал (0 = бесплатно)
TRIAL_PAYMENT_ENABLED=false
TRIAL_ACTIVATION_PRICE=0

# Плюсовать оставшиеся дни триала при покупке платной подписки
TRIAL_ADD_REMAINING_DAYS_TO_PAID=false
```

Триал включает:

* Настраиваемый период, трафик, устройства
* Уведомления об истечении
* Автоконверсию в платную подписку
* Контроль обязательной подписки на канал
* Платный триал с выбором способа оплаты (если `TRIAL_ACTIVATION_PRICE > 0`)

***

## Промогруппы и скидки

Промогруппы — основной инструмент скидочной системы. У каждого пользователя может быть несколько промогрупп. Для расчёта скидки используется группа с **наивысшим приоритетом**.

### Параметры промогруппы

| Параметр                         | Описание                                                        |
| -------------------------------- | --------------------------------------------------------------- |
| `name`                           | Название группы                                                 |
| `priority`                       | Приоритет (чем выше — тем важнее)                               |
| `server_discount_percent`        | Скидка на серверы (%)                                           |
| `traffic_discount_percent`       | Скидка на трафик (%)                                            |
| `device_discount_percent`        | Скидка на устройства (%)                                        |
| `period_discounts`               | Скидки за длительные периоды: `{"60": 10, "90": 20, "180": 40}` |
| `auto_assign_total_spent_kopeks` | Авто-назначение при достижении суммы трат (лояльность)          |
| `apply_discounts_to_addons`      | Применять скидки к доп. услугам                                 |

### Как скидки работают в режиме конструктора

В конструкторе каждый компонент стоимости дисконтируется отдельно:

1. **Цена периода** — скидка `period_discounts[период_дней]`%
2. **Трафик** — скидка `traffic_discount_percent`%
3. **Устройства** — скидка `device_discount_percent`%
4. **Серверы** — скидка `server_discount_percent`%

```
Итого = (Период - скидка) + (Трафик - скидка) + (Устройства - скидка) + (Серверы - скидка)
```

**Пример:** промогруппа «VIP» со скидкой 20% на серверы, 15% на трафик, 10% на устройства:

| Компонент         | Без скидки                    | Скидка       | Итого         |
| ----------------- | ----------------------------- | ------------ | ------------- |
| Период 90 дней    | 369 руб                       | 0%           | 369 руб       |
| Трафик 100 ГБ     | 150 руб/мес × 3 мес = 450 руб | 15% = 67 руб | 383 руб       |
| 2 доп. устройства | 100 руб/мес × 3 мес = 300 руб | 10% = 30 руб | 270 руб       |
| 1 сервер          | 100 руб/мес × 3 мес = 300 руб | 20% = 60 руб | 240 руб       |
| **Итого**         | **1 419 руб**                 | **157 руб**  | **1 262 руб** |

### Как скидки работают в режиме тарифов

В тарифном режиме промогруппа применяет **скидку за период** (`period_discounts`) к итоговой цене тарифа:

```
Итого = Цена тарифа за период - period_discounts[период]%
```

Скидки на серверы, трафик и устройства в режиме тарифов **не применяются** — эти параметры фиксированы и уже включены в цену тарифа.

**Пример:** тариф «Premium» стоит 5 000 руб/90 дней. Промогруппа с `period_discounts: {"90": 20}`:

```
5 000 руб - 20% = 4 000 руб
```

### Скидки за период для базовых пользователей

Для пользователей **без промогруппы** (или в группе `is_default=true`) можно включить скидки за длительные периоды через `.env`:

```env theme={null}
# Включить скидки за период для базовых пользователей
BASE_PROMO_GROUP_PERIOD_DISCOUNTS_ENABLED=false

# Формат: период_дней:процент_скидки
BASE_PROMO_GROUP_PERIOD_DISCOUNTS=60:10,90:20,180:40,360:70
```

Эти скидки **не работают** для пользователей в именных промогруппах — у тех свои `period_discounts`.

<Warning>
  `BASE_PROMO_GROUP_PERIOD_DISCOUNTS` применяются как скидка ко **всем компонентам** (серверы, трафик, устройства), если у базовой группы `server_discount_percent`, `traffic_discount_percent` и `device_discount_percent` равны 0. Это сделано для упрощения — базовые пользователи получают единую скидку за длительный период.
</Warning>

### Система лояльности

Промогруппы автоматически назначаются по сумме трат:

```
Промогруппа «Silver» → auto_assign_total_spent_kopeks = 500000  (5 000 руб)
Промогруппа «Gold»   → auto_assign_total_spent_kopeks = 1500000 (15 000 руб)
Промогруппа «VIP»    → auto_assign_total_spent_kopeks = 5000000 (50 000 руб)
```

При достижении порога пользователь автоматически повышается на следующий уровень.

### Приоритет промогрупп

Пользователь может состоять в нескольких промогруппах (например, назначенная вручную + за лояльность). Для расчёта скидки используется группа с **наивысшим `priority`**. При одинаковом приоритете — группа с большим `id`.

### Ограничение тарифов по промогруппам

В режиме тарифов можно ограничить доступ к тарифу по промогруппам. Если у тарифа задан список `allowed_promo_groups` — его увидят только пользователи из этих групп. Если список пуст — тариф доступен всем.

***

## Модем-подписки

Дополнительный функционал для устройств-модемов.

```env theme={null}
# Включить модем-функционал
MODEM_ENABLED=false

# Цена модема за месяц (копейки)
MODEM_PRICE_PER_MONTH=10000

# Скидки за длительный срок: "месяцев:процент"
MODEM_PERIOD_DISCOUNTS=3:15,6:20,12:25
```

***

## Сквозная корзина

Система сохраняет выбранные параметры при недостатке баланса:

1. Пользователь выбирает подписку
2. Баланса не хватает — корзина сохраняется в Redis (TTL 1 час)
3. Пользователь пополняет баланс
4. Кнопка «Вернуться к оформлению» восстанавливает корзину
5. Подписка оформляется автоматически

<Note>
  Для работы корзины требуется Redis: `REDIS_URL=redis://redis:6379/0`
</Note>

***

## Автопродление

Бот автоматически продлевает подписки при наличии средств на балансе:

* Фоновый сервис мониторинга проверяет подписки по расписанию
* За N дней до истечения — уведомление пользователю
* При наличии средств — автоматическое продление
* При недостатке — уведомление о необходимости пополнения
* Поддержка настройки дня списания

***

## Промо-коды

| Тип          | Описание                          |
| ------------ | --------------------------------- |
| Деньги       | Начисляет сумму на баланс         |
| Дни подписки | Продлевает подписку на N дней     |
| Триал        | Выдаёт пробную подписку           |
| Промогруппа  | Назначает промогруппу со скидками |
