> ## 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` | Готовые тарифные планы с фиксированными параметрами |

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

***

## Конструктор (`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]
>
> В режиме тарифов докупка трафика настраивается индивидуально для каждого тарифа: `traffic_topup_enabled`, `traffic_topup_packages`, `max_topup_traffic_gb`.

***

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

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

```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`.

***

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

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

```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`.

```env theme={null}
# Разрешить опускать лимит устройств ниже включённого в тариф
ALLOW_DEVICES_BELOW_TARIFF_LIMIT=false
```

> [!NOTE]
>
> С `v3.67.0` нижняя граница уменьшения по умолчанию равна `device_limit` тарифа — оплаченные устройства нельзя «потерять», уменьшив лимит. `ALLOW_DEVICES_BELOW_TARIFF_LIMIT=true` возвращает прежнее поведение, при котором лимит можно опустить вплоть до 1 устройства.

> [!WARNING]
>
> `device_limit = 0` означает **безлимит**, а не «ноль устройств» — так это и показывается пользователю, и в админке (`v3.66.0`).

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

***

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

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

```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 и так упрощён — пользователь просто выбирает тариф и период.

***

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

```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`)

> [!NOTE]
>
> `TRIAL_ADD_REMAINING_DAYS_TO_PAID` действует **только** при `TARIFF_SWITCH_RESET_FREE_DAYS=false`: сброс бесплатного периода перебивает перенос дней. См. [Сброс бесплатного периода](/bot/tariffs.md#сброс-бесплатного-периода).

> [!NOTE]
>
> При покупке платной подписки истёкший триал **того же тарифа переиспользуется**, а не дублируется новой строкой (`v3.62.0`) — и в боте, и при гостевой покупке с лендинга. В мультитарифном режиме живой триал конвертируется в платную подписку (`v3.65.0`).
>
> Неоплаченный черновик триала не показывается пользователю как подписка (`v3.67.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. Это сделано для упрощения — базовые пользователи получают единую скидку за длительный период.

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

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

```
Промогруппа «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`

***

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

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

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

***

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

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


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.