H5对接App场景商品卡片跳转对接说明

商品卡片跨 App 跳转对接指南

1. 功能说明

goodsCard 商品卡片新增 crossApp 参数,用于控制点击卡片后的处理方式。

crossApp 点击行为
未传、false H5 使用 window.open(url, "_blank") 打开商品页面
true H5 将完整的商品卡片 content 传递给外层,由外层完成跨 App 跳转

注意:商品卡片必须提供非空 url 才能点击。即使 crossApptrue,也需要提供 url

crossApptrue,但当前渠道没有可用的外层桥接能力,或桥接调用发生异常时,将回退到 H5 新窗口打开 url

2. 消息数据格式

{
  "contentType": "goodsCard",
  "content": {
    "img": "https://example.com/goods.png",
    "title": "商品名称",
    "desc": "商品描述",
    "remark": "商品备注",
    "url": "https://example.com/goods/10001",
    "crossApp": true
  }
}

content 字段

字段 类型 必填 说明
img string 商品图片地址
title string 商品标题
desc string 商品描述
remark string 商品备注
url string 商品页面地址,同时用于桥接失败时的 H5 回退
crossApp boolean 是否由外层执行跨 App 跳转,默认 false

外层收到的数据是完整的 content 对象,不仅是 url

3. 通用协议

跨 App 跳转的统一事件名为:

goodsCardClick

统一业务参数为:

{
  "img": "https://example.com/goods.png",
  "title": "商品名称",
  "desc": "商品描述",
  "remark": "商品备注",
  "url": "https://example.com/goods/10001",
  "crossApp": true
}

不同渠道只在通信调用方式和外层包装结构上存在差异。

4. 各渠道对接方式

4.1 uni-app WebView

建议在客服页面 URL 中增加:

embedded=uniapp

H5 调用方式:

uni.postMessage({
  data: {
    action: "goodsCardClick",
    params: content
  }
});

外层收到的数据结构:

{
  "action": "goodsCardClick",
  "params": {
    "img": "https://example.com/goods.png",
    "title": "商品名称",
    "desc": "商品描述",
    "remark": "商品备注",
    "url": "https://example.com/goods/10001",
    "crossApp": true
  }
}

uni-app 页面接收示例:

<template>
  <web-view :src="chatUrl" @message="handleWebViewMessage" />
</template>

<script setup>
const handleWebViewMessage = (event) => {
  const messages = event.detail?.data || [];
  const message = messages[messages.length - 1];

  if (message?.action !== "goodsCardClick") return;

  const content = message.params;
  // 根据 content.url 或其他业务字段执行跨 App 跳转
};
</script>

4.2 Flutter InAppWebView

建议在客服页面 URL 中增加:

embedded=flutter

H5 调用方式:

window.flutter_inappwebview.callHandler("goodsCardClick", content);

Flutter 接收示例:

InAppWebView(
  initialUrlRequest: URLRequest(url: WebUri(chatUrl)),
  onWebViewCreated: (controller) {
    controller.addJavaScriptHandler(
      handlerName: 'goodsCardClick',
      callback: (arguments) {
        if (arguments.isEmpty) return null;

        final content = Map<String, dynamic>.from(arguments.first);
        final url = content['url'] as String?;
        // 根据 content 执行跨 App 跳转
        return true;
      },
    );
  },
)

4.3 Android WebView JSInterface

H5 调用方式:

window.Android.goodsCardClick(JSON.stringify({
  params: content,
  callback: null
}));

Android 需要向 WebView 注册名称为 Android 的 JavaScript Interface,并提供 goodsCardClick 方法。

Kotlin 接收示例:

class ChatJavascriptInterface {
    @JavascriptInterface
    fun goodsCardClick(message: String) {
        val root = JSONObject(message)
        val content = root.getJSONObject("params")
        val url = content.optString("url")

        // 切回主线程后,根据 content 执行跨 App 跳转
    }
}

webView.settings.javaScriptEnabled = true
webView.addJavascriptInterface(ChatJavascriptInterface(), "Android")

Android 收到的 JSON 字符串结构:

{
  "params": {
    "img": "https://example.com/goods.png",
    "title": "商品名称",
    "desc": "商品描述",
    "remark": "商品备注",
    "url": "https://example.com/goods/10001",
    "crossApp": true
  },
  "callback": null
}

4.4 iOS WKWebView

H5 调用方式:

window.webkit.messageHandlers.goodsCardClick.postMessage({
  params: content,
  callback: null
});

iOS 需要注册名称为 goodsCardClick 的 Script Message Handler。

Swift 接收示例:

final class ChatMessageHandler: NSObject, WKScriptMessageHandler {
    func userContentController(
        _ userContentController: WKUserContentController,
        didReceive message: WKScriptMessage
    ) {
        guard message.name == "goodsCardClick",
              let body = message.body as? [String: Any],
              let content = body["params"] as? [String: Any]
        else {
            return
        }

        let url = content["url"] as? String
        // 根据 content 执行跨 App 跳转
    }
}

let contentController = WKUserContentController()
contentController.add(ChatMessageHandler(), name: "goodsCardClick")

let configuration = WKWebViewConfiguration()
configuration.userContentController = contentController

iOS 收到的对象结构:

{
  "params": {
    "img": "https://example.com/goods.png",
    "title": "商品名称",
    "desc": "商品描述",
    "remark": "商品备注",
    "url": "https://example.com/goods/10001",
    "crossApp": true
  },
  "callback": null
}

4.5 JS iframe 嵌入渠道

适用于通过 ueChatCore.js 将客服页面嵌入业务网页的场景。

iframe 向父页面发送的原始 postMessage

window.parent.postMessage({
  type: "goodsCardClick",
  source: "iframe",
  data: content
}, "*");

使用 ueChatCore.js 时,父页面可以选择以下任一方式接收。

方式一:注册全局回调。

window.ueGoodsCardClick = function (content) {
  console.log("商品卡片参数:", content);
  // 根据 content 执行跨 App 跳转
};

方式二:监听自定义事件。

window.addEventListener("ueChat:goodsCardClick", function (event) {
  const content = event.detail;
  console.log("商品卡片参数:", content);
  // 根据 content 执行跨 App 跳转
});

未使用 ueChatCore.js、自行嵌入 iframe 时,可以直接监听 message 事件:

window.addEventListener("message", function (event) {
  const message = event.data;

  if (
    message?.source !== "iframe" ||
    message?.type !== "goodsCardClick"
  ) {
    return;
  }

  const content = message.data;
  // 根据 content 执行跨 App 跳转
});

生产环境中,自行监听 message 时应校验 event.origin,不要直接信任任意来源的数据。

4.6 普通 H5

普通浏览器页面不需要对接外层桥接。

crossApp 未传或为 false 时,H5 直接执行:

window.open(content.url, "_blank");

当误传 crossApp: true,但页面中不存在任何可用桥接时,也会回退到上述 H5 打开方式。

5. 渠道识别与调用优先级

H5 按以下顺序尝试将商品卡片交给外层:

  1. URL 参数 embedded=uniapp 对应的 uni-app 桥接
  2. URL 参数 embedded=flutter 对应的 Flutter 桥接
  3. Android 的 window.Android.goodsCardClick
  4. iOS 的 window.webkit.messageHandlers.goodsCardClick
  5. iframe 父页面 postMessage
  6. 页面中实际存在的 Flutter 或 uni-app 桥接对象
  7. 所有桥接均不可用时,回退 H5 打开 url

一个页面应只接入一种主要容器桥接,避免同时注入多个原生桥对象造成渠道识别歧义。

6. 对接检查清单

  • 消息的 contentTypegoodsCard
  • content.url 是非空字符串
  • 需要跨 App 跳转时,content.crossApp 是布尔值 true,不是字符串 "true"
  • uni-app 页面 URL 已传 embedded=uniapp
  • Flutter 页面 URL 已传 embedded=flutter
  • Android 已注册 Android.goodsCardClick(String)
  • iOS 已注册 goodsCardClick Script Message Handler
  • JS iframe 渠道已注册 ueGoodsCardClick 或监听 ueChat:goodsCardClick
  • 外层能处理完整 content,并自行校验目标地址及业务参数
  • 已验证外层桥接不可用时可以正常回退 H5 打开商品页面

7. 安全建议

  • 外层跳转前应校验 content.url 的协议、域名和业务白名单。
  • iframe 自行监听 postMessage 时应校验 event.originevent.source
  • 不要直接将未校验的商品字段拼接为原生路由或系统 Scheme。
  • Android 开启 JavaScript Interface 时,仅暴露必要的方法。
  • iOS 应在 WebView 销毁时移除 Script Message Handler,避免生命周期问题。
2026-07-21