<!-- Source: https://docs.infraio.xyz/ru/concepts/orders -->
<!-- Last updated: 2026-10-04 -->

# Заказы

Если [CheckoutSession](https://docs.infraio.xyz/ru/concepts/sessions) — это то, что видит
покупатель, то **Order** — это то, что важно *вам*. Это постоянная
запись о:

- Что покупалось (позиции)
- Сколько было к оплате и сколько фактически оплачено
- Возвраты в работе и применённые возвраты
- Ваша внешняя ссылка (`external_ref`) — обычно ваш собственный
  ID заказа, хранится на Order и возвращается на
  `GET /b2b/v1/orders/{id}` (в payload'ах webhook не отзеркаливается —
  см. ниже)

Заказу не обязательны позиции: отправьте `amount` вместо `items`, чтобы выставить просто сумму (счёт, депозит, платёжная ссылка на произвольную сумму). Отправляйте что-то одно, не оба поля.

CheckoutSession умирает после того, как один из её PaymentIntent
рассчитан, либо после истечения TTL. Order живёт всегда.

## Когда создаётся Order

Когда вы вызываете `POST /b2b/v1/checkout-sessions/quick`,
InfraIO Pay создаёт **и** новый Order, **и** новую CheckoutSession
в одной транзакции. Если у вас уже есть Order и вы хотите повторить
checkout (например, после того, как покупатель забросил), используйте
`POST /b2b/v1/checkout-sessions`, чтобы привязать свежую сессию к
существующему заказу — сохраняя аудиторскую цепочку.

## Жизненный цикл

```mermaid
stateDiagram-v2
    [*] --> PENDING: order created
    PENDING --> PAID:              full payment settled
    PENDING --> PARTIAL_PAID:      underpayment settled
    PENDING --> CANCELED:          session expired or canceled
    PARTIAL_PAID --> PAID:         merchant resolves OR remainder paid
    PAID --> PARTIALLY_REFUNDED:   partial refund executed
    PAID --> REFUNDED:             full refund executed
    PARTIALLY_REFUNDED --> REFUNDED: subsequent refund covers the rest
    PAID --> [*]
    REFUNDED --> [*]
    CANCELED --> [*]
```

| Состояние | Значит |
| --- | --- |
| `PENDING` | CheckoutSession активна и не разрешена. |
| `PAID` | Полная сумма рассчитана. **Webhook `payment.settled` срабатывает здесь.** Безопасно начинать обработку заказа. |
| `PARTIAL_PAID` | Деньги пришли, но меньше общей суммы. См. «Недоплата» ниже. |
| `CANCELED` | Сессия истекла или мерчант отменил. `metadata.canceled_reason` объясняет почему (`payment_timeout`, `merchant_canceled`, …). |
| `REFUNDED` | Вся оплаченная сумма возвращена. |
| `PARTIALLY_REFUNDED` | Часть возврата исполнена, остаток остаётся оплаченным. |

> **Note:**
>
> **Состояние, по которому переключается ваша логика обработки заказа,
> — `PAID`**, а не `COMPLETED` у CheckoutSession. Webhook
> `payment.settled` — канонический сигнал.

## Поле `external_ref`

При создании сессии вы можете включить `external_ref` (любая строка до
255 символов — обычно ваш собственный ID заказа). Он проходит через
весь pipeline:

- Хранится на Order
- Виден в панели мерчанта для поиска при поддержке
- Возвращается на `GET /b2b/v1/orders/{id}`, чтобы обработчик webhook
  мог получить его после получения `payment.settled`

> **Warning:**
>
> Payload'ы webhook **не** содержат `external_ref`. Чтобы сопоставить
> webhook `payment.*` с вашей собственной записью, возьмите `order_id` из
> payload'а и запросите заказ.

## Недоплата

Если on-chain перевод покупателя прошёл на меньшую сумму, чем общая
сумма заказа, Order переходит в `PARTIAL_PAID`. У вас три варианта:

1. **Принять и разрешить.** Переводит заказ в `PAID` и отправляет
   `order.resolved`. Это недоступно через REST API, поэтому для
   self-serve потока используйте вариант 2 (собрать остаток).
2. **Ждать остатка.** Создайте новую CheckoutSession против того же
   Order с `amount_due` = остаток. Покупатель доплачивает разницу;
   когда она рассчитается, Order перейдёт в `PAID`.
3. **Отменить и вернуть.** Верните частичную сумму и отмените Order.
   Покупатель отвечает за любые chain-комиссии.

## Возвраты

Возвраты — это отдельный API-уровень и отдельная страница концепции.
См. [Концепции → Возвраты](https://docs.infraio.xyz/ru/concepts/refunds).

## Endpoint'ы API

| Метод | Путь | Заметки |
| --- | --- | --- |
| `POST` | `/b2b/v1/orders` | Создание Order без сессии (редко) |
| `GET` | `/b2b/v1/orders/:order_id` | Чтение полного заказа с позициями + историей платежей |
| `PATCH` | `/b2b/v1/orders/:order_id/cancel` | Отмена неоплаченного заказа |
| `PATCH` | `/b2b/v1/orders/:order_id/reopen` | Переоткрытие авто-отменённого заказа (`payment_timeout`) |

## Что дальше

- [Концепции → Сессии](https://docs.infraio.xyz/ru/concepts/sessions) — обращённая к
  покупателю оболочка, охватывающая Order.
- [Концепции → Возвраты](https://docs.infraio.xyz/ru/concepts/refunds) — состояния возврата и
  ручной шаг on-chain отправки.
- [Webhooks → Обзор](https://docs.infraio.xyz/ru/webhooks/overview) — каждое событие, которое
  отправляется в течение жизненного цикла Order.
