> ## 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.crypto-cash.world/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 https://buy.cryptocash.world/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
```
