文章目录
本文会使用 Gadget 和 Shopify Checkout UI Extension,开发一个简单的“结账页预购推荐”应用。
商家可以在 Shopify Admin 中选择一个推荐商品,顾客进入结账流程后,应用会在结账页展示这个商品。顾客点击按钮后,推荐商品会被加入购物车。
完整流程如下:
商家在 Shopify Admin 选择推荐商品
↓
商品 ID 保存到 Shopify Shop Metafield
↓
Checkout UI Extension 读取这个 Metafield
↓
Extension 查询商品标题、图片和价格
↓
顾客在结账页看到推荐商品
↓
顾客点击 Add
↓
推荐商品加入购物车
这个项目适合用来学习:
- Gadget Shopify Connection
- Shopify OAuth
- Shopify API Scopes
- Shopify 商品同步
- Shopify Shop Metafield
- Gadget 自定义 Action
- React 管理页面
- Shopify Polaris
- Checkout UI Extension
- Storefront API
useCartLinesapplyCartLinesChange- Shopify CLI
- Development 和 Production 部署
先确认 Checkout UI Extension 的可用范围
Checkout UI Extension 并不是所有位置、所有 Shopify 套餐都完全开放。
目前常见情况是:
- Thank you 和 Order status 页面有较广泛的应用区块支持
- 结账信息、配送、支付等核心 Checkout 页面上的部分扩展能力可能需要 Shopify Plus
- Development Store 可以用于测试扩展,但正式商店能否使用,取决于扩展目标和商店套餐
- Checkout 编辑器中是否出现 App Block,也取决于当前扩展目标、主题配置和商店资格
本教程中的“结账前推荐商品”属于 Checkout 页面扩展场景。开发时建议使用支持对应 Checkout Extension target 的开发商店;正式部署前,必须确认目标商店套餐和扩展目标兼容。
Shopify 的 Checkout 编辑器可以通过 Apps 或 Sections 侧边栏添加可用的应用区块,但只有当前页面和当前商店支持的区块才会显示。citation:Checkout style and checkout blocks
应用的整体架构
这个项目分成三部分。
Shopify Admin 中的 Gadget 应用
商家在这里选择一个商品作为预购推荐商品。
Gadget 后端和数据库
Gadget 负责:
- 保存当前商店连接
- 同步商品数据
- 保存商家选择的商品 ID
- 执行自定义 Action
- 提供前端 API
- 管理应用部署
Checkout UI Extension
Checkout UI Extension 运行在结账页面中,负责:
- 读取商家选择的商品 ID
- 查询商品信息
- 显示推荐商品
- 将推荐商品加入购物车
数据流如下:
React Admin 页面
↓
Gadget API
↓
Shop Metafield
↓
Checkout UI Extension
↓
Storefront API
↓
商品信息
↓
Cart Line Change
创建 Gadget Shopify App
进入 Gadget 的应用创建页面,创建一个新的 Shopify App。
应用名称可以设置为:
Pre Purchase Offer
如果只是为一个指定商家开发,可以选择 Custom App 方向。
前端可以使用:
Remix
React
TypeScript
Gadget 会准备:
- PostgreSQL 数据库
- Node.js 后端
- React 前端
- Shopify Connection
- App Bridge 集成
- Session Token 处理
- Shopify API 客户端
- Development 环境
- Production 环境
Gadget 的界面名称可能会随着版本变化。看到 Shopify Connection、Shopify App 或 Connect to Shopify 等类似入口时,选择对应的 Shopify 应用连接即可。
在 Shopify Dev Dashboard 创建应用
如果 Gadget 没有自动完成 Shopify 应用创建,可以在 Shopify Dev Dashboard 中创建应用。
应用名称建议保持一致:
Pre Purchase Offer
创建完成后获取:
Client ID
Client Secret
将它们填入 Gadget 的 Shopify Connection 配置中。
Client Secret 只能放在服务端或 Gadget 的私密配置中,不要放进:
- React 前端
- 浏览器 Local Storage
- GitHub 公开仓库
- 截图
- URL 参数
Shopify 当前建议使用 Dev Dashboard 管理应用、版本、API 权限、URL 和 Webhook 配置。需要完整本地项目时,也可以使用 Shopify CLI 创建应用和扩展。citation:Building apps
配置 API 权限和数据模型
本项目需要读取 Shopify 商品,因此至少需要:
read_products
如果应用不直接修改商品,可以先不要申请:
write_products
在 Gadget 的 Shopify Connection 中选择:
Shopify Product
Gadget 通常会生成或连接以下模型:
- Shopify Product
- Shopify Shop
- Shopify Sync
- GDPR Request
选择 Shopify Product 后,Gadget 通常还会配置商品相关 Webhook,使商品在 Shopify 中发生变化时,Gadget 数据库能够同步更新。
商品数据模型中通常会包含:
id
title
handle
body
status
productType
createdAt
updatedAt
本项目主要需要:
id
title
image
variant
如果只是让商家选择商品,也可以通过 Gadget 商品模型读取商品列表,再将选中的商品 ID保存到 Shop 记录中。
配置应用 URL 和回调地址
从 Gadget 的 Shopify Connection 页面复制:
App URL
OAuth Callback URL
回到 Shopify App 的 URL 配置中填写:
App URL
Allowed redirection URL
检查:
- App URL 没有复制错误
- OAuth 回调路径完整
- 使用 HTTPS
- 没有混用 Development 和 Production 地址
- Embedded App 设置正确
- Client ID 属于当前 Shopify App
保存后,发布当前应用版本。
安装到开发商店
在 Shopify App 的测试或安装页面中选择一个 Development Store。
安装时检查:
- 应用申请的权限是否正确
- 应用是否能够打开
- Shopify Admin 中是否能显示应用
- Gadget 中是否出现 Shop 记录
- Shopify Connection 是否显示已连接
- Webhook 是否已经注册
安装成功后,应用页面通常会在 Shopify Admin 内部打开。
Gadget 可以代为处理 OAuth、App Bridge 和嵌入式应用的基础配置,因此不需要从零手动实现整套安装流程。
执行商品历史同步
Webhook 只能接收安装之后发生的变化。
开发商店中已有的商品,需要通过历史同步导入 Gadget。
在 Gadget 的 Shopify Connection、Installs 或 Sync 页面中,找到类似:
Sync
Start sync
Historical sync
Data sync
执行同步后检查:
- 商品是否进入 Gadget
- 商品标题是否正确
- 商品 ID 是否正确
- 商品变体是否存在
- 商品图片是否存在
- 同步是否完成
- 是否有失败记录
完成同步后,进入 Shopify Product 数据页面,随机检查几条商品。
在 Shop 模型中添加推荐商品字段
商家选择的推荐商品需要被保存起来,Checkout UI Extension 才能读取。
有两种常见方式:
- 保存到 Gadget 自定义字段
- 同时保存到 Shopify Shop Metafield
本项目使用 Shop Metafield,因为 Checkout UI Extension 可以读取应用配置相关的 Metafield。
在 Gadget 的 Shopify Shop 模型中添加一个自定义 Shopify Metafield,建议使用:
Namespace: gadget_tutorial
Key: pre_purchase_product
Type: Product reference
实际字段类型和 Gadget 当前界面显示可能不同。如果平台要求以字符串保存,则保存 Shopify Product 的完整 GID,例如:
gid://shopify/Product/123456789
不要只保存一个不明确的数字 ID,除非你的读取逻辑已经统一处理这种格式。
这个字段的作用是:
Shopify Shop
└── gadget_tutorial.pre_purchase_product
它保存商家当前选择的推荐商品。
创建保存推荐商品的自定义 Action
Gadget 会为数据模型自动生成基础 CRUD Action,也可以在 Shop 模型中增加自定义 Action。
可以创建:
savePrePurchaseProduct
这个 Action 接收:
shopId
productId
执行时完成:
- 验证当前商店身份
- 检查商品 ID 是否有效
- 将商品 ID写入 Shop Metafield
- 写入日志
- 返回保存结果
这里要注意多租户隔离。
应用不能允许某个商店保存或读取另一个商店的数据。Gadget 的 Action 应使用当前商店上下文,并检查传入的 shopId 是否属于当前请求。
如果这是公开应用,还需要特别关注:
- 当前用户属于哪家商店
- Shop 记录是否属于当前会话
- 商品是否属于当前商店
- 不能仅依赖前端传入的 shop ID
配置 Action 权限
Gadget 默认可能不会允许 Shopify App 用户直接调用新建的自定义 Action。
在 Access Control 中,为 Shopify App Users 或对应商家角色开放:
Read Shop
Update Shop
Run savePrePurchaseProduct
如果忘记配置,前端点击保存时可能出现:
Permission denied
出现这个错误时,依次检查:
- Action 是否已经创建
- Action 是否允许 API 调用
- Shopify App Users 是否有权限
- 当前用户是否已经完成安装
- 当前 Shop 记录是否属于当前商店
开发 Admin 管理页面
现在为商家开发一个简单的选择页面。
页面需要完成:
- 读取商品列表
- 显示商品标题
- 显示商品图片
- 允许商家选择商品
- 保存选择结果
- 页面重新打开时显示当前已选商品
推荐页面结构:
Pre-purchase offer product
[商品选择框]
[Save product]
前端通过 Gadget API 读取商品:
Shopify Product → 商品下拉选项
同时读取当前 Shop 记录,获取已经保存的推荐商品。
页面逻辑可以理解为:
加载页面
↓
读取商品列表
↓
读取当前 Shop 的 Metafield
↓
将商品列表转换成下拉选项
↓
默认选中已经保存的商品
使用 Shopify Polaris 组件可以快速构建:
- Select
- Card
- Button
- Banner
- Image
- Text
- Spinner
保存表单时:
点击 Save
↓
调用 savePrePurchaseProduct
↓
保存 productId
↓
刷新 Shop 数据
↓
显示保存成功
测试 Admin 页面
在 Shopify Admin 中打开应用。
选择一个商品,例如:
Oxygen Snowboard
点击:
Save
然后进入 Gadget 的 Shop 数据页面,查看:
gadget_tutorial.pre_purchase_product
如果字段中保存了正确的 Product GID,说明后台保存逻辑正常。
如果页面没有显示商品,可以检查:
- 历史同步是否完成
read_products是否已授权- 当前 Shop 是否有商品
- 前端是否正确读取 Product 模型
- API 请求是否返回权限错误
创建 Checkout UI Extension
Checkout UI Extension 应该使用 Shopify CLI 生成,而不是完全手动创建文件。
在 Gadget 项目中,可以通过 Gadget 提供的 CLI 集成方式连接本地代码和 Gadget 项目。
当前具体命令可能随 Gadget CLI 和 Shopify CLI 版本变化。建议从 Gadget 编辑器的 Cloud、CLI 或 Source Control 入口复制当前项目提供的命令。
生成扩展时选择:
Checkout UI Extension
并连接到已经用于 Gadget Shopify Connection 的同一个 Shopify App。
不要在这里重新创建第二个 Shopify App,否则容易出现:
- OAuth 应用不一致
- Extension 绑定到错误 App
- Development Store 安装的是另一个应用
- 生产部署时无法找到扩展
扩展的命名可以是:
pre-purchase-extension
管理 Gadget 和 Extension 的代码
如果 Gadget 项目使用本地开发和 Source Control,建议将:
- Gadget 后端
- Gadget 前端
- Shopify Extension
- 应用配置
- 部署配置
放在同一个代码仓库中。
同时将扩展构建产物排除在 Gadget 同步之外,避免开发过程中把依赖目录和构建文件重复同步。
可以使用类似 .gadgetignore 的忽略配置,具体格式以当前 Gadget CLI 文档和项目生成文件为准。
通常需要排除:
node_modules
extensions/*/dist
build
临时缓存目录
不要把:
Client Secret
Access Token
提交到代码仓库。
配置 Extension 读取 Shop Metafield
Checkout Extension 需要知道要读取哪个 Metafield。
Namespace 和 Key 必须与前面保存数据时完全一致:
Namespace: gadget_tutorial
Key: pre_purchase_product
扩展读取到这个值后,应该得到一个 Shopify Product GID。
读取不到时,不要渲染推荐区块:
没有 Metafield
↓
不显示推荐商品
这样可以避免商家没有配置商品时,结账页出现空白卡片或错误提示。
在 Checkout 中查询商品信息
Shop Metafield 通常只保存商品引用,不包含完整的:
- 商品标题
- 商品图片
- 价格
- 商品变体
- 可购买状态
因此,Checkout Extension 还需要根据 Product GID 查询商品信息。
可以读取:
- 商品标题
- 商品图片
- 商品变体
- 商品价格
- 商品是否可用
本项目为了简化,只选择第一个商品变体。
但生产应用不应默认使用第一个变体,因为商品可能有:
- 多个颜色
- 多个尺寸
- 不同价格
- 缺货变体
- 不同库存状态
更完整的设计应该允许商家直接选择:
具体商品
具体变体
显示图片
显示价格
显示预购推荐卡片
Checkout UI Extension 可以包含:
- 商品图片
- 商品标题
- 商品价格
- 简短说明
- Add 按钮
- Loading 状态
- 错误提示
逻辑如下:
读取 Shop Metafield
↓
查询商品和变体
↓
读取当前购物车
↓
判断推荐商品是否已经在购物车
↓
如果已存在,不重复显示
↓
如果不存在,显示推荐卡片
使用购物车相关 API 时,需要处理:
- 商品已经在购物车
- 商品无可售变体
- 商品库存不足
- 商品查询失败
- 顾客重复点击
- 加购正在进行中
如果推荐商品已经存在于购物车,建议隐藏推荐卡片,避免重复添加。
将商品加入购物车
顾客点击 Add 按钮后,Extension 使用 Shopify Checkout UI Extension 提供的购物车修改能力,将选中的商品变体加入购物车。
操作流程是:
顾客点击 Add
↓
显示 Loading
↓
调用 Cart Line Change
↓
添加商品变体
↓
刷新购物车状态
↓
显示成功或错误结果
每次加购必须使用具体的 Variant ID,而不是只使用 Product ID。
同时设置合理数量:
quantity: 1
如果添加失败,需要向顾客显示明确提示,不要让按钮一直处于 Loading 状态。
本地运行 Checkout Extension
开发时需要同时考虑两部分:
Gadget 项目
负责:
- Admin 页面
- Shop Metafield
- 商品读取
- 自定义 Action
- 后端 API
Shopify CLI Extension
负责:
- Checkout UI Extension
- 本地预览
- Checkout 页面渲染
- 购物车加购逻辑
启动开发预览时:
- 使用 Gadget CLI 保持本地项目同步
- 使用 Shopify CLI 启动扩展开发
- 选择连接 Gadget 的同一个 Development Store
- 打开 Shopify Checkout 预览
- 检查 Extension 是否出现
本地开发地址、命令名称和端口由当前 CLI 版本决定,建议优先使用 Gadget 编辑器和 Shopify CLI 输出的命令。
在 Checkout Editor 中添加扩展
扩展部署到 Shopify 后,进入 Shopify Admin:
Settings → Checkout
找到当前 Checkout 配置,点击:
Customize
在 Checkout Editor 中:
- 打开需要显示扩展的页面
- 点击 Add app block 或 Apps
- 选择 Pre-purchase Extension
- 将区块拖动到合适位置
- 点击 Save
只有扩展目标与当前页面和商店套餐兼容时,才会显示在可选列表中。
如果看不到扩展,检查:
- 扩展是否成功部署
- 是否连接到了正确的 Shopify App
- 当前页面是否支持该 Extension target
- 当前商店套餐是否支持该扩展
- 当前 Checkout 配置是否已经发布
- 是否使用了正确的 Development Store
部署 Checkout Extension
开发完成后,使用当前 Shopify CLI 项目中的部署命令发布扩展。
常见流程类似:
构建 Extension
↓
推送到 Shopify
↓
创建新的 App Version
↓
发布 App Version
↓
在 Checkout Editor 中添加 App Block
↓
保存 Checkout 配置
扩展代码发布后,不代表它已经出现在顾客结账页中。
还需要在 Checkout Editor 中:
- 添加扩展区块
- 调整位置
- 点击 Save
- 使用真实结账流程验证
Shopify 会托管 Checkout Extension 的前端资源。Gadget 主要负责应用管理后台、后端逻辑和数据连接。
生产环境部署
开发环境测试完成后,准备 Production。
生产环境应使用独立的:
- Shopify Production App
- Gadget Production 环境
- Client ID
- Client Secret
- App URL
- OAuth 回调地址
- 数据库
- 环境变量
部署前检查:
- Shopify Admin 应用可以正常打开
- 商家选择商品后可以保存
- Shop Metafield 能正确写入
- Checkout Extension 能读取 Metafield
- 商品信息能正确显示
- 推荐商品可以加入购物车
- 商品已经在购物车时不会重复显示
- 失败时有错误提示
- 生产环境没有使用开发凭据
如果是 Custom App,应用适合指定商家或组织使用。如果要面向多个独立商家,则需要按照 Public App 的分发和审核要求重新规划。
常见问题排查
Admin 应用能打开,但 Checkout 没有扩展
检查:
- Extension 是否已经发布
- 是否绑定到正确的 Shopify App
- Checkout target 是否正确
- 当前 Checkout 页面是否支持该 target
- 当前商店套餐是否支持该扩展
- 是否在 Checkout Editor 中添加并保存了 App Block
Metafield 没有值
检查:
- Namespace 是否一致
- Key 是否一致
- 保存的值是否是正确的 Product GID
- Action 是否有 Shopify Shop 的写入权限
- 当前 Shop 是否属于当前商店
- 是否查看了正确的 Development 或 Production 环境
推荐商品显示不出来
检查:
- Extension 是否读取到了 Metafield
- Product GID 是否有效
- Storefront API 查询是否成功
- 商品是否有可售变体
- 当前商品是否已经在购物车
- 是否因为加载状态提前返回
点击 Add 没有反应
检查:
- 是否使用了 Variant ID
quantity是否有效- 是否正在显示 Loading
- Cart Line Change 是否返回错误
- 商品是否缺货
- 当前 Checkout 页面是否支持购物车修改操作
前端出现 Permission denied
检查:
- Shopify App Users 是否有 Shop 模型读取权限
- 自定义 Action 是否允许 Shopify App Users 调用
- 当前 Shop 记录是否属于当前商店
- Session Token 是否正确传递
- 前端 API 是否调用了正确的 Action
本地修改没有反映到 Gadget
检查:
- Gadget CLI 是否正在运行
- 本地目录是否进入正确项目
- 是否被
.gadgetignore排除了 - 当前修改的是 Development 环境
- 浏览器是否需要刷新
- Gadget 前端热更新是否因为长时间空闲而暂停
项目验收清单
完成以下检查后,项目才算真正跑通:
- Shopify App 可以安装到 Development Store
- OAuth 能够正常完成
- Gadget 中出现 Shop 记录
- 商品历史同步成功
- Product 数据进入 Gadget
- Admin 页面可以显示商品
- 商家可以选择推荐商品
- 商品 ID 可以写入 Shop Metafield
- 自定义 Action 可以正常执行
- Shopify App Users 拥有正确权限
- Checkout Extension 成功生成
- Extension 读取到正确的 Metafield
- 推荐商品标题和图片显示正常
- 商品价格显示正常
- 商品变体查询正常
- 商品不在购物车时显示推荐
- 商品已经在购物车时不重复显示
- 点击 Add 可以加入购物车
- 加购失败时有错误提示
- Checkout Editor 可以添加和保存 App Block
- Production 环境使用独立凭据
- 生产环境可以完成一次真实结账测试
结语
这个预购推荐应用虽然功能不复杂,但它串起了 Shopify App 开发中非常重要的几条链路:
Shopify App 安装
↓
Gadget Shopify Connection
↓
商品数据同步
↓
Shop Metafield
↓
Admin React 管理页面
↓
Checkout UI Extension
↓
Storefront API
↓
Cart Line Change
↓
生产部署
Gadget 负责:
- 数据库
- 后端服务
- OAuth
- Session Token
- 商品同步
- Webhook
- 应用 API
- 开发和生产环境
Shopify 负责:
- Admin 应用环境
- 商品和商店数据
- Checkout 页面
- Checkout Extension 运行环境
- Storefront API
- 购物车操作
- Checkout Editor
开发者真正需要设计的是:
- 商家如何选择推荐商品
- 推荐商品如何安全保存
- Checkout 如何读取配置
- 如何处理商品变体和库存
- 如何避免重复加购
- 如何处理 Extension 不可用的套餐和页面限制
- 如何让 Development 和 Production 环境保持隔离
当这个项目完成后,还可以继续扩展:
- 根据购物车商品推荐配件
- 根据商品标签推荐相关产品
- 设置不同商店的推荐规则
- 支持多个推荐商品
- 允许商家选择具体变体
- 增加折扣码或优惠信息
- 统计推荐商品的加购率
- 根据顾客地区显示不同商品
- 使用 AI 生成个性化推荐内容




