跳转到内容

LLM 集成指南

本页面向为商家实现 Headless SDK 接入的编码助手(LLM),也供复核这份代码的工程师使用。它说明接入前必须从站点确认哪些事实、以哪份接口为准、代码按什么顺序执行、失败如何判断,以及怎样用浏览器和购物车中的实际结果验收。方法签名、类型和常见错误见 SDK 参考,本页不重复。

先读 接入前指引 和 快速开始,再按本页把接入拆成可验证的单元。每次只改一个单元,在浏览器中观察结果后再进入下一个。把下面这段任务说明连同站点信息交给编码助手,尖括号内容由你替换。

实现 Xcotton Headless SDK 接入。
文档站根地址:<docs-origin>
站点仓库:<repository>
框架与渲染方式:<Next.js App Router / Remix / Nuxt / SvelteKit / 其他>
当前市场:country=<US>,currency=<USD>,language=<en>
店铺能力:SP=<开启或关闭>,PP=<开启或关闭>,SP 适配模式=<cart / checkout / checkoutPlus(文档写作 checkout+),或写“从 getMode() 读取”>
购物车读写:<Shopify Storefront API / 站点 BFF>
已有购物车 ID 的来源:<首次查询、登录后合并、服务端注入等>
站点自有函数:<reloadCartView、写主商品、结账跳转、错误展示>
先读文档站的以下路径:
- /zh/v2/platform-integration/shopify/headless-sdk-prepare/
- /zh/v2/platform-integration/shopify/headless-sdk/
- /zh/v2/platform-integration/shopify/headless-sdk-cart-sync/
- /zh/v2/platform-integration/shopify/headless-sdk-modes/
- /zh/v2/platform-integration/shopify/headless-sdk-api/
约束:
- 只调用 window.xcotton 的公开方法;不直接写费用行或保障行。
- 每次页面加载只调用一次 init;站点独立写车后 await syncCart。pp.addToCart 的回调只写主商品并返回身份,由 SDK 完成同步。
- 不实现或改写 SDK 内部逻辑,不伪造站点函数,不用 mock 数据作为交付。
- 只提交实际验证过的步骤,并列出未验证项。
交付:加载与初始化、SP 结账路径、PP 商品页与购物车路径(按店铺开启情况)、失败处理。
验收:按《LLM 集成指南》的“验收”一节在浏览器中逐项观察。

编码助手不应从旧版本、历史报告或内部注释推断接口。公开方法以 SDK 参考 和交付的 xcotton-types.ts 为准;运行时的适配模式、报价和补购候选分别用 sp.getMode()、sp.getQuote()、pp.getCartQuotes() 读取。

需要确认从哪里得到对代码的影响
店铺域名与 Storefront Public Access Token商家后台、站点环境变量写入 window.xcottonConfig 的 shop 与 publicAccessToken;token 必须能读写购物车
当前市场的 country、currency、language站点市场解析(country、language)与购物车 cost.totalAmount.currencyCode(校验 currency)三个值来自当前站点市场;只有 currency 必须与购物车币种一致,language 只影响界面文案;切换市场要新页面、新配置
SP 与 PP 是否开启商家后台配置;window.xcotton.pp 在 init 后即可检查,window.xcotton.sp 要在首次绑车并同步后检查关闭的业务不生成代码;无车启动时 sp 为空不代表 SP 关闭
SP 适配模式首次购物车同步成功且 sp 存在后调用 sp.getMode()决定结账按钮调用 resolveSP() 还是 addSpFee() 与 declineSpFee()
购物车读写路径站点架构与既有接口决定使用默认 adapter 还是自定义 adapter
购物车 ID 何时可用站点购物车查询、登录合并逻辑启动时已知就传给 init({ cartId });晚到则走 syncCart(cartId)
站点自有函数站点仓库reloadCartView()、写主商品、结账跳转、错误提示都由站点实现,编码助手不能编造实现
面板容器放在哪里页面布局容器选择器要同时写进 xcottonConfig 和页面;容器可晚挂载,SDK 在节点出现后挂载面板,抽屉与 SPA 路由切换同理
验收环境测试店、市场、测试商品真实报价和写车需要可用店铺;生产店铺不做破坏性测试

如果某项无法从代码或商家处确认,先把它列为待确认项,不要用默认值猜。country、currency、language 由站点按当前市场提供,并随配置请求下发;PP 报价也使用这些值。SDK 的币种守卫只校验购物车及其金额字段的币种。country 与买家国家不一致不会被 SDK 拦截,但会让配置与报价属于错误市场;language 只影响界面文案,不改变结算币种。

顺序固定:先让 SDK 可用,再让购物车同步正确,最后接 SP 与 PP。每一步都要有可观察结果。

  1. 先配置,再加载脚本。window.xcottonConfig 必须在 sdk.js 之前声明。SSR 只输出配置和脚本标签,不在服务端访问 window;脚本要在框架 hydrate 前执行。观察:typeof window.xcotton?.init === 'function'。
  2. 放置容器。SP 容器放在购物车页与抽屉,PP 容器放在商品详情页;选择器与配置一致。观察:document.querySelector(selector) 命中,且 SDK 渲染后容器内有面板。
  3. 只初始化一次。每次页面加载调用一次 await window.xcotton.init(options),options 中按需提供 cartId、adapter 和 onCartChange。启动时已有购物车就传 cartId,没有就省略。观察:Promise 成功解析;PP 开启时 window.xcotton.pp 存在;SP 开启且首次绑车同步成功后 window.xcotton.sp 存在。命名空间存在本身不能证明同步成功。
  4. 同步站点写车。站点独立完成加购、改数量、删除、清空或换车后,await window.xcotton.syncCart(cart.id);同步完成前禁用结账和下一次写车。pp.addToCart 已包含同步,其主商品回调与该回调触发的站点事件不要再调用 syncCart。观察:返回的 Cart 与页面一致,onCartChange 内只刷新界面。
  5. 按模式接 SP。cart 与 checkout 在跳转前 await sp.resolveSP();checkout+ 的两个按钮分别 await sp.addSpFee() 与 await sp.declineSpFee()。任何拒绝都不跳转。观察:购物车出现或移除费用行,结账页金额一致。
  6. 按场景接 PP。商品页规格变化时 await pp.loadPlans(variantId),离开商品页时 await pp.loadPlans(null);加购用 pp.addToCart({ addMainProduct })。购物车补购先 getCartQuotes(),再用 offerProtection() 或 addProtection(),移除用 removeProtection({ mainLineId })。观察:主商品只写一次,保障数量等于同规格同方案的被保主行数量之和。
  7. 收口失败路径。环境初始化失败重载页面;首次同步失败用 syncCart(cartId);操作冲突等当前操作结束;写入结果未知先读车。观察:失败不跳转、不重复写、不自动重试。

在 SSR 框架中,配置和脚本标签由服务端按当前市场输出,SDK 仍在浏览器执行。容器可以在页面渲染后再挂载,SDK 会观察节点并挂载面板;抽屉晚开或 SPA 路由切换也按同一机制恢复。整页刷新会清空 window,需要重新 init;SPA 路由切换保留已初始化的命名空间。离开商品页时调用 loadPlans(null),不要靠页面卸载代替。

默认 adapter 直接调用 Shopify Storefront API,适合大多数站点。只有购物车读写必须经过站点 BFF、服务端鉴权或风控时,才实现 XcottonCartAdapter。选择依据是站点现有接口,不是偏好。

场景选择交付要求
站点已有 Storefront token,购物车直接用 Storefront API默认 adapter不传 adapter;不要包装一层空实现
所有读写必须经过 BFF自定义 adapter实现 getCart、addLines、updateLines、removeLines、updateAttributes;返回完整 Cart
只有部分方法走 BFF部分覆盖未覆盖方法回退默认实现;两边必须操作同一购物车、同一行 ID;行 ID 不同则必须完整实现

自定义 adapter 必须满足以下要求:

  • 返回操作后的完整 Cart,包含全部分页与报价字段,lines.pageInfo.hasNextPage 为 false。
  • 保留 cart.id、行 ID、金额与币种的原值;不换算币种,不把 0 元改成其他金额。
  • 网络、鉴权、限流失败必须 reject;不要把失败变成 null。限流错误至少让 status: 429、code: 'THROTTLED' 或 message 中的 429 / THROTTLED 之一成立,并尽量带上 retryAfterMs;缺省时 SDK 按 30 秒冷却。
  • 行属性省略或传 null 表示保留,传 [] 表示清空,传非空数组表示整体替换;updateAttributes 接收完整属性数组。
  • 不吞错、不自动重试、不伪造行 ID。测试替身只能用于测试,不能进入站点运行路径。

编码助手不得生成一个只返回假数据的 adapter 来让页面看起来能用。站点没有 BFF 时,使用默认 adapter。

SP 的入口由适配模式决定。模式用 sp.getMode() 读取,不要从页面控件或旧版本推断;返回值 'checkoutPlus' 对应文档中的 checkout+。三种模式的差别见 适配模式、Checkout 适配模式 与 Checkout+ 适配模式。

顾客动作调用说明
购物车面板开关(cart)SDK 内部写车站点不额外写费用行;结账前仍要 resolveSP()
点击结账(cart、checkout)await sp.resolveSP()成功结果的 action 为 added、removed 或 skipped;拒绝则不跳转
含保护结账(checkout+)await sp.addSpFee()返回 { added: true, extId };不可保时拒绝
不含保护结账(checkout+)await sp.declineSpFee()清费并写拒绝标记;不要直接跳转
展示保障价格await sp.getQuote()只读;零元报价是有效结果,不可保会拒绝
打开保障说明await sp.showModal()需要已挂载的 SP 面板

不要在 checkout+ 调用 resolveSP();它没有开关意愿,会以 INVALID_ARGUMENT 拒绝。不要自行读写费用行,也不要用 OpenAPI 重复写同一份保障。

PP 分商品页与购物车两段。商品页的报价与选择不依赖购物车;写保障行需要已同步的购物车。场景细节见 接入延保险 与 购物车加购保险按钮。

  • 商品页:const { context, options } = await pp.loadPlans(variantId)。options 是本次报价可提交的候选;context 可省略,省略时使用调用开始时的全局上下文,显式传入时不能手工构造或跨商品复用。离开商品页调用 loadPlans(null)。
  • 加购:await pp.addToCart({ addMainProduct })。回调只写主商品,接收 (attributes, context),返回 { cartId, mainLineId };回调内不要调用会取得购物车权限的 SDK 根方法(如 init、syncCart、pp.addToCart),也不要读页面当前规格。顾客取消方案弹窗时仍会加主商品,结果 protection: 'skipped'。
  • 手动链:getSelectedPlan()、站点写主商品、syncCart(cartId)、addProtection({ mainLineId, selection })。它与 addToCart() 二选一,不要在同一流程里混用。
  • 购物车补购:先 await pp.getCartQuotes(),按 mainLineId 匹配行;offerProtection({ mainLineId }) 由 SDK 弹窗,只有一个方案也弹窗;addProtection({ mainLineId, selection }) 提交站点自绘列表的原始候选。移除保障用 removeProtection({ mainLineId }),传主商品行 ID,不传保障行 ID。

不要用商品名或方案名关联行;不要根据 mainVariantId 推断 mainLineId;不要自己拼 PPSelection 或写 _PlanID。可提交的 PPSelection 来自 SDK 当前报价或选择结果:loadPlans() 与 getCartQuotes() 的 options,以及 getSelectedPlan() 与 showModal() 返回的非空结果。具体规则见 SDK 参考。

SDK 公开方法失败为 XcottonError,包括未初始化时的 SDK_NOT_INITIALIZED。站点函数、sdk.js 加载失败或 window.xcotton 尚未存在时的普通异常不适用该守卫;先检查 window.xcotton,对 SDK 错误用 window.xcotton.isXcottonError(error) 收窄,再按 code 分支,其他错误按普通异常处理。message 只用于日志,不要按文案分支。

code含义处理
SDK_NOT_INITIALIZEDSDK 环境尚未就绪尚未初始化时调用一次 init;已开始时等待共享的初始化 Promise,并处理其失败
SDK_INIT_IN_PROGRESSinit() 仍在进行不要再次 init;等待第一次调用的 Promise 完成,站点应共享同一个 Promise
SDK_ALREADY_INITIALIZEDinit() 已结束或已失败;phase 可能是 ready、environment_failed、sync_failed、busy不要再次 init;按 phase 重载页面或 syncCart
OPERATION_IN_PROGRESS另一项购物车操作在进行等当前操作结束,再决定是否同步
CART_NOT_BOUND首次同步尚未完成取得购物车后 syncCart(cartId)
CART_CURRENCY_MISMATCH购物车币种与初始化 currency 不一致切到当前市场的购物车与配置
PP_CONTEXT_CHANGED商品上下文已切换或报价被替代用最新 loadPlans() 结果,不重试旧调用
PP_SELECTION_IN_PROGRESS另一 PP 选择或购买流程在进行等当前流程结束
PP_NO_OPTIONS没有可用方案,或补购开关关闭不弹窗、不写车;按场景隐藏入口
PP_ALREADY_PROTECTED主行已有实际保障行刷新购物车,隐藏补购入口
SP_NOT_AVAILABLE当前购物车不可保,原因在 context.reason不跳转;按 recovery 提示调整购物车,或继续不含保护
SP_COVERAGE_LIMIT_EXCEEDED购物车总额超过保额上限不跳转;按 recovery 提示顾客减少金额,或继续不含保护
SP_PANEL_REQUIRED没有已挂载的 SP 面板先挂载容器,再调用 showModal()
ADAPTER_RATE_LIMITED购物车请求处于冷却期按 context.retryAfterMs 等待;没有该字段时按 30 秒

写车失败不一定没有生效。读取 context.writeOutcome:not_sent 表示请求尚未发送,not_applied 表示明确拒绝且未生效,partial 表示已确认部分写入,unknown 表示无法确认写入结果。partial 与 unknown 不要自动重试;先读车核对。pp.addToCart 的 PP_WRITE_CONFIRMATION_FAILED 还带有 stage、mainProduct、protection、writeOutcome:mainProduct 可能是 added,也可能是 not_sent、unknown 等写入状态;不要把 unknown 说成已加入,也不要把回调未执行说成主商品已成功。

结账只以对应 API 的 Promise 为准。onCartChange 是已确认事实的通知,不是成功信号;它可能在失败后仍因补读成功而触发。无车 init() 成功只证明环境就绪;带 cartId 的 init 成功还包含该车的首次同步。

只使用实际返回的 code、context 与可用的 cause。上游没有给出的细分原因、写入结果或回调执行状态,不要推断,也不要在日志或界面里写成已发送、已成功。

验收以观察到的结果为准:购物车中的行、Network 请求、Promise 的 code 与 context、页面跳转。编译通过、类型检查通过或截图能渲染,都不等于接入完成。

检查操作通过标准
加载打开页面,查看 Network 与 Consolesdk.js 成功加载;window.xcotton.init 可用;无未处理异常
初始化打开页面并让多个组件挂载init 只执行一次;重复挂载的组件共享同一个 Promise,不会各自触发 init;sdk.js 只加载一次
命名空间PP 开启时检查 window.xcotton.pp;SP 开启且有购物车时检查 window.xcotton.sppp 在 init 后存在;sp 在带 cartId 的首次同步后存在;无车启动时 sp 缺失不代表 SP 关闭,按后台配置判断
同步站点加购、改数量、删除、清空每次独立写车后 await syncCart(cart.id);pp.addToCart 由 SDK 同步;返回的 Cart 与页面一致;onCartChange 不触发同步循环
SP cart、checkout拨动开关,点击结账费用行按意愿写入或移除;resolveSP() 成功后才跳转;拒绝时不跳转
SP checkout+分别点击两个结账按钮含保护写入费用行,不含保护移除并写拒绝标记;resolveSP() 不被调用
PP 商品页切换规格,选中方案,点击加购面板只展示最新规格;主商品写入一次;有选择时保障数量等于同规格同方案的被保主行数量之和;取消弹窗仍加主商品且 protection 为 skipped
PP 购物车读取候选,补购,移除候选按 mainLineId 匹配;单方案也弹窗;取消零写入;移除后主商品保留
失败路径模拟网络失败、限流、上下文切换不跳转、不重复写;writeOutcome 为 partial 或 unknown 时先读车;冷却期内不重试
视口桌面与移动宽度各走一遍面板、按钮与弹窗可操作;容器在抽屉和页面中都能挂载

最后,把未验证项和限制写进交付说明。真实写车只在测试店验证;不要在生产店铺做清空购物车、重复加购或删除保障等破坏性测试。