tasks.md 15 KB


description: "Task list for 蓝新金流(NewebPay)线上支付接入"

Tasks: 蓝新金流(NewebPay)线上支付接入

Input: Design documents from /specs/011-newebpay-payment/

Prerequisites: plan.md, spec.md, research.md, data-model.md, contracts/, quickstart.md(均已就绪)

Tests: 未要求 TDD;验证以 NewebPayEncryptUtil.main 自测 + quickstart.md 端到端场景为准。

Organization: 按 user story 组织,每个 story 可独立实现与验证。MVP = US1 + US2(支付闭环)。

Format: [ID] [P?] [Story] Description

  • [P]: 可并行(不同文件、无依赖)
  • [Story]: 归属 user story(US1~US5)
  • 描述含精确文件路径;前端 CRLF 文件用 Python 脚本编辑

Phase 1: Setup(基础设施)

Purpose: 数据库 DDL 与配置就位

  • T001 追加两表建表 DDL 到 updatesql/sql.md(pos_store_newebpay、pos_order_payment,取自 data-model.md,标注 2026-06-22 蓝新支付接入)
  • T002 [P] 在 ruoyi-admin/src/main/resources/application.yml 新增 newebpay 配置段(base-url=ccore.newebpay.com、notify-url、return-url、mpg-version=2.3),仿 ezpay 段

Phase 2: Foundational(阻塞前置,所有 story 依赖)

Purpose: 加密工具、HTTP 客户端、实体、Mapper——所有 story 的公共地基

⚠️ CRITICAL: 本阶段完成前不得开始任何 user story

  • T003 [P] 创建 ruoyi-admin/src/main/java/com/ruoyi/app/utils/newebpay/NewebPayConfig.java(merchantId/hashKey/hashIV,仿 EzPayConfig)
  • T004 创建 ruoyi-admin/src/main/java/com/ruoyi/app/utils/newebpay/NewebPayEncryptUtil.java:AES-256-CBC/PKCS7Padding(块16) encrypt/decrypt(hex)、genTradeSha(HashKey=&{enc}&HashIV=)、genCheckValue(IV=&{Amt/MerchantID/MerchantOrderNo 排序}&Key=)、genCheckCode(HashIV=&{Amt/MerchantID/MerchantOrderNo/TradeNo 排序}&HashKey=);含 main 自测用 NDNF §4.1 示例(Key=Fs5cX1TGqYM2PpdbE14a9H83YQSQF5jn/IV=C6AcmfqJILwgnhIP/MID=MS127874575)验证加解密 round-trip 与 TradeSha 规则
  • T005 [P] 创建 ruoyi-system/src/main/java/com/ruoyi/system/domain/PosStoreNewebpay.java@TableName pos_store_newebpay,字段见 data-model.md)
  • T006 [P] 创建 ruoyi-system/src/main/java/com/ruoyi/system/domain/PosOrderPayment.java@TableName pos_order_payment,字段见 data-model.md)
  • T007 [P] 创建 ruoyi-system/src/main/java/com/ruoyi/system/mapper/PosStoreNewebpayMapper.java + ruoyi-system/src/main/resources/mapper/chanting/PosStoreNewebpayMapper.xml(仿 PosStoreEzpayMapper,基础 CRUD)
  • T008 [P] 创建 ruoyi-system/src/main/java/com/ruoyi/system/mapper/PosOrderPaymentMapper.java + ruoyi-system/src/main/resources/mapper/chanting/PosOrderPaymentMapper.xml(insert、selectByTradeNo、selectByDdId、updateById)
  • T009 [P] 创建 ruoyi-admin/src/main/java/com/ruoyi/app/utils/newebpay/NewebPay.java HTTP 客户端:端点常量(URL_MPG_GATEWAY、URL_QUERY_TRADE_INFO、BASE_TEST=ccore/BASE_PROD=core)、createMpgForm(baseUrl,cfg,params)→加密form字段Map、queryTrade(baseUrl,cfg,amt,orderNo)→JSONObject、buildQuery(http_build_query)、postForm

Checkpoint: 加密自测通过、表可建、实体/Mapper 就绪 → 可开始 user story


Phase 3: User Story 1 - 发起蓝新支付(信用卡) (Priority: P1) 🎯 MVP part 1

Goal: C端用户下单后调发起接口拿到加密form参数,前端Form Post跳转蓝新付款页

Independent Test: 对已开通蓝新的门店下单 → 调 /pay/newebpay/create → 返回 gatewayUrl+TradeInfo+TradeSha → 前端跳转蓝新页出现信用卡选项;pos_order_payment 落一行(pay_status=0)、pos_order.pay_type=6

  • T010 [P] [US1] 创建 ruoyi-system/src/main/java/com/ruoyi/system/domain/dto/StoreNewebpayCredentialDto.java(storeId/merchantId/hashKey/hashIv/enabledPayments)与 ruoyi-system/src/main/java/com/ruoyi/system/domain/vo/PosStoreNewebpayVo.java(列表/详情VO)
  • T011 [US1] 实现 ruoyi-system/.../service/IPosStoreNewebpayService.java + impl/PosStoreNewebpayServiceImpl.java(纯DB:getOrCreateByStoreId、getEnabledConfig(storeId)返回启用且已开通的凭证或null、基础保存),仿 PosStoreEzpayServiceImpl
  • T012 [US1] 实现 ruoyi-system/.../service/IPosOrderPaymentService.java + impl/PosOrderPaymentServiceImpl.java(createPayment(ddId,merchantOrderNo,storeId,merchantId,amount)发起流水、getByTradeNo、getByMerchantOrderNo、markSuccess(tradeNo,payType,authCode,payTime,callbackRaw)、markFail)
  • T013 [US1] 在 ruoyi-system/.../domain/PosOrder.java 附近定义 payType 蓝新取值常量(PAY_TYPE_NEWEBPAY="6"),或新建枚举类;不改表结构
  • T014 [US1] 创建 ruoyi-admin/src/main/java/com/ruoyi/app/pay/NewebpayPayController.java 实现 POST /pay/newebpay/create@Anonymous+@Auth):校验订单存在/未支付/属当前用户 → 查门店启用凭证(无则报错) → 生成 merchant_order_no="NB"+ddId → 组装TradeInfo明文(MerchantID/RespondType=JSON/TimeStamp/Version=2.3/MerchantOrderNo/Amt=amount/ItemDesc/NotifyURL/ReturnURL/CREDIT=1) → NewebPayEncryptUtil 加密+TradeSha → 落 pos_order_payment → 更新 pos_order.payType=6/payUrl=gatewayUrl → 返回 form 字段(见 contracts/api.md B1)
  • T015 [US1] 前端 E:\QtwCode\foodie\foodie-store\ 新增"蓝新支付"入口:用户下单后调 /pay/newebpay/create,用返回字段构建隐藏 form 自动 submit 到 gatewayUrl;支付方式相关文案用 $t() 四语 i18n(zh/tw/en/vi,key 加到对应对象层级,如支付/订单对象内)

Checkpoint: 发起支付链路通,可跳转蓝新测试页(此时回调未接,订单状态待 US2 更新)


Phase 4: User Story 2 - 支付结果回调处理 (Priority: P1) 🎯 MVP part 2

Goal: 蓝新NotifyURL回调到达后,解密验签+幂等+金额校验,更新订单payStatus=1并触发推送;ReturnURL仅引导

Independent Test: US1发起并跳转后用测试卡付款 → 蓝新回调 /pay/newebpay/notify → 订单payStatus变1、pos_order_payment写trade_no/pay_type=CREDIT、商家/骑手收到推送;重复回调不变;金额不符/伪造签名拒绝

  • T016 [US2] 在 NewebpayPayController.java 实现 POST /pay/newebpay/notify@Anonymous):收Form参数 → 记IpnLog → 由MerchantID查门店凭证 → 无凭证记录返回
  • T017 [US2] notify 验签解密:genTradeSha(TradeInfo,key,iv)==TradeSha 校验,不符拒绝记录;通过则 NewebPayEncryptUtil.decrypt(TradeInfo) 解出 Status/MerchantOrderNo/TradeNo/Amt/PaymentType/PayTime/Auth
  • T018 [US2] notify 幂等+金额校验:按TradeNo查 pos_order_payment(已存在且pay_status=1→直接返回成功应答);Amt==订单amount校验(不符→拒绝更新、记录告警);由MerchantOrderNo反查pos_order(不存在→记录待查返回)
  • T019 [US2] notify 成功业务链路(Status=SUCCESS且校验通过):markSuccess写流水、pos_order.payStatus=1(state不变)、orderLogHelper.logSync记日志、复用 pushEventService.PublisherEvent + sendAcceptRiderPush 推送用户/商家/骑手(参照 PayController.payipn);返回 {"Status":"SUCCESS","Message":"OK"}
  • T020 [US2] notify 失败处理(Status≠SUCCESS):markFail记 pos_order_payment(pay_status=2)、订单保持未支付、记失败原因、返回成功应答
  • T021 [US2] 在 NewebpayPayController.java 实现 GET/POST /pay/newebpay/return@Anonymous):仅302重定向前端结果页(带ddId),不改订单状态(FR-015)
  • T022 [US2] 前端 E:\QtwCode\foodie\foodie-store\ 支付结果页:展示支付状态,轮询订单payStatus(因ReturnURL不改状态,以轮询/回调后状态为准);文案四语 i18n

Checkpoint: US1+US2 构成完整 MVP——下单→付款→订单已支付→推送,可端到端验证(quickstart 场景2、3)


Phase 5: User Story 3 - LINE Pay / Apple Pay (Priority: P2)

Goal: 用户在蓝新页可选 LINE Pay / Apple Pay,回调方式记录正确

Independent Test: 门店enabled_payments含LINEPAY/APPLEPAY → 发起参数带LINEPAY=1/APPLEPAY=1 → 蓝新页出现选项 → 付款后回调pay_type为LINEPAY/APPLEPAY

  • T023 [P] [US3] 扩展 NewebpayPayController.create:按门店 enabled_payments 在TradeInfo明文中设 LINEPAY=1 / APPLEPAY=1(与CREDIT并存),读取门店 pos_store_newebpay.enabled_payments
  • T024 [US3] notify 解析 PaymentType 映射到 pos_order_payment.pay_type(CREDIT/LINEPAY/APPLEPAY,回调字段 PaymentType/PaymentMethod),确保 US2 的 markSuccess 正确写入方式
  • T025 [US3] 前端 E:\QtwCode\foodie\foodie-store\ 支付方式展示:按门店启用方式渲染可选标识(发起前提示),文案四语 i18n

Checkpoint: 三种支付方式均可完成闭环(quickstart 场景4;Apple Pay 需具备 HTTPS+Apple 配置)


Phase 6: User Story 4 - 门店蓝新凭证开通管理 (Priority: P2)

Goal: 运营在平台后台录入/验证/启停门店蓝新凭证,复用009模式

Independent Test: 后台为门店录入测试凭证 → 调QueryTradeInfo验证通过 → 状态已开通 → 该门店可发起支付;停用后不可

  • T026 [US4] 创建 ruoyi-admin/src/main/java/com/ruoyi/app/mendian/PosStoreNewebpayController.java(/system/storeNewebpay,@PreAuthorize chanting:storeNewebpay:*):list/{storeId}/apply/saveCredentials/toggleEnable/reset/enabledPayments,仿 PosStoreEzpayController
  • T027 [US4] saveCredentials 凭证验证:Controller 调 NewebPay.queryTrade(虚构订单)探测,返回金钥/商店错误(MPG01001类/Status≠SUCCESS且Message含金钥)→视为无效拒绝;否则置 newebpay_status=2/is_enabled=1、记last_verify_result
  • T028 [US4] 扩展 PosStoreNewebpayServiceImpl:apply(0→1)、enableWithCredentials、toggleEnable、reset、setEnabledPayments、recordVerifyResult(仿 PosStoreEzpayServiceImpl 状态机)
  • T029 [US4] 平台后台 E:\QtwCode\foodie\foodie-admin-vue\ 新增门店蓝新凭证管理页(列表分页/详情/录入凭证/启停/重置/设置支付方式),文案四语 i18n
  • T030 [US4] 菜单与权限:新增菜单项 + 权限标识 chanting:storeNewebpay:list/query/apply/saveCredentials/toggleEnable,SQL 写入 updatesql/sql.md(sys_menu 插入,仿 storeEzpay 菜单)

Checkpoint: 凭证可全流程管理,US1/US3 发起支付有真实凭证来源(不必手插数据)


Phase 7: User Story 5 - 单笔交易查询/补单 (Priority: P3)

Goal: 回调丢失或对账时,主动查蓝新并校正订单状态

Independent Test: 回调丢失(订单未支付) → 调 /pay/newebpay/query → 蓝新返回TradeStatus=1 → 补更新payStatus=1+推送

  • T031 [US5] 在 NewebpayPayController.java 实现 POST /pay/newebpay/query@Auth):查订单+门店凭证 → genCheckValue → NewebPay.queryTrade 调 /API/QueryTradeInfo → 解析 TradeStatus/PaymentType/PayTime
  • T032 [US5] 查询补单:若蓝新TradeStatus=1且订单payStatus仍0 → 执行与US2相同的状态更新+流水+推送(抽取公共方法供 notify/query 复用,避免重复)
  • T033 [US5] 商家端 E:\QtwCode\foodie\foodie-store\ 订单详情增加"查询支付状态"入口(展示蓝新TradeStatus/支付方式/时间),文案四语 i18n

Checkpoint: 对账与补单可用(quickstart 场景5)


Phase 8: Polish & Cross-Cutting

Purpose: 多 story 共享的收尾

  • T034 [P] 四语 i18n 统一校验:foodie-store 与 foodie-admin-vue 的 zh.js/tw.js/en.js/vi.js 中支付相关 key 四文件齐全、命名有意义、位于正确对象层级(用 i18n-consistency-checker 或人工核)
  • T035 [P] 安全复核:NotifyURL/ReturnURL 确为 @Anonymous 且仅此两接口匿名;HashKey/HashIV 不出现在任何前端响应;回调 TradeSha 验签为强制前置;金额校验不可绕过
  • T036 运行 specs/011-newebpay-payment/quickstart.md 全部 6 场景端到端验证(测试环境 ccore + 测试卡 + 内网穿透),记录结果
  • T037 文档一致性:确认 spec.md/plan.md/tasks.md 描述一致;更新 updatesql/sql.md 中所有 SQL 齐全(两建表 + 菜单权限)

Dependencies & Execution Order

Phase 依赖

  • Phase 1 Setup: 无依赖,立即开始
  • Phase 2 Foundational: 依赖 Phase 1(配置/DDL)—— 阻塞所有 user story
  • Phase 3 US1: 依赖 Phase 2
  • Phase 4 US2: 依赖 US1(回调处理依赖发起产生的流水与订单关联)
  • Phase 5 US3: 依赖 US1+US2(复用主链路,增量在参数开关与方式解析)
  • Phase 6 US4: 依赖 Phase 2(凭证表/Mapper);可与 US1 并行(US1 测试可临时手插凭证数据)
  • Phase 7 US5: 依赖 US1+US2(查询补单复用状态更新逻辑)
  • Phase 8 Polish: 依赖所有欲交付的 story 完成

User Story 独立性

  • US1: 依赖 Foundational,无跨 story 依赖
  • US2: 依赖 US1(闭环下半段)
  • US3: 依赖 US1+US2,但增量小(参数+解析)
  • US4: 依赖 Foundational,可与 US1 并行
  • US5: 依赖 US1+US2

并行机会

  • Phase 2 的 T003/T005/T006/T007/T008/T009 互不冲突,可全部并行
  • US4(Phase 6)可与 US1/US2(Phase 3/4)并行(不同文件、不同端)
  • Phase 8 的 T034/T035 可并行

Parallel Example: Foundational

# 以下任务文件互不冲突,可并行:
Task: "NewebPayConfig in ruoyi-admin/.../utils/newebpay/NewebPayConfig.java"
Task: "PosStoreNewebpay in ruoyi-system/.../domain/PosStoreNewebpay.java"
Task: "PosOrderPayment in ruoyi-system/.../domain/PosOrderPayment.java"
Task: "PosStoreNewebpayMapper + XML"
Task: "PosOrderPaymentMapper + XML"
Task: "NewebPay HTTP 客户端骨架 in ruoyi-admin/.../utils/newebpay/NewebPay.java"
# 仅 NewebPayEncryptUtil(T004) 含被他人依赖的加密契约,建议优先完成并跑通自测

Implementation Strategy

MVP First(US1 + US2)

  1. Phase 1 Setup + Phase 2 Foundational(加密自测必须先过
  2. Phase 3 US1(发起支付)+ Phase 4 US2(回调处理)→ 端到端验证(quickstart 场景2、3)
  3. STOP and VALIDATE: 下单→信用卡付款→订单已支付→推送,全链路通
  4. 此时已是最小可用产品

增量交付

  1. MVP(US1+US2)→ 验证 → 可上测试环境
  2. + US3(LINE Pay/Apple Pay)→ 验证
  3. + US4(凭证管理)→ 去除手插数据、运营自助
  4. + US5(查询补单)→ 异常补救就绪
  5. Polish(i18n/安全/全场景验证)→ 可转正式

并行策略

  • Foundational 完成后:开发者 A 做 US1+US2(主链路),开发者 B 做 US4(凭证管理前端+后端),互不阻塞
  • US3/US5 待主链路稳定后接力

Notes

  • [P] 任务 = 不同文件、无依赖;同文件多改动须串行
  • 前端 CRLF 文件(foodie-store/foodie-admin-vue)用 Python 脚本编辑,避免 Edit 匹配失败
  • 所有数据库变更写 updatesql/sql.md,不直接执行
  • 每个任务或逻辑组完成后提交(按用户指示);在 test 分支开发,不新建分支
  • HashKey/HashIV 为敏感凭证:仅后端持有,日志脱敏,不下发前端