# Feature Specification: OMG 支付接入(订单在线支付) **Feature Branch**: `(待创建,暂未建分支/未提交)` **Created**: 2026-07-29 **Status**: Draft —— 关键决策已定(D1–D6,见「已定决策」) **Input**: 接入台湾 OMG(歐買尬 / 金流引擎 FunPoint)AIO 幕前支付(`https://developers.omg.com.tw/payment/aio/api/01_order.html`)用于订单在线支付。替代已就绪未启用的蓝新 NewebPay(011,不再启用);OMG 独立实现、不复用蓝新代码,仅复用平台共享订单/推送链路。 --- ## 集成背景(已确认,来自 OMG AIO 文档) - **网关端点**: - 测试:`https://payment-stage.funpoint.com.tw/Cashier/AioCheckOut/V5` - 生产:`https://payment.funpoint.com.tw/Cashier/AioCheckOut/V5` - `POST`,`application/x-www-form-urlencoded` - **签名模型(关键差异)**:OMG 用 **`CheckMacValue`(SHA256,`EncryptType=1`)** 单字段签名;这与蓝新 NewebPay 的 `TradeInfo`(AES) + `TradeSha`(SHA256) 双层模型**不同**。结论:**需独立的 OMG 签名工具,不能复用 `NewebPayEncryptUtil`**。具体算法(参数排序、URI 编码、HashKey/HashIV 包夹、SHA256 大写)在文档附錄 `07_appendices` / 范例 `06_check-value-sample`,留待 plan/research 落地。 - **支持的支付方式**(`ChoosePayment`):`Credit`(信用卡)、`ApplePay`、`ATM`、`CVS`(超商代码)、`BarcodeATM`、`AFTEE`、`ALL`。 - ⚠️ 文档**未把「LINE Pay」列为独立 ChoosePayment** —— 用户提及 LINE Pay,需确认其接入方式(可能走 Credit 子支付或独立通道),见 D4。 - **幕前流程**:商户组参 + 计算 `CheckMacValue` → POST 到 `AioCheckOut/V5` → 消费者在 OMG 收银台支付 → OMG **服务端 POST 回 `ReturnURL`**(商户须回 `1|OK`)→ 可选 `OrderResultURL` 客户端跳转(仅即时支付方式支持)。 - **非即时支付(ATM/超商/BarcodeATM)**:OMG 生成虚帐/缴费码 → 回调 `PaymentInfoURL`(服务端 POST)+ 可选 `ClientRedirectURL`(客户端展示)。 - **退款**:`05_refund` API;**订单查询**:`04_order_query`;**付款结果通知**:`03_payment_notify`。 - **发票**:`InvoiceMark` 必须 `N` —— 电子发票仍走本项目 ezPay 链路(010/014),与支付解耦。 - **约束**:不可用 iframe;iOS 下不可另开新窗口;`MerchantTradeNo` 全平台唯一;`ReturnURL` 与 `OrderResultURL` 不可相同。 ## 与本项目现有支付的关系 - 本功能把 **OMG 启用为订单在线支付**。蓝新 NewebPay(011)代码就绪**未启用**;VNPay/ZaloPay 已废弃(见 `project-deprecated-code` 记忆)。 - **复用现有链路**:订单状态流转入口(`PosOrderShOprate`/`PosOrderQsOprate`/`UserOrderController`/`PosOrderController`)、`PayPush`(iOS uni 云函数 msduser/msdrider/msdstore + `PushEvent`→`push_message`)、以及 `sendAcceptRiderPush`(迁移契机,见 CLAUDE.md「当前在用的订单操作入口」)。 - **凭证门店级**:每门店独立 OMG 凭证(MerchantID/HashKey/HashIV),存于门店维度配置表 `pos_store_omg`。 - **不复用蓝新代码(用户硬约束)**:OMG 全部代码(工具类/客户端/实体/Service/Controller)与表(凭证/流水/退款)独立新建、零 `newebpay` 依赖,不复用蓝新的 `pos_store_newebpay` / `pos_order_payment`;仅复用平台共享基础设施(订单状态机、推送、订单日志)。 --- ## 已定决策(D1–D6,2026-07-29 确认) - **D1 — 支付方式范围**:支持 OMG 全部支付方式(`ChoosePayment=ALL`:信用卡 Credit / Apple Pay / ATM / 超商 CVS / BarcodeATM / AFTEE)。 - **D2 — 与 NewebPay(011) 的关系**:**OMG 替代蓝新金流**,作为订单在线支付启用;NewebPay 不再启用。 - **D3 — 退款/取消**:**本期范围**,订单取消走 OMG 退款 API(`05_refund`)。 - **D4 — LINE Pay**:**本期不做**(文档未列为独立 ChoosePayment,后续再议)。 - **D5 — 覆盖订单范围**:**本期仅餐饮订单**;旅游·机票订单(015)后续衔接,不在本期。 - **D6 — 凭证粒度**:**门店级**(每门店独立 MerchantID / HashKey / HashIV,启用开关,同 011 模式)。 > 说明:OMG 文档可正常获取(与 015 的 iFlight 不同),无硬阻塞。决策已定,可直接进 `/speckit-plan`。 --- ## User Scenarios & Testing *(mandatory)* ### User Story 1 - 消费者在线支付订单(Priority: P1) 消费者下单后选择在线支付,选定方式(如信用卡/Apple Pay),跳转 OMG 收银台完成付款;付款成功后订单变为已支付并进入履约。 **Why this priority**:在线支付是订单交易闭环的核心,没有它订单无法在线收款。 **Independent Test**:从一笔待支付订单出发,能跳转 OMG、用测试卡完成支付,订单状态变为已支付。 **Acceptance Scenarios**: 1. **Given** 一笔待支付订单,**When** 消费者发起支付并选择支付方式,**Then** 跳转 OMG 收银台且金额/订单号正确。 2. **Given** 消费者在 OMG 完成付款,**When** OMG 回调平台,**Then** 订单核销为已支付,消费者看到支付成功。 3. **Given** 消费者取消或关闭支付页未付款,**When** 超时,**Then** 订单保持未支付并可重试,不误判为已付。 --- ### User Story 2 - 支付回调核销与履约触发(Priority: P2) OMG 服务端回调(`ReturnURL`)到达后,验签 + 幂等更新订单,并触发后续履约(状态流转、商家/骑手推送)。 **Why this priority**:支付成功必须可靠地驱动订单履约,否则「付了钱却没人接单」。 **Independent Test**:模拟 OMG 回调(合法/伪造/重复),合法回调能更新订单并触发推送,伪造被拒、重复不重复发货。 **Acceptance Scenarios**: 1. **Given** OMG 合法回调到达,**When** 平台验签通过,**Then** 订单更新为已支付并回 `1|OK`,履约链路(含 `sendAcceptRiderPush` 推送)被触发。 2. **Given** 同一订单的重复回调,**When** 再次到达,**Then** 幂等处理,不重复更新/不重复推送。 3. **Given** 伪造或验签失败的回调,**When** 到达,**Then** 拒绝并记录,订单状态不变。 --- ### User Story 3 - 非即时支付(ATM / 超商)(Priority: P3) 消费者选择 ATM 转账或超商缴费时,平台展示 OMG 生成的虚帐/缴费码,消费者在期限内完成后由 OMG 回调核销。 **Why this priority**:扩大支付覆盖面,但非即时到账,可晚于信用卡 MVP。 **Independent Test**:选择 ATM/超商,能拿到虚帐/缴费码并展示;模拟付款完成回调能核销订单。 **Acceptance Scenarios**: 1. **Given** 消费者选 ATM/超商,**When** OMG 生成虚帐/缴费码回调 `PaymentInfoURL`,**Then** 平台展示付款信息与期限。 2. **Given** 消费者在期限内完成付款,**When** OMG 回调 `ReturnURL`,**Then** 订单核销为已支付并触发履约。 3. **Given** 超过付款期限未付,**When** 逾期,**Then** 订单按规则失效/可重试。 --- ### User Story 4 - 订单取消与退款(Priority: P3) 订单取消(用户取消/商家取消/超时退款)时,通过 OMG 退款 API(`05_refund`)原路退款,并同步状态。 **Why this priority**:售后资金闭环,依赖 D3 是否本期。 **Independent Test**:对一笔已支付订单发起取消,能调用 OMG 退款并同步为已退款。 **Acceptance Scenarios**: 1. **Given** 一笔已支付订单可退款,**When** 发起取消,**Then** 调 OMG 退款 API 并将订单标记为退款中/已退款。 2. **Given** 退款失败,**When** OMG 返回失败,**Then** 记录原因并可重试,不误标已退款。 --- ### User Story 5 - 商家/平台配置 OMG 凭证(Priority: P2) 商家或平台在后台维护 OMG 凭证(MerchantID/HashKey/HashIV),支持测试/生产切换与启用开关。 **Why this priority**:支付前提是凭证可用,属 P2 基础设施。 **Independent Test**:在后台录入门店 OMG 凭证,能用于发起支付与验签。 **Acceptance Scenarios**: 1. **Given** 已授权运营,**When** 录入门店 OMG 凭证并启用,**Then** 该门店订单可发起 OMG 支付。 2. **Given** 未配置或未启用凭证的门店,**When** 尝试在线支付,**Then** 不展示/不可用在线支付(可降级货到付款)。 --- ### Edge Cases - 回调签名验证失败(伪造/篡改)—— 必须拒绝并记录。 - 重复回调(网络重试)—— 按 `MerchantTradeNo` 幂等,不重复发货/推送。 - 消费者支付页关闭/超时未付 —— 订单不误判,可重试或按规则失效。 - 金额不一致(下单价 vs 支付价,如改了单/用了券)—— 以平台实际应收金额为准。 - 回调与「用户取消」并发 —— 明确最终状态优先级,避免已付款却被取消。 - 退款失败 / 部分退款 / 跨日退款。 - 多门店凭证:A 门店凭证不可用于 B 门店订单。 - 测试凭证误用到生产、或反之。 ## Requirements *(mandatory)* ### Functional Requirements - **FR-001**:系统 MUST 支持消费者对未支付订单发起 OMG 幕前在线支付,跳转 OMG 收银台(金额、订单号、商品描述正确)。 - **FR-002**:系统 MUST 支持 OMG 全部支付方式(`ChoosePayment=ALL`:信用卡 / Apple Pay / ATM / 超商 / BarcodeATM / AFTEE);LINE Pay 本期不做(D4)。 - **FR-003**:系统 MUST 使用 OMG `CheckMacValue`(SHA256,`EncryptType=1`)对请求签名、对回调验签;签名工具**独立于**现有 NewebPay 工具。 - **FR-004**:系统 MUST 在 OMG `ReturnURL` 回调时验签 + 按 `MerchantTradeNo` 幂等更新订单为已支付,并回 `1|OK`。 - **FR-005**:系统 MUST 在支付成功后触发现有订单履约链路(状态流转 + iOS 推送 + `sendAcceptRiderPush`)。 - **FR-006**:系统 MUST 支持非即时支付(ATM/超商)的虚帐/缴费码生成、展示与期限管理(`PaymentInfoURL` 回调)。 - **FR-007**:系统 MUST 支持订单取消时经 OMG 退款 API(`05_refund`)退款并同步状态(本期范围,D3)。 - **FR-008**:系统 MUST 支持门店级 OMG 凭证配置(MerchantID/HashKey/HashIV,启用开关,测试/生产切换)。 - **FR-009**:本期功能覆盖范围**仅餐饮订单**的在线支付;旅游·机票订单(015)后续衔接,不在本期(D5)。 - **FR-010**:系统 MUST 持久化支付流水(`MerchantTradeNo`、金额、方式、时间、回调原始报文、退款记录),支持对账。 - **FR-011**:系统 MUST 在 `InvoiceMark=N` 下与 ezPay 电子发票链路解耦,支付不干涉开票。 - **FR-012**:OMG **替代蓝新 NewebPay(011)** 作为订单在线支付启用,NewebPay 不再启用(D2)。 - **FR-013**:系统 MUST NOT 复用或依赖任何蓝新 NewebPay 代码/表;OMG 凭证、流水、退款表与工具类/Controller 全部独立新建,仅可使用平台共享基础设施(订单状态机、推送、订单日志)。 ### Key Entities *(include if feature involves data)* - **门店 OMG 凭证(StoreOmgCredential)**:门店关联、MerchantID、HashKey、HashIV、环境(测试/生产)、启用开关。 - **支付请求(OmgPaymentRequest)**:`MerchantTradeNo`(幂等键)、订单、金额、`ChoosePayment`、ReturnURL/NotifyURL、生成的 `CheckMacValue`。 - **支付流水(OrderPayment)**:`MerchantTradeNo`、关联订单、金额、支付方式、状态(待付/已付/失败/退款)、OMG 交易号、回调原始报文、时间。 - **退款记录(OmgRefund)**:关联支付流水、退款金额、退款状态、OMG 回应、时间。 ## Success Criteria *(mandatory)* ### Measurable Outcomes - **SC-001**:消费者从下单到完成在线支付(即时方式)端到端时长满足体验预期(如主流在数分钟内)。 - **SC-002**:支付回调核销成功率达标;重复回调 **100% 幂等**,不重复发货/推送。 - **SC-003**:支付成功后订单履约(状态流转 + 推送)**100% 被触发**,无「已付款未接单」。 - **SC-004**:伪造/验签失败的回调 **100% 被拒绝**,资金安全零失误。 - **SC-005**:退款(若本期)在目标时长内完成且订单状态与资金同步。 ## Assumptions - 平台已/将向 OMG 申请商店号与 HashKey/HashIV(测试 + 生产)。 - 复用现有订单状态机、`PayPush` 推送链路、用户体系,不为此功能重建。 - OMG AIO `CheckMacValue` 算法与 ECPay 同源(业界标准),具体实现留待 plan/research。 - 电子发票仍走 ezPay(010/014),与 OMG 支付解耦(`InvoiceMark=N`)。 - 测试先用 `payment-stage.funpoint.com.tw` 测试端点与测试凭证,生产环境与凭证后续切换。 - 凭证签名等敏感操作在服务端完成,HashKey/HashIV 不下发前端。 - 在线支付与现有「货到付款」并存,由门店配置决定是否提供在线支付。