فهرست منبع

修复:调整 OMG 收银台支付渠道

qmj 2 هفته پیش
والد
کامیت
ccf7ec94f6

+ 2 - 3
ruoyi-admin/src/main/java/com/ruoyi/app/omgpay/OmgPaymentFormFactory.java

@@ -46,9 +46,8 @@ public class OmgPaymentFormFactory {
         fields.put("ItemName", "Order " + safeOrderId);
         fields.put("ReturnURL", properties.requireSafeReturnUrl());
         fields.put("OrderResultURL", properties.requireSafeOrderResultUrl());
-        // OMG documents Apple Pay under Credit. UnionPay=2 hides the remaining UnionPay option.
-        fields.put("ChoosePayment", "Credit");
-        fields.put("UnionPay", "2");
+        fields.put("ChoosePayment", "ALL");
+        fields.put("IgnorePayment", "ATM#CVS#BarcodeATM");
         fields.put("EncryptType", "1");
         fields.put("InvoiceMark", "N");
         fields.put("NeedExtraPaidInfo", "Y");

+ 8 - 6
ruoyi-admin/src/test/java/com/ruoyi/app/omgpay/OmgPaymentFormFactoryTest.java

@@ -24,7 +24,7 @@ import static org.mockito.Mockito.when;
 
 class OmgPaymentFormFactoryTest {
     @Test
-    void buildsSignedFormThatOnlyOffersCreditAndApplePay() {
+    void buildsSignedFormThatUsesOmgPageForCreditAndApplePay() {
         OmgPaymentProperties properties = properties();
         OmgCheckMacSigner signer = mock(OmgCheckMacSigner.class);
         when(signer.sign(anyMap(), eq("KEY"), eq("IV"))).thenReturn("A".repeat(64));
@@ -36,11 +36,12 @@ class OmgPaymentFormFactoryTest {
         assertEquals(OmgPaymentFormFactory.STAGE_GATEWAY_URL, form.gatewayUrl());
         assertEquals(Set.of("MerchantID", "MerchantTradeNo", "MerchantTradeDate", "PaymentType",
                 "TotalAmount", "TradeDesc", "ItemName", "ReturnURL", "ChoosePayment", "EncryptType",
-                "InvoiceMark", "NeedExtraPaidInfo", "UnionPay", "OrderResultURL",
+                "InvoiceMark", "NeedExtraPaidInfo", "IgnorePayment", "OrderResultURL",
                 "CheckMacValue"), form.fields().keySet());
         assertEquals("2026/08/13 15:30:23", form.fields().get("MerchantTradeDate"));
-        assertEquals("Credit", form.fields().get("ChoosePayment"));
-        assertEquals("2", form.fields().get("UnionPay"));
+        assertEquals("ALL", form.fields().get("ChoosePayment"));
+        assertEquals("ATM#CVS#BarcodeATM", form.fields().get("IgnorePayment"));
+        assertFalse(form.fields().containsKey("UnionPay"));
         assertEquals("Y", form.fields().get("NeedExtraPaidInfo"));
         assertEquals("https://foodieapi.waimai-paotui.com/pay/omg/result",
                 form.fields().get("OrderResultURL"));
@@ -53,8 +54,9 @@ class OmgPaymentFormFactoryTest {
         assertFalse(form.fields().get("ItemName").contains("|"));
         assertTrue(form.fields().get("ItemName").length() <= 120);
         verify(signer).sign(org.mockito.ArgumentMatchers.argThat(fields -> fields.size() == 14
-                && "Credit".equals(fields.get("ChoosePayment"))
-                && "2".equals(fields.get("UnionPay"))
+                && "ALL".equals(fields.get("ChoosePayment"))
+                && "ATM#CVS#BarcodeATM".equals(fields.get("IgnorePayment"))
+                && !fields.containsKey("UnionPay")
                 && !fields.containsKey("CheckMacValue")), eq("KEY"), eq("IV"));
         assertThrows(UnsupportedOperationException.class, () -> form.fields().put("extra", "value"));
     }

+ 10 - 8
specs/020-omg-payment-rebuild/omg-app-integration.md

@@ -3,7 +3,7 @@
 **适用对象**:用户端 App(uni-app)前端开发人员
 **接口版本**:`specs/020-omg-payment-rebuild` 新实现
 **当前环境**:OMG 测试环境
-**支付方式**:信用卡、Apple Pay
+**支付方式目标**:OMG 官方页面优先展示信用卡、Apple Pay
 **更新时间**:2026-08-14
 
 > 本文只描述当前新 OMG 实现。`specs/016-omg-payment` 中的旧 Controller、旧流水、旧退款和旧前端文档已经退役,不得作为接入依据。
@@ -19,6 +19,8 @@ OMG 是网页托管式收银台,App 不需要接入 OMG SDK。前端只需要
 5. App 结果页取得 `ddId`,调用 `POST /pay/omg/query` 确认最终支付状态。
 6. 只有 query 返回 `status = "PAID"` 才能展示支付成功。
 
+本次渠道过滤由后端表单完成。App 顶层仍然只显示“OMG 支付”和“LINE Pay”,不增加“信用卡/Apple Pay”二级选择页面,也不接入原生 Apple Pay SDK。
+
 当前测试环境不支持退款。`POST /pay/omg/refund` 只会返回“测试环境不支持退款”,不会发起真实退款,也不会修改订单。
 
 ## 2. 整体流程
@@ -34,7 +36,7 @@ OMG 是网页托管式收银台,App 不需要接入 OMG SDK。前端只需要
              |
              | 当前 WebView 原样 Form POST
              v
-[OMG 测试收银台:信用卡 / Apple Pay]
+[OMG 测试收银台:优先显示信用卡 / Apple Pay]
              |
              +-------------------------------+
              |                               |
@@ -174,8 +176,8 @@ token: <用户登录 token>
       "ItemName": "Order 991786433092835",
       "ReturnURL": "https://foodieapi.waimai-paotui.com/pay/omg/notify",
       "OrderResultURL": "https://foodieapi.waimai-paotui.com/pay/omg/result",
-      "ChoosePayment": "Credit",
-      "UnionPay": "2",
+      "ChoosePayment": "ALL",
+      "IgnorePayment": "ATM#CVS#BarcodeATM",
       "EncryptType": "1",
       "InvoiceMark": "N",
       "NeedExtraPaidInfo": "Y",
@@ -194,8 +196,8 @@ token: <用户登录 token>
 | `formFields` | object | 所有键值都必须原样创建为隐藏 input 并 POST |
 | `MerchantTradeNo` | string | 后端生成的 OMG 交易编号;前端只透传,不作为业务订单号 |
 | `TotalAmount` | string | 后端根据订单金额生成;前端不得修改 |
-| `ChoosePayment` | string | 当前固定 `Credit`,包含信用卡与 Apple Pay |
-| `UnionPay` | string | 当前固定 `2`,隐藏银联选项 |
+| `ChoosePayment` | string | 当前固定 `ALL`,使用 OMG 官方付款方式选择页面 |
+| `IgnorePayment` | string | 当前固定 `ATM#CVS#BarcodeATM`,由 OMG 隐藏 ATM、CVS 和 BarcodeATM;前端不得修改 |
 | `ReturnURL` | string | OMG 服务端付款通知地址;前端不调用 |
 | `OrderResultURL` | string | OMG 浏览器付款结果地址;前端不直接调用 |
 | `CheckMacValue` | string | 表单签名;修改任意已签名字段都会导致 OMG 拒绝 |
@@ -646,7 +648,7 @@ token: <用户登录 token>
 10. `tradeNo`、`merchantTradeNo` 始终按字符串处理,不转换为 JavaScript Number。
 11. query 会访问 OMG 网关,不做高频轮询。
 12. `PAYMENT_ATTEMPT_EXISTS` 不能靠重复调用 create 解决,应进入 query 确认流程。
-13. 当前测试收银台只开放信用卡与 Apple Pay,不实现 ATM、CVS、BarcodeATM、银联、延期付款或取号页面
+13. 后端通过 `ChoosePayment=ALL` 与 `IgnorePayment=ATM#CVS#BarcodeATM` 优先保留信用卡和 Apple Pay。OMG 官方公开参数不能隐藏 AFTEE;如果门店开通 AFTEE,需要以 stage 页面实测结果为准
 14. 当前没有真实退款能力。
 
 ## 10. App 验收清单
@@ -659,7 +661,7 @@ token: <用户登录 token>
 - [ ] create 成功后,当前完整 WebView 使用 Form POST 打开 `gatewayUrl`。
 - [ ] 所有 `formFields` 原样提交,`gatewayUrl` 不作为 input。
 - [ ] 没有 iframe、GET 跳转或新窗口。
-- [ ] 测试收银台显示正确金额,并只提供信用卡/Apple Pay 范围
+- [ ] 测试收银台显示正确金额,信用卡与 Apple Pay 可用,ATM/CVS/BarcodeATM 已隐藏;如果门店开通 AFTEE,记录它是否仍显示
 
 ### 10.2 App 返回与状态确认
 

+ 62 - 0
specs/020-omg-payment-rebuild/plan.md

@@ -1127,3 +1127,65 @@ No constitution violations. The separate form factory, signer, generator and per
 4. `UNPAID` 替换在独立写事务中锁订单并再次验证订单,要求活动尝试交易号与 query 结果精确一致,然后以 `id + CREATED` 条件更新为 `SUPERSEDED` 并插入新尝试。
 5. 两个并发 retry 即使都查到同一旧交易,也只有先取得订单锁者能替换;后取得者看到活动交易号变化后返回 `PAYMENT_RETRY_NOT_AVAILABLE`。
 6. retry 成功复用 create 响应,返回新 `MerchantTradeNo` 的新表单。无需数据库结构变更,现有 `attempt_status=3` 与生成列唯一键已覆盖该流程。
+
+## 2026-08-14 OMG 官方收银台渠道过滤实施计划
+
+**Goal:** 保持 App 直接进入 OMG 官方收银台,通过服务端固定表单参数优先只展示信用卡与 Apple Pay。
+
+**Architecture:** 不修改 `POST /pay/omg/create`、`POST /pay/omg/retry` 的请求或响应结构。两条路径继续复用 `OmgPaymentFormFactory#create(...)`;工厂将单渠道 `Credit + UnionPay=2` 改为 `ChoosePayment=ALL + IgnorePayment=ATM#CVS#BarcodeATM`,并把过滤字段纳入 `CheckMacValue`。
+
+**Tech Stack:** Java 21、Spring Boot、JUnit 5、Mockito、Maven、OMG AIO stage。
+
+### Task 1: 表单渠道参数与签名
+
+**Files:**
+
+- Modify: `ruoyi-admin/src/test/java/com/ruoyi/app/omgpay/OmgPaymentFormFactoryTest.java`
+- Modify: `ruoyi-admin/src/main/java/com/ruoyi/app/omgpay/OmgPaymentFormFactory.java`
+
+**Interfaces:**
+
+- Consumes: `OmgPaymentFormFactory#create(String orderId, Integer amount, String merchantId, String hashKey, String hashIv, String merchantTradeNo)`
+- Produces: 原接口不变;`OmgPaymentForm.fields()` 返回包含 `ChoosePayment=ALL`、`IgnorePayment=ATM#CVS#BarcodeATM` 且不含 `UnionPay` 的不可变字段集合。
+
+- [x] 将测试改为 `buildsSignedFormThatUsesOmgPageForCreditAndApplePay`:字段集合移除 `UnionPay`、加入 `IgnorePayment`;断言 `ChoosePayment=ALL`、`IgnorePayment=ATM#CVS#BarcodeATM`、无 `UnionPay`;签名入参仍为 14 个非 `CheckMacValue` 字段。
+- [x] 使用 JDK 21 运行 `OmgPaymentFormFactoryTest`,确认当前 `ChoosePayment=Credit`、缺少 `IgnorePayment` 的实现导致测试失败。
+- [x] 修改 `OmgPaymentFormFactory`:删除错误的 Apple Pay 注释、`ChoosePayment=Credit` 和 `UnionPay=2`,改为以下固定字段:
+
+```java
+fields.put("ChoosePayment", "ALL");
+fields.put("IgnorePayment", "ATM#CVS#BarcodeATM");
+```
+
+- [x] 再次运行 `OmgPaymentFormFactoryTest`,确认字段集合、值和签名入参全部通过。
+
+### Task 2: App 文档、回归与提交
+
+**Files:**
+
+- Modify: `specs/020-omg-payment-rebuild/omg-app-integration.md`
+- Modify: `specs/020-omg-payment-rebuild/tasks.md`
+
+**Interfaces:**
+
+- Consumes: create/retry 现有 `{status, gatewayUrl, formFields}` 响应。
+- Produces: App 仍直接 POST 全部 `formFields`,不增加支付渠道参数、二级选择页面或原生 Apple Pay SDK。
+
+- [x] 更新 App 文档中的示例表单和字段说明:`ChoosePayment=ALL`、`IgnorePayment=ATM#CVS#BarcodeATM`,删除 `UnionPay`;注明 AFTEE 不在官方可隐藏值中,需要 stage 实测。
+- [x] 使用 JDK 21 运行全部 OMG 定向测试:
+
+```powershell
+$env:JAVA_HOME='C:\Users\qmj\.jdks\graalvm-jdk-21.0.7'
+$env:PATH="$env:JAVA_HOME\bin;$env:PATH"
+mvn -pl ruoyi-admin -am "-Dtest=com.ruoyi.app.omgpay.*Test,com.ruoyi.system.omgpay.*Test" "-Dsurefire.failIfNoSpecifiedTests=false" test
+```
+
+- [x] 运行模块构建:
+
+```powershell
+mvn -pl ruoyi-admin -am -DskipTests package
+```
+
+- [x] 静态检查源码中只存在预期渠道参数,`git diff --check` 无错误,暂存清单只包含本阶段源码、测试和文档;不包含 `.claude/homunculus/*`。
+- [x] 将 T088-T091 标记为完成;T092 保持未完成,等待部署到 OMG stage 后人工查看收银台。
+- [x] 使用中文提交信息提交:`修复:调整 OMG 收银台支付渠道`。

+ 4 - 4
specs/020-omg-payment-rebuild/tasks.md

@@ -147,10 +147,10 @@
 
 ## Phase 15: OMG 官方收银台渠道过滤
 
-- [ ] T088 [US1] 更新表单工厂测试,先断言 `ChoosePayment=ALL`、`IgnorePayment=ATM#CVS#BarcodeATM` 且不发送 `UnionPay`,并观察当前实现失败
-- [ ] T089 [US1] 修改 `OmgPaymentFormFactory`,由服务端固定生成并签名上述渠道参数,不改变创建、retry 请求或响应结构
-- [ ] T090 [US1] 更新 App 接入文档,明确 App 继续直接提交 OMG 表单,不增加二级选择页面
-- [ ] T091 [US1] 使用 JDK 21 运行表单定向测试、OMG 回归和模块构建,并检查签名字段集合
+- [x] T088 [US1] 更新表单工厂测试,先断言 `ChoosePayment=ALL`、`IgnorePayment=ATM#CVS#BarcodeATM` 且不发送 `UnionPay`,并观察当前实现失败
+- [x] T089 [US1] 修改 `OmgPaymentFormFactory`,由服务端固定生成并签名上述渠道参数,不改变创建、retry 请求或响应结构
+- [x] T090 [US1] 更新 App 接入文档,明确 App 继续直接提交 OMG 表单,不增加二级选择页面
+- [x] T091 [US1] 使用 JDK 21 运行表单定向测试、OMG 回归和模块构建,并检查签名字段集合
 - [ ] T092 [US1] 在 OMG stage 手动确认信用卡与 Apple Pay 保留、ATM/CVS/BarcodeATM 隐藏,并记录 AFTEE 在门店开通时的实际展示结果
 
 ## Dependencies & Execution Order