# Журнал изменений API

Все изменения публичного API: новые маршруты, поля, события вебхуков и коды ошибок. Новые записи добавляются сверху, даты указаны по UTC. Изменение, ломающее совместимость, отмечается словом **несовместимо** и объясняет, что должен сделать интегратор.

## 2026-09-13, срок инвойса и адреса возврата

- `expires_in` в `POST /v1/invoices` стал необязательным: без него инвойс ждёт платежа 3600 секунд (1 час).
- **Несовместимо** для запросов со сроком меньше 5 минут или больше 72 часов. Границы `expires_in` теперь от 300 до 259200 секунд — в `POST /v1/invoices` и в `POST /cabinet/v1/invoices`. Раньше были от 60 до 604800 (7 дней). Запрос вне границ получает `400 validation_failed`, в том числе повтор запроса, принятого раньше: форма проверяется до поиска инвойса. Если интеграция ставит срок больше 72 часов, уменьшите его. Уже созданные инвойсы сохраняют свой срок.
- Переопределения `return_url` и `cancel_url` в `POST /v1/invoices` допускаются и в пределах сайта проекта из заявки (`site_url`): origin должен совпасть с адресом проекта того же вида или с сайтом проекта. На `t.me` и `telegram.me` совпасть должно и имя бота или канала — первый сегмент пути, без учёта регистра; это же правило теперь действует и для адреса проекта того же вида. Раньше без адреса того же вида в настройках проекта переопределение отклонялось с `422 redirect_url_not_allowed`. Требования к форме адреса не изменились — см. [адреса возврата](https://stons.io/docs/checkout.md#адреса-возврата).
- **Проверьте интеграцию.** Пока инвойс можно оплатить, страница оплаты показывает кнопку «Вернуться в магазин», если у инвойса или проекта есть адрес отмены. Кнопка ведёт на `cancel_url` с подписью и `status=created` или `status=seen`: инвойс при этом открыт и ещё может быть оплачен. Если обработчик адреса отмены отменяет заказ, пусть сначала проверит статус через `GET /v1/invoices/{id}` — см. [переход до исхода](https://stons.io/docs/checkout.md#переход-до-исхода). Сама страница, пока инвойс открыт, никуда не переводит.
- `POST /cabinet/v1/invoices`: необязательные `return_url` и `cancel_url` по тем же правилам.
- Маршрут страницы оплаты `GET /checkout/v1/invoices/{id}`: новое поле `back_url` — подписанный адрес для кнопки «Вернуться в магазин».
- Соответствие параметров Heleket — в [странице оплаты](https://stons.io/docs/checkout.md#если-вы-переходите-с-heleket).

## 2026-09-13, процент и фиксированная сумма комиссий

- У комиссии сервиса инвойса появилась фиксированная сумма по активу. Новое поле `fees.service.fixed` — сумма, записанная в инвойс при создании; `fees.service.amount` — по-прежнему всё удержанное, теперь это процент и фиксированная сумма вместе. Поля только добавлены. То же — в данных событий `invoice.*`, в ответах API кабинета и страницы оплаты. См. [комиссии](https://stons.io/docs/invoices.md#комиссии).
- **Проверьте интеграцию.** Если комиссию сервиса платит покупатель, `amount_expected` = цена + процент + фиксированная сумма. Сравнивайте свою цену с `amount_requested`, как и раньше. Пока оператор фиксированную сумму не задал, она равна нулю, и ничего не меняется.
- Фиксированная сумма удерживается один раз на инвойс и не больше полученного: платёж меньше неё удерживается целиком. Пополнение `per_user` без инвойса — отдельная операция со своей фиксированной суммой.
- У комиссии выплаты появился процент от суммы: новые поля `fees.network.percent` и `fees.network.fixed`, а `fees.network.amount` — вся комиссия, фиксированная сумма плюс процент от `amount`, округлённый вниз. То же — в данных событий `payout.confirmed` и `payout.failed`. См. [комиссию сети](https://stons.io/docs/payouts.md#комиссия-сети).
- Административный API: маршруты `GET /admin/v1/invoice-fees` и `POST /admin/v1/invoice-fees`, коды `invoice_fee_confirmation_required` и `invoice_fee_valid_from_in_past` — административный API. **Несовместимо** для клиентов административного API: `POST /admin/v1/payout-fees` требует поле `percent` и принимает `confirm_high_rate`, а в ответе у действующей комиссии и у строк тарифа появилось поле `percent`. Консоль обновлена в том же изменении.

## 2026-09-13, комиссия сети

- **Несовместимо** для кода, который проверяет `fees.network.status` инвойса по перечню `quoted`, `pending`, `settled`. У инвойса нет отдельной комиссии сети: затраты сети на приём входят в комиссию сервиса, и `fees.network.status` теперь всегда `included`. Прежние значения больше не приходят. Ключи сохранены: `quote`, `quote_rate` и `actual` — всегда `null`, `payer` — всегда `merchant`. Если интеграция проверяет перечень строго, добавьте `included`. См. [комиссии](https://stons.io/docs/invoices.md#комиссии). То же — в событиях `invoice.*` и в ответах API кабинета и страницы оплаты.
- Коды `422 network_fee_payer_not_supported` и `503 network_fee_quote_unavailable` остаются: покупатель комиссию сети не несёт. Изменились только их сообщения.
- У выплаты новое поле `fees.network`: `payer` — всегда `merchant`, `amount` — фиксированная комиссия сети, записанная в выплату при создании, `charged` — сколько списано с баланса проекта. То же — в данных событий `payout.confirmed` и `payout.failed`. См. [комиссию сети](https://stons.io/docs/payouts.md#комиссия-сети).
- Комиссия сети списывается с баланса проекта при подтверждении выплаты, отдельной проводкой реестра; выплата, провалившаяся в цепи, её не несёт. Получатель по-прежнему получает ровно `amount`.
- Административный API: тариф комиссии сети за выплату — `GET /admin/v1/payout-fees` и `POST /admin/v1/payout-fees`, коды `payout_fee_confirmation_required` и `payout_fee_valid_from_in_past`; новый вид счёта реестра `network_cost_recovery` в `GET /admin/v1/balances` и в проводках — административный API.

## 2026-09-13, комиссия сервиса

- У инвойса новые поля: `amount_requested` — цена мерчанта, `amount_credited` — полученное за вычетом удержанных комиссий, `fees` — комиссия сервиса и комиссия сети. Поля только добавлены, прежние не меняют смысла: `amount_expected` — по-прежнему сумма, которую должен заплатить покупатель. См. [комиссии](https://stons.io/docs/invoices.md#комиссии).
- **Проверьте интеграцию.** Если в проекте комиссию сервиса платит покупатель, `amount_expected` больше цены на эту комиссию. Код, который сравнивает `amount_expected` со своей ценой заказа, должен сравнивать `amount_requested`. В проектах, где комиссию платит мерчант (так по умолчанию), `amount_expected` равен цене, как раньше.
- Идемпотентность `POST /v1/invoices` по `order_id` сравнивает `amount` запроса с ценой `amount_requested`: повтор присылает цену, а не сумму с комиссией.
- Комиссия сервиса удерживается с каждого подтверждённого платежа отдельной проводкой реестра и уменьшает баланс мерчанта. Ставка записывается в инвойс при создании: смена тарифа не меняет уже выставленные инвойсы.
- Те же поля — в данных событий `invoice.*` ([вебхуки](https://stons.io/docs/webhooks.md#тело)) и в ответе маршрута страницы оплаты; страница оплаты показывает разбивку «Цена · Комиссия сервиса · Итого», когда комиссию платит покупатель ([страница оплаты](https://stons.io/docs/checkout.md)).
- Новый код `503 network_fee_quote_unavailable`: проект настроен так, что комиссию сети платит покупатель, а посчитать её сейчас нельзя. Такую настройку API кабинета больше не принимает — `422 network_fee_payer_not_supported`.
- Административный API: тарифы комиссии сервиса — `GET /admin/v1/service-fees`, `POST /admin/v1/service-fees/platform`, `GET /admin/v1/merchants/{id}/service-fees`, `POST /admin/v1/merchants/{id}/service-fees`, коды `service_fee_confirmation_required` и `service_fee_valid_from_in_past` — административный API. `GET /admin/v1/fees` остался отчётом о комиссиях сети.

## 2026-09-13, проверка проектов и настройки проекта

- Проект, который бизнес заводит сам, принимает платежи только после проверки. Пока он не одобрен или если отклонён, `POST /v1/invoices` и `POST /v1/addresses` отвечают новым кодом `403 merchant_not_approved`. Ключи API при этом работают, чтение инвойсов тоже. Все мерчанты, созданные раньше, одобрены: для существующих интеграций ничего не меняется.
- Владелец может выключить приём в активе для своего проекта. Тогда `POST /v1/invoices` в этом активе отвечает новым кодом `422 asset_disabled_by_merchant`, а `POST /v1/addresses` не включает актив в список `assets`. Уже открытые инвойсы оплачиваются и закрываются, повтор принятого запроса возвращает инвойс.
- `403 merchant_disabled` теперь означает выключение и оператором, и владельцем проекта.
- Проект без адреса вебхуков событий не получает: они не ставятся в очередь и после появления адреса не досылаются ([вебхуки](https://stons.io/docs/webhooks.md#доставка)).
- Административный API: у мерчанта новые поля — `review_status`, `review_reason`, `reviewed_at`, `reviewed_by`, `site_url`, `description`, `network_fee_payer`, `service_fee_payer`, `disabled_by`. Появились фильтр `GET /admin/v1/merchants?review_status=`, маршруты `POST /admin/v1/merchants/{id}/approve` и `POST /admin/v1/merchants/{id}/reject` — административный API. **Несовместимо** для клиентов административного API: `webhook_url` мерчанта может быть `null`. Консоль обновлена в том же изменении.
- Маршруты `/cabinet/v1` для приложения кабинета: служебные, снаружи недоступны и в `GET /v1/openapi.json` не входят.

## 2026-09-13, переключатели приёма и выплат

- Оператор может временно выключить приём или выплаты в активе. Пока приём выключен, `POST /v1/invoices` в этом активе отвечает новым кодом `422 asset_payments_disabled`, а `POST /v1/addresses` не включает актив в список `assets`. Пока выключены выплаты, `POST /v1/payouts` отвечает новым кодом `422 asset_payouts_disabled`.
- Повторы запросов, принятых раньше, отвечают как прежде: инвойс по тому же `order_id` и выплата по тому же `reference` возвращаются.
- Уже открытые инвойсы оплачиваются и закрываются, уже созданные выплаты исполняются: переключатель касается только нового.
- Административный API: `PATCH /admin/v1/assets/{id}`, поля `payments_enabled` и `payouts_enabled` в объекте актива, новые коды `asset_not_found` и `asset_flag_locked` — административный API.

## 2026-09-13, административный API консоли

- Маршруты `/admin/v1` для консоли оператора: мерчанты и их ключи, инвойсы, остатки счетов реестра, движение средств, комиссии сети, каталог активов, доставки вебхуков и журнал аудита — административный API. Доступ по токену консоли `ADMIN_API_TOKEN` и заголовку оператора `X-Operator`; без токена маршрутов нет (ADR-0033).
- Адреса возврата мерчанта `return_url` и `cancel_url` видны в объекте мерчанта и меняются `PATCH /admin/v1/merchants/{id}` по правилам [страницы оплаты](https://stons.io/docs/checkout.md#адреса-возврата).
- Новые коды ошибок, только у этих маршрутов: `merchant_not_found`, `api_key_not_found`, `delivery_not_found`, `delivery_not_failed`.
- Маршруты `/v1` и описание `GET /v1/openapi.json` не изменились: административные маршруты в него не входят.

## 2026-09-13, страница оплаты и адреса возврата

- У инвойса новое поле `checkout_url` — ссылка на [страницу оплаты](https://stons.io/docs/checkout.md), если оператор её настроил, иначе `null`.
- Необязательные поля `return_url` и `cancel_url` в `POST /v1/invoices`: куда страница оплаты вернёт покупателя. Допускаются только в пределах origin адресов, заданных у мерчанта оператором; иначе новый код `422 redirect_url_not_allowed`. Те же поля появились в ответе инвойса.
- Повтор `POST /v1/invoices` с другими `return_url` или `cancel_url` — `409 order_id_conflict`, как повтор с другой суммой. Запросы без этих полей ведут себя как раньше.
- Возврат покупателя подписан: параметры `invoice_id`, `order_id`, `status`, `ts`, `signature`, подпись HMAC-SHA256 секретом вебхуков. Формат и код проверки — в [странице оплаты](https://stons.io/docs/checkout.md#подпись). Решение — ADR-0032.
- `GET /checkout/v1/invoices/{id}` — служебный маршрут сервера страницы оплаты, без ключа API, в приватной сети. Мерчантам он не нужен.

## 2026-09-12, USDC в сети TRON

- Новый актив `USDC_TRON`: инвойсы и выплаты в USDC принимаются наравне с USDT, 6 знаков после запятой, 20 подтверждений. Контракт `TEkxiTehnzSmSe2XqrBj4w32RUN966rdz8` — прочитан в сети, а не взят из статьи: `symbol() = USDC`, `name() = USD Coin`, `decimals() = 6`. Обоснование и граница решения — в ADR-0026.
- USDC на адресе приёма больше **не** считается неожиданным активом. Для TRON `unexpected_asset` теперь означает токен, которого нет в каталоге.
- TRX платёжным активом пока **не** стал: в режиме сжигания энергии (`SWEEP_ENERGY_MODE=burn`) комиссию за свип платит сам адрес приёма, поэтому TRX на него кладёт оператор — и с включённым флагом эти операционные деньги были бы зачислены как чей-то платёж. Сначала правило, отличающее переводы с наших адресов, и тест; потом флаг. Выплаты в TRX не появятся и после: суточные лимиты складывают суммы в одной единице, что верно только для привязанных к доллару активов.
- Активы тестовых сетей не изменились: проверяемого контракта USDC в Nile и Shasta нет.

## 2026-09-12, домен в установщике

- `POST /setup/domain` — панель находит у провайдера DNS зону, которой принадлежит указанное имя, создаёт или исправляет запись A на адрес этого сервера и возвращает то, что видно в публичном DNS прямо сейчас. Проверяется **не своя запись, а ответ сети**: записанное и видимое — разные утверждения, и работает только второе.
- Если зоны у провайдера нет, панель **создаёт её сама** и показывает NS-серверы, которые нужно прописать у регистратора домена: без делегирования зону не видит никто, какими бы правильными ни были записи в ней. Зона создаётся под регистрируемым именем — последние две метки.
- Ответ маршрута говорит, видит ли провайдер делегирование (`delegated`), создавалась ли зона (`zone_created`) и какие NS-серверы нужны (`nameservers`).
- Шаг «Домен» стоит первым и необязателен: без домена сервис доступен по адресу сервера. Переменные `SETUP_DOMAIN` и `BUNNY_API_KEY` пишет сама панель; ключ провайдера используется только для записи и проверки и обратно не показывается.
- Панель первого запуска теперь открывается в браузере по адресу сервера — порт API публикуется на время установки одной переменной и возвращается на loopback после неё (ADR-0025). Маршруты `/v1` не изменились.

## 2026-09-12, установщик записывает конфигурацию сам

- Страница первого запуска больше не печатает блок переменных для вставки руками: она **записывает** файл окружения на сервере — `POST /setup/env`. Значения проверяются теми же разборщиками, которыми сервис читает конфигурацию при старте, поэтому записанный файл — это файл, с которым сервис запускается. Маршруты `/v1` не изменились.
- `POST /setup/signer-values` и `GET /setup/signer-env` — выбор владельца для сервера подписи и **готовый файл окружения** для него вместе с командами по порядку. Пароль роли базы и два значения сида в него не попадают: первый уже есть на этом сервере, вторые создаются на том.
- Шаги «Сеть и узлы» и «Файл окружения» — новые и обязательные. Второй закрывается только когда значения не просто записаны, но и применены: переменные читаются один раз, при старте.
- Секрет, принятый страницей, обратно не отдаётся: в ответе `GET /setup/state` у него только признак «задано». Пустое поле означает «не менять».
- Поле, в которое вставили 12 или 24 коротких слова, отвергается: мнемоника не вводится на этой странице ни в одно поле. Обоснование и границы — в ADR-0024.

## 2026-09-12, страница первого запуска

- Пока настройка сервиса не завершена, API отдаёт страницу `GET /setup`, которая ведёт по шагам и проверяет каждый. Когда все обязательные шаги закрыты, её маршруты отвечают `404`. Мерчантов это не касается: маршруты `/v1` не изменились.
- Служебные маршруты страницы: `GET /setup/state`, `POST /setup/verify`, `POST /setup/steps/{key}`, `POST /setup/merchant`. Доступны только из приватной сети и только с одноразовым кодом из лога сервиса. См. первый запуск.
- Страница не принимает и не создаёт мнемонику, парольную фразу и приватную половину операторского ключа — обоснование в ADR-0024.

## 2026-09-12, отправка выплат

- События `payout.confirmed` и `payout.failed`: выплата подтверждена сетью или её транзакция попала в блок неуспешной. Поля — в [вебхуках](https://stons.io/docs/webhooks.md#события). Как и раньше, тело вебхука — сигнал «сходи проверь»: состояние выплаты берите из `GET /v1/payouts/{id}`.
- Выплата в `failed` не повторяется автоматически: новая попытка требует нового `reference`.
- Остальное в этом обновлении внутреннее: подпись выплат с горячего кошелька, вторая независимая проверка вайтлиста вместе с подписью оператора, предохранитель, останавливающий выплаты сам. Публичные маршруты не изменились.

## 2026-09-12, выплаты

- `POST /v1/payouts` — создание выплаты, идемпотентное по `reference`. Ответ `202` у созданной выплаты, `200` у повтора. См. [выплаты](https://stons.io/docs/payouts.md).
- `GET /v1/payouts/{id}` — текущее состояние выплаты с её статусом и причинами ожидания.
- Выплата возможна только на адрес из вайтлиста мерчанта и только после задержки перед первым использованием. Переход лимита выплату не отклоняет: она принимается со статусом `pending_review` и ждёт подтверждения человека, а `review_reasons` называет перейденные пределы. См. [лимиты](https://stons.io/docs/payouts.md#лимиты).
- Новые коды ошибок: `payout_not_found`, `reference_conflict`, `asset_not_payable`, `address_not_whitelisted`, `payouts_halted`. См. [коды ошибок](https://stons.io/docs/errors.md).
- Отправка выплат в сеть и события вебхуков `payout.confirmed` и `payout.failed` появятся в следующем обновлении: пока выплата доходит до статуса `queued` и ждёт исполнения.

## 2026-09-12, свипы

- Инвойс доходит до статуса `settled`: средства по нему перенесены в холодное хранилище. Для мерчанта это то же, что `confirmed`, вебхука у перехода нет — товар отдаётся на `confirmed`, как и раньше. См. [статусы](https://stons.io/docs/invoices.md#статусы).
- Остальное в этом обновлении внутреннее: подпись и отправка свипов, подтверждение их в цепи и проводки в реестре. Публичные маршруты и события вебхуков не изменились.

## 2026-09-11, операционные маршруты

- `GET /admin/health` — состояние сервиса для оператора и мониторинга: отставание сканера в блоках и глубина очереди вебхуков. См. операционное состояние.
- `GET /metrics` — те же показатели в текстовом формате Prometheus.
- Оба маршрута служебные: аутентификации не требуют, данных мерчантов не содержат и доступны только в приватной сети.

## 2026-09-11, доставка вебхуков

- События из очереди уходят на URL мерчанта с подписью `X-Signature` и заголовком `X-Timestamp`, с повторами по расписанию из [описания вебхуков](https://stons.io/docs/webhooks.md#доставка). До этого события только записывались.
- Заголовок `User-Agent` исходящих вебхуков содержит название продукта.
- Ответ `3xx` не считается успешной доставкой и не выполняется.

## 2026-09-11, сканер платежей

- Инвойсы меняют статус по переводам в сети: `seen`, `confirmed`, `underpaid`, `expired`, `paid_late`. См. [статусы](https://stons.io/docs/invoices.md#статусы).
- В ответе `GET /v1/invoices/{id}` поле `deposits` заполняется платежами по инвойсу.
- Сервис формирует события вебхуков `invoice.seen`, `invoice.confirmed`, `invoice.overpaid`, `invoice.underpaid`, `invoice.expired`, `invoice.paid_late`, `deposit.confirmed` и `deposit.orphaned`. См. [события](https://stons.io/docs/webhooks.md#события). Отправка событий на URL мерчанта включится в следующем обновлении.

## 2026-09-11, адреса и инвойсы

- Аутентификация по заголовку `X-Api-Key` и лимиты запросов по ключу и по IP-адресу — см. [аутентификацию](https://stons.io/docs/authentication.md).
- `POST /v1/addresses` — постоянный адрес пользователя для мерчантов в режиме `per_user`. См. [адреса](https://stons.io/docs/addresses.md).
- `POST /v1/invoices` — создание инвойса, идемпотентное по `order_id`. См. [инвойсы](https://stons.io/docs/invoices.md).
- `GET /v1/invoices/{id}` — текущее состояние инвойса с его платежами.
- `GET /v1/openapi.json` — машиночитаемое описание API в формате OpenAPI 3.1.
- Новые коды ошибок: `validation_failed`, `unauthorized`, `merchant_disabled`, `invoice_not_found`, `address_mode_mismatch`, `order_id_conflict`, `asset_not_supported`, `amount_below_minimum`, `rate_limited`, `addresses_exhausted`. См. [коды ошибок](https://stons.io/docs/errors.md).

## 2026-09-11, служебные маршруты

- Добавлены служебные маршруты `GET /healthz` и `GET /readyz` — см. мониторинг.
- Введён единый формат ошибок `{ "error": { "code", "message" } }` и коды `bad_request`, `route_not_found`, `internal_error`, `service_unavailable` — см. [коды ошибок](https://stons.io/docs/errors.md).
- Каждый ответ содержит заголовок `X-Request-Id` для поиска запроса в логах.
