|
|
@@ -1,12 +1,12 @@
|
|
|
-# Feature Specification: OMG AIO 支付重建——创建支付订单
|
|
|
+# Feature Specification: OMG AIO 支付重建——创建支付与付款结果回调
|
|
|
|
|
|
**Feature Branch**: `020-omg-payment-rebuild`
|
|
|
|
|
|
**Created**: 2026-08-13
|
|
|
|
|
|
-**Status**: Implemented and automated verification passed; pending developer-applied DDL and manual stage acceptance
|
|
|
+**Status**: 创建支付已实现;付款结果回调设计已批准并进入实现
|
|
|
|
|
|
-**Input**: 以 OMG 全方位金流 AIO 官方技术文件 V1.5.3(2026-07)为唯一外部事实来源,从零重建创建支付订单;现有 OMG 支付代码、旧支付流水和 `specs/016-omg-payment` 均不作为需求或设计依据。第一阶段只完成测试环境首次创建并进入 OMG 收银台,其余能力后续逐项重建。
|
|
|
+**Input**: 以 OMG 全方位金流 AIO 官方技术文件 V1.5.3(2026-07)为唯一外部事实来源,从零重建创建支付订单和 `ReturnURL` 付款结果回调;现有 OMG 支付代码、旧支付流水和 `specs/016-omg-payment` 均不作为需求或设计依据。查询、补单、退款与推送后续逐项重建。
|
|
|
|
|
|
## 1. Scope and Trust Boundary
|
|
|
|
|
|
@@ -17,17 +17,19 @@
|
|
|
- 根据 OMG 官方 AIO 规则生成完整表单和 `CheckMacValue`。
|
|
|
- 由客户端在当前页面以表单 POST 进入 OMG 测试收银台。
|
|
|
- 防止同一业务订单同时产生多个未结束 OMG 支付尝试。
|
|
|
+- 在 `POST /pay/omg/notify` 接收 OMG 最终付款结果,按创建尝试的凭证快照验签并幂等更新支付事实。
|
|
|
+- 每次回调独立写入现有 `ipn_log`,并保存完整、可重放的 form-urlencoded 回传内容。
|
|
|
+- 成功回调只把订单 `payStatus` 更新为已付款,不推进订单、配送或推送流程。
|
|
|
- 停用旧 OMG Controller 及其补单、退款、定时任务和订单取消调用入口。
|
|
|
|
|
|
### 1.2 Explicitly out of scope
|
|
|
|
|
|
-- `ReturnURL` 付款结果通知的接收、验签、核销和订单状态变更。
|
|
|
- ATM、CVS、BarcodeATM 取号结果通知及缴费信息展示。
|
|
|
- `OrderResultURL`、`PaymentInfoURL`、`ClientRedirectURL`、`ClientBackURL`。
|
|
|
- OMG 订单查询、自动补单、人工补单。
|
|
|
- 退款、取消交易、信用卡关账。
|
|
|
- 分期、定期定额、记忆卡号、银联专用流程。
|
|
|
-- 正式环境开放和真实付款完成验收。
|
|
|
+- 正式环境开放。
|
|
|
- 旧 OMG 支付流水的数据迁移、兼容或清理。
|
|
|
- 客户端页面代码;本阶段只定义客户端必须遵守的表单 POST 契约。
|
|
|
|
|
|
@@ -46,10 +48,12 @@
|
|
|
- `ChoosePayment` 固定为 `ALL`;具体显示渠道以该门店在 OMG 后台实际开通的能力为准。
|
|
|
- 客户端在当前页面提交表单,不使用 iframe,不打开新窗口。
|
|
|
- 当前仅接 OMG 测试环境。
|
|
|
-- 新可信公开路径继续使用 `/pay/omg/*`;创建入口为 `POST /pay/omg/create`,后续可信回调仍预定为 `POST /pay/omg/notify`。
|
|
|
+- 新可信公开路径继续使用 `/pay/omg/*`;创建入口为 `POST /pay/omg/create`,可信回调为 `POST /pay/omg/notify`。
|
|
|
- 旧 Controller 完全作废,不保留 `/legacy/*` 或任何其他旧 OMG 接口。
|
|
|
- 所有新实现代码放在新的 `omgpay` 包目录;新代码不得引用旧支付 Controller、旧签名器、旧表单工具或旧支付流水服务。
|
|
|
- 新支付尝试使用全新表 `pos_order_omg_attempt`。
|
|
|
+- 创建时把该次尝试实际使用的 `HashKey / HashIV` 保存为凭证快照;回调不读取可能已被覆盖的新凭证。
|
|
|
+- `ipn_log` 是可信的通用 IPN 流水表,但旧 OMG 写入逻辑不可信;新回调只复用其现有表结构和通用插入 Service。
|
|
|
|
|
|
## User Scenarios & Testing
|
|
|
|
|
|
@@ -57,7 +61,7 @@
|
|
|
|
|
|
已登录用户为自己的单门店餐饮订单选择 OMG 后,调用创建接口并取得由服务端签名的表单。客户端在当前页面 POST 该表单,进入对应门店的 OMG 测试收银台,并看到 `ALL` 下该门店已开通的付款方式。
|
|
|
|
|
|
-**Why this priority**: 这是本阶段唯一交付的用户价值,也是后续回调、查询和退款的前置能力。
|
|
|
+**Why this priority**: 这是进入 OMG 收银台的基础,也是本期最终付款回调及后续查询、退款的前置能力。
|
|
|
|
|
|
**Independent Test**: 为测试门店配置有效测试凭证,创建一笔合法未支付订单,调用接口并提交响应表单,确认浏览器进入官方测试端点且收银台显示正确订单金额及可用渠道。
|
|
|
|
|
|
@@ -101,6 +105,27 @@
|
|
|
4. **Given** 服务配置不是允许的 OMG 测试端点,**When** 调用创建接口,**Then** 系统拒绝生成表单。
|
|
|
5. **Given** 创建成功或失败,**When** 检查 API 响应和应用日志,**Then** 所有响应和日志均不存在 `HashKey`、`HashIV`;完整 `CheckMacValue` 与签名表单只存在于订单本人获准取得的创建成功响应,不出现在错误响应或日志中。
|
|
|
|
|
|
+---
|
|
|
+
|
|
|
+### User Story 4 - 可信接收最终付款结果 (Priority: P1)
|
|
|
+
|
|
|
+OMG 向 `ReturnURL` 发送最终付款结果时,系统保存本次 HTTP 回传原文,使用创建支付时的门店凭证快照验证全部实际字段,并幂等同步支付尝试与订单付款状态。
|
|
|
+
|
|
|
+**Why this priority**: 创建支付后必须依靠可信 Server POST 确认资金事实;客户端跳转、旧回调和本地推测都不能证明付款结果。
|
|
|
+
|
|
|
+**Independent Test**: 对同一 `MerchantTradeNo` 分别提交合法成功、模拟成功、合法失败、重复、失败后成功、成功后失败、金额不符、商户不符和验签失败的 form-urlencoded 请求,确认支付尝试状态、订单 `payStatus`、`ipn_log` 流水和纯文本响应符合契约。
|
|
|
+
|
|
|
+**Acceptance Scenarios**:
|
|
|
+
|
|
|
+1. **Given** 回调字段完整且签名、`MerchantID`、`MerchantTradeNo`、`TradeAmt` 均匹配创建快照,**When** `RtnCode=1`,**Then** 支付尝试标记 `PAID`,订单只把 `payStatus` 改为 `1`,并返回精确的 `1|OK`。
|
|
|
+2. **Given** 合法成功回调的 `SimulatePaid=1`,**When** 当前系统仍处于测试阶段,**Then** 仍按已付款处理,同时在尝试记录和日志中保留模拟付款标记。
|
|
|
+3. **Given** 回调验签通过但 `RtnCode!=1`,**When** 处理最终失败结果,**Then** 尝试标记 `FAILED` 并保存原始 `RtnCode/RtnMsg`,订单保持未付款,活动尝试被释放,返回 `1|OK`。
|
|
|
+4. **Given** 同一尝试先失败后成功,**When** 后续成功通知到达,**Then** 状态从 `FAILED` 升级为 `PAID`;已 `PAID` 的尝试不得被后续失败通知降级。
|
|
|
+5. **Given** 订单已取消但收到合法成功通知,**When** 处理真实资金事实,**Then** 尝试仍标记 `PAID`、订单仍更新为已付款,并记录严重异常日志;本阶段不自动退款。
|
|
|
+6. **Given** 同一成功或失败通知重复到达,**When** 系统已处理相同事实,**Then** 不重复修改订单或推进业务状态,并返回 `1|OK`。
|
|
|
+7. **Given** 回调无法验证或持久化,**When** 交易号不存在、商户/金额不符、验签失败、字段非法或业务事务失败,**Then** 不修改订单或支付尝试,并返回 `0|ERROR` 以允许 OMG 重试。
|
|
|
+8. **Given** 任意回调请求到达,**When** 后续验签或业务事务失败,**Then** 本次完整回传内容仍以独立事务新增到 `ipn_log`;日志表故障不得阻断真实支付处理。
|
|
|
+
|
|
|
### Edge Cases
|
|
|
|
|
|
- 业务订单号含有不适合 OMG `MerchantTradeNo` 的字符时,系统使用独立生成的英数字编号,不直接拼接或截断业务订单号。
|
|
|
@@ -110,7 +135,9 @@
|
|
|
- 订单或凭证在并发过程中发生变化时,最终写入必须仍满足订单合法状态、凭证归属门店和单活跃尝试约束。
|
|
|
- `TradeDesc`、`ItemName` 不接受客户端文本,必须由服务端生成,无 HTML 标签或未经允许的特殊符号,并满足官方长度限制。
|
|
|
- `ReturnURL` 必须是服务端受控的 HTTPS URL,路径固定指向新的 `/pay/omg/notify`;客户端不得覆盖。
|
|
|
-- 第一阶段没有 `/pay/omg/notify` 处理器是预期行为;测试付款通知不会被旧回调接收或改变订单状态。
|
|
|
+- 表单中出现重复参数名、无法解码的 percent encoding 或超出受控大小时,按非法请求处理,避免参数污染或资源滥用。
|
|
|
+- OMG 新增未列明的回传字段时,只要请求合法,字段也必须被 DTO 边界完整捕获并参加验签;不能依赖固定字段白名单计算检查码。
|
|
|
+- 同一订单存在另一笔活动尝试时,一笔成功将关闭其他活动尝试;以后若另一历史交易也收到合法成功通知,仍记录其支付事实并输出严重异常日志,不吞掉第二笔资金事实。
|
|
|
|
|
|
## Requirements
|
|
|
|
|
|
@@ -131,22 +158,32 @@
|
|
|
- **FR-013**: 创建表单 MUST NOT 发送 `PlatformID`、`PaymentInfoURL`、`OrderResultURL`、`ClientRedirectURL`、`ClientBackURL`、`Language`、分期、定期定额、记忆卡号或银联专用参数。
|
|
|
- **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` 结尾;第一阶段只把它作为 OMG 必填值,不实现该路径的处理器。
|
|
|
+- **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-019**: 后续回调阶段 MUST 遵守同一完整字段原则:除 `CheckMacValue` 外,OMG 实际返回的全部字段均参加验签;启用 `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 查询链接。
|
|
|
-- **FR-022**: 系统 MUST NOT 在数据库保存 `HashKey`、`HashIV`、完整 `CheckMacValue` 或整份签名表单;支付尝试只保存本地可确认事实和创建快照。
|
|
|
-- **FR-023**: 系统 MUST NOT 在任何响应或日志中输出登录 token、`HashKey`、`HashIV`。完整 `CheckMacValue` 和签名表单只允许出现在通过归属及业务校验的创建成功响应中,不得出现在错误响应或日志中。日志可记录本地支付尝试 ID、脱敏后的 `MerchantTradeNo`、业务订单号和阶段结果。
|
|
|
+- **FR-022**: 支付尝试 MUST 保存创建时实际使用的 `HashKey / HashIV` 快照,确保门店凭证被覆盖或停用后仍可验证在途交易;不得保存完整创建表单。完整回调内容保存到现有 `ipn_log`,不新增该表字段。
|
|
|
+- **FR-023**: 创建接口和创建日志 MUST NOT 输出登录 token、`HashKey`、`HashIV`。回调入口按已批准的排障策略 MUST 在应用日志和 `ipn_log.ipn_log` 直接记录完整回传内容,包括完整 `MerchantTradeNo`、`TradeNo`、额外参数、空值和 `CheckMacValue`;任何日志均不得输出数据库中的 `HashKey / HashIV`。
|
|
|
- **FR-024**: 业务校验错误 MUST 使用项目国际化机制,不得硬编码单一语言错误;新增错误 key 必须同步 `vi/zh/tw/en` 支持来源。
|
|
|
- **FR-025**: 旧 `OmgPayController` MUST 取消 Spring Controller 身份且所有旧接口不可访问;不得保留 legacy 路径。
|
|
|
- **FR-026**: 旧 OMG 定时补单任务、订单取消链路中的旧 OMG 退款调用、旧管理端 OMG 补单/退款接口和其他对旧 Controller 的运行时调用 MUST 一并停用或移除;不得影响非 OMG 订单取消及其他支付通道。
|
|
|
- **FR-027**: 在开发者删除旧 OMG 支付及退款表后,任何可达运行链路 MUST NOT 再查询或写入这些旧表;旧表对应的 Mapper/Service 即使暂时保留源码,也不得被新流程或当前有效业务入口调用。`pos_store_omg` 凭证表不属于该禁用范围。
|
|
|
- **FR-028**: 所有新建表、索引或约束 SQL MUST 只追加到 `updatesql/sql.md`,实现过程不得执行数据库变更。
|
|
|
-- **FR-029**: 第一阶段 MUST 保持测试环境边界,验收不得把旧回调或旧查询结果当成新流程成功证据。
|
|
|
+- **FR-029**: 创建与回调 MUST 保持测试环境边界,验收不得把旧回调或旧查询结果当成新流程成功证据。
|
|
|
- **FR-030**: 新 `omgpay` 代码 MUST 包含标准且必要的注释:公开类型说明职责和安全边界;协议字段、检查码编码、事务与数据库唯一约束等非显然逻辑说明“为什么”;不为显然的赋值、访问器或框架样板添加重复注释。注释不得包含真实凭证、完整签名原文或可用测试秘密。
|
|
|
- **FR-031**: 新创建流程 MUST 使用项目日志框架输出足够的结构化排障上下文。创建开始记录业务订单号和请求用户标识;校验通过后记录门店 ID;落库成功记录支付尝试 ID、业务订单号、门店 ID、金额、状态和脱敏 `MerchantTradeNo`;业务拒绝记录稳定业务错误码及已有的安全上下文;非预期异常记录相同安全上下文并保留服务端异常堆栈。不得以拼接整份 DTO、凭证对象或表单对象的方式记录日志。
|
|
|
+- **FR-032**: `POST /pay/omg/notify` MUST 是无需登录 token 的第三方 form-urlencoded 接口,Controller MUST 使用明确 DTO 作为唯一业务入参,不得使用 `Map` 或 `HttpServletRequest` 作为 Controller 入参。DTO 边界 MUST 保留全部实际参数、空值、原始可重放表单内容和请求 IP。
|
|
|
+- **FR-033**: 每次回调 MUST 先以独立事务向现有 `ipn_log` 新增一行:`type="omg"`、`ip` 为请求来源、`cretim` 为接收时间、`ipn_log` 为完整可重放 form-urlencoded 内容。该写入失败只记录应用错误日志,不得阻断后续验签与付款处理。
|
|
|
+- **FR-034**: 回调 MUST 先按 `MerchantTradeNo` 读取并锁定新支付尝试,再使用该尝试的 `HashKey / HashIV` 快照验签;不得只凭请求 `MerchantID` 选择密钥,也不得使用门店当前可能已变更的凭证。
|
|
|
+- **FR-035**: 验签成功后 MUST 同时校验请求 `MerchantID`、`MerchantTradeNo`、`TradeAmt` 与尝试快照完全一致。交易号不存在、字段缺失/格式非法、商户或金额不符、验签失败及业务事务异常 MUST NOT 修改订单或尝试,并返回纯文本 `0|ERROR`。
|
|
|
+- **FR-036**: 当 `RtnCode=1` 时,无论 `SimulatePaid` 为 `0` 或 `1`,系统 MUST 把尝试标记 `PAID` 并保存网关交易事实;订单仅把 `pay_status` 从未付款改为已付款,不得修改 `state`、`delivery_status` 或触发接单、出餐、完成、推送、退款。
|
|
|
+- **FR-037**: 当验签通过且 `RtnCode!=1` 时,系统 MUST 把非 `PAID` 尝试标记 `FAILED`,原样保存 `RtnCode/RtnMsg` 并释放活动尝试;订单保持未付款,下一次创建支付生成新的 `MerchantTradeNo`。
|
|
|
+- **FR-038**: 回调状态 MUST 成功优先且不可逆:`CREATED/FAILED -> PAID`,`CREATED -> FAILED`,`PAID` 不得降级。重复通知不得重复改变订单;正确处理或已处理的通知均返回精确 `1|OK`。
|
|
|
+- **FR-039**: 合法成功通知到达时,即使订单已取消也 MUST 记录付款事实并把订单 `pay_status` 更新为 `1`;系统 MUST 输出异常日志,但本阶段不得自动退款。
|
|
|
+- **FR-040**: 一笔尝试成功后 MUST 关闭同订单其他仍活动的尝试。若历史尝试后来也收到合法成功通知,仍 MUST 记录第二笔付款事实并输出严重异常日志,不得因本地单活跃约束丢弃真实资金通知。
|
|
|
+- **FR-041**: 支付尝试 MUST 保存 `trade_no`、`rtn_code`、`rtn_msg`、`payment_type`、`payment_date`、`trade_date`、`payment_type_charge_fee`、`simulate_paid`、`last_notify_time` 和最终回调原文;`trade_no` 非空时全局唯一,防止同一 OMG 交易被绑定到多个本地尝试。
|
|
|
|
|
|
### API Contract
|
|
|
|
|
|
@@ -201,7 +238,7 @@ token: <login-token>
|
|
|
|
|
|
#### `OmgPaymentAttempt` / `pos_order_omg_attempt`
|
|
|
|
|
|
-表示本次重建产生的一次不可覆盖的 OMG 支付尝试。第一阶段字段如下:
|
|
|
+表示本次重建产生的一次不可覆盖的 OMG 支付尝试。创建和最终付款回调字段如下:
|
|
|
|
|
|
| Field | Meaning | Constraint |
|
|
|
|---|---|---|
|
|
|
@@ -211,8 +248,17 @@ token: <login-token>
|
|
|
| `store_id` | 创建时订单门店 | 非空 |
|
|
|
| `merchant_id` | 创建时门店 MerchantID 快照 | 非空、≤10 位 |
|
|
|
| `amount` | 创建时订单整数 TWD 金额快照 | 非空、>0 |
|
|
|
-| `attempt_status` | 本地事实状态 | `0=CREATED` |
|
|
|
+| `hash_key_snapshot` | 创建时 HashKey 快照 | 非空,不得输出到日志或 API |
|
|
|
+| `hash_iv_snapshot` | 创建时 HashIV 快照 | 非空,不得输出到日志或 API |
|
|
|
+| `attempt_status` | 本地事实状态 | `0=CREATED, 1=PAID, 2=FAILED, 3=SUPERSEDED` |
|
|
|
| `active_dd_id` | 单活跃约束生成列 | `attempt_status=0` 时为 `dd_id`,否则为 `NULL` |
|
|
|
+| `trade_no` | OMG 金流交易编号 | 可空;非空时全局唯一 |
|
|
|
+| `rtn_code` / `rtn_msg` | OMG 原始结果码及说明 | 可空,不维护固定错误码枚举 |
|
|
|
+| `payment_type` | OMG 回覆付款方式 | 可空 |
|
|
|
+| `payment_date` / `trade_date` | OMG 付款及建单时间 | 可空,Asia/Taipei 语义 |
|
|
|
+| `payment_type_charge_fee` | OMG 回传手续费 | 可空 |
|
|
|
+| `simulate_paid` | 模拟付款标记 | 可空;`1` 仍按已付款处理 |
|
|
|
+| `last_notify_time` | 最近一次合法通知处理时间 | 可空 |
|
|
|
| `create_time` | 本地创建时间 | 非空 |
|
|
|
| `update_time` | 本地更新时间 | 非空 |
|
|
|
|
|
|
@@ -220,12 +266,17 @@ token: <login-token>
|
|
|
|
|
|
- `UNIQUE (merchant_trade_no)`;
|
|
|
- `UNIQUE (active_dd_id)`,利用 MySQL 唯一索引允许多个 `NULL` 的语义,为后续终态释放活跃键;
|
|
|
-- `CREATED` 只表示本地已生成并持久化表单所需事实,不能解释为 OMG 已接收、已建立订单或未付款;
|
|
|
-- 后续回调/查询规格负责定义可信的后续状态;本阶段不得预先猜测完整状态机。
|
|
|
+- `CREATED` 只表示本地已生成并持久化表单所需事实,不能解释为 OMG 已接收或已建立订单;
|
|
|
+- `PAID` 是不可逆资金事实;`FAILED` 可被后续合法成功通知升级为 `PAID`;`SUPERSEDED` 表示同订单已有其他尝试成功,并非 OMG 返回的支付失败;
|
|
|
+- 回调保存归一化最终事实,逐次完整请求历史由 `ipn_log` 承载。
|
|
|
|
|
|
#### Existing `PosStoreOmg` / `pos_store_omg`
|
|
|
|
|
|
-可信门店凭证来源。本阶段不修改其表结构、录入流程或启停流程。新创建服务只读取与订单 `storeId` 对应、已启用的 `MerchantID / HashKey / HashIV`。
|
|
|
+可信门店凭证来源。不修改其表结构、录入流程或启停流程。创建服务读取与订单 `storeId` 对应、已启用的 `MerchantID / HashKey / HashIV`,并把密钥写入本次尝试快照;回调不再依赖门店当前行。
|
|
|
+
|
|
|
+#### Existing `IpnLog` / `ipn_log`
|
|
|
+
|
|
|
+每个回调 HTTP 请求新增一行,不新增字段:`ip` 保存来源地址,`cretim` 保存接收时间,`type` 固定为 `omg`,`ipn_log` 保存完整原始 form-urlencoded 请求体。该流水使用独立事务,后续业务回滚不删除已经接收的通知记录。
|
|
|
|
|
|
## 3. Error Handling and Security
|
|
|
|
|
|
@@ -247,7 +298,9 @@ token: <login-token>
|
|
|
- 使用 OMG 官方 AioCheckOut 示例参数和官方期望 `CheckMacValue` 验证完整 SHA-256 签名链路;不得用旧实现测试或其他金流向量代替官方依据。
|
|
|
- 验证字段排序、HashKey/HashIV 包夹、.NET URL 编码替换、转小写、SHA-256 和大写十六进制各步骤。
|
|
|
- 验证实际发送字段集合与签名输入集合完全一致;删除、增加或修改任一非 `CheckMacValue` 字段都会改变签名。
|
|
|
-- 验证空值字段在未来回调验签集合中不会被静默删除;本阶段至少通过签名器单元测试锁定“保留空值”的通用能力。
|
|
|
+- 验证回调所有实际字段、未知额外字段和空值字段均进入签名集合,只有 `CheckMacValue` 被排除。
|
|
|
+- 验证 `ipn_log` 在验签失败和业务事务回滚时仍独立保留,且日志写入失败不阻断付款处理。
|
|
|
+- 验证成功、模拟成功、失败、失败后成功、成功后失败、重复通知、取消后成功和同订单迟到第二笔成功的状态机。
|
|
|
- 验证 `MerchantTradeNo` 仅含英数字、长度不超过 20、重复冲突不会覆盖旧行。
|
|
|
- 验证台北时区与 `yyyy/MM/dd HH:mm:ss` 格式。
|
|
|
- 验证固定字段和值、禁止字段不出现在表单、金额只取订单、文字字段安全和长度限制。
|
|
|
@@ -275,7 +328,7 @@ token: <login-token>
|
|
|
- 在当前页面将全部 `formFields` POST 到返回的测试 `gatewayUrl`。
|
|
|
- 确认进入 OMG 测试收银台,金额、商品说明和 `ALL` 可用渠道显示正确。
|
|
|
- 确认不使用 iframe 或新窗口。
|
|
|
-- 本阶段不以付款、回调、查询、取号或退款结果作为完成标准。
|
|
|
+- 创建和付款结果回调按本规格完成;查询、补单、取号、退款与推送不作为本阶段完成标准。
|
|
|
|
|
|
## Success Criteria
|
|
|
|