---
title: "客户端 SDK"
description: "在 H5 和微信小程序中接入公共页面、会话、请求与作用域保护。"
canonical_url: "https://docs.mcent.cn/qicore/client-sdk"
---
# 客户端 SDK

> 在 H5 和微信小程序中接入公共页面、会话、请求与作用域保护。

`@qicore/application-sdk/client/*` 提供 H5 与小程序共用的页面、组件、请求、会话和支付交互源码。业务应用将它们与自己的页面组合，由自己的构建流程产出客户端。

SDK 不携带某个业务应用的默认身份。客户端必须在构建时注入自己的 `__QICORE_APPLICATION_KEY__`，并使用匹配的 SDK kit 和锁文件。

## 应用工程提供的运行环境

SDK 交付的是源码及素材，不是完整 uni-app 工程。应用工程提供 `vue`、`@dcloudio/uni-app` 和公共富文本页面使用的 `mp-html`，以及所选 H5/小程序目标对应的 uni-app 平台包与构建工具。依赖版本由应用自己的匹配工具链和锁文件管理，不能只安装 SDK 后使用普通 Node 或普通 Vue 编译器运行全部页面。

SDK 页面同时使用 `import.meta.env.UNI_PLATFORM` 和 `H5 / MP-WEIXIN` 条件编译指令。交给对应 uni-app 工具链选择分支；平台全局对象、生命周期和模板标签也由该环境提供。构建配置需注入应用业务 key，例如以下 Vite `define` 片段：

```ts
define: {
  __QICORE_APPLICATION_KEY__: JSON.stringify('notes-app')
}
```

应用构建器将公共页面、组件及配套 `static` 素材与自身业务输入组合，并保留相对导入关系；不能只搬页面而漏掉登录、会员和支付素材。生产 `static/runtime.json` 由发布流程另行注入，不能从共享 SDK 取得或固化为构建常量。

## 运行配置从哪里来

使用 `client/lib/runtime-config` 的 `runtimeConfig()` 读取配置，不将生产运行身份编译进业务 bundle。

<table>
<thead>
  <tr>
    <th>
      环境
    </th>
    
    <th>
      配置来源与核验
    </th>
  </tr>
</thead>

<tbody>
  <tr>
    <td>
      H5
    </td>
    
    <td>
      服务端注入的 <code>
        qicore-h5-runtime
      </code>
      
       数据节点；核对应用 key、租户、API 基址和页面基址
    </td>
  </tr>
  
  <tr>
    <td>
      小程序开发
    </td>
    
    <td>
      显式开发环境配置与微信运行环境读取的 AppID
    </td>
  </tr>
  
  <tr>
    <td>
      小程序正式包
    </td>
    
    <td>
      发布流程注入的 <code>
        static/runtime.json
      </code>
      
      ；核对站点、HTTPS API 域名及当前小程序 AppID
    </td>
  </tr>
</tbody>
</table>

H5 的 API 基址为 `/api/v1`；页面基址由站点入口提供。应用不要通过 URL 查询参数选择任意其他应用。

正式小程序的运行文件包含 `siteId`、`apiDomain` 和 `appId`。它属于发布配置，不能用示例值冒充真实站点，也不要在应用源码中导入它并固化进 bundle。

## 请求接口

```ts
import {
  api,
  ensureSession,
  ApiError
} from '@qicore/application-sdk/client/lib/api'

await ensureSession()
const profile = await api('/profile')
```

`api<T>(path, method?, data?, headers?)` 返回成功响应中的 `data`。当前方法集合为 `GET / POST / PUT / DELETE`，默认 `GET`；第四个参数是额外请求头，不是控制台 SDK 的 `AbortSignal` 选项。

路径解析规则如下：

<table>
<thead>
  <tr>
    <th>
      传入路径
    </th>
    
    <th>
      请求目标
    </th>
  </tr>
</thead>

<tbody>
  <tr>
    <td>
      <code>
        /profile
      </code>
    </td>
    
    <td>
      <code>
        /api/v1/apps/<当前应用>/profile
      </code>
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        /notes
      </code>
    </td>
    
    <td>
      <code>
        /api/v1/apps/<当前应用>/notes
      </code>
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        /consumer/auth/login
      </code>
    </td>
    
    <td>
      <code>
        /api/v1/consumer/auth/login
      </code>
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        /public/site
      </code>
    </td>
    
    <td>
      <code>
        /api/v1/public/site
      </code>
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        /apps/<当前应用>/notes
      </code>
    </td>
    
    <td>
      使用已声明应用前缀，不重复拼接
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        /apps/<其他应用>/notes
      </code>
    </td>
    
    <td>
      以 <code>
        APPLICATION_MISMATCH
      </code>
      
       拒绝
    </td>
  </tr>
</tbody>
</table>

`notes` 是示例业务路由，需要应用自行声明并实现；SDK 不会自动生成业务 API。

普通应用请求自动附带当前会话的 Bearer Token；`/consumer/auth/*` 和 `/public/*` 是公共入口，不自动附带该令牌。

成功响应必须有 `data` 字段。异常通过 `ApiError` 暴露 `code`、`message`、`status`；网络未知结果使用 `NETWORK_UNKNOWN`，不要据此重新创建订单或更换幂等编号。

## 登录与退出

优先复用公共登录页面和流程：

<table>
<thead>
  <tr>
    <th>
      方法
    </th>
    
    <th>
      用途
    </th>
  </tr>
</thead>

<tbody>
  <tr>
    <td>
      <code>
        ensureSession({ redirect?: boolean })
      </code>
    </td>
    
    <td>
      检查当前运行范围内的本地会话；缺失时按选项引导登录
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        consumerReady()
      </code>
    </td>
    
    <td>
      无跳转地确认当前消费者会话是否可用
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        consumerLogin()
      </code>
    </td>
    
    <td>
      打开对应端的公共登录入口
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        loginPassword(account, password, register?, consent?, loginType?)
      </code>
    </td>
    
    <td>
      账号/手机号密码登录，或按配置注册
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        loginSms(phone, code, consent?)
      </code>
    </td>
    
    <td>
      手机验证码登录
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        loginWechat()
      </code>
    </td>
    
    <td>
      微信小程序登录，核对配置 AppID
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        startOfficialLogin(returnPath?, consent?)
      </code>
    </td>
    
    <td>
      H5 公众号授权流程
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        signOut()
      </code>
    </td>
    
    <td>
      清理本地会话，并记录主动退出状态
    </td>
  </tr>
</tbody>
</table>

`ensureSession` 的本地检查不能代替 API 服务端鉴权。原始消费者令牌仍由服务端核验当前会话、租户、应用和消费者状态。

`signOut()` 处理本地状态；需要撤销当前服务端会话时，先调用当前应用的 `DELETE /session`，成功后再清理本地状态。

## 业务状态必须按用户隔离

公共身份始终限定 `tenantId / applicationKey / userId`。相同昵称、手机号或账号文本不能作为跨域合并依据。

<table>
<thead>
  <tr>
    <th>
      辅助方法
    </th>
    
    <th>
      实际含义
    </th>
  </tr>
</thead>

<tbody>
  <tr>
    <td>
      <code>
        sessionStorageKey(key)
      </code>
    </td>
    
    <td>
      给 key 添加租户和应用后缀，本身不添加用户编号
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        identityScope()
      </code>
    </td>
    
    <td>
      当前已记录身份的租户、应用、用户组合
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        consumerScope()
      </code>
    </td>
    
    <td>
      身份组合及当前令牌变化标识，用于判断异步结果是否仍属于当前会话
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        getToken()
      </code>
    </td>
    
    <td>
      当前租户和应用范围内保存的消费者令牌
    </td>
  </tr>
</tbody>
</table>

用户草稿、私有列表和缓存不能只使用 `sessionStorageKey('draft')`。在确认已登录后，使用完整用户身份构造业务 key；切换用户时重新读取并清空上一身份的页面状态。

`consumerScope()` 含令牌信息，只用于内存中的相等比较，不写入日志、URL、埋点或持久业务 key。异步结果应用前还应核对 API 基址：

```ts
import { api, consumerScope, ensureSession }
  from '@qicore/application-sdk/client/lib/api'
import { runtimeConfig }
  from '@qicore/application-sdk/client/lib/runtime-config'

await ensureSession()
const own = consumerScope()
const apiBase = runtimeConfig().apiBase
const data = await api('/notes')
if (own === consumerScope() && apiBase === runtimeConfig().apiBase) {
  // 此时才把 data 应用到当前页面。
}
```

## 公共页面、样式与支付

公共登录、个人中心、帮助、会员、钱包、充值和订单页面可以由应用装配复用。业务页面负责自身业务，不复制公共会话和支付实现。

普通页面及继承主题的装修页复用公共留白和背景变量；底部导航复用 `ClientBottomNavigation`。小程序组件装配启用 `mergeVirtualHostAttributes`，需要验证实际组件样式与导航表现。

订单交互使用 `client/lib/order-payment` 中的 `payOrder(order, options?)` 和 `queryOrder(order)`。SDK 处理端内交互、外跳及查单，并校验会话和页面上下文。

外跳或授权尚未结束时，函数可能返回原订单；回到页面后应再次查询。Promise 正常完成也不能直接解释为付款成功。

客户端支付成功回调不是资金回执。网络未知结果先查询原订单；微信、支付宝、余额与虚拟支付的适用条件继续由宿主和渠道配置核验。

## 验证范围

至少覆盖 H5 与小程序条件分支、登录/退出、租户和用户切换、迟到响应、支付未知结果及公开资源路径。H5 构建或截图不能代替微信模拟器与真机权限联调。

接口响应和 HTTP 身份约定见[HTTP 接口](https://docs.mcent.cn/qicore/http-api)，资金与存储边界见[存储与事务](https://docs.mcent.cn/qicore/storage-and-transactions)。


## Sitemap

See the full [sitemap](https://docs.mcent.cn/sitemap.md) for all pages.
