SHOPLINE AppBridge

1. 介绍

1.1. 概览

AppBridge 是 SHOPLINE 提供的一个 JavaScript SDK,通过 AppBridge 的 API 可以使 SHOPLINE 内嵌应用调用特定的 SHOPLINE Admin 开放功能以及渲染基于 SHOPLINE Admin 设计规范的组件,满足商家的特定需求。以下是 SHOPLINE AppBridge 的主要用途和功能:

  1. 嵌入式应用开发: SHOPLINE 允许开发人员在 SHOPLINE 商店的管理界面中创建嵌入式应用。通过AppBridge 无缝集成到 SHOPLINE 的后台管理中,使商家能够在一个界面中管理他们的店铺和使用应用功能。

  2. 与 SHOPLINE 后台通信: SHOPLINE AppBridge 提供了一套 API,使开发人员能够与 SHOPLINE 后台进行通信。这包括获取商店信息、执行 CRUD 操作、管理订单和产品等。

  3. 界面组件: AppBridge 提供了一系列的界面组件,例如 Modal(模态框)、Message(通知消息)、Loading(加载状态)等,帮助开发人员实现一致的用户体验,同时保持与 SHOPLINE 界面的一致性。

  4. 路由跳转: 提供了路由功能,使开发人员能够管理和控制 SHOPLINE 后台的导航,以确保用户能够流畅地浏览应用和商店。

  5. 认证和安全性: SHOPLINE AppBridge 提供了一种安全的认证机制,确保只有授权的应用能够访问商店数据,保障商家和客户的隐私和安全性。

总体而言,SHOPLINE AppBridge 旨在简化 SHOPLINE 应用的开发过程,提高应用的质量,并提供更好的用户体验。这使得开发人员能够专注于为商家提供有价值的功能,同时利用 SHOPLINE 提供的平台和工具。

1.2. 流程设计

阐述:目前,SHOPLINE通过使用iframe加载开发者提供的应用。由于iframe的性质,SHOPLINE Admin 与插件之间存在一定的隔离。为了弥合这种差异,AppBridge发挥了桥接的作用。它通过将底层通信消息序列化并定义相关的动作协议,使得SHOPLINE Admin与插件之间能够相互调用和感知,从而在交互方面实现最大程度的统一。

1.3 框架说明

2. 入门

2.1. 前提

要求

  • 本地安装过 Node.js,支持npm 包形式调用

  • :warning::使用SDK的应用必须为内嵌应用

2.2. 安装

npm install @shoplinedev/appbridge
### or
yarn add @shoplinedev/appbridge

2.3. 使用

2.3.1. 初始化

使用 AppBridge 的时候,需要事先进行客户端初始化,以此来建立通信机制

import Client, { shared } from '@shoplinedev/appbridge'
const app = Client.createApp({
  appKey: "appkey",
  host: shared.getHost()
})

提示:appKey需要与当前应用的appKey一致,否则会出现无法使用某些功能的问题。

API 名称 参数 描述 是否必须
createApp appKey 应用对应的appkey Y
host 当前登陆的域名,格式为{{handle}.myshopline.com} Y

2.3.2. 提示

如果你在通过使用shared.getHost()进行初始化后遇到问题,建议你尝试使用{handle}.myshopline.com来替代该函数。

2.3.3. 示例

import Client, { Modal } from '@shoplinedev/appbridge'

// 调用 Modal 组件
Modal.create(app).open({
  type: 'confirm',
  title: 'Hello SHOPLINE',
  content: 'Welcome to SHOPLINE'
})

3. 动作集

AppBridge设计了一系列动作集,使开发者能够通过调用相应的动作集来触发SHOPLINE Admin对应的组件或接口功能。

3.1. 授权

3.1.1. 概述

授权能力在AppBridge中扮演着一个关键的角色。由于当前插件的授权流程由开发者发起,考虑到插件处于内嵌状态,如果开发者通过服务端进行重定向到授权页面,可能会导致页面嵌套问题。因此,建议开发者使用前端调用该API来执行授权操作,以避免可能出现的授权问题

3.1.2. 示例

以下代码的调用将使你的页面自动跳转至SHOPLINE插件的授权页面

import { Oauth } from '@shoplinedev/appbridge'

Oauth.create(app).invoke({
  scope,
  appKey,
  redirectUri
})

3.1.3. 参数说明

名称 类型 描述 是否必须
scope string 应用所需要的权限,详情见权限点列表,多个权限点逗号分隔 Y
appKey string 应用所属的appkey Y
redirectUri string 应用配置的应用回调地址 Y

3.1.4. 调用时间点

商家点击你的应用后,SHOPLINE Admin 将加载你的应用并转至你配置的应用地址。建议将应用地址配置为你的接口服务地址,该接口用于验证当前商家是否已安装你的应用。当应用校验到店铺需要授权时,则可以调用此动作集帮助快速加载OAuth授权页面

商家点击你的应用后,SHOPLINE Admin 将加载你的应用并转至你配置的应用地址。建议将应用地址配置为你的接口服务地址,该接口用于验证当前商家是否已安装你的应用。当应用校验到店铺需要授权时,则可以调用此动作集帮助快速加载OAuth授权页面

3.2. 路由

3.2.1. 概述

路由机制使插件能够轻松跳转至SHOPLINE Admin内的任意页面,同时也可以通过调用相关的API在新的标签页中打开指定页面。举例来说,如果你的应用是支付应用,在授权安装完成后进入你的应用,你可以通过调用路由机制的相关API来实现无缝跳转到支付应用的激活页面

3.2.2. 示例

import { Redirect } from '@shoplinedev/appbridge'
const redirect = Redirect.create(app)

const replaceUri = 'https://www.shopline.com'
// 替换浏览器的地址并进行跳转至SHOPLINE官网
redirect.replaceTo(replaceUri)

const redirectUri = 'admin/orders'
// 由插件跳转至商家订单页面
redirect.routerTo(redirectUri)

// 跳转到指定业务相关页面
redirect.ToAdminPage(Redirect.ADMIN_SECTION.CATEGORIES, {
  create: true,
  id: '1000001',
  newContext: true
})

3.2.3. API说明

名称 类型 说明
replaceTo (url: string)=> void 替换浏览器的地址并进行跳转至指定地址
routerTo (url: string)=> void 跳转至SHOPLINE admin对应的路由页面,如订单页面’admin/orders’
ToAdminPage (url: Redirect.ADMIN_SECTION, options: RedirectOptions)=> void 跳转到指定业务对应的页面

3.2.4. 类型说明

ADMIN_SECTION

业务页面名称 描述 特殊说明
SALES 折扣列表页
OAUTH 授权页 参数为授权流程对应的参数
ORDERS 订单列表
CUSTOMER 客户列表
CATEGORIES 商品列表

RedirectOptions

名称 说明 是否必须
id 资源页面对应的ID,当存在该值时会跳转至对应的详情页 N
newContext 是否新开页面 N
create 是否跳转至对应的资源创建页 N

3.3. 保存栏

3.3.1. 概述

在 SHOPLINE AppBridge JavaScript 库中,Savebar 是一个用于在 SHOPLINE 应用中管理保存操作的组件。它的主要作用是提供一个上下文相关的保存栏,用于通知用户在编辑页面或表单中所做的更改,并允许用户保存这些更改。通过 Savebar,开发人员可以轻松实现保存功能,并提供与 SHOPLINE 后台一致的用户体验。

以下是 Savebar 的一些主要用途和功能:

  1. 上下文感知:Savebar 是上下文感知的,它可以检测到用户在编辑页面或表单中所做的更改,并在需要时显示保存栏,通知用户有未保存的更改。

  2. 保存操作: 用户可以通过保存栏上的按钮执行保存操作,将他们在页面上所做的更改提交到后端。这有助于确保用户的修改得到保存,而不会遗漏任何重要的更改。

  3. 实时反馈:Savebar 提供了实时的反馈,通知用户何时进行了保存操作以及保存的状态。这有助于用户了解操作的结果,并提高用户体验。

  4. 自定义配置: 开发人员可以根据应用的需要进行配置,例如定义保存操作的回调函数、自定义按钮文本等

效果图

3.3.2. 示例

import Client, { shared, SaveBar } from '@shoplinedev/appbridge'

const app = Client.createApp({
  appKey,
  host: shared.getHost()
})

const savebar = SaveBar.create(app)
// 触发显示savebar
const show = () => {
  savebar.show()
}

// 关闭savebar
const close = () => {
  savebar.hide()
}

// 设置保存按钮加载
const btnLoading = () => {
  savebar.setLoading(true)
}

3.3.3. API说明

API 名称 类型 描述
API 名称 类型 描述
show (config?: TShowConf) => void 唤起保存栏
hide () => void 关闭保存栏
setLoading (loading: boolean) => void 按钮是否loading
exitComfirm (option: TExitComfimOption) => void 调用退出拦截弹窗
getStatus Promise => {isEditing: bolean} 获取当前保存栏状态
subscribe (Action: ESaveBarAction,callback: () => void) => void 进行动作订阅
unsubscribe (Action: ESaveBarAction) => void 取消订阅,建议在组件卸载时进行取消订阅动作

3.3.4. 参数说明

名称 参数 类型 描述
TShowConf 可选参数
formType ‘edit’ ‘add’
onFinishText string
ignoreExitFun boolean
formLayoutEditing boolean
disabledBeforeFieldsChange boolean
disabled boolean
ignoreBlockRoutes string
hideSubmit boolean
destroyOnReset boolean
hideDivider boolean
usePageHeader boolean
TExitComfimOption
okText string 确认按钮文本
cancelText string 取消按钮文本
content string 弹框内容文本
reservedPage boolean 是否停留在当前页

3.3.5. 动作说明

当点击保存/取消按钮的时候,需要触发对应的订阅动作,以此来执行你所定义的回调事件

动作名称 描述
DISCARD_SAVEBAR 点击退出编辑进行回调
SAVE_SAVEBAR 点击保存栏保存后回调开发者自定义函数
LOADING_SAVEBAR 当前保存栏处于Loading状态时回调
CANCEL_LOADING_SAVEBAR 结束Loading状态时回调
EXIT_SAVEBAR_COMFIRM 确认退出当前操作回调
GET_STATUS 获取当前保存栏状态

3.3.6. 订阅动作示例

import Client, { shared, SaveBar } from '@shoplindev/AppBridge'

const app = Client.createApp({
  appKey,
  host: shared.getHost()
})

const savebar = SaveBar.create(app)
const saveCallBack = () => {
  savebar.subscribe(SaveBar.ACTION.SAVE_SAVEBAR, () => {
    try {
      // 当点击保存时,会触发以下代码
      console.log('this is callBack') 
    } catch {
      // 当回调抛出异常,需要重新订阅,否则重新进行保存会导致回调失效
      saveCallBack()
    }
  })
}

// 取消订阅
const discardsaveCallBack = () => {
  savebar.unsubscribe(SaveBar.ACTION.SAVE_SAVEBAR)
}

3.3.7. 特殊说明

  1. 在执行订阅动作操作时,请注意在退出页面时手动执行取消订阅动作操作,以避免在后续重新进入页面时再次订阅,可能导致回调事件被多次触发的情况。

  2. 当执行保存操作时,保存异常导致回调事件被消费完毕但并未退出编辑状态时,如果后续继续保存操作发现无回调事件调用,需要在保存异常操作后,重新进行事件订阅。

3.4. 会话机制

3.4.1. SessionToken

在sessionToken之前,开发者通常通过在请求的cookie中包含当前店铺的handle和appkey来判断应用请求的合法性。在服务端,通过验证这两个参数来确保当前请求的合法性。尽管这种方案适用于大多数业务场景,但在安全性方面存在一定的风险,可能容易受到请求伪造的攻击。因此,我们引入了sessionToken机制,以提高插件请求的安全性。

3.4.2. SessionToken流程

3.4.3. 使用

代码示例:以下代码为获取SessionToken并加入验证机制操作

import { shared } from '@shoplinedev/appbridge'
const app = Client.createApp({
  appKey: 'your appkey',
  host: `{handle}.myshopline.com`
})

const getSessionToken = async () => {
  return shared.getSessionToken(app)
}

// 将sessionToken加入请求头中
const request = async (url, method = 'GET', headers = {}, data = nul) => {
  return fetch(url, {
    method: method,
    headers: {
      ...headers,
      'X-Requested-With', 'XMLHttpRequest',
      'Authorization': `Bearer ${await getSessionToken()}`,
    },
    body: data
  })
    .then(response => {
      if (!response.ok) {
        throw new Error('Network response was not ok');
      }
      return response.json();
    })
    .catch(error => {
      console.error('There was a problem with the fetch operation:', error);
      throw error;
    });
}

说明:SessionToken属于JWT,所以本质上主要由三部分组成**,Header, PayloadSignature,**以下主要详细说明这三部分

3.4.4. Header

{
  "typ": "JWT",  
  "alg": "HS256" 
}

3.4.5. Payload

{
  "handle": "shop-handle",
  "sid": "session ID",
  "iss": "shop-name.myshopline.com", 
  "aud": "client ID&appKey",
  "sub": "user ID&uid", 
  "iat": 1678847783, 
  "nbf": 1678847783,
  "exp": 1678847843,
  "jti": "3f626004-dbf9-4736-a0d6-e0e60464ef70"
}
  • iss:发行者,当前为店铺的admin地址

  • aud:audience,当前为应用的appKey

  • sub:签发主体,当前为登录用户的uid

  • exp:超时时间,激活时间+1分钟

  • nbf:激活时间,签发后立即生效

  • iat:发行时间,当前为token的签发时间

  • jti:随机的uuid

  • sid:用户会话ID,当前为http session id

3.4.6. Signature

3.4.7. 校验机制

会话令牌是具有以下结构的 JWT 字符串:..。可以从负载中获取会话详细信息,然后验证内容,如下所示:

  1. exp从payload中提取值。验证日期时间值是否为未来值。

  2. nbf从payload中提取值。验证日期时间值是否是过去的值。

  3. 从payload中提取isshandle。handle字段应该与iss中的handle一致。例如,myshop.mySHOPLINE.com.

  4. aud从payload中提取值。验证该值是否与appKey一致

  5. sub从payload中提取值。这是发出请求的用户的 ID,判断是否为空

如果上述任何步骤失败,则丢弃payload,停止处理请求,并以错误响应。

3.4.8. 验证会话令牌签名

  1. appSecretbase64 加密,设置加密类型为 hmacsha256 得到 signKey

  2. signKey 解密 token,得到 payload

/**
 * decode bearer token => jwt payload
 * @param config
 * @returns
 */
export const decodeSessionToken = (config: ApiConfigParams) => {
  return async (token: string): Promise<JwtPayload> => {
    let payload: JwtPayload;
    try {
      payload = (await jwt.verify(token, Base64.encode(config.appSecret), {
        algorithms: ['HS256'],
      })) as JwtPayload;
    } catch (error) {
      throw new InvalidJwtError(`Failed to parse bearer token '${token}': ${error.message}`);
    }
    return payload;
  };
};

3.4.9. 生命周期

sessionToken 的有效时间为一分钟。在这一分钟内,SHOPLINE Admin会对当前的sessionToken进行缓存。当插件通过AppBridge方式发起请求时,如果在有效期内,将返回缓存的sessionToken值;否则,系统将重新请求接口并返回新的sessionToken

3.5. 资源选择器

3.5.1. 概述

资源选择器允许开发者选择并与商店资源(如产品、订单、客户等)进行交互。通过使用该组件,开发者可以构建自定义的界面,使商家能够方便地选择、编辑和管理其商店的各种资源。这可以包括在应用中创建新的活动、编辑现有资源,或者执行其他与商店数据相关的操作。

3.5.2. 示例

import Client, { ResourcePicker } from '@shoplinedev/appbridge'

const app = Client.createApp({
  appKey,
  host: shared.getHost()
})

const resourcePicker = ResourcePicker.create(app)

// 打开资源选择器
resourcePicker.open({
  type: 'Variant'
})

// 点击 选择/添加 按钮后进行的回调操作
resourcePicker.subscribe(ResourcePicker.ACTION.SELECTED, (selectedList) => {
  // selectedList数据为勾选的对应行数据
  console.log(selectedList)
})

3.5.3. 效果图

3.5.4. API说明

API名称 类型 描述
open (TRousourcePickerOption)=> void 打开资源选择器弹窗
subscribe (action: RousourcePicker.ACTION, callbackFn: Function) => void 订阅操作
unsubscribe (action: RousourcePicker.ACTION) => void 取消订阅操作

3.5.5. 类型说明

TRousourcePickerOption 名称 描述 类型 是否必须
title 弹窗标题文本 string N
okBtnText 选择按钮文本 string N
type 弹窗数据类型 ‘Product’ ‘Variant’ N

3.5.6. 动作说明

动作名称 描述
SELECTED 点击选择/新增后执行的回调操作
DISCARD 点击取消后执行的回调操作

3.5.7. 特殊说明

如果你在代码中执行了订阅操作,建议在订阅回调事件中进行取消订阅的操作,或者在退出页面时执行取消订阅的操作。否则,当你后续重新进入页面或重新调用组件时,可能会出现回调事件被多次执行的问题。

3.6. 自定义资源选择器

3.6.1. 概述

自定义资源选择器是为开发者提供的一种弹窗组件,用于进行资源选择。与标准资源选择器不同的是,在自定义资源选择器中,开发者可以自行定义数据。开发者只需按照AppBridge定义的数据结构,即可在SHOPLINE Admin中实现无缝的资源选择操作。这样设计使得界面交互更加符合SHOPLINE的统一UI风格。

3.6.2. 示例

import Client, { UnstableResourcePicker } from '@shoplinedev/appbridge'

const app = Client.createApp({
  appKey,
  host: shared.getHost()
})

const unstableResourcePicker = UnstableResourcePicker.create(app)

// 打开资源选择器
unstableResourcePicker.open()

// 点击 选择/添加 按钮后进行的回调操作
unstableResourcePicker.subscribe(unstableResourcePicker.ACTION.SELECTED, (selectedList) => {
  // selectedList数据为勾选的对应行数据
  console.log(selectedList)
})

3.6.3. 效果图

3.6.4. API说明

API名称 类型 描述
open (option: TUnstablePickerOption)=> void 打开自定义资源选择器弹窗
subscribe (action: RousourcePicker.ACTION, callbackFn: Function) => void 订阅操作
unsubscribe (action: RousourcePicker.ACTION) => void 取消订阅操作

3.6.5. 类型说明

名称 参数 描述 类型 是否必须
TUnstablePickerOption title 弹窗标题文本 string N
okBtnText 选择按钮文本 string N

3.6.6. 动作说明

动作名称 描述
SELECTED 点击选择/新增后执行的回调操作
DISCARD 点击取消后执行的回调操作

3.7. 弹窗

3.7.1. 概述

SHOPLINE AppBridge Modal 是 SHOPLINE AppBridge JavaScript 库中的一部分,用于在 SHOPLINE 应用中创建模态框(Modal)。Modal 是一个弹出式窗口,通常用于显示额外的信息、收集用户输入或执行特定的任务,而不必离开当前页面。

具体而言,AppBridge Modal 提供了一种在 SHOPLINE 上下文中创建和管理模态框的方式。通过使用这个库,开发人员可以在 SHOPLINE 应用中轻松地实现以下功能:

  1. 弹窗通知: 在用户界面上显示通知、警告或确认消息。

  2. 操作确认: 弹出模态框来确保用户在执行重要操作之前确认。

3.7.2. 示例

import Client, { Modal } from '@shoplinedev/appbridge'

const app = Client.createApp({
  appKey,
  host: shared.getHost()
})

const modal = Modal.create(app)

// 打开 Modal
modal.open({
  type: 'confirm',
  title: 'SHOPLINE',
  content: 'Hello, Welcome To SHOPLINE'
})

3.7.3. 效果图

3.7.4. API说明

API名称 类型 描述
open (option: TModalOption)=> void 打开 Modal

3.7.5. 类型说明

名称 参数 描述 类型 是否必须
TModalOption title 弹窗标题文本 string Y
type 弹窗类型 confirm | danger | error | warn | info | success Y
content 弹窗内容 string Y

3.8. 消息

3.8.1. 概述

在 SHOPLINE AppBridge JavaScript 库中,Message 是用于在 SHOPLINE 应用中显示短暂通知消息的组件。Message 消息通常是一种轻量级的通知,用于向用户传达一些信息,如成功操作、错误消息或其他短时提示。

SHOPLINE AppBridge Message 具有以下特点和用途:

  1. 短时通知: Message 消息是短暂的通知,通常会在屏幕的某个位置弹出一小段时间,然后自动消失。

  2. 用户友好: 通过 Message,开发人员可以以非侵入性的方式向用户提供信息,而不会打断他们的工作流程。这对于向用户传达成功操作或错误消息而不打断他们的体验非常有用。

  3. 异步操作反馈: 在执行异步操作时,Message 可以用来提供反馈,以通知用户操作的进度或结果。

3.8.2. 示例

import Client, { Message } from '@shoplinedev/appbridge'

const app = Client.createApp({
  appKey,
  host: shared.getHost()
})

const message = Message.create(app)

// 打开 Modal
message.open({
  type: 'success',
  messageInfo: 'create product success'
})

3.8.3. 效果图

3.8.4. API 说明

API名称 类型 描述
open (option: TMessageOption)=> void 打开消息通知

3.8.5. 类型说明

名称 参数 描述 类型 是否必须
TMessageOption messageInfo 消息标题文本 string Y
type 消息类型 “error” “warn”

3.9. 全屏

3.9.1. 概述

在 SHOPLINE AppBridge JavaScript 库中,Fullscreen 是用于全屏显示内容的组件。通过 Fullscreen,开发人员可以在 SHOPLINE 应用中实现全屏体验,将用户焦点完全集中在特定的内容或任务上。这通常用于展示模态对话框、编辑器、图表等需要占据整个屏幕空间的功能。

3.9.2. 示例

import Client, { FullScreen } from '@shoplinedev/appbridge'

const app = Client.createApp({
  appKey,
  host: shared.getHost()
})

const fullScreen = FullScreen.create(app)

// 插件全屏显示
fullScreen.trigger()

// 2秒后退出全屏
setTimeout(() => {
  fullScreen.exit()
})

3.9.3. 效果图

3.9.4. API 说明

API名称 类型 描述
trigger ()=> void 触发全屏
exit ()=> void 退出全屏

3.10. 加载

3.10.1. 概述

在SHOPLINE AppBridge JavaScript 库中,Loading 是用于在 SHOPLINE 应用中显示加载状态的组件。通过 Loading,开发人员可以提供用户有关正在进行的操作的反馈,确保用户知道应用正在处理请求或加载数据。这有助于提高用户体验,使用户感到应用是响应的,并在需要时等待操作完成。

以下是 Loading 的一些主要用途:

  1. 异步操作反馈: 在执行可能需要一些时间完成的异步操作时,Loading 可以显示一个加载指示器,让用户知道应用正在处理请求。

  2. 数据加载状态: 在从后端获取大量数据或执行复杂计算时,Loading 可以用于显示加载状态,以避免用户感到不确定或无响应。

  3. 操作等待: 当用户执行需要一些时间的操作时,例如提交表单或保存更改,Loading 可以提供反馈,让用户知道操作正在进行中。

3.10.2. 示例

import Client, { Loading, shared } from '@shoplinedev/appbridge'

const app = Client.createApp({
  appKey,
  host: shared.getHost()
})

const loading = Loading.create(app)

// 触发Loading效果
loading.trigger()

// 2秒后结束Loading
setTimeout(() => {
  loading.exit()
})

3.10.3. 效果图

3.10.4. API说明

API名称 类型 描述
trigger ()=> void 触发加载效果
exit ()=> void 结束加载效果

3.11. 状态

3.11.1. 概述

在SHOPLINE AppBridge JavaScript 库中,AppState 是用于在 SHOPLINE 应用中获取当前店铺的某部分对应的数据,比如当前店铺的Handle,当前店铺的联系人,或者当前店铺的语种。

3.11.2. 示例

import Client, { Loading, shared } from '@shoplinedev/appbridge'

const app = Client.createApp({
  appKey,
  host: shared.getHost()
})

const getAppState = async () => {
  const state = await shared.getAppstate(app)
}

3.12. 认证请求

3.12.1. 概述

认证请求是基于Sessiontoken进行封装的工具API,帮助开发者在发送http请求时将对应的Sessiontoken存入当前http请求的请求头里,开发者无需额外进行获取Sessiontoken的操作。

3.12.2. 示例

import { authenticatedFetch } from '@shoplinedev/appbridge'

const fetch = authenticatedFetch(app)

// 正常发送请求
fetch(url, {
  method: 'POST',
  header: {
    'X-SHOPLINE': 'SHOPLINE'
  }
})