用 Gadget 开发 Shopify Checkout 预购推荐:从后台选品到结账页加购

文章目录

本文会使用 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
  • useCartLines
  • applyCartLinesChange
  • 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

执行时完成:

  1. 验证当前商店身份
  2. 检查商品 ID 是否有效
  3. 将商品 ID写入 Shop Metafield
  4. 写入日志
  5. 返回保存结果

这里要注意多租户隔离。

应用不能允许某个商店保存或读取另一个商店的数据。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 页面渲染
  • 购物车加购逻辑

启动开发预览时:

  1. 使用 Gadget CLI 保持本地项目同步
  2. 使用 Shopify CLI 启动扩展开发
  3. 选择连接 Gadget 的同一个 Development Store
  4. 打开 Shopify Checkout 预览
  5. 检查 Extension 是否出现

本地开发地址、命令名称和端口由当前 CLI 版本决定,建议优先使用 Gadget 编辑器和 Shopify CLI 输出的命令。

在 Checkout Editor 中添加扩展

扩展部署到 Shopify 后,进入 Shopify Admin:

Settings → Checkout

找到当前 Checkout 配置,点击:

Customize

在 Checkout Editor 中:

  1. 打开需要显示扩展的页面
  2. 点击 Add app block 或 Apps
  3. 选择 Pre-purchase Extension
  4. 将区块拖动到合适位置
  5. 点击 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 生成个性化推荐内容

citation:Building apps

citation:Checkout style and checkout blocks

citation:Shopify Checkout