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

# Промо-система

> Промокоды, промо-группы и персональные предложения

## Обзор

Промо-система включает три связанных механизма:

1. **Промокоды** — одноразовые/многоразовые коды для бонусов
2. **Промо-группы** — тарифные группы со скидками и приоритетами
3. **Персональные промо-предложения** — адресные акции от администратора

## Промокоды

### 6 типов промокодов

| Тип | Что дает |
| - | - |
| **balance** | Начисление на баланс (в копейках) |
| **subscription\_days** | Продление/создание платной подписки на N дней |
| **balance\_and\_days** | Комбинированный набор: баланс, дни и трафик одним кодом |
| **trial\_subscription** | Активация триальной подписки |
| **promo\_group** | Назначение в промо-группу |
| **discount** | Разовая процентная скидка на следующую покупку |

### Комбинированный промокод

Тип `balance_and_days` появился в `v3.65.0` как «баланс + дни», а в `v4.1.0` к набору добавился трафик. Набор задаётся тремя независимыми полями:

| Поле | Что делает | Нейтральное значение |
| - | - | - |
| `balance_bonus_kopeks` | Начисление на баланс | `0` |
| `subscription_days` | Дни к подписке | `0` |
| `traffic_gb` | Гигабайты к подписке | `0` |

Достаточно любого одного ненулевого поля — код только с трафиком или только с балансом валиден.

> [!WARNING]
>
> Тип `balance_and_days` **не означает** «есть и баланс, и дни»: он стоит у любого набора, включая набор с одним лишь трафиком. Ориентируйтесь на значения полей, а не на название типа.

> [!NOTE]
>
> Трафик добавляется к уже существующей подписке без смены тарифа. В мультитарифном режиме целевая подписка выбирается **один раз за активацию** — дни и гигабайты всегда попадают в одну и ту же подписку.
>
> Порядок начисления фиксирован: сначала всё, что трогает подписку, потом баланс. Пополнение баланса коммитится внутри себя, а подписочные шаги могут прерваться с откатом активации — при обратном порядке неудачная активация всё равно подарила бы деньги.

### Создание промокода (админ)

1. Выбор типа (5 вариантов)
2. Ввод кода (3-20 символов, `[A-Za-z0-9_-]`, уникальный)
3. Параметры в зависимости от типа:
   * **Balance**: сумма в рублях (1-10 000)
   * **Days / Trial**: дни (1-3650)
   * **Discount**: процент (1-100)
   * **Group**: выбор из существующих промо-групп
4. Максимум использований (0 = безлимит)
5. Срок действия в днях (0 = бессрочно)
6. Для discount: длительность скидки в часах (0 = до первой покупки)

### Активация пользователем

Пользователь вводит код через кнопку "Промокод" в главном меню.

**Проверки перед активацией:**

1. Формат кода: 3-50 символов, `[A-Za-z0-9_-]`
2. Rate limiting: максимум 5 неудачных попыток за 5 минут
3. Дневной лимит: максимум 5 успешных активаций за 24 часа
4. Код существует, активен, не исчерпан
5. Пользователь еще не использовал этот код
6. Для `first_purchase_only` — пользователь не имел платной подписки

### Эффекты при активации

| Тип | Действие |
| - | - |
| **balance** | Начисление на баланс |
| **subscription\_days** | Если есть подписка — продление. Если нет — создание новой платной подписки с синхронизацией в RemnaWave |
| **balance\_and\_days** | Дни и трафик — в одну выбранную подписку, затем начисление на баланс |
| **trial\_subscription** | Создание триальной подписки (только если нет активной) |
| **promo\_group** | Назначение в указанную промо-группу |
| **discount** | Установка `promo_offer_discount_percent` на пользователе. Ошибка, если уже есть активная скидка |

> [!NOTE]
>
> Если у промокода любого типа указан promo\_group\_id, промо-группа назначается дополнительно при активации.

### Управление (админ-панель)

* Список промокодов с пагинацией (10 на страницу)
* Детали: тип, статус, использования, срок
* Редактирование: срок, максимум использований, сумма/дни
* Переключение `first_purchase_only`
* Статистика: общее число, сегодня, история активаций с именами
* Удаление с подтверждением

## Промо-группы

### Что такое промо-группы

Промо-группы — это тарифные уровни, определяющие скидки для пользователей. Каждый пользователь может состоять в нескольких группах, а основная определяется по приоритету (чем выше — тем важнее).

### Типы скидок

| Категория | Поле | Описание |
| - | - | - |
| Серверы | `server_discount_percent` | Скидка на серверы (0-100%) |
| Трафик | `traffic_discount_percent` | Скидка на трафик (0-100%) |
| Устройства | `device_discount_percent` | Скидка на устройства (0-100%) |
| Период | `period_discounts` | JSON: скидка за длительный период |

Пример period\_discounts: `{60: 10, 90: 20, 180: 40, 360: 70}` — 10% за 60 дней, 70% за год.

### Скидки для базовой группы

Для пользователей без промо-группы (или в группе по умолчанию) доступны базовые скидки за период:

```env theme={null}
BASE_PROMO_GROUP_PERIOD_DISCOUNTS_ENABLED=true
BASE_PROMO_GROUP_PERIOD_DISCOUNTS=60:10,90:20,180:40,360:70
```

### Ограничение серверов

Промо-группы могут ограничивать доступные серверы через связь с server\_squads. Если привязаны серверы — пользователь видит только их.

### Скидки на аддоны

Флаг `apply_discounts_to_addons` (по умолчанию true) определяет, применяются ли скидки группы к дополнительным покупкам (трафик, устройства).

### Автоматическое назначение по тратам

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

1. После каждого платежа проверяется порог
2. Система находит лучшую подходящую группу (порог меньше или равен тратам пользователя)
3. Группа добавляется (не заменяет другие)
4. Администратор получает уведомление

Пример: Промо-группа "VIP" с порогом 50 000 коп. (500 руб.) — автоматически назначается после траты 500+ рублей.

### Управление (админ-панель)

* **Создание**: название, приоритет, скидки (трафик, серверы, устройства), период-скидки, авто-порог
* **Редактирование**: все поля, переключение скидок на аддоны
* **Просмотр участников** с пагинацией
* **Удаление**: с подтверждением, участники переносятся в группу по умолчанию. Группу по умолчанию удалить нельзя.

## Персональные промо-предложения

### Типы предложений

| Тип | Описание | Сегменты |
| - | - | - |
| **test\_access** | Временный доступ к тестовым серверам | Платные активные, триальные активные |
| **extend\_discount** | Скидка на продление подписки | Платные активные |
| **purchase\_discount** | Скидка на покупку подписки | Платные/триальные истекшие, триальные активные |

### Как работают

1. Администратор выбирает шаблон предложения
2. Выбирает сегмент пользователей или конкретного пользователя
3. Система создает индивидуальные записи DiscountOffer с ограниченным сроком действия
4. Пользователям отправляется сообщение с кнопкой CTA
5. Пользователь нажимает кнопку и предложение активируется

### Тестовый доступ к серверам

Предложение test\_access:

1. Указываются серверы (UUID) и длительность (часы)
2. При активации создаются записи SubscriptionTemporaryAccess
3. Серверы добавляются в подписку пользователя и синхронизируются с RemnaWave
4. По истечении срока доступ автоматически убирается фоновой задачей

### Шаблоны предложений

Шаблоны содержат:

* Текст сообщения (с плейсхолдерами: `{discount_percent}`, `{valid_hours}`, `{test_duration_hours}`, `{server_name}`)
* Текст кнопки
* Срок действия предложения (часы)
* Процент скидки или длительность тестового доступа
* Список серверов (для test\_access)

Все шаблоны редактируются из админ-панели.

### Отправка предложений

**По сегменту:**

* Выбор шаблона, выбор сегмента, массовая отправка
* Для test\_access: фильтруются пользователи, уже подключенные к целевому серверу
* Батчевая отправка: 100 за раз, параллелизм до 20

**Индивидуально:**

* Поиск пользователя, детальный профиль, подтверждение, отправка

**Охват и прогресс** (`v3.67.0`): перед отправкой видно, сколько пользователей попадает в сегмент, а во время отправки — прогресс доставки.

**Доставка на email** (`v3.65.0`): пользователи без Telegram (зарегистрированные в кабинете по email) получают промопредложения письмом. Такие письма считаются маркетинговыми и подчиняются [отписке](/getting-started/smtp-setup.md#отписка-от-маркетинговых-писем) и тумблеру `promo_offers_enabled` в настройках пользователя.

### Журнал операций

Все действия логируются в PromoOfferLog:

* claimed — пользователь принял предложение
* consumed — предложение использовано
* disabled — предложение деактивировано (вручную или по истечении)

## Настройка

| Переменная | По умолчанию | Описание |
| - | - | - |
| `BASE_PROMO_GROUP_PERIOD_DISCOUNTS_ENABLED` | `false` | Скидки за период для базовой группы |
| `BASE_PROMO_GROUP_PERIOD_DISCOUNTS` | `60:10,90:20,180:40,360:70` | Формат: дни и процент через запятую |


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