Merge branch 'dev' into patch-1
This commit is contained in:
@@ -59,6 +59,10 @@
|
||||
| `PANEL_DEVICES_CACHE_TTL_SECONDS` | TTL кеша устройств пользователя Remnawave. |
|
||||
| `PANEL_ALL_USERS_CACHE_TTL_SECONDS` | TTL кеша полных сканов пользователей Remnawave. |
|
||||
| `PANEL_ALL_USERS_PAGE_SIZE` | Размер страницы Remnawave `/users`. |
|
||||
| `PANEL_API_TOTAL_TIMEOUT_SECONDS` | Общий timeout запроса к Remnawave API. |
|
||||
| `PANEL_API_CONNECT_TIMEOUT_SECONDS` | Timeout получения соединения с Remnawave API. |
|
||||
| `PANEL_API_SOCK_CONNECT_TIMEOUT_SECONDS` | Timeout TCP/TLS-подключения к Remnawave API. |
|
||||
| `PANEL_API_SOCK_READ_TIMEOUT_SECONDS` | Timeout ожидания данных ответа Remnawave API. |
|
||||
| `ADMIN_PANEL_STATS_CACHE_TTL_SECONDS` | TTL статистики Remnawave в админке. |
|
||||
| `ADMIN_DB_STATS_CACHE_TTL_SECONDS` | TTL дорогих DB-агрегатов админки. |
|
||||
| `ADMIN_USERS_LIST_CACHE_TTL_SECONDS` | TTL списка пользователей админки. |
|
||||
@@ -101,11 +105,10 @@
|
||||
| `DEFAULT_CURRENCY_SYMBOL` | Символ/код валюты в интерфейсе. |
|
||||
| `SUPPORT_LINK` | Внешняя ссылка поддержки. |
|
||||
| `SERVER_STATUS_URL` | Страница статуса сервиса. |
|
||||
| `TERMS_OF_SERVICE_URL` | Условия использования. |
|
||||
| `PRIVACY_POLICY_URL` | Политика конфиденциальности. |
|
||||
| `USER_AGREEMENT_URL` | Пользовательское соглашение. |
|
||||
| `REQUIRED_CHANNEL_ID` | ID обязательного Telegram-канала. |
|
||||
| `REQUIRED_CHANNEL_LINK` | Ссылка на обязательный канал. |
|
||||
| `REQUIRED_CHANNEL_ID` | ID обязательного Telegram-канала. Используется для проверки подписки и автоматического получения ссылки кнопки, если бот видит канал. |
|
||||
| `REQUIRED_CHANNEL_LINK` | Необязательная запасная ссылка на обязательный канал (`@username` или invite-link), если ссылку нельзя получить по ID. |
|
||||
| `START_COMMAND_DESCRIPTION` | Описание `/start` для меню Telegram. |
|
||||
| `DISABLE_WELCOME_MESSAGE` | Отключить приветствие на `/start`. |
|
||||
|
||||
@@ -157,9 +160,6 @@
|
||||
| `TELEGRAM_OAUTH_REQUEST_ACCESS` | `.env` | Дополнительные разрешения, например `write`. |
|
||||
| `WEBAPP_PRIMARY_COLOR` | Админка | Устаревшее env-поле, игнорируется. |
|
||||
| `WEBAPP_LOGO_URL` | Админка | Устаревшее env-поле, игнорируется. |
|
||||
| `WEBAPP_LOGO_USE_EMOJI` | Админка | Устаревшее env-поле, игнорируется. |
|
||||
| `WEBAPP_LOGO_EMOJI` | Админка | Устаревшее env-поле, игнорируется. |
|
||||
| `WEBAPP_LOGO_EMOJI_FONT` | Админка | Устаревшее env-поле, игнорируется. |
|
||||
| `WEBAPP_FAVICON_USE_CUSTOM` | Админка | Устаревшее env-поле, игнорируется. |
|
||||
| `WEBAPP_FAVICON_URL` | Админка | Устаревшее env-поле, игнорируется. |
|
||||
| `WEBAPP_LOGO_FAVICON_URL` | Админка | Устаревшее env-поле, игнорируется. |
|
||||
@@ -197,7 +197,7 @@
|
||||
|
||||
| Переменная | Назначение |
|
||||
| --- | --- |
|
||||
| `PAYMENT_METHODS_ORDER` | Порядок кнопок оплаты: `severpay,wata,freekassa,platega,yookassa,stars,cryptopay,heleket`. |
|
||||
| `PAYMENT_METHODS_ORDER` | Порядок кнопок оплаты: `severpay,wata,freekassa,platega,yookassa,stars,cryptopay,heleket,paykilla`. |
|
||||
| `SUBSCRIPTION_PURCHASE_DESCRIPTION_ENABLED` | Показывать описание подписки перед выбором срока. |
|
||||
| `SUBSCRIPTION_PURCHASE_DESCRIPTION_RU` / `SUBSCRIPTION_PURCHASE_DESCRIPTION_EN` | Локализованное описание подписки. |
|
||||
| `PAYMENT_<METHOD>_WEBAPP_LABEL_RU` / `PAYMENT_<METHOD>_WEBAPP_LABEL_EN` | Текст кнопки провайдера в Web App. |
|
||||
@@ -213,6 +213,7 @@
|
||||
| `WATA_ENABLED` | Включает Wata. |
|
||||
| `CRYPTOPAY_ENABLED` | Включает CryptoPay. |
|
||||
| `HELEKET_ENABLED` | Включает Heleket. |
|
||||
| `PAYKILLA_ENABLED` | Включает PayKilla. |
|
||||
|
||||
Конкретные ключи отображения:
|
||||
|
||||
@@ -271,6 +272,12 @@ PAYMENT_HELEKET_WEBAPP_ICON
|
||||
PAYMENT_HELEKET_TELEGRAM_LABEL_RU
|
||||
PAYMENT_HELEKET_TELEGRAM_LABEL_EN
|
||||
PAYMENT_HELEKET_TELEGRAM_EMOJI
|
||||
PAYMENT_PAYKILLA_WEBAPP_LABEL_RU
|
||||
PAYMENT_PAYKILLA_WEBAPP_LABEL_EN
|
||||
PAYMENT_PAYKILLA_WEBAPP_ICON
|
||||
PAYMENT_PAYKILLA_TELEGRAM_LABEL_RU
|
||||
PAYMENT_PAYKILLA_TELEGRAM_LABEL_EN
|
||||
PAYMENT_PAYKILLA_TELEGRAM_EMOJI
|
||||
```
|
||||
|
||||
### YooKassa
|
||||
@@ -357,6 +364,33 @@ PAYMENT_HELEKET_TELEGRAM_EMOJI
|
||||
| `HELEKET_VERIFY_WEBHOOK_SIGNATURE` | Проверять подпись webhook. |
|
||||
| `HELEKET_TRUSTED_IPS` | Список доверенных IP webhook-источников. |
|
||||
|
||||
### PayKilla
|
||||
|
||||
Для приема оплат нужен API key типа **HMAC** с правом **INVOICE**. Право **WITHDRAWAL** для оплаты подписок не требуется; включайте его только для отдельной интеграции выплат.
|
||||
|
||||
Webhook настраивается в PayKilla Dashboard: **Settings -> Webhooks**. Укажите `WEBHOOK_BASE_URL` + `/webhook/paykilla`, например `https://bot.example.com/webhook/paykilla`. Включите события `INVOICE_PAID` и `INVOICE_EXPIRED` как минимум. Рекомендуемый набор галочек: `INVOICE_PAID`, `PAYMENT_COMPLETED`, `PAYMENT_FAILED`, `PAYMENT_OVERPAID`, `PAYMENT_UNDERPAID`, `PAYMENT_PARTIAL`, `INVOICE_EXPIRED`, `COMPLIANCE_FAILED`. Если хотите видеть промежуточные статусы в логах PayKilla, дополнительно включите `INVOICE_CREATED`, `PAYMENT_PENDING`, `TRANSACTION_CONFIRMED` и `TRANSACTION_FINAL`.
|
||||
|
||||
| Переменная | Назначение |
|
||||
| --- | --- |
|
||||
| `PAYKILLA_BASE_URL` | Базовый URL API, по умолчанию `https://account-api.paykilla.com`. |
|
||||
| `PAYKILLA_WIDGET_URL` | URL hosted checkout, по умолчанию `https://gopay.paykilla.com`. |
|
||||
| `PAYKILLA_API_KEY` / `PAYKILLA_V2_API_KEY` | Public HMAC key с правом `INVOICE`. |
|
||||
| `PAYKILLA_SECRET_KEY` / `PAYKILLA_V2_SECRET_KEY` | Secret HMAC key для подписи API-запросов и проверки webhook. |
|
||||
| `PAYKILLA_CURRENCY` | Резервная валюта инвойса PayKilla для платежей, чья валюта тарифа не входит в `PAYKILLA_INVOICE_CURRENCIES`. По умолчанию `USD`. |
|
||||
| `PAYKILLA_INVOICE_CURRENCIES` | Валюты, которые PayKilla принимает в поле `currency` при создании invoice. По умолчанию `USD,EUR`. Если тариф в `RUB`, Minishop конвертирует сумму в `PAYKILLA_CURRENCY`. |
|
||||
| `PAYKILLA_PAYMENT_CURRENCIES` | Crypto tickers для оплаты. Рекомендуемый стартовый вариант: `USDTTRC`; добавляйте `BTC`, `ETH` и другие тикеры только если они доступны в PayKilla Dashboard для merchant account. |
|
||||
| `PAYKILLA_SUPPORTED_CURRENCIES` | Валюты тарифов/платежей, которым разрешено использовать PayKilla в этом магазине. |
|
||||
| `PAYKILLA_INVOICE_TYPE` | Необязательный override: `FIAT_BASED`, `FIXED_AMOUNT` или `OPEN_AMOUNT`. |
|
||||
| `PAYKILLA_LIFETIME_SECONDS` | TTL инвойса, отправляется как `expiredAt`. |
|
||||
| `PAYKILLA_RECV_WINDOW_MS` | `recvWindow` для подписанных API-запросов. |
|
||||
| `PAYKILLA_USER_PAYS_SERVICE_FEE` | `true`, если пользователь оплачивает service fee. |
|
||||
| `PAYKILLA_USER_PAYS_NETWORK_FEE` | `true`, если пользователь оплачивает network fee. |
|
||||
| `PAYKILLA_EXCHANGE_RATE_URL` | Бесплатный no-key endpoint курса для конвертации валюты тарифа в валюту инвойса. По умолчанию `https://open.er-api.com/v6/latest/{source}`. Поддерживает placeholders `{source}` и `{target}`. |
|
||||
| `PAYKILLA_EXCHANGE_RATE_CACHE_SECONDS` | Кэш курса и PayKilla currency limits в секундах. По умолчанию `3600`. |
|
||||
| `PAYKILLA_VERIFY_WEBHOOK_SIGNATURE` | Проверять `X-API-SIGN` по raw body webhook. |
|
||||
| `PAYKILLA_WEBHOOK_URL` | Точный публичный webhook URL для проверки подписи, если он отличается от `WEBHOOK_BASE_URL` + `/webhook/paykilla`. |
|
||||
| `PAYKILLA_TRUSTED_IPS` | Необязательный список доверенных IP webhook-источников. |
|
||||
|
||||
## Тарифы и legacy-цены
|
||||
|
||||
Рекомендуемый способ настройки тарифов - раздел **Система -> Тарифы** в админке. Он сохраняет JSON в `TARIFFS_CONFIG_PATH`.
|
||||
@@ -386,10 +420,13 @@ PAYMENT_HELEKET_TELEGRAM_EMOJI
|
||||
| `TRIAL_DURATION_DAYS` | Длительность пробного периода. |
|
||||
| `TRIAL_TRAFFIC_LIMIT_GB` | Лимит трафика пробного периода. |
|
||||
| `TRIAL_TRAFFIC_STRATEGY` | Стратегия лимита пробного периода. |
|
||||
| `TRIAL_WITHOUT_TELEGRAM_ENABLED` | Разрешает активацию trial пользователям без привязанного Telegram. Disposable email домены всё равно требуют Telegram. |
|
||||
| `TRIAL_SQUAD_UUIDS` | Internal Squads для trial через запятую. Если пусто, используется `USER_SQUAD_UUIDS`. |
|
||||
| `REFERRAL_ONE_BONUS_PER_REFEREE` | Ограничить бонусы одним успешным платежом приглашенного. |
|
||||
| `REFERRAL_WELCOME_BONUS_DAYS` | Приветственный бонус пришедшему по реферальной ссылке. |
|
||||
| `REFERRAL_WELCOME_BONUS_WITHOUT_TELEGRAM_ENABLED` | Разрешает начислять реферальный приветственный бонус пользователям без привязанного Telegram. Disposable email домены всё равно требуют Telegram. |
|
||||
| `LEGACY_REFS` | Разрешить ссылки `ref_<telegram_id>`. |
|
||||
| `DISPOSABLE_EMAIL_DOMAINS` | Домены одноразовой почты через запятую. Для таких email trial и реферальный welcome bonus доступны только после привязки Telegram. |
|
||||
| `REFERRAL_BONUS_DAYS_1_MONTH`, `REFERRAL_BONUS_DAYS_3_MONTHS`, `REFERRAL_BONUS_DAYS_6_MONTHS`, `REFERRAL_BONUS_DAYS_12_MONTHS` | Legacy-бонусы пригласившему без JSON-каталога. В JSON-тарифах используйте `referral_bonus_days_inviter`. |
|
||||
| `REFEREE_BONUS_DAYS_1_MONTH`, `REFEREE_BONUS_DAYS_3_MONTHS`, `REFEREE_BONUS_DAYS_6_MONTHS`, `REFEREE_BONUS_DAYS_12_MONTHS` | Legacy-бонусы приглашенному без JSON-каталога. В JSON-тарифах используйте `referral_bonus_days_referee`. |
|
||||
| `SUBSCRIPTION_NOTIFICATIONS_ENABLED` | Включает напоминания о подписке. |
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
- блокировка пользователей, входящий список тикетов поддержки, рассылки, промокоды и просмотр логов;
|
||||
- ручная синхронизация с Remnawave;
|
||||
- редактор разрешенных настроек приложения из manifest-файла;
|
||||
- раздел **Внешний вид** для логотипа, emoji-логотипа, выбора темы, accent-цвета, масштаба логотипа и предпросмотра тем;
|
||||
- раздел **Внешний вид** для логотипа, выбора темы, accent-цвета, масштаба логотипа и предпросмотра тем;
|
||||
- раздел **Инструкции подключения** для встроенной страницы установки, поведения кнопок бота и Remnawave Subscription Page config;
|
||||
- раздел **Бэкапы** для просмотра локальных ZIP-архивов, загрузки архива и восстановления БД/compose-папки;
|
||||
- редактор JSON-каталога тарифов;
|
||||
@@ -49,7 +49,7 @@
|
||||
В manifest сейчас входят:
|
||||
|
||||
- общие параметры: язык, валюта, ссылки поддержки, документы, обязательный канал, Remnawave-доступы и поведение `/start`;
|
||||
- внешний вид и доступность Web App: название, цвет, логотип, emoji-логотип и `WEBAPP_ENABLED`;
|
||||
- внешний вид и доступность Web App: название, цвет, логотип и `WEBAPP_ENABLED`;
|
||||
- инструкции подключения: `SUBSCRIPTION_GUIDES_ENABLED`, `SUBSCRIPTION_GUIDES_BOT_MENU_ENABLED`, чтение конфига из Remnawave Panel, JSON-переопределение и резервный путь к файлу;
|
||||
- legacy-тарифы без JSON-каталога: периоды подписки, RUB/Stars цены, реферальные бонусы и пакеты трафика;
|
||||
- платежные провайдеры: включение методов, порядок кнопок, публичные параметры и секреты YooKassa, FreeKassa, Platega, SeverPay, Wata, CryptoPay, Heleket и Stars, а также текст и иконки кнопок оплаты;
|
||||
@@ -102,9 +102,11 @@
|
||||
|
||||
## Внешний вид
|
||||
|
||||
Раздел **Внешний вид** объединяет настройки бренда и темы Web App. Логотип можно загрузить файлом или по HTTPS-ссылке; backend сохраняет файл в `data/webapp-logo/uploads` и подставляет локальный URL. Если включен emoji-логотип, картинка скрывается, а для emoji можно выбрать системный, Twemoji, Noto Color, animated Noto и другие варианты отрисовки.
|
||||
Раздел **Внешний вид** объединяет настройки бренда и темы Web App. Логотип можно загрузить файлом или по HTTPS-ссылке; backend сохраняет файл в `data/webapp-logo/uploads` и подставляет локальный URL. Если логотип не задан, показывается логотип проекта по умолчанию. Favicon генерируется из логотипа или загружается отдельно.
|
||||
|
||||
В блоке тем админка читает каталог из `WEBAPP_THEMES_DIR`, показывает встроенные и кастомные темы, позволяет выбрать текущую тему, изменить accent, включить или выключить тему для админки и настроить масштаб логотипа на главной и экране входа. Кнопка предпросмотра открывает `/home?theme_preview=<key>` и не меняет глобальную тему до сохранения.
|
||||
Этот же бренд используется в HTML-письмах. Загруженный локальный логотип встраивается в письмо как inline image (`cid:webapp-logo`), поэтому email-клиенту не нужен прямой доступ к `/webapp-uploaded-logo/...`. Логотип по публичной HTTPS-ссылке остается внешней картинкой в письме.
|
||||
|
||||
В блоке тем админка читает каталог из `WEBAPP_THEMES_DIR`, показывает встроенные и кастомные темы, позволяет выбрать текущую тему, изменить accent, включить или выключить тему для админки и настроить отдельный масштаб логотипа для desktop и mobile layout. Кнопка предпросмотра открывает `/home?theme_preview=<key>` и не меняет глобальную тему до сохранения.
|
||||
|
||||
Подробный формат `theme.json`, CSS/asset-роуты и пошаговый пайплайн создания новой темы описаны в [webapp-themes.md](webapp-themes.md).
|
||||
|
||||
@@ -122,6 +124,8 @@
|
||||
- **Premium**: названия premium-раздела RU/EN, premium Internal Squads, месячный premium-лимит и RUB/Stars пакеты premium-докупки;
|
||||
- **Устройства**: RUB/Stars пакеты докупки HWID-устройств.
|
||||
|
||||
Порядок периодов, traffic-пакетов, обычных докупок, premium-докупок и HWID-пакетов меняется перетаскиванием строк в редакторе. Этот порядок сохраняется в JSON и используется на витрине Web App и в Telegram-боте.
|
||||
|
||||
Базовые и premium Internal Squads выбираются из Remnawave через `/api/admin/panel/internal-squads`. Если панель недоступна, можно сохранить уже существующие UUID в JSON, но выпадающий список не загрузится.
|
||||
|
||||
## Практические замечания
|
||||
|
||||
@@ -44,6 +44,12 @@ BRUTE_FORCE_WINDOW_SECONDS=900
|
||||
BRUTE_FORCE_LOCK_SECONDS=900
|
||||
```
|
||||
|
||||
## Брендинг писем
|
||||
|
||||
HTML-письма используют тот же бренд, что и Mini App: название из `WEBAPP_TITLE`, accent из внешнего вида и логотип из раздела **Внешний вид**. Если логотип загружен через админку файлом, backend прикладывает его к письму как inline image (`cid:webapp-logo`), поэтому получателю не нужен доступ к внутреннему `/webapp-uploaded-logo/...`.
|
||||
|
||||
Если в качестве логотипа задан публичный `https://` URL, письмо использует его как обычный внешний `<img>`. В этом режиме некоторые почтовые клиенты могут скрыть картинку, пока получатель не разрешит загрузку внешних изображений.
|
||||
|
||||
Для Brevo обычно подходит порт `587` с STARTTLS. Если основной порт недоступен, приложение пробует порты из `SMTP_FALLBACK_PORTS`; порт `465` используется через SSL wrapper автоматически.
|
||||
|
||||
`SMTP_FROM_EMAIL` должен быть подтвержден у SMTP-провайдера, иначе письмо часто отклоняется или попадает в спам. `SMTP_FROM_NAME` можно оставить пустым, тогда используется название Web App.
|
||||
|
||||
@@ -6,6 +6,8 @@ Minishop отправляет уведомления в Telegram и на email.
|
||||
|
||||
Для уведомлений жизненного цикла подписки есть отдельный флаг `SUBSCRIPTION_EMAIL_NOTIFICATIONS_ENABLED`. Если он включен, пользовательские уведомления об окончании подписки отправляются в Telegram при наличии привязанного Telegram-аккаунта и на email при наличии привязанной почты.
|
||||
|
||||
Все HTML-письма используют общий email-шаблон с брендом из Web App: заголовком, accent-цветом и логотипом. Логотип, загруженный через раздел **Внешний вид**, отправляется как inline image, а публичный HTTPS-логотип остается внешней картинкой.
|
||||
|
||||
## Сводная таблица
|
||||
|
||||
| Событие | Получатель | Telegram | Email | Условия и ограничения |
|
||||
@@ -16,7 +18,7 @@ Minishop отправляет уведомления в Telegram и на email.
|
||||
| Успешная покупка отдельного пакета трафика | Пользователь | ✓ | ✓ | Для `traffic` / `traffic_package`; email отправляется, если SMTP настроен и у пользователя есть email. |
|
||||
| Успешная докупка обычного трафика к тарифу | Пользователь | ✓ | ✓ | Для `topup`; email отправляется, если SMTP настроен и у пользователя есть email. |
|
||||
| Успешная покупка premium-трафика | Пользователь | ✓ | ✓ | Для `premium_topup`; email отправляется, если SMTP настроен и у пользователя есть email. |
|
||||
| Успешная покупка HWID-устройств | Пользователь | ✓ | ✓ | Отправляется после оплаты `hwid_devices` или `hwid_devices_renewal`; email отправляется, если SMTP настроен и у пользователя есть email. |
|
||||
| Успешная покупка HWID-устройств | Пользователь | ✓ | ✓ | Отправляется после отдельной оплаты `hwid_devices`; при продлении устройств вместе с подпиской добавляется примечание к уведомлению об успешной оплате подписки. Email отправляется, если SMTP настроен и у пользователя есть email. |
|
||||
| Платное повышение тарифа | Пользователь | ✓ | ✓ | Для `tariff_upgrade`; email отправляется, если SMTP настроен и у пользователя есть email. |
|
||||
| Способ оплаты YooKassa привязан | Пользователь | ✓ | ✓ | Отправляется после успешного сохранения платежного метода через webhook YooKassa; email отправляется, если SMTP настроен и у пользователя есть email. |
|
||||
| Ошибка оплаты по webhook провайдера | Пользователь | ✓ | ✓ | Отправляется, когда платежный провайдер сообщает о неуспешном платеже; email отправляется, если SMTP настроен и у пользователя есть email. |
|
||||
|
||||
+111
-14
@@ -22,6 +22,30 @@
|
||||
- [Тарифы](tariffs.md) — цены, Telegram Stars и сценарии покупки.
|
||||
- [Логи](../troubleshooting/logs.md) — проверка webhook и создания платежных ссылок.
|
||||
|
||||
## Webhook URL провайдеров
|
||||
|
||||
Все платежные webhook URL строятся от `WEBHOOK_BASE_URL` — публичного HTTPS-адреса backend/webhook-домена.
|
||||
|
||||
Это должен быть домен, который проксируется на backend-сервер вебхуков (`backend:8080`), а не frontend/Mini App домен из `SUBSCRIPTION_MINI_APP_URL`.
|
||||
|
||||
Если `WEBHOOK_BASE_URL=https://bot.example.com`, полный webhook URL получается как `https://bot.example.com` + путь из таблицы.
|
||||
|
||||
| Провайдер | URL |
|
||||
| --- | --- |
|
||||
| YooKassa | `WEBHOOK_BASE_URL` + `/webhook/yookassa` |
|
||||
| FreeKassa | `WEBHOOK_BASE_URL` + `/webhook/freekassa` |
|
||||
| Platega | `WEBHOOK_BASE_URL` + `/webhook/platega` |
|
||||
| SeverPay | `WEBHOOK_BASE_URL` + `/webhook/severpay` |
|
||||
| Wata | `WEBHOOK_BASE_URL` + `/webhook/wata` |
|
||||
| CryptoPay | `WEBHOOK_BASE_URL` + `/webhook/cryptopay` |
|
||||
| Heleket | `WEBHOOK_BASE_URL` + `/webhook/heleket` |
|
||||
| PayKilla | `WEBHOOK_BASE_URL` + `/webhook/paykilla` |
|
||||
| Telegram Stars | `WEBHOOK_BASE_URL` + `/tg/webhook` |
|
||||
|
||||
После настройки сделайте тестовый платеж и проверьте, что в логах `backend` виден входящий `POST` на нужный путь.
|
||||
|
||||
Если провайдер сообщает, что адрес недоступен, проверьте DNS, HTTPS и reverse proxy для `WEBHOOK_BASE_URL`. Путь должен начинаться с `/webhook/...` без `/api`, `/auth` и frontend-домена.
|
||||
|
||||
## YooKassa
|
||||
|
||||
YooKassa используется для рублевых оплат. Провайдер также может участвовать в сценариях автопродления period-подписок.
|
||||
@@ -29,7 +53,7 @@ YooKassa используется для рублевых оплат. Прова
|
||||
### Настройка
|
||||
|
||||
1. Включите `YOOKASSA_ENABLED`.
|
||||
2. Заполните `YOOKASSA_SHOP_ID`, `YOOKASSA_SECRET_KEY`, `YOOKASSA_RETURN_URL`.
|
||||
2. Заполните `YOOKASSA_SHOP_ID`, `YOOKASSA_SECRET_KEY` и `YOOKASSA_RETURN_URL`.
|
||||
3. Скопируйте URL вебхука из админ-панели и укажите его в кабинете YooKassa.
|
||||
|
||||
### Справочник
|
||||
@@ -43,10 +67,10 @@ FreeKassa подключается как отдельный платежный
|
||||
### Настройка
|
||||
|
||||
1. Включите `FREEKASSA_ENABLED`.
|
||||
2. Заполните `FREEKASSA_MERCHANT_ID`, `FREEKASSA_FIRST_SECRET`, `FREEKASSA_SECOND_SECRET`, `FREEKASSA_API_KEY`.
|
||||
2. Заполните `FREEKASSA_MERCHANT_ID`, `FREEKASSA_FIRST_SECRET`, `FREEKASSA_SECOND_SECRET` и `FREEKASSA_API_KEY`.
|
||||
3. Проверьте настройки подписи.
|
||||
4. Скопируйте URL вебхука из админ-панели и укажите его в кабинете FreeKassa.
|
||||
5. При необходимости заполните список доверенных IP.
|
||||
5. При необходимости заполните `FREEKASSA_TRUSTED_IPS`.
|
||||
|
||||
### Справочник
|
||||
|
||||
@@ -59,11 +83,20 @@ Platega подключается как отдельный платежный п
|
||||
### Настройка
|
||||
|
||||
1. Включите `PLATEGA_ENABLED`.
|
||||
2. Укажите `PLATEGA_MERCHANT_ID` и `PLATEGA_SECRET`.
|
||||
2. Укажите `PLATEGA_BASE_URL`, `PLATEGA_MERCHANT_ID` и `PLATEGA_SECRET`.
|
||||
3. Скопируйте URL вебхука из админ-панели и укажите его в кабинете Platega.
|
||||
4. Проверьте `PLATEGA_RETURN_URL` и `PLATEGA_FAILED_URL`.
|
||||
5. При необходимости укажите `PLATEGA_PAYMENT_METHOD`.
|
||||
|
||||
### Дополнительные кнопки
|
||||
|
||||
- `PLATEGA_SBP_ENABLED` — отдельная кнопка СБП/карта.
|
||||
- `PLATEGA_SBP_METHOD` — ID метода для СБП/карты.
|
||||
- `PLATEGA_CRYPTO_ENABLED` — отдельная crypto-кнопка Platega.
|
||||
- `PLATEGA_CRYPTO_METHOD` — ID метода для crypto-кнопки.
|
||||
- `PAYMENT_PLATEGA_SBP_*` — текст и иконка кнопки СБП/карта.
|
||||
- `PAYMENT_PLATEGA_CRYPTO_*` — текст и иконка crypto-кнопки.
|
||||
|
||||
### Справочник
|
||||
|
||||
- [Platega](../configuration/env-vars.md#platega)
|
||||
@@ -75,9 +108,11 @@ SeverPay подключается как отдельный платежный
|
||||
### Настройка
|
||||
|
||||
1. Включите `SEVERPAY_ENABLED`.
|
||||
2. Укажите `SEVERPAY_MID`, `SEVERPAY_TOKEN`, `SEVERPAY_BASE_URL`
|
||||
3. Скопируйте URL вебхука из админ-панели и укажите его в кабинете SeverPay.
|
||||
4. При необходимости задайте `SEVERPAY_LIFETIME_MINUTES`.
|
||||
2. Укажите `SEVERPAY_BASE_URL`.
|
||||
3. Заполните `SEVERPAY_MID` и `SEVERPAY_TOKEN`.
|
||||
4. Проверьте `SEVERPAY_RETURN_URL`.
|
||||
5. Скопируйте URL вебхука из админ-панели и укажите его в кабинете SeverPay.
|
||||
6. При необходимости задайте `SEVERPAY_LIFETIME_MINUTES`.
|
||||
|
||||
### Справочник
|
||||
|
||||
@@ -116,8 +151,8 @@ CryptoPay используется для криптовалютных плат
|
||||
2. Укажите `CRYPTOPAY_TOKEN`.
|
||||
3. Выберите `CRYPTOPAY_NETWORK`: `mainnet` или `testnet`.
|
||||
4. Задайте `CRYPTOPAY_CURRENCY_TYPE`: `fiat` или `crypto`.
|
||||
5. Скопируйте URL вебхука из админ-панели и укажите его в CryptoPay.
|
||||
6. Проверьте `CRYPTOPAY_ASSET`.
|
||||
5. Проверьте `CRYPTOPAY_ASSET`, например `RUB`, `USDT` или `BTC`.
|
||||
6. Скопируйте URL вебхука из админ-панели и укажите его в CryptoPay.
|
||||
|
||||
### Проверка
|
||||
|
||||
@@ -138,10 +173,12 @@ Heleket используется для крипто-инвойсов с merchan
|
||||
1. Включите `HELEKET_ENABLED`.
|
||||
2. Укажите `HELEKET_BASE_URL`, `HELEKET_MERCHANT_ID` и `HELEKET_API_KEY`.
|
||||
3. Настройте `HELEKET_CURRENCY`.
|
||||
4. Скопируйте URL вебхука из админ-панели и укажите его в кабинете Heleket.
|
||||
5. При необходимости задайте `HELEKET_TO_CURRENCY` и `HELEKET_NETWORK`.
|
||||
7. При необходимости включите `HELEKET_VERIFY_WEBHOOK_SIGNATURE`.
|
||||
8. Для IP-фильтрации заполните `HELEKET_TRUSTED_IPS`.
|
||||
4. При необходимости задайте `HELEKET_TO_CURRENCY` и `HELEKET_NETWORK`.
|
||||
5. Проверьте `HELEKET_RETURN_URL` и `HELEKET_SUCCESS_URL`.
|
||||
6. Настройте `HELEKET_LIFETIME_SECONDS`.
|
||||
7. Скопируйте URL вебхука из админ-панели и укажите его в кабинете Heleket.
|
||||
8. При необходимости включите `HELEKET_VERIFY_WEBHOOK_SIGNATURE`.
|
||||
9. Для IP-фильтрации заполните `HELEKET_TRUSTED_IPS`.
|
||||
|
||||
### Ограничения
|
||||
|
||||
@@ -151,6 +188,64 @@ Heleket используется для крипто-инвойсов с merchan
|
||||
|
||||
- [Heleket](../configuration/env-vars.md#heleket)
|
||||
|
||||
## PayKilla
|
||||
|
||||
PayKilla используется для крипто-инвойсов V2 через hosted checkout `https://gopay.paykilla.com/{invoice_id}`.
|
||||
|
||||
API-запросы подписываются HMAC-SHA256. Webhook проверяется по заголовку `X-API-SIGN` и raw body.
|
||||
|
||||
### Особенности
|
||||
|
||||
- PayKilla строго валидирует текстовые поля invoice.
|
||||
- В `purpose` и `description` Minishop отправляет простой английский текст `<WEBAPP_TITLE> payment <id>`.
|
||||
- Локализованное описание платежа остается только внутри Minishop.
|
||||
- ASCII-safe sanitizer допускает ASCII-буквы, цифры, пробелы, `_`, `.`, `,`.
|
||||
|
||||
### Валюта invoice
|
||||
|
||||
Minishop создает invoice в валюте, которую PayKilla принимает в поле `currency`.
|
||||
|
||||
Если валюта тарифа входит в `PAYKILLA_INVOICE_CURRENCIES`, сумма отправляется как есть.
|
||||
|
||||
Если валюта тарифа не входит в список, сумма конвертируется в `PAYKILLA_CURRENCY`. По умолчанию рублевые тарифы конвертируются в `USD` через ExchangeRate-API с кэшем `PAYKILLA_EXCHANGE_RATE_CACHE_SECONDS`.
|
||||
|
||||
### Payload invoice
|
||||
|
||||
Payload создания invoice содержит обязательные поля `type`, `purpose`, `currency`, `totalPrice` и `paymentCurrencies`.
|
||||
|
||||
Дополнительно отправляются `clientOrderId`, `description`, `expiredAt`, `userPaysServiceFee` и `userPaysNetworkFee`.
|
||||
|
||||
Redirect URLs в PayKilla не отправляются. Завершение платежа обрабатывается через webhook.
|
||||
|
||||
### API key
|
||||
|
||||
1. В PayKilla Dashboard откройте **Settings -> API keys**.
|
||||
2. Создайте ключ типа **HMAC**.
|
||||
3. Для приема оплат включите permission **INVOICE**.
|
||||
4. Permission **WITHDRAWAL** не нужен для Minishop-платежей.
|
||||
5. Сохраните `publicKey` в `PAYKILLA_API_KEY`.
|
||||
6. Сохраните `secretKey` в `PAYKILLA_SECRET_KEY`.
|
||||
|
||||
### Webhook
|
||||
|
||||
1. В PayKilla Dashboard откройте **Settings -> Webhooks**.
|
||||
2. Скопируйте URL вебхука из админ-панели и укажите его в PayKilla.
|
||||
3. Включите минимальные события: `INVOICE_PAID`, `INVOICE_EXPIRED`.
|
||||
4. Для production также включите `PAYMENT_COMPLETED`, `PAYMENT_FAILED`, `PAYMENT_OVERPAID`, `PAYMENT_UNDERPAID`, `PAYMENT_PARTIAL`, `COMPLIANCE_FAILED`.
|
||||
5. Оставьте `PAYKILLA_VERIFY_WEBHOOK_SIGNATURE=True`.
|
||||
|
||||
### Настройка
|
||||
|
||||
1. Включите `PAYKILLA_ENABLED`.
|
||||
2. Укажите `PAYKILLA_API_KEY` и `PAYKILLA_SECRET_KEY`.
|
||||
3. Оставьте `PAYKILLA_CURRENCY=USD`, если PayKilla не принимает валюту тарифов как invoice currency.
|
||||
4. В `PAYKILLA_INVOICE_CURRENCIES` укажите валюты invoice, например `USD,EUR`.
|
||||
5. В `PAYKILLA_PAYMENT_CURRENCIES` начните с `USDTTRC`.
|
||||
|
||||
### Справочник
|
||||
|
||||
- [PayKilla](../configuration/env-vars.md#paykilla)
|
||||
|
||||
## Telegram Stars
|
||||
|
||||
Telegram Stars используются напрямую и поддерживаются в legacy-ценах и JSON-каталоге тарифов.
|
||||
@@ -171,9 +266,11 @@ Telegram Stars используются напрямую и поддержива
|
||||
|
||||
### Ограничения
|
||||
|
||||
- Отдельный платежный webhook не нужен.
|
||||
- Stars-события приходят через webhook Telegram-бота: `WEBHOOK_BASE_URL` + `/tg/webhook`.
|
||||
- XTR/Stars-докупки не конвертируются без явно заданного курса.
|
||||
|
||||
### Справочник
|
||||
|
||||
- [Переменные платежей](../configuration/env-vars.md#платежи)
|
||||
- [Тарифы](tariffs.md)
|
||||
- [Тарифы](tariffs.md)
|
||||
+65
-58
@@ -28,6 +28,8 @@ JSON-каталог может содержать несколько тариф
|
||||
- настройка premium-раздела: названия RU/EN, premium Internal Squads, месячный premium-лимит и пакеты докупки premium-трафика в платежной валюте/Stars;
|
||||
- настройка базового HWID-лимита и пакетов докупки устройств.
|
||||
|
||||
Порядок продаваемых вариантов управляется в админке перетаскиванием строк: это работает для периодов покупки подписки, traffic-пакетов, обычных докупок трафика, premium-докупок и HWID-пакетов. Такой же порядок сохраняется в JSON и затем используется в Web App и Telegram-боте.
|
||||
|
||||
После сохранения изменения применяются к новым запросам Web App сразу, потому что конфиг тарифов загружается из JSON при обращении. Уже созданные подписки сохраняют свой `tariff_key`; при удалении или отключении тарифа проверьте, что активные подписки с этим ключом не требуют дальнейшего продления или смены.
|
||||
|
||||
Подробности по админ-панели, правам доступа, сохранению настроек и списку разделов есть в [админ-панели](admin-panel.md).
|
||||
@@ -56,16 +58,16 @@ Legacy-поля остаются алиасами: `prices_rub`, `conversion_rat
|
||||
|
||||
Платежные провайдеры не принимают произвольный код валюты одинаково. Бот фильтрует способы оплаты и блокирует создание платежа, если текущая валюта каталога не поддерживается провайдером:
|
||||
|
||||
| Провайдер | Валюты по умолчанию |
|
||||
| --- | --- |
|
||||
| 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-цены |
|
||||
| Провайдер | Валюты по умолчанию |
|
||||
| -------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
||||
| 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 список валют нужно держать в соответствии с условиями вашего мерчанта.
|
||||
|
||||
@@ -113,43 +115,43 @@ Legacy-поля остаются алиасами: `prices_rub`, `conversion_rat
|
||||
|
||||
Основные поля:
|
||||
|
||||
| Поле | Назначение |
|
||||
| --- | --- |
|
||||
| `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`. |
|
||||
| `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-докупки. |
|
||||
| Поле | Назначение |
|
||||
| ---------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `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` | Периоды, доступные для покупки. |
|
||||
| `topup_packages` | Пакеты докупки трафика именно для этого тарифа. Если поле не задано или списки пустые, докупка для тарифа не показывается в Web App и Telegram-боте. |
|
||||
| Поле | Назначение |
|
||||
| ----------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `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. |
|
||||
| `conversion_rate_per_gb` | Курс для конвертации оставшихся дней period-тарифа в GB при смене на traffic-тариф в валюте каталога. |
|
||||
| `conversion_rate_rub_per_gb` | Legacy-алиас для рублевых каталогов. |
|
||||
| Поле | Назначение |
|
||||
| ---------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `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` обязателен.
|
||||
|
||||
@@ -278,7 +280,10 @@ limit_after = current_used + balance_after
|
||||
- полная цена HWID-пакета берется из `prices[duration_months]`; если периода нет, используется fallback `price * duration_months`;
|
||||
- фактическая цена докупки считается пропорционально оплачиваемому окну `valid_from -> valid_until` относительно периода подписки и фиксируется в платежe;
|
||||
- для Telegram Stars цена округляется вверх до целого Stars, для платежной валюты — вверх до копеек; `min_price` защищает от микроплатежей в конце периода;
|
||||
- при продлении подписки докупленные устройства не продлеваются автоматически: старая докупка действует до прежнего `end_date`, а для нового срока создается отдельная `hwid_devices_renewal`-покупка;
|
||||
- кнопка докупки устройств всегда покупает устройства только для текущей активной подписки и только до текущего срока ее действия;
|
||||
- при продлении подписки пользователь видит отдельный чекбокс продления действующих докупленных устройств; чекбокс включен по умолчанию, цена считается по текущему тарифу и добавляется в тот же платеж подписки;
|
||||
- если пользователь продлил подписку без продления устройств, старая докупка продолжает действовать до своего `valid_until`, а Web App показывает предупреждение о возможном временном возврате к базовому лимиту;
|
||||
- админские продления, промокоды и реферальные бонусы добавляют фиксированное количество дней отдельно к подписке и к действующим докупкам устройств, не склеивая даты окончания;
|
||||
- `traffic`-тарифы не показывают и не принимают докупку HWID-устройств, потому что у них нет срока подписки;
|
||||
- при смене тарифа базовый лимит берется из целевого тарифа, а неиспользованная стоимость HWID-докупок в платежной валюте конвертируется в дни нового period-тарифа или GB traffic-тарифа; XTR/Stars-докупки не конвертируются без явного курса и продолжают жить по своему `valid_until`;
|
||||
- история докупок пишется в `hwid_device_purchases`;
|
||||
@@ -292,12 +297,12 @@ limit_after = current_used + balance_after
|
||||
|
||||
Варианты расчета:
|
||||
|
||||
| Переход | Поведение |
|
||||
| --- | --- |
|
||||
| `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-тарифа. |
|
||||
| Переход | Поведение |
|
||||
| ----------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `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-тарифа. |
|
||||
|
||||
При смене тарифа бот меняет:
|
||||
|
||||
@@ -313,15 +318,15 @@ limit_after = current_used + balance_after
|
||||
|
||||
В платежах используются поля:
|
||||
|
||||
| Поле | Назначение |
|
||||
| --- | --- |
|
||||
| `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` | Количество месяцев для подписки на срок; также используется платежными обработчиками как числовое поле покупки. |
|
||||
| Поле | Назначение |
|
||||
| ----------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------- |
|
||||
| `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`.
|
||||
|
||||
@@ -345,12 +350,14 @@ Remnawave ограничивает доступ при достижении `tra
|
||||
|
||||
Автопродление через 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_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-меню в этом случае показывает только диапазоны по каждому тарифу.
|
||||
|
||||
@@ -67,7 +67,7 @@ SUPPORT_TICKET_RATE_LIMIT_PER_HOUR=5
|
||||
|
||||
Если `WEBAPP_ENABLED=False`, пользовательское веб-приложение и админ-панель не регистрируются. Чтобы снова попасть в админку, включите `WEBAPP_ENABLED=True` в `.env` и перезапустите backend/frontend контейнеры.
|
||||
|
||||
Внешний вид настраивается в админке: раздел **Внешний вид** управляет логотипом, emoji-логотипом, accent-цветом, выбранной темой и масштабом логотипа. Кастомные темы читаются из `WEBAPP_THEMES_DIR`, а `WEBAPP_DEFAULT_THEME` может принудительно выбрать тему по ключу. Подробный контракт `theme.json`, CSS/asset-роуты и пайплайн создания темы описаны в [webapp-themes.md](webapp-themes.md).
|
||||
Внешний вид настраивается в админке: раздел **Внешний вид** управляет логотипом, favicon, accent-цветом, выбранной темой и отдельным масштабом логотипа для desktop/mobile layout. Кастомные темы читаются из `WEBAPP_THEMES_DIR`, а `WEBAPP_DEFAULT_THEME` может принудительно выбрать тему по ключу. Подробный контракт `theme.json`, CSS/asset-роуты и пайплайн создания темы описаны в [webapp-themes.md](webapp-themes.md).
|
||||
|
||||
## Авторизация
|
||||
|
||||
|
||||
@@ -13,7 +13,6 @@ Web App поддерживает файловые темы, предпросмо
|
||||
- включить или выключить применение темы в админ-панели;
|
||||
- настроить масштаб логотипа на главной и экране входа;
|
||||
- загрузить логотип файлом или по HTTPS-ссылке;
|
||||
- включить emoji-логотип и выбрать способ его отрисовки;
|
||||
- открыть предпросмотр темы через `/home?theme_preview=<key>`.
|
||||
|
||||
Через файлы темы можно менять намного больше:
|
||||
@@ -51,7 +50,9 @@ WEBAPP_DEFAULT_THEME=
|
||||
|
||||
В compose-примерах `data/themes` - это локальная папка рядом с выбранным `docker-compose.yml`; она монтируется в контейнер как `/app/data/themes`. Правки в `backend/bot/app/web/themes` попадают в прод только при сборке собственного образа; опубликованный образ их не видит.
|
||||
|
||||
Важно: `WEBAPP_PRIMARY_COLOR`, `WEBAPP_LOGO_URL`, `WEBAPP_LOGO_USE_EMOJI`, `WEBAPP_LOGO_EMOJI` и `WEBAPP_LOGO_EMOJI_FONT` больше не являются рабочим способом первичной настройки через `.env`. Эти значения редактируются в админке и сохраняются как overrides в базе. Тема при этом может использовать сохраненный primary color как fallback accent.
|
||||
Важно: `WEBAPP_PRIMARY_COLOR` и `WEBAPP_LOGO_URL` больше не являются рабочим способом первичной настройки через `.env`. Эти значения редактируются в админке и сохраняются как overrides в базе. Тема при этом может использовать сохраненный primary color как fallback accent.
|
||||
|
||||
Email-шаблоны берут тот же бренд из настроек внешнего вида. Загруженный логотип добавляется в письма как inline image (`cid:webapp-logo`), а публичный HTTPS-логотип остается внешней картинкой, которую почтовый клиент может скрыть до разрешения загрузки изображений.
|
||||
|
||||
## Контракт `theme.json`
|
||||
|
||||
@@ -114,7 +115,8 @@ WEBAPP_DEFAULT_THEME=
|
||||
"font_sans": "Inter, system-ui, sans-serif",
|
||||
"font_logo": "Inter, system-ui, sans-serif",
|
||||
"font_mono": "\"JetBrains Mono\", \"Fira Code\", monospace",
|
||||
"home_logo_scale": 120,
|
||||
"home_logo_scale_desktop": 120,
|
||||
"home_logo_scale_mobile": 95,
|
||||
"admin_bg": "#05040a",
|
||||
"admin_surface": "#11101c",
|
||||
"admin_surface_2": "#090815",
|
||||
@@ -130,52 +132,54 @@ WEBAPP_DEFAULT_THEME=
|
||||
|
||||
Поля верхнего уровня:
|
||||
|
||||
| Поле | Назначение |
|
||||
| --- | --- |
|
||||
| `key` | Уникальный ключ темы, 1-64 символа: латиница, цифры, `_` и `-`. Если ключ не указан, берется имя папки. |
|
||||
| `names` | Локализованные названия, например `ru` и `en`. |
|
||||
| `enabled` | Показывать тему пользователям. Отключенная тема не попадает в публичный каталог. |
|
||||
| `default` | Делает тему выбранной по умолчанию, если `WEBAPP_DEFAULT_THEME` не задан. |
|
||||
| Поле | Назначение |
|
||||
| -------------------- | ------------------------------------------------------------------------------------------------------- |
|
||||
| `key` | Уникальный ключ темы, 1-64 символа: латиница, цифры, `_` и `-`. Если ключ не указан, берется имя папки. |
|
||||
| `names` | Локализованные названия, например `ru` и `en`. |
|
||||
| `enabled` | Показывать тему пользователям. Отключенная тема не попадает в публичный каталог. |
|
||||
| `default` | Делает тему выбранной по умолчанию, если `WEBAPP_DEFAULT_THEME` не задан. |
|
||||
| `use_primary_accent` | Если `true`, тема может получить accent из настройки внешнего вида, когда в `tokens.accent` ничего нет. |
|
||||
| `use_in_admin` | Если `false`, пользовательская часть использует тему, но админка откатывается на `dark`. |
|
||||
| `css_file` | CSS-файл внутри папки темы. Может быть `style.css` или вложенный путь вроде `css/theme.css`. |
|
||||
| `assets_version` | Версия ассетов. Для встроенных тем используется для обновления старых файлов в `data/themes`. |
|
||||
| `tokens` | Дизайн-токены, которые превращаются в CSS-переменные на `.app-shell`. |
|
||||
| `use_in_admin` | Если `false`, пользовательская часть использует тему, но админка откатывается на `dark`. |
|
||||
| `css_file` | CSS-файл внутри папки темы. Может быть `style.css` или вложенный путь вроде `css/theme.css`. |
|
||||
| `assets_version` | Версия ассетов. Для встроенных тем используется для обновления старых файлов в `data/themes`. |
|
||||
| `tokens` | Дизайн-токены, которые превращаются в CSS-переменные на `.app-shell`. |
|
||||
|
||||
## Токены
|
||||
|
||||
Поддерживаемые токены:
|
||||
|
||||
| Токен | CSS-переменная | Что меняет |
|
||||
| --- | --- | --- |
|
||||
| `color_scheme` | `color-scheme` | Нативная светлая/темная схема браузера: `dark` или `light`. |
|
||||
| `style_preset` | CSS-класс пресета | Сейчас `win95`/`windows95` добавляет `theme-preset-win95`; остальные значения не дают специального класса. |
|
||||
| `accent` | `--accent` | Главный акцент: активные элементы, кнопки, прогресс, фокус. Только hex `#RGB` или `#RRGGBB`. |
|
||||
| `bg` | `--bg` | Основной фон приложения. |
|
||||
| `panel` | `--panel` | Основные карточки и поверхности. |
|
||||
| `panel_2` | `--panel-2` | Вторичные поверхности. |
|
||||
| `panel_3` | `--panel-3` | Поверхности повышенной вложенности, dropdown/popover. |
|
||||
| `border` | `--border` | Обычные границы. |
|
||||
| `border_strong` | `--border-strong` | Усиленные границы и hover-состояния. |
|
||||
| `text` | `--text` | Основной текст. |
|
||||
| `muted` | `--muted` | Вторичный текст. |
|
||||
| `dim` | `--dim` | Еще более тихий текст и служебные подписи. |
|
||||
| `danger` | `--danger` | Ошибки и опасные действия. |
|
||||
| `blue` | `--blue` | Синий вспомогательный цвет. |
|
||||
| `radius` | `--radius` | Базовый радиус карточек, кнопок и контролов. |
|
||||
| `font_sans` | `--font-sans` | Основной шрифт интерфейса. |
|
||||
| `font_logo` | `--font-logo` | Шрифт бренда и заголовка. |
|
||||
| `font_mono` | `--font-mono` | Моноширинный шрифт. |
|
||||
| `home_logo_scale` | `--home-logo-scale` | Масштаб логотипа на главной и входе, от `50` до `300` процентов. |
|
||||
| `admin_bg` | `--admin-bg` | Фон админ-панели. |
|
||||
| `admin_surface` | `--admin-surface` | Основные карточки админки. |
|
||||
| `admin_surface_2` | `--admin-surface-2` | Вторичные поверхности админки. |
|
||||
| `admin_elev` | `--admin-elev` | Elevated-поверхности админки. |
|
||||
| `admin_border` | `--admin-border` | Границы админки. |
|
||||
| `admin_border_strong` | `--admin-border-strong` | Усиленные границы админки. |
|
||||
| `admin_text` | `--admin-text` | Основной текст админки. |
|
||||
| `admin_muted` | `--admin-muted` | Вторичный текст админки. |
|
||||
| `admin_dim` | `--admin-dim` | Тихие подписи админки. |
|
||||
| Токен | CSS-переменная | Что меняет |
|
||||
| ------------------------- | --------------------------- | ---------------------------------------------------------------------------------------------------------- |
|
||||
| `color_scheme` | `color-scheme` | Нативная светлая/темная схема браузера: `dark` или `light`. |
|
||||
| `style_preset` | CSS-класс пресета | Сейчас `win95`/`windows95` добавляет `theme-preset-win95`; остальные значения не дают специального класса. |
|
||||
| `accent` | `--accent` | Главный акцент: активные элементы, кнопки, прогресс, фокус. Только hex `#RGB` или `#RRGGBB`. |
|
||||
| `bg` | `--bg` | Основной фон приложения. |
|
||||
| `panel` | `--panel` | Основные карточки и поверхности. |
|
||||
| `panel_2` | `--panel-2` | Вторичные поверхности. |
|
||||
| `panel_3` | `--panel-3` | Поверхности повышенной вложенности, dropdown/popover. |
|
||||
| `border` | `--border` | Обычные границы. |
|
||||
| `border_strong` | `--border-strong` | Усиленные границы и hover-состояния. |
|
||||
| `text` | `--text` | Основной текст. |
|
||||
| `muted` | `--muted` | Вторичный текст. |
|
||||
| `dim` | `--dim` | Еще более тихий текст и служебные подписи. |
|
||||
| `danger` | `--danger` | Ошибки и опасные действия. |
|
||||
| `blue` | `--blue` | Синий вспомогательный цвет. |
|
||||
| `radius` | `--radius` | Базовый радиус карточек, кнопок и контролов. |
|
||||
| `font_sans` | `--font-sans` | Основной шрифт интерфейса. |
|
||||
| `font_logo` | `--font-logo` | Шрифт бренда и заголовка. |
|
||||
| `font_mono` | `--font-mono` | Моноширинный шрифт. |
|
||||
| `home_logo_scale_desktop` | `--home-logo-scale-desktop` | Масштаб логотипа на desktop layout, от `50` до `300` процентов. |
|
||||
| `home_logo_scale_mobile` | `--home-logo-scale-mobile` | Масштаб логотипа на mobile layout, от `50` до `300` процентов. |
|
||||
| `home_logo_scale` | `--home-logo-scale` | Legacy fallback для старых тем; используется, если desktop/mobile token не задан. |
|
||||
| `admin_bg` | `--admin-bg` | Фон админ-панели. |
|
||||
| `admin_surface` | `--admin-surface` | Основные карточки админки. |
|
||||
| `admin_surface_2` | `--admin-surface-2` | Вторичные поверхности админки. |
|
||||
| `admin_elev` | `--admin-elev` | Elevated-поверхности админки. |
|
||||
| `admin_border` | `--admin-border` | Границы админки. |
|
||||
| `admin_border_strong` | `--admin-border-strong` | Усиленные границы админки. |
|
||||
| `admin_text` | `--admin-text` | Основной текст админки. |
|
||||
| `admin_muted` | `--admin-muted` | Вторичный текст админки. |
|
||||
| `admin_dim` | `--admin-dim` | Тихие подписи админки. |
|
||||
|
||||
Если `css_file` не задан, интерфейс полностью строится на токенах и общих стилях. Если `css_file` задан, токены все равно применяются первыми, а CSS темы может уточнить или полностью переопределить внешний вид.
|
||||
|
||||
@@ -254,7 +258,8 @@ CSS можно писать для пользовательской части
|
||||
content: "";
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
background: url("/webapp-theme-assets/neon/icons/spark.png") center / contain no-repeat;
|
||||
background: url("/webapp-theme-assets/neon/icons/spark.png") center / contain
|
||||
no-repeat;
|
||||
}
|
||||
```
|
||||
|
||||
@@ -302,7 +307,7 @@ CSS можно писать для пользовательской части
|
||||
|
||||
7. Подберите accent и масштаб логотипа.
|
||||
|
||||
В админке можно менять accent и `home_logo_scale` без ручного редактирования JSON. При сохранении backend перепишет `theme.json` в `WEBAPP_THEMES_DIR`, выставит ровно один `default` и сбросит кеш публичных настроек.
|
||||
В админке можно менять accent, `home_logo_scale_desktop` и `home_logo_scale_mobile` без ручного редактирования JSON. При сохранении backend перепишет `theme.json` в `WEBAPP_THEMES_DIR`, выставит ровно один `default` и сбросит кеш публичных настроек. Старый `home_logo_scale` сохраняется как fallback для уже существующих тем.
|
||||
|
||||
8. Добавьте `style.css`, если токенов мало.
|
||||
|
||||
@@ -329,7 +334,6 @@ CSS можно писать для пользовательской части
|
||||
11. Сделайте тему дефолтной.
|
||||
|
||||
Есть два способа:
|
||||
|
||||
- в админке выбрать тему и сохранить;
|
||||
- указать `WEBAPP_DEFAULT_THEME=neon` в `.env`, если нужен жесткий override на уровне окружения.
|
||||
|
||||
|
||||
@@ -87,7 +87,7 @@ openssl rand -hex 32
|
||||
Перед первым запуском создайте каталоги и отдайте их пользователю контейнера:
|
||||
|
||||
```bash
|
||||
mkdir -p data/themes data/webapp-logo data/webapp-emoji data/tariffs
|
||||
mkdir -p data/themes data/webapp-logo data/tariffs
|
||||
touch data/locales-overrides.json
|
||||
chown -R 10001:10001 data
|
||||
chmod -R u+rwX data
|
||||
|
||||
@@ -11,6 +11,8 @@
|
||||
- [Админка: пользователи](/demo/admin/users)
|
||||
- [Админка: бэкапы](/demo/admin/backups)
|
||||
- [Пробный период](/demo/home?mock=trial)
|
||||
- [Email-only: нужен Telegram для триала](/demo/home?mock=trial-telegram)
|
||||
- [Email-only: нужен Telegram для реферального бонуса](/demo/home?mock=referral-telegram)
|
||||
- [Докупка устройств](/demo/devices?mock=devices)
|
||||
- [Запуск бота для Telegram-уведомлений](/demo/home?mock=notifications)
|
||||
- [Вход и регистрация](/demo/login?mock=auth)
|
||||
|
||||
@@ -13,6 +13,44 @@ docker compose ps
|
||||
docker compose logs -f backend worker frontend
|
||||
```
|
||||
|
||||
## Интерактивный install wizard
|
||||
|
||||
Для нового сервера скачайте install-скрипт и запустите его:
|
||||
|
||||
```bash
|
||||
curl -fsSL https://raw.githubusercontent.com/3252a8/remnawave-minishop/main/scripts/install.sh -o install.sh
|
||||
sh install.sh
|
||||
```
|
||||
|
||||
Wizard работает через меню с цифрами и подтверждениями `y/n`. Он умеет:
|
||||
|
||||
- скачать выбранный compose-профиль (`Caddy`, `Nginx`, `Pangolin/Newt` или `no-proxy`);
|
||||
- сгенерировать минимальный `.env`, включая пароли и стабильные secrets;
|
||||
- сохранить backup существующих файлов перед перезаписью;
|
||||
- подготовить writable `data/` для файлов приложения;
|
||||
- запустить `docker compose pull && docker compose up -d`;
|
||||
- проверить текущий стек через `docker compose ps` и логи `migrate`;
|
||||
- запустить миграцию из поддерживаемых ботов: Remnashop и старый
|
||||
`remnawave-tg-shop`;
|
||||
|
||||
Для тестирования другой ветки или форка задайте источник перед запуском:
|
||||
|
||||
```bash
|
||||
MINISHOP_INSTALL_REPO=3252a8/remnawave-minishop \
|
||||
MINISHOP_INSTALL_REF=main \
|
||||
sh install.sh
|
||||
```
|
||||
|
||||
Миграция Remnashop в wizard сначала запускает `dry-run`, показывает JSON-сводку
|
||||
и только после отдельного подтверждения применяет изменения в целевую БД. Если
|
||||
указать старый Remnashop `.env`, wizard передаст importer-у `APP_CRYPT_KEY`,
|
||||
Remnawave API settings и поддерживаемые payment provider settings из таблицы
|
||||
`payment_gateways`. После применения wizard печатает новые webhook URL для
|
||||
Remnawave Panel и платежных провайдеров.
|
||||
Миграция со старого `remnawave-tg-shop` работает как upgrade совместимой БД:
|
||||
либо копирует старый Docker volume, либо делает `pg_dump` по source DSN,
|
||||
восстанавливает дамп в целевую compose-БД и запускает сервис `migrate`.
|
||||
|
||||
Обычный `docker compose up -d --build` поднимает:
|
||||
|
||||
- `postgres` и `redis` с проверками здоровья;
|
||||
@@ -339,7 +377,7 @@ distributed lock; код подготовлен к нескольким репл
|
||||
Перед первым запуском на сервере заранее дайте права пользователю контейнера `10001`:
|
||||
|
||||
```bash
|
||||
mkdir -p data/themes data/webapp-logo data/webapp-emoji data/tariffs
|
||||
mkdir -p data/themes data/webapp-logo data/tariffs
|
||||
touch data/locales-overrides.json
|
||||
chown -R 10001:10001 data
|
||||
chmod -R u+rwX data
|
||||
|
||||
@@ -5,3 +5,4 @@
|
||||
| Источник | Поддерживаемый случай | Документы |
|
||||
| --- | --- | --- |
|
||||
| [remnawave-tg-shop](https://github.com/kavore/remnawave-tg-shop/) | Полный перенос всех данных | [Инструкция](remnawave-tg-shop.md) |
|
||||
| [Remnashop](https://github.com/snoups/remnashop/) | Автоматический импорт пользователей, подписок, платежей, рефералов, промокодов и поддерживаемых платежных настроек | [Инструкция](remnashop.md) |
|
||||
|
||||
@@ -0,0 +1,124 @@
|
||||
# Миграция из Remnashop
|
||||
|
||||
Remnashop импортируется через общий скрипт импорта `backend/scripts/import_legacy.py`.
|
||||
Самый удобный путь - интерактивный install wizard:
|
||||
|
||||
```bash
|
||||
curl -fsSL https://raw.githubusercontent.com/3252a8/remnawave-minishop/main/scripts/install.sh -o install.sh
|
||||
sh install.sh
|
||||
```
|
||||
|
||||
В меню выберите `Install new stack and run migration` для нового сервера
|
||||
или `Run migration only`, если compose-папка и `.env` уже готовы.
|
||||
|
||||
## Что переносится
|
||||
|
||||
- пользователи Telegram, username, email, Remnawave UUID и метаданные профиля;
|
||||
- старые referral codes и связи рефералов;
|
||||
- подписки, сроки, лимиты трафика, HWID/device limit и UUID подписок панели;
|
||||
- платежи и статусы платежей;
|
||||
- промокоды на дни подписки и их активации, если таблицы есть в source DB;
|
||||
- служебные mappings, чтобы повторный запуск мог работать в режиме `merge`;
|
||||
- настройки совместимости Remnashop в админке: старые ref-ссылки и promo codes.
|
||||
|
||||
Данные, которые не имеют прямого аналога, сохраняются в служебных таблицах миграции или
|
||||
message logs как заметки, чтобы администратор мог проверить их после переноса.
|
||||
|
||||
## Настройки и платежные провайдеры
|
||||
|
||||
Если указать старый Remnashop `.env`, importer дополнительно переносит часть
|
||||
настроек в админские overrides:
|
||||
|
||||
- `REMNAWAVE_HOST` -> `PANEL_API_URL`;
|
||||
- `REMNAWAVE_TOKEN` -> `PANEL_API_KEY`;
|
||||
- `REMNAWAVE_WEBHOOK_SECRET` -> `PANEL_WEBHOOK_SECRET`;
|
||||
- `BOT_SUPPORT_USERNAME` -> `SUPPORT_LINK`;
|
||||
- `APP_DEFAULT_LOCALE` -> `DEFAULT_LANGUAGE`.
|
||||
|
||||
`BOT_MINI_APP` из Remnashop не переносится автоматически. В Remnashop эта
|
||||
переменная управляет кнопкой подключения к subscription page или внешнему Mini
|
||||
App, а не веб-кабинетом Remnashop. В Minishop `SUBSCRIPTION_MINI_APP_URL`
|
||||
должен указывать на текущий frontend/Mini App этого стека; wizard настраивает
|
||||
его из `WEBHOOK_HOST`/`MINIAPP_HOST` или `MINIAPP_PUBLIC_URL`.
|
||||
|
||||
Значения-заглушки вроде `change_me` importer пропускает, чтобы случайно не
|
||||
записать шаблонные секреты в рабочую конфигурацию.
|
||||
|
||||
Платежные провайдеры берутся из таблицы Remnashop `payment_gateways`.
|
||||
Поддерживаются и автоматически маппятся: Telegram Stars, YooKassa, WATA,
|
||||
CryptoPay, Heleket, PayKilla, FreeKassa и Platega. Для них importer переносит флаги
|
||||
включения, API-ключи/merchant IDs и прямые технические параметры, без которых
|
||||
провайдер не сможет работать: YooKassa receipt email/VAT, FreeKassa second
|
||||
secret/payment method/server IP и Platega payment method.
|
||||
|
||||
Provider currency и supported-currency ограничения не переносятся автоматически:
|
||||
в Minishop валюта платежа управляется тарифами и `DEFAULT_CURRENCY_SYMBOL`.
|
||||
Если старый gateway Remnashop был настроен на нестандартную валюту, importer
|
||||
оставит предупреждение в JSON-сводке; проверьте `CRYPTOPAY_ASSET`,
|
||||
`HELEKET_CURRENCY`, `HELEKET_SUPPORTED_CURRENCIES`, `PAYKILLA_CURRENCY`,
|
||||
`PAYKILLA_PAYMENT_CURRENCIES` или
|
||||
`PLATEGA_SUPPORTED_CURRENCIES` вручную.
|
||||
|
||||
Провайдеры YooMoney, Cryptomus, MulenPay, PayMaster, RoboKassa и UrlPay сейчас
|
||||
не имеют прямого аналога в Minishop. Если они были в Remnashop, importer
|
||||
оставит предупреждение в JSON-сводке и notes миграции, а настроить их нужно
|
||||
вручную или через будущий отдельный provider.
|
||||
|
||||
Remnashop может хранить секреты в формате `enc_...`. Для расшифровки нужен
|
||||
старый `APP_CRYPT_KEY`; проще всего указать путь к старому `.env` в wizard или
|
||||
передать `--source-env-file`. Если ключ не передан или неверный, зашифрованные
|
||||
значения будут пропущены с предупреждением, остальные данные продолжат
|
||||
импортироваться.
|
||||
|
||||
После успешного применения wizard печатает список новых адресов webhook. Их
|
||||
нужно указать во внешних сервисах вместо старых Remnashop URL:
|
||||
|
||||
- Remnawave Panel -> `WEBHOOK_URL`: `WEBHOOK_BASE_URL` + `/webhook/panel`;
|
||||
- YooKassa HTTP notifications URL: `WEBHOOK_BASE_URL` + `/webhook/yookassa`;
|
||||
- WATA webhook/callback URL: `WEBHOOK_BASE_URL` + `/webhook/wata`;
|
||||
- CryptoBot/Crypto Pay webhook URL: `WEBHOOK_BASE_URL` + `/webhook/cryptopay`;
|
||||
- Heleket payment webhook/callback URL: `WEBHOOK_BASE_URL` + `/webhook/heleket`;
|
||||
- PayKilla webhook URL: `WEBHOOK_BASE_URL` + `/webhook/paykilla`;
|
||||
- FreeKassa notification/result URL: `WEBHOOK_BASE_URL` + `/webhook/freekassa`;
|
||||
- Platega webhook URL: `WEBHOOK_BASE_URL` + `/webhook/platega`;
|
||||
- Telegram webhook `WEBHOOK_BASE_URL` + `/tg/webhook` выставляется ботом
|
||||
автоматически при старте.
|
||||
|
||||
## Flow wizard
|
||||
|
||||
1. Wizard скачивает compose-профиль и `backend/scripts/import_legacy.py` через
|
||||
`raw.githubusercontent.com`, без клонирования репозитория.
|
||||
2. Вы указываете source PostgreSQL DSN Remnashop и schema, обычно `public`.
|
||||
3. Опционально указываете путь к старому Remnashop `.env` для `APP_CRYPT_KEY`,
|
||||
Remnawave API settings и переносимых settings.
|
||||
4. Вы выбираете целевую БД: текущую compose-БД или ручной target DSN.
|
||||
5. При необходимости указываете JSON map тарифов Remnashop в локальные
|
||||
`tariff_key`, например `{"basic": "standard_month"}`.
|
||||
6. Wizard запускает `dry-run` и показывает JSON-сводку.
|
||||
7. После подтверждения `y` importer применяет изменения, печатает список новых
|
||||
webhook URL для Remnawave Panel и платежных провайдеров, затем перезапускает
|
||||
`backend`/`worker`, чтобы настройки совместимости перечитались.
|
||||
|
||||
Если source DB находится на том же Docker host, помните, что DSN выполняется
|
||||
из backend-контейнера. Для подключения к сервису вне compose-сети может
|
||||
понадобиться host name вроде `host.docker.internal`, внешний адрес сервера или
|
||||
ручное подключение контейнеров к общей Docker network.
|
||||
|
||||
## Ручной запуск
|
||||
|
||||
Если нужно запустить importer без wizard:
|
||||
|
||||
```bash
|
||||
docker compose run --rm backend \
|
||||
python backend/scripts/import_legacy.py \
|
||||
--source-type remnashop \
|
||||
--source-dsn 'postgresql://old_user:old_password@old_host:5432/remnashop' \
|
||||
--source-schema public \
|
||||
--source-env-file /path/to/remnashop/.env \
|
||||
--dry-run
|
||||
```
|
||||
|
||||
После успешного `dry-run` повторите команду без `--dry-run`. По умолчанию
|
||||
режим конфликтов `merge`: существующие пользователи и платежи сопоставляются,
|
||||
а новые записи добавляются. Для узкого импорта используйте `--only`, например
|
||||
`--only users,referrals,promocodes`.
|
||||
@@ -1,90 +1,60 @@
|
||||
# Миграция с `remnawave-tg-shop` (≤ v2.7.0) на `remnawave-minishop` (v3.4+)
|
||||
# Миграция с `remnawave-tg-shop` на `remnawave-minishop`
|
||||
|
||||
Эта страница - готовый сценарий для legacy-стека `remnawave-tg-shop`. Это единственная миграция с другого бота, которая сейчас описана в документации. Для других Telegram-ботов, самописных панелей и ручных таблиц готового сценария пока нет: их нельзя переносить по этой инструкции без отдельного анализа схемы БД, тарифов, платежей и связи с Remnawave Panel.
|
||||
|
||||
Автоматический скрипт ниже рассчитан именно на родственный стек `remnawave-tg-shop`, где структура БД и Docker volumes известны заранее. Для других ботов нужен отдельный адаптер экспорта/импорта.
|
||||
|
||||
## Короткий путь без смены ветки и сборки
|
||||
|
||||
Если вы используете только готовые Docker-образы и не собираете проект
|
||||
локально, git-команды из ручного способа не нужны. Достаточно обновить
|
||||
compose-файл до одного из готовых примеров в `deploy/examples` и
|
||||
перенести/обновить БД. Самый прямой вариант без встроенного обратного прокси -
|
||||
`deploy/examples/no-proxy/docker-compose.yml`; для Caddy, Nginx и Newt есть
|
||||
такие же самостоятельные папки.
|
||||
|
||||
Минимальная последовательность:
|
||||
Для переноса со старого родственного стека используйте общий install wizard:
|
||||
|
||||
```bash
|
||||
docker compose down
|
||||
|
||||
# Скопируйте старый .env в выбранную папку примера и обновите значения там.
|
||||
cp .env deploy/examples/no-proxy/.env
|
||||
nano deploy/examples/no-proxy/.env
|
||||
|
||||
# Подготовьте стек из готовых образов.
|
||||
IMAGE_TAG=3.4.0 docker compose \
|
||||
--env-file deploy/examples/no-proxy/.env \
|
||||
-f deploy/examples/no-proxy/docker-compose.yml \
|
||||
up --no-start
|
||||
|
||||
# Нужно только при переходе со старого имени volume remnawave-tg-shop-db-data.
|
||||
# Если у вас уже есть remnawave-minishop-db-data, этот шаг пропустите.
|
||||
docker run --rm \
|
||||
-v remnawave-tg-shop-db-data:/from:ro \
|
||||
-v remnawave-minishop-db-data:/to \
|
||||
alpine sh -c "cd /from && cp -a . /to"
|
||||
|
||||
IMAGE_TAG=3.4.0 docker compose \
|
||||
--env-file deploy/examples/no-proxy/.env \
|
||||
-f deploy/examples/no-proxy/docker-compose.yml \
|
||||
up -d
|
||||
docker compose \
|
||||
--env-file deploy/examples/no-proxy/.env \
|
||||
-f deploy/examples/no-proxy/docker-compose.yml \
|
||||
logs migrate
|
||||
curl -fsSL https://raw.githubusercontent.com/3252a8/remnawave-minishop/main/scripts/install.sh -o install.sh
|
||||
sh install.sh
|
||||
```
|
||||
|
||||
Сервис `migrate` сам применит недостающие схемные миграции к перенесённому
|
||||
тому PostgreSQL. Новые тома `remnawave-minishop-redis-data` и
|
||||
`remnawave-minishop-shop-data` переносить не нужно: они создаются пустыми.
|
||||
В меню выберите `Install new stack and run migration` для нового
|
||||
сервера или `Run migration only`, если compose-папка уже готова. Затем
|
||||
выберите источник `Old remnawave-tg-shop`.
|
||||
|
||||
Этот документ описывает обновление стека, поднятого по `remnawave-tg-shop`
|
||||
(включая последний релиз `v2.7.0` форка `kavore/remnawave-tg-shop`), до
|
||||
текущей версии `remnawave-minishop` (v3.4+). Между этими версиями произошли
|
||||
две независимые перетряски, и скрипт пытается отработать обе одной командой:
|
||||
Wizard поддерживает два способа переноса:
|
||||
|
||||
1. **Переименование стека** (v3.1.0): контейнеры и тома `remnawave-tg-shop-*`
|
||||
стали `remnawave-minishop-*`. Простой `docker compose up -d` после
|
||||
`git pull` создаёт пустую БД — без переноса тома данные теряются.
|
||||
2. **Разделение бота на сервисы** (v3.4.0): из одного контейнера выделены
|
||||
`backend`, `worker`, `frontend`, `migrate` + новые `postgres`, `redis`.
|
||||
Появились новые volumes `redis-data` и `shop-data`, новые обязательные
|
||||
переменные окружения, а схема БД обновляется автоматически one-shot
|
||||
сервисом `migrate`.
|
||||
- `Copy old Docker volumes` - для старого compose-стека на том же Docker host.
|
||||
Скрипт подготавливает новый stack, копирует
|
||||
`remnawave-tg-shop-db-data` в `remnawave-minishop-db-data`, опционально
|
||||
переносит Caddy volumes и запускает новый stack.
|
||||
- `Dump from a source PostgreSQL DSN` - для старой БД, доступной по DSN.
|
||||
Скрипт поднимает целевой `postgres`, сбрасывает целевую БД, делает
|
||||
`pg_dump` из старой БД, восстанавливает дамп в compose-БД и запускает
|
||||
сервис `migrate`.
|
||||
|
||||
После миграции `docker compose ps` должен показать как минимум: `backend`,
|
||||
`worker`, `frontend`, `postgres`, `redis` (running) и `migrate` (exited 0).
|
||||
Логи: `docker compose logs -f backend worker frontend`.
|
||||
В обоих режимах старые volumes и старая БД не удаляются автоматически.
|
||||
|
||||
Доступные пути:
|
||||
## Как работает перенос
|
||||
|
||||
- [Автоматический](#автоматический-способ-через-скрипт) — скрипт-обёртка
|
||||
останавливает старый стек, накатывает свежий код, переносит том БД,
|
||||
поднимает новые сервисы. Идемпотентный.
|
||||
- [Ручной](#ручной-способ) — те же шаги командами, для тех, кому нужно
|
||||
понимать каждое действие или выполнить выборочно.
|
||||
`remnawave-tg-shop` и `remnawave-minishop` имеют совместимую историю схемы.
|
||||
После переноса старой PostgreSQL-БД сервис `migrate` накатывает недостающие
|
||||
миграции из `backend/db/migrator.py`: сначала применяются `Base.metadata`,
|
||||
затем последовательные записи `schema_migrations`. Это one-shot сервис: он
|
||||
должен завершиться с кодом `0`, после чего стартуют `backend` и `worker`.
|
||||
|
||||
В обоих случаях:
|
||||
При volume-миграции wizard:
|
||||
|
||||
- старые тома **не удаляются** автоматически — это безопасный бэкап на случай
|
||||
отката;
|
||||
- сертификаты Caddy (если используется `deploy/examples/caddy/docker-compose.yml`)
|
||||
тоже переносятся, чтобы Let's Encrypt не выписывал их заново и не упереться
|
||||
в rate limit;
|
||||
- схема БД обновляется автоматически: при первом `docker compose up -d` сервис
|
||||
`migrate` накатывает на перенесённый том все недостающие миграции (от
|
||||
alembic-схемы v2.7.0 до текущей).
|
||||
1. Останавливает известные контейнеры старого и переходного стеков, если вы
|
||||
подтверждаете этот шаг.
|
||||
2. Запускает `docker compose up --no-start`, чтобы Docker Compose создал новые
|
||||
volumes.
|
||||
3. Копирует старый volume БД:
|
||||
|
||||
```bash
|
||||
docker run --rm \
|
||||
-v remnawave-tg-shop-db-data:/from:ro \
|
||||
-v remnawave-minishop-db-data:/to \
|
||||
alpine sh -c "cd /from && cp -a . /to"
|
||||
```
|
||||
|
||||
4. Если старые Caddy volumes существуют, переносит
|
||||
`remnawave-tg-shop-caddy-data` -> `remnawave-minishop-caddy-data` и
|
||||
`remnawave-tg-shop-caddy-config` -> `remnawave-minishop-caddy-config`.
|
||||
5. Запускает новый stack через Docker Compose.
|
||||
|
||||
Если целевой DB volume уже непустой, wizard не перетирает его молча: он
|
||||
останавливается и просит отдельное подтверждение на продолжение без копирования
|
||||
старой БД.
|
||||
|
||||
## Что меняется в архитектуре
|
||||
|
||||
@@ -93,256 +63,55 @@ docker compose \
|
||||
| Версия | Сервисы |
|
||||
| --- | --- |
|
||||
| `v2.7.0` | `remnawave-tg-shop`, `remnawave-tg-shop-db` |
|
||||
| `v3.1.x–v3.3.x` | `remnawave-minishop`, `remnawave-minishop-db` |
|
||||
| `v3.4+` (текущая) | `remnawave-minishop-backend`, `remnawave-minishop-worker`, `remnawave-minishop-frontend`, `remnawave-minishop-migrate`, `remnawave-minishop-postgres`, `remnawave-minishop-redis` |
|
||||
|
||||
Внутри Docker-сети сервисы доступны по коротким DNS-именам (`backend`, `worker`,
|
||||
`frontend`, `postgres`, `redis`), а не по полному `container_name`. Это важно
|
||||
для внешнего reverse-proxy — см. раздел [Внешний reverse-proxy](#внешний-reverse-proxy) ниже.
|
||||
| `v3.1.x-v3.3.x` | `remnawave-minishop`, `remnawave-minishop-db` |
|
||||
| `v3.4+` | `remnawave-minishop-backend`, `remnawave-minishop-worker`, `remnawave-minishop-frontend`, `remnawave-minishop-migrate`, `remnawave-minishop-postgres`, `remnawave-minishop-redis` |
|
||||
|
||||
**Volumes**:
|
||||
|
||||
| Volume | v2.7.0 | v3.4+ | Что внутри |
|
||||
| --- | --- | --- | --- |
|
||||
| `remnawave-minishop-db-data` | переименовать из `remnawave-tg-shop-db-data` | переносится скриптом | PostgreSQL |
|
||||
| `remnawave-minishop-redis-data` | — | создаётся пустым | Redis (FSM, rate-limit, cache, очередь вебхуков, distributed locks) |
|
||||
| `remnawave-minishop-shop-data` | — | создаётся пустым | `/app/data`: `tariffs.json`, темы Web App, кэш логотипа/emoji |
|
||||
| `remnawave-minishop-caddy-data` / `remnawave-minishop-caddy-config` | переименовать из `remnawave-tg-shop-caddy-*` | переносится скриптом | только при Caddy-варианте |
|
||||
| Volume | Что происходит |
|
||||
| --- | --- |
|
||||
| `remnawave-minishop-db-data` | переносится из `remnawave-tg-shop-db-data` или восстанавливается из source DSN |
|
||||
| `remnawave-minishop-redis-data` | создается пустым |
|
||||
| `remnawave-minishop-shop-data` | создается пустым; runtime-файлы в `/app/data` дальше настраиваются через админку или вручную |
|
||||
| `remnawave-minishop-caddy-data` / `remnawave-minishop-caddy-config` | переносятся из `remnawave-tg-shop-caddy-*`, если старый стек использовал Caddy |
|
||||
|
||||
`redis-data` и `shop-data` стартуют пустыми — это нормально. Redis ничего
|
||||
долгоживущего не хранит (всё либо FSM, либо кеш с TTL), а `data/` инициализируется
|
||||
из образа при первом старте (`tariffs.json` пуст пока вы не сконфигурируете
|
||||
тарифы через админ-панель).
|
||||
Доступные compose-профили: `docker-compose.yml`,
|
||||
`deploy/examples/caddy/docker-compose.yml`,
|
||||
`deploy/examples/nginx/docker-compose.yml`,
|
||||
`deploy/examples/newt/docker-compose.yml`,
|
||||
`deploy/examples/no-proxy/docker-compose.yml`.
|
||||
|
||||
## Переменные окружения, которые могли исчезнуть или переехать
|
||||
## Переменные окружения
|
||||
|
||||
Перед запуском нового стека проверьте `.env`. Ниже — только то, что точно
|
||||
менялось между v2.7.0 и v3.4+:
|
||||
Перед запуском нового стека проверьте `.env`. Самые важные изменения:
|
||||
|
||||
| Было (v2.7.0) | Стало (v3.4+) | Действие |
|
||||
| Было | Стало | Действие |
|
||||
| --- | --- | --- |
|
||||
| `TELEGRAM_WEBHOOK_SECRET` | `WEBHOOK_SECRET_TOKEN` | Переименовать. Если пусто — будет сгенерирован при старте, но тогда Telegram переустановит webhook (на это не реагирует существующий запрос). |
|
||||
| `TELEGRAM_WEBHOOK_PATH` | удалена | Путь вебхука теперь генерируется из `BOT_TOKEN` автоматически. |
|
||||
| `REQUIRED_CHANNEL_SUBSCRIBE_TO_USE` | удалена | Гейт включается автоматически, как только задан `REQUIRED_CHANNEL_ID`. |
|
||||
| `STARS_PROVIDER_TOKEN` | удалена | Telegram Stars (XTR) используются напрямую. |
|
||||
| `REFERRAL_ENABLED` | удалена | Реферальная программа активна по умолчанию. В legacy-режиме без JSON-каталога отключайте платежные бонусы через нули в `REFERRAL_BONUS_DAYS_*` и `REFEREE_BONUS_DAYS_*`; в JSON-тарифах обнуляйте или удаляйте `referral_bonus_days_inviter` и `referral_bonus_days_referee` у period-тарифов. |
|
||||
| `POSTGRES_HOST=remnawave-tg-shop-db` | в `.env` — `remnawave-minishop-db` или пусто | Под compose значение всё равно переопределяется на сервисное имя `postgres` (см. `environment:` в compose-файлах), поэтому скрипт правит `.env` только для bare-metal сценариев. |
|
||||
| `WEBHOOK_BASE_URL` | **обязательна** | Polling-режим удалён, без публичного URL бот не стартует. |
|
||||
| — | `REDIS_URL=redis://redis:6379/0` | Обязательна для воркера, очередей и rate-limit. По умолчанию в compose-файлах уже задана. |
|
||||
| — | `WEBAPP_SESSION_SECRET`, `WEBAPP_ENABLED`, `WEBAPP_SERVER_PORT`, `WEBAPP_THEMES_DIR`, `TARIFFS_CONFIG_PATH` | Новые настройки Web App / тарифного каталога. Безопасные дефолты есть в `.env.example`. |
|
||||
| `TELEGRAM_WEBHOOK_SECRET` | `WEBHOOK_SECRET_TOKEN` | Перенести значение или сгенерировать новый stable secret. |
|
||||
| `TELEGRAM_WEBHOOK_PATH` | удалена | Путь вебхука теперь рассчитывается автоматически. |
|
||||
| `REQUIRED_CHANNEL_SUBSCRIBE_TO_USE` | удалена | Гейт включается, когда задан `REQUIRED_CHANNEL_ID`. |
|
||||
| `STARS_PROVIDER_TOKEN` | удалена | Telegram Stars используются напрямую. |
|
||||
| `POSTGRES_HOST=remnawave-tg-shop-db` | `postgres` внутри Compose | В compose-файлах `POSTGRES_HOST` переопределяется service name `postgres`. |
|
||||
| `WEBHOOK_BASE_URL` | обязательна | Без публичного URL backend не стартует корректно. |
|
||||
| - | `REDIS_URL=redis://redis:6379/0` | В compose-профилях задано автоматически. |
|
||||
| - | `WEBAPP_SESSION_SECRET`, `WEBAPP_ENABLED`, `TARIFFS_CONFIG_PATH` | Новые настройки Web App и каталога тарифов. |
|
||||
|
||||
Полный референс — [docs/getting-started/configuration.md](../getting-started/configuration.md). Скрипт миграции
|
||||
эти переменные **не правит** автоматически (только `POSTGRES_HOST`), потому
|
||||
что у каждой инсталляции свой шаблон `.env` с кастомными значениями. Лучше
|
||||
сравнить свой `.env` с `.env.example` глазами один раз, чем получить
|
||||
несовместимый шаблон автоматом.
|
||||
Остальные продуктовые настройки удобнее проверить после первого входа в
|
||||
админку.
|
||||
|
||||
## Автоматический способ (через скрипт)
|
||||
## Reverse Proxy
|
||||
|
||||
Если helper ещё не лежит у вас локально, запускайте его прямо из `raw` из
|
||||
корня старого репозитория:
|
||||
В старом стеке часто был один upstream `remnawave-tg-shop:8000`. В текущем
|
||||
split-arch stack маршруты разделены:
|
||||
|
||||
```bash
|
||||
bash <(curl -fsSL https://raw.githubusercontent.com/3252a8/remnawave-minishop/main/scripts/migrate_to_minishop.sh)
|
||||
```
|
||||
|
||||
> Команда выше рассчитана на `bash` / Git Bash / WSL. Если вы запускаете из
|
||||
> PowerShell, удобнее сначала открыть Git Bash.
|
||||
|
||||
Если вы уже подтянули новую версию и файл есть локально, можно запускать так:
|
||||
|
||||
```bash
|
||||
bash scripts/migrate_to_minishop.sh
|
||||
```
|
||||
|
||||
По умолчанию скрипт работает с `docker-compose.yml` и переключается на ветку
|
||||
`main`. Можно переопределить через переменные окружения:
|
||||
|
||||
| Переменная | Назначение | По умолчанию |
|
||||
| ----------------- | ----------------------------------------------------------------------- | ---------------------- |
|
||||
| `PROJECT_ROOT` | Явный путь к корню старого репозитория, если запуск не из него | текущая директория |
|
||||
| `COMPOSE_FILE` | Какой compose-файл стартовать в конце | `docker-compose.yml` |
|
||||
| `TARGET_BRANCH` | На какую ветку переключаться и подтягивать обновления | `main` |
|
||||
| `GIT_REMOTE` | Какой remote использовать для `fetch`/`pull` | `origin` |
|
||||
| `NEW_ORIGIN_URL` | Если задано и не совпадает с URL выбранного remote — он будет обновлён | (не меняется) |
|
||||
| `ASSUME_YES` | `1` — не задавать интерактивных вопросов | `0` |
|
||||
|
||||
Примеры:
|
||||
|
||||
```bash
|
||||
# Caddy-вариант из raw-файла.
|
||||
# Перед запуском скопируйте старый .env в deploy/examples/caddy/.env
|
||||
# и заполните WEBHOOK_HOST / MINIAPP_HOST.
|
||||
COMPOSE_FILE=deploy/examples/caddy/docker-compose.yml \
|
||||
bash <(curl -fsSL https://raw.githubusercontent.com/3252a8/remnawave-minishop/main/scripts/migrate_to_minishop.sh)
|
||||
|
||||
# С переключением origin на форк 3252a8
|
||||
NEW_ORIGIN_URL=https://github.com/3252a8/remnawave-minishop.git \
|
||||
bash <(curl -fsSL https://raw.githubusercontent.com/3252a8/remnawave-minishop/main/scripts/migrate_to_minishop.sh)
|
||||
|
||||
# Без интерактива
|
||||
ASSUME_YES=1 \
|
||||
bash <(curl -fsSL https://raw.githubusercontent.com/3252a8/remnawave-minishop/main/scripts/migrate_to_minishop.sh)
|
||||
```
|
||||
|
||||
Что делает скрипт:
|
||||
|
||||
1. **Останавливает текущий стек**: ищет известные контейнеры старой схемы
|
||||
(`remnawave-tg-shop`, `…-db`, `…-caddy`), переходного периода
|
||||
(`remnawave-minishop`, `…-db`, `…-caddy`) и новой схемы
|
||||
(`…-backend`, `…-worker`, `…-frontend`, `…-migrate`, `…-postgres`, `…-redis`)
|
||||
и останавливает их, если запущены. Безопасно при повторном запуске.
|
||||
2. **Переключает `origin`**, если задана переменная `NEW_ORIGIN_URL`, иначе
|
||||
оставляет как есть.
|
||||
3. **Подтягивает целевую ветку** (`git fetch` + `git switch` + `git pull --ff-only`).
|
||||
Прерывается, если в рабочем дереве есть незакоммиченные изменения.
|
||||
4. **Правит `POSTGRES_HOST` в `.env`** (только для bare-metal сценариев — в
|
||||
compose это значение перебивает `environment:` блок).
|
||||
5. **Подготавливает новый стек в режиме `--no-start`**, чтобы Compose сам
|
||||
создал тома `db-data`, `redis-data`, `shop-data` и не ругался на уже
|
||||
существующий volume.
|
||||
6. **Переносит том БД** `remnawave-tg-shop-db-data` → `remnawave-minishop-db-data`
|
||||
(и Caddy-тома, если применимо) через одноразовый `alpine`-контейнер. Если
|
||||
новый том уже непустой — копирование пропускается. Новые volumes
|
||||
`redis-data` и `shop-data` остаются пустыми (их и не должно быть в старом
|
||||
стеке).
|
||||
7. **Стартует новый стек** (`docker compose up -d --remove-orphans` плюс
|
||||
`--build` для локальной сборки). `migrate` отработает первым, накатит
|
||||
на перенесённый том все недостающие миграции (от alembic-схемы v2.7.0 до
|
||||
текущей) и завершится. Затем стартуют `backend`, `worker`, `frontend`.
|
||||
|
||||
Скрипт идемпотентен: повторный запуск ничего не сломает, просто пропустит уже
|
||||
выполненные шаги.
|
||||
|
||||
После того как убедитесь, что бот работает и данные на месте, удалите старые
|
||||
тома:
|
||||
|
||||
```bash
|
||||
docker volume rm remnawave-tg-shop-db-data
|
||||
docker volume rm remnawave-tg-shop-caddy-data remnawave-tg-shop-caddy-config 2>/dev/null || true
|
||||
```
|
||||
|
||||
## Ручной способ
|
||||
|
||||
1. **Остановите старый стек и обновите код:**
|
||||
|
||||
```bash
|
||||
docker compose down
|
||||
git fetch origin
|
||||
git checkout main
|
||||
git pull --ff-only origin main
|
||||
```
|
||||
|
||||
2. **(Только для bare-metal без compose)** обновите `.env`, если в нём ещё
|
||||
жёстко прописан старый контейнер БД:
|
||||
|
||||
```bash
|
||||
sed -i.bak 's/^POSTGRES_HOST=remnawave-tg-shop-db$/POSTGRES_HOST=remnawave-minishop-db/' .env
|
||||
```
|
||||
|
||||
Под `docker compose up` это не нужно: compose сам выставляет
|
||||
`POSTGRES_HOST: postgres` (имя сервиса) в `environment:` и `.env`-значение
|
||||
не используется.
|
||||
|
||||
3. **Проверьте `.env`** на наличие переменных, которые исчезли или
|
||||
переименовались — см. раздел
|
||||
[Переменные окружения](#переменные-окружения-которые-могли-исчезнуть-или-переехать)
|
||||
выше. Главное: `WEBHOOK_SECRET_TOKEN` (бывший `TELEGRAM_WEBHOOK_SECRET`),
|
||||
обязательный `WEBHOOK_BASE_URL` и наличие `REDIS_URL` (по умолчанию задано
|
||||
в compose).
|
||||
|
||||
4. **Подготовьте новый стек без запуска**, чтобы Compose создал новые volumes
|
||||
(`db-data`, `redis-data`, `shop-data`) и контейнеры:
|
||||
|
||||
```bash
|
||||
# Локальная сборка
|
||||
docker compose up --no-start --build
|
||||
|
||||
# Или готовый Caddy-вариант из GHCR-образов
|
||||
cp .env deploy/examples/caddy/.env
|
||||
nano deploy/examples/caddy/.env
|
||||
docker compose \
|
||||
--env-file deploy/examples/caddy/.env \
|
||||
-f deploy/examples/caddy/docker-compose.yml \
|
||||
up --no-start
|
||||
|
||||
# Другие готовые варианты:
|
||||
# deploy/examples/nginx/docker-compose.yml
|
||||
# deploy/examples/newt/docker-compose.yml
|
||||
# deploy/examples/no-proxy/docker-compose.yml
|
||||
```
|
||||
|
||||
5. **Перенесите том БД в новое имя:**
|
||||
|
||||
```bash
|
||||
docker run --rm \
|
||||
-v remnawave-tg-shop-db-data:/from:ro \
|
||||
-v remnawave-minishop-db-data:/to \
|
||||
alpine sh -c "cd /from && cp -a . /to"
|
||||
```
|
||||
|
||||
`remnawave-minishop-redis-data` и `remnawave-minishop-shop-data` — новые,
|
||||
переносить нечего. Они инициализируются на лету: Redis пуст, а `data/`
|
||||
наполняется при первом обращении к настройкам Web App / каталогу тарифов.
|
||||
|
||||
6. **(Только для Caddy)** перенесите тома Caddy с TLS-сертификатами и
|
||||
состоянием ACME:
|
||||
|
||||
```bash
|
||||
for v in caddy-data caddy-config; do
|
||||
docker run --rm \
|
||||
-v "remnawave-tg-shop-$v":/from:ro \
|
||||
-v "remnawave-minishop-$v":/to \
|
||||
alpine sh -c "cd /from && cp -a . /to"
|
||||
done
|
||||
```
|
||||
|
||||
7. **Запустите новый стек:**
|
||||
|
||||
```bash
|
||||
docker compose up -d
|
||||
# или
|
||||
docker compose \
|
||||
--env-file deploy/examples/caddy/.env \
|
||||
-f deploy/examples/caddy/docker-compose.yml \
|
||||
up -d
|
||||
```
|
||||
|
||||
Сервис `migrate` запустится первым, обнаружит перенесённый том,
|
||||
применит недостающие схемные миграции (`Base.metadata.create_all` +
|
||||
последовательные миграции `0001..00NN` из `backend/db/migrator.py`) и
|
||||
выйдет с кодом 0. Только после этого стартуют `backend` и `worker`.
|
||||
|
||||
8. **Проверьте состояние:**
|
||||
|
||||
```bash
|
||||
docker compose ps
|
||||
docker compose logs -f backend worker frontend
|
||||
docker compose logs migrate # должен закончиться "Migrator: migration 00NN applied successfully"
|
||||
```
|
||||
|
||||
9. **(Опционально) удалите старые тома**, когда убедитесь, что новый стек
|
||||
стабилен:
|
||||
|
||||
```bash
|
||||
docker volume rm remnawave-tg-shop-db-data
|
||||
docker volume rm remnawave-tg-shop-caddy-data remnawave-tg-shop-caddy-config 2>/dev/null || true
|
||||
```
|
||||
|
||||
## Внешний reverse-proxy
|
||||
|
||||
В v2.7.0 был один upstream — `remnawave-tg-shop:8000`. В v3.4+ функциональность
|
||||
разнесена по портам и сервисам:
|
||||
|
||||
| Назначение | DNS-имя сервиса | Порт |
|
||||
| Назначение | Service | Port |
|
||||
| --- | --- | --- |
|
||||
| Telegram / платежные / вебхуки панели | `backend` | `8080` |
|
||||
| Health-чек | `backend` | `8080` (`/healthz`) |
|
||||
| Web App API (`/api/*`, `/auth/*`, ассеты тем и логотипов) | `backend` | `8081` (доступен только из Docker-сети) |
|
||||
| Статический фронт Web App | `frontend` | `80` (внутри `frontend` уже проксирует `/api/*` и `/auth/*` на `backend:8081`) |
|
||||
| Telegram, платежные и panel webhooks | `backend` | `8080` |
|
||||
| Health-check | `backend` | `8080` (`/healthz`) |
|
||||
| Web App API и auth | `backend` | `8081` внутри Docker-сети |
|
||||
| Статический Web App frontend | `frontend` | `80` |
|
||||
|
||||
Минимальная замена для внешнего Nginx, который раньше слал всё на один
|
||||
upstream:
|
||||
Минимальная схема для внешнего Nginx:
|
||||
|
||||
```nginx
|
||||
upstream remnawave_backend_webhooks { server backend:8080; }
|
||||
@@ -351,33 +120,32 @@ upstream remnawave_frontend { server frontend:80; }
|
||||
server {
|
||||
server_name app.domain.com;
|
||||
listen 443 ssl;
|
||||
http2 on;
|
||||
# ssl_certificate / ssl_certificate_key — без изменений
|
||||
|
||||
location /webhook/ { proxy_pass http://remnawave_backend_webhooks; }
|
||||
location /healthz { proxy_pass http://remnawave_backend_webhooks; }
|
||||
location / { proxy_pass http://remnawave_frontend; }
|
||||
location / { proxy_pass http://remnawave_frontend; }
|
||||
}
|
||||
```
|
||||
|
||||
Полные примеры (Caddy, Nginx, Newt/Pangolin и запуск без обратного прокси) — в
|
||||
[docs/getting-started/deployment.md](../getting-started/deployment.md) и [docs/features/web-app.md](../features/web-app.md). Если раньше прокси указывал на
|
||||
`remnawave-tg-shop:8000` напрямую, после миграции нужно либо переключиться на
|
||||
`backend:8080` / `frontend:80`, либо использовать готовый Caddy/Nginx/Newt
|
||||
пример, который уже знает правильную маршрутизацию.
|
||||
Готовые Caddy, Nginx, Pangolin/Newt и no-proxy профили уже содержат нужную
|
||||
маршрутизацию.
|
||||
|
||||
## Если что-то пошло не так
|
||||
## Проверка
|
||||
|
||||
`migrate` упал → читайте `docker compose logs migrate`. Том БД остался
|
||||
не тронут, можно откатиться, переключив compose-файл обратно на старый
|
||||
коммит и подняв старый стек на старом томе `remnawave-tg-shop-db-data`
|
||||
(пока вы его не удалили).
|
||||
После переноса:
|
||||
|
||||
`backend` не стартует → чаще всего `WEBHOOK_BASE_URL` пуст, либо
|
||||
`WEBHOOK_SECRET_TOKEN` отличается от того, что Telegram ждёт. Поставьте
|
||||
свежий секрет в `.env` и перезапустите — Telegram переустановит webhook
|
||||
автоматически.
|
||||
```bash
|
||||
docker compose ps
|
||||
docker compose logs migrate
|
||||
docker compose logs -f backend worker frontend
|
||||
```
|
||||
|
||||
Web App пуст / 502 → проверьте, что `frontend` живёт (`docker compose ps`),
|
||||
а внешний прокси шлёт на `frontend:80`, а не на старый
|
||||
`remnawave-tg-shop:8000`.
|
||||
`migrate` должен завершиться успешно, а `backend`, `worker`, `frontend`,
|
||||
`postgres` и `redis` должны быть running/healthy.
|
||||
|
||||
Когда убедитесь, что новый stack работает, старые volumes можно удалить вручную:
|
||||
|
||||
```bash
|
||||
docker volume rm remnawave-tg-shop-db-data
|
||||
docker volume rm remnawave-tg-shop-caddy-data remnawave-tg-shop-caddy-config 2>/dev/null || true
|
||||
```
|
||||
|
||||
Reference in New Issue
Block a user