文章目录
如果你准备开发一个面向多个商家的 Shopify Public App,除了应用功能本身,还需要解决:
- 订阅计划
- 免费试用
- 价格展示
- 商家选择套餐
- 账单授权
- 当前订阅状态
- 试用期判断
- 功能权限控制
- 收入和订阅数据
本文使用 Gadget 构建 Shopify App 的基础架构,再接入 Mantle 管理应用计费,完成一个可以展示套餐、发起订阅和判断试用状态的 Public App。
完整流程如下:
创建 Gadget Shopify App
↓
创建 Shopify Public App
↓
连接 Shopify 和 Gadget
↓
创建 Mantle 应用
↓
配置 Mantle API Key
↓
识别已安装商店
↓
将商店注册到 Mantle
↓
创建 Basic 和 Pro 套餐
↓
在 Gadget 前端展示套餐
↓
商家选择套餐
↓
进入 Shopify 计费确认流程
↓
根据订阅状态开放应用功能
Shopify App 的计费方式
Shopify App 的分发方式会影响计费能力。
Public App
Public App 面向多个独立商家,可以提交到 Shopify App Store,也可以向商家收费。
Shopify 当前推荐使用 Shopify App Pricing 管理公开应用的定价和收费。它支持:
- 固定订阅
- 按使用量收费
- 固定费用加使用量的组合方案
Custom App
Custom App 通常针对一个特定商家或组织使用。
Custom App 不适合使用 Shopify Billing API 向商家收费。本文使用 Public App 路线,因此需要提前规划应用分发和计费方式。
Shopify 官方说明中,应用的分发方式选择后不能随意切换,因此创建应用时要先确定目标是单一客户,还是多个独立商家。citation:Building apps
Shopify 原生计费和 Mantle 的关系
Shopify 原生提供 Shopify App Pricing 和 Billing API。
Mantle 是一个第三方计费管理服务,可以帮助应用处理:
- 套餐管理
- 试用期
- 订阅状态
- 计划展示
- 功能门控
- 收入指标
- 应用计费相关的业务逻辑
选择 Mantle 时,仍然需要确认:
- 当前 Mantle 版本支持的 Shopify 计费流程
- Mantle 生成的订阅是否符合 Shopify 要求
- Shopify App Store 审核要求
- 测试收费和真实收费的环境区别
- 应用卸载、取消订阅和退款的处理方式
本文使用 Mantle 的开发环境和测试收费,不代表正式应用可以跳过 Shopify 的应用审核、隐私和计费要求。
开始前的准备
需要准备:
- Shopify Partners 或 Shopify 开发者账号
- Shopify Development Store
- Gadget 账号
- Mantle 账号
- Node.js
- Shopify Dev Dashboard
- VS Code 或其他代码编辑器
项目技术栈:
Backend: Gadget Serverless Node.js
Database: Gadget PostgreSQL
Frontend: React + Vite
Admin UI: Shopify Polaris
Billing: Mantle
Platform: Shopify Public App
创建 Gadget Shopify App
进入 Gadget 应用创建页面:
https://gadget.new
选择:
Shopify App
应用名称可以设置为:
Mantle Sample
创建后,Gadget 会准备:
- PostgreSQL 数据库
- Serverless Node.js 后端
- React + Vite 前端
- Shopify Connection
- Shopify API 客户端
- Development 环境
- Production 环境
- 应用模型和 Action
Gadget 的价值在于减少 Shopify App 的基础代码,例如:
- OAuth
- App Bridge
- Session Token
- Webhook
- Shopify 数据同步
- 数据库
- 后端部署
创建 Shopify Public App
在 Shopify Dev Dashboard 中创建应用。
应用名称可以设置为:
Mantle Sample
创建后获取:
Client ID
Client Secret
将它们复制到 Gadget 的 Shopify Connection。
Client Secret 只能放在:
- Gadget 私密环境变量
- 服务端环境变量
- 密钥管理服务
不能放在:
- React 前端
- 浏览器 Local Storage
- GitHub 公开仓库
- 公开文档
- 前端请求参数
配置 Shopify API Scope
如果本项目只演示计费,不需要同步商品数据,可以暂时不选择 Shopify Product 数据模型。
如果应用功能需要商品数据,再选择:
read_products
并启用:
Shopify Product
权限和数据模型应该围绕实际功能选择。
例如:
商品管理应用:
read_products
write_products
订单分析应用:
read_orders
库存应用:
read_inventory
不要因为应用是 Public App,就一次性申请所有权限。
配置 App URL 和 OAuth 回调地址
从 Gadget Shopify Connection 页面复制:
App URL
OAuth Callback URL
回到 Shopify App 配置中填写:
App URL
Allowed redirection URL
检查:
- App URL 没有填错
- OAuth 回调路径完整
- 使用 HTTPS
- Development 和 Production 地址没有混用
- Client ID 属于当前 Shopify App
- 当前应用版本已经保存和发布
如果地址错误,常见问题包括:
- OAuth 无限跳转
- 安装后显示 Not Found
- Embedded App 无法打开
- Gadget 无法识别当前商店
选择 Public Distribution
回到 Shopify App 的分发设置,选择:
Public distribution
Public App 的目标是:
- 面向多个独立商家
- 提交 Shopify App Store
- 使用应用订阅收费
- 建立公开应用品牌
正式发布前还需要准备:
- 应用名称
- 应用介绍
- 隐私政策
- 服务条款
- 技术支持页面
- 应用截图
- 定价说明
- 数据删除流程
- 客户数据处理说明
开发阶段可以先使用 Development Store 测试,不需要立即提交审核。
创建 Mantle 开发应用
进入 Mantle 控制台,创建应用。
应用名称可以设置为:
Mantle Sample
选择:
Development App
然后选择:
Platform: Shopify
开发环境建议启用测试收费:
Use test charges: Yes
这样可以在 Development Store 中测试套餐选择和订阅流程,不会直接产生真实账单。
Mantle 通常需要 Shopify 应用的:
App ID
Client ID
根据 Mantle 当前界面提示填写。
获取 Shopify App ID
Shopify App ID 通常可以从 Shopify 应用后台 URL 或应用详情页面获取。
需要区分:
App ID
Client ID
Client Secret
它们不是同一个值:
- App ID:识别 Shopify App 的编号
- Client ID:应用身份编号
- Client Secret:应用私密密钥
将正确的值分别填入 Mantle。
保存后,Mantle 应该显示应用验证成功。
配置 Mantle API Key
在 Mantle 中创建一个 API Key:
Gadget Integration
创建后会得到:
Mantle App ID
Mantle API Key
将它们添加到 Gadget 的环境变量。
Mantle App ID
App ID 可能需要被 Gadget 前端使用,因此可以使用 Gadget 的公开环境变量命名规则,例如:
GADGET_PUBLIC_MANTLE_APP_ID
它不是秘密,可以用于初始化前端的 Mantle Provider。
Mantle API Key
Mantle API Key 必须是服务端私密变量,例如:
MANTLE_API_KEY
不要添加公开前缀,不要让它进入浏览器。
环境变量结构如下:
GADGET_PUBLIC_MANTLE_APP_ID
MANTLE_API_KEY
其中:
GADGET_PUBLIC_MANTLE_APP_ID
可以被前端读取。
MANTLE_API_KEY
只能被后端读取。
Gadget 的环境变量通常按 Development、Production 等环境分别保存。创建新环境后,需要重新配置对应的变量。
安装 Mantle 前端和后端依赖
Gadget 项目基于 Node.js,可以通过项目命令安装 Mantle 包。
例如 Mantle 文档可能会要求安装:
@mantle/polaris
实际包名和版本以 Mantle 当前文档为准。
安装后,在 package.json 中确认依赖已经出现。
Mantle 通常会提供:
- React Provider
useMantleHook- 套餐数据
- 当前订阅
- 试用状态
- 订阅操作
- 计划选择组件
在 Shopify Shop 模型中添加 Mantle Token 字段
Gadget 的 Shopify Connection 通常会生成:
Shopify Shop
在 Shop 模型中添加字段:
mantleApiToken
字段类型可以使用:
String
这个字段用于保存每一家 Shopify 商店对应的 Mantle API Token。
数据关系如下:
Shopify Shop A
↓
Mantle API Token A
Shopify Shop B
↓
Mantle API Token B
每个商店都需要有自己的订阅状态,不能把所有商店共用一个 Token。
这个字段属于服务端数据,不应该直接展示给前端。
创建 Mantle 客户端服务
在 Gadget 后端创建服务文件,例如:
api/services/mantle.js
这个服务负责:
- 创建 Mantle Client
- 读取 Mantle App ID
- 读取 Mantle API Key
- 识别当前 Shopify Shop
- 将商店注册到 Mantle
- 获取该商店的 Mantle API Token
- 保存 Token 到 Shopify Shop 记录
逻辑可以理解为:
当前 Shopify Shop 安装应用
↓
Gadget 读取 Shop 信息
↓
调用 Mantle identify shop
↓
Mantle 返回该商店的 API Token
↓
保存到 Gadget Shopify Shop 记录
服务端需要使用:
MANTLE_API_KEY
不要在浏览器中执行识别商店或保存 API Token 的操作。
在 Shop 安装流程中注册 Mantle
应用安装时,需要调用 Mantle 的商店识别逻辑。
建议接入以下 Shop Action:
Install
Reinstall
Update
原因是:
- 新安装需要注册商店
- 重新安装需要确保订阅关系存在
- 应用更新后可能需要同步计费信息
推荐流程:
Shopify App 安装
↓
Gadget 创建 Shop 记录
↓
Mantle 识别商店
↓
Mantle 返回 API Token
↓
Gadget 保存 Token
↓
商店可以查看套餐并订阅
安装过程中如果 Mantle 注册失败,需要记录错误并阻止应用在没有计费上下文的情况下继续执行关键功能。
添加 Mantle Provider
Gadget 前端通常位于:
web/
在应用入口组件中添加 Mantle Provider。
Provider 需要使用:
Mantle App ID
Mantle API Token
其中:
- Mantle App ID 可以从公开环境变量读取
- Mantle API Token 从当前 Shopify Shop 记录读取
- API Token 不应该出现在日志中
- 数据加载过程中需要显示 Loading 状态
前端启动过程如下:
读取当前 Shopify Shop
↓
获取 Mantle API Token
↓
初始化 Mantle Provider
↓
加载当前订阅
↓
显示套餐或应用首页
如果 Token 还没有生成,前端不应该直接显示订阅按钮,而应该显示:
Preparing your billing account...
创建订阅计划
进入 Mantle 控制台创建套餐。
可以先创建两个计划:
Basic
Pro
示例价格:
Basic: $10 / month
Pro: $25 / month
示例试用期:
7 days
计划中可以配置:
- 计划名称
- 月费
- 年费
- 试用期
- 功能描述
- 使用量计费
- 计划可见范围
- 特定商家优惠
- 功能限制
示例:
| 套餐 | 价格 | 功能 |
|---|---|---|
| Basic | $10/月 | 基础功能 |
| Pro | $25/月 | 高级功能和更高使用量 |
价格只是示例,正式应用需要根据产品成本和商业模式重新设计。
按功能设置套餐差异
Mantle 可以帮助管理功能门控。
例如:
Basic:
最多创建 3 个项目
Pro:
不限项目数量
前端可以根据当前订阅状态决定:
- 是否显示功能
- 是否禁用按钮
- 是否显示升级提示
- 是否允许访问某个页面
- 是否允许创建更多数据
后端也必须重新验证权限。
不能只依赖前端隐藏按钮,因为用户可以直接请求后端 API。
创建 Plans 页面
在 Gadget React 路由中创建:
plans.jsx
Plans 页面负责:
- 读取可用计划
- 显示计划卡片
- 显示价格
- 显示试用期
- 显示当前订阅
- 提供选择按钮
- 调用 Mantle 订阅操作
页面可以使用:
- Shopify Polaris Card
- Layout
- Button
- Banner
- Text
- Badge
- Spinner
计划卡片可以显示:
Basic
$10 / month
7-day free trial
Basic features
[Choose Basic]
Pro
$25 / month
7-day free trial
Advanced features
[Choose Pro]
启动订阅流程
商家点击套餐按钮后,前端调用 Mantle 的订阅方法。
订阅流程通常是:
商家点击 Choose Plan
↓
Mantle 创建订阅请求
↓
Shopify 显示计费确认页面
↓
商家批准收费
↓
返回应用
↓
Mantle 更新订阅状态
↓
应用开放对应功能
不要在商家批准之前就直接开放付费功能。
前端需要处理:
- 商家取消
- 计费失败
- 计划已经订阅
- 试用期仍然有效
- 当前商店已有其他计划
- 返回 URL 无效
- 订阅状态还在同步
在应用首页显示试用状态
可以在 Gadget 应用首页读取 Mantle 当前订阅。
应用可以显示:
You are on a free trial.
还可以显示试用结束时间:
Your trial ends on June 15, 2026.
如果试用已经结束:
Your free trial has expired.
Choose a plan to continue.
显示状态时要使用服务器返回的当前订阅数据,不要完全相信前端本地保存的日期。
推荐状态:
No subscription
Trial active
Trial expired
Active subscription
Canceled
Past due
不同状态对应不同的应用行为。
订阅状态和功能权限
应用不应只检查用户是否点击过订阅按钮。
真正的功能权限应根据:
当前商店
当前订阅
计划状态
试用期
付款状态
来决定。
例如:
trial_active:
可以使用 Basic 功能
active:
按照订阅计划开放功能
trial_expired:
显示计划页面
canceled:
按照服务政策决定是否进入宽限期
past_due:
限制部分高级操作
后端也应该执行同样的检查,避免用户绕过前端直接调用 API。
Development Store 中测试计费
开发阶段使用:
Test charges
这样可以测试:
- 计划展示
- 试用期
- 订阅确认
- 订阅成功返回
- 订阅取消
- 当前计划读取
- 试用期结束状态
- 计划升级和降级
测试完成后,检查:
- Shopify 是否显示测试收费
- Mantle 是否记录订阅
- Gadget 是否保存 Shop Token
- 应用首页是否显示正确状态
- 计划页面是否显示当前订阅
- 用户取消后应用是否正确处理
不要在测试环境中使用真实收费配置。
生产环境配置
正式上线前,需要分别准备:
Development Shopify App
Production Shopify App
同时准备:
Development Mantle App
Production Mantle App
生产环境不能继续使用:
Test charges
Development Mantle API Key
Development Client Secret
Development App ID
Production 环境需要单独配置:
- Shopify Production Client ID
- Shopify Production Client Secret
- Mantle Production App ID
- Mantle Production API Key
- Production App URL
- Production OAuth 回调地址
- Production 数据库
- Production 环境变量
如果 Gadget 创建了新环境,所有环境变量都需要重新设置。
Public App 上线前的准备
Public App 上线前需要准备:
- Shopify App Store 应用介绍
- 隐私政策
- 服务条款
- 计费说明
- 试用政策
- 取消订阅政策
- 退款政策
- 客服联系方式
- 数据删除机制
- 应用截图
- 技术支持页面
Shopify 官方说明中,只有适合公开分发并完成相应要求的 Public App 才能向商家收费。Shopify App Pricing 是当前推荐的公开应用计费方式;Mantle 作为第三方计费服务时,仍需要与 Shopify 的分发、收费和审核要求保持一致。citation:Building apps
常见问题排查
Mantle 无法验证 Shopify App
检查:
- App ID 是否复制正确
- Client ID 是否填反
- Client Secret 是否属于当前 Shopify App
- Mantle 选择的平台是否为 Shopify
- Development 和 Production 应用是否混用
商店安装后没有 Mantle Token
检查:
- Shop Install Action 是否执行
- Reinstall 和 Update Action 是否导入了识别逻辑
- Mantle API Key 是否设置
- API Key 是否保存为服务端密钥
- Shopify Shop 记录是否存在
- Gadget 日志是否有错误
前端无法初始化 Mantle Provider
检查:
- Mantle App ID 是否配置为公开环境变量
- Mantle API Token 是否已经保存
- 当前 Shop 是否正确读取
- 页面是否正在等待数据
- Development 和 Production 环境是否混用
订阅按钮没有跳转
检查:
- 当前 Mantle 计划是否已创建
- 当前商店是否允许测试收费
- 是否使用了测试收费环境
- 返回 URL 是否有效
- 当前订阅状态是否已经存在
- 浏览器是否阻止了跳转
试用期显示错误
检查:
- 当前订阅数据是否来自 Mantle
- 服务器和浏览器时区是否不同
- 是否错误使用了本地日期
- 试用开始时间是否记录正确
- 试用结束时间是否已经过期
- 是否把测试环境订阅当成生产订阅
用户绕过前端使用高级功能
检查:
- 后端是否验证订阅计划
- Action 是否检查当前 Shop
- 是否只在前端隐藏按钮
- API 是否允许未订阅商店调用
- 计划状态是否在服务端重新读取
项目验收清单
完成以下检查后,Public App 计费流程才算跑通:
- Gadget Shopify App 创建成功
- Shopify Public App 创建成功
- Shopify Connection 正常
- OAuth 安装成功
- App URL 和回调地址正确
- Mantle Development App 创建成功
- Mantle 能验证 Shopify App
- Mantle API Key 已配置为服务端密钥
- Mantle App ID 可以供前端使用
- Shopify Shop 模型有 Mantle Token 字段
- Install Action 可以注册商店
- Reinstall Action 可以重新注册商店
- Update Action 可以同步商店
- Basic 计划创建成功
- Pro 计划创建成功
- 试用期配置正确
- Plans 页面可以显示计划
- 订阅按钮可以启动收费流程
- 测试收费可以批准
- 订阅状态可以读取
- 试用期状态可以显示
- 未订阅商店无法使用受限功能
- 后端能够重新验证计划权限
- Development 和 Production 环境彼此隔离
- Production 不使用测试收费
结语
使用 Gadget 和 Mantle 构建 Shopify Public App 时,可以把复杂流程拆成三层:
Shopify
↓
负责应用安装、商店授权和官方计费确认
Gadget
↓
负责数据库、后端、React Admin 和 Shopify 连接
Mantle
↓
负责套餐、试用期、订阅状态和计费业务逻辑
完整开发链路如下:
创建 Shopify Public App
↓
创建 Gadget 应用
↓
连接 Shopify
↓
创建 Mantle 应用
↓
配置 App ID 和 API Key
↓
注册每一家安装商店
↓
保存商店计费 Token
↓
创建 Basic 和 Pro 套餐
↓
开发 Plans 页面
↓
启动 Shopify 计费确认
↓
读取订阅状态
↓
按照计划开放功能
↓
部署 Production
↓
提交 Shopify App Store 审核
Gadget 让开发者不用从零搭建 Shopify OAuth、数据库和应用后台,Mantle 则减少了套餐、试用期和订阅状态管理的重复工作。
但计费系统仍然是商业应用的核心部分。正式上线前,必须确认:
- 应用分发方式正确
- 计费模式符合 Shopify 当前要求
- 测试收费和真实收费没有混用
- 订阅状态由服务端验证
- 取消、退款和到期流程有明确处理方式
- 隐私、服务条款和客户支持页面已经准备完成
这样构建出来的 Public App,才不仅能完成安装和收费,也能在生产环境中稳定管理商家的订阅生命周期。




