Checkout extension 参考
本页是 Checkout Extension 的查阅手册:全部扩展点、CheckoutAPI 数据接口、extend 函数与 HTML 模板机制。开发流程见 创建一个结账扩展;实战场景见 Checkout extension 场景教程。
扩展点(Extension Points)
扩展点决定自定义内容插入到页面的具体位置。在 extend() 的 extensionPoint 字段中指定对应名称,内容即会渲染在该位置。
结账页
| 扩展点 | 位置说明 |
|---|---|
Checkout::RenderBefore | 页面头部之前 |
Checkout::RenderAfter | 页面底部之后 |
Checkout::Head::RenderAfter | 页面头部之后 |
Checkout::FilledInformation::RenderAfter | 已填写信息卡片之后 |
Checkout::SpecialInstruction::RenderAfter | 特殊说明之后 |
Checkout::Summary::RenderBefore | 订单摘要之前 |
Checkout::Navigate::RenderBefore | 导航栏之前 |
Checkout::Navigate::RenderAfter | 导航栏之后 |
Checkout::ContactInformation::RenderBefore | 联系信息模块之前 |
Checkout::ContactInformation::RenderAfter | 联系信息模块之后 |
Checkout::ShippingLinesTitle::RenderBefore | 配送列表标题之前 |
Checkout::ShippingLinesTitle::RenderAfter | 配送列表标题之后 |
Checkout::ShippingList::RenderAfter | 配送列表之后 |
Checkout::ProductList::RenderBefore | 商品列表之前 |
Checkout::ProductList::RenderAfter | 商品列表之后 |
Checkout::Reductions::RenderBefore | 优惠券/礼品卡之前 |
Checkout::Reductions::RenderAfter | 优惠券/礼品卡之后 |
Checkout::SectionPayment::RenderBefore | 支付模块之前 |
Checkout::SectionPayment::RenderAfter | 支付模块之后 |
感谢页
| 扩展点 | 位置说明 |
|---|---|
Checkout::ThankyouHeader::RenderBefore | 感谢页头部之前 |
Checkout::ThankyouContent::RenderBefore | 感谢页内容区域之前 |
关于扩展点的对称性: 部分模块只提供
RenderBefore或RenderAfter中的一个(例如ShippingList只有RenderAfter),是因为另一侧紧贴上层标题或没有清晰边界,注入易引发布局错乱。
CheckoutAPI 参考
CheckoutAPI 挂载在 window.CheckoutAPI 上,提供对结账数据的读取与事件监听能力。
CheckoutAPI 在页面加载完成后才可用:直接访问 window.CheckoutAPI 可能得到 undefined,需等待挂载后再调用。建议使用 setTimeout + 最大重试次数轮询等待(约 5 秒),参见 场景教程 中「API 调用与事件监听」一节的 mount 函数实现;不推荐 requestAnimationFrame(60fps 忙等会占用结账页 CPU)。
订单信息(store)
interface OrderInfo {
currencyCode: string; // 结账货币,如 "USD"
currencySymbol: string; // 货币符号,如 "$"
alreadyPaymentLines: AlreadyPaymentLines;
failCode: string;
id: string;
status: OrderStatus;
checkoutStatus: string;
financialStatus: string;
orderNo: string;
cancelReason: string;
exceptionError?: string; // 仅感谢页可用
exceptionErrorMessage?: string; // 仅感谢页可用
additionalPrices?: AdditionalPrice[];
}
type OrderStatus = 'opened' | 'placed' | 'cancelled' | 'finished';
// 获取订单信息
CheckoutAPI.store.getOrderInfo(): OrderInfo;
// 获取订单状态
CheckoutAPI.store.getOrderStatus(): OrderStatus;
// 获取参考信息
CheckoutAPI.store.getReferInfo(): ReferInfo;
商品信息(summary)
interface UIProduct {
id: string;
variantId: string;
productTitle: string;
properties: ProductItem['properties'];
discountApplications: ProductItem['discountApplications'];
options: ProductItem['options'];
isFreeGift: boolean;
quantity: ProductItem['quantity'];
linePrice: ProductItem['linePrice'];
discountTotal?: ProductItem['discountTotal'];
finalLinePrice?: ProductItem['finalLinePrice'];
compareAtPrice: ProductItem['compareAtPrice'];
price: ProductItem['price'];
coverUrl: string;
}
// 获取商品列表(包含 properties 字段的 line items)
CheckoutAPI.summary.getProductList(): UIProduct[];
价格信息(store)
interface CheckoutPrices {
subtotalPrice: string;
shippingPrice: string;
taxPrice: string;
discountCodePrice: string;
discountPrice: string;
totalPrice: string;
discountLineItemPrice: string;
totalTipReceived: string;
discountShippingPrice: string;
giftCardPrice: string;
paymentDue: string; // 待支付金额
paidTotal: string;
discountSubTotal?: string;
shippingTaxTotal?: string;
allTaxTotal?: string;
paymentDiscountTotal?: string;
prePaymentAmount?: string;
additionalPrices?: AdditionalPrice[];
}
// 获取价格信息
CheckoutAPI.store.getPrices(): CheckoutPrices;
// 监听价格变化
CheckoutAPI.store.onPricesChange(cb: (prices: CheckoutPrices) => void): void;
// 移除价格变化监听
CheckoutAPI.store.removePricesChangeCb(cb: (prices: CheckoutPrices) => void): void;
价格字段格式:所有价格字段为字符串形式的小数(主单位),例如
"10.99"(10.99 美元),不是最小单位(不是"1099"美分)。计算时建议用Decimal.js等库避免 JavaScript 浮点误差。
用户信息(user)
// 判断用户是否已登录
CheckoutAPI.user.isLogin(): boolean;
// 跳转到登录页,可附加 return URL 参数
CheckoutAPI.user.doLogin(returnUrlSearchParams?: Record<string, string>): void;
// 跳转到注册页
CheckoutAPI.user.doRegister(): void;
// 退出登录
CheckoutAPI.user.doLogout(): Promise<void>;
// 获取已登录用户信息
CheckoutAPI.user.getUserInfo(): UserInfo;
地址信息(address)
interface AddressValues {
id?: string;
firstName: string;
lastName: string;
countryCode: string;
country: string;
provinceCode: string;
province: string;
area: string;
city: string;
address: string;
address1: string;
zip: string; // 邮政编码
cpf?: string; // 税号
taxText?: string; // 税号名称
idNumber?: string; // 身份证号
idNumberText?: string; // 身份证号名称
company: string;
latitude?: string;
longitude?: string;
phone: string;
emailOrPhone: string;
email: string;
phoneAreaCode: string;
// 兼容不同国家/地区可能扩展的自定义地址字段(如各国不同税号),只读取上述标准字段时可忽略
[key: string]: string | undefined;
}
// 获取收货地址
CheckoutAPI.address.getShippingAddress(): AddressValues;
// 监听收货地址变化(回调参数为本次变化的字段,非完整地址对象)
CheckoutAPI.address.onShippingAddressChange(cb: (patch: Partial<AddressValues>) => void): void;
// 移除收货地址变化监听
CheckoutAPI.address.removeShippingAddressChangeCb(cb: (patch: Partial<AddressValues>) => void): void;
步骤控制(step)
type CheckoutStep = 'contact_information' | 'shipping_method' | 'payment_method';
// 获取当前步骤
CheckoutAPI.step.getStep(): CheckoutStep;
// 跳转到联系信息步骤
CheckoutAPI.step.stepNavToInformation(): Promise<void>;
// 跳转到配送信息步骤
CheckoutAPI.step.stepNavToShipping(): Promise<void>;
// 跳转到支付信息步骤
CheckoutAPI.step.stepNavToPayment(): Promise<void>;
// 监听步骤变化
CheckoutAPI.step.onStepChange(cb: () => void): void;
// 移除步骤变化监听
CheckoutAPI.step.removeStepChangeCb(cb: () => void): void;
// 跳转到指定 URL
CheckoutAPI.step.locationHref(url: string): void;
// 跳转到首页
CheckoutAPI.step.goToHomePage(): void;
extend 函数与 HTML 模板
extend 函数
extend 是将内容注册到扩展点的核心函数,从 shoplazza-extension-ui 引入:
import { extend } from 'shoplazza-extension-ui';
extend({
extensionPoint: 'Checkout::Navigate::RenderBefore',
component: '<h1>Hello, Shoplazza!</h1>',
});
component 接收 HTML 字符串,渲染到对应扩展点位置。
⚠️ 安全提示(XSS 风险):
component是直接渲染的 HTML 字符串,不要把未转义的用户输入或外部 API 返回值拼接进去,否则会导致 XSS 注入。任何动态内容请先做 HTML escape,或改用createElement/textContent等 DOM API 构造。多次调用同一扩展点: 同一个
extensionPoint可以被多次extend,内容按调用顺序追加渲染,而不是覆盖。
HTML 模板
对于内容较复杂的扩展,推荐将 HTML 拆分到独立文件中管理。脚手架默认生成以下文件结构:
src/index.html(可选,组装入口):
<div>
import './style.html'
import './content.html'
import './script.html'
</div>
import './xxx.html'不是 ES Module 语法。 它是shoplazza-cli在构建时识别的文本拼接指令:在打包阶段,CLI 会把import './xxx.html'这一行原地替换为./xxx.html文件的内容(按出现顺序内联),而不是运行时动态加载。
- 该语法只能写在
src/index.html顶层作为组装入口;写在<script>内部或 JS 文件里都不会被识别。- 被 import 的 HTML 片段不应再写
import,避免递归。- 内容简单或需要动态生成 HTML 时,可以跳过模板文件,直接在
index.js中拼接字符串传给component。
src/style.html(可选,样式):
<style>
.my-block {
padding: 12px;
background: #f5f5f5;
}
</style>
src/content.html(可选,页面内容):
<div class="my-block">
<h2>Hello, Shoplazza!</h2>
</div>
src/script.html(可选):脚手架占位,若要在 HTML 模板片段内编写脚本,直接使用普通 <script> 标签即可:
<script>
document.querySelector('.my-block').addEventListener('click', function() {
window.CheckoutAPI.step.goToHomePage();
});
</script>
也可以不使用 HTML 模板,直接在 index.js 中构造 HTML 字符串传入 component,适合内容简单或需要动态生成的场景。
结账页性能直接影响转化率,扩展 JS / CSS 应尽量精简:单个扩展 gzip 后建议 < 30KB,避免引入大型依赖;优先用 setTimeout / IntersectionObserver 等被动方式而不是高频轮询。