Files
remnawave-minishop/docs/tariffs.md
T
2026-05-08 18:14:30 +03:00

16 KiB
Raw Blame History

Тарифы

Бот поддерживает два способа описания продаж:

  • 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 берется из последнего успешного платежа или из цены тарифа по умолчанию.

Это позволяет существующим активным подпискам отображаться и управляться в интерфейсах тарифов.