# 构建结账扩展

> 用 shoplazza app 命令构建 Shoplazza 结账扩展：初始化项目、创建扩展、在结账页预览并发布。

# Checkout Extension 开发指南

## 概述

Checkout Extension 允许开发者在店铺结账页面中注入自定义内容和业务逻辑，无需修改主题代码。

使用 `shoplazza app` 指令开发的 Checkout Extension 支持以下两类能力：

- **在指定扩展点插入自定义内容**：在结账页面的固定位置渲染自定义 HTML，例如在联系信息下方显示提示横幅、在支付模块上方添加说明文本等。

- **读取 checkout 数据并响应变化**：通过 `CheckoutAPI` 获取订单、商品、价格、地址、用户等信息，并监听其变化执行自定义逻辑。

### 开发方式说明

- **在空目录中开发**：生成的扩展仅供自己店铺使用，无法发布给其他商家。

- **在已有 app 项目中开发**：将扩展作为 app 的一部分发布，其他商家可安装使用。完整 App 开发教程请参考：[App 开发指南](/docs/app/getting-started/create-public-app/)

## 你将学到什么

- 如何用 CLI 初始化并创建一个 Checkout Extension

- 如何选择扩展点，把自定义内容渲染到结账页指定位置

- 如何用 `extend` + HTML 模板写出一个最小可预览的扩展

## 前置条件

- 已安装 [shoplazza-cli](https://www.npmjs.com/package/shoplazza-cli)，并已登录到开发用的店铺（`shoplazza app dev` 会把扩展推送到这个店铺）：`shoplazza auth login --store-domain <your-store>.myshoplaza.com --domain app`

- 拥有 Shoplazza 开发者账号

- 已有 shoplazza app 项目，或在空目录中开发

## 开发流程

```mermaid
flowchart LR
    A[shoplazza app init --name: 创建 app] --> B[shoplazza app extension create --type checkout: 创建扩展]
    B --> C[在 src/ 下开发]
    C --> D[shoplazza app dev: 在结账页编辑器预览]
    D --> E{分发给其他商家?}
    E -- 暂不 --> C
    E -- 是 --> F[先完成 app 开发, 再 shoplazza app deploy]
```

### 第一步：初始化

在 app 项目根目录（或空目录）执行：

```bash
shoplazza app init --name "my-app"
```

该命令会创建一个名为 `my-app` 的应用，并在当前目录下生成同名项目子目录。

:::tip 已经有应用了？
如果你已按[开发独立应用](/docs/app/getting-started/create-public-app/develop-app)构建了应用，你的工程已具备 CLI 配置——跳过本步，在工程根目录直接从第二步继续。
:::

### 第二步：创建 Checkout Extension

进入生成的 app 目录后执行：

```bash
shoplazza app extension create --type checkout --name my-checkout
```

该命令会在 `extensions/` 目录下创建一个名为 `my-checkout` 的结账扩展。目录结构如下：

```text
my-app/
└── extensions/
    └── my-checkout/
        ├── shoplazza.extension.toml   # 扩展配置文件
        ├── extension.json             # 可选：隐藏原生模块（deleteTarget）
        ├── package.json
        └── src/
            ├── index.js               # 扩展入口
            ├── index.html             # 主 HTML 模板
            ├── style.html             # 样式
            ├── content.html           # 页面内容
            └── script.html            # 脚本逻辑
```

在 `src/` 目录下进行扩展开发。

### 第三步：启动预览

开发完成后，在 app 目录中执行：

```bash
shoplazza app dev
```

扩展会自动发布到绑定的店铺，在店铺主题编辑「结账页编辑器」中即可查看效果。

`shoplazza app dev` 只在启动时推送一次扩展，不监听文件变更——每次修改后按 Ctrl+C 停止并重新运行，再刷新结账页编辑器。

:::note
扩展只有在所属应用安装到店铺后才会出现。如果还没有安装，按[测试应用](/docs/app/getting-started/create-public-app/test-app)操作；尚未实现应用本身的，走其中的场景一安装。
:::

## 扩展点速览

扩展点决定自定义内容插入到结账页的具体位置，在 `extend()` 的 `extensionPoint` 字段中指定。常用扩展点：

|扩展点|位置说明|
|---|---|
|`Checkout::RenderBefore`|页面头部之前|
|`Checkout::ContactInformation::RenderAfter`|联系信息模块之后|
|`Checkout::Summary::RenderBefore`|订单摘要之前|
|`Checkout::SectionPayment::RenderBefore`|支付模块之前|

全部扩展点见[扩展点](/docs/app/extensions/checkout/extension-points)。

## 一个最小 UI 示例

从 `shoplazza-extension-ui` 引入 `extend`，指定一个扩展点并传入 HTML 字符串，即可在结账页渲染内容：

```javascript
import { extend } from 'shoplazza-extension-ui';

extend({
  extensionPoint: 'Checkout::RenderBefore',
  component: '<div style="padding:12px;background:#f5f5f5;">Hello, Shoplazza!</div>',
});
```

内容较复杂时可拆分到 `src/style.html` / `src/content.html` / `src/script.html` 并在 `src/index.html` 组装。`extend` 函数签名与 HTML 模板机制见 [Checkout extension 参考](/docs/app/extensions/checkout/extension-reference)；完整的 API 见 [CheckoutAPI 参考](/docs/app/extensions/checkout/checkout-api)。

:::warning 别漏写分号
JS 文件每条语句末尾必须加分号。若漏写分号，`dev` 运行时会报错并提示 `extend` 必须写在文件顶部，这是解析失败导致的误导性报错，并非 `extend` 位置问题。
:::

## 预览与发布

在 app 目录中执行：

```bash
shoplazza app dev
```

扩展自动发布到绑定店铺后，打开店铺后台「结账页编辑器」即可查看效果。

如果需要其他商家也可以使用本扩展，需要首先完成 [app 开发](/docs/app/getting-started/create-public-app/)，并进行发布：

```bash
shoplazza app deploy
```

其他商家在安装 app 后，即可在店铺主题编辑的结账页查看效果。

## 下一步

- **查扩展点**：见[扩展点](/docs/app/extensions/checkout/extension-points)
- **查 API**：见 [CheckoutAPI 参考](/docs/app/extensions/checkout/checkout-api)
- **看实战场景**：见 [Checkout extension 场景教程](/docs/app/extensions/checkout/extension-recipes)
- **做一个真实功能**：见[满额免运费进度条场景](/docs/app/extensions/checkout/extension-recipes#场景五根据购物车金额展示免运费进度条)
- **完整验证应用与扩展**：见[测试应用](/docs/app/getting-started/create-public-app/test-app)

## 排障

### `extend` 报错说它必须写在文件顶部

**问题：** `dev` 运行时报错，提示 `extend` 必须写在文件顶部，但它其实已经在顶部了。

**原因：** 文件别处缺了分号导致解析失败，错误被误报成 `extend` 的位置问题。

**解决：** 确保每条语句都以分号结尾（参见上文最小示例中的提示）。

### `window.CheckoutAPI` 是 `undefined`

**问题：** 直接读取 `window.CheckoutAPI` 返回 `undefined`。

**原因：** `CheckoutAPI` 只有在结账页完全加载后才挂载。

**解决：** 调用前先等待挂载——用 `setTimeout`/`setInterval` 轮询（最多约 5 秒）。挂载写法 `mount` 参见[场景三](/docs/app/extensions/checkout/extension-recipes#场景三api-调用与事件监听)。
