Перейти к содержимому

Тарифы

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

  • 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
LAVA RUB
Pally RUB, USD, EUR
Stripe по умолчанию без локального фильтра; ограничьте через STRIPE_SUPPORTED_CURRENCIES под ваш аккаунт Stripe
Telegram Stars XTR, отдельные Stars-цены

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

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

{
  "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,
      "traffic_limit_strategy": "MONTH",
      "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[].legacy_keys Предыдущие ключи этого тарифа. Позволяют дообработать сохранённые платежи и подписки после переименования ключа; каждый legacy-ключ должен быть уникален во всём каталоге.
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[].premium_topup_always_available Показывать premium-докупку независимо от процента расхода premium-лимита. По умолчанию false: предложение появляется после 80% использования premium-лимита (см. когда показывается докупка).
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 в админке.

При переименовании тарифа перенесите его прежний key в legacy_keys, а не удаляйте его сразу. Платежи и подписки хранят ключ тарифа в момент создания; при последующей активации старый ключ разрешается через alias, после чего сервис использует текущий канонический key. Текущий ключ нельзя дублировать в legacy_keys, а один legacy-ключ нельзя назначить нескольким тарифам.

Разрешение и проверка алиасов реализованы в TariffsConfig, а activate_subscription канонизирует сохранённый ключ перед обработкой платежа. Регрессионные сценарии находятся в test_tariffs_config.py.

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

Поле Назначение
monthly_gb Базовый месячный лимит трафика тарифа. 0 означает безлимит.
traffic_limit_strategy Стратегия сброса трафика Remnawave: NO_RESET, DAY, WEEK, MONTH или MONTH_ROLLING. Если поле отсутствует, используется legacy-настройка USER_TRAFFIC_STRATEGY.
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 в админке.
topup_always_available Показывать докупку трафика независимо от процента расхода. По умолчанию false: предложение докупки появляется, когда израсходовано не менее 80% лимита трафика (см. когда показывается докупка).

Для 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 продает доступ на срок с месячным лимитом трафика.

При покупке или продлении:

  • дата начала берется от текущей активной подписки, если она еще действует, иначе от текущего времени;
  • срок считается календарными месяцами через add_months;
  • промокод может добавить бонусные дни к рассчитанному сроку;
  • tier_baseline_bytes получает значение monthly_gb;
  • topup_balance_bytes сохраняется из текущей активной подписки;
  • traffic_limit_bytes становится tier_baseline_bytes + topup_balance_bytes;
  • в Remnawave отправляется trafficLimitStrategy из тарифа с fallback на USER_TRAFFIC_STRATEGY;
  • в Remnawave отправляются Internal Squads из тарифа;
  • в Remnawave отправляется эффективный HWID-лимит тарифа.

traffic_limit_strategy задаётся отдельно для каждого period-тарифа. Выбранное значение применяется уже в CREATE нового пользователя Remnawave, а также при активации, продлении и смене тарифа. Если поле отсутствует в старом tariffs.json, сохраняется прежнее поведение через USER_TRAFFIC_STRATEGY; редактор показывает режим наследования, пока администратор явно не выберет стратегию. При NO_RESET панель не выполняет календарный сброс; DAY, WEEK, MONTH и MONTH_ROLLING задают соответствующий период. Для traffic-тарифов поле запрещено, а обычный пакет всегда использует NO_RESET.

Карточка пользователя показывает фактическую стратегию из Remnawave, поэтому ручной per-user override виден сразу. Фоновые синки не заменяют его значением тарифа; следующее изменение entitlement (активация, продление или смена тарифа) снова применяет стратегию текущего тарифа.

Докупка трафика для period-тарифа увеличивает topup_balance_bytes и общий traffic_limit_bytes. Этот баланс сохраняется в подписке и учитывается при продлении period-тарифа. В панель отправляется актуальный лимит, а доступ переводится в ACTIVE.

Предложение докупки не показывается пользователю сразу после покупки подписки. По умолчанию оно разблокируется, когда израсходовано не менее 80% соответствующего лимита. Правило действует одинаково в Web App и в Telegram-боте: кнопка докупки в меню бота скрыта, пока предложение заблокировано, а нажатие на устаревшую кнопку показывает алерт с остатком трафика и обновляет меню.

  • Обычный и premium-трафик разблокируются независимо: каждый считает свой процент от своего лимита.
  • Тумблеры topup_always_available (обычная докупка) и premium_topup_always_available (premium-докупка) — в редакторе тарифа это переключатель «Докупка доступна всегда» на вкладках «Докупки» и «Premium» — убирают порог для своего типа трафика.
  • Для traffic-тарифов порог не применяется: покупка пакетов и есть продукт, докупка доступна всегда.
  • Докупка не предлагается при безлимитном лимите (0) или включенном безлимитном оверрайде у подписки.

Тариф может включать дополнительный набор 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-учёт у period-тарифов следует trafficLimitStrategy пользователя из Remnawave с fallback сначала на traffic_limit_strategy тарифа, затем на USER_TRAFFIC_STRATEGY; у traffic-тарифов premium-учёт использует USER_TRAFFIC_STRATEGY, потому что обычный traffic-пакет в Remnawave живёт с NO_RESET. При NO_RESET premium-период идет от start_date подписки и не сбрасывает premium_topup_used_bytes; при DAY, WEEK, MONTH и MONTH_ROLLING счетчик premium-трафика и premium_topup_used_bytes сбрасываются на границе соответствующего периода, а premium_topup_balance_bytes переносится дальше. Для rolling-стратегии бот использует lastTrafficResetAt из Remnawave, если панель его отдаёт.

Если 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-периоде;
  • premium_used_bytes - использованный premium-трафик за текущий premium-период;
  • premium_period_start_at - начало premium-периода, к которому относится 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 продает объем трафика без пользовательского срока действия.

При покупке:

  • 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_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 из Remnawave, а стратегию тарифа и затем USER_TRAFFIC_STRATEGY применяет как fallback, если панель не вернула стратегию;
  • отправляет предупреждения на уровнях из TARIFF_TRAFFIC_WARNING_LEVELS (по умолчанию 85,90,95);
  • дедуплицирует предупреждения через traffic_warnings.

Для period-тарифов дедупликация предупреждений привязана к началу текущего периода по стратегии из Remnawave с fallback на стратегию тарифа и USER_TRAFFIC_STRATEGY; уведомления о сбросе отправляются только когда эффективная стратегия сброса не NO_RESET. Для 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; premium-сквады из тарифного каталога в обычном списке trial не выдаются как обычный доступ. Premium-доступ для trial включается отдельно через TRIAL_PREMIUM_SQUAD_UUIDS; если список пустой, premium-сквады не выдаются. Отдельный лимит задаёт TRIAL_PREMIUM_TRAFFIC_LIMIT_GB, где 0 означает доступ без отдельного premium-ограничения, а его период следует TRIAL_TRAFFIC_STRATEGY. Переключатель TRIAL_WITHOUT_TELEGRAM_ENABLED управляет активацией trial для аккаунтов без Telegram, а домены из DISPOSABLE_EMAIL_DOMAINS требуют привязки Telegram независимо от этого переключателя.

Промокоды поддерживают несколько режимов: мгновенные бонусные дни, бонусные дни после оплаты, скидку на checkout, множитель срока подписки и множитель выдаваемого трафика. Бонусные дни и множитель срока работают с period-подписками; множитель трафика работает с traffic-пакетами и докупками; скидка может применяться и к подпискам, и к traffic/HWID-покупкам. Подробности: промокоды.

Реферальные бонусы за оплату в 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 перед начислением.

Переключатель REFERRAL_ONE_BONUS_PER_REFEREE ограничивает платёжные реферальные бонусы первым успешным платежом приглашенного пользователя. Если он включен, повторные покупки того же приглашенного не начисляют бонусы ни ему, ни пригласившему. Приветственный бонус за регистрацию управляется отдельно и этим ограничением не отключается.

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

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

В legacy-режиме без JSON-каталога используются переменные REFERRAL_BONUS_DAYS_* и REFEREE_BONUS_DAYS_*; при продаже трафика без JSON-каталога Web App не показывает детализацию бонусов по месяцам. Переключатель LEGACY_REFS принимает старые ссылки вида /start ref_<telegram_id>, где payload содержит Telegram/user ID пригласившего; в админке он находится в блоке Совместимость с legacy-тарифами.

При запуске с активным JSON-каталогом бот заполняет активные подписки без tariff_key:

  • tariff_key получает default_tariff;
  • tier_baseline_bytes берется из текущего лимита подписки или из monthly_gb тарифа по умолчанию;
  • topup_balance_bytes становится 0, если значение отсутствовало;
  • period_start_at очищается;
  • effective_monthly_price_rub берется из последнего успешного платежа или из цены тарифа по умолчанию.

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