跳到主要内容

用 Deep Link 引导商家安装/启用你的主题应用扩展

本文教你为主题应用扩展(Theme App Extension)构造深度链接(Deep Link):商家点一下链接就被带进主题编辑器,并自动把你的扩展装到指定位置(App Block)或定位到开关引导其开启(App Embed),免去"自己去主题装修里找、加、调位置"的一连串手动操作。

备注

本文假设你已经会编写主题应用扩展。如果还不熟扩展的代码结构与 Schema,请先阅读 创建主题扩展主题扩展参考

主题应用扩展有两种形态,引导方式也对应两套深度链接:

扩展类型商家侧位置引导目标用哪种深度链接
App Block(基础扩展)主题编辑器中手动添加到支持 @app 的卡片(Section)自动插入到指定页面 / 卡片 / block 位置…/admin/card?template=…&addAppBlockId=…&target=…
App Embed(嵌入扩展)主题编辑器 → 主题设置 → 应用嵌入,默认停用需手动开启跳转到应用管理面板,定位并引导开启开关…/admin/card?context=apps&activateAppBlockId=…

适用场景

什么时候需要给商家做这种引导:

  • 商家装完你的 app 后,不知道要去主题编辑器把扩展加上 / 开启,导致扩展实际没生效、使用率低。
  • 版本升级:把"待配置"入口放进 app 后台,一键把商家带到正确的安装/开启位置。

扩展标识 ${appId}/${blockName}

两种深度链接都要用到扩展标识,格式统一为 ${appId}/${blockName}

  • ${blockName}:扩展 blocks/ 目录下对应 block 的文件名(去掉 .liquid)。例如 blocks/t0609.liquidt0609本地即可确定。
  • ${appId}:平台分配给 app 的 handle 标识(如 painterbpublicapp-add-to-cart)。它不在你的项目文件里,必须从平台侧取。

获取方式:抓接口取 type 字段

接口返回里每个块的 type 字段就是完整的资源标识,形如 shoplazza://apps/{appId}/blocks/{blockName}/{实例id}两种扩展类型抓的接口不同:

App Embed(嵌入扩展) —— 打开「主题编辑 → 应用管理」,看控制台请求:

https://{store_domain}/admin/api/theme-extensions/available-blocks?operation=appEmbeds&limit=25

App Block(基础扩展) —— 打开主题编辑页面,看控制台请求:

https://{store_domain}/admin/api/theme-extensions/available-blocks?template=index&limit=100

在主题编辑器控制台抓取 available-blocks 接口

在返回里找到你扩展对应的项(按 name 认领),取它的 type。例如:

{
"name": { "en-US": "t0609(dev)", "zh-CN": "t0609(开发)" },
"type": "shoplazza://apps/custom-2386979-653920915276443053/blocks/t0609/653920915276443053"
}

去掉 shoplazza://apps/ 前缀和末尾实例 id,即得 ${appId}/${blockName}

type 资源标识推导出的 ${appId}/${blockName}
shoplazza://apps/custom-2386979-653920915276443053/blocks/t0609/653920915276443053custom-2386979-653920915276443053/t0609
shoplazza://apps/public/blocks/video_hero/41113124874493855public/video_hero
shoplazza://apps/painterb/blocks/strengthen_trust/279078739021670375painterb/strengthen_trust
shoplazza://apps/app-add-to-cart/blocks/add_to_cart/279078739021670375app-add-to-cart/add_to_cart
备注

全文以 {store_domain} 表示商家店铺的后台域名(如 your-shop.myshoplaza.com),实际拼链接时替换成商家自己的域名。


场景 A · 引导安装 App Block(基础扩展)

App Block 需要商家手动添加到页面的卡片里。用下面的深度链接,商家点击后会跳进主题编辑器,按你指定的位置自动插入,并提示商家保存/预览。

场景 A 引导安装 App Block 效果

URL 基本形态

https://{store_domain}/admin/card?template=${template}&addAppBlockId=${appId}/${blockName}&target=${target}

参数说明

参数含义取值说明是否必填
template目标页面模板模板名见文末附录的 template 取值表,如 productindex
addAppBlockId要插入的扩展 Id${appId}/${blockName},见上文
target插入目标位置两种写法:newAppsSection,或 sectionType:…/blockType:…/pos:…/toType:…
sectionType目标卡片类型支持逗号分隔多个以兼容不同主题,如 product,product_detail仅支持主题卡片,不支持扩展/插件卡片插入到指定卡片时必填
blockType目标 block 类型支持逗号分隔多个,如 buy_buttons,buttons;仅支持卡片内置的 block 类型插入到指定 block 时必填
toType插入类型section(作为新卡片)或 block(作为卡片内 block),不传时默认按 section 处理目标 section 存在、目标 block 不存在时必填
pos插入位置默认 1(锚定点下方);-1(锚定点上方)
is_auto_close安装完成后关闭编辑器、自动聚焦回原页面配合 window.open 打开编辑器,返回时调用 window.close()
desc插入成功后引导弹窗的提示文案多个用 , 分隔,与 addAppBlockId 一一对应
is_block_when_error批量插入时某项出错是否阻断后续true 阻断;默认不阻断

下面按"插入精度"从粗到细给三种用法。

用法 ①:作为新卡片插入到任意页面

用法①:作为新卡片插入到任意页面

不指定具体卡片,直接作为一张新卡片插入到目标页面(target=newAppsSection)。

https://{store_domain}/admin/card?template=product&addAppBlockId=custom-2386979-653920915276443053/t0609&target=newAppsSection
警告

目标 template 必须在主题支持的规范内。不在规范内时默认跳转到首页,且不插入、不提示。

用法 ②:插入到指定的目标卡片(Section)

用法②:插入到指定的目标卡片

指定 sectionType 定位到某张卡片,用 toType 决定是作为相邻卡片插入还是塞进卡片内部。

https://{store_domain}/admin/card?template=${template}&addAppBlockId=${appId}/${blockName}&target=sectionType:${sectionType}/pos:${pos}/toType:${toType}
  • 插到"商品详情"卡片下方(作为 section):
https://{store_domain}/admin/card?template=product&addAppBlockId=custom-2386979-653920915276443053/t0609&target=sectionType:product_detail/pos:1/toType:section
  • 插到"商品详情"卡片上方(作为 section):把 pos 改为 -1
  • 塞进"商品详情"卡片内部(作为 block):把 toType 改为 block
https://{store_domain}/admin/card?template=product&addAppBlockId=custom-2386979-653920915276443053/t0609&target=sectionType:product_detail/toType:block
警告
  1. 作为 section 插入、且目标 section 存在时,pos 生效(上方/下方),默认下方。
  2. 作为 block 插入、但目标 block 不存在时,位置信息不生效,默认插到卡片 block 列表最后。
  3. 目标 block 不存在时必须指定 toType,否则不插入。
  4. 指定的目标卡片不存在时,默认退化为"作为新卡片插入到指定页面"。

用法 ③:插入到指定 block 的相邻位置

用法③:插入到指定 block 的相邻位置

进一步指定 blockType,把扩展插到某个 block 的紧邻上/下方。

https://{store_domain}/admin/card?template=${template}&addAppBlockId=${appId}/${blockName}&target=sectionType:${sectionType}/blockType:${blockType}/pos:${pos}
  • 插到"商品详情"的"加购按钮"上方
https://{store_domain}/admin/card?template=product&addAppBlockId=custom-2386979-653920915276443053/t0609&target=sectionType:product_detail/blockType:buy_buttons/pos:-1
  • 插到"加购按钮"下方:把 pos 改为 1
备注
  1. 目标 block 存在时定位生效:1 下方,-1 上方。
  2. 指定到 block 时默认插入类型即为 block,此时 toType 不生效。
  3. 目标 block 已被商家删除时,默认插到目标卡片 block 列表最后。

批量插入多个 App Block

addAppBlockId / target / template / desc 都支持用英文逗号 , 分隔传多个,一一对应,编辑器按队列逐个插入,商家用「上一步 / 下一步」翻页确认。

https://{store_domain}/admin/card?addAppBlockId=appA/block1,appB/block2&target=sectionType:product_detail/toType:block,newAppsSection&template=product,product&desc=组件A已添加,组件B已添加
  • desc 给每个插入项单独设置成功提示文案。
  • is_block_when_error=true 后,队列中某项失败会中断、不再处理后续项;默认不中断,商家可「下一步」继续。

插入规则与边界(适用于以上所有用法)

平台按"页面 → 卡片 → block"逐级定位,找不到就按下面的规则回退。若存在多个匹配的锚定位置,取最下面的那个作为基准。

插入动作行为
插入 block(页头、页尾等固定卡片同理)有锚定位置 → 按锚定位置插入;无锚定位置 → 插到当前卡片最后;要插入的卡片不存在 → 不插入
插入 section(卡片)有锚定卡片位置 → 按锚定位置插入;无锚定位置 → 插到当前可配置卡片的最下方;整个页面找不到 → 不插入、也不提示

此外还有几条自动行为:

  • 跨页跳转template 指定的页面不是当前页时,会先跳转到目标页面再执行插入。
  • 降级到底部:找不到目标 Section、但页面未达 Section 数量上限时,组件块会被插到页面底部,并提示商家自行拖拽调整。
  • @app 嵌套查找:目标 Section 不直接支持 @app 类型 Block 时,会递归向下查找第一个支持 App 插入的子 Section。

错误码与提示

插入失败时,编辑器会弹出对应错误提示:

错误码含义提示文案
TEMPLATE_NOT_MATCH当前主题没有目标 template 对应的页面当前主题未找到"{page}"模板,该卡片未添加,可联系客服进行确认
APP_NOT_FOUND可用组件列表里找不到目标 App当前主题未找到"{app}"插件组件块,该卡片未添加,可联系客服进行确认
NOT_MATCH_TARGETtarget 格式不对或没匹配到插入位置当前主题未找到目标位置,该插件卡片未添加,可联系客服进行确认
MAX_BLOCK_LIMIT目标区域 Block 数已达 schema 定义的上限目标区域已达到最大卡片数量,该卡片未添加,可联系客服进行确认
警告

拼接深度链接时,target 等含特殊字符的参数值需先做 encodeURIComponent 编码。


场景 B · 引导激活 App Embed(嵌入扩展)

App Embed 安装后默认停用,需要商家在"主题设置 → 应用嵌入"里手动开启。用下面的深度链接把商家直接带到应用管理面板,定位到你的嵌入扩展并引导其打开开关。

场景 B 引导激活 App Embed 效果

URL 形态

https://{store_domain}/admin/card?context=apps&activateAppBlockId=${appId}/${blockName}

示例(引导开启"加购"嵌入扩展):

https://{store_domain}/admin/card?context=apps&activateAppBlockId=app-add-to-cart/add_to_cart

激活 App Embed 的深度链接示例

参数说明

参数含义取值说明
context主题编辑器侧边栏上下文固定 apps,定位到"应用管理"面板
activateAppBlockId要激活的扩展 Id${appId}/${blockName};应用管理只支持嵌入扩展(App Embed)

自定义激活提示文案(guidePageName

商家点击激活 deeplink 后,主题编辑器(平台侧)会弹出一个确认弹窗,文案结构是固定模板,只有两个槽位可由扩展控制:

  • 标题:已开启{name} —— {name} 取你 block schema 的 name
  • 正文:可到{guidePageName}查看效果,完成后,请保存你的更改。 —— {guidePageName} 默认是「商品页 / product page」,可由扩展覆盖。

激活后的确认弹窗

要自定义正文里的页面名,在 block 的 {% schema %} 里加一个多语言对象 guidePageName(写法同 name):

{
"name": { "en-US": "v1-te-embed(dev)", "zh-CN": "v1-te-embed(开发)" },
"target": "body",
"guidePageName": { "en-US": "checkout page", "zh-CN": "结账页" },
"settings": []
}

重新部署后,弹窗正文即变为:可到结账页查看效果,完成后,请保存你的更改。

备注
  • 不配置 guidePageName 时,正文默认使用「商品页」。
  • 除这两个槽位外,提示语的其余文字(「可到…查看效果,完成后,请保存你的更改」)由主题编辑器写死,扩展无法修改。

检测扩展是否已安装

在 app 后台展示「待配置 / 已安装」状态、决定是否给商家显示引导入口(深度链接)之前,先检测扩展当前是否已装到主题里 / 已开启。两种扩展类型的检测方式不同:

扩展类型检测方式
App Block(基础扩展)themes/card-exist 接口,查页面模板里是否存在该 section / block 卡片
App Embed(嵌入扩展)拉主题 settings_data.json,在 current.app_embeds 里匹配并看 disabled

App Block:用 card-exist 接口

App Block 是被加进页面模板的 section / block 卡片。themes/card-exist 用于查认某个页面模板里是否包含某个 section / block 卡片。

GET https://{store_domain}/admin/api/themes/card-exist?templates=...&ext_app_id=...&ext_app_block=...

参数:

参数必填含义
templates页面模板名称,多个逗号隔开(如 productglobal_sections
theme_id店铺主题 id(默认当前主题)
card_name卡片名称
ext_app_id扩展 id
ext_app_block扩展卡片名(blockName)
show_more_info是否返回 template 详情

card-exist 接口返回示例

返回按 templates 分组,每组带 block_exist / section_exist,任一为 true 即视为已安装:

const fetchCardExist = async () => {
const { data } = await request.get('themes/card-exist', {
params: {
templates: 'global_sections',
ext_app_id: 'Geolocation',
ext_app_block: 'multi_market',
},
});
const info = data?.template_info?.global_sections;
return info?.block_exist || info?.section_exist;
};
备注

card-exist/admin/api/ 下的后台内部接口。app 嵌在商家后台里运行时,请求 base 已是当前店铺 admin 域名,代码里用相对路径 themes/card-exist 即可,无需写死域名。该接口只覆盖 App Block;App Embed 的开启状态查不到,见下一节。

App Embed:读 settings_data.json 匹配

App Embed 没有专用查询接口,开启状态记录在已发布主题的 settings_data.json 里。流程:拉配置 → 在 current.app_embeds 里按扩展标识匹配 → 看 disabled

第 1 步:拿到当前发布主题的 theme_id

GET https://{store_domain}/openapi/2025-06/themes/default-theme

第 2 步:拉取该主题的 settings_data.json

GET https://{store_domain}/openapi/2025-06/themes/{theme_id}/doc?type=config&location=settings_data.json

(OpenAPI 开放接口,需要 access token + 主题读取 scope。)

第 3 步:在 current.app_embeds 里匹配

app_embedsappId 为键,值是该 app 的嵌入块数组,每条带 typedisabled

{
"app_embeds": {
"custom-2386979-653765666276388655": [
{
"disabled": false,
"settings": [],
"type": "shoplazza://apps/custom-2386979-653765666276388655/blocks/v1-te-embed/653765666276388655"
}
]
}
}

${appId}/${blockName} 匹配 type、并看 disabled

function isAppEmbedEnabled(settingsData, appId, blockName) {
const entries = settingsData.current?.app_embeds?.[appId] ?? [];
const targetType = `shoplazza://apps/${appId}/blocks/${blockName}`;
return entries.some(
(e) =>
e?.disabled === false &&
typeof e?.type === 'string' &&
e.type.includes(targetType)
);
}
// isAppEmbedEnabled(data, 'custom-2386979-653765666276388655', 'v1-te-embed') === true

判定:

  • appId 键不存在 → 没安装。
  • 命中但 disabled: true → 安装了但未开启。
  • 命中且 disabled: false → 已开启。
警告

必须按 blockName 匹配 type,不能只看 appId 键——一个 app 可能挂多个嵌入块(同一 appId 数组里多条)。


附录:template 取值表

template 参数对应商家店铺的页面模板。常用取值如下(按使用频率排序):

页面template加载模板文件
首页indexindex.liquid
商品详情productproduct.liquid
专辑(分类)详情collectioncollection.liquid
自定义页面pagepage.liquid
购物车cartcart.liquid
搜索searchsearch.liquid
博客文章articlearticle.liquid
博客专辑blogblog.liquid
404404404.liquid
密码访问页passwordpassword.liquid
个人中心customers/accountcustomers/account.liquid
订单列表customers/ordercustomers/order.liquid
订单详情orderorder.liquid
登录logincustomers/login.liquid
注册registercustomers/register.liquid
结账页checkoutchick-next 实现
支付成功页checkoutorder_status.liquid