VPN·HUB·CRACK VPN для отпуска: «заморозка» подписки и доступ к РФ-сервисам из-за границы · VPN HUB CRACK
Все статьи
Клиенты и бизнес PRO

VPN для отпуска: «заморозка» подписки и доступ к РФ-сервисам из-за границы

Обновлено 11 августа 2026 Проверено

VPN для отпуска: заморозка + доступ к РФ-сервисам

Отпуск это два встречных запроса от одного клиента, и оба бьют по выручке, если их не закрыть:

  1. «Зачем платить, пока я не пользуюсь?» Человек улетел на 3 недели, VPN ему пока не нужен, а оплаченные дни тают. В голове клиента это «деньги на ветер»: пишет в поддержку, просит вернуть, ставит на паузу через отмену автоплатежа, и часто не возвращается.
  2. «Выключите VPN!», а он и так выключен. Тот же клиент за границей хочет зайти в российский банк / Госуслуги / Ozon / Mir Pay, а приложение ругается: «Вы используете VPN, отключите его». Он его выключает. Не помогает. Потому что дело не в VPN, а в том, что его IP принадлежит другой стране.

Закрой оба, и вместо оттока получишь лояльность и «сервис, который думает за меня». Ниже два приёма.

📖 Незнакомые слова? Все термины простыми словами есть в Словаре терминов.


Приём 1. «Заморозка» подписки: банк дней вместо оттока

Идея. Клиент нажимает «❄️ Заморозить», подписка встаёт на паузу, оплаченные дни не сгорают, а складываются в «банк». Вернулся из отпуска → «▶️ Разморозить» → дни продолжают отсчёт ровно с того места, где встали. Вместо «верните деньги, я не пользуюсь» вы получаете «ок, заморожу и вернусь».

Это дешевле возврата, убирает повод для спора и заметно снижает отток на сезонных просадках (лето, новогодние каникулы). Живой пример в проде: сервис T-VPN, там это сделано именно так.

Как это работает (механика T-VPN)

В самой панели Remnawave «заморозки» нет, приём собирается в вашем боте. Хранение и две операции:

# поля в строке подписки/ключа:
frozen          # 0/1 — заморожена ли подписка
frozen_at       # когда заморозили
banked_days     # сколько дней "положили в банк" (остаток на момент заморозки)
last_freeze_at  # метка для лимита "раз в 30 дней"

ЗАМОРОЗИТЬ  (по кнопке клиента):
  если frozen                          → "уже заморожена"
  если (now - last_freeze_at) < 30 дн  → "заморозка раз в месяц, подождите N дн."
  days_left = expireAt - now
  если days_left < 2                   → "осталось меньше 2 дней — нельзя"
  Remnawave  PATCH /api/users {status: DISABLED}   # доступ выкл; сквады/hwid/telegramId/трафик сохраняются
  frozen = 1, frozen_at = now, banked_days = days_left, last_freeze_at = now

РАЗМОРОЗИТЬ  (по кнопке клиента ИЛИ авто через 30 дней):
  Remnawave  PATCH /api/users {status: ACTIVE, expireAt: now + banked_days}
  frozen = 0, frozen_at = NULL, expireAt = now + banked_days

Ключевой момент: пока подписка заморожена, expireAt не уменьшается: часы стоят. При разморозке срок пересчитывается как now + banked_days, поэтому ни один оплаченный день не теряется. Статус DISABLED просто отключает доступ, сохраняя сквады, лимит трафика, HWID (привязку подписки к «железу» устройства) и привязку Telegram, после разморозки клиент подключается тем же ключом, ничего не переоформляя.

Обязательные ограничения (иначе «заморозка» = бесплатная парковка)

  • Раз в 30 дней. Без лимита клиент будет морозить/размораживать по дню, и жить вечно. Проверяем last_freeze_at.
  • Остаток ≥ 2 дней. Морозить «хвостик» бессмысленно и плодит баги на границе срока.
  • Авто-разморозка через 30 дней. Замороженный аккаунт не должен висеть вечно: шедулер сам размораживает по истечении 30 дней (expireAt = now + banked_days) и шлёт уведомление: «❄️➡️☀️ Подписка автоматически разморожена: прошло 30 дней, срок продолжил отсчёт». Так вы не держите слот/ресурсы под «мёртвыми» паузами.
  • Не дёргать замороженных. Напоминания «подписка кончается» и автопродление в шедулере должны пропускать frozen-ключи, иначе клиент в отпуске получает спам «продлите» по замороженной подписке.

Частичная заморозка: морозим не подписку, а конкретные устройства

Полная заморозка ломается на самом частом случае: одной подпиской пользуется семья. Владелец уехал за границу и VPN ему не нужен, а жена, родители и дети остались в России и продолжают им пользоваться. Нажал «заморозить», выключил интернет всей семье. Клиент такое не жмёт и просто уходит в отток, ради борьбы с которым заморозку и делали.

Лечится тем, что человек перед заморозкой выбирает устройства-исключения: их доступ остаётся живым, всё остальное морозится. Подписка при этом остаётся одна, никаких «купите второй ключ».

Тариф на исключения: по половине дня за устройство:

Что выбрал Списывается из банка
Полная заморозка, исключений нет 0 дней за сутки
1 устройство продолжает работать 0,5 дня за сутки
2 устройства продолжают работать 1 день за сутки

💡 Почему это работает само. На двух устройствах частичная заморозка стоит ровно столько же, сколько обычная подписка, и смысл в ней пропадает. То есть тариф сам разводит людей по честным сценариям: уехал один из двоих → платишь половину; пользуется вся семья → платишь как обычно; не нужно никому → морозишь целиком и не платишь вовсе. Никаких дополнительных запретов писать не нужно, экономика ограничивает сама.

Ставку выше 1 дня за сутки не берите. При трёх исключениях по формуле выходит 1,5 дня, то есть человек платил бы за паузу дороже, чем за работающую подписку. Ограничивайте сверху единицей: rate = min(1.0, 0.5 × devices).

Как это делается на Remnawave. Отдельной ручки «отключить одно устройство» в панели нет, но нужное поведение собирается из HWID-лимита:

  1. Запомнить HWID устройств-исключений (они уже есть в списке привязок пользователя).
  2. Удалить HWID-привязки остальных устройств.
  3. Выставить hwidDeviceLimit равным числу исключений.

Дальше замороженные устройства при попытке подключиться не смогут зарегистрировать свой HWID (лимит занят исключениями), а выбранные продолжают работать без перерыва. На разморозке возвращаете прежний лимит, и все устройства спокойно привязываются заново.

⚠️ Грабли. Если оставить лимит с запасом, любое устройство просто займёт свободный слот, и «заморозка» окажется фикцией. Лимит должен быть ровно по числу исключений.

Что добавить в модель данных к обычной заморозке:

frozen_devices_json   TEXT     -- HWID устройств-исключений (JSON-массив)
frozen_daily_rate     REAL     -- сколько дней списывать за сутки: 0 / 0.5 / 1.0
hwid_limit_before     INTEGER   -- лимит до заморозки, чтобы вернуть на разморозке

Шедулер, который раз в сутки уменьшает банк, вместо «пропустить замороженного» делает banked_days -= frozen_daily_rate. Банк становится дробным: храните его как REAL, иначе половинки будут молча округляться в пользу клиента.

Идея частичной заморозки: Борис Яновский, 28.07.2026. Спасибо: случай с семейной подпиской в исходную механику не попадал.

🤖 Готовый промпт: вшей «Заморозку» в своего бота через нейросеть

Не хочешь кодить руками, отдай задачу Claude / ChatGPT. Ниже промпт со спецификацией, проверенный на живом проде T-VPN (remnawave-shopbot). Как пользоваться: скопируй блок «Задача» как инструкцию нейросети, а спецификацию приложи как контекст вместе с кодом своего бота. Стек: панель Remnawave + aiogram-бот + FastAPI мини-апп + SQLite.

1) Задача (вставь как инструкцию нейросети)

Ты — backend/full-stack инженер VPN-сервиса на базе панели Remnawave + Telegram-бота
(aiogram) с мини-аппом (FastAPI) и SQLite. Реализуй фичу «Заморозка подписки»:
клиент может поставить подписку на паузу (например, на время отпуска), чтобы
НЕ сгорали оплаченные дни. Пока подписка заморожена — трафик не идёт, а срок
действия стоит на месте. По разморозке (вручную или автоматически) оставшиеся
дни продолжают отсчёт с текущего момента.

В самой панели Remnawave «заморозки» НЕТ — это надстройка над ботом:
4 поля в БД + 2 функции работы с панелью + 3 функции БД + 2 HTTP-эндпоинта +
1 задача шедулера + кнопки в интерфейсе. Соблюди ВСЕ нюансы из спецификации —
именно они отличают рабочую фичу от той, что теряет клиентам оплаченные дни.
Делай минимально инвазивно, с проверками владельца/идемпотентности, и обязательно
исключи замороженные ключи из всех фоновых рассылок и автосписаний.

2) Нюансы (тут все грабли, соблюдать обязательно)

  1. expireAt НЕ уменьшается при заморозке. Дни сохраняются именно потому, что при DISABLED доступа нет. Ошибка: пересчитывать expireAt в момент заморозки.
  2. При разморозке пересчитывай expireAt = now + banked_days. Нельзя вернуть ACTIVE со старым expireAt, иначе клиент может потерять дни. Всегда now + банк.
  3. banked_days это снимок оставшихся дней на момент заморозки (целое число).
  4. Лимит «раз в 30 дней» через last_freeze_at. Ставится при заморозке и НЕ обнуляется при разморозке (иначе разморозился → тут же заморозился снова). Кулдаун считается от last_freeze_at.
  5. Минимум 2 дня остатка, чтобы разрешить заморозку.
  6. PATCH в Remnawave сохраняет служебные поля: uuid, email, activeInternalSquads (массив uuid), hwidDeviceLimit, telegramId, trafficLimitBytes, trafficLimitStrategy, иначе панель сбросит сквады / лимит устройств / привязку Telegram / лимит трафика.
  7. Замороженные ключи исключай из ВСЕХ фоновых процессов: напоминания «подписка кончается», автопродление с баланса, winback, grace-логика, везде ранний if key.frozen: continue.
  8. Авто-разморозка через 30 дней: шедулер находит frozen=1 AND frozen_at <= now-30д, размораживает + уведомляет. Верхняя граница паузы.
  9. Идемпотентность и владелец: ключ принадлежит юзеру; нельзя заморозить дважды; нельзя разморозить незамороженный.
  10. Часовые пояса: для Remnawave expireAt это UTC ISO (…Z); в локальной БД свой формат. Не перепутать.
  11. Фронт перечитывает состояние (loadUser) после freeze/unfreeze; бэкенд отдаёт в ключе frozen, banked_days, days_left, last_freeze_at (+ _ms для JS-кулдауна).

3) Модель данных (идемпотентные миграции)

колонка тип назначение
frozen INTEGER DEFAULT 0 заморожена ли
frozen_at TIMESTAMP когда заморозили (для авто-разморозки)
banked_days INTEGER DEFAULT 0 «банк» дней
last_freeze_at TIMESTAMP последняя заморозка (лимит раз/30д)
_ensure_table_column(cursor, "vpn_keys", "frozen", "INTEGER DEFAULT 0")
_ensure_table_column(cursor, "vpn_keys", "frozen_at", "TIMESTAMP")
_ensure_table_column(cursor, "vpn_keys", "banked_days", "INTEGER DEFAULT 0")
_ensure_table_column(cursor, "vpn_keys", "last_freeze_at", "TIMESTAMP")

4) Слой панели Remnawave: одна функция set_user_frozen

async def set_user_frozen(user_uuid, frozen, *, host_name=None, banked_days=None) -> bool:
    """DISABLED (заморозка) / ACTIVE + expireAt=now+banked (разморозка).
    Сохраняет squads/email/hwid/telegramId/traffic."""
    if not user_uuid:
        return False
    cur = await get_user_by_uuid(user_uuid, host_name=host_name)   # GET текущего
    if not cur:
        return False
    payload = {"uuid": cur.get("uuid"), "email": cur.get("email")}
    sq = []
    for s in (cur.get("activeInternalSquads") or []):
        if isinstance(s, dict) and s.get("uuid"): sq.append(s["uuid"])
        elif s: sq.append(s)
    if sq: payload["activeInternalSquads"] = sq
    if cur.get("hwidDeviceLimit") is not None:   payload["hwidDeviceLimit"] = cur["hwidDeviceLimit"]
    if cur.get("telegramId") is not None:        payload["telegramId"] = cur["telegramId"]
    if cur.get("trafficLimitBytes") is not None: payload["trafficLimitBytes"] = cur["trafficLimitBytes"]
    if cur.get("trafficLimitStrategy"):          payload["trafficLimitStrategy"] = cur["trafficLimitStrategy"]
    if frozen:
        payload["status"] = "DISABLED"
        if cur.get("expireAt"):
            payload["expireAt"] = cur["expireAt"]        # НЕ меняем — часы стоят
    else:
        payload["status"] = "ACTIVE"
        days = max(1, int(banked_days or 1))
        exp = datetime.now(timezone.utc) + timedelta(days=days)
        payload["expireAt"] = exp.strftime("%Y-%m-%dT%H:%M:%S.000Z")   # UTC ISO
    resp = await _request("PATCH", "/api/users", json_payload=payload, expected_status=(200, 201))
    return resp.status_code in (200, 201)

5) Слой БД (3 функции)

def freeze_key(key_id, banked_days) -> bool:
    now = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
    return _exec("UPDATE vpn_keys SET frozen=1, frozen_at=?, banked_days=?, last_freeze_at=? WHERE key_id=?",
                 (now, int(banked_days), now, key_id))

def unfreeze_key(key_id, new_expire_at) -> bool:
    # ВНИМАНИЕ: last_freeze_at НЕ трогаем — держит кулдаун 30 дней
    return _exec("UPDATE vpn_keys SET frozen=0, frozen_at=NULL, expire_at=? WHERE key_id=?",
                 (new_expire_at, key_id))

def get_frozen_keys_to_unfreeze(older_than_days=30) -> list[dict]:
    cutoff = (datetime.now() - timedelta(days=older_than_days)).strftime("%Y-%m-%d %H:%M:%S")
    # SELECT * FROM vpn_keys WHERE frozen=1 AND frozen_at IS NOT NULL AND frozen_at <= cutoff
    ...

6) HTTP-эндпоинты мини-аппа

POST /api/freeze  {user_id, key_id}
  1. авторизовать + взять реальный telegram_id
  2. key = get_key_by_id; проверить владельца (key.user_id == user_id)
  3. если key.frozen               → "уже заморожена"
  4. uuid = key.remnawave_user_uuid; нет → "нет привязки"
  5. лимит: (now - last_freeze_at) < 30д → "раз в месяц, ещё N дн"
  6. days_left; если < 2           → "осталось меньше 2 дней"
  7. set_user_frozen(uuid, True);  ошибка → "не удалось на сервере"
  8. freeze_key(key_id, days_left) → {ok:True, banked_days}

POST /api/unfreeze  {user_id, key_id}
  1. авторизовать + владелец
  2. если не frozen → "не заморожена"
  3. banked = key.banked_days
  4. set_user_frozen(uuid, False, banked_days=banked)
  5. new_exp = now + max(1, banked); unfreeze_key(key_id, new_exp) → {ok:True}

7) Шедулер (авто-разморозка + пропуски)

async def auto_unfreeze_expired(bot):
    for key in get_frozen_keys_to_unfreeze(30):
        banked = int(key.get("banked_days") or 0)
        if await set_user_frozen(key["remnawave_user_uuid"], False,
                                 host_name=key.get("host_name"), banked_days=banked):
            unfreeze_key(key["key_id"], now_plus_days(max(1, banked)))
            await bot.send_message(key["user_id"],
                "❄️➡️☀️ Подписка автоматически разморожена — прошло 30 дней. Срок продолжил отсчёт.")

И в КАЖДОМ фоновом проходе по ключам (напоминания, автопродление, winback, grace) нужен ранний пропуск:

if key.get("frozen"):
    continue

8) Фронт (мини-апп)

  • Пункт меню ❄️ Заморозить подписку. Лист с тремя состояниями: заморожено (банк дней + кнопка «Разморозить»), кулдаун («заморозка раз в месяц · ещё N дн»), можно («оставшиеся N дней сохранятся» + «Заморозить»).
  • После действия: тост + loadUser() (перечитать состояние). Главный экран при frozen показывает «ледяную» плашку ❄️ заморожена · авторазморозка через 30 дн.
function doFreeze(){
  var k = window.__KEY; if(!k||!k.key_id){ toast('Нет активного ключа'); return; }
  authFetch('/api/freeze',{method:'POST',headers:{'Content-Type':'application/json'},
    body:JSON.stringify({user_id:Number(window.__UID), key_id:k.key_id})})
   .then(r=>r.json())
   .then(j=>{ if(j&&j.ok){ toast('Подписка заморожена ❄️'); loadUser(); }
             else toast((j&&j.error)||'Не удалось заморозить'); });
}

Бэкенд отдаёт в объекте ключа: frozen (0/1), banked_days, days_left, last_freeze_at (+ _ms).

9) Чек-лист тестов

  • [ ] Заморозка при days_left < 2 → отказ.
  • [ ] Повторная заморозка в течение 30 дней → отказ с числом дней кулдауна.
  • [ ] Заморозка замороженного / разморозка не-замороженного / чужой key_id → отказ.
  • [ ] После заморозки: DISABLED, сквады/hwid/telegramId/трафик на месте, expireAt не изменился.
  • [ ] После разморозки: ACTIVE, expireAt ≈ now + banked_days.
  • [ ] Замороженный не получает «подписка кончается», с него не списывается автопродление.
  • [ ] Через 30 дней шедулер сам размораживает + уведомляет.
  • [ ] Мини-апп после действия показывает актуальное состояние (loadUser).

TL;DR: 4 колонки в vpn_keysset_user_frozen (GET→PATCH, сохрани поля; заморозка = expireAt как есть, разморозка = now + банк) → 3 функции БД → 2 эндпоинта с валидацией (владелец, ≥ 2 дня, раз/30д) → шедулер (авто-разморозка 30д + if frozen: continue во всех рассылках) → кнопки + loadUser(). Соблюди раздел «Нюансы»: там всё, из-за чего фича «теряет дни».

🛠 На практике это 4 поля в БД, две ручки (/freeze, /unfreeze), один PATCH в Remnawave и пара кнопок. Промпт выше проверен на проде, если нужна помощь с внедрением под ваш код, пишите @qwe8nxtroud.

Связанные приёмы удержания: «Спасательный круг» (grace) и Воронка и удержание.


Приём 2. РФ-нода: чтобы российские сервисы работали из-за границы

Почему «Выключите VPN» не лечится выключением VPN

Российские банки (Сбер, Т‑Банк, Альфа), Госуслуги, Ozon, Wildberries, Mir Pay, отечественные стриминги, гео‑ и антифрод‑фильтры смотрят на страну IP. Если IP не российский, варианты ответа: «доступно только в России», требование лишней верификации или баннер «вы используете VPN, отключите его».

Клиент за границей упирается в это с выключенным VPN тоже: его реальный IP (отель, местная симка) иностранный, аккаунт российский → сервис недоволен. Включит ваш VPN с заграничным выходом, IP снова не РФ → то же самое. Проблема не в «детекте VPN», а в географии IP. Выключать нечего: нужно наоборот дать клиенту российский IP.

Решение: добавить ноду в РФ

Поднимаете обычную ноду в России и настраиваете на ней стелс‑вход: vless-tcp-reality или vless-tcp-selfsteal. Клиент ходит в РФ‑сервисы через эту ноду → видит российский IP → банк и Госуслуги довольны. Reality/selfsteal здесь нужны не ради обхода (внутри РФ эти сервисы не блокируют), а чтобы сам хостер и провайдер не опознали ноду как прокси и не прирезали её.

Два способа отдать это клиенту:

  • Отдельный выход «🇷🇺 Россия». РФ‑нода добавляется как обычный сервер подписки. Клиент вручную переключается на неё, когда надо открыть банк, и обратно на заграничный выход для всего остального. Просто и понятно.
  • Авто‑роутинг (премиальный UX). Заграничный выход остаётся по умолчанию, а трафик к РФ‑сервисам (geosite:category-ru + домены банков) автоматически уходит через РФ‑ноду. Клиент вообще ничего не переключает: банк идёт по РФ‑IP, всё прочее через загранку. Как сделать правила, смотрите в Маршрутизация.

⚠️ Не гоните весь трафик через РФ. Российский выход только под РФ‑сервисы. Если завернуть туда всё, клиент получит и российскую цензуру, и слежку, то есть ровно то, от чего он покупал VPN.

Грабли: «грязный» РФ‑IP

Банки и антифрод не любят айпи дата‑центров. Если РФ‑выход даёт сбои на банке, IP, скорее всего, «грязный»: возьмите чистый адрес (см. Чистые IP и репутация, а массово подбирает MultiRoller) и по возможности хостера, чей пул не зашорен. Под этот сценарий abuse‑риск низкий: нода обслуживает только банковский/госовский трафик, а не «запрещёнку», поэтому жалоб хостеру по ней практически не бывает.


Какой РФ‑хостер взять под ноду

Нужен дешёвый VPS, локация Россия, Ubuntu/Debian, 1 vCPU / 1 ГБ: нагрузка минимальная. Три проверенных варианта:

Хостер Статус Под что
Beget 🇷🇺 рекомендую Удобная панель, оплата картой РФ, стабильные РФ‑локации. Хорош как нода РФ‑выхода под банки/Госуслуги. 👉 Регистрация
Timeweb 🇷🇺 пользуюсь сам Рубли/карта РФ, почасовая оплата, от ~150 ₽/мес, быстрый разворот. Мой основной РФ‑хост под инфраструктуру и релеи. 👉 Регистрация по нашей реф‑ссылке
UFO 🇷🇺 сам не пользовался По отзывам, недорогая альтернатива под РФ‑ноду; беру на заметку как запасной вариант.

📌 Эти РФ‑хосты берём именно под РФ‑выход (доступ к российским сервисам). Под основной заграничный выход РФ‑хостинг не используем: там abuse‑риск и блокировки; почему, смотрите в «Сервер за 15 минут».


Чек‑лист «отпускного» VPN

  • [ ] В боте/мини‑аппе есть кнопки ❄️ Заморозить / ▶️ Разморозить (банк дней banked_days, статус Remnawave DISABLED/ACTIVE).
  • [ ] Лимиты заморозки: раз в 30 дней, остаток ≥ 2 дней, авто‑разморозка через 30 дней.
  • [ ] Шедулер напоминаний/автопродления пропускает frozen‑ключи.
  • [ ] Поднята РФ‑нода (vless‑tcp‑reality или selfsteal) на дешёвом РФ‑VPS, локация Россия.
  • [ ] РФ‑выход отдан клиенту: отдельный сервер «🇷🇺 Россия» или авто‑роутинг РФ‑сервисов через Маршрутизацию.
  • [ ] IP РФ‑ноды чистый (проверен под банки), весь остальной трафик НЕ через РФ.

Связано: «Спасательный круг» (grace) и Воронка и удержание (другие приёмы против оттока); VLESS‑TCP‑Reality и Self‑steal с nginx (как поднять РФ‑ноду); Маршрутизация (завернуть РФ‑сервисы на РФ‑выход); Сервер за 15 минут (хостеры и фундамент).