Files
remnawave-minishop/docs/features/tariffs.md
T

48 KiB
Raw Blame History

Тарифы

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

  • JSON-каталог тарифов из TARIFFS_CONFIG_PATH (по умолчанию data/tariffs.json);
  • конфигурация через переменные .env, если JSON-файл отсутствует.

JSON-каталог может содержать несколько тарифов разных моделей: подписки на срок, пакеты трафика без срока действия, разные наборы Internal Squads, лимиты устройств и пакеты докупки. Пример формата: data/tariffs.example.json.

Важно для Docker: TARIFFS_CONFIG_PATH обычно указывает на файл внутри /app/data, и этот каталог должен быть доступен migrate, backend и worker. Мигратор тоже читает каталог тарифов, когда привязывает существующие активные подписки без tariff_key к тарифу по умолчанию и заполняет тарифные лимиты. В штатных compose-файлах это уже настроено: корневой compose использует shop-data:/app/data, а production-примеры - ./data:/app/data для всех трех сервисов.

Коротко по моделям:

  • period - подписка на срок с месячным лимитом трафика и опциональной докупкой GB поверх месячного лимита;
  • traffic - покупка пакетов GB без пользовательского срока действия, где повторная покупка добавляет трафик к текущему остатку;
  • premium-сквады - дополнительный набор Internal Squads внутри любого тарифа, с отдельным названием, счетчиком, месячным лимитом и отдельными пакетами докупки.

Управление через админку

Каталог тарифов можно настраивать из Web App админки: раздел Система → Тарифы. Админка читает и сохраняет файл из TARIFFS_CONFIG_PATH, валидирует данные той же моделью TariffsConfig, что и бот, и атомарно перезаписывает JSON только после успешной проверки.

В интерфейсе доступны:

  • добавление, редактирование и удаление тарифов;
  • включение и выключение тарифа на витрине;
  • выбор тарифа по умолчанию;
  • настройка тарифов на срок (period): месячный лимит, периоды, цены в платежной валюте/Stars, реферальные бонусы и пакеты докупки трафика;
  • настройка тарифов по трафику (traffic): пакеты GB, цены в платежной валюте/Stars, курс конвертации;
  • настройка базовых Internal Squads из списка Remnawave;
  • настройка premium-раздела: названия RU/EN, premium Internal Squads, месячный premium-лимит и пакеты докупки premium-трафика в платежной валюте/Stars;
  • настройка базового HWID-лимита и пакетов докупки устройств.

Порядок продаваемых вариантов управляется в админке перетаскиванием строк: это работает для периодов покупки подписки, traffic-пакетов, обычных докупок трафика, premium-докупок и HWID-пакетов. Такой же порядок сохраняется в JSON и затем используется в Web App и Telegram-боте.

После сохранения изменения применяются к новым запросам Web App сразу, потому что конфиг тарифов загружается из JSON при обращении. Уже созданные подписки сохраняют свой tariff_key; при удалении или отключении тарифа проверьте, что активные подписки с этим ключом не требуют дальнейшего продления или смены.

Подробности по админ-панели, правам доступа, сохранению настроек и списку разделов есть в админ-панели.

Как выбирается режим

Если файл из 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_currency. По умолчанию используется rub, поэтому существующие каталоги с prices_rub, rub-пакетами и .env-ценами продолжают работать без изменений. Для другой валюты укажите код в нижнем регистре, например usd, eur или usdt, и задайте цены в generic-полях:

  • prices: { "usd": { "1": 4.99, "3": 12.99 } };
  • traffic_packages, topup_packages, premium_topup_packages, hwid_device_packages: ключ валюты вместо rub, например { "usd": [{ "gb": 50, "price": 2.5 }] };
  • conversion_rate_per_gb: курс конвертации оплаченной стоимости в GB для выбранной валюты.

Legacy-поля остаются алиасами: prices_rub, conversion_rate_rub_per_gb и ключ rub автоматически попадают в generic-модель. Telegram Stars остаются отдельной валютой stars/XTR и не могут быть default_currency.

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

Провайдер Валюты по умолчанию
YooKassa RUB
WATA RUB, USD, EUR
FreeKassa RUB, USD, EUR, UAH, KZT
CryptoPay fiat: USD, EUR, RUB, BYN, UAH, GBP, CNY, KZT, UZS, GEL, TRY, AMD, THB, INR, BRL, IDR, AZN, AED, PLN, ILS; crypto: USDT, TON, BTC, ETH, LTC, BNB, TRX, USDC
Heleket настраиваемый список HELEKET_SUPPORTED_CURRENCIES
Platega настраиваемый список PLATEGA_SUPPORTED_CURRENCIES
SeverPay настраиваемый список SEVERPAY_SUPPORTED_CURRENCIES
Telegram Stars XTR, отдельные Stars-цены

В админке раздел Система → Тарифы показывает текущую платежную валюту и матрицу провайдеров: включен ли метод, настроен ли сервис и будет ли он доступен при выбранной валюте. Для Platega, SeverPay и Heleket список валют нужно держать в соответствии с условиями вашего мерчанта.

Структура JSON-каталога

Минимальная структура:

{
  "default_tariff": "standard",
  "default_currency": "rub",
  "tariffs": [
    {
      "key": "standard",
      "names": { "ru": "Стандарт", "en": "Standard" },
      "descriptions": { "ru": "Базовый набор серверов" },
      "premium_names": { "ru": "Premium-серверы", "en": "Premium servers" },
      "squad_uuids": ["uuid-1"],
      "billing_model": "period",
      "monthly_gb": 500,
      "prices_rub": { "1": 200, "3": 600 },
      "referral_bonus_days_inviter": { "1": 3, "3": 7 },
      "referral_bonus_days_referee": { "1": 1, "3": 3 },
      "enabled_periods": [1, 3],
      "topup_packages": {
        "rub": [{ "gb": 10, "price": 99 }],
        "stars": [{ "gb": 10, "price": 2500 }]
      },
      "hwid_device_packages": {
        "rub": [
          {
            "count": 1,
            "price": 99,
            "prices": { "1": 99, "3": 249 },
            "min_price": 20
          }
        ],
        "stars": [{ "count": 1, "price": 50, "prices": { "1": 50, "3": 130 } }]
      },
      "enabled": true
    }
  ]
}

Основные поля:

Поле Назначение
default_tariff Тариф по умолчанию для первичного выбора и привязки активных подписок без tariff_key.
default_currency Валюта цен по умолчанию для JSON-каталога. По умолчанию rub; stars запрещен, потому что Stars используют отдельные цены.
tariffs[].key Стабильный ключ тарифа. Используется в платежах, подписках и смене тарифа.
tariffs[].names Названия тарифа по языкам.
tariffs[].descriptions Описания тарифа по языкам.
tariffs[].enabled Доступность тарифа на витрине.
tariffs[].squad_uuids Internal Squads Remnawave для пользователей тарифа.
tariffs[].premium_names Название premium-раздела по языкам. Используется в карточке лимита, модалке докупки premium-трафика и предупреждениях. Если поле не задано, используется Premium-серверы / Premium servers.
tariffs[].premium_squad_uuids Internal Squads с отдельным premium-лимитом. Ноды для учета берутся автоматически из accessible nodes этих сквадов через API панели.
tariffs[].premium_monthly_gb Отдельный месячный лимит трафика по premium-сквадам. 0 или отсутствие поля отключает отдельное ограничение.
tariffs[].premium_topup_packages Пакеты докупки premium-трафика в формате { "rub": [{ "gb": 10, "price": 99 }], "stars": [...] }. Требуют premium_squad_uuids. Порядок строк задает порядок premium-докупок на витрине и меняется drag&drop в админке.
tariffs[].billing_model Модель тарифа: period или traffic.
tariffs[].hwid_device_limit Базовый лимит HWID-устройств. 0 означает безлимит, отсутствие поля использует USER_HWID_DEVICE_LIMIT.
tariffs[].hwid_device_packages Пакеты докупки устройств. price — legacy/monthly fallback, prices задаёт полную цену пакета для периодов тарифа ("1", "3", "6", "12"), min_price задаёт минимальную цену prorate-докупки. Порядок строк задает порядок HWID-докупок и меняется drag&drop в админке.

Для period-тарифа также используются:

Поле Назначение
monthly_gb Базовый месячный лимит трафика тарифа. 0 означает безлимит.
prices Generic-цены периодов по валютам, например { "usd": { "1": 4.99 } }.
prices_rub Legacy-цены периодов в рублях, ключ - количество месяцев. Эквивалент prices.rub.
prices_stars Цены периодов в Telegram Stars.
referral_bonus_days_inviter Бонус пригласившему в днях для каждого периода. Ключ - количество месяцев, как в enabled_periods.
referral_bonus_days_referee Бонус приглашенному в днях для каждого периода. Ключ - количество месяцев, как в enabled_periods.
enabled_periods Периоды, доступные для покупки. Порядок элементов в массиве задаёт порядок периодов на витрине (в Telegram-боте и Web App) — отсортируйте их так, как нужно показывать. В веб-админке этот порядок меняется перетаскиванием строк периодов.
topup_packages Пакеты докупки трафика именно для этого тарифа. Если поле не задано или списки пустые, докупка для тарифа не показывается в Web App и Telegram-боте. Порядок строк задает порядок докупок на витрине и меняется drag&drop в админке.

Для traffic-тарифа используются:

Поле Назначение
traffic_packages Пакеты трафика в GB по валютам каталога и Telegram Stars. Порядок пакетов в списке задаёт порядок на витрине (в Telegram-боте и Web App): сначала идут пакеты валюты каталога, затем пакеты, доступные только за Stars. В веб-админке порядок меняется перетаскиванием строк.
conversion_rate_per_gb Курс для конвертации оставшихся дней period-тарифа в GB при смене на traffic-тариф в валюте каталога.
conversion_rate_rub_per_gb Legacy-алиас для рублевых каталогов.

Если у traffic-тарифа нет пакетов в default_currency, conversion_rate_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.

Premium-сквады и отдельный лимит

Тариф может включать дополнительный набор Internal Squads с отдельным лимитом трафика. Это удобно для сценария “обычные серверы без изменений, premium-серверы ограничены отдельно”.

{
  "squad_uuids": ["standard-squad-uuid"],
  "premium_names": { "ru": "Premium-серверы", "en": "Premium servers" },
  "premium_squad_uuids": ["premium-squad-uuid"],
  "premium_monthly_gb": 50,
  "premium_topup_packages": {
    "rub": [{ "gb": 10, "price": 99 }],
    "stars": [{ "gb": 10, "price": 2500 }]
  }
}

Правила:

  • обычный лимит тарифа продолжает работать через trafficLimitBytes Remnawave;
  • premium-трафик считается отдельно по нодам, доступным из premium_squad_uuids;
  • список UUID нод не хранится в тарифе: бот запрашивает accessible nodes каждого premium-сквада у Remnawave и кеширует результат;
  • пока premium-лимит не исчерпан, пользователь получает squad_uuids + premium_squad_uuids;
  • при исчерпании premium-лимита бот убирает только premium-сквады, обычный доступ остается;
  • после докупки premium-трафика бот возвращает premium-сквады, если новый лимит снова больше использованного premium-трафика.
  • докупленный premium-трафик не сгорает в конце месяца: каждый месяц сначала расходуется premium_monthly_gb, а докупленный остаток уменьшается только на трафик сверх месячного лимита;
  • при новом календарном месяце счетчик premium-трафика и premium_topup_used_bytes сбрасываются, но premium_topup_balance_bytes переносится дальше.

Если premium_squad_uuids заданы, но premium_monthly_gb пустой или 0 и нет premium_topup_packages, premium-сквады работают как дополнительный доступ без отдельного ограничения. Если заданы premium_topup_packages или положительный premium_monthly_gb, premium_squad_uuids обязательны.

Состояние хранится в подписке:

  • premium_baseline_bytes - базовый premium-лимит тарифа;
  • premium_topup_balance_bytes - оставшийся докупленный premium-трафик;
  • premium_topup_used_bytes - часть докупленного premium-трафика, уже потраченная в текущем месяце;
  • premium_used_bytes - использованный premium-трафик за текущий календарный месяц;
  • premium_period_start_at - месяц, к которому относится premium_used_bytes;
  • premium_is_limited - признак, что premium-сквад временно снят.

В пользовательском Web App premium-лимит показывается отдельной карточкой: использовано, лимит, остаток, докупленный переносимый остаток и список серверов/сквадов, на которые действует отдельное ограничение. В Telegram-разделе “Моя подписка” выводится тот же блок.

Обычная докупка и premium-докупка показываются отдельно. Обычная докупка использует topup_packages у period-тарифа или traffic_packages у traffic-тарифа. Premium-докупка использует только premium_topup_packages, получает sale_mode=premium_topup и в заголовке показывает premium_names, а не название обычной докупки.

Предупреждения по premium-лимиту отправляются отдельно от обычного трафика на тех же процентах TARIFF_TRAFFIC_WARNING_LEVELS. Сообщение использует название из premium_names, перечисляет серверы/сквады и ведет пользователя в докупку premium-трафика.

В Web App админке premium-сквады можно выбрать из выпадающего списка на вкладке Premium в редакторе тарифа. Список берется из API Remnawave (/api/admin/panel/internal-squads), поэтому UUID обычно не нужно копировать вручную.

Тарифы по трафику (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,
        "prices": { "1": 99, "3": 249, "6": 449, "12": 799 },
        "min_price": 20
      }
    ],
    "stars": [{ "count": 1, "price": 50, "prices": { "1": 50, "3": 130 } }]
  }
}

Правила:

  • hwid_device_limit хранит базовый лимит тарифа;
  • extra_hwid_devices хранит только текущую активную сумму докупленных устройств;
  • срок действия каждой докупки хранится в hwid_device_purchases.valid_from / valid_until;
  • эффективный лимит равен hwid_device_limit + active extra_hwid_devices;
  • базовый лимит 0 означает безлимит, в Remnawave отправляется hwidDeviceLimit = 0;
  • при безлимитном базовом лимите докупка устройств не применяется;
  • полная цена HWID-пакета берется из prices[duration_months]; если периода нет, используется fallback price * duration_months;
  • фактическая цена докупки считается пропорционально оплачиваемому окну valid_from -> valid_until относительно периода подписки и фиксируется в платежe;
  • для Telegram Stars цена округляется вверх до целого Stars, для платежной валюты — вверх до копеек; min_price защищает от микроплатежей в конце периода;
  • кнопка докупки устройств всегда покупает устройства только для текущей активной подписки и только до текущего срока ее действия;
  • при продлении подписки пользователь видит отдельный чекбокс продления действующих докупленных устройств; чекбокс включен по умолчанию, цена считается по текущему тарифу и добавляется в тот же платеж подписки;
  • если пользователь продлил подписку без продления устройств, старая докупка продолжает действовать до своего valid_until, а Web App показывает предупреждение о возможном временном возврате к базовому лимиту;
  • админские продления, промокоды и реферальные бонусы добавляют фиксированное количество дней отдельно к подписке и к действующим докупкам устройств, не склеивая даты окончания;
  • traffic-тарифы не показывают и не принимают докупку HWID-устройств, потому что у них нет срока подписки;
  • при смене тарифа базовый лимит берется из целевого тарифа, а неиспользованная стоимость HWID-докупок в платежной валюте конвертируется в дни нового period-тарифа или GB traffic-тарифа; XTR/Stars-докупки не конвертируются без явного курса и продолжают жить по своему valid_until;
  • история докупок пишется в hwid_device_purchases;
  • платеж хранит количество устройств в payments.purchased_hwid_devices.

Докупка устройств доступна в Web App через /api/devices/topup-options и /api/payments, а также в Telegram-боте из раздела устройств.

Смена тарифа

Смена тарифа доступна для активных подписок с tariff_key и записывается в таблицу tariff_changes.

Варианты расчета:

Переход Поведение
period -> period Остаток оплаченных дней оценивается по legacy-полю effective_monthly_price_rub, где хранится месячная цена в платежной валюте каталога, затем пересчитывается в дни целевого тарифа через месячную цену целевого тарифа. Неиспользованная стоимость HWID-докупок в платежной валюте добавляется к этому расчету как дополнительные дни. Количество дней округляется вниз.
period -> period с доплатой Если целевой тариф дороже, может быть создан платеж tariff_upgrade; неиспользованная стоимость HWID-докупок в платежной валюте уменьшает сумму доплаты. После оплаты применяется целевой тариф, а конвертированные HWID-окна закрываются.
period -> traffic Остаток оплаченных дней и неиспользованная стоимость HWID-докупок в платежной валюте конвертируются в GB по conversion_rate_per_gb или минимальной цене 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, premium_topup, tariff_upgrade, hwid_devices.
tariff_key Ключ тарифа, к которому относится платеж.
purchased_gb Купленный объем GB для traffic-пакетов и докупки трафика.
purchased_hwid_devices Количество устройств при докупке HWID.
hwid_valid_from, hwid_valid_until Зафиксированное окно действия HWID-докупки на момент создания платежа.
hwid_pricing_period_months, hwid_proration_ratio, hwid_full_price Метаданные расчета цены HWID-докупки: период тарифа, коэффициент prorate и полная цена пакета для периода.
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 и TRIAL_SQUAD_UUIDS. Он не выбирает тариф из JSON-каталога, но его можно настроить на странице Система → Тарифы рядом с каталогом продаж. Если TRIAL_SQUAD_UUIDS пустой, для trial применяются squads из USER_SQUAD_UUIDS. Переключатель TRIAL_WITHOUT_TELEGRAM_ENABLED управляет активацией trial для аккаунтов без Telegram, а домены из DISPOSABLE_EMAIL_DOMAINS требуют привязки Telegram независимо от этого переключателя.

Промокоды с бонусными днями применяются к покупке period-подписки.

Реферальные бонусы за оплату в JSON-каталоге задаются прямо в period-тарифе рядом с ценами периода: referral_bonus_days_inviter для пригласившего и referral_bonus_days_referee для приглашенного. Ключи этих словарей - месяцы периода ("1", "3", "6", "12" или любые другие периоды тарифа, например "2", "4", "8", "16"). Для traffic-тарифов такие бонусы не применяются.

Приветственный бонус приглашённому (REFERRAL_WELCOME_BONUS_DAYS) настраивается в отдельном блоке Реферальная программа на странице тарифов. REFERRAL_WELCOME_BONUS_WITHOUT_TELEGRAM_ENABLED разрешает или запрещает выдачу этого бонуса аккаунтам без Telegram; disposable email домены из DISPOSABLE_EMAIL_DOMAINS всегда требуют Telegram перед начислением.

Если приглашенный покупает один тариф, а пригласивший находится на другом, размер бонуса берется из тарифа и периода, который купил приглашенный. При этом подписка пригласившего только продлевается на бонусные дни: лимиты, Internal Squads и другие параметры его текущего тарифа не пересчитываются под тариф приглашенного.

В Web App и Telegram-меню подробные строки по периодам показываются только для legacy-режима или когда активен один period-тариф. Если включено несколько period-тарифов, Web App показывает сообщение, что бонус зависит от тарифа и периода оплаты друга, затем список тарифов с диапазонами "от N до N дней" и раскрытием подробностей по иконке вопроса. Telegram-меню в этом случае показывает только диапазоны по каждому тарифу.

В legacy-режиме без JSON-каталога используются переменные REFERRAL_BONUS_DAYS_* и REFEREE_BONUS_DAYS_*; при продаже трафика без 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 берется из последнего успешного платежа или из цены тарифа по умолчанию.

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