16 KiB
Тарифы
Бот поддерживает два способа описания продаж:
- JSON-каталог тарифов из
TARIFFS_CONFIG_PATH(по умолчаниюconfig/tariffs.json); - конфигурация через переменные
.env, если JSON-файл отсутствует.
JSON-каталог может содержать несколько тарифов разных моделей: подписки на срок, пакеты трафика без срока действия, разные наборы Internal Squads, лимиты устройств и пакеты докупки. Пример формата: config/tariffs.example.json.
Как выбирается режим
Если файл из TARIFFS_CONFIG_PATH существует и проходит валидацию, используется каталог тарифов. В этом режиме TRAFFIC_PACKAGES и цены подписок из .env не формируют витрину продаж, потому что цены и пакеты берутся из JSON.
Если JSON-файл отсутствует, бот использует значения .env:
RUB_PRICE_*,STARS_PRICE_*и*_MONTHS_ENABLEDдля подписок на срок;TRAFFIC_PACKAGESиSTARS_TRAFFIC_PACKAGESдля продажи пакетов трафика;USER_TRAFFIC_LIMIT_GB,USER_TRAFFIC_STRATEGY,USER_SQUAD_UUIDS,USER_HWID_DEVICE_LIMITдля пользователей Remnawave.
В режиме без JSON-каталога наличие TRAFFIC_PACKAGES или STARS_TRAFFIC_PACKAGES переключает витрину на продажу трафика вместо подписок на срок.
Структура JSON-каталога
Минимальная структура:
{
"default_tariff": "standard",
"topup_packages_default": {
"rub": [{ "gb": 10, "price": 99 }],
"stars": [{ "gb": 10, "price": 2500 }]
},
"tariffs": [
{
"key": "standard",
"names": { "ru": "Стандарт", "en": "Standard" },
"descriptions": { "ru": "Базовый набор серверов" },
"squad_uuids": ["uuid-1"],
"billing_model": "period",
"monthly_gb": 500,
"prices_rub": { "1": 150, "3": 400 },
"enabled_periods": [1, 3],
"enabled": true
}
]
}
Основные поля:
| Поле | Назначение |
|---|---|
default_tariff |
Тариф по умолчанию для первичного выбора и привязки активных подписок без tariff_key. |
topup_packages_default |
Пакеты докупки трафика для period-тарифов, у которых не задан topup_packages. |
tariffs[].key |
Стабильный ключ тарифа. Используется в платежах, подписках и смене тарифа. |
tariffs[].names |
Названия тарифа по языкам. |
tariffs[].descriptions |
Описания тарифа по языкам. |
tariffs[].enabled |
Доступность тарифа на витрине. |
tariffs[].squad_uuids |
Internal Squads Remnawave для пользователей тарифа. |
tariffs[].billing_model |
Модель тарифа: period или traffic. |
tariffs[].hwid_device_limit |
Базовый лимит HWID-устройств. 0 означает безлимит, отсутствие поля использует USER_HWID_DEVICE_LIMIT. |
tariffs[].hwid_device_packages |
Пакеты докупки устройств: { "count": 1, "price": 99 }. |
Для period-тарифа также используются:
| Поле | Назначение |
|---|---|
monthly_gb |
Базовый месячный лимит трафика тарифа. 0 означает безлимит. |
prices_rub |
Цены периодов в рублях, ключ - количество месяцев. |
prices_stars |
Цены периодов в Telegram Stars. |
enabled_periods |
Периоды, доступные для покупки. |
topup_packages |
Пакеты докупки трафика именно для этого тарифа. |
Для traffic-тарифа используются:
| Поле | Назначение |
|---|---|
traffic_packages |
Пакеты трафика в GB для рублей и Telegram Stars. |
conversion_rate_rub_per_gb |
Курс для конвертации оставшихся дней period-тарифа в GB при смене на traffic-тариф. |
Если у traffic-тарифа нет RUB-пакетов, conversion_rate_rub_per_gb обязателен.
Period-тарифы
period продает доступ на срок с месячным лимитом трафика.
При покупке или продлении:
- дата начала берется от текущей активной подписки, если она еще действует, иначе от текущего времени;
- срок считается календарными месяцами через
add_months; - промокод может добавить бонусные дни к рассчитанному сроку;
tier_baseline_bytesполучает значениеmonthly_gb;topup_balance_bytesсохраняется из текущей активной подписки;traffic_limit_bytesстановитсяtier_baseline_bytes + topup_balance_bytes;- в Remnawave отправляется
trafficLimitStrategy = MONTH; - в Remnawave отправляются Internal Squads из тарифа;
- в Remnawave отправляется эффективный HWID-лимит тарифа.
MONTH означает, что сброс использованного трафика выполняет Remnawave. Бот не рассчитывает дату сброса самостоятельно и не хранит отдельный период сброса для period-тарифов.
Докупка трафика для period-тарифа увеличивает topup_balance_bytes и общий traffic_limit_bytes. Этот баланс сохраняется в подписке и учитывается при продлении period-тарифа. В панель отправляется актуальный лимит, а доступ переводится в ACTIVE.
Traffic-тарифы
traffic продает объем трафика без пользовательского срока действия.
При покупке:
end_dateставится в дальнюю дату2099-01-01 UTC, если у активной подписки нет более поздней даты;duration_months = 0;period_start_at = NULL;tier_baseline_bytes = 0;topup_balance_bytesхранит доступный остаток трафика;- в Remnawave отправляется
trafficLimitStrategy = NO_RESET; - автопродление и уведомления о скором окончании срока отключаются для такой подписки.
Очередная покупка добавляет GB к фактическому остатку:
remaining = max(0, current_limit - current_used)
balance_after = remaining + purchased
limit_after = current_used + balance_after
Так пользователь не теряет уже оплаченный остаток, а Remnawave продолжает считать общий лимит от текущего использованного трафика.
Если докупка трафика вызывается для traffic-тарифа, она обрабатывается как покупка очередного пакета этого же traffic-тарифа.
HWID-устройства
Тариф может задавать базовый лимит устройств и пакеты докупки:
{
"hwid_device_limit": 5,
"hwid_device_packages": {
"rub": [{ "count": 1, "price": 99 }],
"stars": [{ "count": 1, "price": 2500 }]
}
}
Правила:
hwid_device_limitхранит базовый лимит тарифа;extra_hwid_devicesхранит количество докупленных устройств;- эффективный лимит равен
hwid_device_limit + extra_hwid_devices; - базовый лимит
0означает безлимит, в Remnawave отправляетсяhwidDeviceLimit = 0; - при безлимитном базовом лимите докупка устройств не применяется;
- при смене тарифа базовый лимит берется из целевого тарифа, а докупленные устройства сохраняются;
- история докупок пишется в
hwid_device_purchases; - платеж хранит количество устройств в
payments.purchased_hwid_devices.
Докупка устройств доступна в Web App через /api/devices/topup-options и /api/payments, а также в Telegram-боте из раздела устройств.
Смена тарифа
Смена тарифа доступна для активных подписок с tariff_key и записывается в таблицу tariff_changes.
Варианты расчета:
| Переход | Поведение |
|---|---|
period -> period |
Остаток оплаченных дней оценивается по effective_monthly_price_rub, затем пересчитывается в дни целевого тарифа через месячную цену целевого тарифа. Количество дней округляется вниз. |
period -> period с доплатой |
Если целевой тариф дороже, может быть создан платеж tariff_upgrade; после оплаты применяется целевой тариф. |
period -> traffic |
Остаток оплаченных дней конвертируется в GB по conversion_rate_rub_per_gb или минимальной RUB-цене GB из пакетов целевого тарифа. |
traffic -> period |
Пользователь выбирает и оплачивает период целевого тарифа; остаток GB сохраняется как topup_balance_bytes поверх лимита period-тарифа. |
При смене тарифа бот меняет:
tariff_key;- Internal Squads в Remnawave;
trafficLimitBytes;trafficLimitStrategy;- базовый HWID-лимит;
effective_monthly_price_rubдля period-тарифов;auto_renew_enabledи уведомления для traffic-тарифов.
Платежи
В платежах используются поля:
| Поле | Назначение |
|---|---|
sale_mode |
Тип продажи: subscription, traffic_package, topup, tariff_upgrade, hwid_devices. |
tariff_key |
Ключ тарифа, к которому относится платеж. |
purchased_gb |
Купленный объем GB для traffic-пакетов и докупки трафика. |
purchased_hwid_devices |
Количество устройств при докупке HWID. |
subscription_duration_months |
Количество месяцев для подписки на срок; также используется платежными обработчиками как числовое поле покупки. |
В callback и metadata платежных провайдеров sale_mode может передаваться с суффиксом тарифа, например subscription@standard или topup@standard. При активации платежа тариф сохраняется отдельно в tariff_key.
Предупреждения и исчерпание трафика
Remnawave ограничивает доступ при достижении trafficLimitBytes, переводя пользователя в статус LIMITED. Бот не удаляет пользователя из Internal Squads при 100% использования трафика.
TariffTrafficWorker запускается, когда активен JSON-каталог тарифов. Раз в 300 секунд он:
- синхронизирует из панели
status,trafficLimitBytes,usedTrafficBytesиtrafficLimitStrategy; - для period-тарифов выставляет
trafficLimitStrategy = MONTH, если панель еще показывает другую стратегию; - отправляет предупреждения на уровнях из
TARIFF_TRAFFIC_WARNING_LEVELS(по умолчанию85,90,95); - не отправляет
status=ACTIVEпри простой синхронизации стратегии, чтобы не снять статусLIMITED, выставленный Remnawave; - дедуплицирует предупреждения через
traffic_warnings.
Для period-тарифов дедупликация предупреждений привязана к началу текущего месяца. Для traffic-тарифов она учитывает текущий trafficLimitBytes, чтобы после покупки очередного пакета пользователь мог получить следующий набор предупреждений.
Подписки, которые были ограничены логикой предыдущих запусков бота (is_throttled=True), восстанавливаются воркером только когда лимит снова больше использованного трафика.
Автопродление, пробный период и бонусы
Автопродление через YooKassa применяется к подпискам на срок. Для режима продажи трафика без JSON-каталога автопродление пропускается. Для traffic-тарифов JSON-каталога покупка является пакетом трафика, а не периодической подпиской.
Пробный период использует настройки TRIAL_DURATION_DAYS, TRIAL_TRAFFIC_LIMIT_GB и TRIAL_TRAFFIC_STRATEGY. Он не выбирает тариф из JSON-каталога.
Промокоды с бонусными днями применяются к покупке period-подписки. Реферальные бонусы по периодам также относятся к подпискам на срок; в режиме продажи трафика без JSON-каталога Web App не показывает детализацию бонусов по месяцам.
Привязка существующих подписок
При запуске с активным JSON-каталогом бот заполняет активные подписки без tariff_key:
tariff_keyполучаетdefault_tariff;tier_baseline_bytesберется из текущего лимита подписки или изmonthly_gbтарифа по умолчанию;topup_balance_bytesстановится0, если значение отсутствовало;period_start_atочищается;effective_monthly_price_rubберется из последнего успешного платежа или из цены тарифа по умолчанию.
Это позволяет существующим активным подпискам отображаться и управляться в интерфейсах тарифов.