S
docs.syncra.money
API ReferenceMerchant API

Выплаты клиентам (PayOut)

Вывод средств через P2P-трейдеров: мерчант создаёт заявку, трейдер переводит деньги получателю со своего банка

Выплаты клиентам (PayOut)

Выплата (PayOut) — это P2P-операция вывода средств, при которой мерчант создаёт заявку на перевод, а трейдер выполняет фактический перевод получателю со своего банковского счёта.


Процесс выплаты (PayOut Flow)

Загрузка диаграммы...

Деньги получателю приходят со счёта трейдера, а не напрямую с баланса мерчанта. Баланс мерчанта компенсирует трейдера за выполненный перевод внутри платформы Syncra.


Эндпоинт создания PayOut

POST /api/v1/p2p/merchant/deals/payout

Параметры запроса (JSON Body)

ПолеТипОбяз.Описание
amountint64ДаСумма выплаты в минорных единицах (копейки). 500000 = 5000.00 RUB.
currencystringДаISO 4217 код fiat-валюты выплаты ("RUB", "KZT", "TRY", ...). Возвращается в ответе эхом.
wallet_idstring(UUID)ДаUSDT-кошелёк мерчанта, с которого списываются средства: холд суммы создаётся в USDT (по SELL-курсу на момент создания), а не на fiat-кошельке. Это wallet_id USDT-строки из GET /balance.
target_requisitestringДаРеквизиты получателя (номер карты, телефона, IBAN).
target_namestringНетИмя получателя для верификации банком/трейдером.
target_bankstringНетБанк получателя: слаг ("ziraat", "garanti") или название. Используется для intra-bank каскадного матчинга (актуально для TRY/Havale). Пусто = без банковской подсказки (карты/СБП).
requisite_detailsmap<string,string>НетИменованные реквизиты получателя (см. ниже). Допустимые ключи: iban, account_number, document_number, bank_name, phone_number. Неизвестные ключи → 400 со списком допустимых.
payment_methodstringДаСсылка на метод выплаты из вашего каталога GET /payment-methods: слаг ("sbp_rub", "card_rub", "mobcom_rub", "express-havale", ...; одинаков на всех контурах stage/prod) или UUID (поле id) — оба значения резолвятся в один и тот же метод. Отсутствие, неизвестное значение или метод чужой валюты → 400 INVALID_ARGUMENT (со списком допустимых слагов).
idempotency_keystringДаУникальный ID операции (идемпотентность).

payment_method в PayOut принимает слаг (sbp_rub, card_rub, mobcom_rub) или UUID метода, а НЕ uppercase enum BANK_CARD / SBP. Enum-значения RequisiteType (BANK_CARD, E_WALLET, ...) относятся к типу реквизита трейдера: в ответе GET /payment-methods (поле requisite_types) это справочное описание канала, в запросах оно не передаётся. Тот же унифицированный контракт у PayIn payment_method_id (UUID или слаг) — обе ноги принимают оба формата.

Валюта метода должна совпадать с валютой сделки. Резолвер проверяет, что найденный метод обслуживает валюту currency запроса: выплата card_rub с currency: "TRY" отклоняется с 400 INVALID_ARGUMENT («payment method currency does not match the deal currency»). Берите метод той же валюты из каталога GET /payment-methods (поле currency_code).

Пример запроса

{
  "amount": 500000,
  "currency": "RUB",
  "wallet_id": "8f870a31-b88d-4cb0-a882-628d08cbda88",
  "target_requisite": "2202201234567890",
  "target_name": "Иван Иванович И.",
  "payment_method": "card_rub",
  "idempotency_key": "payout_order_99812"
}

Пример ответа (200 OK)

Ответ — объект MerchantDealInfo в поле deal верхнего уровня (обёртки data нет). Идентификатор сделки — deal_id (НЕ id). Для PAYOUT payment_url пуст (он есть только у PayIn); вместо него заполнено target_requisite. currency — эхо ISO-кода fiat-валюты выплаты из запроса.

Статус в ответе зависит от синхронного авто-матчинга каскада: если есть свободный трейдер — MATCHED (пример ниже); если свободного трейдера нет — UNASSIGNED (далее retry-воркер, см. Статусы PayOut сделки).

{
  "deal": {
    "deal_id": "e81d77a0-0d3a-4422-b91c-7cb052a2119c",
    "status": "MATCHED",
    "amount": "500000",
    "currency": "RUB",
    "created_at": "2026-07-12T17:21:00Z",
    "client_id": "",
    "deal_type": "PAYOUT",
    "updated_at": "2026-07-12T17:21:00Z",
    "payment_url": "",
    "target_requisite": "2202201234567890",
    "received_amount": "0",
    "amount_match_status": ""
  }
}

В V2 target_requisite в ответе — эхо значения из запроса, без маскировки: это собственные реквизиты мерчанта, а не данные трейдера. Маска вида 220220******7890 осталась только в legacy-эндпоинтах V1 (GET /v1/payments/outgoing/{id} и callback-конверте V1).

Созданная выплата — полноценная сделка: deal_type=PAYOUT-записи видны в GET /deals (объединённый листинг с PayIn) и доступны по GET /deals/{deal_id} по deal_id из ответа создания (см. Deals).


Статусы PayOut сделки

Канонический жизненный цикл — domain.PayOutStatus (7 значений). PayOut использует отдельный домен статусов от PayIn. Полная таблица с V1-кодами — в Справочнике статусов.

Статус V2Финальный?Описание
INITIALIZEDНетВыплата создана, баланс захолдирован, ожидает мэтчинга.
MATCHEDНетТрейдер/команда назначена. Типичный статус сразу после создания при синхронном авто-матчинге (есть свободный трейдер).
UNASSIGNEDНетСвободного трейдера нет на момент создания. Retry-воркер продолжает мэтчинг; по истечении SLA — EXPIRED.
PROCESSINGНетТрейдер осуществляет перевод.
COMPLETEDДа (Успех)Средства отправлены, холд списан.
FAILEDДа (Отказ)Отклонена (неверные реквизиты, отмена). Холд возвращён мерчанту.
EXPIREDДа (Таймаут)Истекла до завершения перевода (в т.ч. по SLA из UNASSIGNED).

Особые статусы

  • UNASSIGNED — на момент создания авто-матчинг каскада не нашёл свободного трейдера (все заняты / лимиты исчерпаны / нет реквизитов под эту валюту и сумму). Сделка возвращается с этим статусом в ответе POST /deals/payout, после чего retry-воркер автоматически повторяет мэтчинг: при успехе статус переходит в MATCHED → далее обычный жизненный цикл; если SLA на назначение истекает — выплата переходит в терминальный EXPIRED, холд возвращается мерчанту. Не терминальный статус. Ручное назначение (admin override) остаётся доступным для оператора.
  • PayOut не использует PayIn-статусы (PAYMENT_NOTIFIED, PAYMENT_VERIFIED, APPEALED, SOFT_DISPUTED).

Webhook-события PayOut используют те же имена, что PayIn: deal.completed (успех) и deal.failed (отказ, status: FAILED). НЕ payout.*. Подробнее — в Callbacks.


Выплаты TRY / IBAN (Havale)

Для Турции выплаты идут как банковский перевод по IBAN (RequisiteType BANK_TRANSFER): базовый реквизит — target_requisite (IBAN получателя), дополнительно заполняются target_bank и requisite_details.

Новые поля запроса

ПолеТипОбяз.Описание
target_bankstringНетБанк получателя: слаг ("ziraat", "garanti", ...) или название. Передаётся провайдеру как банк выплаты и используется для intra-bank каскадного матчинга. Пусто = без подсказки (карты/СБП).
requisite_detailsmap<string,string>НетИменованные реквизиты сверх target_requisite/target_name. Допустимые ключи: iban, account_number, document_number, bank_name, phone_number. Неизвестные ключи → 400 InvalidArgument со списком допустимых. Отсутствие/пустая карта = «дополнительных реквизитов нет».

Значения requisite_details — это собственные реквизиты мерчанта (данные его получателя), поэтому в ответах они возвращаются эхом, без маскировки (в отличие от реквизитов трейдеров в PayIn). В логах платформа сохраняет только набор ключей, без значений.

Пример запроса (TRY / Havale)

{
  "amount": 2500000,
  "currency": "TRY",
  "wallet_id": "8f870a31-b88d-4cb0-a882-628d08cbda88",
  "target_requisite": "TR33 0006 1005 1978 6457 8413 26",
  "target_name": "Ahmet Yilmaz",
  "target_bank": "ziraat",
  "requisite_details": {
    "iban": "TR33 0006 1005 1978 6457 8413 26",
    "document_number": "12345678901"
  },
  "payment_method": "express-havale",
  "idempotency_key": "payout_try_order_00042"
}
  • amount: 2500000 = 25 000.00 TRY (минорные единицы — куруш, 2 знака).
  • payment_methodслаг (или UUID) Havale-метода, заведённого для вашего тенанта (например, express-havale); значение резолвится против активных методов тенанта — неизвестная ссылка или метод другой валюты отклоняется с 400 (список методов и слагов вашего тенанта — GET /api/v1/p2p/merchant/payment-methods, см. Banks).
  • requisite_details.document_number — пример дополнительного реквизита (TC kimlik / номер документа), нужен не всегда: передавайте, когда метод или банк получателя его требует.

Пример ответа (200 OK)

{
  "deal": {
    "deal_id": "c25f9b31-7e40-4a11-8f0a-6d92b7c4e155",
    "status": "MATCHED",
    "amount": "2500000",
    "currency": "TRY",
    "created_at": "2026-08-18T11:02:00Z",
    "client_id": "",
    "deal_type": "PAYOUT",
    "updated_at": "2026-08-18T11:02:00Z",
    "payment_url": "",
    "target_requisite": "TR33 0006 1005 1978 6457 8413 26",
    "received_amount": "0",
    "amount_match_status": ""
  }
}

target_requisite — эхо из запроса без маскировки (см. NOTE выше).


Лимиты на разовые выплаты

Лимиты сумм не захардкожены — они динамически определяются конфигурацией тенанта:

Источник лимитаУправлениеОписание
Каскадные правила (cascade_rules.max_amount)Админ-панельМаксимальная сумма для конкретного каскада маршрутизации.
Реквизиты трейдеров (requisites.max_amount)Трейдер / АдминЛимит на одну транзакцию для конкретного реквизита.
Конфиг тенанта (system_config)СуперадминГлобальные лимиты автоодобрения.

Значения лимитов можно менять администратором в реальном времени без перезапуска сервисов. При необходимости отправить сумму выше лимита одной транзакции — разделите её на несколько запросов с уникальными idempotency_key.


События вебхуков

СобытиеСтатус выплатыОписание
deal.createdINITIALIZED / MATCHED / UNASSIGNEDВыплата создана (статус — на момент ответа создания после синхронного авто-матчинга).
deal.completedCOMPLETEDВыплата завершена
deal.failedFAILEDВыплата отклонена
deal.expiredEXPIREDВыплата истекла до завершения перевода (в т.ч. по SLA из UNASSIGNED); холд возвращён

On this page