Сессии
CheckoutSession — это то, с чем взаимодействует покупатель: одноразовый объект с ограниченным временем жизни, владеющий 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 символов после префикса. Размещённая страница
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 прошло без расчёта. Все открытые Order’ы отменяются. |
CANCELED | Явная отмена — либо покупатель нажал «cancel», либо вы вызвали endpoint отмены. |
Три терминальных состояния взаимоисключающие и финальные. Новая CheckoutSession против того же Order может быть создана, если вы хотите повторить (например, после недоплаты).
TTL
- По умолчанию: 30 минут (настраивается через поле
expires_inпри создании, в секундах). - Границы: Минимум и максимум не ограничены. Выбирайте значение, соответствующее ожидаемому окну принятия решения покупателем: менее 60 секунд рискует выйти за тайминги для легитимных покупателей, а сессия, остающаяся открытой более 7 дней, почти наверняка заброшена.
- Дефолт per-merchant: Можно задать в панели, но соблюдает его только
POST /b2b/v1/checkout-sessions(двухшаговый).POST /b2b/v1/checkout-sessions/quickиспользует 30 минут, еслиexpires_inопущен, независимо от настройки в панели. Чтобы на/quickиспользовать другой дефолт, отправляйтеexpires_inпри каждом вызове. - Проверка: Сессия, у которой
expires_atпрошло, трактуется какEXPIRED, даже если её состояние ещё не обновилось, поэтому не полагайтесь на значение состояния в точный момент истечения.
Недоплата
Если покупатель отправляет меньше суммы сессии, PaymentIntent всё равно
рассчитывается на частичную сумму, а Order переходит в PARTIAL_PAID.
CheckoutSession переходит в COMPLETED (один PaymentIntent рассчитан),
поэтому она больше не пригодна к повторному использованию.
Чтобы принять недоплату как полную оплату, заказ можно разрешить в
PAID См. Концепции → Заказы (разрешение недоступно
через API; чтобы остаться в self-serve, вместо этого собирайте остаток).
Чтобы собрать остаток, создайте новую CheckoutSession
против того же Order на остаточную сумму.
Переплата
Если покупатель отправляет больше суммы сессии (редко, но случается при ручных переводах), InfraIO Pay обнаруживает переплату в течение 24 часов и уведомляет вас. Автоматически она не возвращается. Выпустите возврат через API возвратов или панель.
Платежи неверным активом
Депозитный адрес генерируется для одной сессии, сети и актива. Если покупатель отправит на него другой актив, платёж не будет сопоставлен, и PaymentIntent останется открытым до истечения сессии. Поддержка может помочь восстановить средства, но это не автоматика. Говорите покупателям отправлять ровно тот актив, который показан на странице checkout.
В TRON, Solana и TON нет депозитного адреса, поэтому это относится только к EVM-сетям: покупатель платит напрямую на ваш кошелёк. См. Сети с оплатой напрямую на кошелёк.
Идемпотентность при создании
POST /b2b/v1/checkout-sessions/quick принимает поле
idempotency_key в теле запроса (заметьте: поле тела, а не
HTTP-заголовок). Сгенерируйте UUID, если у вас нет естественного ключа.
Ключ дедуплицирует Order, а не CheckoutSession. При повторе с тем
же ключом /quick возвращает оригинальный заказ (order_id
стабилен), но создаёт свежую CheckoutSession — новый session_key
и checkout_url каждый раз. Это намеренно: один Order может стоять за
несколькими попытками checkout (см. Заказы),
поэтому повторный /quick отдаёт покупателю чистую сессию без
дублирования заказа.
Два поведения, о которых нужно знать:
- Тело не хэшируется и не сравнивается. Повторное использование
ключа с другим телом не возвращает
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 | Список всех сессий для заказа (история повторов) |
См. Быстрый старт для полного тела запроса создания и подписания.
Что дальше
- Концепции → Заказы — сущность Order (та, которую следует считать источником истины для обработки).
- Концепции → Сети и активы — поддерживаемые сети и предположения окончательности per сеть.
- Webhooks → Обзор — какие события отправляются на каждом переходе состояния сессии.