docs: document tariff catalog

This commit is contained in:
3252a8
2026-04-28 14:34:25 +03:00
parent 829758dc44
commit 338008bbd0
+44 -102
View File
@@ -1,125 +1,67 @@
# Тарифы
# Тарифы 2.0
В Minishop два режима тарификации, и они **взаимоисключающие**: бот в каждый момент времени продаёт либо подписку на срок (1 / 3 / 6 / 12 месяцев), либо пакеты трафика. Ниже — что именно настраивается, что происходит при покупке и как переключаться.
Бот поддерживает каталог тарифов в JSON-файле. Путь задается через `TARIFFS_CONFIG_PATH`, по умолчанию `config/tariffs.json`.
## Краткое сравнение
Если файл отсутствует, включается legacy fallback: используются старые `.env` поля `RUB_PRICE_*`, `STARS_PRICE_*`, `USER_TRAFFIC_LIMIT_GB`, `USER_SQUAD_UUIDS`, а также старый режим `TRAFFIC_PACKAGES`.
| | Подписка по времени | Пакеты трафика |
| --- | --- | --- |
| Что покупает пользователь | Период доступа (1/3/6/12 мес.) | Объём трафика в ГБ |
| Срок действия в панели | До конца купленного периода | До 01.01.2099 (фактически бессрочно) |
| Лимит трафика | Из `USER_TRAFFIC_LIMIT_GB`, сбрасывается по `USER_TRAFFIC_STRATEGY` | Кумулятивно: каждый платёж **прибавляет** ГБ к лимиту |
| Стратегия сброса | `NO_RESET` / `WEEK` / `MONTH` / `DAY` | Принудительно `NO_RESET` |
| Автопродление YooKassa | Поддерживается | Выключено |
| Реферальные бонусы | Доступны (бонусные дни) | Не выдаются |
| Триал | Доступен (`TRIAL_*`) | Доступен (включается отдельно) |
## Конфиг
Бот определяет режим по наличию переменных `TRAFFIC_PACKAGES` или `STARS_TRAFFIC_PACKAGES`: если хотя бы одна из них непустая — включается режим продажи трафика и переменные `*_MONTHS_ENABLED` / `RUB_PRICE_*` игнорируются.
См. пример: [`config/tariffs.example.json`](../config/tariffs.example.json).
## Режим «Подписка по времени» (по умолчанию)
Основные поля:
Используется, когда `TRAFFIC_PACKAGES` и `STARS_TRAFFIC_PACKAGES` пусты.
### Цены и периоды
Для каждого периода настраивается доступность и две цены — в основной валюте (`DEFAULT_CURRENCY_SYMBOL`, по умолчанию RUB) и в Telegram Stars:
| Переменная | Описание |
| Поле | Описание |
| --- | --- |
| `1_MONTH_ENABLED` / `3_MONTHS_ENABLED` / `6_MONTHS_ENABLED` / `12_MONTHS_ENABLED` | `true`/`false` — показывать ли период в выборе |
| `RUB_PRICE_1_MONTH` / `RUB_PRICE_3_MONTHS` / `RUB_PRICE_6_MONTHS` / `RUB_PRICE_12_MONTHS` | Цена в рублях. Если не задана — кнопка не появится |
| `STARS_PRICE_1_MONTH` / `STARS_PRICE_3_MONTHS` / `STARS_PRICE_6_MONTHS` / `STARS_PRICE_12_MONTHS` | Цена в Stars. Используется, когда оплата идёт через `STARS_ENABLED` |
| `default_tariff` | Тариф для миграции существующих подписок и выбора по умолчанию |
| `topup_packages_default` | Пакеты докупки для period-тарифов без собственных пакетов |
| `tariffs[].billing_model` | `period` или `traffic` |
| `tariffs[].squad_uuids` | Internal squads Remnawave для тарифа |
| `prices_rub` / `prices_stars` | Цены period-тарифов по месяцам |
| `traffic_packages` | Пакеты GB для traffic-тарифов |
Если `RUB_PRICE_*` равно `0` — соответствующий период просто скрывается.
## Period-Тариф
### Трафик пользователя
Period-тариф продает доступ на срок и личный 30-дневный лимит трафика.
Лимит трафика и стратегия его сброса настраиваются глобально и применяются ко всем платным пользователям (в том числе при продлении):
- `monthly_gb` превращается в `tier_baseline_bytes`.
- Докупленные пакеты хранятся в `topup_balance_bytes`.
- В Remnawave пушится `trafficLimitBytes = tier_baseline_bytes + topup_balance_bytes`.
- При продлении до окончания подписки дата 30-дневного сброса не меняется.
- При покупке после лапса новый период начинается с момента оплаты.
| Переменная | Описание |
| --- | --- |
| `USER_TRAFFIC_LIMIT_GB` | Лимит трафика в ГБ. `0` — безлимит |
| `USER_TRAFFIC_STRATEGY` | Когда сбрасывается счётчик: `NO_RESET`, `DAY`, `WEEK`, `MONTH` |
## Traffic-Тариф
### Что происходит при оплате
Traffic-тариф продает GB без срока действия.
1. Если у пользователя ещё нет активной подписки стартовая дата = «сейчас». Если есть — новая длительность прибавляется к её `end_date` (продление, а не перезапись).
2. К итогу могут добавиться бонусные дни промокода (`promo_codes`) и реферальной программы (`REFERRAL_BONUS_DAYS_*`, `REFEREE_BONUS_DAYS_*`).
3. В панели Remnawave у пользователя обновляются `expireAt`, `trafficLimitBytes` и `trafficLimitStrategy` под текущие настройки.
- `end_date` технически ставится в `2099-01-01 UTC`.
- `period_start_at = NULL`.
- `trafficLimitStrategy = NO_RESET`.
- Новая покупка добавляет GB к фактическому остатку: `limit = used + remaining + purchased`.
- Доступ ограничивается только при исчерпании купленного трафика.
### Автопродление (только YooKassa)
## Смена Тарифа
Включается через `YOOKASSA_AUTOPAYMENTS_ENABLED=true`. Если включено и пользователь сохранил карту, то за `SUBSCRIPTION_NOTIFY_DAYS_BEFORE` дней до окончания бот пытается списать сумму, равную `RUB_PRICE_*` для длительности из последнего платежа. См. `YOOKASSA_AUTOPAYMENTS_REQUIRE_CARD_BINDING` для управления чекбоксом «сохранить карту».
Смена пишется в `tariff_changes`.
В режиме трафика автопродление принудительно выключается, даже если YooKassa-настройки разрешают его.
- `period -> period`: расчет идет от `effective_monthly_price_rub`; пересчет дней использует `floor`.
- `period -> traffic`: остаток оплаченных дней конвертируется в GB по `conversion_rate_rub_per_gb` или минимальной цене GB в RUB-пакетах.
- `traffic -> period`: пользователь покупает период, а остаток GB сохраняется как топ-ап поверх нового тарифа.
### Уведомления
## Платежи
Управляются `SUBSCRIPTION_NOTIFICATIONS_ENABLED`, `SUBSCRIPTION_NOTIFY_ON_EXPIRE`, `SUBSCRIPTION_NOTIFY_AFTER_EXPIRE`, `SUBSCRIPTION_NOTIFY_DAYS_BEFORE`. Работают по `end_date` подписки — поэтому в режиме трафика, где `end_date` всегда «2099», они эффективно не отправляются.
Новые платежи сохраняют:
## Режим «Пакеты трафика»
- `sale_mode`: `subscription`, `traffic_package`, `topup`, `tariff_upgrade`;
- `tariff_key`;
- `purchased_gb` для GB-покупок.
Включается заданием переменной `TRAFFIC_PACKAGES` (и/или `STARS_TRAFFIC_PACKAGES`) в `.env`.
Legacy поле `subscription_duration_months` остается для совместимости.
### Формат
## Воркеры
```env
# Пакеты в основной валюте (RUB), формат "<GB>:<цена>", через запятую
TRAFFIC_PACKAGES=10:199,50:799,200:1999
`TariffTrafficWorker` запускается, только если активен `tariffs.json`.
# Пакеты в Telegram Stars (опционально, если включён STARS_ENABLED)
STARS_TRAFFIC_PACKAGES=10:2500,50:9000
```
Для дробных размеров допускается `0.5:99`, `1.5:299` и т. п. Цены допускают `0` (например, для теста).
Если задана только `STARS_TRAFFIC_PACKAGES`, при выборе пакета бот покажет цену в звёздах и автоматически подменит символ валюты на `⭐`. Если фиатные провайдеры (`YOOKASSA_ENABLED`, `FREEKASSA_ENABLED`, `PLATEGA_ENABLED`, `SEVERPAY_ENABLED`, `CRYPTOPAY_ENABLED`) включены, но `TRAFFIC_PACKAGES` не задана — бот сочтёт это ошибкой конфигурации и не пропустит покупку.
### Что происходит при оплате
1. К текущему лимиту трафика пользователя в панели **прибавляется** купленный объём (`new_limit = current_limit + purchase_bytes`). Это работает и для существующих подписчиков.
2. Стратегия сброса принудительно ставится в `NO_RESET` — иначе докупленные ГБ обнулялись бы по расписанию.
3. `end_date` ставится в `2099-01-01 UTC` (или сохраняется более поздняя, если по какой-то причине уже была). Это нужно, чтобы Remnawave не помечал пользователя как просроченного.
4. Реферальные бонусы и автопродление при таких платежах отключены: даже если в `.env` они настроены, в этом режиме бот их не применяет.
5. Поле `subscription_duration_months` в БД и в CSV-выгрузке платежей в этом режиме интерпретируется как «количество ГБ», а не как месяцы. В UI и админке это учитывается, но имейте в виду при ручных запросах к БД.
### Триал в режиме трафика
Триал работает по той же логике, что и в режиме подписок: задаётся `TRIAL_ENABLED`, `TRIAL_DURATION_DAYS`, `TRIAL_TRAFFIC_LIMIT_GB`, `TRIAL_TRAFFIC_STRATEGY`. То есть пробный период всё ещё ограничен по времени, даже если основная продажа — это пакеты трафика.
## Как переключиться между режимами
1. В `.env` либо заполните `TRAFFIC_PACKAGES` (включится режим трафика), либо очистите её (включится режим подписок).
2. Перезапустите контейнер: `docker compose up -d`.
3. Существующие подписчики не теряют доступ:
- При переключении в режим трафика их `end_date` остаётся прежним до следующей покупки. Покупка пакета установит `end_date` = «2099» и переведёт стратегию в `NO_RESET`.
- При обратном переключении следующий платёж задаст обычный `end_date` от «сейчас» (или продлит существующий).
> ⚠️ Одновременно совмещать продажу подписок и трафика бот **не умеет**. Проверка идёт по одному булевому флагу `traffic_sale_mode`.
## Чеки для самозанятых (nalog.ru)
Для каждого режима используется отдельный шаблон названия в чеке:
| Переменная | Назначение |
| --- | --- |
| `NALOGO_RECEIPT_NAME_SUBSCRIPTION` | Шаблон для подписки. Поддерживает `{months}` |
| `NALOGO_RECEIPT_NAME_TRAFFIC` | Шаблон для пакета трафика. Поддерживает `{gb}` |
Пример: `NALOGO_RECEIPT_NAME_TRAFFIC=traffic package {gb} GB`.
## Связанные переменные
| Переменная | Описание |
| --- | --- |
| `DEFAULT_CURRENCY_SYMBOL` | Что показывается рядом с фиатной ценой (`RUB`, `USD`, `EUR`, …) |
| `PAYMENT_METHODS_ORDER` | Порядок кнопок оплаты — общий для обоих режимов |
| `STARS_ENABLED` | Без него Stars-цены не предложатся, даже если заданы |
| `USER_HWID_DEVICE_LIMIT` | Лимит устройств — общий для обоих режимов, к тарификации не относится |
## Где это всё в коде
- Чтение и парсинг переменных: [config/settings.py](../config/settings.py) — `subscription_options`, `traffic_packages`, `stars_traffic_packages`, `traffic_sale_mode`.
- Активация платежа: [bot/services/subscription_service.py](../bot/services/subscription_service.py) — `activate_subscription` (ветка по `sale_mode`) и `_activate_traffic_package`.
- Выбор тарифа в боте: [bot/handlers/user/subscription/core.py](../bot/handlers/user/subscription/core.py) и [bot/handlers/user/subscription/payments_subscription.py](../bot/handlers/user/subscription/payments_subscription.py).
- Web App: [bot/app/web/subscription_webapp.py](../bot/app/web/subscription_webapp.py) — поле `traffic_mode` в payload и логика отображения трафика.
- Раз в несколько минут проверяет 30-дневные сбросы.
- Отправляет/дедуплицирует уровни предупреждений 80/95/100 через `traffic_warnings`.
- При 100% удаляет пользователя из squad-ов тарифа и ставит `is_throttled`.
- Возвращает пользователя в squad-ы, когда лимит снова больше использованного трафика.