跳到主要内容

UCP — 结算状态机与错误处理

本章讲一次结算(checkout)从创建到下单的全过程:它的状态怎么流转、平台怎么读懂商家想要什么、金额怎么显示、以及自主 agent 场景下的安全锁定。

1. 它要解决的小问题

结算是个多步过程:缺地址、缺支付方式、要 3DS 验证、要买家点确认……平台(尤其 agent)需要一套确定性的协议来知道“现在卡在哪、下一步该我做什么”。UCP 用一个由商家驱动的状态机回答这个问题——商家设状态,平台读 messages 决定动作。来源:docs/specification/checkout.md:54-59

2. 状态机

+------------+ continue_url +---------------------+
| incomplete |◀────────────────────────▶| requires_escalation |
+-----+------+ | (交回买家:经 |
│ 信息齐了 | continue_url) |
▼ +----------+----------+
+-------------------+ │
| ready_for_complete| 平台可调 Complete │ continue_url
+--------+----------+ │
│ Complete Checkout │
▼ │
+----------------------+ │
| complete_in_progress | │
+----------+-----------+ │
└───────────────┬──────────────────────────────┘

+-----------+ +----------+
| completed | | canceled | (任意态可进:会话失效/过期)
+-----------+ +----------+

这些状态值是闭枚举(天生封闭的状态机,所以这里用 enum 是对的)——见 source/schemas/shopping/checkout.json:65-77。各状态含义见 docs/specification/checkout.md:93-116。状态机原图:docs/specification/checkout.md:60-91

关键规则:商家返回 requires_escalationMUST 提供 continue_url,且 MUST 至少带一条 requires_buyer_inputrequires_buyer_review 的消息。docs/specification/checkout.md:589-598521-525

3. 核心机制:把响应当“优先级错误栈”处理

这是平台开发者最该掌握的一招。每条 messages[] 带一个 severity,它规定了平台该怎么做

severity含义平台动作
recoverable平台改输入就能解改资源 + 重试(Update Checkout)
requires_buyer_input商家要的输入 API 给不了(checkout 仍未完成continue_url 交回买家
requires_buyer_reviewcheckout 已完成,但政策/法规要买家授权continue_url 交回买家
unrecoverable没有可操作的资源用新资源/输入重试,或交回买家

来源:docs/specification/checkout.md:127-141。注意 requires_buyer_inputrequires_buyer_review 都导致交回买家,但代表不同的 checkout 状态(未完成 vs 已完成待审)——这个区分很精细。

处理算法(示意,非源码)

规范给的处理逻辑(docs/specification/checkout.md:207-236)翻成代码长这样。重点看优先级顺序

# 示意,非源码:平台处理 checkout 响应里的 messages
errors = [m for m in response["messages"] if m["type"] == "error"]

if any(e["severity"] == "unrecoverable" for e in errors):
retry_with_new_resource_or_handoff(); return # 最高优先:没救了

recoverable = [e for e in errors if e["severity"] == "recoverable"]
if recoverable:
for e in recoverable:
if "path" in e: # path 定位到具体出错字段
fix_field_at(e["path"]) # 如重排 $.buyer.phone_number
else:
generic_fix(e["code"])
update_checkout(); return # 先把能改的改了再说

# 剩下的才是“必须交回买家”
handoff_via(response["continue_url"])

重点看:先穷尽 recoverable,最后才 handoff——平台应尽量自己解决,别动不动把用户踢去网页。

path 字段是 RFC 9535 JSONPath,相对响应根,把错误精确锚到某字段/某行项(如 $.line_items[0].quantity),便于平台高亮表单字段。docs/specification/checkout.md:259-301

4. Totals 渲染契约:商家权威,平台只渲染

金额展示是个有“政治性”的设计决定:因为多辖区税务、强制费用披露等都是商家的法定义务,所以 totals[] 由商家完全掌控其内容与顺序,平台 MUST NOT 解释、过滤、重排、聚合或自行套展示逻辑docs/specification/checkout.md:824-849

不变量(值得记):

  • 每条带 type + amount;金额是带符号整数,负数=减项(折扣),正数=加项——符号即方向
  • subtotal 恰好一条、total 恰好一条;其余类型可重复(如多辖区税行)。
  • 子行 lines[] 的金额之和 MUST 等于父项金额。

来源:docs/specification/checkout.md:851-913878-892

平台可以核验但不能替换:算出的和对不上 total 时,平台 MUST NOT 改渲染输出,但 MUST NOT 自主完成这笔结算——应拒绝或经 continue_url 请买家复核。docs/specification/checkout.md:861-874

5. 披露式警告:合规内容的强渲染契约

messages 里的 warning 可带 presentation 控制渲染契约。disclosure(如过敏原、Prop 65、能效标签)比默认的 notice 严格得多:

notice(默认)disclosure
显示内容MUSTMUST
靠近 path 所指组件MAYMUST
可关闭MAYMUST NOT
渲染 image_urlMAYMUST
无法满足契约时MUST 经 continue_url 升级

来源:docs/specification/checkout.md:382-441。这把“法定披露必须显示且不可隐藏”的语义直接写进了协议层。

6. AP2:自主 agent 的会话锁定

dev.ucp.shopping.ap2_mandate 扩展协商命中,会话进入 Security Locked

  • 商家 MUST 在所有 checkout 响应里嵌入对条款(价格、line items)的密码学签名(ap2.merchant_authorization)。
  • 平台 MUSTcomplete 时提供签名的 mandate(ap2.checkout_mandate),证明用户授权了这个具体的 checkout 状态。
  • 任何一方都不得退回到无保护的普通流程。

来源:docs/specification/ap2-mandates.md:26-38107-117

所有 AP2 字段统一收纳在一个 ap2 对象下,好处是:商家签名计算时只需一条规则——排除 ap2,未来加 AP2 字段自动适配。docs/specification/ap2-mandates.md:42-53

7. 五个操作 + 幂等

Checkout 能力定义五个逻辑操作(docs/specification/checkout.md:607-681):

操作干什么注意
Create Checkout创建会话用户表达购买意图时调
Get Checkout取当前状态TTL 由商家用 expires_at 决定
Update Checkout整体替换资源平台 REQUIRED 发送完整 checkout 资源(非 patch)
Complete Checkout下单响应回带 order 字段
Cancel Checkout取消非 completed/canceled 的会话应可取消

Update 是全量替换而非增量——这点容易踩坑。状态改变型操作配幂等键防重放/防重复扣款(详见 05 章)。

8. 代码地图

主题文件符号 / 锚点
状态机 + 状态值docs/specification/checkout.md“Checkout Status Lifecycle” §54-116
状态枚举(真值)source/schemas/shopping/checkout.jsonproperties.status.enum
severity 错误栈docs/specification/checkout.md“Error Handling”/“Error Processing Algorithm” §117-236
Totals 渲染契约docs/specification/checkout.md“Total”/“Rendering Contract” §820-959
披露式警告docs/specification/checkout.md“Warning Presentation” §382-441
AP2 锁定docs/specification/ap2-mandates.md“Overview”/“Activation and Session Locking”
checkout 必填/各操作字段source/schemas/shopping/checkout.jsonrequired、各属性 ucp_request(见 06 章)