# Tasks: LINE Pay Online / Offline API v4 支付
Input: spec.md, plan.md, research.md, data-model.md, contracts/api.md, offline-merchant-scan-design.md
Tests: 本功能涉及支付、凭证、外部 HTTP 和并发状态,所有实现任务必须先添加失败测试。
T001~T043及T033A~T033G是已完成的 Online API v4 任务。2026-08-18 Offline 商家扫码增量从T044开始;生产代码、测试源码、SQL、规格及统一验证已经完成。
.specify/feature.json 指向 specs/019-line-pay,并把 spec.md 标记为 2026-08-12 已批准research.md、data-model.md、contracts/api.md、quickstart.md 和 plan.mdupdatesql/sql.md 和 observation 变更ruoyi-system/src/test/java/com/ruoyi/system/service/impl/PosStoreLinePayServiceImplTest.java 写失败测试:相同凭证幂等、轮换新版本、并发只一个 current、失败验证不切换ruoyi-system/src/test/java/com/ruoyi/system/service/impl/PosOrderLinePaymentServiceImplTest.java 写失败测试:创建 REQUESTING、同订单唯一活跃、稳定键查询、CAS 状态、只有明确终态释放、App 选择多 PAID 转人工核对ruoyi-system/src/test/java/com/ruoyi/system/service/impl/PosOrderLineRefundServiceImplTest.java 写失败测试:paymentId 唯一、claim/UNKNOWN/REFUNDED CAS 和扫描租约ruoyi-system/src/main/java/com/ruoyi/system/domain/ 新建 4 个实体,在 domain/dto/ 新建 StoreLinePayCredentialDto.java、StoreLinePayToggleDto.java,在 domain/vo/ 新建 PosStoreLinePayVo.javaruoyi-system/src/main/java/com/ruoyi/system/mapper/ 与 ruoyi-system/src/main/resources/mapper/chanting/ 新建凭证、支付、退款、网关日志 Mapper/XML;所有状态推进使用 expected status + version CAS,扫描使用覆盖索引条件ruoyi-system/src/main/java/com/ruoyi/system/service/ 和 service/impl/ 实现 4 个持久化服务,使 T004-T006 通过ruoyi-system/src/main/java/com/ruoyi/system/mapper/PosOrderMapper.java 与 resources/mapper/system/PosOrderMapper.xml 添加订单支付渠道领取、支付事实、退款事实的条件更新,并补相应 Mapper/Service 测试mvn -pl ruoyi-system -am -Dtest='*Line*Test' -Dsurefire.failIfNoSpecifiedTests=false testruoyi-admin/src/test/java/com/ruoyi/app/utils/linepay/LinePaySignerTest.java 写官方格式的 POST JSON、GET 精确 query、非 ASCII、空 body 和 nonce 签名失败测试ruoyi-admin/src/test/java/com/ruoyi/app/utils/linepay/LinePayClientTest.java 写 method/URI/body/header、字符串 transactionId、超时分档、Request packages、默认浏览器 redirect、full refund {} 契约测试ruoyi-admin/src/main/java/com/ruoyi/app/utils/linepay/ 实现 properties、signer、transport 和 client;固定 base URL 配置,不允许请求控制目标地址ruoyi-admin/src/test/java/com/ruoyi/app/mendian/PosStoreLinePayControllerTest.java 写凭证探测 1150/0000/认证错误/网络未知、详情权限、普通列表不含 Secret 的失败测试ruoyi-admin/src/main/java/com/ruoyi/app/mendian/PosStoreLinePayController.java 实现列表/详情/save/toggle,使用明确 DTO 和业务 i18n;在 application.yml 添加 LINE 非秘密配置并移除有效 Zalo sandbox 配置块ruoyi-admin/src/main/java/com/ruoyi/app/pay/LinePayGatewayAuditService.java 实现追加日志包装,测试日志失败不回滚/改变支付事实ruoyi-admin/src/test/java/com/ruoyi/app/pay/LinePayServiceCreateTest.java 写失败测试:用户归属、payType=3、历史 Zalo 无 Line 行不识别、单门店、金额、凭证、重复点击复用、明确终态新增行、Request UNKNOWNruoyi-admin/src/test/java/com/ruoyi/app/pay/LinePaySelectionTest.java 写 App 查询选择规则与旧回跳不污染新尝试测试ruoyi-admin/src/test/java/com/ruoyi/app/pay/LinePayReturnPageRendererTest.java 写 HTML/JS/URL 转义、固定 Scheme、安全响应头、无同步 Confirm 测试ruoyi-admin/src/main/java/com/ruoyi/app/pay/PaymentCreateGuardService.java 实现共享 watchdog 锁 + 订单条件领取,并对 OmgPayController#create 做仅限 create 门禁的最小调整及回归测试ruoyi-admin/src/main/java/com/ruoyi/app/pay/LinePayService.java 实现 create/query 与独立事务 intent/result;在 pay/dto/ 新建请求/响应 DTOruoyi-admin/src/main/java/com/ruoyi/app/pay/LinePayController.java 实现 create/query/confirm/cancel;confirm/cancel 快速返回 self-contained HTML,cancel 不写终态ruoyi-system/src/main/java/com/ruoyi/system/domain/PosOrder.java 和 ruoyi-admin/src/main/java/com/ruoyi/app/order/dto/OrderPositionInfo.java 的有效 payType 注释为 3=LINE Pay,不修改废弃 Zalo 实体/Controllerruoyi-admin/src/test/java/com/ruoyi/app/pay/LinePayReconcileTest.java 覆盖 Retrieve 唯一 CAPTURE、1150→Check、0000/0110/0121/0122/0123、Confirm UNKNOWN、deadline→MANUAL_REVIEWruoyi-admin/src/test/java/com/ruoyi/app/pay/LinePayCancellationRaceTest.java 覆盖支付先成功后取消、取消先发生后迟到成功、堂食 state=1、退款 intent 唯一ruoyi-admin/src/test/java/com/ruoyi/app/pay/LinePayRefundTest.java 覆盖全额退款省略 amount、UNKNOWN 只 Retrieve、refundList 恢复、PAID 不改写、订单 payStatus=2LinePayService 实现 Retrieve-first 状态机、Confirm 本地门禁、支付事实事务、退款状态机与凭证版本解析,使 T025-T027 通过ruoyi-admin/src/test/java/com/ruoyi/app/task/LinePayReconcileTaskTest.java 写 watchdog 锁、batch=20、时间预算、行租约、单笔失败继续和取消订单不排除测试ruoyi-admin/src/main/java/com/ruoyi/app/task/LinePayReconcileTask.java 实现每分钟支付/退款恢复任务,不调用 Controller,不修改 TestTask.javaUserOrderController 和 PosOrderShOprateController 的真实取消入口:取消后调用 create-refund-if-absent;商家入口先按 userType/shId/mdId 鉴权PosOrderShOprateController 接单入口并禁止真实 LINE 订单走 PosOrderController#/setorderuzt;历史 payType=3 且无 Line payment 不进入新 LINE 资金动作OrderLifecycleService、AdminOrderStatusContext 和 PosOrderController:显示 LINE 上下文,提供独立权限的人工 reconcile/full refund,并补现有 OrderLifecycle 测试updatesql/sql.md 末尾追加 2026-08-12 LINE DDL、索引、页面公共 chanting:storePayment:list 和 LINE list/query/save/toggle、订单 reconcile/refund 权限 SQL;不改写既有 OMG SQLruoyi-admin/src/main/resources/i18n/messages.properties 及 messages_{zh_CN,zh_TW,en_US,vi}.properties 添加相同 LINE 业务错误 keyfoodie-admin-vue/src/api/chanting/storeLinePay.js 新建凭证 API;在 views/mendian/storePayment/components/LinePayTab.vue 实现列表、详情、凭证验证/轮换和启停;修改 index.vue 替换占位页并保持 CRLFsrc/api/language/language.*.js 文件添加完全相同的 storeLinePay key,并调整 storePayment 文本,不硬编码用户可见中文src/api/system/order.js 与 src/views/system/order/index.vue,显示 LINE 状态并按独立权限提供查询/全额退款;UNKNOWN/MANUAL_REVIEW 明确提示不可重复副作用OrderLifecycleServiceTest,PosOrderAdminStatusControllerTest 回归(99 项通过);OMG 全量测试仍有与本功能无关的既有日志脱敏期望差异,未修改 OMGmvn -pl ruoyi-admin -am -DskipTests packagefoodie-admin-vue 对 LINE 新增文件运行 lint、运行 production build、核对四语 storeLinePay 32 个子 key 集合和 Vue CRLFgit diff --check、检查 git status --short 和 diff 统计;确认无数据库执行、无废弃代码改动、无用户文件覆盖quickstart.md 完成可在本地模拟的验收;真实 Sandbox、LINE App 和商户 App Scheme 真机验证留在交付说明Purpose: 为 Online/Offline 并存提供可审计的订单来源、支付模式和状态 CAS;本阶段阻塞 US6。
ruoyi-system/src/test/java/com/ruoyi/system/service/impl/PosOrderLinePaymentServiceImplTest.java 和 ruoyi-system/src/test/java/com/ruoyi/system/mapper/LinePayOfflinePersistenceContractTest.java 添加失败测试,覆盖 paymentMode=ONLINE/OFFLINE 显式写入、历史默认 ONLINE、order_source/payment_mode/captured_amount Mapper 往返、Offline 状态 CAS、认证截止改写、只有 CANCEL/FAIL 释放活跃键以及 AMOUNT_MISMATCH/MANUAL_REVIEW 持续阻断(FR-OFF-002、FR-OFF-003、FR-OFF-009、FR-OFF-012)ruoyi-system/src/main/java/com/ruoyi/system/domain/PosOrder.java、ruoyi-system/src/main/java/com/ruoyi/system/domain/PosOrderLinePayment.java、ruoyi-system/src/main/java/com/ruoyi/system/service/IPosOrderLinePaymentService.java、ruoyi-system/src/main/java/com/ruoyi/system/service/impl/PosOrderLinePaymentServiceImpl.java、ruoyi-system/src/main/java/com/ruoyi/system/mapper/PosOrderMapper.java、ruoyi-system/src/main/java/com/ruoyi/system/mapper/PosOrderLinePaymentMapper.java、ruoyi-system/src/main/resources/mapper/system/PosOrderMapper.xml、ruoyi-system/src/main/resources/mapper/chanting/PosOrderLinePaymentMapper.xml,并在 updatesql/sql.md 追加 order_source、payment_mode、captured_amount 和索引 DDL,使 T044 通过;默认值只兼容历史,业务创建必须显式写值且不得执行 SQL(FR-OFF-002、FR-OFF-003、FR-OFF-016)Checkpoint: Entity、Mapper、Service 和 SQL 已能稳定区分订单来源及 Online/Offline 模式,现有 Online 行仍按 ONLINE 工作。
Goal: 商家使用自己的 token 为商家端创建的单门店 LINE Pay 订单扫描客户 My Code,后端安全完成 Offline 自动请款、状态收敛和全额退款,不修改任何前端。
Independent Test: 使用商家 token 创建 MERCHANT 单门店订单并提交 Sandbox oneTimeKey,验证 Pay 只调用一次,AUTH_REQUIRED/PROCESSING/PAID/CANCELLED/FAILED/MANUAL_REVIEW 正确收敛,数据库和日志不含 My Code;用户端、历史、跨商家和多门店订单在网关调用前拒绝;Offline 退款按 lineOrderId、Online 退款仍按 transactionId。
以下测试任务必须先编写并确认失败,再执行对应实现任务;不在中途运行完整回归。
ruoyi-admin/src/test/java/com/ruoyi/app/order/PosOrderShOprateControllerTest.java 添加失败测试,覆盖有效商家身份、普通/夜市商家 shId=userId + PosStore.userId=userId、其他摊位商家 mdId=InfoUser.storeId、单门店 items、跨店组合与非法归属拒绝以及新订单显式写 orderSource=MERCHANT(FR-OFF-003、FR-OFF-004)ruoyi-admin/src/test/java/com/ruoyi/app/utils/linepay/LinePayClientTest.java 添加失败契约测试,精确断言 Offline Pay/Check/Refund method、URI、UTF-8 JSON/HMAC、18 位 oneTimeKey、Pay 40 秒、Check/Refund 20 秒、无 redirect/Capture/Void/device headers 和字符串 transactionId(FR-OFF-005、FR-OFF-006、FR-OFF-007、FR-OFF-008、FR-OFF-013)ruoyi-admin/src/test/java/com/ruoyi/app/pay/LinePayGatewayAuditServiceTest.java 添加失败安全测试,覆盖 Offline REQUEST 正常、异常、不可解析响应和审计失败路径均不保存或输出原始 oneTimeKey,且 Channel Secret/HMAC/堆栈不进入响应(FR-OFF-014、FR-OFF-015)ruoyi-admin/src/test/java/com/ruoyi/app/pay/LinePayOfflineServiceTest.java,添加失败测试覆盖订单来源/归属/payType/金额/凭证二次门禁、服务端请求事实、0000 严格核对、非空 paymentProvider 原值保存、1145/1169 映射、未知码阻断、CAS 冲突、明确失败释放和重复请求复用(FR-OFF-004、FR-OFF-005、FR-OFF-009、FR-OFF-010、FR-OFF-012)ruoyi-admin/src/test/java/com/ruoyi/app/pay/LinePayOfflineServiceTest.java 与 ruoyi-admin/src/test/java/com/ruoyi/app/task/LinePayReconcileTaskTest.java 添加失败测试,覆盖 Offline AUTH_READY/COMPLETE/CANCEL/FAIL、1152/1172/1198/1199/190X/9000、未识别返回码、超时后只按 lineOrderId Check、首次 30 分钟认证截止且不顺延、24 小时未知截止和任务按 paymentMode 分派(FR-OFF-007~FR-OFF-010)ruoyi-admin/src/test/java/com/ruoyi/app/pay/LinePayRefundTest.java 与 ruoyi-admin/src/test/java/com/ruoyi/app/pay/LinePayCancellationRaceTest.java 添加失败测试,覆盖 Offline 按 lineOrderId 全退、Online 按 transactionId 回归、金额不一致实际扣款额持久化/原子唯一退款意图/禁止履约/原订单永久关闭、退款 UNKNOWN 只读 Retrieve 以及取消后迟到付款(FR-OFF-011、FR-OFF-013)ruoyi-admin/src/main/java/com/ruoyi/app/order/PosOrderShOprateController.java,在创建父子订单前完成商家身份、shId/mdId 门店归属和单门店校验,并为创建出的 PosOrder 显式写 orderSource=MERCHANT,使 T046 通过;不修改 UserOrderController 的 USER 默认行为(FR-OFF-001、FR-OFF-003、FR-OFF-004)ruoyi-admin/src/main/java/com/ruoyi/app/utils/linepay/ 新建 LinePayOfflineRequest.java 并扩展 LinePayClient.java 的 payOffline/checkOffline/refundOfflineFull,复用 signer/transport/base URL allowlist,使 T047 通过(FR-OFF-005~FR-OFF-008、FR-OFF-013、FR-OFF-015)ruoyi-admin/src/main/java/com/ruoyi/app/pay/LinePayGatewayAuditService.java,为 Offline REQUEST 建立只接收脱敏摘要的审计入口并阻止异常拼接请求对象,使 T048 通过;原始 oneTimeKey 不得离开内存调用链(FR-OFF-014)ruoyi-admin/src/main/java/com/ruoyi/app/pay/dto/LinePayOfflinePayRequest.java、ruoyi-admin/src/main/java/com/ruoyi/app/pay/dto/LinePayOfflineResult.java、ruoyi-admin/src/main/java/com/ruoyi/app/pay/LinePayOfflineService.java 和 ruoyi-admin/src/main/java/com/ruoyi/app/order/PosOrderLinePayOfflineController.java,实现 /system/orderShOprate/linePay/offline/pay 与 /status、共享支付锁、OFFLINE 尝试创建/复用、明确 DTO 注解和规范状态响应,使 T049 通过(FR-OFF-001、FR-OFF-004~FR-OFF-010、FR-OFF-012)ruoyi-admin/src/main/java/com/ruoyi/app/pay/LinePayOfflineService.java、ruoyi-admin/src/main/java/com/ruoyi/app/pay/LinePayService.java、ruoyi-admin/src/main/java/com/ruoyi/app/pay/LinePayFactService.java、ruoyi-admin/src/main/java/com/ruoyi/app/task/LinePayReconcileTask.java、ruoyi-system/src/main/java/com/ruoyi/system/service/IPosOrderLinePaymentService.java、ruoyi-system/src/main/java/com/ruoyi/system/service/impl/PosOrderLinePaymentServiceImpl.java、ruoyi-system/src/main/java/com/ruoyi/system/mapper/PosOrderLinePaymentMapper.java 和 ruoyi-system/src/main/resources/mapper/chanting/PosOrderLinePaymentMapper.xml,使恢复流程按 paymentMode 分派并落实 WAITING_AUTH、REQUEST_UNKNOWN、AMOUNT_MISMATCH、MANUAL_REVIEW、CAS 冲突重读与支付副作用至多一次,使 T050 通过(FR-OFF-008~FR-OFF-012)ruoyi-admin/src/main/java/com/ruoyi/app/pay/LinePayOfflineService.java、ruoyi-admin/src/main/java/com/ruoyi/app/pay/LinePayRefundService.java、ruoyi-admin/src/main/java/com/ruoyi/app/pay/LinePayFactService.java 和 ruoyi-system/src/main/java/com/ruoyi/system/service/impl/PosOrderLineRefundServiceImpl.java,复用现有唯一退款意图与退款状态 CAS,按持久化 paymentMode 选择 Offline lineOrderId 或 Online transactionId 全额退款,并以实际扣款额原子完成金额不一致及取消竞态收口,使 T051 通过(FR-OFF-011、FR-OFF-013)ruoyi-admin/src/main/resources/i18n/messages.properties、ruoyi-admin/src/main/resources/i18n/messages_zh_CN.properties、ruoyi-admin/src/main/resources/i18n/messages_zh_TW.properties、ruoyi-admin/src/main/resources/i18n/messages_en_US.properties、ruoyi-admin/src/main/resources/i18n/messages_vi.properties 添加集合完全一致的商家扫码、归属、My Code、状态和人工核对业务错误 key;Controller/Service 只使用 MessageUtils.message(...)(FR-OFF-004、FR-OFF-014)Checkpoint: US6 可在不修改 uni-app 或管理前端的情况下独立完成 Sandbox My Code 付款、轮询、恢复和退款验收,并保持 Online 回归。
Purpose: 所有 Offline 生产代码、测试源码、SQL 和规格完成后统一验证,不把局部测试结果描述为功能完成。
mvn -pl ruoyi-system -am -Dtest='*Line*Test' -Dsurefire.failIfNoSpecifiedTests=false test 与 mvn -pl ruoyi-admin -am -Dtest='*LinePayOffline*Test,PosOrderShOprateControllerTest,*LinePay*Test,OrderLifecycleServiceTest,OmgPaymentControllerTest' -Dsurefire.failIfNoSpecifiedTests=false test:2026-08-18 系统层 14/14 通过;管理组合命令 139/139 通过(管理模块 128、依赖模块 11),零失败、零错误、零跳过。既有 Maven model/Lombok/deprecation/动态 Java agent 警告不影响结果。mvn -pl ruoyi-admin -am -DskipTests package、git diff --check 和 quickstart.md 的 Offline 可执行验收:2026-08-18 JDK 21 七模块打包成功,差异、Controller 契约、oneTimeKey 生产代码流向、五套 i18n 各 9 个同名 key 和 SQL 追加范围检查通过;仅修改后端、测试、规格、CLAUDE.md 与 updatesql/sql.md。未执行数据库 DDL、真实 Sandbox My Code/生产商户 Offline 权限验证和前端构建。并行测试批次:T046 商家下单、T047 网关契约、T048 审计脱敏、T049 付款服务、T050 恢复、T051 退款。
并行实现批次:T052 商家下单来源、T053 Offline Client、T054 审计脱敏、T058 i18n。
串行集成批次:T055 Controller/付款编排 → T056 状态恢复 → T057 退款与异常金额。
order_source=MERCHANT 的新商家单门店订单可发起 Offline;用户端、历史、跨商家和多门店订单在网关调用前拒绝。payment_mode 选择 Pay/Check/Refund 端点,任何未知资金结果不得盲目重复副作用。oneTimeKey 在数据库、普通日志、异常、响应和网关审计中均不存在;金额不一致不履约、只创建一次全额退款且原订单不再支付。