#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_url | — | URL картинки/скрина. Скачаем сами и положим в хранилище |
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_timeout | PayIn ушёл в expired, но клиент реально оплатил |
payin_trader_reject | Трейдер отклонил, но клиент оплатил |
underpaid | Прислали меньше — добиваем до paid |
overpaid | Прислали больше — забираем излишек |
payout_not_received | PayOut: получатель деньги не получил |
Если поле не передано, тип определяется автоматически:
transaction_type = payout→payout_not_receivedstatus = expired→payin_timeout- иначе →
payin_trader_reject
#Какие статусы можно диспутить
Разрешено открывать диспут только если транзакция в одном из статусов:
pending, paid, underpaid, overpaid, processing,
pending_confirmation, error, expired, cancelЗапрещено:
chargeback→400 CANNOT_DISPUTEdisputed(уже в диспуте) →400 CANNOT_DISPUTE- любой другой статус не из списка →
400 CANNOT_DISPUTE
Также блокируется, если по этой транзакции уже есть открытый диспут (статус не в cancelled / rejected / resolved_approved / resolved_rejected / expired) → 400 DISPUTE_EXISTS.
#Коды ошибок POST /merchant/dispute
| HTTP | Code | Когда |
|---|---|---|
| 400 | INVALID_REQUEST | Битый body |
| 400 | VALIDATION_ERROR | transaction_id пуст |
| 400 | DISPUTE_EXISTS | По этой tx уже открыт диспут |
| 400 | CANNOT_DISPUTE | Статус транзакции не позволяет |
| 400/500 | DISPUTE_FAILED | Внутренняя ошибка обработки |
| 404 | TRANSACTION_NOT_FOUND | Транзакция не ваша или не существует |
| 500 | INTERNAL_ERROR | Внутренний сервис недоступен |
Формат ошибки одинаков:
{ "error": "CODE", "message": "human text" }