Input: spec.md, plan.md, research.md, data-model.md, contracts/api.md, quickstart.md
Tests: 本功能强制 TDD。每个生产任务先写失败测试并实际观察失败,再写最小实现。
Scope: 完成测试环境创建支付订单、最终付款结果回调及用户触发的当前支付查询补偿;不实现取号、定时/批量补单、退款、通用支付推送、关账或正式环境。2026-08-28 增量仅追加外送订单支付成功后的骑手开放推送。
ruoyi-system/src/test/java/com/ruoyi/system/omgpay/service/OmgPaymentAttemptServiceTest.java 编写新尝试字段校验、CREATED 插入和生成列不由 Java 写入的失败测试ruoyi-system/src/test/java/com/ruoyi/system/omgpay/mapper/OmgPaymentAttemptMapperContractTest.java 编写 FOR UPDATE、新表名、唯一键及无旧表 SQL 的失败合约测试updatesql/sql.md 仅追加 pos_order_omg_attempt DDL,不执行数据库变更ruoyi-system/src/main/java/com/ruoyi/system/omgpay/domain/ 新建 OmgPaymentAttempt 和 OmgPaymentOrderSnapshotruoyi-system/src/main/java/com/ruoyi/system/omgpay/mapper/OmgPaymentAttemptMapper.java 与 ruoyi-system/src/main/resources/mapper/omgpay/OmgPaymentAttemptMapper.xml 实现锁定订单、活跃尝试、交易号查询和显式 INSERTruoyi-system/src/main/java/com/ruoyi/system/omgpay/service/ 实现 IOmgPaymentAttemptService 与校验严格的 OmgPaymentAttemptServiceImplOmgPaymentAttemptServiceTest,OmgPaymentAttemptMapperContractTest 并确认通过Checkpoint: 新表 DDL 已登记,system 模块可锁定订单并只写新尝试表。
OmgCheckMacSignerTest 写入官方期望值 AA5842FDA7E55ACEB7118D6353E9822CA6D6FF09A0D1FC129A879DD5CAF93266、额外字段、空值字段、输入不可变和非法字段失败测试com.ruoyi.app.omgpay.OmgCheckMacSigner 实现排序、HashKey/HashIV 包夹、官方 .NET URL encode、小写、SHA-256 和大写输出OmgCheckMacSignerTest,确认官方向量和字段完整性全部通过OmgMerchantTradeNoGeneratorTest 编写 20 位大写英数字与样本唯一性失败测试OmgPaymentFormFactoryTest 编写 Taipei 时间、stage URL、15 字段、固定字段、全部字段签名及不安全 ReturnURL 失败测试OmgPaymentProperties、OmgMerchantTradeNoGenerator、OmgPaymentForm 与 OmgPaymentFormFactoryapplication.yml 新增独立 omgpay.return-url,新代码不读取旧 omg.* 创建/回调配置Checkpoint: 可从服务端事实生成一份严格 stage-only、全字段签名的表单。
OmgPaymentCreateServiceTest 编写订单不存在/越权、非单店、非法状态、已支付、金额、payType、门店和凭证失败矩阵PAYMENT_ATTEMPT_EXISTS 且不调用生成器/表单/凭证的失败测试OmgPaymentErrorCode、OmgPaymentBusinessException、OmgPaymentCreateOutcome 和响应 DTO@Transactional OmgPaymentCreateService#create,保证锁订单和活跃查询发生在 MerchantTradeNo 生成前OmgPaymentCreateServiceTest 并检查新包对旧 OMG 类/Service 零引用Checkpoint: 首次请求创建,重复请求稳定拒绝;没有任何网关 HTTP 或旧支付表访问。
OmgPaymentControllerTest 编写 /pay/omg/create、token header、显式 DTO、无 Map 入参和请求字段白名单失败测试omg.pay.* key 集合OmgPaymentTokenUserResolver、请求/错误 DTO 与新 OmgPaymentControllerOmgPaymentControllerTest 和 i18n key 对齐检查Checkpoint: 新 Controller 独占 /pay/omg/create,响应与日志满足安全契约。
OmgLegacyRetirementTest 编写旧 Controller、旧任务、旧支付实体必须不存在及新 Controller 只有 create 路由的失败测试OmgPayController、专属 DTO、OmgReconcileTask、OmgQueryThrottle 和旧 Controller 测试pos_store_omg 全部可信实现UserOrderController 与 PosOrderShOprateController 取消链路移除旧 OMG 退款调用,不改变其他取消行为PosOrderController 移除旧 OMG 补单、退款、人工确认端点及注入/辅助方法OrderLifecycleService 移除旧表依赖和旧 OMG 补单/退款方法,保留 payType="2" 的支付前进门禁OrderLifecycleServiceTest,移除旧表测试/构造参数并保留已有 LINE 和订单生命周期测试OmgLegacyRetirementTest,OrderLifecycleServiceTest,并确认 main Java/XML 对旧支付/退款表及服务零匹配application.yml 的旧 omg 配置收敛为凭证验证所需 base-url,移除旧回调/查询/补单/退款配置并保留新 omgpay.return-urlCheckpoint: 旧表删除后没有可达运行链路访问它们;旧回调、查询、补单和退款接口全部不可达。
OrderLifecycleServiceTest,LinePayCancellationRaceTestmvn -pl ruoyi-admin -am -DskipTests packagegit diff --check、git status --short、git diff --stat 并确认无无关文件quickstart.md 验证首次创建、当前页 POST、重复、真实并发、负例和日志pos_order_omg_attempt DDL:新增 HashKey/HashIV 快照、PAID/FAILED/SUPERSEDED 状态、网关结果字段和 trade_no 唯一键;只写 updatesql/sql.md,不执行 SQLpay_statusOmgPaymentAttempt、Mapper XML 和 IOmgPaymentAttemptService,实现带当前状态条件的原子更新createCreated 并持久化快照OmgNotifyRequest 与专用参数解析/ArgumentResolver;Controller 业务入参保持一个 DTO,不使用 Map 或 HttpServletRequesttype=omg、完整原文以及日志写入失败不阻断处理com.ruoyi.system.omgpay 新建 OmgIpnAuditService,以 REQUIRES_NEW 复用 IIpnLogService 写入现有 ipn_logOmgPaymentNotifyService,在一个业务事务内锁定尝试、验签、校验并更新尝试与订单;不触发订单/配送状态、推送或退款副作用1|OK/0|ERROR、完整请求日志和无旧回调引用OmgPaymentController 增加 POST /pay/omg/notify,先独立记录 IPN,再执行业务处理;所有请求完整记录,数据库密钥不入日志ipn_log/pos_store_omg 创建来源、无旧 OMG 业务引用quickstart.md 的回调重放示例、成功/失败/重复/模拟/取消后付款与 IPN 查询步骤git diff --check、定向 rg 和 staged diff 审计;本阶段不运行 Maven、编译或测试CREATED 或已付款订单的首条 PAID 尝试,不接受客户端指定 MerchantTradeNoOmgPaymentQueryGateway,只 POST OMG stage QueryTradeInfo/V5,使用尝试的 MerchantID/HashKey/HashIV 快照签名并设置连接/读取超时POST /pay/omg/query:核验订单归属、交易号、商户号、金额和完整响应签名,只返回字段白名单TradeStatus=1 复用回调同一幂等事务补偿为已付款;0 不修改;10200095 同步失败;任何结果都不得降级已付款事实ipn_log,因为它不是网关主动 IPNorderId DTO、稳定状态、异常映射和无网关/无状态依赖OmgRefundPaymentRequest 与 OmgPaymentRefundService,在身份/订单号格式校验后直接返回测试环境不支持POST /pay/omg/refund,返回 PAYMENT_REFUND_UNAVAILABLE_IN_TEST_ENVIRONMENT,不接受金额、交易号或 ActionOmgLegacyRetirementTest,先确认旧 com.ruoyi.app.utils.omg 三个类仍存在而失败com.ruoyi.app.omgpay 实现 OmgStoreCredentialVerifierPosStoreOmgController 切换到新 verifier,删除旧工具类、旧工具测试和 omg.base-url;保留 pos_store_omg 全部凭证持久化与管理能力updatesql/sql.md 追加删除 pos_order_omg_refund、pos_order_omg_payment 的 SQL,不执行数据库变更id + CREATED 条件原子更新单条尝试为 SUPERSEDED;复用现有表结构,不新增或执行 SQLMerchantTradeNo 精确比较、旧尝试关闭与新表单创建的同事务替换OmgPaymentRetryService 与 POST /pay/omg/retry,按 PAID/FAILED/UNPAID/UNKNOWN 安全分流并增加五语言错误状态ChoosePayment=ALL、IgnorePayment=ATM#CVS#BarcodeATM 且不发送 UnionPay,并检查两个固定字段进入签名集合OmgPaymentFormFactory,由服务端固定生成并签名上述渠道参数,不改变创建、retry 请求或响应结构TradeStatus=0/10200095 响应编写回归测试,并观察固定字段校验导致测试失败PAID 必需的结算字段;所有实际字段继续参与验签10200047 + TradeAmt=0 回归测试,并验证非零金额时 fail closed10200047 + TradeAmt=0 同步为失败,释放旧尝试并复用 retry 新表单流程pages/OrderList/buy/omgCheckout.vue 确认 renderjs Form 替换当前页面 WebView,并记录旧版本停留结果页的失败基线omgCheckout.vue 的普通 <script> 中仅对 App-Plus iOS 监听当前 this.$scope.$getAppWebview() 的 loaded,不得读取 children()[0]https://foodieapi.waimai-paotui.com/pay/omg/result 及其 query/hash 形式,只触发一次 handleOmgReturnDetected({ orderId, currentUrl }),页面卸载时移除监听OmgPaymentClientReturnServiceTest,确认后端现有 HTML、繁体提示、“返回 App”按钮与 Scheme 兜底保持不变result_page_detected 单次触发,并用中间页/取消页、Android 和外部浏览器完成负例及兼容验收ALL + IgnorePayment 方案CREDIT/APPLE_PAY、非法枚举无副作用、动态字段集合和完整签名输入paymentMethod,统一映射 CREDIT -> Credit + UnionPay=2、APPLE_PAY -> ApplePay,新增五语言 PAYMENT_METHOD_INVALID,禁止客户端 OMG 原始参数OmgPaymentNotifyService 首次更新订单支付状态后加载业务订单,并复用配送推送服务按统一条件开放给骑手POST /pay/omg/create 首次返回可提交的 stage 表单。POST /pay/omg/notify 对所有实际字段验签,并幂等同步成功/失败支付事实。ipn_log;不新增该表字段。payStatus,不推进业务状态或触发退款;外送订单首次支付成功可触发骑手开放推送,其他通用支付推送不触发。CREATED,后续请求为 PAYMENT_ATTEMPT_EXISTS。CREDIT/APPLE_PAY;新支付表单分别固定为 ChoosePayment=Credit + UnionPay=2 或 ChoosePayment=ApplePay,不发送 ChoosePayment=ALL 或 IgnorePayment。omgpay 包,不引用旧 OMG 支付实现。pos_store_omg 凭证表、持久化和管理能力保留;联网探测只使用新 omgpay 实现。omgCheckout 监听当前页面 WebView,精确结果 URL 单次触发交接;后端现有结果页、按钮和 Scheme 兜底保持兼容。