# OMG 支付 iOS WebView 返回 App 交接设计

## 最终决策（2026-08-17）

本方案只约定后端结果页与用户端 App 支付页之间的交接方式。支付状态查询、轮询、提示文案和最终业务页面跳转由前端继续实现，不属于本次后端修改范围。

- 用户端实际支付页是 `pages/OrderList/buy/omgCheckout`。
- 该页面通过 renderjs 创建并提交 HTML Form，OMG 收银台会加载到当前页面 WebView，而不是 `<web-view>` 子组件。
- iOS 主返回流程由 `omgCheckout` 获取当前页面 `WebviewObject`，监听原生 `loaded` 事件并读取当前 URL。
- 当前 URL 精确进入 `https://foodieapi.waimai-paotui.com/pay/omg/result` 时，视为收到 `OMG_RETURNED` 交接信号。
- `/pay/omg/result` 继续返回 HTML 页面。现有提示内容、“返回 App”按钮和 App Scheme 可以保留，作为外部浏览器、Android 或监听失效时的兼容兜底。
- iOS 主流程不依赖返回页中的 `uni.webView.redirectTo`、`uni.postMessage`、`plus.webview.currentWebview`、`parent.evalJS` 或 App Scheme。
- 返回页到达只表示浏览器流程已经返回，不表示支付成功。前端不得仅凭该信号展示支付成功。
- `omgCheckout` 是 OMG 专用页面，不需要额外增加 `payChannel=omg`。

后续如与 2026-08-14 的旧示例或旧描述存在差异，以本节和本文当前内容为准。

## 1. 问题与根因

OMG 信用卡付款完成后，iOS 会停留在后端返回的“正在返回 App”页面，自动返回和手动按钮都可能没有效果。

真机日志已经确认：

```text
[OMG-RETURN] ios_parent_redirect_exception
ReferenceError: Can't find variable: sync

[OMG-RETURN] bridge_redirect_exception
ReferenceError: Can't find variable: sync
```

同一个 App Scheme 可以从 iOS 外部浏览器打开 App，说明 Scheme 注册和目标路由有效。失败边界位于 App 内 OMG WKWebView 的 Bridge 或同 App Scheme 交接。

因此，iOS 主流程不能继续依赖远程结果页主动控制 App 路由。可靠的交接点是 App 原生 WebView 自身的 `loaded` 事件。

## 2. 实际页面结构

`pages/OrderList/buy/omgCheckout` 不是承载 `<web-view>` 子组件的普通宿主页。它在 renderjs 中执行：

```text
接收 omgCheckoutPayload
        ↓
创建 HTML Form
        ↓
POST 到 payload.gatewayUrl
        ↓
当前页面 WebView 被 OMG 收银台内容替换
```

所以前端需要监听：

```js
this.$scope.$getAppWebview()
```

而不是：

```js
this.$scope.$getAppWebview().children()[0]
```

renderjs 只负责提交 OMG Form。URL 监听和 App 路由接管必须放在普通 `<script>` 的 App 逻辑层中。

## 3. 返回交接流程

```text
用户在 OMG 收银台完成或结束支付流程
        |
        v
OMG POST /pay/omg/result
        |
        v
后端校验返回资料并返回现有 HTML 结果页
        |
        v
omgCheckout 当前 WebView 触发 loaded
        |
        v
omgCheckout 通过 getURL() 读取当前地址
        |
        +-- 不是 /pay/omg/result --> 忽略
        |
        +-- 精确匹配结果页 -------> 触发一次 OMG_RETURNED
                                      |
                                      v
                              前端接管后续业务
```

这里所说的“结果页通知外层”是指：结果页加载使原生 WebView 自动触发 `loaded`，外层 App 逻辑再读取 URL。结果页不需要主动调用 `postMessage`。

## 4. 后端结果页边界

`POST /pay/omg/result` 继续存在，并继续返回 `text/html` 页面，不能删除接口或改为 `204 No Content`。

后端继续负责：

- 接收 OMG 的 `application/x-www-form-urlencoded` Client POST。
- 按现有规则校验返回资料。
- 返回禁止缓存的安全 HTML 页面。
- 保留现有页面提示内容。
- 可以保留“返回 App”按钮及 App Scheme 作为兼容兜底。

后端结果页不负责保证 iOS App 内路由成功。即使保留现有 Bridge、父 WebView 或 Scheme 尝试，它们也只能作为非关键兜底，失败不得影响页面正常返回。

结果页不得直接宣称支付成功，也不得在 HTML、URL 或日志中暴露 token、完整 `formFields`、`CheckMacValue` 或其他支付凭证。

## 5. 前端交接契约

前端应在 renderjs 提交 Form 后、支付完成前，保证当前页面 WebView 已经绑定 `loaded` 监听。监听绑定后应立即读取一次当前 URL，防止目标页面在绑定前已经加载完成。

结果页匹配规则：

```text
https://foodieapi.waimai-paotui.com/pay/omg/result
https://foodieapi.waimai-paotui.com/pay/omg/result?...query...
https://foodieapi.waimai-paotui.com/pay/omg/result#...hash...
```

必须同时精确匹配协议、域名和路径，不能只判断 URL 包含 `omg` 或 `result`。

匹配后只触发一次 `OMG_RETURNED`，防止 `loaded` 重复触发造成重复查询或重复跳转。`omgCheckout` 已经保存 `orderId`，结果页不需要重新传递订单号。

页面销毁时，前端需要移除 `loaded` 监听器并清理本页面创建的定时器。具体支付查询、状态处理和路由由前端实现。

## 6. 安全约束

- `/pay/omg/result` 到达不等于支付成功。
- 只有后端支付状态查询确认成功后，前端才能展示支付成功页面。
- token 只能由 App 请求层发送给后端，不能传给 OMG 页面或拼入结果页 URL。
- 日志不得包含 token、完整 `formFields`、`CheckMacValue` 或完整回调 URL。
- 必须防止重复 `loaded` 事件造成重复业务处理。

## 7. 兼容策略

| 通道 | 定位 | 说明 |
|---|---|---|
| 当前 WebView `loaded` + 精确 URL | iOS 主流程 | 不依赖远程页面 Bridge，由 `omgCheckout` 接管 |
| 现有“返回 App”按钮 | 兼容兜底 | 页面外观可以保留，不作为 iOS 成功判据 |
| App Scheme | 外部浏览器/Android 兜底 | 同 App iOS WKWebView 中不保证有效 |
| 远程 Bridge、`parent.evalJS` | 非关键旧兜底 | 已有真机失败证据，不能承担主流程 |

保留旧页面和按钮不等于继续依赖旧方案。iOS 是否能够自动返回，必须以 `omgCheckout` 的原生 WebView 监听为准。

## 8. 预期日志

前端至少应能观察到以下交接日志；字段名称可按 App 现有规范调整：

```text
[OMG-APP] webview_loaded {"isOmgResultPage":true}
[OMG-APP] result_page_detected {"orderRef":"***7389"}
```

结果页旧兜底失败日志可以继续存在，但不得影响上述交接：

```text
[OMG-RETURN] ios_parent_redirect_exception ...
[OMG-RETURN] bridge_redirect_exception ...
```

## 9. 验收清单

1. 使用 iPhone 打开 App 并进入 `pages/OrderList/buy/omgCheckout`。
2. 确认当前页面 WebView 的 `loaded` 监听在支付完成前已绑定。
3. 完成一笔 OMG 支付，确认后端仍返回现有 HTML 结果页。
4. 确认结果页仍可显示原有提示和“返回 App”按钮。
5. 确认 `omgCheckout` 能读取到精确的 `/pay/omg/result` URL。
6. 确认同一次返回只触发一次 `OMG_RETURNED`。
7. 确认 OMG 收银台中间页面、取消页面及其他地址不会触发返回交接。
8. 确认后续支付状态处理由前端接管，并且不会仅凭结果页到达显示支付成功。
9. 使用 Android 和外部浏览器复核现有按钮及 Scheme 兜底未被破坏。

## 10. 本次范围

本次只确认和记录交接设计，不修改用户端 App 代码，也不修改支付查询、轮询、提示文案或最终路由实现。

参考资料：

- [uni-app WebView 组件](https://uniapp.dcloud.net.cn/component/web-view.html)
- [HTML5+ Webview API](https://www.html5plus.org/doc/zh_cn/webview.html)
