# Convert API

> Конвертуйте криптовалюти напряму з балансу вашого мерчанта — отримайте живу котирування та виконайте за ринковою ціною.

Convert API дозволяє обмінювати валюти, які зберігаються на балансі вашого мерчанта, за поточною ринковою ціною — той самий рушій, що працює у вкладці **Обмін** панелі мерчанта, тепер доступний із вашого бекенду.

> **WARNING:** Ендпоінти Convert підписуються вашим **звичайним API-ключем** — тим самим, що використовується для запитів [Payment API](/docs/payments), а **не** ключем Payout API. Виконання конвертації одразу списує та зараховує баланс вашого мерчанта, тож ставтеся до цього ключа з такою ж обережністю, як до будь-яких облікових даних, що рухають гроші.

## Отримати ціну конвертації

Повертає орієнтовну котирування для конвертації за поточною ринковою ціною — ефективний курс і підсумкові суми. Нічого не списується і не резервується; викликайте скільки завгодно разів перед виконанням.

`POST /v1/convert/price`

### Параметри запиту

| Поле | Тип | Обов'язково | Опис | Значення |
|------|-----|-------------|------|----------|
| `from_currency` | string | так | Валюта, з якої конвертуємо | `BTC`, `ETH`, `USDT`, `USDC`, `TRX`, `BNB`, `GRAM`, `SOL`, `POL`, `LTC`, `DASH`, `DOGE`, `ZEC`, `XRP`, `XMR` |
| `to_currency` | string | так | Валюта, в яку конвертуємо. Має відрізнятися від `from_currency` | `USDT`, `USDC`, `BTC`, `ETH`, `TRX`, `BNB`, `GRAM`, `SOL`, `POL`, `LTC`, `DASH`, `DOGE`, `ZEC`, `XRP`, `XMR` |
| `amount` | decimal | так | Сума конвертації, більша за `0` |  |
| `amount_type` | string | так | До якої сторони належить `amount` | `from`, `to` |

> **INFO:** `amount_type=from` — витратити рівно `amount` у `from_currency`. `amount_type=to` — отримати рівно `amount` у `to_currency`.

**🟢 200 OK** · `application/json`

```json
{
  "state": 0,
  "result": {
    "success": true,
    "from_currency": "BTC",
    "to_currency": "USDT",
    "amount_type": "from",
    "from_amount": "0.01000000",
    "to_amount": "947.86690000",
    "effective_rate": "94786.69000000",
    "from_amount_usd": "947.87",
    "to_amount_usd": "947.87"
  }
}
```

#### Поля відповіді

| Поле | Тип | Опис |
|------|-----|------|
| `success` | boolean | Чи вдалося успішно розрахувати котирування |
| `from_currency` | string | Вихідна валюта |
| `to_currency` | string | Цільова валюта |
| `amount_type` | string | Повторює `amount_type` із запиту |
| `from_amount` | string | Сума, яка буде списана в `from_currency` |
| `to_amount` | string | Сума, яка буде зарахована в `to_currency` |
| `effective_rate` | string | Курс, застосований до цього котирування — 1 одиниця `from_currency` у `to_currency` (уже враховує ціноутворення платформи) |
| `from_amount_usd` | string \| null | Еквівалент `from_amount` у USD |
| `to_amount_usd` | string \| null | Еквівалент `to_amount` у USD |

- Котирування є **лише орієнтовним** — ринкова ціна може змінитися між отриманням котирування і викликом виконання.
- Цей виклик не списує і не резервує жодного балансу.

> Use your project UUID and the endpoint-appropriate API key from the merchant dashboard.

#### Interactive request: `POST /v1/convert/price`
  - `from_currency` (enum, required): BTC,ETH,USDT,USDC,TRX,BNB,GRAM,SOL,POL,LTC,DASH,DOGE,ZEC,XRP,XMR
  - `to_currency` (enum, required): USDT,USDC,BTC,ETH,TRX,BNB,GRAM,SOL,POL,LTC,DASH,DOGE,ZEC,XRP,XMR
  - `amount` (decimal, required)
  - `amount_type` (enum, required): from,to

## Виконати конвертацію

Виконує конвертацію за поточною ринковою ціною та оновлює баланс вашого мерчанта. Окремого кроку «підтвердити котирування» немає — викликайте цей ендпоінт напряму із сумою, яку хочете конвертувати.

`POST /v1/convert`

> **INFO:** **Ідемпотентність.** Повторення точно того самого запиту (ті самі `from_currency`, `to_currency`, `amount`, `amount_type`) протягом приблизно хвилини після першого виклику поверне вже наявну конвертацію замість створення другої. Після закінчення цього вікна ідентичний запит вважатиметься новою конвертацією — не повторюйте виклик наосліп після таймауту, спершу перевірте результат попереднього виклику.

> **WARNING:** Цей ендпоінт обмежено **10 запитами на хвилину** на одного викликача — суворіше за загальний ліміт API, — оскільки кожен виклик рухає реальний баланс.

### Параметри запиту

| Поле | Тип | Обов'язково | Опис | Значення |
|------|-----|-------------|------|----------|
| `from_currency` | string | так | Валюта, з якої конвертуємо | `BTC`, `ETH`, `USDT`, `USDC`, `TRX`, `BNB`, `GRAM`, `SOL`, `POL`, `LTC`, `DASH`, `DOGE`, `ZEC`, `XRP`, `XMR` |
| `to_currency` | string | так | Валюта, в яку конвертуємо. Має відрізнятися від `from_currency` | `USDT`, `USDC`, `BTC`, `ETH`, `TRX`, `BNB`, `GRAM`, `SOL`, `POL`, `LTC`, `DASH`, `DOGE`, `ZEC`, `XRP`, `XMR` |
| `amount` | decimal | так | Сума конвертації, більша за `0` |  |
| `amount_type` | string | так | До якої сторони належить `amount` | `from`, `to` |

**🟢 200 OK** · `application/json`

```json
{
  "state": 0,
  "result": {
    "id": 12345,
    "type": "manual",
    "status": "completed",
    "from_currency": "BTC",
    "to_currency": "USDT",
    "from_amount": "0.01000000",
    "requested_from_amount": "0.01000000",
    "refund_amount": null,
    "to_amount": "947.86690000",
    "exchange_rate": "94786.69000000",
    "fee_amount": "0.00000000",
    "from_amount_usd": "947.87",
    "to_amount_usd": "947.87",
    "processed_at": "2026-01-20T15:30:24Z",
    "created_at": "2026-01-20T15:30:22Z"
  }
}
```

#### Поля відповіді

| Поле | Тип | Опис |
|------|-----|------|
| `id` | int | ID ордера конвертації, присвоєний системою |
| `type` | string | Завжди `manual` для цього API |
| `status` | string | Поточний статус (див. «Статуси конвертації» нижче) |
| `from_currency` | string | Вихідна валюта |
| `to_currency` | string | Цільова валюта |
| `from_amount` | string | Списана сума в `from_currency` |
| `requested_from_amount` | string \| null | Запитана вами вихідна сума при `amount_type = from`. `null` при `amount_type = to` |
| `refund_amount` | string \| null | Частина попередньо списаної суми, повернена вам після часткового виконання. `null`, якщо ордер виконано повністю |
| `to_amount` | string | Зарахована сума в `to_currency` |
| `exchange_rate` | string | Курс, фактично застосований до цієї конвертації — 1 одиниця `from_currency` у `to_currency` (уже враховує ціноутворення платформи) |
| `fee_amount` | string | Комісія платформи за цю конвертацію, у `from_currency` або `to_currency` залежно від напрямку операції. Вже врахована в `exchange_rate` — показана для прозорості |
| `from_amount_usd` | string \| null | Еквівалент `from_amount` у USD |
| `to_amount_usd` | string \| null | Еквівалент `to_amount` у USD |
| `processed_at` | string (ISO 8601) \| null | Коли конвертація завершила виконання. `null`, поки вона в обробці |
| `created_at` | string (ISO 8601) | Коли було створено ордер конвертації |

#### Статуси конвертації

| Статус | Опис |
|--------|------|
| `pending` | Створено, ще не надіслано на ринок |
| `processing` | Баланс заблоковано, ордер розміщено на ринку |
| `completed` | Повністю виконано — `to_amount` зараховано на ваш баланс |
| `failed` | Не вдалося виконати — попередньо списану суму автоматично повернуто |
| `partially_completed` | Лише для валютних пар без прямого ринку (маршрутизація через проміжну валюту): перший етап завершився успішно, але другий — ні. Вам зараховується проміжна валюта замість `to_currency` — сконвертуйте її ще раз, щоб досягти початкової мети |

#### Interactive request: `POST /v1/convert`
  - `from_currency` (enum, required): BTC,ETH,USDT,USDC,TRX,BNB,GRAM,SOL,POL,LTC,DASH,DOGE,ZEC,XRP,XMR
  - `to_currency` (enum, required): USDT,USDC,BTC,ETH,TRX,BNB,GRAM,SOL,POL,LTC,DASH,DOGE,ZEC,XRP,XMR
  - `amount` (decimal, required)
  - `amount_type` (enum, required): from,to

## Помилки

У разі помилки відповідь містить `state: 1` та `error_code` — спільні для `/v1/convert/price` і `/v1/convert`:

**🔴 422 / 400** · `application/json`

```json
{
  "state": 1,
  "error_code": "amount_too_small",
  "errors": {
    "amount": "Amount is too small for this conversion. Please increase the amount and try again."
  }
}
```

| `error_code` | HTTP-статус | Опис |
|--------------|-------------|------|
| `validation_failed` | 422 | Некоректні або відсутні параметри, або відмова через бізнес-правило (наприклад, недостатньо балансу) — деталі в полі `errors` |
| `amount_too_small` | 422 | `amount` менше за мінімальний торговельний розмір для цієї валютної пари |
| `convert_unavailable` | 400 | Наразі не вдалося виконати конвертацію (дані ринку недоступні або немає маршруту між двома валютами) — спробуйте трохи пізніше |
| `internal_error` | 400 | Неочікувана внутрішня помилка сервера під час обробки запиту |

## Автоматичне конвертування вхідних платежів

Автоконвертація — це налаштування проєкту для вхідних рахунків-фактур та кредитів статичного гаманця. Вона налаштовується на панелі управління торговця, а не шляхом додавання полів до `/v1/payment`. Кожне правило обирає одну або кілька валют-джерел та цільову валюту.

Після завершення конвертації, інформація про платіж та вебхуки торговця можуть включати:

```json
{
  "payment_amount": "0.14800000",
  "merchant_amount": "0.146520000000000000",
  "payer_currency": "XMR",
  "convert": {
    "to_currency": "USDT",
    "commission": "0.09000000",
    "rate": "323.21000000",
    "amount": "47.262015740000000000"
  }
}
```

Діапазони сум свідомо розділені:

- `payment_amount` — що було зафіксовано в мережі в валюті джерела платежу;
- `merchant_amount` — чиста сума джерела, що належить торговцю до конвертації;
- `convert.amount` — сума, зарахована у `convert.to_currency`;
- `convert.rate` і `convert.commission` — виконаний результат конвертації, а не ціна, яку ви повинні перераховувати локально.

> **WARNING:** Відсутність `convert` має значення: конвертація могла не завершитися, можливо, не налаштована для цього джерела або повернулася до кредиту у валюті джерела. Ніколи не вигадуйте цільову суму з `/exchange-rates` або публічної ринкової ціни.

### Не вдалося автоматично конвертувати та відбулося повернення

Конвертація відбувається після отримання платежу у блокчейні. Наявність на ринку, мінімальні розміри замовлень, обмеження точності, тайм-аути бірж та недостатня ліквідність для виконання можуть затримати або запобігти конвертації.

- Депозити нижче глобального/проектного мінімуму обходять конвеєр конвертації та зараховуються у валюті джерела.
- Перехідні збої можуть бути повторені асинхронно.
- Великі або неприбуткові депозити можуть повертатися до кредиту у вихідній валюті після вичерпання політики повторних спроб.
- Отже, платіж може бути дійсним навіть тоді, коли бажане конвертування в цільову валюту не відбулося.

Ваша інтеграція повинна спочатку зберегти підтверджений платіж, а потім узгодити фактично зараховану валюту з інформації про платіж, необов’язкового блоку `convert` та балансів продавця. Не блокувати підтвердження webhook платежу, очікуючи на власні аналітичні або інформаційні системи.

### Тести прийнятності автоматичної конверсії

Перевірте принаймні: успішне пряме конвертування, конвертацію через проміжну валюту/багатоступеневу, пил нижче мінімуму, тимчасова повторна спроба, повернення до вихідної валюти, недоплата, переплата, дубльоване веб-хуків, відсутність `convert` та звірка після неоднозначного тайм-ауту.

## Крайові випадки ручного конвертування

- `/v1/convert/price` є орієнтовним переглядом; рух ринку може змінити результат виконання.
- `amount_type: from` виправляє запит зі сторони джерела, тоді як `amount_type: to` запитує суму зі сторони цілі. Не міняйте значення при представленні інтерфейсу підтвердження.
- Пару без прямого ринку можна провести через проміжну валюту. Якщо завершиться лише одна частина, `partially_completed` повідомляє про проміжну кредитну суму.
- Якщо виклик execute перевищує час очікування, виконайте узгодження перед повторною спробою. Ринковий ордер може бути виконаний навіть якщо його HTTP-відповідь втрачена.
- Ставтеся до `failed` як до стану для узгодження, а не як до дозволу застосовувати локальний запис компенсуючого балансу; платформа володіє обліком дебету/пільг.