| New file |
| | |
| | | # 微赞 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 反射不通,评估上原生插件。 |