#POST /merchant/dispute

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

Поддерживает два формата: application/json и multipart/form-data (если нужно прикрепить файлы-доказательства).


#JSON

POST /merchant/dispute
Content-Type: application/json

// REQ

{
  "transaction_id": "8ZNVCJ5S",
  "reason": "Customer paid, but the order is expired",
  "dispute_type": "payin_timeout",
  "sent_amount": "5000",
  "evidence_url": "https://your-cdn.example/screenshot.jpg",
  "files": [
    { "content": "<base64>", "ext": "jpg" }
  ]
}

// RES

{
  "id": "f47ac10b-58cc-4372-a567-0e02b2c3d479"
}

id в ответе — UUID апелляции. Сохраните его, по нему дальше дёргаете GET /merchant/dispute?id=….


#Multipart form-data

Тот же набор полей, но текстовые поля идут как fields, а файлы — как attachments (несколько частей с одинаковым именем).

POST /merchant/dispute
Content-Type: multipart/form-data; boundary=...

transaction_id: 8ZNVCJ5S
reason:         Customer paid, but order expired
dispute_type:   payin_timeout
sent_amount:    5000
attachments:    <file>
attachments:    <file>

#Поля запроса

ПолеОбяз.Описание
transaction_idКороткий 8-символьный id платформы или ваш merchant_transaction_id
reasonСвободный текст. Если пусто → подставится Dispute from merchant (type=…)
dispute_typeСм. таблицу ниже. Если не передать — выводится автоматически из типа/статуса транзакции
sent_amount (или amount)Для underpaid / overpaid — фактически отправленная клиентом сумма
evidence_urlURL картинки/скрина. Скачаем сами и положим в хранилище
files (только в JSON)Массив { content: <base64>, ext: "jpg" }
attachments (только в multipart)До 5 файлов, до 10 MB каждый

#Ограничения файлов

  • Максимум 5 файлов за запрос
  • Каждый файл до 10 MB
  • Разрешённые MIME: application/pdf, image/jpeg, image/png, image/heic, image/webp, image/gif, video/mp4, video/quicktime, video/webm
  • Всё остальное молча отбрасывается

#Типы диспутов

dispute_typeКогда использовать
payin_timeoutPayIn ушёл в expired, но клиент реально оплатил
payin_trader_rejectТрейдер отклонил, но клиент оплатил
underpaidПрислали меньше — добиваем до paid
overpaidПрислали больше — забираем излишек
payout_not_receivedPayOut: получатель деньги не получил

Если поле не передано, тип определяется автоматически:

  • transaction_type = payoutpayout_not_received
  • status = expiredpayin_timeout
  • иначе → payin_trader_reject

#Какие статусы можно диспутить

Разрешено открывать диспут только если транзакция в одном из статусов:

pending, paid, underpaid, overpaid, processing,
pending_confirmation, error, expired, cancel

Запрещено:

  • chargeback400 CANNOT_DISPUTE
  • disputed (уже в диспуте) → 400 CANNOT_DISPUTE
  • любой другой статус не из списка → 400 CANNOT_DISPUTE

Также блокируется, если по этой транзакции уже есть открытый диспут (статус не в cancelled / rejected / resolved_approved / resolved_rejected / expired) → 400 DISPUTE_EXISTS.


#Коды ошибок POST /merchant/dispute

HTTPCodeКогда
400INVALID_REQUESTБитый body
400VALIDATION_ERRORtransaction_id пуст
400DISPUTE_EXISTSПо этой tx уже открыт диспут
400CANNOT_DISPUTEСтатус транзакции не позволяет
400/500DISPUTE_FAILEDВнутренняя ошибка обработки
404TRANSACTION_NOT_FOUNDТранзакция не ваша или не существует
500INTERNAL_ERRORВнутренний сервис недоступен

Формат ошибки одинаков:

{ "error": "CODE", "message": "human text" }