docs: update docs
This commit is contained in:
@@ -0,0 +1,135 @@
|
||||
# Настройка окружения
|
||||
|
||||
Конфигурация читается из `.env`. За основу удобно взять `.env.example` и заполнить значения под свою панель, домены и платежные провайдеры.
|
||||
|
||||
```bash
|
||||
cp .env.example .env
|
||||
nano .env
|
||||
```
|
||||
|
||||
## Основные настройки
|
||||
|
||||
| Переменная | Назначение |
|
||||
| --- | --- |
|
||||
| `BOT_TOKEN` | Токен Telegram-бота. |
|
||||
| `ADMIN_IDS` | Telegram ID администраторов через запятую. |
|
||||
| `DEFAULT_LANGUAGE` | Язык по умолчанию для пользователей: `ru` или `en`. |
|
||||
| `SUPPORT_LINK` | Ссылка на поддержку. |
|
||||
| `PRIVACY_POLICY_URL` | Ссылка на политику конфиденциальности в Web App. |
|
||||
| `USER_AGREEMENT_URL` | Ссылка на пользовательское соглашение в Web App. |
|
||||
| `REQUIRED_CHANNEL_ID` | ID канала, на который пользователь должен подписаться перед использованием. |
|
||||
| `REQUIRED_CHANNEL_LINK` | Ссылка на канал для кнопки проверки подписки. |
|
||||
|
||||
Если используется проверка подписки на канал, добавьте бота администратором в этот канал. После первой успешной проверки пользователь продолжает работу без повторной блокировки действий.
|
||||
|
||||
## Remnawave
|
||||
|
||||
| Переменная | Назначение |
|
||||
| --- | --- |
|
||||
| `PANEL_API_URL` | URL API панели Remnawave, например `https://panel.domain.com/api`. |
|
||||
| `PANEL_API_KEY` | API-ключ панели. |
|
||||
| `PANEL_WEBHOOK_SECRET` | Секрет для проверки вебхуков Remnawave. |
|
||||
| `USER_SQUAD_UUIDS` | Internal Squads, в которые добавляются пользователи. |
|
||||
| `USER_EXTERNAL_SQUAD_UUID` | External Squad для пользователей, если он используется. |
|
||||
| `USER_TRAFFIC_LIMIT_GB` | Лимит трафика для режима без JSON-каталога тарифов. `0` означает безлимит. |
|
||||
| `USER_TRAFFIC_STRATEGY` | Стратегия лимита трафика для режима без JSON-каталога тарифов. |
|
||||
| `USER_HWID_DEVICE_LIMIT` | Лимит HWID-устройств для пользователей. `0` означает безлимит. |
|
||||
|
||||
При включенном каталоге тарифов значения `squad_uuids`, `monthly_gb`, `traffic_packages` и `hwid_device_limit` берутся из выбранного тарифа. Подробно это описано в [tariffs.md](tariffs.md).
|
||||
|
||||
## Платежи
|
||||
|
||||
| Переменная | Назначение |
|
||||
| --- | --- |
|
||||
| `PAYMENT_METHODS_ORDER` | Порядок кнопок оплаты через запятую: `severpay`, `freekassa`, `platega`, `yookassa`, `stars`, `cryptopay`. |
|
||||
| `YOOKASSA_ENABLED` | Включает YooKassa. |
|
||||
| `YOOKASSA_SHOP_ID` / `YOOKASSA_SECRET_KEY` | Данные магазина YooKassa. |
|
||||
| `YOOKASSA_AUTOPAYMENTS_ENABLED` | Включает автопродление через сохраненные способы оплаты YooKassa. |
|
||||
| `YOOKASSA_AUTOPAYMENTS_REQUIRE_CARD_BINDING` | Управляет обязательной привязкой карты при оплате. |
|
||||
| `FREEKASSA_ENABLED` | Включает FreeKassa. |
|
||||
| `FREEKASSA_MERCHANT_ID` / `FREEKASSA_API_KEY` / `FREEKASSA_SECOND_SECRET` | Данные FreeKassa и секрет уведомлений. |
|
||||
| `FREEKASSA_PAYMENT_IP` | Внешний IP сервера для запроса оплаты FreeKassa. |
|
||||
| `FREEKASSA_PAYMENT_METHOD_ID` | ID метода оплаты FreeKassa. |
|
||||
| `PLATEGA_ENABLED` | Включает Platega. |
|
||||
| `PLATEGA_MERCHANT_ID` / `PLATEGA_SECRET` | Данные Platega. |
|
||||
| `PLATEGA_PAYMENT_METHOD` | ID способа оплаты Platega. |
|
||||
| `PLATEGA_RETURN_URL` / `PLATEGA_FAILED_URL` | URL возврата после оплаты или ошибки. |
|
||||
| `SEVERPAY_ENABLED` | Включает SeverPay. |
|
||||
| `SEVERPAY_MID` / `SEVERPAY_TOKEN` | Данные SeverPay. |
|
||||
| `SEVERPAY_BASE_URL` | Базовый URL API SeverPay. |
|
||||
| `SEVERPAY_RETURN_URL` | URL возврата после оплаты. |
|
||||
| `SEVERPAY_LIFETIME_MINUTES` | Время жизни платежной ссылки. |
|
||||
| `CRYPTOPAY_ENABLED` | Включает CryptoPay. |
|
||||
| `CRYPTOPAY_TOKEN` | Токен CryptoPay App. |
|
||||
| `STARS_ENABLED` | Включает Telegram Stars. |
|
||||
|
||||
Вебхуки платежных систем должны проксироваться на порт `WEB_SERVER_PORT`. Примеры маршрутов есть в [deployment.md](deployment.md).
|
||||
|
||||
## Тарифы
|
||||
|
||||
| Переменная | Назначение |
|
||||
| --- | --- |
|
||||
| `TARIFFS_CONFIG_PATH` | Путь к JSON-каталогу тарифов. По умолчанию `config/tariffs.json`. |
|
||||
| `TARIFF_TRAFFIC_WARNING_LEVELS` | Проценты предупреждений по трафику, например `85,90,95`. |
|
||||
| `RUB_PRICE_1_MONTH`, `RUB_PRICE_3_MONTHS`, `RUB_PRICE_6_MONTHS`, `RUB_PRICE_12_MONTHS` | Цены подписок в рублях для режима без JSON-каталога. |
|
||||
| `STARS_PRICE_1_MONTH`, `STARS_PRICE_3_MONTHS`, `STARS_PRICE_6_MONTHS`, `STARS_PRICE_12_MONTHS` | Цены подписок в Telegram Stars для режима без JSON-каталога. |
|
||||
| `1_MONTH_ENABLED`, `3_MONTHS_ENABLED`, `6_MONTHS_ENABLED`, `12_MONTHS_ENABLED` | Доступность периодов подписки для режима без JSON-каталога. |
|
||||
| `TRAFFIC_PACKAGES` | Пакеты трафика в рублях для режима без JSON-каталога, например `10:199,50:799`. |
|
||||
| `STARS_TRAFFIC_PACKAGES` | Пакеты трафика в Telegram Stars для режима без JSON-каталога. |
|
||||
|
||||
Если файл из `TARIFFS_CONFIG_PATH` существует, бот использует каталог тарифов. Если файла нет, применяется конфигурация из переменных `.env`.
|
||||
|
||||
## Web App и email-вход
|
||||
|
||||
| Переменная | Назначение |
|
||||
| --- | --- |
|
||||
| `WEBAPP_ENABLED` | Включает Web App в том же контейнере. |
|
||||
| `WEBAPP_SERVER_HOST` / `WEBAPP_SERVER_PORT` | Хост и порт Web App. По умолчанию порт `8081`. |
|
||||
| `SUBSCRIPTION_MINI_APP_URL` | Публичный URL Web App. |
|
||||
| `WEBAPP_TITLE` | Заголовок Web App. |
|
||||
| `WEBAPP_PRIMARY_COLOR` | Основной цвет интерфейса. |
|
||||
| `WEBAPP_LOGO_URL` | URL логотипа Web App. |
|
||||
| `WEBAPP_SESSION_SECRET` | HMAC-секрет сессий Web App. |
|
||||
| `TELEGRAM_OAUTH_CLIENT_ID` / `TELEGRAM_OAUTH_CLIENT_SECRET` | Данные Telegram OAuth / OpenID Connect из BotFather. |
|
||||
| `TELEGRAM_OAUTH_REQUEST_ACCESS` | Дополнительные разрешения Telegram Login, например `write`. |
|
||||
| `SMTP_HOST`, `SMTP_PORT`, `SMTP_FALLBACK_PORTS` | SMTP-подключение для email-кодов. |
|
||||
| `SMTP_USERNAME` / `SMTP_PASSWORD` | Логин и пароль или SMTP key. |
|
||||
| `SMTP_FROM_EMAIL` / `SMTP_FROM_NAME` | Отправитель писем с кодом. |
|
||||
| `EMAIL_CODE_TTL_SECONDS` | Срок действия email-кода. |
|
||||
| `EMAIL_CODE_RESEND_SECONDS` | Пауза перед повторной отправкой кода. |
|
||||
| `EMAIL_CODE_MAX_ATTEMPTS` | Максимум попыток ввода одного кода. |
|
||||
| `BRUTE_FORCE_MAX_FAILURES` | Количество неудачных попыток до временной блокировки. |
|
||||
| `BRUTE_FORCE_WINDOW_SECONDS` | Окно учета неудачных попыток. |
|
||||
| `BRUTE_FORCE_LOCK_SECONDS` | Длительность временной блокировки. |
|
||||
| `MY_DEVICES_SECTION_ENABLED` | Показывает раздел "Мои устройства" и включает API устройств. |
|
||||
|
||||
Настройка домена, BotFather и callback URL описана в [webapp.md](webapp.md).
|
||||
|
||||
## Пробный период
|
||||
|
||||
| Переменная | Назначение |
|
||||
| --- | --- |
|
||||
| `TRIAL_ENABLED` | Включает пробный период. |
|
||||
| `TRIAL_DURATION_DAYS` | Длительность пробного периода в днях. |
|
||||
| `TRIAL_TRAFFIC_LIMIT_GB` | Лимит трафика пробного периода. `0` означает безлимит. |
|
||||
| `TRIAL_TRAFFIC_STRATEGY` | Стратегия лимита трафика пробного периода. |
|
||||
|
||||
## Реферальная программа
|
||||
|
||||
| Переменная | Назначение |
|
||||
| --- | --- |
|
||||
| `REFERRAL_WELCOME_BONUS_DAYS` | Бонус пользователю, который пришел по реферальной ссылке. |
|
||||
| `REFERRAL_ONE_BONUS_PER_REFEREE` | Ограничивает бонусы одним успешным платежом приглашенного пользователя. |
|
||||
| `REFERRAL_BONUS_DAYS_*` | Бонусные дни пригласившему по периодам подписки. |
|
||||
| `REFEREE_BONUS_DAYS_*` | Бонусные дни приглашенному по периодам подписки. |
|
||||
| `LEGACY_REFS` | Разрешает ссылки формата `ref_<telegram_id>`. |
|
||||
|
||||
В режиме продажи трафика без JSON-каталога бонусы по периодам не отображаются, потому что покупка не привязана к сроку подписки.
|
||||
|
||||
## Секреты
|
||||
|
||||
`WEBAPP_SESSION_SECRET` и `WEBHOOK_SECRET_TOKEN` могут генерироваться при старте, но для рабочего окружения их лучше задать явно. Иначе после рестарта сессии Web App станут невалидными, а Telegram webhook будет установлен с другим secret token.
|
||||
|
||||
```bash
|
||||
openssl rand -hex 32
|
||||
```
|
||||
@@ -0,0 +1,176 @@
|
||||
# Развертывание
|
||||
|
||||
Документ описывает запуск через Docker Compose, маршруты вебхуков и варианты reverse proxy. Перед запуском заполните `.env` по [configuration.md](configuration.md).
|
||||
|
||||
## Docker Compose
|
||||
|
||||
Локальная сборка:
|
||||
|
||||
```bash
|
||||
docker compose up -d --build
|
||||
docker compose logs -f remnawave-minishop
|
||||
```
|
||||
|
||||
Запуск из готового образа:
|
||||
|
||||
```bash
|
||||
IMAGE_TAG=3.1.0 docker compose -f docker-compose-remote-server.yml up -d
|
||||
```
|
||||
|
||||
`docker-compose-remote-server.yml` можно использовать как шаблон и заменить `image:` на нужный образ. По умолчанию используется `ghcr.io/3252a8/remnawave-minishop:latest`.
|
||||
|
||||
## Порты
|
||||
|
||||
| Порт | Назначение |
|
||||
| --- | --- |
|
||||
| `WEB_SERVER_PORT` (`8080`) | Telegram webhook, платежные вебхуки, Remnawave webhook. |
|
||||
| `WEBAPP_SERVER_PORT` (`8081`) | Web App / Mini App. |
|
||||
|
||||
Web App не должен проксироваться на `WEB_SERVER_PORT`.
|
||||
|
||||
## Маршруты вебхуков
|
||||
|
||||
Проксируйте платежные и системные вебхуки на `WEB_SERVER_PORT`:
|
||||
|
||||
- `https://<webhook-domain>/webhook/yookassa` -> `http://remnawave-minishop:<WEB_SERVER_PORT>/webhook/yookassa`;
|
||||
- `https://<webhook-domain>/webhook/freekassa` -> `http://remnawave-minishop:<WEB_SERVER_PORT>/webhook/freekassa`;
|
||||
- `https://<webhook-domain>/webhook/platega` -> `http://remnawave-minishop:<WEB_SERVER_PORT>/webhook/platega`;
|
||||
- `https://<webhook-domain>/webhook/severpay` -> `http://remnawave-minishop:<WEB_SERVER_PORT>/webhook/severpay`;
|
||||
- `https://<webhook-domain>/webhook/cryptopay` -> `http://remnawave-minishop:<WEB_SERVER_PORT>/webhook/cryptopay`;
|
||||
- `https://<webhook-domain>/webhook/panel` -> `http://remnawave-minishop:<WEB_SERVER_PORT>/webhook/panel`.
|
||||
|
||||
Telegram webhook устанавливается приложением, если задан `WEBHOOK_BASE_URL`. Путь формируется как `https://<webhook-domain>/<BOT_TOKEN>`.
|
||||
|
||||
## Nginx рядом с Remnawave
|
||||
|
||||
Пример upstream и server-блока для домена вебхуков:
|
||||
|
||||
```nginx
|
||||
upstream remnawave-minishop {
|
||||
server remnawave-minishop:8080;
|
||||
}
|
||||
|
||||
map $http_upgrade $connection_upgrade {
|
||||
default upgrade;
|
||||
"" close;
|
||||
}
|
||||
|
||||
server {
|
||||
server_name webhook.domain.com;
|
||||
listen 443 ssl;
|
||||
http2 on;
|
||||
|
||||
ssl_certificate "/etc/nginx/ssl/webhook_fullchain.pem";
|
||||
ssl_certificate_key "/etc/nginx/ssl/webhook_privkey.key";
|
||||
ssl_trusted_certificate "/etc/nginx/ssl/webhook_fullchain.pem";
|
||||
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection $connection_upgrade;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
proxy_set_header X-Forwarded-Host $host;
|
||||
proxy_set_header X-Forwarded-Port $server_port;
|
||||
proxy_send_timeout 60s;
|
||||
proxy_read_timeout 60s;
|
||||
proxy_intercept_errors on;
|
||||
error_page 400 404 500 502 @redirect;
|
||||
|
||||
location / {
|
||||
proxy_pass http://remnawave-minishop$request_uri;
|
||||
}
|
||||
|
||||
location @redirect {
|
||||
return 404;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Для Web App используйте отдельный upstream на `WEBAPP_SERVER_PORT`; пример есть в [webapp.md](webapp.md).
|
||||
|
||||
## SSL для домена вебхуков
|
||||
|
||||
Пример выпуска сертификата через `acme.sh`:
|
||||
|
||||
```bash
|
||||
sudo apt-get install cron socat
|
||||
curl https://get.acme.sh | sh -s email=EMAIL
|
||||
source ~/.bashrc
|
||||
ufw allow 80/tcp
|
||||
ufw reload
|
||||
|
||||
acme.sh --set-default-ca --server letsencrypt
|
||||
acme.sh --issue --standalone -d 'webhook.domain.com' \
|
||||
--key-file /opt/remnawave/nginx/webhook_privkey.key \
|
||||
--fullchain-file /opt/remnawave/nginx/webhook_fullchain.pem
|
||||
```
|
||||
|
||||
Если Nginx панели Remnawave запускается в Docker, добавьте сертификаты в `volumes` сервиса Nginx:
|
||||
|
||||
```yaml
|
||||
services:
|
||||
remnawave-nginx:
|
||||
volumes:
|
||||
- ./webhook_fullchain.pem:/etc/nginx/ssl/webhook_fullchain.pem:ro
|
||||
- ./webhook_privkey.key:/etc/nginx/ssl/webhook_privkey.key:ro
|
||||
```
|
||||
|
||||
После изменения конфигурации перезапустите Nginx:
|
||||
|
||||
```bash
|
||||
cd /opt/remnawave/nginx
|
||||
docker compose down
|
||||
docker compose up -d
|
||||
docker compose logs -f -t
|
||||
```
|
||||
|
||||
## Caddy
|
||||
|
||||
Для схемы с Caddy используйте `docker-compose-caddy.yml` и `Caddyfile`. Caddy публикует наружу `80` и `443`, выпускает TLS-сертификаты и проксирует вебхуки и Web App на разные внутренние порты.
|
||||
|
||||
Пример `Caddyfile`:
|
||||
|
||||
```caddyfile
|
||||
webhook.domain.com {
|
||||
encode zstd gzip
|
||||
reverse_proxy remnawave-minishop:{$WEB_SERVER_PORT:8080}
|
||||
}
|
||||
|
||||
app.domain.com {
|
||||
encode zstd gzip
|
||||
reverse_proxy remnawave-minishop:{$WEBAPP_SERVER_PORT:8081}
|
||||
}
|
||||
```
|
||||
|
||||
В `.env` укажите:
|
||||
|
||||
```env
|
||||
WEBHOOK_BASE_URL=https://webhook.domain.com
|
||||
SUBSCRIPTION_MINI_APP_URL=https://app.domain.com/
|
||||
```
|
||||
|
||||
Запуск:
|
||||
|
||||
```bash
|
||||
docker compose -f docker-compose-caddy.yml up -d --build
|
||||
```
|
||||
|
||||
В BotFather укажите домен Mini App через настройки домена, чтобы он совпадал с `SUBSCRIPTION_MINI_APP_URL`.
|
||||
|
||||
## Проверка
|
||||
|
||||
```bash
|
||||
docker compose ps
|
||||
docker compose logs -f remnawave-minishop
|
||||
```
|
||||
|
||||
Проверьте:
|
||||
|
||||
- бот отвечает в Telegram;
|
||||
- Telegram webhook установлен без ошибок в логах;
|
||||
- платежные вебхуки доходят до приложения;
|
||||
- Remnawave webhook проходит проверку `PANEL_WEBHOOK_SECRET`;
|
||||
- Web App открывается по домену из `SUBSCRIPTION_MINI_APP_URL`;
|
||||
- в BotFather разрешены URL Web App и callback.
|
||||
+193
-65
@@ -1,99 +1,227 @@
|
||||
# Тарифы 2.0
|
||||
# Тарифы
|
||||
|
||||
Бот поддерживает каталог тарифов в JSON-файле. Путь задается через `TARIFFS_CONFIG_PATH`, по умолчанию используется `config/tariffs.json`.
|
||||
Бот поддерживает два способа описания продаж:
|
||||
|
||||
Если файл отсутствует, включается legacy-режим: используются старые `.env` поля `RUB_PRICE_*`, `STARS_PRICE_*`, `USER_TRAFFIC_LIMIT_GB`, `USER_SQUAD_UUIDS`, а также старый режим продажи трафика через `TRAFFIC_PACKAGES`.
|
||||
- JSON-каталог тарифов из `TARIFFS_CONFIG_PATH` (по умолчанию `config/tariffs.json`);
|
||||
- конфигурация через переменные `.env`, если JSON-файл отсутствует.
|
||||
|
||||
## Конфиг
|
||||
JSON-каталог может содержать несколько тарифов разных моделей: подписки на срок, пакеты трафика без срока действия, разные наборы Internal Squads, лимиты устройств и пакеты докупки. Пример формата: [config/tariffs.example.json](../config/tariffs.example.json).
|
||||
|
||||
Пример конфига: [`config/tariffs.example.json`](../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-каталога
|
||||
|
||||
Минимальная структура:
|
||||
|
||||
```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` | Тариф по умолчанию для миграции существующих подписок и первичного выбора |
|
||||
| `topup_packages_default` | Пакеты докупки трафика для period-тарифов без собственных пакетов |
|
||||
| `tariffs[].billing_model` | Модель тарификации: `period` или `traffic` |
|
||||
| `tariffs[].squad_uuids` | Internal Squads Remnawave, которые получает пользователь на этом тарифе |
|
||||
| `tariffs[].hwid_device_limit` | Базовый лимит HWID-устройств для тарифа; `0` означает без ограничений, отсутствие поля использует `USER_HWID_DEVICE_LIMIT` |
|
||||
| `tariffs[].hwid_device_packages` | Пакеты докупки HWID-устройств, например `{ "count": 1, "price": 99 }` |
|
||||
| `prices_rub` / `prices_stars` | Цены period-тарифов по месяцам |
|
||||
| `traffic_packages` | Пакеты GB для traffic-тарифов |
|
||||
| `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-Тариф
|
||||
Для `period`-тарифа также используются:
|
||||
|
||||
Period-тариф продает доступ на срок и лимит трафика с календарным ежемесячным сбросом.
|
||||
| Поле | Назначение |
|
||||
| --- | --- |
|
||||
| `monthly_gb` | Базовый месячный лимит трафика тарифа. `0` означает безлимит. |
|
||||
| `prices_rub` | Цены периодов в рублях, ключ - количество месяцев. |
|
||||
| `prices_stars` | Цены периодов в Telegram Stars. |
|
||||
| `enabled_periods` | Периоды, доступные для покупки. |
|
||||
| `topup_packages` | Пакеты докупки трафика именно для этого тарифа. |
|
||||
|
||||
- `monthly_gb` превращается в `tier_baseline_bytes`.
|
||||
- Докупленные пакеты трафика хранятся в `topup_balance_bytes`.
|
||||
- В Remnawave отправляется `trafficLimitBytes = tier_baseline_bytes + topup_balance_bytes`.
|
||||
- Для period-тарифов бот выставляет `trafficLimitStrategy = MONTH`, а дальнейший сброс выполняет сама панель.
|
||||
- Дата сброса больше не считается в боте.
|
||||
- Если покупка или продление были в середине месяца, сброс все равно произойдет по правилам панели для `MONTH`.
|
||||
- Бот меняет только лимиты в GB и следит за предупреждениями на основе текущего usage из панели.
|
||||
Для `traffic`-тарифа используются:
|
||||
|
||||
## Traffic-Тариф
|
||||
| Поле | Назначение |
|
||||
| --- | --- |
|
||||
| `traffic_packages` | Пакеты трафика в GB для рублей и Telegram Stars. |
|
||||
| `conversion_rate_rub_per_gb` | Курс для конвертации оставшихся дней period-тарифа в GB при смене на traffic-тариф. |
|
||||
|
||||
Traffic-тариф продает GB без срока действия.
|
||||
Если у traffic-тарифа нет RUB-пакетов, `conversion_rate_rub_per_gb` обязателен.
|
||||
|
||||
- `end_date` технически ставится в `2099-01-01 UTC`.
|
||||
- `period_start_at = NULL`.
|
||||
- `trafficLimitStrategy = NO_RESET`.
|
||||
- Новая покупка добавляет GB к фактическому остатку: `limit = used + remaining + purchased`.
|
||||
- Доступ ограничивается только при исчерпании купленного трафика.
|
||||
## Period-тарифы
|
||||
|
||||
## HWID-Устройства
|
||||
`period` продает доступ на срок с месячным лимитом трафика.
|
||||
|
||||
Тарифы поддерживают лимит HWID-устройств и платную докупку устройств.
|
||||
При покупке или продлении:
|
||||
|
||||
- При покупке или продлении тарифа бот отправляет в Remnawave `hwidDeviceLimit`.
|
||||
- `subscriptions.hwid_device_limit` хранит базовый лимит тарифа.
|
||||
- `subscriptions.extra_hwid_devices` хранит количество докупленных устройств.
|
||||
- Эффективный лимит равен `hwid_device_limit + extra_hwid_devices`.
|
||||
- Если базовый лимит равен `0`, это безлимит; докупка не нужна, а в панель отправляется `0`.
|
||||
- Докупка устройств использует `sale_mode=hwid_devices`.
|
||||
- Количество купленных устройств сохраняется в `payments.purchased_hwid_devices`.
|
||||
- История докупок пишется в `hwid_device_purchases`.
|
||||
- Докупка доступна в Web App через `/api/devices/topup-options` и `/api/payments`.
|
||||
- Докупка доступна в Telegram-боте из раздела устройств.
|
||||
- При смене тарифа базовый HWID-лимит берется из нового тарифа, а уже докупленные устройства сохраняются.
|
||||
- дата начала берется от текущей активной подписки, если она еще действует, иначе от текущего времени;
|
||||
- срок считается календарными месяцами через `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-тарифов.
|
||||
|
||||
Смена тарифа пишется в `tariff_changes`.
|
||||
Докупка трафика для period-тарифа увеличивает `topup_balance_bytes` и общий `traffic_limit_bytes`. Этот баланс сохраняется в подписке и учитывается при продлении period-тарифа. В панель отправляется актуальный лимит, а доступ переводится в `ACTIVE`.
|
||||
|
||||
- `period -> period`: расчет идет от `effective_monthly_price_rub`; пересчет дней использует `floor`.
|
||||
- `period -> traffic`: остаток оплаченных дней конвертируется в GB по `conversion_rate_rub_per_gb` или по минимальной цене GB в RUB-пакетах.
|
||||
- `traffic -> period`: пользователь покупает период, а остаток GB сохраняется как top-up поверх нового тарифа.
|
||||
- При смене тарифа обновляются Internal Squads, лимит трафика, стратегия сброса и базовый HWID-лимит.
|
||||
## 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 к фактическому остатку:
|
||||
|
||||
```text
|
||||
remaining = max(0, current_limit - current_used)
|
||||
balance_after = remaining + purchased
|
||||
limit_after = current_used + balance_after
|
||||
```
|
||||
|
||||
Так пользователь не теряет уже оплаченный остаток, а Remnawave продолжает считать общий лимит от текущего использованного трафика.
|
||||
|
||||
Если докупка трафика вызывается для traffic-тарифа, она обрабатывается как покупка очередного пакета этого же traffic-тарифа.
|
||||
|
||||
## HWID-устройства
|
||||
|
||||
Тариф может задавать базовый лимит устройств и пакеты докупки:
|
||||
|
||||
```json
|
||||
{
|
||||
"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-покупок;
|
||||
- `purchased_hwid_devices` для докупки HWID-устройств.
|
||||
| Поле | Назначение |
|
||||
| --- | --- |
|
||||
| `sale_mode` | Тип продажи: `subscription`, `traffic_package`, `topup`, `tariff_upgrade`, `hwid_devices`. |
|
||||
| `tariff_key` | Ключ тарифа, к которому относится платеж. |
|
||||
| `purchased_gb` | Купленный объем GB для traffic-пакетов и докупки трафика. |
|
||||
| `purchased_hwid_devices` | Количество устройств при докупке HWID. |
|
||||
| `subscription_duration_months` | Количество месяцев для подписки на срок; также используется платежными обработчиками как числовое поле покупки. |
|
||||
|
||||
Legacy-поле `subscription_duration_months` остается для совместимости с существующими платежными обработчиками.
|
||||
В callback и metadata платежных провайдеров `sale_mode` может передаваться с суффиксом тарифа, например `subscription@standard` или `topup@standard`. При активации платежа тариф сохраняется отдельно в `tariff_key`.
|
||||
|
||||
## Поведение При Исчерпании Трафика
|
||||
## Предупреждения и исчерпание трафика
|
||||
|
||||
Remnawave сама ограничивает пользователя при достижении `trafficLimitBytes`: панель переводит пользователя в статус `LIMITED`. Бот не должен удалять пользователя из Internal Squads при 100% использования трафика.
|
||||
Remnawave ограничивает доступ при достижении `trafficLimitBytes`, переводя пользователя в статус `LIMITED`. Бот не удаляет пользователя из Internal Squads при 100% использования трафика.
|
||||
|
||||
Текущее поведение воркера:
|
||||
`TariffTrafficWorker` запускается, когда активен JSON-каталог тарифов. Раз в 300 секунд он:
|
||||
|
||||
- синхронизирует из панели `status`, `trafficLimitBytes`, `usedTrafficBytes` и `trafficLimitStrategy`;
|
||||
- для period-тарифов выставляет `trafficLimitStrategy = MONTH`, если панель еще показывает другую стратегию;
|
||||
- отправляет предупреждения на уровнях из `TARIFF_TRAFFIC_WARNING_LEVELS` (по умолчанию `85,90,95`);
|
||||
- оставляет блокировку и разблокировку доступа штатной логике Remnawave.
|
||||
- не отправляет `status=ACTIVE` при простой синхронизации стратегии, чтобы не снять статус `LIMITED`, выставленный Remnawave;
|
||||
- дедуплицирует предупреждения через `traffic_warnings`.
|
||||
|
||||
## Воркеры
|
||||
Для period-тарифов дедупликация предупреждений привязана к началу текущего месяца. Для traffic-тарифов она учитывает текущий `trafficLimitBytes`, чтобы после покупки очередного пакета пользователь мог получить следующий набор предупреждений.
|
||||
|
||||
`TariffTrafficWorker` запускается только при активном `tariffs.json`.
|
||||
Подписки, которые были ограничены логикой предыдущих запусков бота (`is_throttled=True`), восстанавливаются воркером только когда лимит снова больше использованного трафика.
|
||||
|
||||
- Раз в несколько минут синхронизирует `trafficLimitStrategy = MONTH` для period-тарифов, если панель еще не переключена.
|
||||
- При синхронизации стратегии не отправляет `status=ACTIVE`, чтобы случайно не снять статус `LIMITED`, выставленный Remnawave.
|
||||
- Дедуплицирует предупреждения через таблицу `traffic_warnings`.
|
||||
- Для traffic-тарифов дедупликация предупреждений учитывает текущий `trafficLimitBytes`, чтобы новая покупка трафика могла создать новый набор предупреждений.
|
||||
## Автопродление, пробный период и бонусы
|
||||
|
||||
Автопродление через 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` берется из последнего успешного платежа или из цены тарифа по умолчанию.
|
||||
|
||||
Это позволяет существующим активным подпискам отображаться и управляться в интерфейсах тарифов.
|
||||
|
||||
+119
@@ -0,0 +1,119 @@
|
||||
# Web App / Mini App
|
||||
|
||||
Web App запускается в том же контейнере, что и бот, но слушает отдельный порт `WEBAPP_SERVER_PORT` (по умолчанию `8081`). Порт `WEB_SERVER_PORT` остается для Telegram, платежных и Remnawave вебхуков.
|
||||
|
||||
## Что показывает Web App
|
||||
|
||||
- текущую ссылку подключения;
|
||||
- статус и дату окончания подписки;
|
||||
- использованный и доступный трафик;
|
||||
- доступные тарифы, способы оплаты и платежный статус;
|
||||
- смену тарифа и докупку трафика при настроенном каталоге тарифов;
|
||||
- раздел "Мои устройства" при `MY_DEVICES_SECTION_ENABLED=True`;
|
||||
- реферальную ссылку и статистику приглашений;
|
||||
- привязку email и Telegram к одному аккаунту.
|
||||
|
||||
## Настройки `.env`
|
||||
|
||||
```env
|
||||
WEBAPP_ENABLED=True
|
||||
WEBAPP_SERVER_HOST=0.0.0.0
|
||||
WEBAPP_SERVER_PORT=8081
|
||||
SUBSCRIPTION_MINI_APP_URL=https://app.domain.com/
|
||||
WEBAPP_TITLE="Моя подписка"
|
||||
WEBAPP_PRIMARY_COLOR="#00fe7a"
|
||||
WEBAPP_LOGO_URL=
|
||||
WEBAPP_SESSION_SECRET=<stable-random-secret>
|
||||
|
||||
TELEGRAM_OAUTH_CLIENT_ID=<client-id-from-botfather>
|
||||
TELEGRAM_OAUTH_CLIENT_SECRET=<client-secret-from-botfather>
|
||||
TELEGRAM_OAUTH_REQUEST_ACCESS=write
|
||||
|
||||
SMTP_HOST=smtp-relay.brevo.com
|
||||
SMTP_PORT=587
|
||||
SMTP_FALLBACK_PORTS=2525,465
|
||||
SMTP_USERNAME=<smtp-login>
|
||||
SMTP_PASSWORD=<smtp-password-or-key>
|
||||
SMTP_FROM_EMAIL=no-reply@domain.com
|
||||
SMTP_FROM_NAME=Remnawave Minishop
|
||||
```
|
||||
|
||||
Если `WEBAPP_LOGO_URL` пустой, логотип в Web App не показывается. Если SMTP-настройки не заполнены, вход по email скрывается.
|
||||
|
||||
## Telegram-авторизация
|
||||
|
||||
Внутри Telegram Mini App пользователь авторизуется через Telegram Mini Apps `initData`. При открытии страницы вне Telegram используется Telegram OAuth / OpenID Connect Authorization Code Flow с PKCE, callback `/auth/telegram/callback`, `nonce` и серверной проверкой `id_token` по JWKS Telegram.
|
||||
|
||||
Настройка в BotFather:
|
||||
|
||||
1. Откройте `@BotFather` -> `/mybots` -> выберите бота.
|
||||
2. В `Bot Settings` -> `Domain` укажите домен Web App без протокола и пути, например `app.domain.com`.
|
||||
3. В `Bot Settings` -> `Mini Apps` укажите URL, например `https://app.domain.com/`.
|
||||
4. В `Bot Settings` -> `Web Login` включите OpenID Connect Login, если BotFather предлагает переключение.
|
||||
5. Скопируйте Client ID и Client Secret в `TELEGRAM_OAUTH_CLIENT_ID` и `TELEGRAM_OAUTH_CLIENT_SECRET`.
|
||||
6. В `Web Login` -> `Allowed URLs` добавьте:
|
||||
|
||||
```text
|
||||
https://app.domain.com/
|
||||
https://app.domain.com/auth/telegram/callback
|
||||
```
|
||||
|
||||
`TELEGRAM_OAUTH_REQUEST_ACCESS=write` разрешает боту написать пользователю после логина. Если дополнительные разрешения не нужны, оставьте переменную пустой.
|
||||
|
||||
## Email-вход
|
||||
|
||||
Email-вход работает через одноразовый код:
|
||||
|
||||
1. Пользователь вводит email.
|
||||
2. Бот отправляет код через SMTP.
|
||||
3. Код вводится в модальном окне Web App.
|
||||
4. После подтверждения создается или находится пользователь, а email можно связать с Telegram-аккаунтом.
|
||||
|
||||
Для Brevo обычно подходит порт `587` с STARTTLS. Если основной порт недоступен, приложение пробует порты из `SMTP_FALLBACK_PORTS`; порт `465` используется через SSL.
|
||||
|
||||
## Проксирование
|
||||
|
||||
Web App должен проксироваться отдельно от вебхуков:
|
||||
|
||||
```nginx
|
||||
upstream remnawave-minishop-webapp {
|
||||
server remnawave-minishop:8081;
|
||||
}
|
||||
|
||||
server {
|
||||
server_name app.domain.com;
|
||||
listen 443 ssl;
|
||||
http2 on;
|
||||
|
||||
ssl_certificate "/etc/nginx/ssl/app_fullchain.pem";
|
||||
ssl_certificate_key "/etc/nginx/ssl/app_privkey.key";
|
||||
|
||||
location / {
|
||||
proxy_pass http://remnawave-minishop-webapp;
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
В `docker-compose.yml` порт Web App публикуется отдельно:
|
||||
|
||||
```yaml
|
||||
ports:
|
||||
- 127.0.0.1:8080:8080
|
||||
- 127.0.0.1:${WEBAPP_SERVER_PORT:-8081}:${WEBAPP_SERVER_PORT:-8081}
|
||||
```
|
||||
|
||||
## Реферальные ссылки
|
||||
|
||||
Реферальные ссылки доступны в двух форматах:
|
||||
|
||||
- Telegram deep-link: `https://t.me/<bot>?start=ref_u<code>`;
|
||||
- Web App ссылка: `https://app.domain.com/?ref=u<code>`.
|
||||
|
||||
Web App учитывает `ref`, `start`, `start_param` и Telegram Mini Apps `start_param`, сохраняет найденный параметр до авторизации и передает его в Telegram OAuth или email-вход.
|
||||
|
||||
Для email-регистраций пользователь в Remnawave создается с username вида `em_<referral_code>`. Email добавляется в описание пользователя панели и, если API панели принимает поле `email`, передается отдельным полем. Для Telegram-регистраций используется username `tg_<telegram_id>`.
|
||||
Reference in New Issue
Block a user