> ## 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 Webhooks

> Webhook-и Fiat Module для интеграторов: типы событий, структура payload, проверка подписи и примеры.

# Fiat Webhooks

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

## Обзор

Fiat Module использует webhook-и Crypto-Cash, которые отправляются мерчанту после изменения статуса транзакции. Они работают только если на Fiat API key настроен параметр `webhookUrl`. Если `webhookUrl` не задан, webhook-и не отправляются.

> URL для уведомлений у платёжного провайдера и `webhookUrl` на Fiat API key — это разные вещи. `webhookUrl` на Fiat API key — это адрес, на который Crypto-Cash отправляет свои события мерчанту.

## Ключевые параметры

| Параметр                 | Значение                                  |
| ------------------------ | ----------------------------------------- |
| Цель webhook Crypto-Cash | `webhookUrl`, настроенный на Fiat API key |
| HTTP-метод               | `POST`                                    |
| Доставка                 | Асинхронная                               |
| Формат данных            | `application/json`                        |

## Как работают webhook-и

```
Пользователь открывает платёжный виджет
↓
Платёжный провайдер обрабатывает платёж
↓
Платёжный провайдер отправляет webhook в Crypto-Cash
↓
Crypto-Cash проверяет подпись провайдера
↓
Crypto-Cash обновляет статус транзакции
↓
Crypto-Cash формирует payment-событие
↓
Если на Fiat API key настроен webhookUrl,
Crypto-Cash отправляет webhook асинхронно
```

## Webhook-и Crypto-Cash

Webhook-и Crypto-Cash отправляются после payment-событий. Они работают только если на Fiat API key настроен `webhookUrl`.

При изменении статуса транзакции система формирует payment-событие и асинхронно отправляет webhook на настроенный `webhookUrl`. Если `webhookUrl` не настроен, webhook не отправляется.

HTTP-формат:

```
Method: POST
Content-Type: application/json
```

Каждый webhook содержит поле `signature` в теле payload. Мерчант использует Fiat API key, чтобы убедиться, что webhook отправлен Crypto-Cash и не был изменён.

Webhook payload и подпись сохраняются для аудита и истории.

## Типы событий

| Событие             | Когда отправляется            | Тип           |
| ------------------- | ----------------------------- | ------------- |
| `payment::created`  | Транзакция создана            | Промежуточное |
| `payment::waiting`  | Статус сменился на `Waiting`  | Промежуточное |
| `payment::paid`     | Статус сменился на `Paid`     | **Финальное** |
| `payment::failed`   | Статус сменился на `Fail`     | **Финальное** |
| `payment::canceled` | Статус сменился на `Canceled` | **Финальное** |

**Промежуточные события** (`payment::created`, `payment::waiting`) означают, что транзакция в работе и возможны дальнейшие изменения статуса.

**Финальные события** (`payment::paid`, `payment::failed`, `payment::canceled`) означают, что транзакция достигла терминального состояния:

| Финальное событие   | Значение                                          |
| ------------------- | ------------------------------------------------- |
| `payment::paid`     | Криптовалюта успешно отправлена на адрес кошелька |
| `payment::failed`   | Платёж не был завершён                            |
| `payment::canceled` | Платёж отменён                                    |

## Что должен проверить мерчант

При получении webhook Crypto-Cash:

1. Проверить поле `signature` с помощью Fiat API key (см. раздел «Подпись webhook»).
2. Прочитать `event.event_type`, чтобы определить событие.
3. Прочитать `event.data` — там полное состояние транзакции.
4. Обработать событие исходя из того, промежуточное оно или финальное.

> Поле `id` уникально для каждой доставки webhook. Используйте его для дедупликации обработки на своей стороне.

## Подпись webhook Crypto-Cash

Каждый webhook Crypto-Cash содержит поле `signature` в теле payload.

Подпись формируется по идентификатору и содержимому webhook:

```
payload = { id, delivered_at, event }
```

Мерчант проверяет подпись так: пересобирает тот же payload из полученного webhook, **исключая поле `signature`**, и проверяет его с помощью Fiat API key.

Способ проверки зависит от типа Fiat API key:

* `ED25519` — проверьте полученное `signature`, используя `publicKey` Fiat API.
* `LEGACY` — пересоберите ожидаемое значение signature, используя `privateKey` Fiat API, и сравните его с полученным.

Используйте ту же пару Fiat API key, которая используется для подписанных запросов мерчанта в API. Приватный ключ должен оставаться на стороне мерчанта — не отправляйте его в ответах на webhook, в логах или в обращениях в поддержку.

## Примеры payload

### `payment::created`

```json theme={null}
{
  "id": "webhook-id",
  "delivered_at": "2026-04-16T10:00:01.000Z",
  "event": {
    "event_type": "payment::created",
    "timestamp": "2026-04-16T10:00:01.000Z",
    "data": {
      "id": "crypto-cash-transaction-id",
      "externalId": "merchant-order-id",
      "status": "New",
      "fiatCurrency": "SGD",
      "fiatAmount": "51",
      "currency": "USDT",
      "network": "TRC20",
      "address": "wallet-address",
      "cryptoAmount": null,
      "exchangeRate": null,
      "paymentMethod": null,
      "hash": null,
      "buyId": null,
      "withdrawId": null,
      "customerId": null,
      "merchantEarning": null,
      "merchantId": "merchant-id",
      "createdAt": "2026-04-16T10:00:00.000Z",
      "updatedAt": "2026-04-16T10:00:00.000Z"
    }
  },
  "signature": "signature"
}
```

### `payment::waiting`

```json theme={null}
{
  "id": "webhook-id",
  "delivered_at": "2026-04-16T10:00:15.000Z",
  "event": {
    "event_type": "payment::waiting",
    "timestamp": "2026-04-16T10:00:15.000Z",
    "data": {
      "id": "crypto-cash-transaction-id",
      "externalId": "merchant-order-id",
      "status": "Waiting",
      "fiatCurrency": "SGD",
      "fiatAmount": "51",
      "currency": "USDT",
      "network": "TRC20",
      "address": "wallet-address",
      "cryptoAmount": null,
      "exchangeRate": null,
      "paymentMethod": "card",
      "hash": null,
      "buyId": "provider-operation-id",
      "withdrawId": null,
      "customerId": "customer-id",
      "merchantEarning": null,
      "merchantId": "merchant-id",
      "createdAt": "2026-04-16T10:00:00.000Z",
      "updatedAt": "2026-04-16T10:00:10.000Z"
    }
  },
  "signature": "signature"
}
```

### `payment::paid`

```json theme={null}
{
  "id": "webhook-id",
  "delivered_at": "2026-04-16T10:07:30.000Z",
  "event": {
    "event_type": "payment::paid",
    "timestamp": "2026-04-16T10:07:30.000Z",
    "data": {
      "id": "crypto-cash-transaction-id",
      "externalId": "merchant-order-id",
      "status": "Paid",
      "fiatCurrency": "SGD",
      "fiatAmount": "51",
      "currency": "USDT",
      "network": "TRC20",
      "address": "wallet-address",
      "cryptoAmount": "50.12",
      "exchangeRate": "1.02",
      "paymentMethod": "card",
      "hash": "blockchain-hash",
      "buyId": "provider-operation-id",
      "withdrawId": "provider-withdraw-id",
      "customerId": "customer-id",
      "merchantEarning": "0.9306",
      "merchantId": "merchant-id",
      "createdAt": "2026-04-16T10:00:00.000Z",
      "updatedAt": "2026-04-16T10:07:00.000Z"
    }
  },
  "signature": "signature"
}
```

### `payment::failed`

```json theme={null}
{
  "id": "webhook-id",
  "delivered_at": "2026-04-16T10:02:05.000Z",
  "event": {
    "event_type": "payment::failed",
    "timestamp": "2026-04-16T10:02:05.000Z",
    "data": {
      "id": "crypto-cash-transaction-id",
      "externalId": "merchant-order-id",
      "status": "Fail",
      "fiatCurrency": "SGD",
      "fiatAmount": "51",
      "currency": "USDT",
      "network": "TRC20",
      "address": "wallet-address",
      "cryptoAmount": null,
      "exchangeRate": null,
      "paymentMethod": "card",
      "hash": null,
      "buyId": "provider-operation-id",
      "withdrawId": null,
      "customerId": "customer-id",
      "merchantEarning": null,
      "merchantId": "merchant-id",
      "createdAt": "2026-04-16T10:00:00.000Z",
      "updatedAt": "2026-04-16T10:02:00.000Z"
    }
  },
  "signature": "signature"
}
```

### `payment::canceled`

```json theme={null}
{
  "id": "webhook-id",
  "delivered_at": "2026-04-16T10:03:05.000Z",
  "event": {
    "event_type": "payment::canceled",
    "timestamp": "2026-04-16T10:03:05.000Z",
    "data": {
      "id": "crypto-cash-transaction-id",
      "externalId": "merchant-order-id",
      "status": "Canceled",
      "fiatCurrency": "SGD",
      "fiatAmount": "51",
      "currency": "USDT",
      "network": "TRC20",
      "address": "wallet-address",
      "cryptoAmount": null,
      "exchangeRate": null,
      "paymentMethod": "card",
      "hash": null,
      "buyId": "provider-operation-id",
      "withdrawId": null,
      "customerId": "customer-id",
      "merchantEarning": null,
      "merchantId": "merchant-id",
      "createdAt": "2026-04-16T10:00:00.000Z",
      "updatedAt": "2026-04-16T10:03:00.000Z"
    }
  },
  "signature": "signature"
}
```
