S
docs.syncra.money
API ReferenceMerchant API

История изменений (Changelog)

2026-09-09

[FIX] V1-адаптер приведён к легаси-спеке (итоги аудита V1-контура)

Compat-адаптер /v1/* сведён с легаси-спекой (swagger_formatted.json): все расхождения контракта закрыты. Изменения касаются только V1-контура, V2 API не затронут.

  • Крипто-вывод (POST /v1/merchant/withdraw): обязательное поле спеки — wallet (алиас address принят; при заполнении обоих выигрывает wallet). Адрес валидируется по формату mainnet Base58Check TRON (T + 33 base58-символа), иначе 400 INVALID_ARGUMENT. Ручка идемпотентна по requestId: повтор с тем же requestId возвращает существующий вывод без второго резерва баланса. Ответ create/get/list — единый профиль из 19 полей (id, withdrawId, userId, status, requestId, blockchain, currency, amount, wallet, comment, isBalanceCommission, exchangeRate, sentAmount, commissionAmount, transactionHash, callbackUrl, createdAt, lastModifiedAt, sentAt).
  • Статистика (POST /v1/payments/incoming/statistics, POST /v1/payments/outgoing/statistics): ответ — {"statistics": [...]} с массивом записей по каждому методу (14 полей для incoming, 12 для outgoing), а не {successCount, totalAmount}. Тело запроса — необязательные startDate/endDate (RFC3339).
  • Диспуты: create/get — полный профиль из 17 полей (receiptType, receiptUrl, description, clientComment, merchantComment, adminComment, finalizedAt, lastModifiedAt и другие); list — сокращённый из 11. Create читает paymentId (обязателен), receiptType, receiptUrl, description, clientComment, merchantComment и возвращает их эхом.
  • Банки (POST /v1/banks/list): элемент списка — BankProfile: id, country, bankName, methodNames, currencies (вместо displayName/supportedMethods).
  • Вебхук PayOut (критично): PROCESSING → код 40, FAILED/EXPIRED → код 30 — ранее оба состояния приходили кодом 1 (WaitingProcessing), и мерчант не узнавал об ошибке выплаты. Теперь отказ доставляется вебхуком: 40 обрабатывайте как промежуточный, 30 — как терминальный отказ.
  • GET /v1/payments/incoming/{id}: сделка в REFUND_PENDING возвращает статус 32 (RollingBack); вебхук на той же стадии шлёт 16 (Returned). Расхождение контуров осознанное и закреплено тестом. Уточнение к записи от 2026-09-08: «32 фактически не выдаётся» устарело — код выдаётся GET-контуром.
  • Изоляция: GET-ручки (/payments/incoming|outgoing/{id}, /merchant/withdraw/{id}, /disputs/{id}) на сущность чужого мерчанта отвечают 404 NOT_FOUND — факт существования не раскрывается.
  • Валюта: неизвестный числовой код в create PayIn/PayOut отвечает 400 INVALID_ARGUMENT (unknown currency code: N); тихая подстановка RUB отключена.
  • List-фильтры: ручки withdraw/list, payments/incoming/list, payments/outgoing/list, disputs/list принимают body-фильтр: status (числовой V1-код), skip, take, startDate, endDate; incoming/list дополнительно — id (UUID).

Инструкция по миграции: интеграциям, написанным по легаси-спеке, действий не требуется — адаптер вернулся к исходному контракту. Проверьте парсинг полей статистики/диспутов/банков по спискам выше и заведите ветки обработки для вебхука PayOut с кодами 40 и 30.

[DOC] Контракты адаптера в Migration guide, вебхук-контур в реестре кодов

  • Migration guide дополнен разделом «Контракты V1-адаптера»: поля withdraw (wallet + алиас address), идемпотентность по requestId, полные формы ответов (вывод, статистика, диспуты, банки), list-фильтры, правила изоляции и валюты.
  • Реестр кодов V1 явно покрывает вебхук-контур: исходящие 40/30 в вебхуке PayOut; входящий 32 (GET) против 16 (вебхук) на стадии REFUND_PENDING.

2026-09-08

[DOC] Полный реестр числовых кодов V1 (закрывает вопрос интегратора по статусу 50)

Интегратор V1-контура получил от платформы статус 50 (PayingRightNow) по выплате и не нашёл его в своей копии документации. Проверили: сводного реестра числовых кодов на сайте не было — коды жили только справочной колонкой в таблицах V2-статусов, часть значений не показывалась числами вовсе.

  • Страница Statuses дополнена секцией «Полный реестр числовых кодов V1»: исходящие (1/40/50 → 10/20/30), входящие (1/31/2/32/21 → 11/12/13/14/15/16, 1000), крипто-выводы (1/2/3/4) — с пометками финальности и перечнем внутренних V2-статусов, которые в каждый код транслируются.
  • Колонки «V1 compat» в таблицах PayIn/PayOut заполнены полностью (прочерки заменены фактическими кодами: MATCHED/UNASSIGNED → 1, EXPIRED-выплата → 30, ESCROW_LOCKED → 1, PAYMENT_VERIFIED → 11, SOFT_DISPUTED → 21).
  • Справка по 50: PayingRightNow — трейдер осуществляет перевод, промежуточный; исход — 10 (успех) или 30 (отказ); sentAt/sentAmount заполняются в финальном статусе.

Инструкция по миграции: не требуется. V1-интеграторам — завести у себя весь реестр, чтобы операции не зависали на незнакомых кодах.

[DOC] Уточнения по кодам 20 / 16 / 32 (ответы на вопросы интегратора)

  • 20 PaidPartially (исходящие) — легаси-код: текущим движком НЕ выдаётся. В V2 у выплат нет частичных статусов; финалы — только 10 или 30. Колонка FAILED в V2-таблице исправлена (было «30/20», стало «30»).
  • 16 Returned (входящие) — отдаётся вебхук-контуром на стадии REFUND_PENDING (инициация возврата). Не безусловный финал: после возврата сделка может вернуться в рабочий цикл.
  • 32 RollingBack — в текущем движке доменный статус не эмитируется, фактически не выдаётся.
  • Подтверждение: по исходящим коды 2/13/14/16/21 не приходят ни в каком случае (их нет в payout-маппере).

2026-09-08

[FIX] Контракт вебхуков и терминальные исходы — закрытие живого прогона интеграции

Три изменения по итогам полного партнёрского прогона на стейдже:

  • Все int64 в телах вебхуков теперь передаются строками — тот же канон, что и в REST-ответах (proto3 JSON): "amount": "330000", "amount_version": "2". Ранее amount шёл голым JSON-числом — парсер, написанный по образцу REST, мог молча промахиваться. Обратите внимание при десериализации: если ваш приёмник ждал число — обновите тип на строку (или гибкий parse). V1-конверт не затронут (легаси-формат).
  • Повторный notify-payment на сделке в PAYMENT_NOTIFIED отвечает 400 FAILED_PRECONDITION (ранее 500): состояние сделки корректно, ретраить не нужно (см. [Retry-матрицу](./errors#retry-матрица-когда- повторять-запрос-безопасно)).
  • Резолв апелляции REJECTED доводит сделку до терминала: возврат эскроу (order-level и все per-match холды) и переход в CANCELLED с финальным вебхуком deal.cancelled (ранее сделка оставалась в промежуточном возврате неограниченно долго). SATISFIED, как и раньше, завершает живую сделку в COMPLETED.

Инструкция по миграции: для вебхук-приёмников — проверить парсинг сумм (строки); ретраи notify завершать по 4xx; терминальные статусы сделок после резолва обрабатывать как раньше (события аддитивны).


2026-09-04

[FEATURE] Баланс: прозрачность «средств в пути» (pending_credits)

Каждый кошелёк в GET /api/v1/p2p/merchant/balance теперь всегда несёт поле pending_credits — сумму нетто-зачислений завершённых сделок, ожидающих фонового распределения комиссий. Раньше между COMPLETED сделки и появлением нетто в available существовало «окно невидимости»: деньги уже заработаны, но ни в одной цифре баланса их не было. Канон — Stripe available/pending: обе цифры отдаются всегда, включая нули. Поле additive; кабинет отображает его как «В пути». Подробности и жизненный цикл — Балансы → Средства в пути.

Инструкция по миграции: действий не требуется — новое поле просто появилось в ответе; обрабатывайте по «игнорируй неизвестное», если не нужно.

[FIX] V1-конверт: инфраструктурные сбои больше не маскируются под 401/404/400

Внутренний рефакторинг p2p-engine (доступ к данным за одной дверью) закрыл класс мисклассификации в Legacy V1-конверте: при сбое инфраструктуры (недоступность БД и т.п.) endpoint'ы аутентификации и чтения раньше отвечали клиентским кодом ошибки — как будто мерчант не найден или запрос неверен. Теперь границы строгие:

  • 401 UNAUTHORIZED "merchant not found" — только когда мерчант действительно не существует/не аутентифицирован. Инфра-сбой → 500 INTERNAL_ERROR (лог + метрика на нашей стороне).
  • 404 NOT_FOUND "merchant not found" / "withdrawal not found" — только реальное отсутствие ресурса. Инфра-сбой → 500 INTERNAL_ERROR.
  • 400 INVALID_ARGUMENT (отсутствующий метод оплаты / незарегистрированный slug) — только ошибки запроса. Инфра-сбой → 500 INTERNAL_ERROR.

Инструкция по миграции: соответствует уже задокументированной Retry-матрице: 4xx — не ретраить, 500 INTERNAL_ERROR — ретраить с backoff. Если ваша V1-интеграция трактует 401/404 как терминальные — поведение корректно и раньше; изменилось только то, что транзитные сбои теперь видны как 500 и подлежат ретраю, а не выглядят как «мерчант исчез». Форматы тел ошибок и все остальные коды не менялись. V2 API не затронут. Вебхуки (типы событий, payload'ы, доставка) не менялись.


2026-09-03

[FEATURE] Изменение суммы сделки (amend) + зачёт просроченного платежа (settle-as-received)

Закрыт фундаментальный раскол кошельков: изменение суммы в кабинете (POST /deals/{id}/amount) раньше меняло только цифры в БД — эскроу-холды оставались на старую сумму, комплит подтверждал чужой остаток, книги расходились. Теперь:

  • Amend (живая сделка): разрешён ТОЛЬКО до PAYMENT_VERIFIED (статусы INITIALIZED / ESCROW_LOCKED / PAYMENT_NOTIFIED / SOFT_DISPUTED — канон Stripe: amount меняем до авторизации). Атомарно в одной транзакции: новые цифры (USDT — по курсу самой сделки), пересоздание ВСЕХ холдов (order-level + per-match, ключи от (deal_id, amount_version)), append-only аудит-строка p2p_deal_amount_changes (причина, оператор, ключ), событие p2p.deal.amount_changed в outbox.
  • Оптимистичная блокировка: поле сделки amount_version (стартует с 1); запрос amend принимает expected_amount_version (Stripe If-Match): два оператора, редактирующие одновременно — второй получает 409 и перечитывает. Идемпотентность оператора: idempotency_key (UNIQUE в аудите) — повтор = replay, другое тело = 409.
  • Новое событие вебхука deal.amount_changed (V2-only!): old_amount, новая amount, amount_version. Legacy V1-конверт не имеет callbackType под него — V1-мерчантам событие не доставляется (контрактно; как и события каталога). Прочие события не менялись.
  • Settle-as-received (EXPIRED + received_amount>0): истёкшая сделка никогда не оживает; по резолву апелляции оператор создаёт НОВУЮ сделку-сеттлмент POST /api/v1/p2p/appeals/{appeal_id}/settlement — сумма = received_amount оригинала (или обоснованный override), курс — новый на момент резолва, полный стандартный путь (эскроу → verify → complete), связи parent_deal_id / origin_appeal_id (UNIQUE: одна апелляция → максимум один сеттлмент, повтор — replay). Оригинал остаётся EXPIRED навсегда. По сеттлменту приходит обычный набор вебхуков deal.createddeal.completed (обе версии API).
  • Кабинет: модалка amend показывает пересчитанный USDT до подтверждения, причина обязательна; в карточке EXPIRED-сделки с полученным платежом — панель «зачесть по факту» (сумма из данных; ручное переопределение — второй шаг подтверждения + причина); листинги помечают сеттлменты и их родителей.

Дока: PayIn → amend и settle-as-received, Disputes → сеттлмент, Callbacks → deal.amount_changed, Статусы → поля суммы.

Инструкция по миграции: действий мерчанта не требуется. Новые поля (amount_version, parent_deal_id, origin_appeal_id) — additive; событие deal.amount_changed обрабатывайте по best-practice «игнорируй неизвестное», если изменение суммы вам не значимо. V1-мерчанты новых событий не получают. Миграции БД: 000017, 000018 (bridge-инкременты, идут с релизом p2p-engine; для контуров с уже применённой 000005-сквошей — идемпотентны).


2026-09-01

[FIX] Возврат к канону конверта списков: data (откат регресса доки от 08-29)

Регресс документации: ночная правка 2026-08-29 переставила примеры листингов на внутренние прото-имена полей ({"deals": [...]}, {"payment_methods": [...]}, total_count) — по ошибке: за источник истины был взят proto, а не фактический HTTP-контракт. Фактическое поведение платформы не менялось с 2026-08-24 (см. запись 2026-08-24: канонический конверт data): egress-маршалер шлюза отдаёт ВСЕ листинги merchant-контура как {"data": [...], "next_page_token": ""} и не выносит на HTTP ничего сверх этого (total_count существует только во внутреннем gRPC-контракте).

  • Исправлены все страницы и примеры (index, deals, callbacks, banks, wallet, disputes, turnover) и openapi.yaml (схемы и примеры листингов).
  • Канон подтверждён живыми запросами на стейдже по каждому листингу: /deals, /callbacks, /appeals, /passes, /banks, /payment-methods, /currencies, /wallet/deposits, /wallet/withdrawals, /deals/turnover — все отвечают {"data": [...], "next_page_token": ...}.
  • Напоминание канона: единичный ресурс — напрямую ({"deal": {...}}); списки — всегда data; GET /balance — исключение (не листинг): {"wallets": [...]}.
  • Сопутствующая находка (ответ интеграторам): в живых каталогах тенантов мобильного метода (mobcom_rub) сейчас нет — ни на стейдже, ни на проде; актуальный набор методов мерчанта возвращает GET /payment-methods.

Инструкция по миграции: не требуется — платформа всегда отвечала data-конвертом; правилась только документация. Если ваш код уже читает data — всё верно. Если ориентировались на вчерашние (29.08) примеры с deals/total_count — замените чтение поля на data и уберите зависимость от total_count.

[DOC] Канон домена платёжной страницы — checkout.syncra.money

  • Канонический вид payment_url: https://checkout.syncra.money/pay/{hash} (это же — компилируемый дефолт платформы в резолвере DB checkout_base_url → env CHECKOUT_BASE_URL → default).
  • Из документации вычищен второй домен (pay.syncra.money/{hash} из Hosted Checkout/Quickstart и openapi.yaml) — оба источника теперь указывают один канон.

Инструкция по миграции: не требуется — значение payment_url берите из ответа создания сделки как есть, не конструируйте домен самостоятельно.

2026-08-29

[FIX] Ночная сверка №2: код догоняет доку (вебхуки, rate-limit-заголовки, идемпотентность per-merchant)

Аудит «дока ↔ proto ↔ код» второй волны. Здесь, в отличие от 2026-08-28, правился и код платформы (ветка feature/docs-consistency-audit), потому что дока обещала поведение, которого в коде не было. Обратная совместимость сохранена: ничего не удалялось и не сужалось — только аддитивные события, заголовки и защита изоляции.

[FEATURE] [callbacks] — Недостающие webhook-события реализованы в коде

  • deal.created для PayOut — теперь эмитится при создании выплаты (ранее — только у PayIn; status несёт MATCHED/UNASSIGNED на момент ответа создания). Повтор идемпотентного создания не шлёт второй вебхук.
  • deal.expired для PayOut — эмитится при EXPIRED-переходе выплаты (в т.ч. по SLA из UNASSIGNED); холд возвращён.
  • p2p.finance.withdrawal_failed (status: CANCELLED) — эмитится при отмене вывода оператором/до broadcast. Ранее событие было задокументировано, но не эмитилось.
  • Обработчики вебхуков обязаны игнорировать неизвестные типы событий (best practice добавлена в Callbacks) — с выхода этих событий мерчанты начнут получать новые event-типы.

[FIX] [ratelimit] — Заголовки X-RateLimit-* действительно выставляются

  • Дока обещала X-RateLimit-Limit / X-RateLimit-Remaining / X-RateLimit-Reset (Unix-секунды сброса UTC-минуты) на каждом ответе HMAC-контура — код их не ставил. Теперь ставит: и на успешных ответах, и на 429 (Stripe-канон). Это аддитивное изменение — клиенты, не читающие заголовки, не затронуты.

[SECURITY] [idempotency] — Ключ идемпотентности скоупится по мерчанту

  • PayOut: детерминистичный ключ выплаты теперь выводится из (tenant, merchant, idempotency_key). Ранее формула не включала мерчанта, а replay-поиск был глобальным — два мерчанта с одинаковой строкой ключа могли сойтись на одной выплате, и второй получал чужую сделку в ответе (утечка target_requisite). Теперь чужая строка под ключом → 409 (idempotency_key is already used by another merchant), replay — только своей сделки (легаси-нескоупленные ключи по-прежнему резолвятся для незавершённых ретраев).
  • PayIn: replay через idem-кэш и recovery-путь проверяют владельца найденной сделки (внутри тенанта); чужая сделка больше не возвращается.
  • Обещание Идемпотентности («одинаковые ключи у разных мерчантов не конфликтуют») теперь — фактическое поведение платформы.

[DOC] [callbacks] — Точная математика ретраев доставки

  • При max_retries=5 (умолчание) доставка пробуется 5 раз суммарно (исходная + 4 повтора, паузы 5s/30s/5m/15m); ступень 1h достижима при повышенном per-merchant max_retries. Прежний текст «до 5 повторов после исходной» и утверждение про «паузу ≤ 5 минут» были неверны — удалены.
  • Успех доставки = любой ответ < 400 (включая 3xx), а не только 2xx.
  • response_body в истории доставок обрезается до 2048 байт.

[DOC] [deals] — Пагинация и фильтры листинга по факту

  • page_size: умолчание 50 (не 20), максимум 250.
  • created_beforeвключительно (inclusive), как и created_after.
  • total_count считает фильтры deal_type/statuses, но не дата-фильтры — исторический контракт, теперь задокументирован явно.
  • Листинг апелляций (GET /appeals) — пагинирован (AIP-158, умолчание 20, максимум 100); прежняя пометка «параметры не применяются» устарела.

[DOC] [migration] — Реальные маршруты V1 compat-слоя

  • Таблица сигнатур V1 указывала несуществующие маршруты (/v1/merchant/payin|payout|dispute). Исправлено на фактические: POST /v1/payments/incoming, POST /v1/payments/outgoing, POST /v1/merchant/withdraw, POST /v1/disputs/create, POST /v1/banks/list.

[DOC] [misc] — Прочие сверки

  • GET /wallet/deposit-address: в ответе есть updated_at; 404-пример приведён к фактическому problem+json (detail — вложенный gRPC-JSON).
  • POST /api-keys помечен admin-only (ротация — dual-homed).
  • GET /my-merchant — dual-homed (JWT или HMAC); PATCH — только JWT.
  • Replay-окно подписи: 5 минут назад / 60 секунд вперёд (не «±300 с»); у V1 replay-защита есть (300 с / +60 с), а не «нет».
  • reason апелляции: обязательный минимум 10 символов (не «рекомендуется»); текст ошибки актуализирован.
  • CSV-экспорт: counterparty = client_id для PAYIN (пусто, если не передан), target_requisite для PAYOUT.
  • SDK-примеры §1: добавлен обязательный payment_method_id во все 4 языка (пример без него получал 400); quickstart — «минимально 5 полей».
  • openapi.yaml: суммы int64 в примерах — строками; wallet_id — UUID (TRC-адрес убран из примера баланса); kill-switch — bodyless toggle; статусы листинга принимают оба набора (OrderStatus | PayOutStatus); добавлены вебхуки p2p.finance.withdrawal_failed и payment_methods.updated (каталог-конверт), статус withdrawal_completed-примера — APPROVED; EmptyResponse — пустой объект; пер-endpoint page_size (deals 50/250, компакт-листинги 20/100).

Инструкция по миграции: не требуется. Новые события вебхуков — additive: добавьте в фильтр deal.created/deal.expired (PayOut) и p2p.finance.withdrawal_failed, если хотите на них реагировать; неизвестные event просто логируйте. Единственное наблюдаемое изменение ошибок — 409 при коллизии idempotency_key между мерчантами (ранее такой запрос мог вернуть чужую сделку — это был баг).

2026-08-28

[DOC] Ночная сверка контрактов: дока ↔ proto ↔ код (Stripe Grade)

Полный аудит публичной документации против фактического поведения платформы (proto platform.p2p.v1 + p2p-engine). Изменения API-контракта — нет: все правки приводят документацию, openapi.yaml и proto-комментарии к уже работающему коду. Ничего удаляться не должно — обратная совместимость сохранена полностью.

[DOC] [payout] — Унифицированная ссылка на метод: UUID и слаг на обеих ногах

  • POST /deals/payout payment_method принимает слаг ИЛИ UUID (ранее дока описывала только слаг) — оба значения резолвятся в один и тот же метод, как и в PayIn payment_method_id.
  • Задокументирован currency cross-check: метод чужой валюты (card_rub при currency: "TRY") → 400 INVALID_ARGUMENT «payment method currency does not match the deal currency».
  • Неизвестная ссылка → 400 со списком допустимых слагов (живой пример добавлен в Коды ошибок).

[DOC] [wallet] — Правда о выводе USDT: статусы, комиссии, идемпотентность

  • Синхронный ответ POST /wallet/withdraw: статус — COMPLETED (авто-проведение) или PENDING_MANUAL (очередь ручного одобрения), а не APPROVED/PENDING, как писала дока. Доменные статусы заявки (PENDING/APPROVED/CANCELLED) — отдельная поверхность (листинги и вебхуки); прежний WARNING «COMPLETED не используется» был неверен и удалён.
  • Добавлены поля ответа fee_usdt_cents / net_usdt_cents (USDT-центы).
  • idempotency_key вывода: поле контракта, но не требуется и не валидируется — идемпотентность гарантируется платформой (детерминированный серверный ключ по мерчант+адрес+сумма+комиссия).
  • Задокументированы WAVE-эндпоинты кошелька, отсутствовавшие на странице: GET /wallet/withdraw-fee (квота комиссии до заявки), GET /wallet/deposits (TRC-20 депозиты с custodial-статусами), GET /wallet/withdrawals (история выводов: очередь ручных одобрений, смёрженная с ledger-списаниями).

[DOC] [payin] — amount_match_status: ровно три значения

  • Значение underpaid не существует: канонический набор — full / partial (= поступило меньше) / overpaid, строго lowercase. Исправлено в Deals и proto-комментарии MerchantDealInfo (вместе с регенерацией pb.go).

[DOC] [passes] — Required-поля создания pass'а

  • Обязательное поле одно — amount. currency_id, requisite_type, payment_method_id, provider_id — опциональные фильтры (пусто = «любой»). payment_method_id на pass-ветке принимает только UUID (слаг не резолвится — в отличие от ног сделки). Прежняя версия страницы требовала currency_id + requisite_type — неверна.
  • 409 Conflict закреплён как единственный в API: конкурентный резерв pass'а (gRPC ABORTED, «pass was concurrently reserved»); errors.mdx раньше категорично заявлял «409 не используется».

[DOC] [general] — Kill-switch, export-путь, V1-маппинг, мелкие фиксы

  • Kill-switch (POST /p2p/merchants/{id}/kill-switch): это toggle без тела (никаких active/reason) и admin-only (не self-service мерчанта; кода ошибки MERCHANT_DISABLED не существует — фактически 403 merchant is disabled). То же про /disable//enable.
  • CSV-экспорт: канонический путь теперь /api/v1/p2p/merchant/deals/export; корневое зеркало /api/v1/p2p/deals/export продолжает работать и помечено deprecated (обратная совместимость сохранена).
  • V1-маппинг PayIn-кода 12 — это CANCELLEDSPAM_REJECTED тоже транслируется в 12); migration-guide исправлен (12 → SPAM_REJECTED было неверно).
  • migration-guide: конверт ответов V2 — proto-поля верхнего уровня (никакой обёртки {data}); payment_method_id — REQUIRED (не optional).
  • Слаги каталога канонизированы по всей доке: card_rub / sbp_rub / mobcom_rub (устаревшие card/sbp/card2card убраны из примеров).
  • balance.mdx: фантомные «типы балансов» MAIN/INSURANCE/DEPOSIT убраны — GET /balance возвращает только фактические кошельки; wallet_id — UUID кошелька, а не TRC-20 адрес.
  • api-reference: идемпотентность — поле idempotency_key в теле (заголовок Idempotency-Key в Merchant API не используется).
  • openapi.yaml: добавлены отсутствовавшие пути (/currencies, /deals/export, /deals/turnover, /callbacks/{id}/retry, /wallet/withdraw-fee, /wallet/deposits, /wallet/withdrawals, /my-merchant), исправлены схемы (withdraw в целых USDT, required-набор CreatePayInRequest, int64-строки в примерах), 402 заменён на канон 400 FAILED_PRECONDITION.

Инструкция по миграции: не требуется — контракты не менялись, менялась только документация. Если вы полагались на строку APPROVED в синхронном ответе вывода — проверьте обработчик: фактическое поведение платформы (COMPLETED/PENDING_MANUAL) не менялось с момента релиза WAVE 3.

2026-08-26

[FEATURE] PayIn: статус CANCELLED — явная отмена сделки игроком или мерчем

  • Новый терминальный статус CANCELLED для депозитных (PayIn) сделок: отличается от EXPIRED (таймаут) — теперь «игрок активно отменил» и «время вышло» различаются.
  • Разрешён переход из INITIALIZED, ESCROW_LOCKED, PAYMENT_NOTIFIED (до подтверждения платежа). Эскроу-холды снимаются автоматически, комиссия обнуляется, матчи → FAILED.
  • Новые эндпоинты:
    • POST /api/v1/p2p/merchant/deals/{deal_id}/cancel — отмена мерчем (HMAC), тело {"reason": "..."} опционально; повтор на уже-CANCELLED сделке → 200 идемпотентно; не-PayIn или нелегальный статус → 400.
    • POST /api/v1/p2p/checkout/{hash}/cancel — отмена игроком на hosted-checkout (по deal hash); терминальная сделка → 409.
  • Новое webhook-событие deal.cancelled (payload идентичен deal.expired, status: "CANCELLED"). Мерчи, фильтрующие по deal.completed, не затронуты.
  • V1-адаптер: CANCELLED → код 12 (Cancelled), фильтр листинга GET /deals?statuses= принимает CANCELLED.

Инструкция по миграции: ничего ломать не нужно — изменение additive. Рекомендуем добавить CANCELLED в списки неуспешных статусов и обработчик deal.cancelled, если хотите отличать отмены от таймаутов.

2026-08-24

[FIX] PayIn: payment_method_id обязателен

  • Ранее: без payment_method_id → 500 "payment method is required for order matching" + создавалась "мусорная" заявка со статусом EXPIRED.
  • Теперь: 400 INVALID_ARGUMENT с подсказкой GET /payment-methods. Заявка не создаётся вообще.
  • payment_method_id — REQUIRED поле. Получите UUID из каталога методов перед созданием PayIn.

Журнал изменений (Changelog)

В данном разделе публикуется официальный список изменений и обновлений Syncra Merchant API. Мы фиксируем все новые функции (features), исправления ошибок (bugfixes), объявления об устаревании (deprecations) и обратно-несовместимые изменения (breaking changes).


Формат записей

Каждая запись в changelog имеет стандартизированную структуру:

  • Заголовок: Дата релиза (в формате YYYY-MM-DD) и краткое резюме.
  • Тип изменения:
    • [FEATURE] — Добавление нового функционала или эндпоинта.
    • [FIX] — Исправление ошибок в логике работы API.
    • [DEPRECATION] — Предупреждение об устаревании функционала и планируемом отключении.
    • [BREAKING] — Обратно-несовместимые изменения, требующие обновления вашего кода.
  • Категория: Сфера влияния изменения (payin, payout, auth, callbacks, disputes, balance, general).
  • Описание: Детальное объяснение изменений и инструкции по обновлению (Migration Path).

Актуальная история версий

2026-08-23 — Волны 7/8: кабинетные эндпоинты и сверка доки с живым стейджем

Все примеры в документации приведены к фактическим ответам стейджа (api-stage.syncra.money, живые curl). Новые страницы: CSV-экспорт, Обороты, Профиль и лимиты.

[FEATURE] [general] — CSV-экспорт сделок

GET /api/v1/p2p/deals/export (путь вне merchant-префикса): UNION-выгрузка PayIn+PayOut, фильтры листинга, Accept: text/csv → сырой RFC 4180-документ (Content-Disposition: attachment; filename="deals-YYYYMMDD.csv"), без Accept — JSON-конверт csv/file_name/row_count/truncated. Cap 10 000 строк.

[FEATURE] [general] — Обороты сделок

GET /api/v1/p2p/merchant/deals/turnover?date_from&date_to (RFC3339, обязательные, окно ≤ 92 дней, deal_type опционален) → rows[] (date,deal_type,amount_minor,currency); исключены EXPIRED/SPAM_REJECTED/FAILED.

[FEATURE] [callbacks] — История доставок и ручной retry

GET /callbacks (keyset-пагинация page_token/next_page_token) — статусы PENDING/SUCCESS/RETRY/ERROR, response_code, retry_count, next_retry_at. POST /callbacks/{id}/retry: только ERROR/RETRY (иначе 400), cooldown 60 с (иначе 429), чужой — 403, неизвестный — 404.

[FEATURE] [general] — Лимиты мерчанта и kill_switch_reason

GET/PATCH /api/v1/p2p/my-merchant (кабинетный JWT): deal_min_amount/deal_max_amount/daily_deal_amount_limit (int64 копейки; отсутствует = не менять, 0 = снять, >0 = установить); нарушения при создании сделок: min/max → 400 INVALID_ARGUMENT, daily → 400 FAILED_PRECONDITION. GET my-merchant отдаёт kill_switch + kill_switch_reason (баннер кабинета).

[FIX] [general] — Сериализация int64 и формат ошибок

Зафиксировано в доках: значения int64 в JSON-ответах передаются строками ("amount": "100000"); ошибки — RFC 9457 problem+json c type: "about:blank", вложенными gRPC-деталями в detail и instance upstream-сервиса; канон «неверное состояние → 400» (не 409).

2026-08-22 — Неделя интеграции: приведение контрактов к фактическому поведению платформы

Волна фиксов 2026-08-21/22 (проверена живым E2E на стейдже). Все изменения ниже отражены в соответствующих разделах документации.

[BREAKING] [general]

Убрана обещанная обёртка data в успешных ответах. grpc-gateway отдаёт proto-поля верхнего уровня (создание и получение PayIn/PayOut, листинг, баланс, пропуска, апелляции):

{"deal": {"deal_id": "..."}}
{"deals": [], "next_page_token": "", "total_count": 0}
{"wallets": []}

Схемы и примеры openapi.yaml исправлены соответственно.

Инструкция по миграции:

- const { payment_url } = response.data
+ const { payment_url } = response.deal

[BREAKING] [general]

Формат всех ошибок — RFC 9457 problem+json. Edge-gateway переписывает все non-2xx ответы в application/problem+json ({"type","title","status","detail","instance"}); старый конверт {"error":{"code","message","details"}} больше не возвращается. gRPC-/ doc-коды (INVALID_ARGUMENT, UNAUTHENTICATED, ...) остаются семантикой (определяют HTTP-статус и текст detail), но полем code в теле не передаются. См. Коды ошибок.

[FIX] [general]

Новые HTTP-коды вместо «зонтичного» 500:

  • 404 Not Found — сделка не найдена в GET /deals/{deal_id} (ранее 500).
  • 503 Service Unavailable — «no matching trader available, retry later» при исчерпании каскада (ранее 500); retry с backoff.

[FIX] [auth]

Auth hardening недели интеграции (кратко):

  • Rotate grace period теперь реален: 15-минутное dual-key окно после ротации; валидна только целиком старая пара (старый токен + старый секрет), смешанные комбинации — 401; исходящие вебхуки подписываются новым секретом немедленно; окно настраивается через MERCHANT_KEY_GRACE_PERIOD (0 = off). Поля ответа issue/rotate — token / secret (НЕ api_token / webhook_secret). См. Управление API-ключами.
  • merchant_id в запросе — advisory при HMAC (claims-win): значение (в т.ч. невалидного формата или чужое) молча игнорируется, реальный скоуп — из подписанного токена; запрос возвращает 200. Исключение — cabinet-JWT: там несоответствие merchant_id403.

[FIX] [callbacks]

  • Per-deal callback_url реально override'ит доставку (PayIn): иерархия per-deal callback_urlmerchant.webhook_url; оба пусты — колбэк не создаётся. Для PayOut per-deal override не поддерживается (поля в запросе нет) — только merchant.webhook_url.
  • Подпись X-Signature вебхука считается от байтов тела ровно в том виде, в котором они доставляются (фикс 2026-08-22): подпись и тело гарантированно разделяют один байтовый источник, HMAC мерчанта по полученному телу совпадает. См. Callbacks → Подпись запроса.

[FIX] [payout]

Статусная модель после создания приведена к фактической: синхронный авто-матчинг каскада возвращает MATCHED (есть свободный трейдер), иначе UNASSIGNED — retry-воркер продолжает мэтчинг до SLA, по истечении которого выплата переходит в терминальный EXPIRED (холд возвращён). deal_type=PAYOUT-сделки видны в GET /deals и GET /deals/{deal_id}. wallet_id в запросе — USDT-кошелёк списания (холд в USDT по SELL-курсу), а не fiat-кошелёк; currency в ответе — эхо ISO-кода fiat-валюты выплаты. См. PayOut.

[FIX] [balance]

GET /balance возвращает реальные кошельки мерчанта из money-сервиса — одна строка на фактический кошелёк со своей валютой и wallet_id (синтетических строк нет); frozen отражает активные холды. См. Балансы.

[FIX] [payin]

GET /deals задокументирован как объединение PAYIN (p2p_orders) и PAYOUT (p2p_payouts) в единый листинг: payment_url присутствует только у PAYIN, target_requisite (эхо, без маскировки) — только у PAYOUT; фильтр deal_type=PAYIN|PAYOUT. См. Deals.

[FIX] [general]

Идемпотентность: формат idempotency_key фактически не валидируется (любая непустая строка; рекомендуемый формат — UUID v4); повторный вызов с тем же ключом возвращает ту же сделку без дубля. Убрано обещание лимита в 128 символов / restrictions на символы. См. Идемпотентность.

[NEW] [balance]

Deposit address — две операции. TRC-20 адрес для зачисления USDT-обеспечения (collateral) на операционный баланс мерчанта (выплаты, крипто-выводы, комиссии; игроки в PayIn платят по реквизитам трейдеров, НЕ на этот адрес):

  • GET /wallet/deposit-address — read-only текущий активный адрес; 404 problem+json («deposit address not provisioned yet — POST /wallet/deposit-address to generate one»), если адрес ещё не сгенерирован.
  • POST /wallet/deposit-address — генерация/ротация: каноничный путь без merchant_id (скоуп — из токена). Ротация немедленно заменяет адрес в строке мерчанта, при этом все ранее выпущенные адреса остаются в постоянном polling-наборе — USDT на старый адрес не теряются. Legacy-путь POST /merchants/{merchant_id}/deposit-address остаётся рабочим для admin-impersonation.

См. Кошелёк → TRC-20 адрес для пополнения.

[BREAKING] [general]

trace_id / traceId удалены из тел problem+json. Тело ошибки — строго RFC 9457 поля (type, title, status, detail, instance); идентификатор трассировки передаётся заголовком X-Trace-Id на каждом ответе с ошибкой (Stripe-канон; генерируется gateway'ем, если upstream не предоставил). Именно его указывайте в обращениях в поддержку. Для браузерных клиентов заголовок экспонируется через CORS (Access-Control-Expose-Headers). См. Коды ошибок → Идентификатор трассировки.

Инструкция по миграции:

- const traceId = response.body.trace_id
+ const traceId = response.headers.get('X-Trace-Id')

[DOCS] [payout]

Lifecycle PayOut выпрямлен в Migration Guide и Справочнике статусов (приведён к PayOut): синхронный авто-матч каскада при создании возвращает сразу MATCHED либо UNASSIGNED (retry-воркер перематчивает; по SLA — EXPIRED с возвратом холда); INITIALIZED — стартовый статус стейт-машины (граничные случаи: провайдер-ветка/гонки); далее MATCHEDPROCESSINGCOMPLETED | FAILED. В statuses добавлена FSM-диаграмма PayOut.


2026-08-21 — Документация PayIn: единый контракт методов, мобильная коммерция (SIM), пример deal.expired

[FEATURE] [payin]

Явная фиксация принципа единого контракта: POST /deals/payin принимает один и тот же набор полей для любого метода оплаты — метод-специфичных параметров в запросе не существует, выбор метода задаётся только payment_method_id. Специфика канала (тип реквизита, формат отображения, инструкция для плательщика) инкапсулирована на стороне платформы и показывается на платёжной странице. См. Прием платежей → Поддерживаемые методы оплаты.

  • В список типовых методов добавлены методы мобильной коммерции (RequisiteType: SIM): реквизит для плательщика — номер телефона оператора связи, название оператора отображается как «банк получателя». Номер телефона самого плательщика (MSISDN) не требуется и не передаётся.
  • Поле phone_number в CheckoutView документировано для SBP и SIM (ранее — только SBP).

[FEATURE] [callbacks]

Добавлен пример payload deal.expired — терминального неуспешного исхода PayIn (истечение payment_timeout_at без поступления средств): поля received_amount и amount_match_status в нём отсутствуют, сверка сумм не выполняется. См. Callbacks → Примеры payload по событиям.


2026-07-12 — Запуск Merchant API V2 и Адаптера V1

[FEATURE] [general]

Официальный запуск Merchant API V2. Новая архитектура предоставляет повышенную стабильность, единый формат интеграции и улучшенные показатели безопасности.

Основные нововведения:

  • HMAC-SHA256 подписи: Внедрена унифицированная схема подписи X-Signature от тела запроса для всех эндпоинтов вместо разрозненных формул в V1.
  • Строковые перечисления статусов: Переход с числовых кодов состояний на строковые String Enums UPPER_SNAKE_CASE (например, код 1INITIALIZED, код 11COMPLETED), что повышает читаемость логов и кода. Полная таблица соответствия кодов V1 и статусов V2 — в Справочнике статусов.
  • Суммы в минимальных единицах (копейках): Суммы больше не передаются в виде строк с точкой ("1500.00"). Теперь используется целочисленный формат int64 (копейки, центы: 150000). Это защищает от ошибок округления чисел с плавающей запятой.
  • OpenAPI-спецификация: Документация теперь содержит полную OpenAPI-схему (openapi.yaml) с возможностью импорта в Postman/Insomnia и навигацией по эндпоинтам в боковой панели.

[FEATURE] [general]

Внедрение V1 Compatibility Adapter (Адаптера совместимости).

  • Позволяет существующим клиентам, интегрированным с API V1, переключиться на новую инфраструктуру Syncra V2 без изменения своего исходного кода.
  • Адаптер автоматически парсит старые заголовки (MERCHANT, SIGNATURE), проверяет подписи по алгоритму HMAC-SHA512, конвертирует форматы сумм и отдает ответы в старом конверте {failures, value, isSuccess}.

[DEPRECATION] [general]

Объявление о выводе из эксплуатации API V1.

  • Период поддержки (Grace Period): до 12 января 2027 года. Адаптер работает в штатном режиме без ограничений. Уже в этот период каждый ответ V1 несёт стандартные IETF-заголовки машиночитаемой депрекации:
    • Deprecation: true
    • Sunset: <дата отключения> (RFC 1123)
    • Link: </api/v2...>; rel="successor-version" — V2-эквивалент пути.
  • Полное отключение (Sunset): 12 января 2027 года V1 адаптер выключается (дата по умолчанию; может быть изменена оператором платформы). Все запросы по старым путям начинают возвращать 410 Gone с RFC 9457 problem details.
  • Заголовок X-Syncra-Deprecated не используется и не будет — сигнализация о депрекации идёт исключительно через Deprecation / Sunset / Link выше.

2026-07-13 — Архитектурная модернизация: Data-Driven Currencies и Appeals API

[BREAKING] [payout]

Поля card_number и full_name в PayOut переименованы в target_requisite и target_name для поддержки всех типов реквизитов (карта, СБП, e-wallet). V1 адаптер обрабатывает маппинг автоматически.

Инструкция по миграции:

- "card_number": "2202201234567890"
- "full_name": "Иван Иванович"
+ "target_requisite": "2202201234567890"
+ "target_name": "Иван Иванович"

[FEATURE] [disputes]

Добавлен полноценный Appeals API для мерчантов:

  • POST /appeals — открытие диспута по сделке
  • GET /appeals — список диспутов с фильтрацией
  • GET /appeals/{id} — получение диспута
  • POST /appeals/{id}/messages — отправка сообщения в чат
  • GET /appeals/{id}/messages — история чата

[FEATURE] [general]

Переход на data-driven конфигурацию: валюты, страны, лимиты и методы оплаты теперь управляются через админ-панель без перезапуска сервисов.

[FEATURE] [balance]

Мультивалютные балансы: поле currency теперь отражает реальную валюту тенанта. USDT wallet_id содержит реальный TRC-20 адрес мерчанта.


Август 2026 — TRY/Havale Channel, Antifraud, White-label и DO Migration

2026-08-18 — Merchant API v0.2.11: канал TRY/Havale для выплат

[FEATURE] [payout]

Выплаты в Турции по IBAN (Havale): в POST /deals/payout добавлены два optional-поля:

  • target_bank (string) — банк получателя (слаг или название, например ziraat); используется для inside-bank каскадного матчинга.
  • requisite_details (map<string,string>) — именованные реквизиты получателя; допустимые ключи: iban, account_number, document_number, bank_name, phone_number. Неизвестные ключи отклоняются с 400 и списком допустимых.

См. PayOut → Выплаты TRY / IBAN (Havale).

[FEATURE] [general]

Новый справочный эндпоинт GET /api/v1/p2p/merchant/payment-methods — активные методы тенанта мерчанта (id UUID, slug, name, requisite_types, currency_code, status). Это канонический источник значения payment_method_id для POST /deals/payin и слагов payment_method для POST /deals/payout. GET /banks UUID не возвращает (его methods — типы реквизитов RequisiteType).

См. Banks → Получение списка методов через API.

Инструкция по миграции: если вы программно получали payment_method_id из GET /banks — переключитесь на GET /payment-methods; если UUID были захардкожены — сверьте их с ответом нового эндпоинта после провижининга методов тенанта.

[FIX] [payin]

Страница PayIn приведена к фактическому поведению API:

  • Исправлен источник payment_method_id: в таблицах параметров ранее ошибочно указывался GET /banks; канонический источник UUID — GET /payment-methods (см. выше). Аналогичная правка внесена в Quickstart и Migration Guide.
  • Статус в примере ответа 200 OKESCROW_LOCKED: подбор трейдера и эскроу-лок выполняются синхронно в рамках запроса, при неудачном матчинге вызов завершается ошибкой, а не «висящей» сделкой. INITIALIZED — кратковременный транзитный статус, в ответе создания практически не наблюдается (возможен в GET /deals до завершения подбора).
  • Добавлен пример ответа для TRY (BANK_TRANSFER / Havale).
  • Новая секция «Где реквизиты получателя при PayIn» (PayIn): реквизиты получателя (PAN, телефон СБП, IBAN) никогда не возвращаются в теле ответа POST /deals/payin (PII-минимизация). Клиент получает их на платёжной странице через публичный эндпоинт GET /api/v1/p2p/checkout/{hash} (контракт CheckoutView: iban, holder_name, payment_method_name, card_number / phone_number); в терминальных состояниях чувствительные поля обнуляются.

[FIX] [payout]

Задокументирована конвенция эха реквизитов выплаты в V2: target_requisite и значения requisite_details в ответах — эхо данных из запроса мерчанта, без маскировки (это собственные реквизиты получателя мерчанта, а не данные трейдера; в логах платформа сохраняет только набор ключей). Маска вида 220220******7890 осталась только в legacy-эндпоинтах V1 (GET /v1/payments/outgoing/{id} и callback-конверте V1). См. PayOut → Выплаты TRY / IBAN (Havale).

[FIX] [callbacks]

Явное уточнение в Callbacks: платёжные реквизиты в вебхуки не передаются — никогда. Тела вебхуков (V2 CallbackEventPayload и legacy V1-конверт) содержат только идентификаторы, суммы, валюту и статус: полные PAN/IBAN/телефоны мерчанту не отправляются. В V1-конверте cardNumber приходит только замаскированным (последние 4 цифры), чувствительные значения в provider_metadata редуцируются. Исчерпывающий источник реквизитов для PayIn — публичный GET /api/v1/p2p/checkout/{hash}.

[FIX] [general]

Migration Guide — исправления и восстановленные справочники:

  • Статусы PayOut: исправлена фактическая ошибка — статус EXPIRED в V2 существует (таймаут), статуса CANCELLED нет. Реальный жизненный цикл: INITIALIZEDMATCHEDPROCESSINGCOMPLETED; при отсутствии трейдеров — UNASSIGNED, при ошибке — FAILED, по таймауту — EXPIRED (в V1 EXPIRED/FAILED маппятся в код 30).
  • Таблица маппинга статусов PayIn (V1 код → V2 enum): все коды 131, включая переплату/недоплату — 14/15COMPLETED + amount_match_status: overpaid / partial.
  • Маппинг полей PayIn: V1 method → V2 payment_method_id (UUID из GET /payment-methods); поля payment_method для PayIn в V2 нет.
  • Восстановлен справочник «V1 сигнатуры запросов» — payload-форматы Compatibility Adapter по всем 4 legacy-эндпоинтам: PayIn/PayOut — {timestamp};{id};{salt}, Withdraw — 4 поля (включая address), Dispute — 2 поля без соли; replay-защита: timestamp не старше 300 секунд и не более чем на 60 секунд в будущем.

[FIX] [general]

Прочие документационные правки:

  • Banks: убрано нереализованное обещание о «временном исчезновении» банков из справочника — состав GET /banks формируется из активных методов тенанта (p2p_payment_methods.status = ACTIVE); доступность конкретного трейдера влияет на матчинг сделки, а не на справочник.
  • Обзор Merchant API и этот changelog: дата полного отключения (sunset) V1 зафиксирована как 12 января 2027 года (согласно коду платформы); каждый ответ V1 несёт заголовки Deprecation, Sunset и Link; rel="successor-version".
  • Quickstart: статусы в примерах выровнены с реальным поведением — создание PayIn возвращает ESCROW_LOCKED (было PENDING), вебхук — amount_match_status: full (было MATCHED).

2026-08-09 — DO Migration Prep и выравнивание документации

[FEATURE] [general]

Подготовка инфраструктуры к DO-migration (Digital Ocean переезд):

  • RedPanda in-cluster — добавлен для событийной шины (замена managed Kafka).
  • Vault HA — High Availability режим для секретов.
  • Blue/Green деплой — стратегия бесшовного переключения между окружениями.
  • Module-registry переведён на SASL/SCRAM-аутентификацию.

[FIX] [general]

Документационный аудит: все страницы Merchant API выровнены под V2 canonical contract. Унифицированы X-Signature (не X-Syncra-Signature), payment_url (не payment_page_url), deal_id (не id), tron_address (не address). Удалены несуществующие mapping'и из migration-guide.


2026-08-05 — Исправление 4 P0-багов в клиентской аналитике

[FIX] [general]

Исправлены 4 критических бага (P0) в детальной статистике клиентов и antifraud-аналитике:

  • Detail stats — некорректный расчёт статистики по сделкам клиента.
  • Double-count — задвоение депозитов при определённых переходах статусов.
  • Error mapping — неправильная трансляция доменных ошибок в HTTP-коды.
  • Fingerprint — обработка пустого/невалидного device fingerprint.

2026-08-04 — IBAN/BANK_TRANSFER и улучшения V1 compat adapter

[FEATURE] [payin]

Добавлен тип реквизита BANK_TRANSFER с поддержкой IBAN для международных рынков (Turkish Havale, SEPA). Игрок видит IBAN получателя, ФИО держателя счёта и название банка/канала. См. Hosted Checkout → Перевод по IBAN.

[FIX] [general]

Улучшения V1 Compatibility Adapter: исправлены withdrawal callback, статус OVERPAID и URL prefix (3 P0-пробела закрыты).


2026-08-03 — Antifraud stack и White-label brand_config

[FEATURE] [general]

Запуск полноценного Antifraud-стека (TD-081). См. Antifraud:

  • Velocity protection — лимит 10 PayIn/час на client_id, ошибка CLIENT_RATE_LIMITED (HTTP 429).
  • Customer blockingIsBlocked + BlockedUntil, ошибка CUSTOMER_BLOCKED (HTTP 403).
  • Device fingerprinting — заголовок X-Player-Fingerprint, SHA-256 хэш, GDPR-compliant.
  • Auto-segmentation — FTD → STD при 3 депозитах, STD → TRUSTED при 10. VIP — ручной оверрайд.
  • Player tracking — сохранение FirstIP / LastIP / LastUserAgent на записи клиента.
  • Trust-based cascade routing — правила каскада могут фильтровать команды по customer_type.

[FEATURE] [payin]

White-label brand_config для payform — мерчанты могут кастомизировать платёжную страницу (primary_color, logo_url, favicon_url, brand_name, theme) через админ-панель. См. Hosted Checkout → Брендирование.

[FIX] [general]

RLS purge (TD-068) — корректная очистка Row-Level Security политик при операциях обслуживания.


Шаблон для добавления будущих записей

При внесении любых изменений в API разработчики Syncra обязаны добавлять новую запись в конец этого документа по следующему шаблону:

### YYYY-MM-DD — Заголовок изменения

#### `[FEATURE|FIX|DEPRECATION|BREAKING]` `[категория]`
Детальное описание изменений для разработчиков мерчантов.

**Инструкция по миграции (если применимо):**
Что именно нужно переписать на стороне мерчанта.

On this page

2026-09-09[FIX] V1-адаптер приведён к легаси-спеке (итоги аудита V1-контура)[DOC] Контракты адаптера в Migration guide, вебхук-контур в реестре кодов2026-09-08[DOC] Полный реестр числовых кодов V1 (закрывает вопрос интегратора по статусу 50)[DOC] Уточнения по кодам 20 / 16 / 32 (ответы на вопросы интегратора)2026-09-08[FIX] Контракт вебхуков и терминальные исходы — закрытие живого прогона интеграции2026-09-04[FEATURE] Баланс: прозрачность «средств в пути» (pending_credits)[FIX] V1-конверт: инфраструктурные сбои больше не маскируются под 401/404/4002026-09-03[FEATURE] Изменение суммы сделки (amend) + зачёт просроченного платежа (settle-as-received)2026-09-01[FIX] Возврат к канону конверта списков: data (откат регресса доки от 08-29)[DOC] Канон домена платёжной страницы — checkout.syncra.money2026-08-29[FIX] Ночная сверка №2: код догоняет доку (вебхуки, rate-limit-заголовки, идемпотентность per-merchant)[FEATURE] [callbacks] — Недостающие webhook-события реализованы в коде[FIX] [ratelimit] — Заголовки X-RateLimit-* действительно выставляются[SECURITY] [idempotency] — Ключ идемпотентности скоупится по мерчанту[DOC] [callbacks] — Точная математика ретраев доставки[DOC] [deals] — Пагинация и фильтры листинга по факту[DOC] [migration] — Реальные маршруты V1 compat-слоя[DOC] [misc] — Прочие сверки2026-08-28[DOC] Ночная сверка контрактов: дока ↔ proto ↔ код (Stripe Grade)[DOC] [payout] — Унифицированная ссылка на метод: UUID и слаг на обеих ногах[DOC] [wallet] — Правда о выводе USDT: статусы, комиссии, идемпотентность[DOC] [payin] — amount_match_status: ровно три значения[DOC] [passes] — Required-поля создания pass'а[DOC] [general] — Kill-switch, export-путь, V1-маппинг, мелкие фиксы2026-08-26[FEATURE] PayIn: статус CANCELLED — явная отмена сделки игроком или мерчем2026-08-24[FIX] PayIn: payment_method_id обязателенЖурнал изменений (Changelog)Формат записейАктуальная история версий2026-08-23 — Волны 7/8: кабинетные эндпоинты и сверка доки с живым стейджем[FEATURE] [general] — CSV-экспорт сделок[FEATURE] [general] — Обороты сделок[FEATURE] [callbacks] — История доставок и ручной retry[FEATURE] [general] — Лимиты мерчанта и kill_switch_reason[FIX] [general] — Сериализация int64 и формат ошибок2026-08-22 — Неделя интеграции: приведение контрактов к фактическому поведению платформы[BREAKING] [general][BREAKING] [general][FIX] [general][FIX] [auth][FIX] [callbacks][FIX] [payout][FIX] [balance][FIX] [payin][FIX] [general][NEW] [balance][BREAKING] [general][DOCS] [payout]2026-08-21 — Документация PayIn: единый контракт методов, мобильная коммерция (SIM), пример deal.expired[FEATURE] [payin][FEATURE] [callbacks]2026-07-12 — Запуск Merchant API V2 и Адаптера V1[FEATURE] [general][FEATURE] [general][DEPRECATION] [general]2026-07-13 — Архитектурная модернизация: Data-Driven Currencies и Appeals API[BREAKING] [payout][FEATURE] [disputes][FEATURE] [general][FEATURE] [balance]Август 2026 — TRY/Havale Channel, Antifraud, White-label и DO Migration2026-08-18 — Merchant API v0.2.11: канал TRY/Havale для выплат[FEATURE] [payout][FEATURE] [general][FIX] [payin][FIX] [payout][FIX] [callbacks][FIX] [general][FIX] [general]2026-08-09 — DO Migration Prep и выравнивание документации[FEATURE] [general][FIX] [general]2026-08-05 — Исправление 4 P0-багов в клиентской аналитике[FIX] [general]2026-08-04 — IBAN/BANK_TRANSFER и улучшения V1 compat adapter[FEATURE] [payin][FIX] [general]2026-08-03 — Antifraud stack и White-label brand_config[FEATURE] [general][FEATURE] [payin][FIX] [general]Шаблон для добавления будущих записей