会话
CheckoutSession 是买家交互的对象 — 带 TTL、一次性,拥有托管结账 URL。它是三大核心实体里最薄的;更重的工作发生在 Orders 和 Payment Intents(见下文)。
三实体数据模型
CheckoutSession ← 1:1 → Order ← 1:N → PaymentIntent
(buyer) (catalog) (each pay attempt)| 实体 | 用途 | 生命周期 |
|---|---|---|
| CheckoutSession | 买家向外 — 有 session_key、checkout_url、TTL | 数分钟(默认 30) |
| Order | 你的目录状态 — 明细项、总额、退款 | 永久记录 |
| PaymentIntent | 一条链/资产上的一次支付尝试 | 数小时;结算或过期 |
你通过 POST /b2b/v1/checkout-sessions/quick 同时创建一个会话和
一个订单。每次买家在结账页上挑一种资产时,都会针对对应的链打开一个
全新的 PaymentIntent。如果他们中途切换资产,之前的 intent 进入
EXPIRED,新 intent 开始。
标识符
会话由其 session_key 标识:
cst_G-SO92J7HNWkwMHEHjD4oO1ZURL 安全,前缀之后约 24 字符(18 个随机字节做 base64url 编码,无 padding)。
托管结账页是 https://checkout.infraio.xyz/<session_key> — 把 session_key
当作那一次结账的 bearer 凭据对待。
生命周期 — CheckoutSession
| 状态 | 含义 |
|---|---|
ACTIVE | 会话已创建并开放。结账 URL 可用。 |
COMPLETED | 该会话上的某条 PaymentIntent 已结算。Order 现在是 PAID(欠付则 PARTIAL_PAID)。 |
EXPIRED | expires_at 已过且未结算。清理 worker 翻转状态并取消了任何打开的 Order。 |
CANCELED | 显式取消 — 要么买家点了”取消”,要么你调用了取消端点。 |
三个终态互斥且终局。如果你想重试(例如欠付后),可以对同一 Order 创建一个新的 CheckoutSession。
TTL
- 默认: 30 分钟(可通过 create 上的
expires_in字段配置,单位秒)。 - 边界: 服务端没有强制的硬性 min/max。请使用合理值 — 低于 60 秒可能让合法买家超时;超过 7 天则在几乎肯定已被放弃的 token 上占用容量。挑一个匹配你买家决策窗口的数字。
- 每商户默认: 可通过仪表板配置,但只有传统的
POST /b2b/v1/checkout-sessions(两步)路径会遵守它。POST /b2b/v1/checkout-sessions/quick路径在省略expires_in时 总是回落到 30 分钟,不论每商户设置如何。如果你需要在 quick 路径上有不同的默认值,请在每次调用时显式传expires_in。 - 执行: 读取时惰性 + 周期性清理 worker。一个
expires_at已过 的会话即便状态字段还没写入,也会被视为EXPIRED,所以不要在 到期的精确时刻依赖通过 API 读取状态。
欠付
如果买家发送的金额少于会话金额,PaymentIntent 仍然按部分金额结算,
Order 转为 PARTIAL_PAID。CheckoutSession 进入 COMPLETED
(一条 PaymentIntent 已结算),所以它不再可重用。
要把缺额视为全额支付,可以把订单解决为 PAID — 见
概念 → 订单(目前没有公共 resolve API;
自助流程更建议改为收齐剩款)。要收齐剩款,对同一 Order 用剩余金额
创建新的 CheckoutSession。
超付
如果买家发送的金额超过会话金额(罕见,但人工转账时会发生),链上 扫描器会在 24 小时内捕获超付并发出商户警报。没有自动退款 — 通过 退款 API 或仪表板手动发起。
错资产支付
充值地址按 (session, chain, asset) 三元组生成。如果买家把错资产
发到地址,链上匹配器认不出,PaymentIntent 一直 open 到 TTL 到期。
我们可以协助找回资金,但走的是支持流程,不是自动 — 请引导买家发送
结账页面显示的精确资产。
create 上的幂等
POST /b2b/v1/checkout-sessions/quick 接受请求 body 中的 idempotency_key
字段(注意:body 字段,不是 HTTP 标头)。如果你的客户端没有天然
key,可以为它自动生成一个 UUID — SDK 默认这样做。
这个 key 去重的是 Order,而不是 CheckoutSession。用同一个 key 重试时,
/quick 返回 原始 order(order_id 稳定),但每次都铸造一个全新的
CheckoutSession — 每次都是新的 session_key 和 checkout_url。这是
有意为之:一个 Order 可以背靠多次结账尝试(见
订单),所以重试的 /quick 会在不重复 order
的前提下给买家一个干净的会话。
有两点行为与 Stripe 风格的幂等层不同 — 别被坑到:
- body 不会被哈希或比较。 用 不同 的 body 复用一个 key 不会
返回
409— 服务端会静默返回该 key 名下已存的 order,并忽略新的 body。所以请把idempotency_key当作单个逻辑 order 的一次性令牌; 绝不要跨不同购物车回收使用。 - 只有 Order 被去重,会话不会。 如果你需要拿回 同一个 结账 URL,
请把第一次响应里的
session_key/checkout_url持久化下来 — 再次 调用/quick不会返回旧的。要枚举某个 order 名下铸造过的每一个会话, 用GET /b2b/v1/checkout-sessions/by-order/:order_id。
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 | 公共 — 买家浏览器命中的端点 |
完整 create 请求 body 与签名见 快速入门。
下一步
- 概念 → 订单 — 作为履约真相之源的 Order 实体。
- 概念 → 链与资产 — 受支持的网络以及 每条链的终局性假设。
- Webhooks → 概览 — 每个会话状态迁移 时哪个事件触发。