hejianhao
2026-08-21 72def61f2e806a8042e155998ca19e87ec7e789e
fix0806: 微赞H5唤起小程序支付-接入方案.md
1个文件已添加
145 ■■■■■ 已修改文件
doc/微赞H5唤起小程序支付-接入方案.md 145 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
doc/微赞H5唤起小程序支付-接入方案.md
New file
@@ -0,0 +1,145 @@
# 微赞 H5 唤起小程序支付 —— APP 端接入方案
> 背景:APP 内嵌微赞 H5 直播页,用户在直播页下单支付。下单时微赞 H5 调用 APP 注入的原生方法,由 APP 用微信 `launchMiniProgram` 唤起小程序完成支付(仅易宝支付)。
>
> 参考:微信开放平台 `launchMiniProgram` 接口。
## 一、已确认的前提
- **微信应用 appId**:`wx074c1f0cf5a0ecf4`(见 `manifest.json` app-plus.distribute.sdkConfigs.share.weixin / oauth)。
- **UA 标识**:需注入 `webchatPay/wx074c1f0cf5a0ecf4`。
- **唤起目标小程序**:`username`(原始 ID)、`path` 由微赞 H5 下单时**动态传回**,APP 不写死,直接透传给 `launchMiniProgram`。
- **JSBridge 方式**:必须按文档原生方式,H5 侧写死为
  - 安卓:`window.android.jumpToMiniProgramPay(JSON.stringify(params))`
  - iOS:`window.webkit.messageHandlers.jumpToMiniProgramPay.postMessage(JSON.stringify(params))`
- **params**:`{ orderId: string, miniprogramType: 0, path: string, username: string }`
- **现有能力**:项目已用 `plus.share.getServices` + `launchMiniProgram`(见 `pages/home/home.vue`),已大量用 `plus.android/plus.ios.importClass` 直接调原生(见 `utils/webview-cache.js`)。
## 二、核心技术障碍
uni-app 标准 `<web-view>` 组件(当前 `pages/webview/webview.vue` 用的)**两点都做不到**:
1. 无法自定义 UA;
2. 无法接住原生 `window.android` / `window.webkit.messageHandlers` 调用(它只支持 uni.postMessage 的 `@message`)。
因此进入微赞直播间的 webview **必须从"组件式 `<web-view>`"改为"命令式 `plus.webview.create`"**,并对底层原生 WebView 做 UA 设置 + JSBridge 注册。
> 说明:仅"微赞直播间"这一个入口需要特殊 webview;其它普通外链继续用现有 `<web-view>` 页面,避免全量风险。建议**新建独立页面** `pages/weizanLive/weizanLive.vue` 承载,不动通用 `webview.vue`。
## 三、实施步骤
### 步骤 1:新建微赞直播专用页面(命令式 webview)
新增 `pages/weizanLive/weizanLive.vue`:
- `onLoad` 拿到后端换取的直播 URL(沿用现有 `createLiveRoomEntry` 返回的 url)。
- 进入前调用 `clearWebViewData()`(复用现有清缓存逻辑)。
- 用 `plus.webview.create` 创建原生 webview,设置 UA,append 到当前页面。
- 注册 JSBridge,`onUnload` 时 close/销毁 webview 并反注册。
页面 `pages.json` 注册该路由。`home.vue` 的 `goWeiZanLive` 跳转目标从 `/pages/webview/webview?...` 改为 `/pages/weizanLive/weizanLive?url=...`。
### 步骤 2:UA 注入
创建 webview 时设置带 `webchatPay/{appId}` 的 UA。5+ 有两种方式:
**方式 A(推荐,webview 级)**:`plus.webview.create(url, id, styles, extras)`,styles 里指定:
```js
const appId = 'wx074c1f0cf5a0ecf4'
const baseUA = plus.navigator.getUserAgent()
const ua = baseUA + ' webchatPay/' + appId
const wv = plus.webview.create(targetUrl, 'weizan-live-webview', {
    plusrequire: 'none',
    'uni-app': 'none',
    // 关键:部分基座支持在 create styles 传 UA;若不支持见方式 B
    additionalHttpHeaders: {},
})
// 若 create styles 不支持 UA,则用下面全局设置法
```
**方式 B(全局,需还原)**:进入前 `plus.navigator.setUserAgent(ua, false)`,退出直播页时还原为 `baseUA`。缺点是设置期间新建的所有 webview 都带此 UA,需确保直播 webview 创建后立即还原,或接受该副作用。
> ⚠️ 待自测:优先试方式 A 的 create styles 是否支持 UA 字段(不同 HBuilderX 基座版本差异大);不支持再退方式 B。**这是需要真机验证的第一个点。**
### 步骤 3:注册原生 JSBridge `jumpToMiniProgramPay`
新增 `utils/weizan-pay-bridge.js`。
**Android**:通过 `plus.android` 反射,给底层 WebView `addJavascriptInterface`,暴露 `window.android`。
```js
// 伪代码骨架,真机联调时补全线程/回调细节
function registerAndroidBridge(webviewInstance, onPay) {
    const Interface = plus.android.implements('io.dcloud.js.WeizanPayJsInterface', {
        // H5 调 window.android.jumpToMiniProgramPay(str)
        'jumpToMiniProgramPay': function (paramsJson) {
            // 注意:JS 接口回调在原生线程,需切回主线程再唤起
            onPay(paramsJson)
        }
    })
    const obj = plus.android.currentWebview() // 或从 webviewInstance 取底层 Android WebView
    plus.android.invoke(obj, 'addJavascriptInterface', Interface, 'android')
}
```
**iOS**:给 WKWebView 注册 `WKScriptMessageHandler`,暴露 `window.webkit.messageHandlers.jumpToMiniProgramPay`。
```js
// iOS 需通过 plus.ios 取到 WKWebView 的 configuration.userContentController
// addScriptMessageHandler:name:'jumpToMiniProgramPay'
// 收到 didReceiveScriptMessage 后回调 onPay(message.body)
```
> ⚠️ 这是本方案**最高风险、最需要真机联调**的部分。Android `addJavascriptInterface` 走 `plus.android` 反射可行但要处理:JS 接口方法运行在非 UI 线程,唤起微信必须切回主线程。iOS 的 messageHandler 通过 `plus.ios` 反射注册较繁琐,可能需要拿到 dcloud 基座的 WKWebView 实例。**如果 plus 反射行不通,退路是写原生插件(uni native plugin)。**
### 步骤 4:唤起小程序(低风险,复用现有能力)
`onPay(paramsJson)` 收到后:
```js
function jumpToMiniProgramPay(paramsJson) {
    let params
    try { params = JSON.parse(paramsJson) } catch { return }
    const { username, path, miniprogramType, orderId } = params || {}
    if (!username) { uni.showToast({ title: '支付参数缺失', icon: 'none' }); return }
    // 切主线程后执行(Android JS 接口回调在子线程)
    plus.share.getServices((services) => {
        const wx = services.find((s) => s.id === 'weixin')
        if (!wx) { uni.showToast({ title: '当前环境不支持微信', icon: 'none' }); return }
        if (!wx.nativeClient) { uni.showToast({ title: '请先安装微信客户端', icon: 'none' }); return }
        wx.launchMiniProgram({
            id: username,                       // 小程序原始 ID(gh_ 开头)
            path: path || '',                   // 微赞传回的支付路径
            type: Number(miniprogramType) || 0, // 固定 0
        }, () => {
            console.log('[weizanPay] 唤起小程序成功, orderId=', orderId)
        }, (err) => {
            console.log('[weizanPay] 唤起失败', err)
            uni.showToast({ title: '唤起支付失败', icon: 'none' })
        })
    }, (err) => {
        uni.showToast({ title: '微信服务不可用', icon: 'none' })
    })
}
```
## 四、改动文件清单
| 文件 | 改动 | 风险 |
|------|------|------|
| `pages/weizanLive/weizanLive.vue`(新增) | 命令式 plus.webview 承载微赞直播 + UA + bridge | 高 |
| `utils/weizan-pay-bridge.js`(新增) | 注册 Android/iOS 原生 JSBridge + 唤起小程序 | 高 |
| `pages.json` | 注册新路由 | 低 |
| `pages/home/home.vue` `goWeiZanLive` | 跳转目标改为新页面 | 低 |
| `pages/webview/webview.vue` | **不动**,通用外链继续用它 | — |
## 五、必须真机验证 / 外部协调的点
1. **UA 是否生效**:进入直播间后,让微赞 H5 侧确认收到的 UA 含 `webchatPay/wx074c1f0cf5a0ecf4`(H5 靠它开启唤起支付逻辑)。方式 A/B 需真机试。
2. **JSBridge 能否接住**:需微赞 H5 提供测试下单入口,真机验证 `window.android` / `window.webkit.messageHandlers` 能否触达 APP。这是成败关键,**建议第一步就和微赞联调这一环**,不通则需上原生插件。
3. **微信侧配置**:APP 的微信 appId 需在微信开放平台配置好"可跳转的小程序"关联(launchMiniProgram 要求已关联的小程序才能拉起),否则会报 badprogram。
4. **微赞后台**:需完成"绑定微信商户号 + 申请易宝支付"(文档前期准备第 1 条,非 APP 端工作,需微赞/运营确认已就绪)。
5. **iOS 唤起小程序资质**:iOS 端 `launchMiniProgram` 需微信 SDK 支持,确认现有分享 SDK 版本可用(现有测一测功能已在用,基本可复用)。
## 六、建议推进顺序
1. 先和微赞确认「前期准备」1、2、3 已具备联调条件,拿到测试直播间 + 测试小程序原始 ID/path。
2. 先做**步骤 4 唤起小程序**(复用现有,最易验证)——可先用一个测试按钮硬编码参数跑通 launchMiniProgram。
3. 再做**步骤 2 UA**,让微赞 H5 确认识别到。
4. 最后攻**步骤 3 JSBridge**,与微赞真机联调。若 plus 反射不通,评估上原生插件。