im2-link-jssdk
v1.4.3
Published
`im2-link-jssdk` 是 H5 小程序与宿主 App/WebView 之间的通信 SDK。Web 侧通过统一 API 请求宿主完成支付 、导航、系统分享、下载、网络请求等原生能力;SDK 负责平台识别、消息封装和回调分发。
Readme
im2-link-jssdk
im2-link-jssdk 是 H5 小程序与宿主 App/WebView 之间的通信 SDK。Web 侧通过统一 API 请求宿主完成支付
、导航、系统分享、下载、网络请求等原生能力;SDK 负责平台识别、消息封装和回调分发。
本 README 以当前源码为准:公开方法、参数类型、消息类型和回调协议均与
src/index.ts、src/types/common.ts保持一致。
在线文档
以上地址指向 npm 当前已发布版本的类型文档。仓库中的最新改动会在下次发布后同步到这两个地址。
目录
安装
npm install im2-link-jssdkSDK 同时提供 ESM、CommonJS 和 TypeScript 类型声明。
快速开始
import IMSDK from 'im2-link-jssdk';
const sdk = new IMSDK({
id: 'your-app-id',
token: 'your-app-token',
debug: false
});
sdk.share({
type: 'system',
param: {
title: '分享标题',
text: '分享描述',
shareUrl: 'https://example.com?share_source=tg',
imageUrl: 'https://example.com/share.png'
}
});初始化参数:
| 参数 | 类型 | 必填 | 说明 |
| ------- | --------- | ---- | ----------------------------------- |
| id | string | 是 | 小程序 App ID |
| token | string | 是 | 小程序 App Token |
| debug | boolean | 否 | 是否打印 SDK 调试日志,默认 false |
H5 小程序要求
- 使用 Webpack、Rspack 或其他可输出浏览器资源的构建工具。
- 使用 Hash 路由,路由状态由
window.location.hash承载。 - 构建入口文件命名为
index.html。 - 发布时将构建产物打包为 ZIP。
- SDK 依赖浏览器的
window和navigator,不要在 SSR 服务端执行初始化。
API 概览
| API | 宿主消息类型 | 用途 |
| ---------------------- | ----------------- | ------------------------ |
| platform | - | 获取当前运行平台 |
| callPayment | payment | 拉起原生支付 |
| callBack | back | 通知宿主返回 |
| callConversation | conversation | 打开指定用户会话 |
| callNavigate | navigate | 调用宿主导航 |
| permissions | permissions | 请求宿主权限能力 |
| callOpenMiniProgram | openminiapp | 打开其他小程序 |
| callSetBarColor | barcolor | 设置状态栏颜色 |
| callPopup | popup | 拉起原生弹框 |
| requestApi | requireAPI | 由宿主发起网络请求 |
| requestApp | requireAPP | 与宿主交换业务数据 |
| adjustInputBoxHeight | inputBoxHeight | 调整输入框高度 |
| switchLandscape | landscape | 切换横屏状态 |
| share | share | 业务分享或系统分享 |
| download | download | 请求宿主下载视频 |
| behaviorCaptcha | behaviorCaptcha | 发送行为验证码结果 |
| getCaptchaAccount | captchaAccount | 索取登录页账号与区号 |
| vibrate | vibrate | 触发宿主系统振动 |
平台信息
const platform = sdk.platform;返回值为:
type Platform = 'win' | 'mac' | 'unix' | 'linux' | 'android' | 'ios' | 'unknown';支付
import { IMChainCurrencyEnum } from 'im2-link-jssdk';
sdk.callPayment(
{
amount: '99.00',
consumeType: 'goods',
chainCurrencyType: IMChainCurrencyEnum.CNY,
productOrderNo: 'ORDER-20260828-001',
productName: '商品名称',
quantity: '1'
},
(isSuccess) => {
console.log('支付是否成功:', isSuccess);
}
);chainCurrencyType 可选值:CNY = 1、KKC = 2、VNC = 3。
支付参数可以省略;省略时表示只校验支付密码:
sdk.callPayment(undefined, (isSuccess) => {
console.log('支付密码校验结果:', isSuccess);
});当初始化的 id 不等于字符串 '0' 时,SDK 会先校验 App ID 和 App Token,再向宿主发送支付消息。校验
失败只会输出 callPayment error,不会继续拉起支付。
callPayment 是当前唯一返回 Promise<void> 的公开方法。等待该 Promise 只表示前置校验和消息发送结束
,支付最终结果仍以回调为准。
返回、会话与导航
返回
sdk.callBack(() => {
console.log('宿主已处理返回');
});打开会话
sdk.callConversation('target-user-open-id', () => {
console.log('宿主已处理会话请求');
});宿主导航
sdk.callNavigate(
{
actionType: 'external',
params: {
route: 'https://example.com'
}
},
() => {
console.log('宿主已处理导航请求');
}
);当前约定的 actionType 包括:
| 值 | 用途 |
| ------------------ | --------------------------------- |
| home | 首页 |
| channel | 超级群,params 中传 channelId |
| trad | 交易页面 |
| inner | 内部链接,params 中传 route |
| external | 外部链接,params 中传 route |
| invite | 邀请页面 |
| share | 系统分享入口 |
| shortVideoSearch | 短视频搜索 |
| customerService | 客服页面 |
actionType 在当前类型定义中是 string,表格列出的是宿主已约定的业务值。
权限
sdk.permissions(
{
actionType: 'camera'
},
(result) => {
console.log('权限结果:', result);
}
);params 当前使用 IMNavigateParams 结构,实际 actionType 及返回数据由宿主约定。
打开小程序
sdk.callOpenMiniProgram(
'miniapp://target-app',
{
source: 'current-app',
scene: 'detail'
},
() => {
console.log('宿主已处理打开请求');
}
);第二个参数为可选的透传对象。若不需要透传参数,可传 undefined:
sdk.callOpenMiniProgram('miniapp://target-app', undefined, () => {
console.log('宿主已处理打开请求');
});状态栏与原生弹框
设置状态栏颜色
sdk.callSetBarColor(
{
titleColor: '#FFFFFF',
backgroundColor: '#1677FF'
},
() => {
console.log('颜色设置完成');
}
);titleColor 和 backgroundColor 均为可选字符串,颜色格式由宿主解析。
拉起原生弹框
sdk.callPopup(
{
url: 'https://example.com/popup',
ratio: 0.75,
popupType: 2
},
() => {
console.log('弹框已处理');
}
);popupType:
| 值 | 展示方式 |
| --- | -------------- |
| 1 | 带标题栏 |
| 2 | 居中弹出 |
| 3 | 从底部向上覆盖 |
网络请求与 App 数据交互
由宿主发起网络请求
interface UserProfile {
id: string;
nickname: string;
}
sdk.requestApi<UserProfile>(
{
type: 'core',
url: '/v1/user/profile',
method: 'GET',
param: {
userId: '10001'
}
},
(data) => {
console.log(data.nickname);
}
);参数说明:
| 参数 | 类型 | 必填 | 说明 |
| -------- | --------------------- | ---- | ---------------------------- |
| type | 'core' \| 'wallet' | 否 | 请求服务类型,默认 'core' |
| url | string | 是 | 请求地址 |
| method | string | 是 | 请求方法,例如 GET、POST |
| param | Record<string, any> | 否 | 请求参数 |
与宿主交换业务数据
sdk.requestApp<{ url: string }>(
{
method: 'h5-register',
param: {
locale: 'zh-CN'
}
},
(data) => {
console.log('注册页面:', data.url);
}
);method 和 param 由 Web 与宿主共同约定;当前已记录的方法为 h5-register。
WebView 布局与方向
调整输入框高度
sdk.adjustInputBoxHeight(320, (result) => {
console.log('调整结果:', result);
});高度单位和结果数据由宿主约定。
切换横屏
sdk.switchLandscape(true, (result) => {
console.log('切换结果:', result);
});第一个参数默认值为 true;传 false 表示取消横屏。
分享
share 通过 type 区分短视频、棋牌游戏和系统分享。
短视频分享
sdk.share(
{
type: 'shortVideo',
param: {
cover_url: 'https://example.com/cover.jpg',
description: '视频描述',
title: '视频标题',
user_id: '10001',
video_id: 'video-001'
}
},
(result) => {
console.log('分享结果:', result);
}
);棋牌游戏分享
sdk.share(
{
type: 'cardGame',
param: {
show_type: 3,
game_path: '/room/10001',
game_preview_width: 375,
game_preview_height: 667,
game_preview_url: 'https://example.com/game-preview',
currencySource: 'CNY',
subGameName: {
ch: '游戏名称',
en: 'Game name'
}
}
},
(result) => {
console.log('分享结果:', result);
}
);show_type 的含义:0 为默认旧样式,1 为不带链接的棋牌游戏样式,2 为带链接的棋牌游戏样式,3
为游戏预览。
系统分享
sdk.share({
type: 'system',
param: {
title: '分享标题',
text: '分享描述',
shareUrl: 'https://example.com?share_source=tg',
imageUrl: 'https://example.com/share.png'
}
});所有字段均为必填字符串:
| 字段 | 说明 |
| ---------- | ---------------------------------------- |
| title | 分享标题 |
| text | 分享描述 |
| shareUrl | 分享链接;游戏分享来源参数 share_source=tg 拼在此链接中 |
| imageUrl | 分享图片 URL;宿主下载图片后用于图文分享 |
宿主收到的消息如下:
{
"type": "share",
"params": {
"type": "system",
"param": {
"title": "分享标题",
"text": "分享描述",
"shareUrl": "https://example.com?share_source=tg",
"imageUrl": "https://example.com/share.png"
}
},
"appid": "your-app-id",
"apptoken": "your-app-token"
}系统分享与其他 share 类型一样,可以传入可选回调。
下载
sdk.download(
{
fileType: 'video',
url: 'https://example.com/video.mp4'
},
(result) => {
console.log('下载结果:', result);
}
);当前 fileType 仅支持 'video'。
行为验证码
sdk.behaviorCaptcha(
{
token: 'captcha-result-token'
},
() => {
console.log('验证码结果已发送给宿主');
}
);此 API 用于 H5 完成滑动验证后,将验证码 token 透传给宿主。
验证码 token 获取失败时,也可沿用同一通道透传上游业务响应;SDK 不解析业务码或文案:
sdk.behaviorCaptcha({
code: 1038,
msg: '由业务服务返回的提示',
data: {}
});成功结果仍保持 { token },以兼容既有 App。宿主可通过是否存在 params.token 区分成功与业务失败。
系统振动
H5 通过 vibrate 通知宿主调用设备系统振动 / 触觉反馈。未传参数时按短振动处理。
// 默认短振动
sdk.vibrate();
// 短振动,指定强度
sdk.vibrate({ type: 'short', style: 'heavy' });
// 长振动
sdk.vibrate({ type: 'long' }, (isSuccess) => {
console.log('振动是否已触发:', isSuccess);
});
// 指定时长(毫秒),宿主按系统能力执行
sdk.vibrate({ duration: 200 });参数说明:
| 参数 | 类型 | 必填 | 说明 |
| ---------- | -------------------------------- | ---- | ------------------------------------------------------------ |
| type | 'short' \| 'long' | 否 | 振动类型,默认 'short' |
| style | 'light' \| 'medium' \| 'heavy' | 否 | 短振动强度,仅 type 为 'short' 时生效 |
| duration | number | 否 | 振动时长(毫秒)。与 type 同时存在时,宿主优先按 duration 执行 |
宿主收到的消息如下:
{
"type": "vibrate",
"params": {
"type": "short",
"style": "heavy"
},
"appid": "your-app-id",
"apptoken": "your-app-token"
}iOS 建议将 short + style 映射为 UIImpactFeedbackGenerator,long 映射为系统振动;Android 建议使用 VibrationEffect。桌面端和无振动能力的设备可直接回 isSuccess: 0。
宿主接入协议
本节供 iOS、Android、桌面端和 H5 容器开发者实现消息桥接。
Web 回调语义
| API | Web 侧收到的回调值 |
| -------------------------- | --------------------------------------------- |
| requestApi、requestApp | 宿主返回的业务数据 |
| permissions | 宿主返回的权限数据 |
| 其他带回调的 API | isSuccess === 1 时为 true,否则为 false |
部分历史 API 的 TypeScript 回调签名为 () => void,调用方可以只把它当作完成通知;SDK 运行时仍会传入
上述布尔结果。
Web 发给宿主的统一消息
interface SDKMessage {
type: MessageTypeEnum;
params?: unknown;
callback?: string;
appid: string;
apptoken: string;
}type:原生能力标识,见“API 概览”。params:对应 API 的业务参数。callback:调用方传入回调时由 SDK 生成;无回调时不发送该字段。appid、apptoken:初始化 SDK 时传入的身份信息。
各平台发送通道
| 环境 | SDK 调用的宿主通道 | 消息形式 |
| ---------------------- | ------------------------------------------------------------------- | ----------- |
| iOS | window.webkit.messageHandlers.JSParent.postMessage(message) | 对象 |
| Android | window.JSParent.postMessage(JSON.stringify(message)) | JSON 字符串 |
| Windows / Unix / Linux | window.miniJSParent.postMessage(message) | 对象 |
| macOS | 优先使用 webkit.messageHandlers.JSParent,否则使用 miniJSParent | 对象 |
| H5 iframe | window.parent.postMessage(message, '*') | 对象 |
原生宿主返回回调
消息带有 callback 时,原生宿主处理完成后应调用对应的全局回调:
// 普通原生能力:payment、navigate、share 等
window.IMCallBack[callback](
JSON.stringify({
isSuccess: 1,
callback
})
);
// requestApi、requestApp
window.receiveData[callback](JSON.stringify(responseData));
// permissions
window.permissions[callback](JSON.stringify(permissionData));IMCallBack 中 isSuccess 为 1 时,Web 回调接收 true;其他值接收 false。receiveData 和
permissions 的数据会先经过安全 JSON 解析,以避免大整数精度丢失。
H5 容器返回回调
父页面通过 postMessage 返回:
iframeWindow.postMessage(
{
type: 'IMCallBack',
callback: message.callback,
data: {
isSuccess: 1
}
},
'*'
);type 与调用类型的对应关系:
| SDK 调用 | 回调 type |
| -------------------------- | ------------- |
| requestApi、requestApp | receiveData |
| permissions | permissions |
| 其他带回调的 API | IMCallBack |
H5 宿主需要在父级 window 上注入:
window['h5-app-version'] = '宿主版本号';跨域 iframe 无法读取父页面字段时,SDK 会按 H5 容器处理。SDK 只接收 event.source === window.parent
的标准浏览器消息;source === null 的回包会被拒绝,网页宿主应通过 IMSDK.web.createHost 绑定真实
iframe WindowProxy。
回调生命周期
- 回调被调用后会立即从全局回调表中删除。
requestApi回调在 30 秒后仍未收到响应时会被清理。- 其他 API 回调在 10 分钟后仍未收到响应时会被清理。
- 当前清理行为不会主动调用 Web 侧回调,也不会生成超时错误。
H5 容器外的行为
SDK 面向 App WebView 和宿主 iframe。在普通浏览器顶层页面中,如果不存在原生桥且未注入
h5-app-version,调用不会发送给宿主;传入回调时,SDK 会以 undefined 调用该回调。
导出的类型与工具
除默认导出的 IMSDK 外,包还导出:
- 类型与枚举
:
MessageTypeEnum、IMSDKConfig、IMPaymentParams、IMNavigateParams、IMRequestParams、IMRequestAppParams、IMPopupParams、IMShareParams、IMSystemShareParams、IMDownLoadParams、IMBehaviorCaptchaParams、IMVibrateParams、IMChainCurrencyEnum等。 - 工具:
safeJSONParse、uuidv4、getOS、Md5。
大整数 JSON 解析示例:
import { safeJSONParse } from 'im2-link-jssdk';
const data = safeJSONParse('[123456789123456789123456789, 2.3]');
// 超出 JavaScript 安全整数范围的数字以字符串形式保留本地开发
npm install
npm run build
npm run docnpm run build:生成 ESM、CommonJS 和类型声明到dist/。npm run doc:根据源码类型和注释生成 TypeDoc 到docs/。- 项目要求 Node.js
>= 22.0.0。
发布流程见 PUBLISH.md。
网页宿主接口 web
网页容器可以直接使用 IMSDK.web.createHost(也可 import { web }),不需要创建游戏端 SDK 实例或传入 app token。该入口没有自动监听副作用,也不会改动 iOS/Android/PC 的原生桥。
import IMSDK from 'im2-link-jssdk';
const host = IMSDK.web.createHost({
getTarget: () => iframe.contentWindow ? {
source: iframe.contentWindow,
origin: new URL(iframe.src).origin,
key: `${currentAccountId}:${currentAppId}:${iframe.src}`
} : null,
onMessage: async (request) => {
if (request.message.type !== 'share') return;
// params 为 { type: 'cardGame', param: { show_type, game_path, ... } }。
// 宿主必须校验 message.appid 与当前 iframe 的可信应用资料,然后让用户选会话。
const accepted = await selectAndShare(request.message.params, request.signal);
request.reply('IMCallBack', { isSuccess: accepted ? 1 : 0 });
}
});
host.listen();
// iframe 关闭/重新加载、切账号和卸载时:
host.stop();getTarget必须来自宿主 iframe,不得用收到的event.origin或消息里的 appid 生成信任规则。origin 必须精确匹配,禁止*。显式绑定origin: 'null'才接受 opaque iframe,此时回包使用浏览器要求的*,仍严格绑定 WindowProxy。key标识当前应用/账号/页面。异步工作前使用request.isCurrent();重新加载同一地址时也应stop()后listen(),中止旧业务。监听只校验窗口来源,不代替宿主的 app token/权限验证。reply('IMCallBack' | 'receiveData' | 'permissions', data)自动带上原 callback,只能回复一次。停止或目标变化后的回复返回 false。业务回调share(params, cb)继续收到 boolean,不改变旧 API。onMessage的 Promise 拒绝时回{isSuccess:0,error:'HOST_ERROR'};可用onError接入宿主诊断,不应输出完整请求凭据。不支持的业务应由宿主明确回失败。- 默认只分发 SDK 已声明消息类型,额外旧游戏事件通过
additionalMessageTypes注册;normalizeMessage只用于拆开宿主已有扩展包装,执行前已经校验 source/origin。 - 已有事件循环可调用
handleMessage(event);不要同时再把同一事件交给自己的业务处理器。web.parseMessage(value)只做对象/JSON 结构解析,不做来源或权限验证。 - 同源与跨域 iframe 都支持网页通信,不要求父网页注入
h5-app-version。原生环境仍优先现有原生桥。没有原生桥、没有标记的顶层独立页仍不冒充宿主。
H5 游戏分享宿主将 isSuccess:1 定义为用户确认后,所有选中目标均被 IM 发送 API 接受;这不代表送达/已读。取消、忙碌、不可用及部分失败回 0,原始响应附带 status/sent/failed。iOS 现有分享没有实现结果回包,不应把“已弹出选择器”写为它的成功合同。
