|
|
@@ -27,7 +27,7 @@
|
|
|
### 1.2 Explicitly out of scope
|
|
|
|
|
|
- ATM、CVS、BarcodeATM 取号结果通知及缴费信息展示。
|
|
|
-- `OrderResultURL`、`PaymentInfoURL`、`ClientRedirectURL`、`ClientBackURL`。
|
|
|
+- `PaymentInfoURL`、`ClientRedirectURL`、`ClientBackURL`。
|
|
|
- 定时/批量自动补单、独立人工补单入口;用户触发的当前支付查询补偿属于本阶段范围。
|
|
|
- 正式环境退款动作、取消交易、信用卡关账;本阶段只实现测试环境安全拒绝入口。
|
|
|
- 分期、定期定额、记忆卡号、银联专用流程。
|
|
|
@@ -47,7 +47,7 @@
|
|
|
|
|
|
- 每个门店使用独立的 `MerchantID / HashKey / HashIV`,不使用平台统一凭证。
|
|
|
- 不传 `PlatformID`。
|
|
|
-- `ChoosePayment` 固定为 `ALL`;具体显示渠道以该门店在 OMG 后台实际开通的能力为准。
|
|
|
+- `ChoosePayment` 固定为 `ALL`,并发送 `IgnorePayment=ATM#CVS#BarcodeATM`;优先让 OMG 官方收银台只展示信用卡与 Apple Pay。官方公开参数不能隐藏 AFTEE,因此商户同时开通 AFTEE 时不承诺严格只剩两个渠道,必须以 stage 实测结果为准。
|
|
|
- 客户端在当前页面提交表单,不使用 iframe,不打开新窗口。
|
|
|
- 当前仅接 OMG 测试环境。
|
|
|
- 新可信公开路径继续使用 `/pay/omg/*`;创建入口为 `POST /pay/omg/create`,可信回调为 `POST /pay/omg/notify`。
|
|
|
@@ -61,7 +61,7 @@
|
|
|
|
|
|
### User Story 1 - 首次创建并进入 OMG 收银台 (Priority: P1)
|
|
|
|
|
|
-已登录用户为自己的单门店餐饮订单选择 OMG 后,调用创建接口并取得由服务端签名的表单。客户端在当前页面 POST 该表单,进入对应门店的 OMG 测试收银台,并看到 `ALL` 下该门店已开通的付款方式。
|
|
|
+已登录用户为自己的单门店餐饮订单选择 OMG 后,调用创建接口并取得由服务端签名的表单。客户端在当前页面 POST 该表单,进入对应门店的 OMG 测试收银台;服务端通过 `ALL + IgnorePayment` 隐藏 ATM、CVS 和 BarcodeATM,优先保留信用卡与 Apple Pay。
|
|
|
|
|
|
**Why this priority**: 这是进入 OMG 收银台的基础,也是本期最终付款回调及后续查询、退款的前置能力。
|
|
|
|
|
|
@@ -71,7 +71,8 @@
|
|
|
|
|
|
1. **Given** 用户拥有一笔 `payType="2"`、未取消、未付款、金额为正的单门店订单,且门店 OMG 凭证已启用,**When** 用户首次调用创建接口,**Then** 系统创建唯一支付尝试并返回可提交的 OMG 表单。
|
|
|
2. **Given** 创建接口返回成功,**When** 客户端在当前页面向 `gatewayUrl` POST 全部 `formFields`,**Then** 浏览器进入 OMG 测试收银台,不通过 iframe 或新窗口加载。
|
|
|
-3. **Given** 门店在 OMG 后台开通多个付款渠道,**When** 收银台接收 `ChoosePayment=ALL`,**Then** 收银台按 OMG 与门店配置展示可用渠道。
|
|
|
+3. **Given** 门店已开通信用卡、Apple Pay、ATM、CVS 和 BarcodeATM,**When** 收银台接收 `ChoosePayment=ALL` 与 `IgnorePayment=ATM#CVS#BarcodeATM`,**Then** OMG 页面隐藏 ATM、CVS 和 BarcodeATM,并保留已开通的信用卡与 Apple Pay。
|
|
|
+4. **Given** 门店还开通 AFTEE 或 OMG 未来新增且不支持 `IgnorePayment` 的渠道,**When** 进入收银台,**Then** 手动验收如实记录额外渠道;本方案不通过非官方参数或自有页面强行隐藏。
|
|
|
|
|
|
---
|
|
|
|
|
|
@@ -176,13 +177,13 @@ OMG 向 `ReturnURL` 发送最终付款结果时,系统保存本次 HTTP 回传
|
|
|
- **FR-009**: 系统 MUST 为每次新尝试生成全局唯一、不可复用、长度不超过 20 且只含 ASCII 英数字的 `MerchantTradeNo`;不得从业务订单号直接派生可冲突或超长的编号。
|
|
|
- **FR-010**: 同一业务订单在任一时刻 MUST 最多存在一条未结束 OMG 尝试;顺序重复或并发创建 MUST 返回业务状态 `PAYMENT_ATTEMPT_EXISTS`,不得返回旧表单、重复提交旧编号或创建新编号。
|
|
|
- **FR-011**: 在可信查询尚未实现前,系统 MUST NOT 基于固定分钟窗口、本地创建时间或用户再次点击自动结束 `CREATED` 尝试。
|
|
|
-- **FR-012**: 创建表单 MUST 发送以下字段和值:`MerchantID`、`MerchantTradeNo`、`MerchantTradeDate`、`PaymentType=aio`、`TotalAmount`、`TradeDesc`、`ItemName`、`ReturnURL`、`ChoosePayment=ALL`、`EncryptType=1`、`InvoiceMark=N`、`NeedExtraPaidInfo=Y`、`ExpireDate=1`、`StoreExpireDate=30`、`BarcodeATMExpireDate=1` 和 `CheckMacValue`。
|
|
|
-- **FR-013**: 创建表单 MUST NOT 发送 `PlatformID`、`PaymentInfoURL`、`OrderResultURL`、`ClientRedirectURL`、`ClientBackURL`、`Language`、分期、定期定额、记忆卡号或银联专用参数。
|
|
|
+- **FR-012**: 创建表单 MUST 发送以下字段和值:`MerchantID`、`MerchantTradeNo`、`MerchantTradeDate`、`PaymentType=aio`、`TotalAmount`、`TradeDesc`、`ItemName`、`ReturnURL`、`OrderResultURL`、`ChoosePayment=ALL`、`IgnorePayment=ATM#CVS#BarcodeATM`、`EncryptType=1`、`InvoiceMark=N`、`NeedExtraPaidInfo=Y` 和 `CheckMacValue`。
|
|
|
+- **FR-013**: 创建表单 MUST NOT 发送 `PlatformID`、`PaymentInfoURL`、`ClientRedirectURL`、`ClientBackURL`、`Language`、`UnionPay`、ATM/CVS/BarcodeATM 期限字段、分期、定期定额或记忆卡号参数。
|
|
|
- **FR-014**: `MerchantTradeDate` MUST 以 `Asia/Taipei` 时区格式化为 `yyyy/MM/dd HH:mm:ss`。
|
|
|
- **FR-015**: `TradeDesc` 和 `ItemName` MUST 由服务端生成,禁止 HTML,符合 OMG 字符及长度限制;`ItemName` 不得超过中文 60 字或英数字 120 字的官方显示限制,字段总长度不得超过官方 `String(200)` 限制。
|
|
|
- **FR-016**: `ReturnURL` MUST 是受控 HTTPS 地址并固定以 `/pay/omg/notify` 结尾;该路径 MUST 由新的 `omgpay` Controller 处理,不得被旧 Controller 接收。
|
|
|
- **FR-017**: `CheckMacValue` MUST 严格按 OMG 官方规则生成:排除 `CheckMacValue` 本身,将其余全部实际发送字段按官方字母顺序排序,以 `&` 串接,前置 `HashKey=...&`、后置 `&HashIV=...`,执行符合官方 .NET 表的 URL 编码并转小写,使用 SHA-256,最后输出大写十六进制。
|
|
|
-- **FR-018**: 除 `CheckMacValue` 自身外,创建请求实际发送的全部字段 MUST 参加签名,包括 `NeedExtraPaidInfo=Y` 和三个期限字段;不得挑选所谓核心字段计算。
|
|
|
+- **FR-018**: 除 `CheckMacValue` 自身外,创建请求实际发送的全部字段 MUST 参加签名,包括 `OrderResultURL`、`ChoosePayment=ALL`、`IgnorePayment=ATM#CVS#BarcodeATM` 和 `NeedExtraPaidInfo=Y`;不得挑选所谓核心字段计算。
|
|
|
- **FR-019**: 回调 MUST 遵守同一完整字段原则:除 `CheckMacValue` 外,OMG 实际返回的全部字段均参加验签;启用 `NeedExtraPaidInfo=Y` 后,全部额外回传字段、未知字段及空值字段也必须进入验签集合。重复参数名必须作为非法请求拒绝,不得静默选取其中一个值。
|
|
|
- **FR-020**: 创建成功响应 MUST 使用明确对象 `{status, gatewayUrl, formFields}`;`status` 固定为 `CREATED`,`gatewayUrl` 为测试 AioCheckOut 端点,`formFields` 含实际需要 POST 的全部字段但不含 `gatewayUrl`。
|
|
|
- **FR-021**: 客户端 MUST 在当前页面以 `application/x-www-form-urlencoded` 表单 POST 全部 `formFields` 到 `gatewayUrl`;不得使用 iframe,不得另开新窗口,不得把响应转换为 GET 查询链接。
|
|
|
@@ -238,13 +239,12 @@ token: <login-token>
|
|
|
"TradeDesc": "Food order 991786433092835",
|
|
|
"ItemName": "Order 991786433092835",
|
|
|
"ReturnURL": "https://example.test/pay/omg/notify",
|
|
|
+ "OrderResultURL": "https://example.test/pay/omg/result",
|
|
|
"ChoosePayment": "ALL",
|
|
|
+ "IgnorePayment": "ATM#CVS#BarcodeATM",
|
|
|
"EncryptType": "1",
|
|
|
"InvoiceMark": "N",
|
|
|
"NeedExtraPaidInfo": "Y",
|
|
|
- "ExpireDate": "1",
|
|
|
- "StoreExpireDate": "30",
|
|
|
- "BarcodeATMExpireDate": "1",
|
|
|
"CheckMacValue": "<64 uppercase hexadecimal characters>"
|
|
|
}
|
|
|
}
|