Заказы
Если CheckoutSession — это то, что видит покупатель, то Order — это то, что важно вам. Это постоянная запись о:
- Что покупалось (позиции)
- Сколько было к оплате и сколько фактически оплачено
- Возвраты в работе и применённые возвраты
- Ваша внешняя ссылка (
external_ref) — обычно ваш собственный ID заказа, хранится на Order и возвращается наGET /b2b/v1/orders/{id}(в payload’ах webhook не отзеркаливается — см. ниже)
CheckoutSession умирает после того, как один из её PaymentIntent рассчитан, либо после истечения TTL. Order живёт всегда.
Когда создаётся Order
Когда вы вызываете POST /b2b/v1/checkout-sessions/quick,
payment-service создаёт и новый Order, и новую CheckoutSession
в одной транзакции. Если у вас уже есть Order и вы хотите повторить
checkout (например, после того, как покупатель забросил), используйте
POST /b2b/v1/checkout-sessions, чтобы привязать свежую сессию к
существующему заказу — сохраняя аудиторскую цепочку.
Жизненный цикл
| Состояние | Значит |
|---|---|
DRAFT | Зарезервировано для будущего drafts-потока. Ни один код-путь сегодня не создаёт заказы DRAFT — каждый заказ создаётся сразу PENDING, так что вы не увидите это состояние. |
PENDING | CheckoutSession активна и не разрешена. |
PAID | Полная сумма рассчитана. Webhook payment.settled срабатывает здесь. Безопасно начинать обработку заказа. |
PARTIAL_PAID | Деньги пришли, но меньше общей суммы. См. «Недоплата» ниже. |
CANCELED | Сессия истекла или мерчант отменил. metadata.canceled_reason объясняет почему (payment_timeout, merchant_canceled, …). |
REFUNDED | Вся оплаченная сумма возвращена. |
PARTIALLY_REFUNDED | Часть возврата исполнена, остаток остаётся оплаченным. |
Состояние, по которому переключается ваша логика обработки заказа,
— PAID, а не COMPLETED у CheckoutSession. Webhook
payment.settled — канонический сигнал.
Поле external_ref
При создании сессии вы можете включить external_ref (любая строка до
255 символов — обычно ваш собственный ID заказа). Он проходит через
весь pipeline:
- Хранится на Order
- Виден в панели мерчанта для поиска при поддержке
- Возвращается на
GET /b2b/v1/orders/{id}, чтобы обработчик webhook мог получить его после полученияpayment.settled
Payload’ы webhook не отзеркаливают external_ref напрямую
сегодня — ранние черновики этой документации утверждали, что
data.external_ref течёт в каждое событие, и это было неверно.
Чтобы сопоставить webhook payment.* с вашей строкой в БД, возьмите
order_id из payload’а и запросите заказ. Нативное поле
external_ref в payload’ах webhook — в roadmap.
Недоплата
Если on-chain перевод покупателя прошёл на меньшую сумму, чем общая
сумма заказа, Order переходит в PARTIAL_PAID. У вас три варианта:
- Принять и разрешить. Переводит заказ в
PAIDи отправляетorder.resolved. Это не публичный REST-endpoint — разрешение сегодня внутреннее/операционное действие, поэтому для self-serve потока предпочитайте вариант 2 (собрать остаток). - Ждать остатка. Создайте новую CheckoutSession против того же
Order с
amount_due= остаток. Покупатель доплачивает разницу; когда она рассчитается, Order перейдёт вPAID. - Отменить и вернуть. Верните частичную сумму и отмените Order. Покупатель отвечает за любые chain-комиссии.
У продукта тут нет рекомендации — разные мерчанты хотят разные политики. Выберите одну и закрепите в своей админке.
Возвраты
Возвраты — это отдельный API-уровень и отдельная страница концепции. См. Концепции → Возвраты.
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) |
Что дальше
- Концепции → Сессии — обращённая к покупателю оболочка, охватывающая Order.
- Концепции → Возвраты — состояния возврата и ручной шаг on-chain отправки.
- Webhooks → Обзор — каждое событие, которое отправляется в течение жизненного цикла Order.