# Создание счёта

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

`POST /invoices`

Создаёт новый счёт. Если передан `rail`, адрес для оплаты выдаётся сразу и его можно показать в своём интерфейсе. Без `rail` клиент выберет монету на платёжной странице `pay_url`.

## Параметры

| Параметр | Тип | Обязательный | Описание |
| --- | --- | --- | --- |
| `amount` | number \| string | да | Сумма счёта в валюте `currency`. Больше 0, не более 1 000 000 000. |
| `currency` | string | нет | Валюта цены: `USD`, `USDT`, `EUR`, `RUB`, `UAH`, `KZT`, `TRY`, `GBP`. По умолчанию `USD`. |
| `order_id` | string | нет | Ваш номер заказа, до 64 символов. По нему можно найти счёт и избежать дубликатов. |
| `description` | string | нет | Описание, которое увидит клиент на платёжной странице. До 300 символов. |
| `metadata` | object | нет | Произвольные данные (например, `user_id`). Возвращаются в ответах и вебхуках. |
| `rail` | string | нет | Сразу выбрать монету и сеть, например `usdt_tron`. Список — в [Монеты и сети](https://docs.ketmus.com/md/rails.md). |
| `rails` | string[] | нет | Ограничить список монет, из которых выбирает клиент. |
| `lifetime_sec` | integer | нет | Время жизни счёта в секундах: от 300 (5 минут) до 86 400 (24 часа). По умолчанию — из настроек магазина (обычно 3600). |
| `fee_payer` | string | нет | Кто платит комиссию: `merchant` или `payer`. По умолчанию — из настроек магазина. |
| `underpaid_coverage_bps` | integer | нет | Допустимая недоплата в базисных пунктах (0–5000), при которой счёт всё равно считается оплаченным. |
| `callback_url` | string | нет | URL для вебхуков этого счёта. Если не передан — используется URL из настроек магазина. |
| `return_url` | string | нет | Куда вернуть клиента по кнопке «Вернуться в магазин». |
| `success_url` | string | нет | Куда перенаправить клиента после успешной оплаты. |
| `fail_url` | string | нет | Куда перенаправить клиента, если счёт истёк или отменён. |
| `payer_email` | string | нет | Email клиента для чека и связи. |
| `customer_id` | string | нет | Ваш идентификатор клиента, до 120 символов. |

> Неизвестные параметры отклоняются с ошибкой `400 invalid` — это защищает от опечаток в названиях полей.

## Пример запроса

```bash
curl -X POST "https://app.ketmus.com/api/merchant/v1/invoices" \
  -H "Authorization: Bearer $KETMUS_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "amount": 25,
    "currency": "USD",
    "order_id": "ORDER-1042",
    "description": "Подписка Pro на 1 месяц",
    "rail": "usdt_tron",
    "metadata": {
      "user_id": "u_731"
    },
    "callback_url": "https://shop.example.com/webhooks/ketmus",
    "success_url": "https://shop.example.com/orders/1042?paid=1",
    "fail_url": "https://shop.example.com/orders/1042?failed=1"
  }'
```

```javascript
const response = await fetch('https://app.ketmus.com/api/merchant/v1/invoices', {
  method: 'POST',
  headers: {
    Authorization: `Bearer ${process.env.KETMUS_API_KEY}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    amount: 25,
    currency: 'USD',
    order_id: 'ORDER-1042',
    description: 'Подписка Pro на 1 месяц',
    rail: 'usdt_tron',
    metadata: {
      user_id: 'u_731',
    },
    callback_url: 'https://shop.example.com/webhooks/ketmus',
    success_url: 'https://shop.example.com/orders/1042?paid=1',
    fail_url: 'https://shop.example.com/orders/1042?failed=1',
  }),
})

const data = await response.json()
if (!response.ok) throw new Error(data.error.message)
```

```python
import os
import requests

response = requests.post(
    "https://app.ketmus.com/api/merchant/v1/invoices",
    headers={"Authorization": f"Bearer {os.environ['KETMUS_API_KEY']}"},
    json={
        "amount": 25,
        "currency": "USD",
        "order_id": "ORDER-1042",
        "description": "Подписка Pro на 1 месяц",
        "rail": "usdt_tron",
        "metadata": {
            "user_id": "u_731",
        },
        "callback_url": "https://shop.example.com/webhooks/ketmus",
        "success_url": "https://shop.example.com/orders/1042?paid=1",
        "fail_url": "https://shop.example.com/orders/1042?failed=1",
    },
    timeout=30,
)
data = response.json()
response.raise_for_status()
```

```php
<?php
$ch = curl_init('https://app.ketmus.com/api/merchant/v1/invoices');
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_POST => true,
    CURLOPT_HTTPHEADER => [
        'Authorization: Bearer ' . getenv('KETMUS_API_KEY'),
        'Content-Type: application/json',
    ],
    CURLOPT_POSTFIELDS => json_encode([
        'amount' => 25,
        'currency' => 'USD',
        'order_id' => 'ORDER-1042',
        'description' => 'Подписка Pro на 1 месяц',
        'rail' => 'usdt_tron',
        'metadata' => [
            'user_id' => 'u_731',
        ],
        'callback_url' => 'https://shop.example.com/webhooks/ketmus',
        'success_url' => 'https://shop.example.com/orders/1042?paid=1',
        'fail_url' => 'https://shop.example.com/orders/1042?failed=1',
    ]),
]);
$data = json_decode(curl_exec($ch), true);
curl_close($ch);
```

## Ответ

HTTP `201 Created`. Объект счёта содержит также `available_rails` — монеты, которыми можно оплатить этот счёт.

```json
{
  "invoice": {
    "type": "invoice",
    "id": "8f0c6a52-3f7e-4b8e-9d61-2c4b0f8a1e7d",
    "order_id": "ORDER-1042",
    "status": "pending",
    "is_final": false,
    "late_payment": false,
    "price": {
      "amount": "25",
      "currency": "USD"
    },
    "payment": {
      "rail": "usdt_tron",
      "asset": "USDT",
      "network": "tron",
      "address": "TQ7nX4yVh3cE9v5G2kRZb8aWm1pLd6sFJt",
      "memo": null,
      "amount_due": "25.31",
      "amount_received": "0",
      "amount_confirmed": "0",
      "late_amount": "0",
      "rate": "1",
      "txids": []
    },
    "fee": {
      "bps": 125,
      "payer": "payer",
      "amount": "0",
      "merchant_amount": "0"
    },
    "pay_url": "https://app.ketmus.com/checkout/inv_5Kq2ZtR8wLmA",
    "metadata": {
      "user_id": "u_731"
    },
    "created_at": "2026-11-03T10:15:00.000Z",
    "expires_at": "2026-11-03T11:15:00.000Z",
    "paid_at": null,
    "public_id": "inv_5Kq2ZtR8wLmA",
    "description": "Подписка Pro на 1 месяц",
    "lifetime_sec": 3600,
    "underpaid_coverage_bps": 0,
    "refresh_count": 0,
    "return_url": "https://shop.example.com/orders/1042",
    "success_url": "https://shop.example.com/orders/1042?paid=1",
    "fail_url": "https://shop.example.com/orders/1042?failed=1",
    "payer_email": null,
    "customer_id": null,
    "payment_uri": null,
    "explorer_address_url": "https://tronscan.org/#/address/TQ7nX4yVh3cE9v5G2kRZb8aWm1pLd6sFJt",
    "available_rails": [
      {
        "id": "usdt_tron",
        "asset": "USDT",
        "network": "tron",
        "label": "USDT",
        "network_label": "Tron (TRC-20)",
        "icon": "/coins/tether.svg",
        "confirmations": 20
      },
      {
        "id": "ton",
        "asset": "TON",
        "network": "ton",
        "label": "Toncoin",
        "network_label": "TON",
        "icon": "/coins/toncoin.svg",
        "confirmations": 1
      }
    ]
  }
}
```

### Счёт без выбранной монеты

Если `rail` не передан, поле `payment` равно `null`, а статус — `new`. Адрес появится, когда клиент выберет монету на `pay_url`:

```json
{
  "invoice": {
    "type": "invoice",
    "id": "8f0c6a52-3f7e-4b8e-9d61-2c4b0f8a1e7d",
    "order_id": "ORDER-1042",
    "status": "new",
    "is_final": false,
    "late_payment": false,
    "price": {
      "amount": "25",
      "currency": "USD"
    },
    "fee": null,
    "pay_url": "https://app.ketmus.com/checkout/inv_5Kq2ZtR8wLmA",
    "metadata": {
      "user_id": "u_731"
    },
    "created_at": "2026-11-03T10:15:00.000Z",
    "expires_at": "2026-11-03T11:15:00.000Z",
    "paid_at": null,
    "public_id": "inv_5Kq2ZtR8wLmA",
    "description": "Подписка Pro на 1 месяц",
    "lifetime_sec": 3600,
    "underpaid_coverage_bps": 0,
    "refresh_count": 0,
    "return_url": "https://shop.example.com/orders/1042",
    "success_url": "https://shop.example.com/orders/1042?paid=1",
    "fail_url": "https://shop.example.com/orders/1042?failed=1",
    "payer_email": null,
    "customer_id": null,
    "payment_uri": null,
    "explorer_address_url": null,
    "payment": null
  }
}
```

## Поля объекта счёта

| Поле | Тип | Описание |
| --- | --- | --- |
| `id` | string | Идентификатор счёта (UUID). Используйте в запросах к API. |
| `public_id` | string | Публичный идентификатор для ссылки на оплату. |
| `status` | string | Текущий статус. → [Статусы счёта](https://docs.ketmus.com/md/invoice-statuses.md) |
| `is_final` | boolean | `true`, если статус окончательный и больше не изменится. |
| `price` | object | Цена счёта: `amount` и `currency`. |
| `payment` | object \| null | Реквизиты и суммы в выбранной монете. `null`, пока монета не выбрана. |
| `payment.address` | string | Адрес, на который нужно отправить перевод. |
| `payment.memo` | string \| null | Memo или destination tag (для XRP). Без него платёж не будет зачислен. |
| `payment.amount_due` | string | Сумма к оплате в монете, включая комиссию, если её платит клиент. |
| `payment.amount_received` | string | Сколько уже пришло, включая неподтверждённые транзакции. |
| `payment.amount_confirmed` | string | Сколько пришло с нужным числом подтверждений. |
| `payment.txids` | string[] | Хеши транзакций по счёту. |
| `fee` | object \| null | Комиссия: `bps`, `payer`, `amount` и `merchant_amount` — сумма к зачислению магазину. |
| `pay_url` | string | Ссылка на платёжную страницу Ketmus. Именно её отправляйте покупателю — для любой монеты и для СБП. |
| `payment_uri` | string \| null | URI для QR-кода в вашем собственном интерфейсе (`bitcoin:`, `ton:`, `ethereum:` и т. д.). `null`, если у сети нет стандартной схемы URI, например у Tron, — тогда кодируйте в QR сам адрес. Для СБП — прямая ссылка банка `https://qr.nspk.ru/…`: не отправляйте её покупателю вместо `pay_url`. |
| `sbp` | object | Только для СБП: `amount_rub` — точная сумма в рублях, `pay_until` — до какого времени действует QR-код. |
| `expires_at` | string | Когда истекает счёт (ISO 8601, UTC). |
| `paid_at` | string \| null | Когда счёт был оплачен. |