# Payment API

> Создание и управление крипто-платежами через Payment API 2328.io.

Payment API позволяет создавать платёжные сессии, перенаправлять клиентов на хостед-чекаут и отслеживать статус платежа.

## Создать платёж

Создаёт платёжную сессию и возвращает URL для оплаты клиентом.

### Параметры запроса

| Поле | Тип | Обязательное | Описание | Значения |
|-------|------|----------|-------------|--------|
| `amount` | decimal | да | Сумма платежа в указанной валюте, например `100.00` |  |
| `currency` | string | да | Фиатная валюта (USD, EUR, RUB, …) или криптовалюта (USDT, TRX, BTC, …) | `USD`, `EUR`, `RUB`, `KZT`, `UAH`, `UZS`, `USDT`, `USDC`, `BTC`, `ETH`, `GRAM`, `SOL`, `TRX`, `BNB`, `POL`, `LTC`, `DASH`, `DOGE`, `ZEC`, `XRP`, `XMR` |
| `order_id` | string | да | Ваш ID заказа, например `ORDER-12345` (до 128 символов) |  |
| `to_currency` | string | нет | Заранее выбранная криптовалюта | `USDT`, `USDC`, `BTC`, `ETH`, `GRAM`, `SOL`, `TRX`, `BNB`, `POL`, `LTC`, `DASH`, `DOGE`, `ZEC`, `XRP`, `XMR` |
| `network` | string | нет\* | Код сети (обязателен, если задан `to_currency` или `currency` — криптовалюта) | `TRX-TRC20`, `ETH-ERC20`, `BASE`, `BSC-BEP20`, `AVAX-C`, `POL-MATIC`, `TON`, `SOL`, `BTC`, `LTC`, `DASH`, `DOGE`, `ZEC`, `XRP`, `XMR` |
| `url_return` | string | нет | URL для редиректа после оплаты, например `https://your-site.com/return` |  |
| `url_success` | string | нет | Альтернатива `url_return` |  |
| `url_callback` | string | да | URL для webhook-уведомлений, например `https://your-site.com/webhook` |  |
| `invite_code` | string | нет | Реферальный код |  |
| `fee_split` | decimal | нет | Доля комиссии мерчанта, перекладываемая на плательщика, 0–100 (%). 0 = мерчант платит полностью, 100 = плательщик платит полностью. Переопределяет настройку проекта. **Пример: `30`** (плательщик покрывает 30% комиссии). |  |
| `price_markup` | decimal | нет | Наценка или скидка к сумме счёта, от −99 до 100 (%). Переопределяет настройку проекта. **Пример: `5`** (+5%) или `-10` (скидка 10%). |  |
| `description` | string | нет | Опциональное описание счёта (до 200 символов). Отображается плательщику на странице оплаты. **Пример: `Premium plan — Order #12345`**. |  |
| `ttl_seconds` | int | нет | Время жизни счёта в секундах, от `300` (5 минут) до `86400` (24 часов). По истечении этого времени счёт истекает и оплатить его больше нельзя. По умолчанию: `3600` (1 час). **Пример: `3600`**. |  |

### Ответ

```json
{
  "state": 0,
  "result": {
    "uuid": "abc123-def456-...",
    "order_id": "ORDER-12345",
    "amount": "100.00",
    "currency": "USD",
    "amount_usd": "100.00",
    "exchange_rate": null,
    "url": "https://2328.io/pay/abc123-def456-...",
    "tg_deeplink": "https://t.me/my2328bot?start=pay_abc123-def456-...",
    "expires_at": "2026-01-11T21:00:00Z",
    "created_at": "2026-01-11T20:00:00Z",
    "payer_currency": "USDT",
    "payer_amount": "100.50",
    "network": "TRX-TRC20",
    "address": "TXYZabc123...",
    "payment_status": "check",
    "txid": null,
    "payment_amount": null,
    "qr": "data:image/png;base64,iVBORw0..."
  }
}
```

- Перенаправьте клиента на `result.url`, чтобы он завершил оплату.
- `tg_deeplink` — диплинк Telegram-бота для оплаты через Telegram MiniApp.
- `qr` — QR-код адреса депозита в формате data URI (Base64). Присутствует, когда адрес уже назначен (когда `network` задан вместе с `to_currency` или когда `currency` — криптовалюта); иначе `null`.
- `txid`, `payment_amount` — `null`, пока клиент не оплатит. Заполняются, как только транзакция обнаружена в блокчейне. Об этом сигнализирует webhook со статусом `payment_status: paid`.
- `exchange_rate` — `null`, если конвертация ещё не применима (например, курс фиат → крипто пока не зафиксирован). Заполняется, когда выбрана валюта плательщика.

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

#### Interactive request: `POST /v1/payment`
  - `amount` (decimal, required)
  - `currency` (enum, required): USD,EUR,RUB,KZT,UAH,UZS,USDT,USDC,BTC,ETH,GRAM,SOL,TRX,BNB,POL,LTC,DASH,DOGE,ZEC,XRP,XMR
  - `order_id` (string, required)
  - `to_currency` (enum): USDT,USDC,BTC,ETH,GRAM,SOL,TRX,BNB,POL,LTC,DASH,DOGE,ZEC,XRP,XMR
  - `network` (enum): TRX-TRC20,ETH-ERC20,BASE,BSC-BEP20,AVAX-C,POL-MATIC,TON,SOL,BTC,LTC,DASH,DOGE,ZEC,XRP,XMR
  - `url_return` (string)
  - `url_success` (string)
  - `url_callback` (string, required)
  - `invite_code` (string)
  - `fee_split` (decimal)
  - `price_markup` (decimal)
  - `description` (string)
  - `ttl_seconds` (integer)

## Ошибки

### Направление недоступно

Если выбранная комбинация `to_currency` + `network` (или `currency` как криптовалюта) временно отключена на платформе, API вернёт:

```json
{
  "state": 1,
  "error_code": "direction_disabled",
  "errors": {
    "direction": "This direction is temporarily unavailable"
  }
}
```

**HTTP статус:** `503 Service Unavailable`

Проверить актуальный статус всех направлений можно через [`GET /v1/directions`](/docs/directions). Скрывайте недоступные направления в UI заранее, чтобы не показывать пользователю ошибку.

## Платёжная страница, H2H и точные суммы в криптовалюте

Один и тот же эндпоинт поддерживает три разных сценария выставления счёта. Выберите нужный сценарий заранее и не смешивайте правила интерпретации суммы.

### Платёжная страница с выбором способа оплаты

Передайте `amount`, `currency`, `order_id` и `url_callback`, но не указывайте `to_currency` и `network`. В ответе придёт `result.url`; поля `address`, `qr`, а иногда и данные плательщика останутся `null`, пока пользователь не выберет направление оплаты на платёжной странице.

```json
{
  "amount": "125.00",
  "currency": "EUR",
  "order_id": "ORDER-2026-1042",
  "url_callback": "https://merchant.example/webhooks/2328",
  "url_return": "https://merchant.example/orders/ORDER-2026-1042"
}
```

### H2H-счёт с прямым адресом

Передайте `to_currency` и `network`. 2328.io создаст блокчейн-счёт прямо во время API-запроса, поэтому данные успешного ответа можно показать в вашей форме оплаты без перенаправления клиента.

```json
{
  "amount": "100.00",
  "currency": "USD",
  "to_currency": "USDT",
  "network": "TRX-TRC20",
  "order_id": "ORDER-2026-1043",
  "url_callback": "https://merchant.example/webhooks/2328"
}
```

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

- `payer_amount` и `payer_currency` — точные реквизиты суммы платежа;
- `network` и `address` — единственное допустимое направление для этого счёта;
- `qr` — data URI с тем же адресом;
- `expires_at` — срок действия счёта;
- `url` — резервная платёжная страница на случай, если пользовательский сценарий не удаётся завершить.

> **DANGER:** Никогда не создавайте и не подменяйте адрес самостоятельно, не используйте адрес от другого счёта и не рассчитывайте `payer_amount` по публичной спотовой цене. Единственный источник истины — ответ API.

### Счёт на точную сумму в криптовалюте

Если счёт выставляется в криптовалюте, укажите её в `currency`:

```json
{
  "amount": "25.000000",
  "currency": "USDT",
  "network": "TRX-TRC20",
  "order_id": "ORDER-2026-1044",
  "url_callback": "https://merchant.example/webhooks/2328"
}
```

Запрошенная криптовалютная сумма сохраняется в `payer_currency` и `payer_amount`. Сервис также может рассчитывать внутренний эквивалент в USD для учёта и курсовых полей; не подменяйте им точные криптовалютные реквизиты. Сохраняйте полученные десятичные строки вместе со всеми знаками после запятой.

Для криптовалюты с единственной поддерживаемой сетью система может выбрать сеть автоматически. Тем не менее для предсказуемого поведения лучше явно передавать `network`. Для активов в нескольких сетях, например стейблкоинов, сеть указывайте всегда.

## Идемпотентность и повторные запросы

`order_id` уникален в пределах авторизованного проекта мерчанта и служит ключом идемпотентности при создании платежа. Если платёж уже существует, API вернёт существующую сессию с `state: 0`.

> **WARNING:** Повторный запрос с тем же `order_id` **не** означает «обновить этот счёт». Изменения суммы, валюты, webhook URL, метаданных, срока действия или направления могут быть проигнорированы, потому что API вернёт существующую сессию. Сохраняйте первый запрос и отклоняйте конфликтующие повторы на своей стороне.

Рекомендуемый алгоритм создания:

1. Вставьте локальную попытку платежа и уникальный `order_id` в одну транзакцию базы данных.
2. Отправьте подписанный запрос API.
3. Сохраните возвращенный `uuid` и полный ответ.
4. Если HTTP-ответ потерян, повторите тот же запрос либо запросите `/v1/payment/info` по `order_id`.
5. Не создавайте второй локальный заказ только из-за тайм-аута запроса к API.

## Нестандартные ситуации при оплате

| Ситуация | Правильная обработка |
|----------|---------------------|
| `address` / `qr` равны `null` | Направление оплаты ещё не выбрано. Перенаправьте клиента на `url` либо создайте новый корректный H2H-счёт с новым `order_id`. |
| Ошибка валидации HTTP `400` | Прочитайте ошибки отдельных полей в `errors`; не повторяйте неизменённый запрос вслепую. |
| HTTP `429` | Повторяйте запрос с экспоненциальной задержкой, сохраняя тот же `order_id`. |
| HTTP `503` / `direction_disabled` | Обновите `/v1/directions`; временно скройте направление или повторите запрос позже. |
| Тайм-аут клиентского запроса | Считайте результат неизвестным. Сначала найдите платёж по `order_id` и только потом решайте, создавать ли что-либо ещё. |
| `underpaid_check` | Сохраните событие о частичной оплате и дождитесь доплаты либо следующего статуса. Не зачисляйте средства повторно при поступлении дополнительных `txid`. |
| `underpaid` | Окончательная недоплата. Примените настроенную политику исполнения или ручной проверки к фактически зачисленной сумме. |
| `overpaid` | Платёж успешен, но внесена лишняя сумма. Исполните заказ идемпотентно и сохраните фактические суммы для сверки и возможного возврата. |
| `aml_lock` | Не исполняйте заказ и не выдавайте средства автоматически; передайте случай в процесс комплаенса или поддержки. |
| `cancel` | Счёт истёк или отменён. Это не исключает поздний перевод в блокчейне; любое последующее событие сверяйте с поддержкой. |

URL возврата в браузере нужен только для навигации. Клиент может открыть его без оплаты, закрыть страницу после оплаты или повторно перейти по ссылке позже. Исполнять заказ мерчанта можно только на основании проверенного статуса из API или webhook.

## Информация о платеже

Получение текущего статуса платежа по `uuid` или `order_id`.

### Параметры запроса

| Поле | Тип | Обязательное | Описание | Значения |
|-------|------|----------|-------------|--------|
| `uuid` | string | да\* | UUID платежа (из `result.uuid` при создании) |  |
| `order_id` | string | да\* | Ваш ID заказа |  |

> **INFO:** Хотя бы одно из полей `uuid` или `order_id` обязательно.

#### Interactive request: `POST /v1/payment/info`
  - `uuid` (string)
  - `order_id` (string)

## Список платежей

Получение списка всех платежей с фильтрацией и пагинацией.

### Параметры запроса

| Поле | Тип | Обязательное | Описание | Значения |
|-------|------|----------|-------------|--------|
| `status` | string | нет | Фильтр по статусу платежа (см. [References](/docs/references)) | `pending`, `check`, `paid`, `underpaid_check`, `underpaid`, `overpaid`, `cancel` |
| `date_from` | date | нет | Начальная дата (YYYY-MM-DD), например `2026-01-01` |  |
| `date_to` | date | нет | Конечная дата (YYYY-MM-DD), например `2026-01-31` |  |
| `page` | int | нет | Номер страницы, по умолчанию `1` |  |
| `per_page` | int | нет | Элементов на странице, по умолчанию `15`, максимум `5000` |  |

#### Interactive request: `POST /v1/payment/list`
  - `status` (enum): pending,check,paid,underpaid_check,underpaid,overpaid,cancel
  - `date_from` (string)
  - `date_to` (string)
  - `page` (integer)
  - `per_page` (integer)