> ## Documentation Index
> Fetch the complete documentation index at: https://omniflow-vpn.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# Ваша первая продажа

> Превратите установленную платформу в работающий магазин — тариф, версия с ценой, платёжный провайдер, бот и тестовая покупка, которую можно проследить до самого Remnawave

[Быстрый старт](https://omniflow-vpn.mintlify.site/ru/getting-started/quickstart.md) оставляет вас с работающей установкой и учётной
записью оператора. Каталога в ней нет, поэтому обе клиентские поверхности показывают пустой список
тарифов и купить нельзя ничего. Эта страница закрывает пробел: один тариф, одна неизменяемая версия
с ценой, один платёжный провайдер, подключённый бот и настоящая покупка, прослеженная от нажатия
клиента до пользователя Remnawave, которого она создаёт.

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

> [!WARNING]
>
> Покупка сейчас не может завершиться расчётом на свежемигрированной базе данных. Расчёт вставляет
> задание на исполнение заказа в той же транзакции, что и деньги, а ни одна закоммиченная миграция
> не создаёт собственных таблиц очереди River, поэтому вставка падает и вся транзакция расчёта
> откатывается. Всё вплоть до передачи управления провайдеру включительно работает, и настроить это
> стоит; шаг, на котором всё останавливается, описан в разделе
> [Где сегодня останавливается тестовая покупка](#где-сегодня-останавливается-тестовая-покупка).

## Сначала определитесь с валютой расчётов

`APP_DEFAULT_CURRENCY` (по умолчанию `RUB`) — это валюта, в которой обе клиентские поверхности
сравнивают каталог; при старте она приводится к верхнему регистру и сверяется с `^[A-Z]{3}$`. Здесь
она важна из-за одного правила, на котором спотыкается каждая первая установка:

**Тариф без цены в валюте расчётов полностью исключается из клиентского каталога.** Он не
показывается как недоступный. Строка, с которой ничего нельзя сделать, — это лишь вопрос, на который
клиент не может ответить, поэтому она вообще не отрисовывается.

Если вы собираетесь продавать только через Telegram Stars, задайте `APP_DEFAULT_CURRENCY=XTR` и
назначайте версиям цену в `XTR`. Stars не рассчитываются ни в какой другой валюте.

## Создайте тариф

Тариф — это устойчивый продукт, обращённый к клиенту: код в нижнем регистре, тип, русский и
английский тексты, флаг видимости и порядок сортировки. Он не несёт ни цены, ни длительности — они
принадлежат его версиям, и именно это позволяет переименовывать и переупорядочивать продукт, не
трогая то, что кто-то уже купил.

Панель оператора не умеет создавать тариф. `/admin/catalog` публикует версии, переключает видимость
и сохраняет дополнения, но создание тарифа существует только в API с bearer-токеном — об этом
пробеле стоит знать до того, как вы пойдёте искать кнопку.

```bash theme={null}
curl -sS -X POST http://localhost:8080/v1/admin/plans \
  -H "Authorization: Bearer $APP_OPERATOR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "code": "pro",
    "kind": "one_time",
    "visible": true,
    "sortOrder": 10,
    "localizations": {
      "en": {"name": "Pro", "description": "100 GiB, three devices, one month."},
      "ru": {"name": "Pro", "description": "100 ГиБ, три устройства, один месяц."}
    },
    "version": {
      "billingPeriod": "month",
      "durationSeconds": 2592000,
      "gracePeriodSeconds": 259200,
      "trafficAllowanceBytes": 107374182400,
      "deviceLimit": 3,
      "squadIDs": ["00000000-0000-0000-0000-000000000000"],
      "upgradePolicy": "extend",
      "downgradePolicy": "at_expiry",
      "cancellationPolicy": "at_expiry",
      "recurringCapable": true,
      "trialEligibility": "new_customer",
      "prices": [{"currency": "RUB", "amountMinor": 49900}]
    }
  }'
```

```json theme={null}
{"id":"…","code":"pro","kind":"one_time","visible":true,"versionId":"…","version":1}
```

Один вызов создаёт тариф, обе локализации, первую версию и её цены в одной сериализуемой транзакции,
поэтому тариф никогда не оказывается видимым без текста и цены, которые делают его продаваемым.

| Поле | Каким оно должно быть |
| - | - |
| `code` | `^[a-z][a-z0-9_-]{1,63}$`, глобально уникальный |
| `kind` | `trial`, `one_time`, `recurring`, `free` или `manual`. Поведение где-либо меняет только `trial` — именно он включает заявку на пробный период и правила права на него. Остальные четыре — просто ярлыки |
| `localizations` | Только ключи `ru` и `en`; что-либо иное отвергается как `422 plan_rejected` с сообщением `unsupported locale`. Название — 1–120 символов, описание — до 2000 |
| `durationSeconds` | Больше нуля. 2 592 000 — это 30 дней |
| `gracePeriodSeconds` | Сколько доступ живёт после истечения, чтобы клиенту можно было сказать точно, сколько у него осталось на продление. Ноль отключает |
| `trafficAllowanceBytes`, `deviceLimit` | Опустите или пришлите `null` для безлимита. Null означает безлимит; ноль означал бы тариф, не разрешающий ничего, и эти два значения намеренно различны |
| `squadIDs` | UUID сквадов Remnawave. Этот набор копируется в право на услугу и отправляется в Remnawave при каждой операции |
| `prices` | Хотя бы одна пара `{currency, amountMinor}`. Суммы — целые числа в минорной единице валюты: 49900 — это 499.00 RUB. Дробных чисел в Omniflow нет нигде |
| `trialEligibility` | `new_customer` (по умолчанию), `never_trialled` или `any` |

Отказы, которых стоит ждать: неизвестное поле JSON — это `400 invalid_request`, потому что декодер
отвергает неизвестные поля, а не молча выбрасывает опечатку; отсутствующая длительность или пустой
список цен — `422 plan_version_rejected` с `duration and prices are required`; неверный bearer-токен
— `401 unauthorized`.

> [!NOTE]
>
> Повторный вызов с тем же `code` не завершится ошибкой. Вставка выполняет upsert по `code`, поэтому
> вы получите обратно существующий тариф — а `createVersion` затем добавит **версию 2**. Смените код
> или воспользуйтесь панелью, если публиковать ещё одну версию вы не собирались.

## Опубликуйте версию с ценой из панели

Любое последующее изменение цены — это новая версия. Маршрута обновления версии нет намеренно:
версия тарифа неизменяема, как только на неё ссылается заказ, а редактор, способный её изменить,
молча переоценил бы историю. Заказ дополнительно хранит JSON-снимок периода, длительности, трафика,
лимита устройств, сквадов и политик жизненного цикла версии на момент покупки, поэтому даже
подделанная строка версии не смогла бы изменить, сколько стоил существующий заказ.

Откройте `/admin/catalog` (право доступа `catalog.read`; для элементов управления нужно
`catalog.write`), найдите строку тарифа и нажмите **Новая версия**. Редактор раскроется под строкой,
предзаполненный из текущей версии, потому что изменение цены обычно и есть единственное отличие.

| Поле | Единицы и значения |
| - | - |
| Период оплаты | Ежедневно, Еженедельно, Ежемесячно, Ежеквартально, Ежегодно, Произвольно |
| Длительность (дней) | Отправляется как `durationSeconds` |
| Льготный период (дней) | Отправляется как `gracePeriodSeconds`. Ноль отключает |
| Трафик (ГБ) | Умножается на 1 073 741 824 и отправляется в байтах. Пусто означает безлимит, и подсказка так и говорит: ноль — это не то же самое |
| Лимит устройств | Целое число. Пусто означает безлимит |
| ID сквадов | Идентификаторы сквадов Remnawave через запятую |
| Выбор сквадов | Оставьте на **Назначаются автоматически** — см. предупреждение ниже |
| Повышение тарифа | Недоступно, Новый период с сегодня, Добавляется к текущему сроку |
| Понижение тарифа | Недоступно, Сразу, По окончании периода |
| Отмена | Сразу, По окончании периода |
| Пробный период | Оставьте на **Только новым клиентам** — см. предупреждение ниже |
| Доступно автосписание | Переключатель. Он сохраняется и отображается; автоматическое продление на деле зависит от собственной возможности платёжного адаптера |
| Цены | Свободный текст, `RUB=49900, USD=599`. Только целые минорные единицы — дробное число отвергается, потому что «4.99» неоднозначно относительно того, минорную единицу какой валюты оно означает |
| Причина | Обязательна в форме и отправляется как `X-Operator-Reason`; попадает в событие аудита |

Кнопка **Опубликовать версию** остаётся неактивной, пока длительность не станет больше нуля, пока не
разберётся хотя бы одна цена и пока не введена причина. Карточка называется «Опубликовать следующую
версию» и объясняет почему: версия тарифа неизменяема, как только на неё ссылается заказ, поэтому
здесь публикуется новая, а не изменяется текущая. Публикация берёт рекомендательную блокировку на
время выделения номера, поэтому два оператора не могут выпустить версию 4 дважды. Редактор версии
сам называет последствие: существующие заказы сохраняют версию, по которой были куплены.

> [!WARNING]
>
> Два выпадающих списка в этом редакторе отправляют значения, которые база данных отвергает.
> **Выбор сквадов** отправляет `automatic`, `customer_choice` или `fixed`, тогда как ограничение
> колонки допускает `automatic`, `optional` и `required`. **Пробный период** отправляет
> `new_customer`, `never` или `any_customer`, тогда как колонка допускает `new_customer`,
> `never_trialled` и `any`. Всё, кроме **Назначаются автоматически** и **Только новым клиентам**,
> проходит собственную валидацию панели и затем нарушает ограничение, что проявляется как
> `500 operation_failed`. Чтобы предложить выбор локаций клиентом или правило пробного периода,
> отличное от значения по умолчанию, вызывайте
> `POST /v1/panel/catalog/plans/{planID}/versions` напрямую со значениями, которые понимает база.

> [!NOTE]
>
> Версия, опубликованная из панели, не предлагает дополнений. Предлагаемый набор заменяется ровно
> теми `addonIds`, что пришли в запросе, а редактор панели это поле не отправляет. Чтобы его
> заполнить, нужен прямой вызов API — см.
> [Дополнения и конфигуратор](https://omniflow-vpn.mintlify.site/ru/commerce/addons-configurator.md).

Что происходит с версией дальше, определяют ещё два элемента управления, оба доступны только через
API: `POST /v1/panel/catalog/plans/{planID}/archive` прекращает новые покупки тарифа целиком и
принудительно ставит `visible = false`, а
`POST /v1/panel/catalog/plan-versions/{planVersionID}/retire` прекращает предложение конкретной
версии в каталоге. Ни то, ни другое не трогает существующее право на услугу: клиент сохраняет то,
что купил.

## Локализуйте его

Оба запроса клиентского каталога **соединяют** локализацию для запрошенной локали через `join`, а не
через left join. Тариф без текста в какой-то локали не отрисовывается пустым — он исчезает для
клиентов, пользующихся этим языком. Именно поэтому вызов создания выше отправляет и `ru`, и `en`.

Чтобы изменить текст потом:

```bash theme={null}
curl -sS -X PUT \
  http://localhost:8080/v1/panel/catalog/plans/<planID>/localizations/en \
  -H "Content-Type: application/json" \
  -d '{"name": "Pro", "description": "100 GiB, three devices, one month."}'
```

Этому маршруту нужна сессия панели с правом `catalog.write` и CSRF-токен, а не bearer-токен
оператора, и он отвечает `204`. Любая локаль, кроме `ru` или `en`, — это `422`. Редактора
локализаций в панели нет.

## Проверьте готовность тарифа к продаже

Прежде чем вовлекать платёжного провайдера, посмотрите на каталог так, как это делает экран клиента.
Публичный маршрут не требует учётных данных:

```bash theme={null}
curl -s "http://localhost:8080/v1/catalog/plans?locale=en"
```

Запись появляется, только когда выполняется **всё** перечисленное:

1. `plans.visible` истинно, а `archived_at` пуст.
2. Существует строка `plan_localizations` для запрошенной локали.
3. У тарифа есть хотя бы одна не выведенная из обращения версия — побеждает наибольшая из таких.
4. У этой версии есть хотя бы одна строка цены.

Клиентские поверхности добавляют ещё одно условие, которого нет у этого маршрута: цена должна быть в
валюте расчётов. Этот эндпоинт возвращает по одной записи на строку цены, поэтому тариф с ценами в
двух валютах появляется здесь дважды, а в боте — один раз.

Если список пуст, идите по этому списку проверок, а не гадайте. `/admin/catalog` показывает тариф,
номер его последней версии и то, сколько строк заказов уже на него ссылаются.

## Настройте платёжного провайдера

Платёжные адаптеры собираются из переменных окружения при старте. `manual` и `telegram_stars`
регистрируются **всегда**; два других появляются, только когда есть их учётные данные, а пустые
учётные данные оставляют адаптер незарегистрированным, вместо того чтобы зарегистрировать сломанный.

| Адаптер | Чем включается | Валюты | Вебхуки | Опрос | Возвраты |
| - | - | - | - | - | - |
| `telegram_stars` | Всегда. Для возвратов дополнительно нужен `APP_TELEGRAM_TOKEN` | Только `XTR` | Нет — расчёт приходит в аутентифицированном потоке обновлений бота | Нет | Только полная сумма списания |
| `cryptobot` | `APP_CRYPTOBOT_TOKEN` | 16 фиатных кодов, включая `RUB`, `USD`, `EUR` | Да, HMAC-SHA256 по точным байтам | Да | Нет |
| `yookassa` | `APP_YOOKASSA_SHOP_ID` **и** `APP_YOOKASSA_SECRET` | `RUB`, `USD`, `EUR` | Да, без подписи — проверяется повторным запросом платежа | Да | Полные и частичные |
| `manual` | Всегда | Любые | Нет | Нет | Записывает возврат в статусе ожидания |

Для первой продажи самый быстрый работающий канал — обычно CryptoBot или Telegram Stars, потому что
ни одному из них не нужен публичный URL:

```env theme={null}
APP_CRYPTOBOT_TOKEN=<token from @CryptoBot>
APP_CRYPTOBOT_TESTNET=true
```

> [!WARNING]
>
> `APP_CRYPTOBOT_TESTNET` по умолчанию равна **`true`**. Счета создаются на тестовом хосте, пока вы
> не поставите `false`, — ровно то, что нужно для тестовой покупки, и ровно то, что нужно не забыть
> изменить, прежде чем принимать настоящие деньги.

YooKassa дополнительно требует `APP_PUBLIC_URL`, потому что размещённому у провайдера оформлению
нужна абсолютная ссылка возврата; бот отказывается стартовать, когда shop ID настроен, а публичного
URL нет. Направьте вебхук провайдера на `POST /v1/payments/webhooks/{provider}` на хосте API. Этот
маршрут неаутентифицирован по необходимости: подлинность обеспечивает проверка подписи адаптером по
точным полученным байтам, и именно поэтому прокси, перекодирующий тела JSON, ломает вебхуки всех
провайдеров.

> [!NOTE]
>
> На `/admin/settings` есть карточка **Платёжные провайдеры**, которая хранит флаг включённости,
> порядок отображения и запечатанные учётные данные по каждому мерчанту. Эти строки не настраивают
> ни один адаптер: реестр строится только из переменных окружения. Проверка соединения в этой
> карточке к тому же записывает значение статуса, которое ограничение самой таблицы отвергает.
> Настраивайте провайдеров в `.env` и перезапускайте, а всю картину читайте в
> [Платёжных провайдерах](https://omniflow-vpn.mintlify.site/ru/integrations/payments.md).

Поскольку `manual` регистрируется всегда, он предлагается каждому клиенту на обеих поверхностях, и
переключателя, чтобы его скрыть, нет. На установке, которая не принимает банковские переводы,
ожидайте увидеть его и объяснять его наличие.

## Подключите бота

```env theme={null}
APP_TELEGRAM_TOKEN=<token from @BotFather>
```

Перезапустите сервис `bot`. Без настроенного webhook-URL бот работает на длинных опросах, которым не
нужны ни входящий порт, ни TLS — по этой причине они и выбраны по умолчанию для первой установки.
При старте он публикует свой список команд в Telegram: девять команд для локали по умолчанию и
восемь для русской.

Бот отвечает только в личных чатах. Каждый экран несёт состояние подписки, а групповой чат — это
аудитория, поэтому `/start`, любая команда, любой callback и операторская панель `/ops` молча ничего
не возвращают, когда чат не является личным.

Отправьте `/start`. Вы должны получить главное меню; `/plans` должен показать только что
опубликованный тариф блоком с периодом, трафиком, устройствами и ценой и кнопкой с подписью
`Pro · 499 RUB`. Если список пуст, пустое состояние называет валюту, которую оно искало, — вернитесь
к разделу [Проверьте готовность тарифа к продаже](#проверьте-готовность-тарифа-к-продаже).

О режиме webhook, домене Mini App и группе операторов см. [Telegram](https://omniflow-vpn.mintlify.site/ru/integrations/telegram.md).

## Совершите тестовую покупку

Возьмите Telegram-аккаунт, который никогда не общался с этим ботом, чтобы увидеть то, что видит
клиент в первый раз. Первый посетитель может смотреть и покупать до того, как появится хоть один
пользователь Remnawave: канонический клиентский профиль Omniflow создаётся на `/start`, а
пользователь Remnawave создаётся выдачей доступа после оплаты заказа.

**1. Откройте тариф и выберите действие**

`/plans` → кнопка тарифа. Экран тарифа показывает описание, период, трафик, устройства, цену,
политики повышения, понижения и отмены словами и льготный период, когда он ненулевой. Политика
`forbid` убирает действие целиком, а не проваливается после нажатия клиента, поэтому кнопки,
которые вы видите, — ровно то, что бэкенд принял бы: `🛒 Buy` для клиента, у которого нет ничего
активного, `♻️ Extend this plan` для той же версии или кнопка перехода для повышения либо
понижения, которое разрешает политика.

**2. Выберите способ оплаты**

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

**3. Проверьте сводку**

Экран оформления заказа — это в точности та разбивка, из которой будет создан заказ: подытог,
скидка, дополнения, кошелёк и итого к оплате. Отсюда клиент может ввести промокод, включить или
выключить списание с кошелька, добавить предлагаемые дополнения, сохранить корзину на потом или
сменить способ оплаты.

**4. Оплатите**

`💳 Pay` создаёт заказ и сразу начинает оплату. Его охраняют два ключа идемпотентности:
собственный ключ оформления переиспользуется для каждой попытки создать заказ, поэтому двойное
нажатие сходится в один заказ, а ключ платежа привязан и к заказу, **и** к провайдеру, поэтому
клиент, бросивший один способ и вернувшийся с другим, получает второй платёж, а не сбивающее с
толку переиспользование первого. Действие подтверждения к тому же защищено от повторов и
ограничено шестью вызовами в минуту.

Для размещённого у провайдера способа экран статуса предлагает `💳 Open payment page`. Для
Telegram Stars он предлагает `⭐ Pay in Telegram`, что отправляет настоящий счёт Telegram; бот
одобряет предварительный вопрос об оплате только тогда, когда заказ принадлежит этому клиенту,
валюта совпадает, итог в точности равен непогашенной сумме, а заказ по-прежнему находится в
состоянии `pending` или `draft`.

Клиентский веб-кабинет — это тот же поток с теми же записями: `/account/store` → тариф →
`/account/checkout` → **Подтвердить**. Оформление, открытое на одной поверхности, — это то же самое
оформление, которое показывает другая.

## Где сегодня останавливается тестовая покупка

Когда провайдер сообщает об успехе, вебхук или обновление Stars доходит до
`RecordProviderPayment`, который открывает одну транзакцию и делает в ней всё сразу: записывает
событие платежа, переводит заказ в `paid`, тратит кредит кошелька через книгу проводок с двойной
записью, создаёт право на услугу, пишет операцию исполнения, вставляет outbox-событие `order.paid`,
начисляет реферальное вознаграждение и ставит в очередь задание исполнения.

Проблема — последняя вставка. Задание уходит в собственные таблицы River, ни одна закоммиченная
миграция их не создаёт, и ничто в стеке не запускает мигратор River, — поэтому вставка падает и вся
транзакция откатывается. Что вы наблюдаете:

* Деньги у провайдера.
* Заказ остаётся в `pending`. Ни права на услугу, ни движения по книге проводок, ни пользователя
  Remnawave.
* Вебхук записан на `/admin/system` → **Вебхуки** со статусом `failed`.
* `worker` тоже не работает: он завершается при старте с записью `start River`.

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

Всё, что предшествует расчёту, — настоящее и стоит проверки: каталог, право на покупку, расчёт цены,
промокод, арифметика кошелька, заказ и передача управления провайдеру работают и записываются.
Следите за этой страницей и за [Заданиями и очередями](https://omniflow-vpn.mintlify.site/ru/operations/jobs-and-queues.md) в ожидании
миграции, которая это закроет.

## Куда попадает завершённая продажа

Когда очередь появится, вот какой след оставит одна покупка, и стоит знать, какой экран отвечает на
какой вопрос.

| Вопрос | Где смотреть |
| - | - |
| Деньги пришли? | `/admin/finance` → Заказы с фильтром по состоянию. В детали заказа видны шесть сумм, каждое платёжное намерение с упорядоченной хронологией событий и каждый возврат |
| Обратный вызов провайдера пришёл и проверился? | `/admin/system` → Вебхуки: время получения, провайдер, значок подписи и статус. Сырое тело не возвращается никогда |
| Выдача доступа выполнена? | `/admin/system` → Неудачные задачи жёстко отфильтрованы по неудавшимся операциям исполнения — это очередь недоставленных. Пустая вкладка и есть хороший исход |
| Что получил клиент? | `/admin/customers/<id>` → Подписки: слот, имя пользователя в Remnawave, код тарифа, статус права на услугу, срок окончания и лимит трафика |
| Что видит клиент? | Экран подписки в боте или `/account` — статус, срок окончания, оставшиеся дни, трафик, устройства и инструкции по подключению |

Воркер забирает задание из очереди `critical` и отправляет желаемое состояние в Remnawave. Имя
пользователя в Remnawave выводится из UUID подписки (`omniflow_` плюс шестнадцать шестнадцатеричных
символов), и воркер ищет его перед тем, как что-либо создавать, поэтому повторный `create` после
потерянного ответа подхватывает существующего пользователя, а не создаёт второго. При успехе более
ранние права на услугу этой подписки помечаются вытесненными, и заказ переходит из `paid` в
`fulfilled`. Отказы провайдера повторяются с задержкой от пяти секунд, удваивающейся до потолка в
1280 секунд, а после трёх неудачных попыток оператору ставится в очередь уведомление — по одному на
операцию, сколько бы раз она ни повторялась.

Любое действие оператора над подпиской после этого идёт через тот же конвейер, а не пишется в
Remnawave напрямую, и поэтому те кнопки сообщают **поставлено в очередь**, а не «сделано»: нажатие в
панели, которое писало бы в Remnawave само, было бы тем самым изменением, которое сверка не смогла
бы объяснить.

## Дальнейшие шаги

> **[Каталог и промоакции](https://omniflow-vpn.mintlify.site/ru/commerce/catalog-promotions.md)**
>
> Пробные периоды, промокоды, персональные предложения и правила, решающие, по какой версии
> оценивается клиент.

> **[Платёжные провайдеры](https://omniflow-vpn.mintlify.site/ru/integrations/payments.md)**
>
> Настройка каждого провайдера, проверка вебхуков, возвраты и то, чего не умеет каждый адаптер.

> **[Исполнение заказов](https://omniflow-vpn.mintlify.site/ru/commerce/fulfillment.md)**
>
> Операции, ключи идемпотентности, повторы, обнаружение расхождений и сверка по расписанию.

> **[Заказы и платежи](https://omniflow-vpn.mintlify.site/ru/commerce/orders-payments.md)**
>
> Машина состояний, классификации платежей и что каждая из них означает для оператора.

> **[Telegram](https://omniflow-vpn.mintlify.site/ru/integrations/telegram.md)**
>
> Режим webhook, домен Mini App и группа операторских уведомлений.

> **[Операции администратора](https://omniflow-vpn.mintlify.site/ru/operations/admin-operations.md)**
>
> Регламенты для возвратов, зависших платежей и всего остального, что следует за продажей.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.