> ## 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=tariffs` (по умолчанию). В отличие от режима конструктора, пользователь выбирает тариф целиком, а не собирает параметры по шагам.

> [!NOTE]
>
> Управление тарифами доступно из **админ-панели бота** в Telegram и через **Cabinet Admin API**. При первом запуске бот автоматически создает тариф "Стандартный" с ценами из `.env`.

## Создание тарифа

### Через админ-панель бота

Мастер создания (6 шагов):

**Шаг 1: Название**

* 2-50 символов
* Примеры: "Базовый", "Премиум", "Бизнес"

**Шаг 2: Трафик**

* Целое число в ГБ
* `0` = безлимитный трафик

**Шаг 3: Устройства**

* Количество устройств (минимум 1)

**Шаг 4: Уровень тарифа (tier)**

* Число от 1 до 10
* Влияет только на визуальное отображение (1 = базовый)

**Шаг 5: Тип тарифа**

* **Периодический** — оплата за период (30/90/180/360 дней)
* **Суточный** — ежедневное списание

**Шаг 6: Цены**

* Для периодического: формат `дни:цена_в_копейках` через запятую
  ```
  30:9900, 90:24900, 180:44900, 360:79900
  ```
* Для суточного: цена в рублях за день (например, `50` или `99.90`)

После создания тариф сразу активен и доступен пользователям.

### Через Cabinet Admin API

```
POST /cabinet/admin/tariffs
```

Принимает JSON со всеми полями тарифа.

## Поля тарифа

### Основные

| Поле | Тип | Описание |
| - | - | - |
| `name` | строка | Название (2-50 символов) |
| `description` | текст | Описание (отображается пользователю) |
| `display_order` | число | Порядок сортировки (меньше = выше в списке) |
| `is_active` | bool | Активен ли тариф |
| `tier_level` | 1-10 | Визуальный уровень |

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

| Поле | Тип | Описание |
| - | - | - |
| `traffic_limit_gb` | число | Лимит трафика (0 = безлимит) |
| `device_limit` | число | Количество устройств |
| `device_price_kopeks` | число/null | Цена доп. устройства. `null` = покупка недоступна |
| `max_device_limit` | число/null | Максимум устройств. `null` = без ограничения |

### Серверы

| Поле | Тип | Описание |
| - | - | - |
| `allowed_squads` | JSON список | UUID доступных серверов. Пустой = все серверы |

### Ценообразование

| Поле | Тип | Описание |
| - | - | - |
| `period_prices` | JSON объект | Цены за периоды: `{"30": 9900, "90": 24900}` (дни: копейки) |

### Суточный тариф

| Поле | Тип | Описание |
| - | - | - |
| `is_daily` | bool | Суточный режим (устанавливается при создании) |
| `daily_price_kopeks` | число | Цена за сутки в копейках |

> [!WARNING]
>
> Тип тарифа (периодический/суточный) выбирается **при создании** и не может быть изменен после.

### Пользовательский выбор дней

| Поле | Тип | Описание |
| - | - | - |
| `custom_days_enabled` | bool | Разрешить выбор произвольного срока |
| `price_per_day_kopeks` | число | Цена за день |
| `min_days` | число | Минимальный срок (по умолчанию 1) |
| `max_days` | число | Максимальный срок (по умолчанию 365) |

При включении пользователь видит кнопки +/- для выбора срока вместо фиксированных периодов. Цена = `price_per_day_kopeks * количество_дней`.

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

| Поле | Тип | Описание |
| - | - | - |
| `custom_traffic_enabled` | bool | Разрешить выбор объема трафика |
| `traffic_price_per_gb_kopeks` | число | Цена за 1 ГБ |
| `min_traffic_gb` | число | Минимум ГБ (по умолчанию 1) |
| `max_traffic_gb` | число | Максимум ГБ (по умолчанию 1000) |

Стоимость трафика прибавляется к стоимости периода:

```
итого = цена_периода + (цена_за_гб * количество_гб)
```

> [!NOTE]
>
> С `v3.67.0` эти поля редактируются и из админ-панели бота, а не только через Cabinet Admin API.

> [!WARNING]
>
> Следите за границами: `min_traffic_gb` больше `max_traffic_gb` или нулевая цена за гигабайт делают покупку возможной вне заданного диапазона или по нулевой цене. Валидация в кабинете эту комбинацию пропускает.

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

| Поле | Тип | Описание |
| - | - | - |
| `traffic_topup_enabled` | bool | Разрешить докупку трафика на этом тарифе |
| `traffic_topup_packages` | JSON объект | Пакеты: `{"5": 5000, "10": 9000}` (ГБ: копейки) |
| `max_topup_traffic_gb` | число | Максимум докупленного трафика (0 = без лимита) |

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

| Поле | Тип | Описание |
| - | - | - |
| `traffic_reset_mode` | строка/null | `DAY`, `WEEK`, `MONTH`, `NO_RESET` или `null` (глобальная настройка) |

### Триал

| Поле | Тип | Описание |
| - | - | - |
| `is_trial_available` | bool | Использовать для триальных подписок |

> [!NOTE]
>
> Только **один** тариф может быть триальным. При назначении тарифа триальным флаг автоматически снимается со всех остальных.

## Ограничение по промо-группам

Тарифы можно ограничить для определенных промо-групп:

* Если у тарифа **нет** привязанных промо-групп — он доступен **всем**
* Если у тарифа **есть** привязанные промо-группы — он виден **только** пользователям из этих групп

Настройка через кнопку **Промо-группы** на странице тарифа в админке. Можно включать/выключать отдельные группы.

## Редактирование

На странице тарифа в админке доступны кнопки для редактирования каждого поля:

| Действие | Описание |
| - | - |
| Название | Изменить название (2-50 символов) |
| Описание | Изменить или удалить описание |
| Трафик | Изменить лимит (0 = безлимит) |
| Устройства | Изменить количество |
| Цены | Изменить цены за периоды (только для периодических) |
| Уровень | Изменить tier (1-10) |
| Цена устройства | Цена доп. устройства (0 = отключить) |
| Макс. устройств | Максимальный лимит (0 = без ограничения) |
| Докупка трафика | Включить/выключить, настроить пакеты и лимит |
| Сброс трафика | Выбрать режим (глобальный/день/неделя/месяц/без сброса) |
| Серверы | Выбрать доступные серверы |
| Промо-группы | Ограничить доступ по промо-группам |
| Суточная цена | Изменить цену за сутки (только для суточных) |
| Сделать триальным | Назначить для триальных подписок |
| Активировать/Деактивировать | Скрыть или показать тариф |
| Удалить | Удалить с подтверждением |

## Сортировка

Тарифы отображаются в порядке `display_order` (по возрастанию). Для изменения порядка:

* **Cabinet Admin API**: `PUT /cabinet/admin/tariffs/order` с массивом ID в нужном порядке
* **Админ-панель бота**: редактирование `display_order` каждого тарифа

## Удаление

При удалении тарифа:

1. Показывается предупреждение с количеством активных подписок на этом тарифе
2. Требуется подтверждение
3. Подписки **не удаляются** — у них обнуляется привязка к тарифу (`tariff_id = NULL`)

## Отображение пользователю

В списке тарифов пользователь видит:

* Название тарифа
* Трафик и количество устройств
* Минимальную цену (с учетом скидок промо-группы)

Примеры:

```
Базовый — 100ГБ / 3 устройства от 179₽
Премиум — Безлимит / 10 устройств от 499₽
Суточный — 50ГБ / 1 устройство 50₽/день
```

### Скидки промо-групп

Если пользователь состоит в промо-группе со скидками за период, скидки отображаются на кнопках выбора периода:

```
30 дней — 179₽
90 дней — 449₽ -15%
180 дней — 799₽ -25%
```

## Формула цены

### Периодический тариф

```
итого = цена_периода - скидка_промогруппы
```

С пользовательским выбором дней:

```
итого = (цена_за_день * дни) - скидка
```

С пользовательским выбором трафика:

```
итого = цена_периода + (цена_за_гб * гб) - скидка
```

### Суточный тариф

```
ежедневное_списание = daily_price_kopeks
```

Списание происходит автоматически каждый день. При недостатке баланса подписка ставится на паузу (`is_daily_paused`).

## Синхронизация цен

После любого изменения тарифов (создание, редактирование, удаление, переключение активности) глобальный словарь `PERIOD_PRICES` автоматически пересчитывается из БД. Перезапуск бота не требуется.

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

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

| Переменная | По умолчанию | Описание |
| - | - | - |
| `TARIFF_SWITCH_UPGRADE_ENABLED` | `true` | Разрешить переход на более дорогой тариф (апгрейд) |
| `TARIFF_SWITCH_DOWNGRADE_ENABLED` | `true` | Разрешить переход на более дешёвый тариф (даунгрейд) |

Сравнение цен тарифов происходит по стоимости за 30-дневный период. Возможные комбинации:

| Апгрейд | Даунгрейд | Поведение |
| - | - | - |
| `true` | `true` | Пользователь может переключиться на любой тариф |
| `true` | `false` | Только переход на более дорогой тариф |
| `false` | `true` | Только переход на более дешёвый тариф |
| `false` | `false` | Смена тарифа полностью отключена |

> [!NOTE]
>
> При смене тарифа оставшиеся дни подписки пересчитываются пропорционально разнице в стоимости.

### Сброс бесплатного периода

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

Мастер-переключатель сброса бесплатного периода при переходе на платный тариф.

* `true` — остаток **не переносится** ни для триала, ни для 0₽-тарифа, даже если `TRIAL_ADD_REMAINING_DAYS_TO_PAID=true`: сброс перебивает перенос. Смена с 0₽-тарифа при этом идёт через покупку периода по полной цене — мгновенное prorated-переключение недоступно, иначе доплата считалась бы за весь накопленный остаток бесплатных дней.
* `false` — перенос разрешён; для триалов тогда действует `TRIAL_ADD_REMAINING_DAYS_TO_PAID`.

> [!WARNING]
>
> До `v3.63.0` эта переменная не сбрасывала бесплатный период фактически — наспамленные на бесплатном тарифе дни уносились на платный. Если вы полагались на прежнее поведение, проверьте настройку после обновления.

## Прямая оплата тарифа

С `v3.67.0` тариф можно оплатить напрямую, без отдельного шага пополнения баланса: пользователь выбирает тариф и сразу попадает на выбор способа оплаты.

Смежные улучшения:

* на экране «недостаточно средств» при продлении сразу показываются способы оплаты (`v4.1.0`);
* если в системе доступен ровно один тариф, экран его выбора пропускается (`v4.1.0`);
* превью цены считается **тем же** `PricingEngine`, что и подтверждение покупки (`v3.67.0`) — раньше расходились, и «итого 100 ₽» при балансе 100 ₽ давало «недостаточно средств»;
* цена за месяц корректно считается для коротких и некратных периодов (`v3.67.0`).

## Связанные настройки

| Переменная | Описание |
| - | - |
| `SALES_MODE` | `tariffs` (по умолчанию) или `classic` |
| `TRIAL_TARIFF_ID` | ID триального тарифа (устаревшее, используйте `is_trial_available`) |
| `TARIFF_SWITCH_UPGRADE_ENABLED` | Разрешить апгрейд тарифа |
| `TARIFF_SWITCH_DOWNGRADE_ENABLED` | Разрешить даунгрейд тарифа |


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