Преглед на файлове

031可配置支付方式spec:平台开关x维度+商家选择+骑手闪送选择+下单统一校验方法+银行卡列表化与027迁移

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
qmj преди 8 часа
родител
ревизия
fd29a66e2e
променени са 3 файла, в които са добавени 219 реда и са изтрити 1 реда
  1. 1 1
      .specify/feature.json
  2. 37 0
      specs/031-pay-method-config/checklists/requirements.md
  3. 181 0
      specs/031-pay-method-config/spec.md

+ 1 - 1
.specify/feature.json

@@ -1,3 +1,3 @@
 {
-  "feature_directory": "specs/029-merchant-self-delivery"
+  "feature_directory": "specs/031-pay-method-config"
 }

+ 37 - 0
specs/031-pay-method-config/checklists/requirements.md

@@ -0,0 +1,37 @@
+# Specification Quality Checklist: 可配置支付方式(031-pay-method-config)
+
+**Purpose**: Validate specification completeness and quality before proceeding to planning
+**Created**: 2026-09-11
+**Feature**: [spec.md](../spec.md)
+
+## Content Quality
+
+- [x] No implementation details (languages, frameworks, APIs)
+- [x] Focused on user value and business needs
+- [x] Written for non-technical stakeholders
+- [x] All mandatory sections completed
+
+## Requirement Completeness
+
+- [x] No [NEEDS CLARIFICATION] markers remain(设计决策已于 2026-09-09 全部定稿,无遗留问题)
+- [x] Requirements are testable and unambiguous
+- [x] Success criteria are measurable
+- [x] Success criteria are technology-agnostic (no implementation details)
+- [x] All acceptance scenarios are defined
+- [x] Edge cases are identified
+- [x] Scope is clearly bounded(FR-015/016 + Assumptions 明确不做项)
+- [x] Dependencies and assumptions identified
+
+## Feature Readiness
+
+- [x] All functional requirements have clear acceptance criteria
+- [x] User scenarios cover primary flows(6 个故事覆盖平台/商家/骑手/用户四角色)
+- [x] Feature meets measurable outcomes defined in Success Criteria
+- [x] No implementation details leak into specification
+
+## Notes
+
+- 设计决策来源:2026-09-09 对话定稿(13 项决策表),全部固化进 FR/Edge Cases/Assumptions,无开放问题。
+- Assumptions 中三项为"可调默认"(LINE Pay 收款信息文本、银行卡无分行栏位、会员不维护收款卡),实现阶段如需变更需回到 spec 更新。
+- FR-017 提及 `updatesql/sql.md` 为项目数据库变更管理流程约束,非技术实现细节。
+- 下一阶段:`/speckit-plan`(数据模型、接口契约、027 迁移方案在 plan 阶段展开)。

+ 181 - 0
specs/031-pay-method-config/spec.md

@@ -0,0 +1,181 @@
+# Feature Specification: 可配置支付方式(平台开关 × 商家选择 × 骑手闪送选择)
+
+**Feature Branch**: `031-pay-method-config`
+
+**Created**: 2026-09-11
+
+**Status**: Draft(设计决策已于 2026-09-09 对话定稿,本 spec 为落地成文)
+
+**Input**: 平台能设置系统可用的支付方式(按"方式 × 维度"开关);商家能选择自己接受的支付方式;骑手能选择闪送收款方式;含银行卡列表化与 027 线下转账迁移。
+
+## 背景与核心模型
+
+支付方式目前在代码中为固定行为:用户下单传什么支付方式后端就接受什么(不校验该店是否可用,存在漏洞);闪送订单没有支付方式字段;线下转账的银行卡信息是 027 实现的固定三字段。
+
+本功能建立**三层闸门模型**,某支付方式对一笔订单可用 = **平台开关(方式 × 维度)** ∩ **收款方选择** ∩ **就绪度(支付凭证或启用银行卡)**。
+
+- **维度**两个,独立开关、可交叉:**商家餐饮单维度**(用户向商家付款的订单)、**骑手闪送维度**(用户直接付骑手的闪送订单)。例如"线下转账"可以商家侧关、骑手侧开。
+- **资金流不变**:闪送款项用户直接付骑手(现金 / LINE Pay 好友转账 / 银行转账),平台不经手。
+- 支付方式取值域(与现有系统一致):1=到付、2=信用卡(OMG)、3=LINE Pay、4=现金(仅商家建单场景)、5=Apple Pay(OMG)、6=线下转账。
+
+## User Scenarios & Testing *(mandatory)*
+
+### User Story 1 - 平台管理支付方式开关 (Priority: P1)
+
+平台管理员在管理后台的"支付方式设置"页,看到每个支付方式在两个维度(商家餐饮单 / 骑手闪送)下的开关,可独立打开或关闭。所有开关默认开启,即平台不做任何设置时系统行为与现状完全一致。
+
+**Why this priority**: 三层闸门的第一层,是整个功能的基础;没有平台开关,商家/骑手的选择无从谈起。
+
+**Independent Test**: 平台关闭"线下转账-商家维度"后,所有商家的餐饮下单流程中该方式立即不可用;重新打开后恢复。仅实现本故事即拥有"平台一键下线某支付方式"的最小价值。
+
+**Acceptance Scenarios**:
+
+1. **Given** 所有开关默认开启,**When** 管理员进入支付方式设置页,**Then** 每个支付方式展示两个维度开关且均为开启,现状不受影响。
+2. **Given** 管理员关闭"信用卡-商家维度",**When** 用户在该平台任意商家下单,**Then** 结算页不再出现信用卡支付(信用卡与 Apple Pay 同组联动)。
+3. **Given** 管理员关闭"LINE Pay-骑手闪送维度",**When** 闪送下单选择支付方式,**Then** LINE Pay 不出现,但商家餐饮单维度不受影响。
+4. **Given** 管理员误关某方式,**When** 重新开启,**Then** 该方式立即恢复可见可用,无需重启或发版。
+
+---
+
+### User Story 2 - 商家选择接受的支付方式 (Priority: P1)
+
+商家在商家端 PC(foodie-store)的支付设置页,勾选自己接受的支付方式,并可管理收款银行卡(增删、启用某一张)。选择在**商家主账号级**生效,连锁门店共享同一配置(与 027 银行信息语义一致)。商家从未设置(或清空)时默认接受全部启用方式,**存量商家行为零变化**。
+
+**Why this priority**: 三层闸门的第二层,是商家侧的核心诉求;与平台开关同为基础能力。
+
+**Independent Test**: 商家取消勾选"LINE Pay"后,用户在该商家下单时看不到 LINE Pay;仅实现本故事(配合 US1)即完成餐饮单维度闭环。
+
+**Acceptance Scenarios**:
+
+1. **Given** 商家从未设置支付方式,**When** 用户在该商家下单,**Then** 可用支付方式 = 平台开启的方式 ∩ 就绪度(与现状一致,零变化)。
+2. **Given** 商家只勾选"到付、现金",**When** 用户下单,**Then** 结算页仅展示到付、现金(仍受平台开关约束)。
+3. **Given** 连锁商家主账号修改支付方式选择,**When** 任意门店视角查看,**Then** 配置一致(主账号级共享)。
+4. **Given** 平台后续关闭了商家已勾选的某方式,**When** 用户下单,**Then** 该方式仍不可见(平台开关优先)。
+
+---
+
+### User Story 3 - 下单校验堵漏洞 (Priority: P1)
+
+用户提交订单时,后端必须校验所传支付方式可用(∈ 平台开启 ∩ 商家接受 ∩ 就绪度),不可用时返回国际化错误提示。现状 `createOrder` 完全不校验,本故事补上。校验逻辑 MUST 收敛为**一个独立的统一校验方法**,所有下单入口在下单时调用该方法,禁止各入口自行实现校验(单一事实来源,闸门规则改动只改一处)。
+
+**Why this priority**: 安全校验,防止绕过界面直接提交被禁方式(当前漏洞);统一方法保证餐饮单、商家建单、闪送单的闸门行为永远一致。
+
+**Independent Test**: 用接口直接提交一个平台已关闭的支付方式创建订单,返回明确错误;前端展示被禁时后端同样拦截,双保险;各下单入口走的是同一个校验方法。
+
+**Acceptance Scenarios**:
+
+1. **Given** "线下转账"平台已关或商家未选,**When** 客户端绕过界面直接以线下转账提交订单,**Then** 下单被拒绝并返回所使用语言的提示文案。
+2. **Given** 商家未配置 OMG 支付凭证(就绪度不满足),**When** 用户选择信用卡,**Then** 该方式不可用。
+3. **Given** 老版本 App 不感知新逻辑,**When** 其提交已下线的支付方式,**Then** 后端拒绝并返回国际化提示(不静默失败)。
+
+---
+
+### User Story 4 - 骑手选择闪送收款方式与抢单过滤 (Priority: P2)
+
+骑手设置自己接受的闪送收款方式;未设置时默认接受平台开放的全部方式(避免新单无人可见)。用户闪送**下单时选择支付方式**,订单记录该方式;骑手抢单列表只展示其接受该支付方式的订单。
+
+**Why this priority**: 闪送资金直达骑手,骑手必须能控制自己接受哪种收款;依赖平台开关(US1)先行。
+
+**Independent Test**: 骑手只选"现金",下单选"银行转账"的闪送订单不出现在其抢单列表;改选后恢复可见。
+
+**Acceptance Scenarios**:
+
+1. **Given** 骑手未设置闪送收款方式,**When** 任意支付方式的闪送订单产生,**Then** 该订单对骑手可见(默认=平台开放全部)。
+2. **Given** 骑手仅接受"现金、LINE Pay",**When** 用户创建"银行转账"闪送订单,**Then** 该订单不进入此骑手的抢单列表,但进入接受银行转账的其他骑手列表。
+3. **Given** 用户闪送下单,**When** 选择支付方式,**Then** 可选项 = 平台闪送维度开启 ∩ 就绪度,选定后随订单固定记录。
+4. **Given** 平台关闭骑手维度某方式,**When** 用户闪送下单,**Then** 该方式不可选,骑手端相应配置项同步失效。
+
+---
+
+### User Story 5 - 银行卡列表化与 027 迁移 (Priority: P2)
+
+收款银行卡从 027 的固定三字段改为**列表管理**:商家与骑手通用,可维护多张卡、同时仅一张启用(启用新卡自动停旧卡);付款方(用户)仅看到收款方当前启用的卡。027 存量数据自动迁移为一张启用卡;现有银行信息查询接口**保留、结构不变**,改为读取启用卡并套用闸门,老客户端无感。
+
+**Why this priority**: "就绪度"闸门与线下转账体验的基础;迁移保证存量商家转账展示不回退。
+
+**Independent Test**: 迁移后原有填写过银行信息的商家,用户下单线下转账仍能看到转账信息(来自新列表的启用卡);商家新增第二张卡并启用,付款方看到新卡。
+
+**Acceptance Scenarios**:
+
+1. **Given** 存量商家已有 027 银行三字段,**When** 升级后用户以线下转账下单,**Then** 转账信息正常展示(迁移自动完成,无人工操作)。
+2. **Given** 商家/骑手添加第二张银行卡并设为启用,**When** 付款方查看转账信息,**Then** 仅看到新启用的卡,旧卡自动停用。
+3. **Given** 收款方没有任何启用的卡,**When** 用户下单,**Then** 线下转账方式不满足就绪度、不可选。
+4. **Given** 027 原"三字段齐全才显示转账信息"规则,**When** 迁移后,**Then** 改为"存在启用卡即显示"。
+
+---
+
+### User Story 6 - 骑手确认收款 (Priority: P3)
+
+骑手送达并实际收到款后,在闪送订单上操作"确认收款";订单记录收款状态(未收→已收)与操作日志。确认收款**不阻塞**送达与完成流程(转账到账有时差,平台不经手资金不设硬闸门)。
+
+**Why this priority**: 资金闭环的运营记录,优先级最低——不影响交易主流程。
+
+**Independent Test**: 骑手送达后未确认收款,订单仍可正常完成;事后确认,状态与日志正确记录。
+
+**Acceptance Scenarios**:
+
+1. **Given** 闪送订单已送达,**When** 骑手确认收款,**Then** 收款状态变为已收并记录操作人与时间。
+2. **Given** 骑手未确认收款,**When** 订单流转送达/完成,**Then** 流程不受任何阻塞,收款状态保持未收。
+
+---
+
+### Edge Cases
+
+- 平台关闭某方式时,已存在但未支付的旧订单如何处理?→ 下单校验(US3)在创建时点生效;已创建订单不追溯改动。
+- 商家清空全部支付方式勾选?→ 视同未设置,回落为"全部启用"(与 NULL 语义一致,防止商家把自己锁死)。
+- 骑手维度关闭平台某方式后,历史闪送订单的支付方式字段?→ 记录的是下单时点值,不追溯。
+- 信用卡与 Apple Pay:作为同一组(OMG 渠道)一个开关,开关与商家勾选均同开同关。
+- 现金(4)特殊:不纳入商家勾选管控(仅商家建单场景使用),平台开关也不覆盖它。
+- 到付(1)纳入管控且默认开。
+- 老客户端(不感知新字段/新逻辑):一律由后端校验拒绝并返回国际化提示;银行信息查询接口保持原结构兼容。
+
+## Requirements *(mandatory)*
+
+### Functional Requirements
+
+- **FR-001**: 系统 MUST 提供平台级支付方式开关,按"支付方式 × 维度(商家餐饮单 / 骑手闪送)"独立控制,默认全部开启。
+- **FR-002**: 某支付方式对一笔订单可用 MUST 满足三层闸门:平台开关开启 ∩ 收款方接受 ∩ 就绪度满足(线上渠道有凭证、线下转账有启用银行卡)。
+- **FR-003**: 商家 MUST 能在商家端选择接受的支付方式,选择在主账号级生效(连锁共享);未设置或清空时 MUST 等同接受全部(存量行为零变化)。
+- **FR-004**: 系统 MUST 在创建订单时校验支付方式满足闸门,不满足时以用户所用语言返回明确错误提示(兼容老 App,不静默失败)。该校验 MUST 封装为单一独立方法,作为唯一校验入口;所有下单路径(用户餐饮下单、商家建单、闪送下单)MUST 在下单时统一调用该方法,MUST NOT 在各入口重复实现校验逻辑。
+- **FR-005**: 信用卡与 Apple Pay MUST 作为同一组方式展示与控制(同开同关)。
+- **FR-006**: 到付(1)MUST 纳入平台开关管控且默认开;现金(4)MUST 不纳入本管控(仅商家建单场景)。
+- **FR-007**: 用户闪送下单时 MUST 选择支付方式且被订单记录;可选项 = 闪送维度平台开关 ∩ 就绪度。
+- **FR-008**: 骑手 MUST 能设置接受的闪送收款方式;未设置时 MUST 默认接受平台开放的全部方式。
+- **FR-009**: 骑手抢单列表 MUST 只展示支付方式为其接受的闪送订单。
+- **FR-010**: 商家与骑手 MUST 能维护收款银行卡列表(多卡、同时仅一张启用、启用新卡自动停旧卡);付款方仅能看到收款方启用的卡。
+- **FR-011**: 027 存量银行三字段 MUST 自动迁移为一张启用卡,迁移不需人工操作;银行信息查询接口 MUST 保持原有结构返回启用卡并套用闸门。
+- **FR-012**: 线下转账就绪度 MUST 由"三字段齐全"改为"存在启用银行卡"。
+- **FR-013**: 骑手 MUST 能对闪送订单执行确认收款(未收→已收 + 操作日志);该操作 MUST NOT 阻塞送达/完成流程。
+- **FR-014**: 平台端开关设置页 MUST 提供四语言界面;商家端 PC 设置页(方式勾选 + 银行卡管理)MUST 实现多语言。
+- **FR-015**: 平台 MUST NOT 提供逐门店覆盖商家支付方式的能力(本期明确不做)。
+- **FR-016**: 商家 App / 骑手 App 端界面本期 MUST NOT 开发(后端出接口,由 App 团队按 027 FR-010 模式另行接入)。
+- **FR-017**: 所有数据库变更 MUST 记入 `updatesql/sql.md` 由开发者统一手动执行(项目惯例)。
+
+### Key Entities *(include if feature involves data)*
+
+- **支付方式**: 业务枚举(到付、信用卡、Apple Pay、LINE Pay、现金、线下转账),信用卡与 Apple Pay 同组。
+- **平台支付方式开关**: 方式 × 维度(商家餐饮单/骑手闪送)× 启停状态。
+- **商家支付方式选择**: 商家主账号 → 接受的方式集合;空=全部。
+- **骑手闪送收款方式选择**: 骑手 → 接受的方式集合;空=平台开放全部。
+- **收款银行卡**: 归属商家或骑手,多卡、单卡启用;含开户行(台湾银行清单)、账号、户名与审计信息。
+- **闪送订单支付信息**: 订单记录的支付方式与收款状态(未收/已收)。
+
+## Success Criteria *(mandatory)*
+
+### Measurable Outcomes
+
+- **SC-001**: 平台切换任一开关后,该方式在全端的可见性立即(下一次查询)生效,无延迟、无重启。
+- **SC-002**: 绕过界面直接提交被禁支付方式的下单请求,100% 被后端拒绝且返回所用语言的提示。
+- **SC-003**: 升级上线后,未做任何设置的存量商家与骑手,其订单支付行为与升级前完全一致(零回归)。
+- **SC-004**: 027 迁移完成后,全部存量有银行信息的商家线下转账展示不回退(迁移覆盖率 100%)。
+- **SC-005**: 闪送抢单列表中不再出现骑手不接受支付方式的订单(过滤准确率 100%)。
+- **SC-006**: 平台设置页与商家端设置页全部文案覆盖四语言(vi/zh/tw/en),无硬编码。
+
+## Assumptions
+
+- 骑手"LINE Pay 收款信息"为文本字段(LINE ID/收款说明),骑手接单后展示给用户——具体展示位置与样式可在实现阶段调整。
+- 银行卡暂不含"分行"栏位;如后续需要按增量补充。
+- 普通付款用户(会员)不维护收款银行卡;收款方仅商家与骑手。
+- 闪送资金流维持"用户直接付骑手、平台不经手"现状,本期不引入平台代收。
+- 商家 App / 骑手 App 前端页面不在本期范围(仅后端接口);OMG / LINE Pay 凭证维持现有门店级平台管理模式。
+- 骑手确认收款为运营记录性质,不与资金到账做系统核对。