Files
cmbuyer/docs/tasks/T-402.md
T

9.4 KiB
Raw Blame History

id, title, phase, deps, status, created, vikunja_task_id, context_ref, work_branch, needs_device, needs_human_review, write_paths
id title phase deps status created vikunja_task_id context_ref work_branch needs_device needs_human_review write_paths
T-402 待付款人工收口与付款事实记录 4
T-403
TODO 2026-08-04 51 8600c33 task/t-402-payment-closure false true
docs/tasks/T-402.md
admin/migrations/00009_payment_closure.sql
admin/internal/migrations/migrations_test.go
admin/internal/domain/payment_closure.go
admin/internal/domain/payment_closure_test.go
admin/internal/paymentclosure/**
admin/internal/taskdetail/**
admin/internal/server/mark_paid.go
admin/internal/server/mark_paid_test.go
admin/internal/server/router.go
admin/internal/server/task_detail.go
admin/internal/server/task_detail_test.go
admin/internal/transport/webui/webui.go
admin/internal/transport/webui/templates/task-detail.html
admin/cmd/server/**
admin/README.md
docs/api.md
docs/04-architecture.md
docs/routes.md
docs/08-interaction-checklist.md
docs/current-state.md

背景

T-208 已冻结创建待付款订单后的 WAITING_PAYMENT 事实:仅当提交记录为 SUBMITTED,或管理员调和结论为 CONFIRMED_CREATED 时,系统才知道订单已创建。T-403 负责展示这些既有事实。本任务只记录管理员已经在拼多多人工完成真实付款这一外部事实,并把任务收口为成功;系统本身没有付款,也不得验证或自动执行付款。

关联

  • Feature:F011
  • Story:US006
  • Integration:IX006
  • 前置任务:T-403
  • 后续消费者:T-404

实现方案

  1. 新增唯一迁移 00009_payment_closure.sql,建立追加式 payment_closures:绑定同一 lineage 的 task、submission、attempt、authorization,以及管理员 actor、服务端 UTC 记录时间、幂等键与规范化请求摘要。唯一约束保证同一任务/提交只能收口一次,并保证同一管理员幂等键不可复用为不同请求;外键/约束必须阻止跨 lineage 事实。不得保存付款方式、账户、卡号、订单凭据、地址、手机号或截图/XML。migration down 只允许在 payment_closures 为空时执行;存在任意事实必须在删除表或约束前原子拒绝,禁止丢失付款审计事实。
  2. 在任务详情的 WAITING_PAYMENT 区域明确显示“订单已创建,系统尚未付款”,同时展示商品、规格、数量、金额上限,以及 T-208/T-403 已有提交、调和和三道闸门证据。不得暗示系统已付款。
  3. 管理员必须先在拼多多外部人工完成真实付款,再完成三个且仅三个确认组:goods_spec_quantity_confirmed 表示商品/规格/数量已核对,amount_within_cap_confirmed 表示订单金额未超过授权上限,externally_paid_confirmed 表示管理员已在拼多多人工完成真实付款。三项 wire 值必须全部严格为 true;任一缺失、为 false 或无法严格解析均返回 400 且零写入。按钮文案明确为“记录人工付款并标记完成(不执行付款)”。
  4. POST /tasks/{id}/mark-paid 仅接受管理员会话与 CSRF,通过严格表单读取 UUIDv4 幂等键、expected task version、当前 submission id 和上述三个固定布尔确认组,不接受别名或额外确认字段。actor 只能来自会话,时间只能来自服务端 UTC。
  5. 在单一事务和写锁内重新读取 task、submission、attempt、authorization:四者必须属于同一 lineage;任务必须正好是 WAITING_PAYMENT,submission 必须正好是该任务当前唯一提交且关联同一 attempt/authorization,authorization 必须正好是 CONSUMED。submission 必须为 SUBMITTED,或为已有 T-208 CONFIRMED_CREATED 最终调和事实的 MANUAL_RESOLVED。旧 task version、错误/过期 submission 固定返回 409 且零写入;任一 lineage、authorization、submission 或 task 状态冲突也必须返回 409、零写入,并让详情进入只读安全核查,绝不能推进为 SUCCEEDED。
  6. 所有检查通过后,同一事务追加 payment closure,并将任务从 WAITING_PAYMENT 单向推进到 SUCCEEDED、version + 1。submission、attempt、authorization 和已消费的提交围栏永久不可变,不得重开、释放、重置、重新授权或重新提交。
  7. 幂等性必须先查事实:同 key、同规范化 payload 在响应丢失和进程重启后稳定重放同一结果;同 key、不同 payload 返回 409;不同 key 在任务已收口后返回 payment_already_recorded 409。规范化 payload 必须包含 task、submission、expected version 和三个固定确认组。不得仅因任务已是 SUCCEEDED 就伪造成功响应。
  8. 成功页/详情页显示记录 actor、服务端时间和“仅记录人工付款事实,系统未执行付款”。失败保留用户输入并给出可操作提示,焦点回到错误摘要;事实冲突时只显示安全核查,不提供重试提交、释放或重新授权动作。
  9. 自动化测试只使用本地数据库夹具构造 WAITING_PAYMENT 和既有提交事实,覆盖事务、冲突、幂等、重启与 UI;不得连接拼多多、请求真实资金、模拟付款控件或要求测试人员实际付款。needs_human_review 只验收语义、权限和交互,不代表测试中发生真实付款。

验收

  • 三个且仅三个 wire 确认组固定为 goods_spec_quantity_confirmed、amount_within_cap_confirmed、externally_paid_confirmed;三项全部严格为 true 才能继续,逐项覆盖缺失、false、非法值和额外别名,均返回 400 且零写入。
  • 只有管理员、有效 CSRF、精确 task version、精确当前 submission、同 lineage 的 task/submission/attempt/authorization 和 CONSUMED authorization 能写入;旧版本和错误/过期 submission 稳定返回 409 且零写入。
  • 覆盖 SUBMITTED 与 CONFIRMED_CREATED 两条允许路径,以及跨 task/attempt/authorization、authorization 非 CONSUMED、其它任务/提交状态和事实冲突;冲突均返回 409、零写入、进入只读安全核查且绝不 SUCCEEDED。
  • 覆盖并发双击、同键同载荷重放、同键异载荷冲突、不同键重复收口和进程重启;事实表和任务状态在同一事务提交或回滚。
  • migration 覆盖 up、重开、外键/唯一/lineage/append-only 约束、空表 down 成功、存在任意 payment closure 时 down 在删表前原子拒绝,以及 foreign_key_check。
  • 页面显著显示“系统尚未付款”和“不执行付款”,键盘操作、焦点、错误摘要、安全核查态及移动宽度可用。
  • 自动测试不需要真实付款,不访问手机或拼多多;代码不得包含任何支付、免密、先用后付或扣款控件操作。
  • 运行 go test ./...、go test -race ./...、go vet ./...、go build ./...、前端交互检查、根目录初始化门禁、上下文校验、导出校验和 git diff --check。

执行记录

(暂无)

边界

  • mark-paid 只记录管理员已经在拼多多外部人工完成真实付款的事实;系统没有执行付款,也不得发起、 验证、模拟或自动化支付,不得读取或保存付款方式、账户、卡号、订单凭据、地址或手机号。
  • 三个且仅三个确认组固定为 goods_spec_quantity_confirmed、amount_within_cap_confirmed、 externally_paid_confirmed;三项 wire 值必须全部严格为 true。任一缺失、为 false、非法值或 别名返回 400 且零写入;旧 task version 与错误/过期 submission 固定返回 409。
  • 只允许精确处于 WAITING_PAYMENT 的任务和精确当前 submission 收口。事务必须确认 task、 submission、attempt、authorization 属于同一 lineage,authorization 正好为 CONSUMED; submission 必须是 SUBMITTED,或已有 T-208 CONFIRMED_CREATED 最终调和事实。任一归属、 状态或事实冲突都返回 409、零写入并进入只读安全核查,绝不能推进为 SUCCEEDED。
  • actor 只能取自已认证管理员会话,记录时间只能取服务端 UTC。幂等事实必须在任何状态短路前检查: 同 key 同规范化载荷稳定重放,同 key 异载荷返回 409;不同 key 重复收口返回 payment_already_recorded 409,不得仅因任务已是 SUCCEEDED 就伪造成功响应。
  • payment closure 事实与 task WAITING_PAYMENT → SUCCEEDED、version + 1 必须在同一事务内原子 提交或回滚。submission、authorization 和已消费围栏永久不可变,不得重开、释放、重置、重新授权、 重新领取或重新提交。
  • 本任务唯一新增 migration 是 00009_payment_closure.sql;事实表只保存同一 lineage 的 task、 submission、attempt、authorization、actor、服务端时间、幂等键和请求摘要,不保存截图、XML、 支付凭据或新的 evidence kind。payment_closures 为空时才允许 migration down;存在任意事实时 必须在删表或约束前原子拒绝,禁止丢失付款审计事实。
  • 自动化测试只使用本地数据库夹具,不访问手机或拼多多,不请求真实资金,也不要求测试人员真实付款。 needs_human_review 只验收管理 UI 的未付款语义、权限、确认和无障碍交互。
  • 本任务不修改 client/ 或拼多多页面判据,不点击支付、免密支付、先用后付或任何扣款控件。