|
|
@@ -22,7 +22,7 @@ OMG(欧买尬/FunPoint)是**网页托管式支付网关**(AIO = All-In-One
|
|
|
```
|
|
|
[App: 订单详情页 点"去支付"]
|
|
|
│
|
|
|
- │ ① POST /pay/omg/create?orderid={ddId} (Header: token)
|
|
|
+ │ ① POST /pay/omg/create JSON {"orderId":"..."} (Header: token)
|
|
|
▼
|
|
|
[后端] 校验订单 → 组参+签名 → 返回 form 字段(含 gatewayUrl)
|
|
|
│
|
|
|
@@ -35,7 +35,7 @@ OMG(欧买尬/FunPoint)是**网页托管式支付网关**(AIO = All-In-One
|
|
|
[OMG 收银台网页] 用户使用信用卡或 Apple Pay 付款
|
|
|
│
|
|
|
│ ③ 付款后 OMG 把 web-view 跳回 OrderResultURL
|
|
|
- │ 后端 /pay/omg/return 302 → 前端结果页?ddId=xxx
|
|
|
+ │ 后端 /pay/omg/result 返回安全桥接页 → App Link
|
|
|
▼
|
|
|
[App: 支付结果页] 读 ddId → 查订单状态 → 展示结果
|
|
|
|
|
|
@@ -54,8 +54,8 @@ OMG(欧买尬/FunPoint)是**网页托管式支付网关**(AIO = All-In-One
|
|
|
|---|------|--------|
|
|
|
| 1 | 放一个 `omgPay.html` 到 `static/`,作为 web-view 收银台跳转页 | uni-app 工程 |
|
|
|
| 2 | 发起支付:调 `/pay/omg/create` → 拼 URL → 打开 web-view | 发起支付页 |
|
|
|
-| 3 | 支付结果页:接收 `ddId` → 查状态 → 展示成功/失败/取号 | 支付结果页 |
|
|
|
-| 4 | 不再实现 ATM/超商取号展示;后端旧接口仅兼容变更前交易 | 无新增页面 |
|
|
|
+| 3 | 支付结果页:接收 `orderId` → 调 `/pay/omg/query` → 展示成功/失败/确认中 | 支付结果页 |
|
|
|
+| 4 | 不实现 ATM/超商取号及延期支付返回入口 | 无新增页面 |
|
|
|
| 5 | (可选)取消订单退款:调 `/pay/omg/refund` | 订单取消逻辑 |
|
|
|
| 6 | 告诉后端两个前端地址(见 §6 配置依赖) | 沟通 |
|
|
|
| 7 | 所有面向用户文字走 i18n 4 语言 | zh/tw/en/vi |
|
|
|
@@ -75,13 +75,16 @@ OMG(欧买尬/FunPoint)是**网页托管式支付网关**(AIO = All-In-One
|
|
|
|----|------|
|
|
|
| Method | `POST` |
|
|
|
| Header | `token: <用户JWT>` |
|
|
|
-| 入参 | query:`orderid` = 订单号 ddId |
|
|
|
+| 入参 | JSON:`{"orderId":"订单号"}` |
|
|
|
| 鉴权 | 登录用户 + 必须是订单本人 |
|
|
|
|
|
|
**请求示例**
|
|
|
```
|
|
|
-POST {baseURL}/pay/omg/create?orderid=DD202608060001
|
|
|
+POST {baseURL}/pay/omg/create
|
|
|
Header: token: eyJhbGciOi...
|
|
|
+Content-Type: application/json
|
|
|
+
|
|
|
+{"orderId":"DD202608060001"}
|
|
|
```
|
|
|
|
|
|
**成功返回**(`code=200`,取 `data`)
|
|
|
@@ -90,23 +93,26 @@ Header: token: eyJhbGciOi...
|
|
|
"code": 200,
|
|
|
"msg": "操作成功",
|
|
|
"data": {
|
|
|
+ "status": "CREATED",
|
|
|
"gatewayUrl": "https://payment.funpoint.com.tw/Cashier/AioCheckOut/V5",
|
|
|
- "MerchantID": "3xxxxxx2",
|
|
|
- "MerchantTradeNo": "OMG20260806143012xxx",
|
|
|
- "MerchantTradeDate": "2026/08/06 14:30:12",
|
|
|
- "PaymentType": "aio",
|
|
|
- "TotalAmount": "350",
|
|
|
- "TradeDesc": "food order DD202608060001",
|
|
|
- "ItemName": "order DD202608060001",
|
|
|
- "ReturnURL": "https://api.xxx.com/pay/omg/notify",
|
|
|
- "ChoosePayment": "Credit",
|
|
|
- "UnionPay": "2",
|
|
|
- "EncryptType": "1",
|
|
|
- "InvoiceMark": "N",
|
|
|
- "NeedExtraPaidInfo": "Y",
|
|
|
- "OrderResultURL": "https://app.xxx.com/.../omgResult",
|
|
|
- "PaymentInfoURL": "https://api.xxx.com/pay/omg/paymentInfo",
|
|
|
- "CheckMacValue": "A1B2C3D4E5F6...(64位)"
|
|
|
+ "formFields": {
|
|
|
+ "MerchantID": "3xxxxxx2",
|
|
|
+ "MerchantTradeNo": "OMG20260806143012xxx",
|
|
|
+ "MerchantTradeDate": "2026/08/06 14:30:12",
|
|
|
+ "PaymentType": "aio",
|
|
|
+ "TotalAmount": "350",
|
|
|
+ "TradeDesc": "food order DD202608060001",
|
|
|
+ "ItemName": "order DD202608060001",
|
|
|
+ "ReturnURL": "https://api.xxx.com/pay/omg/notify",
|
|
|
+ "ChoosePayment": "Credit",
|
|
|
+ "UnionPay": "2",
|
|
|
+ "EncryptType": "1",
|
|
|
+ "InvoiceMark": "N",
|
|
|
+ "NeedExtraPaidInfo": "Y",
|
|
|
+ "OrderResultURL": "https://api.xxx.com/pay/omg/result",
|
|
|
+ "ClientBackURL": "https://api.xxx.com/pay/omg/back?merchantTradeNo=...",
|
|
|
+ "CheckMacValue": "A1B2C3D4E5F6...(64位)"
|
|
|
+ }
|
|
|
}
|
|
|
}
|
|
|
```
|
|
|
@@ -125,67 +131,27 @@ Header: token: eyJhbGciOi...
|
|
|
|
|
|
---
|
|
|
|
|
|
-### 3.2 支付结果页跳转 — `GET/POST /pay/omg/return`(后端自动 302)
|
|
|
+### 3.2 支付结果返回 — `POST /pay/omg/result` / `GET /pay/omg/back`
|
|
|
|
|
|
-**不是前端直接调的接口**。付款完成后 OMG 把网页跳到后端 `/pay/omg/return`,后端反查出 `ddId` 后 **302 重定向**到前端结果页:
|
|
|
+**不是前端直接调的接口**。付款完成后 OMG Client POST 到 `/pay/omg/result`;用户点收银台“返回商店”时进入 `/pay/omg/back`。两个入口都只返回 no-store 安全桥接页,通过配置的 App Link 唤醒 App,不修改支付状态。
|
|
|
|
|
|
```
|
|
|
-{omg.order-result-url}?ddId=DD202608060001
|
|
|
+{omgpay.app-return-url}?orderId=DD202608060001
|
|
|
```
|
|
|
|
|
|
-前端只需:**注册一个结果页路由**接收 query 上的 `ddId`(见 §4.3)。`omg.order-result-url` 由后端配置(§6),需要前端把这个页面地址给后端。
|
|
|
+App 打开后必须调用 `/pay/omg/query` 确认结果;`/result` 验签通过也不等于可以直接宣称已付款。
|
|
|
|
|
|
---
|
|
|
|
|
|
-### 3.3 历史 ATM/超商取号查询(新客户端不得调用)
|
|
|
-
|
|
|
-**用途**:仅用于排查或兼容变更前已经创建的延期支付交易。新客户端统一调用 `/pay/omg/query` 查询信用卡/Apple Pay 状态,不再展示虚拟帐号或缴费码。
|
|
|
-
|
|
|
-| 项 | 内容 |
|
|
|
-|----|------|
|
|
|
-| Method | `GET` |
|
|
|
-| Header | `token: <用户JWT>` |
|
|
|
-| 入参 | path:`orderid` = ddId |
|
|
|
-| 鉴权 | 登录用户 + 订单本人 |
|
|
|
+### 3.3 支付状态查询 — `POST /pay/omg/query`
|
|
|
|
|
|
-**请求示例**
|
|
|
-```
|
|
|
-GET {baseURL}/pay/omg/paymentInfo/DD202608060001
|
|
|
-Header: token: eyJ...
|
|
|
-```
|
|
|
-
|
|
|
-**返回**
|
|
|
-```json
|
|
|
-{
|
|
|
- "code": 200,
|
|
|
- "data": {
|
|
|
- "payType": "ATM_FIRST", // 付款方式(见 §5)
|
|
|
- "amount": 350,
|
|
|
- "payStatus": 0, // 0未支付/1已支付/2失败/3已退款
|
|
|
- "info": { // 取号信息(信用卡即时支付时 info 为空 {})
|
|
|
- "BankCode": "807",
|
|
|
- "vAccount": "99123450000012",
|
|
|
- "ExpireDate": "2026/08/09",
|
|
|
- "TradeNo": "..."
|
|
|
- }
|
|
|
- }
|
|
|
-}
|
|
|
-```
|
|
|
-
|
|
|
-`info` 字段说明(延期支付才有):
|
|
|
-
|
|
|
-| 付款方式 payType | info 里的关键字段 |
|
|
|
-|-----------------|------------------|
|
|
|
-| `ATM_*` | `BankCode`(银行代码) `vAccount`(虚拟帐号) `ExpireDate`(缴费期限) |
|
|
|
-| `CVS_*` | `PaymentNo`(缴费码) `ExpireDate`(期限) |
|
|
|
-| `BarcodeATM_*` | 同 CVS |
|
|
|
-| `AFTEE_AFTEE` | AFTEE 先享后付信息 |
|
|
|
+App 返回后用 Header `token` 和 JSON `{"orderId":"..."}` 调用。接口只查询该订单唯一有效支付尝试;若成功回调丢失,会向 OMG 查询并同步本地支付与订单状态。
|
|
|
|
|
|
---
|
|
|
|
|
|
### 3.4 退款(取消已支付订单)— `POST /pay/omg/refund`
|
|
|
|
|
|
-**用途**:用户取消已支付订单时退款(仅信用卡/Apple Pay 可自动退;ATM/超商需人工,接口会返回提示)。
|
|
|
+**用途**:用户取消已支付的信用卡/Apple Pay 订单时退款。
|
|
|
|
|
|
| 项 | 内容 |
|
|
|
|----|------|
|
|
|
@@ -202,7 +168,6 @@ Header: token: eyJ...
|
|
|
|
|
|
**返回**
|
|
|
| code=200 | 信用卡/Apple Pay 退刷成功,`pos_order.pay_status` 置 2 |
|
|
|
-| code=500 + 「该支付方式需人工在 OMG 后台退款」 | ATM/超商/BarcodeATM,无自动退款 API,后端已记工单 |
|
|
|
|
|
|
---
|
|
|
|
|
|
@@ -304,29 +269,27 @@ export default {
|
|
|
|
|
|
### 4.3 支付结果页 `pages/pay/omgResult`
|
|
|
|
|
|
-后端 `/pay/omg/return` 会 302 到 `omgResult?ddId=xxx`。这个页面在 web-view 内被加载——加载后建议**立即跳回原生页**或直接渲染结果。最稳的做法:web-view 监听 URL 变化,命中 `omgResult` 就 `navigateBack` 回原生订单详情,由原生页轮询状态。
|
|
|
+后端 `/pay/omg/result` 或 `/pay/omg/back` 返回桥接页并尝试打开 `omgpay.app-return-url`。App 结果页取得 `orderId` 后查询后端确认支付状态。
|
|
|
|
|
|
```js
|
|
|
-// pages/pay/omgCheckout.vue 增强:监听收银台跳到结果页
|
|
|
-// uni-app web-view 可通过 @message 或 bindmessage 接收 H5 postMessage
|
|
|
-// 更简单:让 omgResult 页 postMessage 通知原生关闭 web-view
|
|
|
+// App Link 对应页面加载后,从链接参数取得 orderId 并开始查询。
|
|
|
```
|
|
|
|
|
|
原生订单详情页(回来后)**轮询确认状态**:
|
|
|
|
|
|
```js
|
|
|
-// 信用卡即时支付:轮询 paymentInfo 看是否 payStatus=1
|
|
|
+// 信用卡/Apple Pay:查询唯一有效支付尝试
|
|
|
const poll = (ddId) => {
|
|
|
uni.request({
|
|
|
- url: baseURL + '/pay/omg/paymentInfo/' + ddId,
|
|
|
+ url: baseURL + '/pay/omg/query',
|
|
|
+ method: 'POST',
|
|
|
header: { token: uni.getStorageSync('token') },
|
|
|
+ data: { orderId: ddId },
|
|
|
success: (res) => {
|
|
|
const d = res.data.data;
|
|
|
- if (d.payStatus === 1) { showSuccess(); } // 支付成功
|
|
|
- else if (d.payStatus === 2) { showFail(); } // 失败
|
|
|
- else if (d.payType && d.payType.startsWith('ATM') ||
|
|
|
- d.payType && d.payType.startsWith('CVS')) { showPickup(d.info); } // 延期→展示取号
|
|
|
- else { setTimeout(() => poll(ddId), 2000); } // 待回调,继续轮询
|
|
|
+ if (d.status === 'PAID') { showSuccess(); }
|
|
|
+ else if (d.status === 'FAILED') { showFail(); }
|
|
|
+ else { setTimeout(() => poll(ddId), 2000); }
|
|
|
}
|
|
|
});
|
|
|
};
|
|
|
@@ -334,21 +297,6 @@ const poll = (ddId) => {
|
|
|
|
|
|
> 轮询建议:每 2s 一次,最多 15 次(30s);超过仍未成功提示「支付结果确认中,请稍后在订单列表查看」。
|
|
|
|
|
|
-### 4.4 历史 ATM/超商取号展示(不再开发)
|
|
|
-
|
|
|
-以下示例只用于维护变更前交易,不属于新客户端范围:
|
|
|
-
|
|
|
-```html
|
|
|
-<view>银行代码:{{ info.BankCode }}</view>
|
|
|
-<view>虚拟帐号:{{ info.vAccount }}</view>
|
|
|
-<view>缴费期限:{{ info.ExpireDate }}</view>
|
|
|
-<!-- 超商则展示 PaymentNo -->
|
|
|
-```
|
|
|
-
|
|
|
-并提示(i18n):「请在期限内完成缴费,缴费后订单将自动变为已支付」。
|
|
|
-
|
|
|
----
|
|
|
-
|
|
|
## 5. 状态码 / 付款方式速查
|
|
|
|
|
|
### `payStatus`(订单 + 流水)
|
|
|
@@ -362,14 +310,9 @@ const poll = (ddId) => {
|
|
|
### `payType`(OMG 回覆的付款方式)
|
|
|
| payType | 名称 | 即时/延期 | 可自动退款 |
|
|
|
|---------|------|----------|-----------|
|
|
|
-| `Credit_CreditCard` | 信用卡(含 Apple Pay / 银联) | 即时 | ✅ |
|
|
|
-| `BarcodeATM_CHINATRUST` | 超商快付代碼繳费 | 即时 | ❌ 人工 |
|
|
|
-| `ATM_FIRST/CHINATRUST/UBOT/KGI` | 各银行 ATM | **延期(取虚帐)** | ❌ 人工 |
|
|
|
-| `CVS_CVS/FAMILY/IBON/HILIFE` | 超商代碼繳费 | **延期(取码)** | ❌ 人工 |
|
|
|
-| `AFTEE_AFTEE` | AFTEE 先享后付 | 延期 | ❌ 人工 |
|
|
|
+| `Credit_CreditCard` | 信用卡(含 Apple Pay;银联已隐藏) | 即时 | ✅ |
|
|
|
|
|
|
-> **即时支付**(信用卡/Apple Pay)→ 结果页轮询 `payStatus` 变 1 即成功。
|
|
|
-> 新订单不再产生延期支付。历史 ATM/超商/AFTEE 交易仍由后端回调兼容,不应重新开放其前端入口。
|
|
|
+> 当前开发测试范围只有信用卡和 Apple Pay,不存在延期支付历史交易兼容。
|
|
|
|
|
|
---
|
|
|
|
|
|
@@ -379,11 +322,12 @@ const poll = (ddId) => {
|
|
|
|
|
|
| 配置项 | 含义 | 需要的值(示例) |
|
|
|
|--------|------|----------------|
|
|
|
-| `omg.order-result-url` | 付款后跳回的前端结果页 | `https://app.xxx.com/#/pages/pay/omgResult` |
|
|
|
-| 历史 `paymentInfo` 路由 | 仅兼容变更前 ATM/超商交易,**前端不用管** | 不进入新表单 |
|
|
|
-| `omg.return-url` | 服务端结果回调(后端地址,**前端不用管**) | `https://api.xxx.com/pay/omg/notify` |
|
|
|
+| `omgpay.order-result-url` | OMG 付款完成 Client POST 地址 | `https://api.xxx.com/pay/omg/result` |
|
|
|
+| `omgpay.client-back-url` | 收银台“返回商店”地址 | `https://api.xxx.com/pay/omg/back` |
|
|
|
+| `omgpay.app-return-url` | 后端桥接页唤醒 App 的 HTTPS App Link | `https://app.xxx.com/pay/omg-result` |
|
|
|
+| `omgpay.notify-url` | 服务端结果回调(前端不用管) | `https://api.xxx.com/pay/omg/notify` |
|
|
|
|
|
|
-> 前端只需要确认:**`omgResult` 页面的完整 URL**(含 hash 路由或 history 路由),交给后端填 `omg.order-result-url`。测试期可用测试域名。
|
|
|
+> 前端需要提供已完成 iOS Universal Link / Android App Link 关联的 HTTPS 地址,交给后端配置 `omgpay.app-return-url`。
|
|
|
|
|
|
---
|
|
|
|
|
|
@@ -394,8 +338,8 @@ const poll = (ddId) => {
|
|
|
3. **iOS web-view 不可另开窗**——收银台必须是完整页 web-view,不能 `window.open`、不能 iframe 嵌套。
|
|
|
4. **query 传参必须 encodeURIComponent**——`MerchantTradeDate` 含 `/` 和空格,不编码会截断。
|
|
|
5. **多次发起**——同一订单可重复发起(每次新 `MerchantTradeNo`),后端按 OMG 交易号 `tradeNo` 幂等,前端重复点「支付」不会重复扣款,但仍建议按钮防抖(后端已加 1s `@RepeatSubmit`)。
|
|
|
-6. **不要重新开放延期支付入口**——新表单只认 `ChoosePayment=Credit`、`UnionPay=2`;历史回调兼容不代表可创建 ATM/超商交易。
|
|
|
-7. **退款范围**——新订单只有信用卡/Apple Pay;历史 ATM/超商交易若出现退款仍按后台人工流程处理。
|
|
|
+6. **付款方式固定**——表单只使用 `ChoosePayment=Credit`、`UnionPay=2`,不实现延期支付入口。
|
|
|
+7. **退款范围**——当前订单只有信用卡/Apple Pay。
|
|
|
|
|
|
---
|
|
|
|
|
|
@@ -410,13 +354,7 @@ const poll = (ddId) => {
|
|
|
| `payOmg.success` | 支付成功 | 结果页 |
|
|
|
| `payOmg.fail` | 支付失败 | 结果页 |
|
|
|
| `payOmg.confirming` | 支付结果确认中 | 轮询中 |
|
|
|
-| `payOmg.bankCode` | 银行代码 | 取号展示 |
|
|
|
-| `payOmg.vAccount` | 虚拟帐号 | 取号展示 |
|
|
|
-| `payOmg.paymentNo` | 缴费代码 | 超商取号 |
|
|
|
-| `payOmg.expireDate` | 缴费期限 | 取号展示 |
|
|
|
-| `payOmg.atmTip` | 请在期限内完成缴费,缴费后订单将自动变为已支付 | 延期支付提示 |
|
|
|
| `payOmg.storeNotSupported` | 该门店暂不支持在线支付 | 门店未开通 |
|
|
|
-| `payOmg.refundManual` | 该支付方式需人工退款,请联系客服 | 退款人工提示 |
|
|
|
|
|
|
> key 必须用有意义英文(驼峰),加到 4 个语言文件**对应层级对象内**(参考项目 CLAUDE.md 的 i18n 规范)。
|
|
|
|
|
|
@@ -427,9 +365,9 @@ const poll = (ddId) => {
|
|
|
| 前端动作 | 后端方法 | 文件 |
|
|
|
|---------|---------|------|
|
|
|
| 发起支付 | `OmgPayController.create` | `app/pay/OmgPayController.java` |
|
|
|
-| 结果页 302 | `OmgPayController.returnCallback` | 同上 |
|
|
|
-| 取号查询 | `OmgPayController.getPaymentInfo` | 同上 |
|
|
|
-| 退款 | `OmgPayController.refund` | 同上 |
|
|
|
-| 服务端回调(前端无感) | `OmgPayController.notify` / `paymentInfoCallback` | 同上 |
|
|
|
+| 支付结果桥接页 | `OmgPaymentReturnController.result` / `back` | `app/omgpay/OmgPaymentReturnController.java` |
|
|
|
+| 状态查询 | `OmgPaymentController.query` | `app/omgpay/OmgPaymentController.java` |
|
|
|
+| 退款 | `OmgPaymentController.refund` | 同上 |
|
|
|
+| 服务端回调(前端无感) | `OmgPaymentController.notify` | 同上 |
|
|
|
|
|
|
后端契约详见 `specs/016-omg-payment/contracts/api.md`。
|