跳转到内容

Checkout 适配模式

在 checkout 模式下,顾客拨动面板开关时不会立即改动购物车,SDK 仅记录顾客的选择并刷新报价。当顾客点击结账按钮时,站点调用 resolveSP(),由 SDK 按当前开关意愿一次性写入或移除费用行,完成后再跳转到结账页。

  • window.xcotton.sp.getMode() 返回 'checkout'。
  • 已按 快速开始 完成初始化,且在结账按钮可点击前已完成当前购物车的同步(即 init({ cartId }) 或 syncCart(cartId) 已解析完成)。
  • 站点提供以下两个函数(示例代码从 lib/cart.ts 导入):
    • getCheckoutUrl():返回当前购物车的结账跳转地址。
    • showCheckoutError(error):在界面上提示错误,并将顾客留在当前页。
  1. 在结账按钮的点击处理函数中调用 await window.xcotton.sp.resolveSP()。
  2. 仅在解析成功后跳转到结账页。
  3. 若 Promise 拒绝(reject),切勿跳转:在页面展示错误提示并让顾客留在当前页。拒绝表示 SDK 无法确认费用行已达到意愿要求,强行跳转会让结账页的保障状态与顾客的选择不符。若 code 是 OPERATION_IN_PROGRESS,说明拨动开关的处理或其他购物车操作仍在进行,等它结束后重试 resolveSP();这种情况不代表报价或费用行失败。
  4. 在请求处理期间锁定按钮,忽略重复点击。
  5. 请勿自行读取开关状态再去调用 addSpFee()——开关状态由面板内部维护,resolveSP() 已按开关意愿处理所有分支。只有站点自绘开关时才需要直接调用 addSpFee() / removeSpFee()。

resolveSP() 返回动作结果,站点据此决定是否继续导航:

返回含义
{ action: 'added', extId }本次写入或调整了费用行
{ action: 'removed' }本次移除了费用行
{ action: 'skipped', protection: 'included', extId }费用行已符合意愿(必要标记仍可能写入)
{ action: 'skipped', protection: 'excluded' }顾客不要保障且车上没有费用行
  • 意愿为开但当前订单不可保时 Promise 会拒绝,code 为 SP_NOT_AVAILABLE,context.reason 说明具体原因(包括金额超出可保区间或保额上限)。不要跳转,也不要把它当作「顾客不要保障」。需要区分是否超过保额上限时,先调用 getQuote():超额时它以 SP_COVERAGE_LIMIT_EXCEEDED 拒绝并带出金额、额度与币种。
  • 本次明确拒绝请使用 declineSpFee()(清费 + 拒绝标记);checkout 模式下它保留顾客拨过的开关意愿。拒绝完成后不要再串接 resolveSP()。
components/checkout-button.tsx
'use client'
import { xcottonInit } from '@/lib/xcotton'
import { getCheckoutUrl, showCheckoutError } from '@/lib/cart'
let checkoutPending = false
async function onCheckoutClick() {
if (checkoutPending) return
checkoutPending = true
try {
await xcottonInit()
const sp = window.xcotton?.sp
if (!sp) throw new Error('[XC] SP is not ready')
await sp.resolveSP()
window.location.assign(getCheckoutUrl())
} catch (error) {
showCheckoutError(error)
} finally {
checkoutPending = false
}
}
export function CheckoutButton() {
return (
<button
type="button"
onClick={() => void onCheckoutClick().catch(console.error)}
>
Checkout
</button>
)
}
async function protectionPrice() {
await xcottonInit()
const sp = window.xcotton?.sp
if (!sp) throw new Error('[XC] SP is not ready')
const quote = await sp.getQuote()
return quote.totalPrice
}

示例中的 protectionPrice() 为可选辅助函数,演示如何调用 getQuote() 读取报价。是否把价格渲染到结账按钮文案由站点决定。报价结构详见 SpQuote。

操作预期结果
拨动面板开关购物车行不发生变化
打开开关后点击结账写入费用行,随后跳转至结账页
关闭开关后点击结账移除费用行,随后跳转至结账页
首次结账请求完成前再次点击结账仅发出一次 resolveSP() 请求
拨动开关后立即点击结账(开关处理尚未结束)resolveSP() 以 OPERATION_IN_PROGRESS 拒绝;等开关处理结束后重试,期间不跳转
关闭开关后,模拟移除失败再点击结账页面展示错误提示且不跳转;费用行是否已移除以错误的 writeOutcome 为准,刷新购物车后重试
意愿为开但订单不可保resolveSP() 拒绝并给出具体原因,页面展示提示且不跳转
现象原因与处理方法
报错 [XC] SP is not ready示例在 window.xcotton.sp 为空时抛出。sp 在购物车同步期间 SP 初始化成功后挂载;同步失败时也可能已挂载,因此结账按钮应以 init / syncCart 的 Promise 成功为准,不能只看 sp 是否存在
报错 [XC] Another SDK operation is already in progress (activeOperation=sp.toggle, …)拨动开关的处理或其他购物车操作尚未结束。等它结束再重试 resolveSP(),不要跳转
点击结账后购物车中仍无费用行确认传给 syncCart 的是 Storefront 购物车 ID(形如 gid://shopify/Cart/...),且站点每次改车后均调用了 syncCart(cart.id)
报错 [XC] Cart requests are rate limited触发 Shopify 接口限流。SDK 在冷却期内(按 adapter 返回的等待时间,默认 30 秒)会拒绝购物车请求,请稍后重试
报错 [XC] cart currency does not match the configured market (…)购物车币种与 window.xcottonConfig.currency 不一致,结账前同步被拒绝