Skip to Content
概念会话

会话

CheckoutSession 是买家交互的对象 — 带 TTL、一次性,拥有托管结账 URL。它是三大核心实体里最薄的;更重的工作发生在 OrdersPayment Intents(见下文)。

三实体数据模型

CheckoutSession ← 1:1 → Order ← 1:N → PaymentIntent (buyer) (catalog) (each pay attempt)
实体用途生命周期
CheckoutSession买家向外 — 有 session_keycheckout_url、TTL数分钟(默认 30)
Order你的目录状态 — 明细项、总额、退款永久记录
PaymentIntent一条链/资产上的一次支付尝试数小时;结算或过期

你通过 POST /b2b/v1/checkout-sessions/quick 同时创建一个会话和 一个订单。每次买家在结账页上挑一种资产时,都会针对对应的链打开一个 全新的 PaymentIntent。如果他们中途切换资产,之前的 intent 进入 EXPIRED,新 intent 开始。

标识符

会话由其 session_key 标识:

cst_G-SO92J7HNWkwMHEHjD4oO1Z

URL 安全,前缀之后约 24 字符(18 个随机字节做 base64url 编码,无 padding)。 托管结账页是 https://checkout.infraio.xyz/<session_key> — 把 session_key 当作那一次结账的 bearer 凭据对待。

生命周期 — CheckoutSession

状态含义
ACTIVE会话已创建并开放。结账 URL 可用。
COMPLETED该会话上的某条 PaymentIntent 已结算。Order 现在是 PAID(欠付则 PARTIAL_PAID)。
EXPIREDexpires_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_keycheckout_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 与签名见 快速入门

下一步