Сессии
CheckoutSession — это то, с чем взаимодействует покупатель — объект с TTL и одноразовостью, владеющий URL размещённого checkout. Самая тонкая из трёх ключевых сущностей; основная работа происходит в Заказах и Payment Intent’ах (см. ниже).
Модель данных из трёх сущностей
CheckoutSession ← 1:1 → Order ← 1:N → PaymentIntent
(покупатель) (каталог) (каждая попытка оплаты)| Сущность | Назначение | Время жизни |
|---|---|---|
| CheckoutSession | Обращённая к покупателю — есть session_key, checkout_url, TTL | Минуты (по умолчанию 30) |
| Order | Состояние вашего каталога — позиции, суммы, возвраты | Постоянная запись |
| PaymentIntent | Одна попытка оплаты на одной сети/активе | Часы; рассчитывается или истекает |
Вы создаёте сессию и заказ вместе (через
POST /b2b/v1/checkout-sessions/quick). Каждый раз, когда покупатель
выбирает актив на странице checkout, открывается свежий PaymentIntent
для соответствующей сети. Если он переключает активы посреди checkout,
предыдущий intent переходит в EXPIRED, и стартует новый.
Идентификатор
Сессия идентифицируется своим session_key:
cst_G-SO92J7HNWkwMHEHjD4oO1ZURL-безопасный, ~24 символа после префикса (18 случайных байт,
закодированных base64url без padding’а). Размещённая страница checkout
— это https://checkout.infraio.xyz/<session_key> — рассматривайте
session_key как bearer-credential для именно этого checkout.
Жизненный цикл — CheckoutSession
| Состояние | Значит |
|---|---|
ACTIVE | Сессия создана и открыта. URL checkout работает. |
COMPLETED | PaymentIntent на этой сессии рассчитан. Order теперь PAID (или PARTIAL_PAID при недоплате). |
EXPIRED | expires_at прошло без расчёта. Cleanup-worker перевёл состояние и отменил все открытые Order’ы. |
CANCELED | Явная отмена — либо покупатель нажал «cancel», либо вы вызвали endpoint отмены. |
Три терминальных состояния взаимоисключающие и финальные. Новая CheckoutSession против того же Order может быть создана, если вы хотите повторить (например, после недоплаты).
TTL
- По умолчанию: 30 минут (настраивается через поле
expires_inпри создании, в секундах). - Границы: На сервере жёсткого min/max не закреплено. Используйте разумные значения — менее 60 секунд рискует выйти за тайминги для легитимных покупателей; более 7 дней удерживает ёмкость на токене, который почти наверняка заброшен. Выбирайте число, соответствующее ожидаемому окну принятия решения покупателем.
- Дефолт per-merchant: Настраивается через панель, но соблюдается
только в legacy-пути
POST /b2b/v1/checkout-sessions(двухшаговый). ПутьPOST /b2b/v1/checkout-sessions/quickвсегда откатывается на 30 минут, еслиexpires_inопущен, независимо от настройки per-merchant. Если на быстром пути нужен другой дефолт, отправляйтеexpires_inявно при каждом вызове. - Проверка: Ленивая на чтении + периодический cleanup-worker.
Сессия, у которой
expires_atпрошло, трактуется какEXPIRED, даже если поле состояния ещё не записано, поэтому не полагайтесь на чтение состояния через API в точный момент истечения.
Недоплата
Если покупатель отправляет меньше суммы сессии, PaymentIntent всё равно
рассчитывается на частичную сумму, а Order переходит в PARTIAL_PAID.
CheckoutSession переходит в COMPLETED (один PaymentIntent рассчитан),
поэтому она больше не пригодна к повторному использованию.
Чтобы принять недоплату как полную оплату, заказ можно разрешить в
PAID — см. Концепции → Заказы (публичного
resolve API сегодня нет; для self-serve потока вместо этого собирайте
остаток). Чтобы собрать остаток, создайте новую CheckoutSession
против того же Order на остаточную сумму.
Переплата
Если покупатель отправляет больше суммы сессии (редко, но случается при ручных переводах), on-chain сканер фиксирует переплату в течение 24 часов и отправляет оповещение мерчанту. Автоматического возврата нет — выпустите его вручную через API возвратов или панель.
Платежи неверным активом
Депозитный адрес генерируется per кортежу (session, chain, asset).
Если покупатель отправит неверный актив на адрес, on-chain matcher не
распознает его, и PaymentIntent останется открытым до истечения TTL.
Мы можем восстановить средства, но это поток через поддержку, не
автоматика — инструктируйте покупателей отправлять ровно тот актив,
который показан на странице checkout.
Идемпотентность при создании
POST /b2b/v1/checkout-sessions/quick принимает поле
idempotency_key в теле запроса (заметьте: поле тела, а не
HTTP-заголовок). Сгенерируйте для него UUID автоматически, если у
вашего клиента нет естественного ключа — SDK делает это по умолчанию.
Ключ дедуплицирует Order, а не CheckoutSession. При повторе с тем
же ключом /quick возвращает оригинальный заказ (order_id
стабилен), но создаёт свежую CheckoutSession — новый session_key
и checkout_url каждый раз. Это намеренно: один Order может стоять за
несколькими попытками checkout (см. Заказы),
поэтому повторный /quick отдаёт покупателю чистую сессию без
дублирования заказа.
Два поведения отличаются от Stripe-стиля idempotency — не попадитесь:
- Тело не хэшируется и не сравнивается. Повторное использование
ключа с другим телом не возвращает
409— сервер молча возвращает заказ, уже сохранённый под этим ключом, и игнорирует новое тело. Поэтому рассматривайтеidempotency_keyкак одноразовый токен для одного логического заказа; никогда не переиспользуйте его между разными корзинами. - Дедуплицируется только Order, не сессия. Если вам нужен тот же
URL checkout, сохраните
session_key/checkout_urlиз первого ответа — повторный вызов/quickне вернёт старый. Чтобы перечислить каждую сессию, выпущенную против заказа, используйтеGET /b2b/v1/checkout-sessions/by-order/:order_id.
Endpoint’ы API
| Метод | Путь | Заметки |
|---|---|---|
POST | /b2b/v1/checkout-sessions/quick | Создание заказа + сессии одним вызовом |
POST | /b2b/v1/checkout-sessions | Создание сессии против существующего заказа |
GET | /b2b/v1/checkout-sessions/by-order/:order_id | Список всех сессий для заказа (история повторов) |
GET | /checkout/:session_key | Публичный — то, что вызывает браузер покупателя |
См. Быстрый старт для полного тела запроса создания и подписания.
Что дальше
- Концепции → Заказы — сущность Order (та, которую следует считать источником истины для обработки).
- Концепции → Сети и активы — поддерживаемые сети и предположения окончательности per сеть.
- Webhooks → Обзор — какие события отправляются на каждом переходе состояния сессии.