Merge branch 'dev' into patch-1

This commit is contained in:
BADtochka
2026-06-04 16:11:34 +03:00
committed by GitHub
172 changed files with 21250 additions and 3254 deletions
+44 -7
View File
@@ -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` | Включает напоминания о подписке. |
+8 -4
View File
@@ -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, но выпадающий список не загрузится.
## Практические замечания
+6
View File
@@ -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.
+3 -1
View File
@@ -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
View File
@@ -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
View File
@@ -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-меню в этом случае показывает только диапазоны по каждому тарифу.
+1 -1
View File
@@ -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).
## Авторизация
+50 -46
View File
@@ -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 на уровне окружения.
+1 -1
View File
@@ -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
+2
View File
@@ -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)
+39 -1
View File
@@ -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
+1
View File
@@ -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) |
+124
View File
@@ -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`.
+99 -331
View File
@@ -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.xv3.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
```