> ## Documentation Index
> Fetch the complete documentation index at: https://docs.crypto-cash.world/llms.txt
> Use this file to discover all available pages before exploring further.

# Fiat Module

> Fiat Module CryptoCash: подписанные API-запросы, создание платежа, виджет, статусы транзакций и обработка ошибок для интеграторов.

# Fiat Module

> **Примечание:** все числовые значения, суммы, курсы и идентификаторы в примерах являются иллюстративными и приведены только для демонстрации. Реальные значения зависят от рыночных условий, конфигурации аккаунта и параметров конкретной транзакции.

## Обзор

Fiat Module — это подсистема платформы CryptoCash, которая позволяет мерчанту принимать фиатные платежи от конечных пользователей и получать криптовалюту на свой кошелёк.

Интеграция выполняется через подписанные API-запросы. Платежи проводятся через платёжного провайдера (Mercuryo). Конечный пользователь завершает оплату в виджете CryptoCash, где выбирает способ оплаты. После обработки платежа мерчант получает webhook с финальным статусом.

**Поддерживаемые возможности:**

* Оплата картой, Apple Pay, Google Pay, SEPA и другие способы оплаты
* Подписанные запросы (`ED25519` или `LEGACY`/`SHA256`) для аутентификации мерчанта
* Webhook-и мерчанту при каждом изменении статуса транзакции
* Автоматический расчёт заработка мерчанта на основе `partner_fee`

## Высокоуровневый сценарий

```text theme={null}
1. Мерчант отправляет подписанный API-запрос → получает URL для оплаты
2. Пользователь открывает URL → видит виджет CryptoCash
3. Пользователь выбирает способ оплаты
4. Платёжный провайдер обрабатывает платёж
5. Мерчант получает webhook при каждой смене статуса
6. После успешной оплаты автоматически рассчитывается заработок мерчанта
```

**Два пути оплаты в виджете:**

| Способ оплаты          | Сценарий                                                                          |
| ---------------------- | --------------------------------------------------------------------------------- |
| Apple Pay (native)     | Пользователь оплачивает прямо в виджете — без редиректа на внешнюю страницу       |
| Карта / другие способы | Пользователь редиректится на страницу платёжного провайдера для завершения оплаты |

Подробное описание native-сценария Apple Pay см. в разделе **Apple Native Pay**.

## Настройка мерчанта и Fiat API Key

### Что настраивает мерчант

После того как администратор подготовил аккаунт, мерчант генерирует Fiat API Key в личном кабинете. При создании ключа задаются параметры, которые применяются ко всем платежам, связанным с этим ключом:

| Параметр               | Описание                                                                                            |
| ---------------------- | --------------------------------------------------------------------------------------------------- |
| `keyType`              | Алгоритм подписи: `ED25519` (рекомендуется) или `LEGACY` (`SHA256`)                                 |
| `keyName`              | Произвольное название ключа                                                                         |
| `partnerFee`           | Доля комиссии провайдера, которая идёт мерчанту; настраивается администратором на provider-аккаунте |
| `paymentMethods`       | Способы оплаты, доступные в виджете (например, `["card", "apple_pay"]`)                             |
| `webhookUrl`           | URL для получения webhook о статусах платежей                                                       |
| `logoUrl`              | Логотип мерчанта, отображаемый в виджете                                                            |
| `ipRestrictionEnabled` | Ограничивать ли API-доступ по IP-адресу                                                             |
| `allowedIpAddresses`   | Список разрешённых IP-адресов или CIDR-диапазонов                                                   |
| `expiresAt`            | Конкретная дата истечения ключа                                                                     |
| `neverExpires`         | Если `true`, ключ не истекает (`expiresAt` = `null`); по умолчанию срок действия ключа — 1 год      |

В ответе возвращается пара `publicKey` / `privateKey`. **Приватный ключ хранится на платформе в зашифрованном виде и показывается мерчанту только один раз** — при создании. Сохраните его сразу.

> Если на аккаунте мерчанта включена двухфакторная аутентификация, для генерации ключа потребуется подтверждение 2FA.

### Поля Fiat API Key

| Поле                   | Тип                   | Описание                                                         |
| ---------------------- | --------------------- | ---------------------------------------------------------------- |
| `publicKey`            | `string`              | Идентификатор ключа, передаётся в каждом запросе                 |
| `privateKey`           | `string`              | Секрет для подписи запросов; на платформе хранится зашифрованным |
| `keyType`              | `ED25519` \| `LEGACY` | Алгоритм подписи                                                 |
| `isActive`             | `boolean`             | Активен ли ключ                                                  |
| `expiresAt`            | `datetime` \| `null`  | Время истечения (`null` — без срока)                             |
| `paymentMethods`       | `string[]`            | Разрешённые способы оплаты                                       |
| `webhookUrl`           | `string` \| `null`    | URL для доставки webhook                                         |
| `logoUrl`              | `string` \| `null`    | URL логотипа                                                     |
| `ipRestrictionEnabled` | `boolean`             | Включено ли ограничение по IP                                    |
| `allowedIpAddresses`   | `string[]`            | Разрешённые IP-адреса                                            |

### ED25519 vs LEGACY

|                   | `ED25519`                                                  | `LEGACY`                                                                           |
| ----------------- | ---------------------------------------------------------- | ---------------------------------------------------------------------------------- |
| **Алгоритм**      | Асимметричная криптография (Ed25519)                       | Подпись на основе SHA256                                                           |
| **Связь ключей**  | `publicKey` математически выводится из `privateKey`        | `publicKey` — идентификатор; `privateKey` — общий секрет, используемый при подписи |
| **Верификация**   | Подпись проверяется по `publicKey` без знания `privateKey` | Платформа использует сохранённый `privateKey` для проверки подписи                 |
| **Рекомендуется** | Да                                                         | Только для совместимости со старыми интеграциями                                   |

### Что проверяется при каждом запросе

При получении подписанного запроса платформа проверяет:

* Ключ активен
* Срок действия ключа не истёк
* **IP API-вызывающего** (сервер мерчанта) входит в список разрешённых, если включено ограничение по IP
* Подпись валидна для выбранного алгоритма

> **Важно:** ограничение по IP применяется к адресу сервера, который делает API-запрос, а не к IP конечного пользователя в поле `ip` payload-а платежа.

## Подписанные запросы

Все API-запросы мерчанта должны быть подписаны. Алгоритм подписи определяется значением `keyType`, выбранным при создании ключа.

### Структура запроса

```json theme={null}
{
  "data": "<base64-encoded JSON payload>",
  "signature": "<base64-encoded signature>"
}
```

`publicKey` передаётся либо в заголовке `x-public-key`, либо внутри объекта `data`. Если присутствуют оба, приоритет у заголовка.

### ED25519 (рекомендуется)

```text theme={null}
1. payload    → JSON.stringify(payload)
2. json       → Buffer.from(json, 'utf8').toString('base64')  → data
3. sign       → Ed25519.sign(Buffer.from(data), privateKeyBytes) → signatureBytes
4. signature  → signatureBytes.toString('base64')
```

### LEGACY (SHA256)

```text theme={null}
1. payload    → JSON.stringify(payload)
2. json       → Buffer.from(json, 'utf8').toString('base64')  → data
3. hash       → SHA256(privateKey + data) → hashHex
4. signature  → Buffer.from(hashHex, 'utf8').toString('base64')
```

### Полный пример

Payload для `form/retrieve`:

```json theme={null}
{
  "publicKey": "a1b2c3d4...",
  "fiatCurrency": "SGD",
  "fiatAmount": "100",
  "currency": "USDT",
  "network": "TRC20",
  "address": "TXxxx...",
  "ip": "203.0.113.5"
}
```

Итоговое тело подписанного запроса:

```json theme={null}
{
  "data": "eyJwdWJsaWNLZXkiOiJhMWIyYzNkNC4uLiIsImZpYXRDdXJyZW5jeSI6IlNHRCIsImZpYXRBbW91bnQiOiIxMDAiLCJjdXJyZW5jeSI6IlVTRFQiLCJuZXR3b3JrIjoiVFJDMjAiLCJhZGRyZXNzIjoiVFh4eHguLi4iLCJpcCI6IjIwMy4wLjExMy41In0=",
  "signature": "base64-encoded-signature"
}
```

## Создание платежа — `form/retrieve`

### Endpoint

```text theme={null}
POST https://rates.crypto-cash.world/merchant/api/v1/pay-fiat/form/retrieve
```

Подписанный запрос. Подпись и IP проверяются до обработки запроса.

### Запрос

Поле `data` подписанного запроса содержит следующий JSON:

| Поле           | Тип    | Обязательное | Описание                                                                      |
| -------------- | ------ | ------------ | ----------------------------------------------------------------------------- |
| `ticker`       | string | ✓\*          | Тикер крипто-актива и сети (например, `"USDTTRC20"`)                          |
| `currency`     | string | ✓\*          | Код криптовалюты (например, `"USDT"`)                                         |
| `network`      | string | ✓\*          | Сеть блокчейна (например, `"TRC20"`)                                          |
| `fiatCurrency` | string | ✓            | Код фиатной валюты (например, `"SGD"`)                                        |
| `fiatAmount`   | string | ✓            | Сумма в фиате как строка (например, `"100"`)                                  |
| `address`      | string | ✓            | Адрес кошелька для зачисления криптовалюты                                    |
| `ip`           | string | ✓            | IP-адрес конечного пользователя                                               |
| `email`        | string | —            | E-mail клиента                                                                |
| `redirectUrl`  | string | —            | URL, на который пользователя нужно вернуть после оплаты                       |
| `lang`         | string | —            | Язык виджета (по умолчанию `"en"`)                                            |
| `externalId`   | string | —            | Внешний ID заказа на стороне мерчанта (должен быть уникальным в рамках ключа) |

`*` Чтобы выбрать крипто-актив, передайте либо `ticker`, либо пару `currency` + `network`.

### Ответ

```json theme={null}
{
  "code": 200,
  "data": {
    "item": {
      "id": "550e8400-e29b-41d4-a716-446655440000",
      "externalId": "order-123",
      "url": "https://buy.cryptocash.world/merchant/payment/550e8400-e29b-41d4-a716-446655440000"
    }
  }
}
```

| Поле         | Описание                                                          |
| ------------ | ----------------------------------------------------------------- |
| `id`         | Внутренний UUID транзакции                                        |
| `externalId` | Внешний ID из запроса либо `null`                                 |
| `url`        | URL виджета CryptoCash — на него нужно перенаправить пользователя |

### Что происходит после запроса

1. Проверяется подпись и IP.
2. Проверяется уникальность `externalId` в рамках ключа мерчанта.
3. Сумма в фиате сверяется с минимально допустимой для выбранной валюты.
4. Значение `network` нормализуется (без учёта регистра).
5. Создаётся транзакция со статусом `New`.
6. Генерируется URL виджета.
7. Мерчанту отправляется webhook `payment::created`.
8. Возвращаются `id` и `url`.

### Виджет CryptoCash и страница провайдера

|                | Виджет CryptoCash                                    | Страница платёжного провайдера |
| -------------- | ---------------------------------------------------- | ------------------------------ |
| **URL**        | `https://buy.cryptocash.world/merchant/payment/{id}` | Генерируется внутри виджета    |
| **Брендинг**   | Логотип мерчанта                                     | Стилистика провайдера          |
| **Назначение** | Выбор способа оплаты, превью курса, native Apple Pay | Прямая обработка платежа       |

**Мерчант всегда получает URL виджета CryptoCash.** URL страницы провайдера формируется внутри виджета после выбора способа оплаты и мерчанту не возвращается.

## Публичное API мерчанта

### Получение платежа

```text theme={null}
POST https://rates/merchant/api/v1/pay-fiat/payments/retrieve
```

Подписанный запрос. Поле `data` содержит одно из:

```json theme={null}
{ "internalId": "550e8400-..." }
```

или

```json theme={null}
{ "externalId": "order-123" }
```

Ответ:

```json theme={null}
{
  "code": 200,
  "data": {
    "item": {
      "id": "550e8400-e29b-41d4-a716-446655440000",
      "externalId": "order-123",
      "status": "Paid",
      "fiatCurrency": "SGD",
      "fiatAmount": "100",
      "cryptoCurrency": "USDT",
      "network": "TRC20",
      "cryptoAmount": "73.21",
      "address": "TXxxx...",
      "hash": "blockchain-hash",
      "widgetUrl": "https://buy.cryptocash.world/merchant/payment/550e8400-...",
      "createdAt": "2026-04-16T10:00:00.000Z",
      "updatedAt": "2026-04-16T10:07:00.000Z"
    }
  }
}
```

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

```text theme={null}
POST https://rates.crypto-cash.world/merchant/api/v1/pay-fiat/payments/list
```

Подписанный запрос. Поле `data`:

| Поле         | Тип      | По умолчанию | Описание                         |
| ------------ | -------- | ------------ | -------------------------------- |
| `page`       | number   | 1            | Номер страницы                   |
| `pageSize`   | number   | 100          | Количество элементов на странице |
| `dateAfter`  | datetime | —            | Фильтр: создано после этой даты  |
| `dateBefore` | datetime | —            | Фильтр: создано до этой даты     |

Ответ:

```json theme={null}
{
  "code": 200,
  "data": {
    "items": [ /* массив платежей в той же структуре, что и retrieve */ ],
    "pagination": {
      "page": 1,
      "pageSize": 100,
      "total": 42
    }
  }
}
```

### Списки валют

```text theme={null}
POST https://rates.crypto-cash.world/merchant/api/v1/fiat-currencies/list
POST https://rates.crypto-cash.world/merchant/api/v1/pay-fiat/crypto-currencies/list
```

Оба endpoint-а используют подписанные запросы. Минимальный payload в `data`:

```json theme={null}
{ "publicKey": "..." }
```

`crypto-currencies/list` возвращает доступные мерчанту криптовалюты вместе с сетями и тикерами. Значения из `tickers` можно передавать в `form/retrieve` вместо пары `currency` + `network`.

### Поля ответа платежа

| Поле             | Тип      | Nullable | Описание                    |
| ---------------- | -------- | -------- | --------------------------- |
| `id`             | string   |          | UUID транзакции             |
| `externalId`     | string   | ✓        | Внешний ID мерчанта         |
| `status`         | string   |          | Текущий статус              |
| `fiatCurrency`   | string   |          | Код фиатной валюты          |
| `fiatAmount`     | string   |          | Сумма в фиате               |
| `cryptoCurrency` | string   |          | Код криптовалюты            |
| `network`        | string   |          | Сеть блокчейна              |
| `cryptoAmount`   | string   | ✓        | Полученная сумма в крипто   |
| `address`        | string   |          | Адрес кошелька              |
| `hash`           | string   | ✓        | Хеш транзакции в блокчейне  |
| `widgetUrl`      | string   | ✓        | URL виджета                 |
| `createdAt`      | datetime |          | Время создания              |
| `updatedAt`      | datetime |          | Время последнего обновления |

### Статусы транзакций

| Статус     | Описание                                            |
| ---------- | --------------------------------------------------- |
| `New`      | Транзакция создана, ожидаются действия пользователя |
| `Waiting`  | Платёж принят и обрабатывается                      |
| `Paid`     | Криптовалюта отправлена на адрес кошелька           |
| `Fail`     | Платёж завершился неуспешно                         |
| `Canceled` | Платёж отменён                                      |

Статусы выставляются платформой на основе событий платёжного провайдера. Мерчант не может изменить статус через API.

## Обработка ошибок

Все ошибки API возвращаются в одном из двух форматов:

**Формат A — кодированная ошибка:**

```json theme={null}
{ "code": 2020, "message": "..." }
```

**Формат B — список ошибок** (используется при ошибках декодирования):

```json theme={null}
{ "errors": [2010] }
```

### Коды ошибок

| Код    | Описание                                                                                     |
| ------ | -------------------------------------------------------------------------------------------- |
| `1000` | Bad request                                                                                  |
| `1102` | Невалидный запрос (неподдерживаемая сеть, сумма ниже минимальной, дублирование `externalId`) |
| `1110` | Ошибка валидации запроса                                                                     |
| `2010` | Ошибка декодирования `data`                                                                  |
| `2011` | Ошибка декодирования `signature`                                                             |
| `2020` | Неверная подпись                                                                             |
| `2021` | API key заблокирован                                                                         |
| `2030` | IP-адрес запрещён                                                                            |
| `3001` | Заказ не найден                                                                              |
| `3002` | Транзакция не найдена                                                                        |

## Apple Pay

Виджет CryptoCash поддерживает native-сценарий Apple Pay: платёжная панель iOS открывается прямо в виджете без редиректа на внешнюю страницу.

Подробное описание условий доступности native-режима, fallback-поведения и обработки ошибок см. в разделе **Apple Native Pay**.

## Диаграммы

### Общий сценарий оплаты

```mermaid theme={null}
sequenceDiagram
    participant M as Merchant
    participant P as Platform
    participant W as Widget
    participant Pr as Payment Provider

    M->>P: POST /merchant/api/v1/pay-fiat/form/retrieve
    P->>P: Validate signature, IP, amount
    P->>P: Create transaction (status: New)
    P->>M: { data: { item: { id, url } } }
    P-->>M: Webhook: payment::created

    M->>W: Open widget URL
    W->>P: Get payment details
    P-->>W: Payment info
    W->>W: User selects payment method

    W->>P: Payment initiated
    P->>Pr: Process payment

    Pr->>P: Status update webhook
    P->>P: Update transaction status
    P-->>M: Webhook: payment::waiting / payment::paid / payment::failed

    Pr->>P: Withdrawal completed webhook
    P->>P: Calculate merchant earning
    P->>P: Update merchant balance
    P-->>M: Webhook: payment::paid
```
