# 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、规格及统一验证已经完成。

## Phase 1: Planning and safety gates

- [x] T001 将 `.specify/feature.json` 指向 `specs/019-line-pay`，并把 `spec.md` 标记为 2026-08-12 已批准
- [x] T002 完成 `research.md`、`data-model.md`、`contracts/api.md`、`quickstart.md` 和 `plan.md`
- [x] T003 阅读并执行支付安全、TDD 和执行计划技能；运行 spec-kit prerequisite 检查并记录初始 dirty worktree，确保不覆盖用户的 `updatesql/sql.md` 和 observation 变更

## Phase 2: Persistence foundation (ruoyi-system)

- [x] T004 [P] 在 `ruoyi-system/src/test/java/com/ruoyi/system/service/impl/PosStoreLinePayServiceImplTest.java` 写失败测试：相同凭证幂等、轮换新版本、并发只一个 current、失败验证不切换
- [x] T005 [P] 在 `ruoyi-system/src/test/java/com/ruoyi/system/service/impl/PosOrderLinePaymentServiceImplTest.java` 写失败测试：创建 REQUESTING、同订单唯一活跃、稳定键查询、CAS 状态、只有明确终态释放、App 选择多 PAID 转人工核对
- [x] T006 [P] 在 `ruoyi-system/src/test/java/com/ruoyi/system/service/impl/PosOrderLineRefundServiceImplTest.java` 写失败测试：paymentId 唯一、claim/UNKNOWN/REFUNDED CAS 和扫描租约
- [x] T007 在 `ruoyi-system/src/main/java/com/ruoyi/system/domain/` 新建 4 个实体，在 `domain/dto/` 新建 `StoreLinePayCredentialDto.java`、`StoreLinePayToggleDto.java`，在 `domain/vo/` 新建 `PosStoreLinePayVo.java`
- [x] T008 在 `ruoyi-system/src/main/java/com/ruoyi/system/mapper/` 与 `ruoyi-system/src/main/resources/mapper/chanting/` 新建凭证、支付、退款、网关日志 Mapper/XML；所有状态推进使用 expected status + version CAS，扫描使用覆盖索引条件
- [x] T009 在 `ruoyi-system/src/main/java/com/ruoyi/system/service/` 和 `service/impl/` 实现 4 个持久化服务，使 T004-T006 通过
- [x] T010 在 `ruoyi-system/src/main/java/com/ruoyi/system/mapper/PosOrderMapper.java` 与 `resources/mapper/system/PosOrderMapper.xml` 添加订单支付渠道领取、支付事实、退款事实的条件更新，并补相应 Mapper/Service 测试
- [x] T011 运行 `mvn -pl ruoyi-system -am -Dtest='*Line*Test' -Dsurefire.failIfNoSpecifiedTests=false test`

## Phase 3: LINE v4 client and credential management (ruoyi-admin)

- [x] T012 [P] 在 `ruoyi-admin/src/test/java/com/ruoyi/app/utils/linepay/LinePaySignerTest.java` 写官方格式的 POST JSON、GET 精确 query、非 ASCII、空 body 和 nonce 签名失败测试
- [x] T013 [P] 在 `ruoyi-admin/src/test/java/com/ruoyi/app/utils/linepay/LinePayClientTest.java` 写 method/URI/body/header、字符串 transactionId、超时分档、Request packages、默认浏览器 redirect、full refund `{}` 契约测试
- [x] T014 在 `ruoyi-admin/src/main/java/com/ruoyi/app/utils/linepay/` 实现 properties、signer、transport 和 client；固定 base URL 配置，不允许请求控制目标地址
- [x] T015 在 `ruoyi-admin/src/test/java/com/ruoyi/app/mendian/PosStoreLinePayControllerTest.java` 写凭证探测 `1150/0000/认证错误/网络未知`、详情权限、普通列表不含 Secret 的失败测试
- [x] T016 在 `ruoyi-admin/src/main/java/com/ruoyi/app/mendian/PosStoreLinePayController.java` 实现列表/详情/save/toggle，使用明确 DTO 和业务 i18n；在 `application.yml` 添加 LINE 非秘密配置并移除有效 Zalo sandbox 配置块
- [x] T017 在 `ruoyi-admin/src/main/java/com/ruoyi/app/pay/LinePayGatewayAuditService.java` 实现追加日志包装，测试日志失败不回滚/改变支付事实

## Phase 4: Create, query and redirect page

- [x] T018 [P] 在 `ruoyi-admin/src/test/java/com/ruoyi/app/pay/LinePayServiceCreateTest.java` 写失败测试：用户归属、payType=3、历史 Zalo 无 Line 行不识别、单门店、金额、凭证、重复点击复用、明确终态新增行、Request UNKNOWN
- [x] T019 [P] 在 `ruoyi-admin/src/test/java/com/ruoyi/app/pay/LinePaySelectionTest.java` 写 App 查询选择规则与旧回跳不污染新尝试测试
- [x] T020 [P] 在 `ruoyi-admin/src/test/java/com/ruoyi/app/pay/LinePayReturnPageRendererTest.java` 写 HTML/JS/URL 转义、固定 Scheme、安全响应头、无同步 Confirm 测试
- [x] T021 在 `ruoyi-admin/src/main/java/com/ruoyi/app/pay/PaymentCreateGuardService.java` 实现共享 watchdog 锁 + 订单条件领取，并对 `OmgPayController#create` 做仅限 create 门禁的最小调整及回归测试
- [x] T022 在 `ruoyi-admin/src/main/java/com/ruoyi/app/pay/LinePayService.java` 实现 create/query 与独立事务 intent/result；在 `pay/dto/` 新建请求/响应 DTO
- [x] T023 在 `ruoyi-admin/src/main/java/com/ruoyi/app/pay/LinePayController.java` 实现 create/query/confirm/cancel；confirm/cancel 快速返回 self-contained HTML，cancel 不写终态
- [x] T024 修改 `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 实体/Controller

## Phase 5: Reconcile, payment facts and refund

- [x] T025 [P] 在 `ruoyi-admin/src/test/java/com/ruoyi/app/pay/LinePayReconcileTest.java` 覆盖 Retrieve 唯一 CAPTURE、1150→Check、0000/0110/0121/0122/0123、Confirm UNKNOWN、deadline→MANUAL_REVIEW
- [x] T026 [P] 在 `ruoyi-admin/src/test/java/com/ruoyi/app/pay/LinePayCancellationRaceTest.java` 覆盖支付先成功后取消、取消先发生后迟到成功、堂食 state=1、退款 intent 唯一
- [x] T027 [P] 在 `ruoyi-admin/src/test/java/com/ruoyi/app/pay/LinePayRefundTest.java` 覆盖全额退款省略 amount、UNKNOWN 只 Retrieve、refundList 恢复、PAID 不改写、订单 payStatus=2
- [x] T028 在 `LinePayService` 实现 Retrieve-first 状态机、Confirm 本地门禁、支付事实事务、退款状态机与凭证版本解析，使 T025-T027 通过
- [x] T029 在 `ruoyi-admin/src/test/java/com/ruoyi/app/task/LinePayReconcileTaskTest.java` 写 watchdog 锁、batch=20、时间预算、行租约、单笔失败继续和取消订单不排除测试
- [x] T030 在 `ruoyi-admin/src/main/java/com/ruoyi/app/task/LinePayReconcileTask.java` 实现每分钟支付/退款恢复任务，不调用 Controller，不修改 `TestTask.java`
- [x] T031 修改 `UserOrderController` 和 `PosOrderShOprateController` 的真实取消入口：取消后调用 create-refund-if-absent；商家入口先按 userType/shId/mdId 鉴权
- [x] T032 修改 `PosOrderShOprateController` 接单入口并禁止真实 LINE 订单走 `PosOrderController#/setorderuzt`；历史 payType=3 且无 Line payment 不进入新 LINE 资金动作
- [x] T033 扩展 `OrderLifecycleService`、`AdminOrderStatusContext` 和 `PosOrderController`：显示 LINE 上下文，提供独立权限的人工 reconcile/full refund，并补现有 OrderLifecycle 测试
- [x] T033A 管理全额退款先在订单行锁内创建唯一退款意图；用户、商家、骑手真实 LINE 完成入口统一使用拒绝非终态退款意图的 CAS，并补并发回归测试
- [x] T033B 退款 PROCESSING 崩溃恢复时初始化 UNKNOWN 截止时间，UNKNOWN 无结论查询使用版本 CAS 退避重排，避免批次队首饥饿
- [x] T033C 定时恢复为支付、取消补偿和退款保留独立执行机会，支付领取租约时同步重排；真实 LINE 用户完成统一触发结算副作用；骑手接单、取餐、送达统一校验骑手角色、合法状态和订单归属
- [x] T033D 退款 UNKNOWN 到达截止时间后执行最后一次只读 Retrieve，不再先重排版本；人工核对 CAS 检查影响行数并重读真实状态，避免接口状态与数据库不一致
- [x] T033E 旧凭证明确定鉴权失败时，仅以同门店/环境/Channel 当前版本执行严格 Retrieve 证明后恢复；不同 Channel 或证据不符禁止 Confirm/Refund；REFUNDED 在应用层和 SQL 层永久禁止降级为人工核对
- [x] T033F 为严格 Retrieve 已证实的部分/歧义退款提供专用证据 CAS，允许 FAILED 升级人工核对但永久排除 REFUNDED，避免资金证据被通用状态白名单吞掉
- [x] T033G 为严格 Retrieve 已证实的全额退款提供专用证据 CAS，允许 FAILED 升级已退款但排除 MANUAL_REVIEW/REFUNDED，避免外部全退事实被历史本地失败状态吞掉

## Phase 6: SQL, messages and platform Vue

- [x] T034 在 `updatesql/sql.md` 末尾追加 2026-08-12 LINE DDL、索引、页面公共 `chanting:storePayment:list` 和 LINE list/query/save/toggle、订单 reconcile/refund 权限 SQL；不改写既有 OMG SQL
- [x] T035 在 `ruoyi-admin/src/main/resources/i18n/messages.properties` 及 `messages_{zh_CN,zh_TW,en_US,vi}.properties` 添加相同 LINE 业务错误 key
- [x] T036 在 `foodie-admin-vue/src/api/chanting/storeLinePay.js` 新建凭证 API；在 `views/mendian/storePayment/components/LinePayTab.vue` 实现列表、详情、凭证验证/轮换和启停；修改 `index.vue` 替换占位页并保持 CRLF
- [x] T037 在平台四个 `src/api/language/language.*.js` 文件添加完全相同的 `storeLinePay` key，并调整 `storePayment` 文本，不硬编码用户可见中文
- [x] T038 修改平台 `src/api/system/order.js` 与 `src/views/system/order/index.vue`，显示 LINE 状态并按独立权限提供查询/全额退款；UNKNOWN/MANUAL_REVIEW 明确提示不可重复副作用

## Phase 7: Full verification and delivery

- [x] T039 运行所有 LINE 定向测试和 `OrderLifecycleServiceTest,PosOrderAdminStatusControllerTest` 回归（99 项通过）；OMG 全量测试仍有与本功能无关的既有日志脱敏期望差异，未修改 OMG
- [x] T040 使用 JDK 21 运行 `mvn -pl ruoyi-admin -am -DskipTests package`
- [x] T041 在 `foodie-admin-vue` 对 LINE 新增文件运行 lint、运行 production build、核对四语 `storeLinePay` 32 个子 key 集合和 Vue CRLF
- [x] T042 运行 `git diff --check`、检查 `git status --short` 和 diff 统计；确认无数据库执行、无废弃代码改动、无用户文件覆盖
- [x] T043 对照 `quickstart.md` 完成可在本地模拟的验收；真实 Sandbox、LINE App 和商户 App Scheme 真机验证留在交付说明

## Phase 8: Offline persistence foundation

**Purpose**: 为 Online/Offline 并存提供可审计的订单来源、支付模式和状态 CAS；本阶段阻塞 US6。

- [x] T044 [P] 在 `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）
- [x] T045 修改 `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 工作。

## Phase 9: User Story 6 - 商家 My Code 扫码支付 (Priority: P1) 🎯 Offline MVP

**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`。

### Tests for User Story 6

> 以下测试任务必须先编写并确认失败，再执行对应实现任务；不在中途运行完整回归。

- [x] T046 [P] [US6] 在 `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）
- [x] T047 [P] [US6] 在 `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）
- [x] T048 [P] [US6] 在 `ruoyi-admin/src/test/java/com/ruoyi/app/pay/LinePayGatewayAuditServiceTest.java` 添加失败安全测试，覆盖 Offline REQUEST 正常、异常、不可解析响应和审计失败路径均不保存或输出原始 `oneTimeKey`，且 Channel Secret/HMAC/堆栈不进入响应（FR-OFF-014、FR-OFF-015）
- [x] T049 [P] [US6] 新建 `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）
- [x] T050 [P] [US6] 在 `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）
- [x] T051 [P] [US6] 在 `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）

### Implementation for User Story 6

- [x] T052 [US6] 修改 `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）
- [x] T053 [P] [US6] 在 `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）
- [x] T054 [P] [US6] 修改 `ruoyi-admin/src/main/java/com/ruoyi/app/pay/LinePayGatewayAuditService.java`，为 Offline REQUEST 建立只接收脱敏摘要的审计入口并阻止异常拼接请求对象，使 T048 通过；原始 `oneTimeKey` 不得离开内存调用链（FR-OFF-014）
- [x] T055 [US6] 新建 `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）
- [x] T056 [US6] 修改 `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）
- [x] T057 [US6] 修改 `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）
- [x] T058 [P] [US6] 在 `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 回归。

## Phase 10: Offline verification and delivery

**Purpose**: 所有 Offline 生产代码、测试源码、SQL 和规格完成后统一验证，不把局部测试结果描述为功能完成。

- [x] T059 使用 JDK 21 依次运行 `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 警告不影响结果。
- [x] T060 运行 `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 权限验证和前端构建。

## Dependencies

- T004-T006 可并行写测试；T007-T010 使其通过后才能开始业务编排。
- T012-T013 可并行；T014 完成后执行 T015-T017。
- T018-T020 可并行；T021-T024 完成 create/query/redirect。
- T025-T027 可并行；T028 完成后再接任务和订单入口 T029-T033。
- 后端契约稳定后执行 T034-T038；最后统一 T039-T043。
- Offline 增量先完成 T044-T045；T046-T051 可按不同测试文件并行编写，且必须在 T052-T057 对应实现前确认失败。
- T052、T053、T054 可在基础字段完成后并行；T055 依赖 T052-T054，T056 依赖 T055，T057 依赖 T055-T056。
- T058 可与 T053-T057 并行；T059-T060 必须等 T044-T058 全部完成后统一执行。

## Parallel Example: User Story 6

```text
并行测试批次：T046 商家下单、T047 网关契约、T048 审计脱敏、T049 付款服务、T050 恢复、T051 退款。
并行实现批次：T052 商家下单来源、T053 Offline Client、T054 审计脱敏、T058 i18n。
串行集成批次：T055 Controller/付款编排 → T056 状态恢复 → T057 退款与异常金额。
```

## Offline Implementation Strategy

1. **Foundation**: 完成 T044-T045，先让数据库模型可区分来源和模式。
2. **Offline MVP**: 完成 T046-T055，可独立演示商家创建订单、扫码发起和本地状态查询。
3. **Recovery and refund**: 完成 T056-T058，补齐未知结果、金额异常、取消和退款闭环。
4. **Unified verification**: 最后一次性执行 T059-T060；真实商户 Offline 权限或 Sandbox My Code 不可用时仅记录为未执行，不伪报通过。

## Definition of Done

- 同一订单可保留多条历史 LINE 尝试，但只有一条阻断性活跃行。
- 凭证轮换不覆盖历史版本，每笔支付持有 credentialId。
- 所有支付/确认/退款未知结果都可只读恢复，绝不盲重试副作用。
- 取消竞态最终只一次全额退款；payment 保持 PAID，退款成功后 order.payStatus=2。
- App 查询、回跳、任务、平台动作都通过稳定键定位正确尝试。
- 平台四语、权限、SQL 和最小 OMG create 门禁完成；目标测试、编译与前端检查通过。
- 只有 `order_source=MERCHANT` 的新商家单门店订单可发起 Offline；用户端、历史、跨商家和多门店订单在网关调用前拒绝。
- Online/Offline 共用唯一活跃门禁并只按 `payment_mode` 选择 Pay/Check/Refund 端点，任何未知资金结果不得盲目重复副作用。
- 原始 `oneTimeKey` 在数据库、普通日志、异常、响应和网关审计中均不存在；金额不一致不履约、只创建一次全额退款且原订单不再支付。
