# 结账页定制

> 在应用里定制 Shoplazza 结账页：在结账扩展点渲染自己的 UI，并用 CheckoutAPI 读写订单。

结账页是顾客下单流程的最后一步：顾客在这个页面填联系方式和收货地址、选配送方式、付款。结账页定制就是应用改动这个页面的方式。你的应用在平台预留的插槽上加自己的内容，读写这一单的数据，隐藏被你的内容替换掉的原生模块，而不接管整个页面。做这件事的扩展叫结账扩展。

## 支持的能力

结账页定制由三部分组成：

- **[扩展点](/docs/app/extensions/checkout/extension-points)**：平台在结账页上预留的位置。`extend()` 把你的 HTML 渲染到你指定的扩展点上，所以你的内容出现在页面的哪里，由扩展点决定。
- **[隐藏原生模块](/docs/app/extensions/checkout/extension-reference#隐藏原生模块deletetarget)**：`extension.json` 里的 `deleteTarget` 按名字关掉平台自带的模块，让你的内容替换它们，而不是和它们并排显示。
- **[`CheckoutAPI`](/docs/app/extensions/checkout/checkout-api)**：平台在页面加载完成后挂到 `window.CheckoutAPI` 上的全局对象，用来读写这一单的数据。它按命名空间分组，下面的表是一份速览，完整的方法签名和类型见参考页。

| 命名空间 | 管什么 | 可以做什么 |
|---|---|---|
| [`order`](/docs/app/extensions/checkout/checkout-api#order) | 顾客下单途中做的选择，以及每次变更之后的价格刷新。 | 增删商品行；在订单上存自己的键值字段；修改交付方式和物流方案；阻止提交，或在提交前先校验。 |
| [`store`](/docs/app/extensions/checkout/checkout-api#store) | 订单数据本身：订单、价格、业务类型和结账页布局。 | 读订单和金额；监听每一次重算价；判断当前是哪一种结账页布局。 |
| [`summary`](/docs/app/extensions/checkout/checkout-api#summary) | 订单摘要那一列：商品行列表、价格明细，以及运费的展示文案。 | 读商品行和价格明细；监听它们的变化；改写摘要最终渲染的商品行列表。 |
| [`address`](/docs/app/extensions/checkout/checkout-api#address) | 收货地址和账单地址两张表单、界面据以渲染的 schema，以及顾客保存过的地址。 | 读写这两张地址表单；调整字段的顺序、标签、显隐和锁定；在内置校验之上加规则；读国家列表和地址模板。 |
| [`coupon`](/docs/app/extensions/checkout/checkout-api#coupon) | 顾客能用的优惠：领用的优惠券、手输的优惠码，以及礼品卡。 | 读当前生效的优惠；应用或取消优惠码和礼品卡；改写优惠码与礼品卡 tag 的展示。 |
| [`user`](/docs/app/extensions/checkout/checkout-api#user) | 顾客是谁、怎么联系。 | 读登录状态和账号信息；读结账页填写的邮箱和手机号；读营销邮件订阅状态。 |
| [`payment`](/docs/app/extensions/checkout/checkout-api#payment) | 可选的支付方式。 | 读支付方式列表和当前选中项；发起支付；在支付尝试、支付失败和支付完成时执行回调。 |
| [`base`](/docs/app/extensions/checkout/checkout-api#base) | 结账页的基础设施：加载状态、格式化、多语言文案，以及原生模块是否展示。 | 注册自己的多语言文案，并按顾客的语言读回；格式化价格和手机号；查询某个原生模块当前是否在页面上。 |
| [`utils`](/docs/app/extensions/checkout/checkout-api#utils) | 结账页自带的弹窗和抽屉，外加三个原样透传的 lodash 函数。 | 打开弹窗；打开抽屉；使用平台透传的 lodash 工具函数。 |
| [`step`](/docs/app/extensions/checkout/checkout-api#step) | 结账的步骤，以及步骤之间的跳转。 | 读顾客当前所在的步骤；跳到另一个步骤；读步骤导航栏的配置；把顾客送去店铺的其他页面。 |
| [`config`](/docs/app/extensions/checkout/checkout-api#config) | 只读的店铺和页面配置：主题、多市场、特性开关，以及当前打开的是哪一个页面。 | 区分当前是结账页还是感谢页；读主题、多市场和店铺配置；判断当前是不是移动端布局。 |
| [`exception`](/docs/app/extensions/checkout/checkout-api#exception) | 结账过程中抛出的业务异常。 | 检查接口返回里有没有异常码；读取或清空已存的异常；监听提交错误。 |
| [`pickup`](/docs/app/extensions/checkout/checkout-api#pickup) | 门店自提。 | 读自提点列表；读顾客选中的自提点；读取货信息表单和它的校验结果。 |
| [`extension`](/docs/app/extensions/checkout/checkout-api#extension) | 扩展自身。 | 读某个点位上已注册的内容；等扩展加载完成；查询哪些原生模块被隐藏；拼出动态扩展点的真实名字。 |
| [`track`](/docs/app/extensions/checkout/checkout-api#track) | 埋点上报。 | 上报自定义事件；上报结账页各个环节的埋点；读上报用的订单数据；给某个事件附加自定义字段。 |
| [`utils.eventBus`](/docs/app/extensions/checkout/checkout-api#utilseventbus) | 同一个结账页上多个扩展之间的页内事件总线。 | 触发事件；注册监听，也可以只监听一次；取消监听。 |

:::warning
名字以 `register` 开头的方法不是监听器。它交给结账页的是一个**返回值会被平台消费**的回调，你返回什么，页面就渲染成什么、就按什么行为运行。返回的结构不对——比如一个必须返回列表的回调返回了 `undefined`——页面去读它上面的属性就会整页崩到报错界面，顾客无法完成下单。

只有 `on*` 和 `add*Cb` 方法没有这个风险：它们只接收值，平台不消费它们的返回值。只想观察某个值、不想改动它，用 `on*` 方法，不要用 `register*`。
:::

读取类方法没有这个风险。`get*`、`is*`、`has*` 从页面已经持有的数据里取值，同步返回，不发请求，需要多少次就调多少次。

## 示例

下面两篇的搭法是一样的。第一篇逐步讲清共用的骨架，再给出每一类改动对应的调用，第一次写结账扩展从它开始。第二篇是在这个骨架上做出来的一个完整功能。

也可以直接跳到任意一篇：
