Files
X-Financial/document/development/2026-07-16/feature/financial-connector-reconciliation/CONCEPT.md

227 lines
21 KiB
Markdown
Raw Normal View History

# 财务连接器与支付对账闭环 概念文档
更新时间2026-07-17
## 功能一句话
把经过租户、来源、密钥版本和请求路径共同认证的 production-mode 财务事件契约接入付款、ERP、对账与 Savings 闭环,同时让所有非生产回执严格停留在只读模拟事实层;真实外部现金仍以目标 provider 联调为准。
## 背景与问题
当前报销单可以由财务人员在平台内确认“已付款”,并能联动申请归档和 Savings 实现记录,但这只是内部业务状态,不是银行、支付平台或 ERP 的外部现金事实。若继续把单一状态当成真实回执,会留下重复付款、金额/币种错配、回执伪造、凭证缺失、对账异常未处置和虚假现金节省等风险。
本功能把外部财务系统接入收敛成统一、可审计的连接器契约。首期允许使用 mock adapter 验证协议和流程,但 mock 必须完整模拟签名、幂等、失败、重试、乱序、退款、凭证和对账差异,不以直接写“已付款”代替连接器事实。
## 目标与非目标
### 目标
- 建立租户隔离、来源可验证、追加式的支付/银行/ERP 事件账本。
- 支持支付批次、结算成功、支付失败、退款/冲回、ERP 入账凭证和对账结果。
- 只有单据、金额、币种、外部引用和签名均通过校验时,才推进报销付款状态。
- 把外部事件与 Expense Case、Business Event、Savings Evidence、归档事件用同一 correlation 链关联。
- 对重复、冲突、乱序和不完整事件 fail-closed并提供可人工处理的对账异常。
- 以版本化 activate/disable/rotate 状态机管理连接器密钥,所有配置动作形成不含密钥材料的追加式审计。
### 非目标
- 不自建银行清算、企业支付网络、税务开票网络或 ERP 总账。
- 不保存银行卡号、银行流水原文、完整付款人账号或连接器密钥明文。
- 不允许连接器绕过报销审批、风险门禁、租户权限或财务确认。
- 不把 mock 回执标记为生产级外部现金证据;运行环境和证据等级必须显式区分。
## 用户与场景
- 财务付款员:提交或查看付款批次,处理失败与待匹配回执。
- 财务复核员:复核金额/币种/收款主体和对账异常,确认或拒绝处置。
- 财务负责人/CFO查看已匹配、待对账、失败、退款和未入账金额。
- 平台管理员:配置连接器公钥/密钥版本、来源白名单和健康状态,但不能代替财务确认业务结果。
- 外部连接器:按租户和来源签名推送支付、银行或 ERP 事件,安全重试并读取幂等结果。
## 功能能力
- 连接器注册与密钥版本:来源、环境、允许事件、时钟偏差、启停和轮换状态。
- 统一事件信封tenant、provider、event ID、event type、occurred at、payload hash、correlation、signature version。
- 支付批次与回执:批次创建、提交、受理、成功、失败和部分成功。
- ERP 入账:凭证号、会计期间、入账时间、受控摘要和原始内容哈希。
- 对账:按单据、金额、币种和外部引用自动匹配;差异进入人工处置。
- 冲回:退款、撤销和补付使用新事件,不更新或删除原事件。
- 可观测性:最近成功时间、失败率、重试次数、积压、签名失败和对账差异。
## 方案设计
### 模块职责
- `financial_connector_auth`:验证来源、签名、时间戳、密钥版本和重放窗口。
- `financial_connector_ingestion`:规范化事件、计算指纹、幂等写入、生产/模拟分流和冲突检测。
- `financial_connector_simulation`:只读校验 test/mock/staging 回执,不创建或修改 Claim、对账、ERP、Business Event 或 Savings。
- `financial_connector_mock_adapter`:平台管理员显式触发的非生产场景适配器;用已激活配置的真实签名链确定性生成成功、失败、乱序、重复、冲突、退款和 ERP 回执,但不提供任意 payload 注入能力。
- `financial_connector_observability`:按租户和配置聚合最近成功、失败率、重试、积压、签名失败、冲突和对账异常;只读取最小化事实与追加式运行事件。
- `financial_connector_payment_evidence`:从 Claim 的付款事实中提取统一证据 DTO明确区分 production-mode 外部回执分类、非生产模拟回执和人工付款内部状态;分类本身不证明真实 provider 已接通。
- `financial_connector_config_lifecycle`:执行带 expected version 的激活、停用和原子密钥轮换。
- `payment_reconciliation`:匹配 Claim、金额、币种和状态生成 matched / exception 结果。
- `financial_connector_actions`:在可信匹配后调用现有付款动作;支付失败、退款和 ERP 入账分别旁写事件。
- `financial_connector_projection`:为财务工作台提供脱敏列表、详情、差异和健康度。
- 供应商 adapter 只负责供应商字段映射,不直接修改 Claim、Savings 或预算。
### 数据与契约
首期新增以下 migration-owned 表,均带 `tenant_id`
- `financial_connector_configs`provider、environment、allowed event types、secret/key version、status、last success/error只保存密钥引用或不可逆验证材料。
- `financial_connector_config_events`created、activated、disabled、rotation started/replacement created 的追加式配置审计;保存 actor、request、reason、expected version 和脱敏前后状态,不保存 `secret_ref`
- `financial_connector_events`:方向、事件类型、外部事件 ID、请求指纹、原始内容哈希、发生/接收时间、验证等级、处理状态、关联单据/Case 和错误码UPDATE/DELETE 禁止。
- `payment_reconciliation_cases`Claim、期望/实际金额与币种、匹配状态、差异、处置版本、负责人和最后事件;作为可变投影,历史动作另存事件。
- `payment_reconciliation_events`:创建、自动匹配、人工确认、拒绝、重开、退款和关闭的追加式审计事件,保存请求指纹和首次响应。
- `financial_connector_operational_events``0022`):保存 `replay / auth_failure / payload_conflict` 三类运行事实的 tenant/config/provider/environment、受控原因码、两类 HMAC 指纹、幂等键和发生时间;不保存原始 payload、签名、外部事件 ID、Claim 引用、correlation 或密钥材料。复合租户外键防止跨租户归属PostgreSQL 触发器禁止 UPDATE/DELETE存在运行事实时拒绝有损降级。
关键约束:
- `(tenant_id, provider, external_event_id)` 唯一。
- 配置从 `disabled/version=1` 创建;激活和停用必须命中 expected version轮换原子地产生新 active key version 并把旧版本置为 rotating。
- 同幂等键不同 payload hash 返回冲突,不能覆盖首次事件。
- 同一个运行事实 candidate 的补偿写入按 `(tenant_id, idempotency_key)` 幂等;不同 HTTP 尝试即使请求内容相同,也因可信发生时间不同而分别计数,避免把真实重放次数永久压成一次。
- Claim、Case、配置和对账记录使用复合租户外键。
- 退款/冲回必须引用同租户原结算事件。
- 生产事件必须通过已激活密钥验证test/mock/staging 即使签名和业务字段全部匹配,也只能生成 `projection_scope=simulation_only` 的连接器事实,绝不进入核心财务状态机。
### 接口
- `POST /api/v1/integrations/financial-events`:连接器签名事件入口;返回稳定接收/重放结果。
- `POST /api/v1/financial-connectors/admin/tenants/{tenant}/configs/{id}/activate|disable|rotate`带版本、actor、request ID 和 reason 的配置状态机。
- `POST /api/v1/financial-connectors/admin/tenants/{tenant}/configs/{id}/simulate`平台管理员运行确定性非生产场景production 配置和未激活配置 fail-closed。
- `GET /api/v1/financial-connectors/admin/tenants/{tenant}/config-events`:读取不含密钥材料的配置审计时间线。
- `GET /api/v1/financial-connectors/admin/tenants/{tenant}/observability`:平台管理员读取目标租户脱敏运行指标;从 config/event/reconciliation/operational event 聚合指定窗口真实值。
- `GET /api/v1/financial-connectors/observability`:财务角色读取当前租户脱敏运行指标;返回 `window_started_at / as_of / generated_at / source_revision`,以及 replay、认证失败、签名失败、payload conflict 的真实计数与最近发生时间。`0022` 起四类指标均标记 `available`,没有事实时真实返回零而不是“待采集”占位。
- `GET /api/v1/financial-connectors/payment-evidence/{claim}`:有单据读取权限的当前租户用户读取付款证据等级,不返回原始连接器内容。
- `GET /api/v1/financial-reconciliation/cases`:财务角色分页查看匹配与异常。
- `GET /api/v1/financial-reconciliation/cases/{id}`:查看脱敏证据和追加式时间线。
- `POST /api/v1/financial-reconciliation/cases/{id}/confirm`:独立财务确认差异或人工匹配。
- `POST /api/v1/financial-reconciliation/cases/{id}/reject`:拒绝错误回执并记录原因。
- 连接器配置管理接口仅平台管理员可用,业务确认接口仅财务角色可用,两类权限不互相继承。
### 匹配算法
1. 验证 tenant/provider/key version/timestamp/signature 和事件类型HMAC canonical request 同时绑定固定 HTTP method/path禁止共享密钥跨 provider 或 key version 重放。
2. 以 canonical JSON 生成 payload hash按外部事件 ID 与幂等键检查首次请求。
3. 通过受控引用解析 Claim不允许仅凭模糊姓名或备注自动匹配。
4. 校验 Claim 已完成审批且处于待付款;比较金额、币种、外部业务引用和事件方向。
5. 只有 `production_verified` 且完全一致时才自动 matched非生产来源只生成模拟投影任何生产差异进入 exception 且不改变 Claim。
6. matched 事件在同事务调用付款动作、记录 Business Event并把外部事件哈希作为 Savings 证据。
7. ERP posted 只表示入账不重复触发付款refund/reversal 追加负向业务与 Savings 冲回候选。
### 权限与安全
- 连接器入口不使用普通用户会话,使用租户绑定的签名认证;普通 Bearer token 不能伪装连接器。
- HMAC/签名比较使用常量时间函数,限制时间窗口并记录 nonce/外部事件 ID 防重放。
- 激活和轮换前由服务端解析 `secret_ref` 并校验至少 128-bit HMAC 密钥;数据库和审计 DTO 均不返回引用或明文。
- 日志、响应和 DTO 最多暴露外部引用后八位或不可逆摘要,不返回原始 payload、签名或密钥。
- replay 随成功重放事务提交;认证失败与 payload 冲突先回滚失败业务事务,再独立提交脱敏运行事实,审计写入异常不得覆盖原始 401/409 响应。
- 只有服务端按 tenant/provider/key version 解析出 active 配置,并成功解析该配置的服务端密钥后,才构造可归属的 operational context。缺认证头、请求 tenant 不一致、未知/未激活配置或密钥不可用均不接受客户端自报租户,只写不带租户归属的结构化警告;签名、时间窗和事件白名单失败才可安全归入已解析配置。
- 运行表只保存 `hmac-sha256:` 请求/外部事件指纹和 `sha256:` 幂等键。HMAC 输入可以包含外部 ID 和业务引用但这些原值不会进入表、DTO 或错误日志。
- 不可变连接器事实的 normalized payload 不再保存完整 `claim_reference``0020` 受控迁移会删除历史冗余值,保留内容哈希、受控 Claim ID 与必要尾号。
- 跨租户资源统一按 404 隐藏;配置管理员不能确认对账,付款申请人不能确认自己的异常。
- 连接器故障、未知密钥、签名异常、金额/币种不一致和数据库异常均 fail-closed。
### 状态转换
- 连接器事件:`received → verified → processed`,失败进入 `rejected`;事实本身追加只读。
- 连接器配置:`disabled → active → rotating → disabled`;正常启用走 `disabled → active`,轮换时旧 active 原子进入 rotating、新 key version 以 active 创建。
- 对账记录:`pending → matched | exception → confirmed | rejected`;退款可从 confirmed 进入 `reopened`,重新处置后关闭。
- Claim 仅在可信 `payment_settled + matched` 后从 `pending_payment` 进入 `paid`
- ERP 凭证从 `pending_posting` 进入 `posted | posting_failed`,不反向伪造支付成功。
### 降级策略
- 连接器离线:保留待付款,不自动标记已付;显示积压和最后成功时间。
- 回执乱序:先保存事实,等待前置事件或进入 pending不猜测状态。
- 回执冲突:保留首次事实并返回 409生成对账异常。
- ERP 未接入:付款事实可进入已付,但“已入账/凭证号”保持待采集。
- test/mock/staging返回 `simulation_only`,只保留追加式 connector fact/response projection不创建对账 Case、不修改 Claim/ERP/归档/Savings也不进入生产现金证明。
- 显式 mock adapter 只能选择预定义场景和当前租户 Claim事件 ID、correlation 和受控外部引用由 tenant/config/scenario/request ID 确定性派生。重复执行同一请求只产生稳定重放,不能借模拟接口注入生产配置或任意字段。
- 升级前若已有非生产事件且响应曾关联对账 Case`0020` 将其标记为 `legacy_nonproduction_effect_unknown` 供审计,不伪装成新策略下的无副作用模拟事实,也不在迁移中猜测性冲回历史财务状态。
### 兼容策略
- 保留现有人工“确认已付款”作为低等级内部证据;付款证据 DTO 使用 `internal_manual_payment`,生产连接器使用 `external_cash`,非生产连接器使用 `simulated_connector`/`staging_connector`。UI 必须同时展示来源标签和可信等级,不能只显示“已付款”。
- 新连接器路径复用现有幂等付款动作、Case 时间线和 Savings 实现服务,不复制第二套状态机。
- 现有 `risk_flags_json` 付款摘要继续只读兼容,新连接器事实进入正式事件表。
## 测试方案
- 单元签名、时间窗口、canonical hash、幂等重放、冲突、乱序和字段白名单。
- 权限:跨租户、普通用户伪造、管理员越权、申请人自证和密钥停用。
- PostgreSQL复合外键、唯一键、append-only、并发同事件、不同 payload 冲突和安全降级。
- 集成:申请 → 票据 → 报销 → 预审 → 审批 → 外部付款 → 对账 → ERP 入账 → 归档 → Savings 待确认。
- 反向:支付失败不推进、金额/币种错配不推进、重复回执只写一次、退款追加冲回。
- adapter逐场景验证确定性结果、重复请求、跨租户 Claim 隐藏、production 拒绝,以及 Claim/对账/ERP/Business Event/Savings 零副作用。
- 可观测性:验证租户隔离、财务/管理员权限、窗口边界、签名失败与重放计数,并断言 DTO/日志无原始 payload、签名和密钥。
- 运行事件:验证同 candidate 并发/补偿重试单赢家、不同请求尝试分别计数、冲突回滚后独立持久化、复合租户外键、HMAC 格式检查和数据库 append-only。
- 所有验证只在 `local-x-financial-linux` 容器内执行,每条命令最长 60 秒。
## 算法与公式
本能力不做概率预测,核心是确定性门禁:
```text
canonical_effect_allowed = (
verification_level == production_verified
AND signature_valid
AND tenant_provider_key_path_bound
AND claim_amount_currency_reference_match
)
```
任一条件为假都不得产生核心财务副作用;非生产环境无论其他条件是否为真,`canonical_effect_allowed` 固定为 false。
运行指标使用确定性窗口聚合,不从应用日志估算:
```text
operational_count(type, window) = COUNT(
tenant_id = current_tenant
AND event_type = type
AND window_started_at <= occurred_at <= as_of
)
operational_idempotency_key = SHA256(
source_revision, tenant, config, type, reason,
HMAC(request), HMAC(external_event), occurred_at
)
```
`source_revision=20260716_0022` 表示当前运行指标的数据源与聚合契约版本;它不是 provider 协议版本。发生时间属于本次接收尝试,因此同一 candidate 重试仍稳定,而新尝试会形成新的真实计数。
## 指标与验收
- 100% 外部结算事件具有租户、来源、签名版本、payload hash、外部 ID 和接收时间。
- 重复相同事件稳定重放,冲突 payload 100% 拒绝。
- 任何金额/币种/Claim/审批状态不一致均不会推进已付款。
- 外部支付、ERP 凭证、对账处置、归档和 Savings 证据可由 correlation 链回放。
- 连接器离线或异常时不出现虚假“已付款”“已入账”或现金节省。
- observability 响应 100% 标明窗口起止和 source revision三类运行事实按租户真实计数并提供各类最近发生时间。
## 风险与开放问题
- 首期真实 provider、签名算法、事件字段和 SLA 需要目标客户确认。
- 部分 ERP 只有批次级凭证,需要明确批次到单据的拆分和舍入规则。
- 多币种付款需要锁定汇率来源和会计期间;本功能不自行猜测汇率。
- 退款、补付、员工自担调整是否进入现金节省,仍需客户财务签字口径。
- 对账大额阈值及双人复核需按企业策略配置。
## 本轮实现记录
- 2026-07-16完成现有内部付款、申请归档、Business Event、Savings 实现链路盘点,并冻结统一连接器、安全认证、对账状态和完整 E2E 方案;实现与容器证据保留在同目录 TODO 中继续执行。
- 2026-07-16完成 `0017` 四表迁移、HMAC/密钥版本/时间窗认证、租户隔离、追加式事件、幂等冲突和对账投影;一次性 PostgreSQL 17 迁移循环与 2 项并发探针通过。
- 2026-07-16支付结算只在金额、币种、Claim、审批状态与来源全部匹配时复用正式付款动作ERP、失败、退款/冲回、Savings 失效和财务处置均进入同一 correlation 审计链,连接器服务/HTTP 7 项通过。
- 2026-07-16真实 provider、批次拆分、汇率、会计期间和大额双人复核仍由目标客户确认当前 mock/test 证据始终标记 simulated不冒充生产现金事实。
- 2026-07-16补齐 `0020` 配置版本与追加式审计迁移;新配置只能停用创建,激活前校验服务端密钥,轮换原子切换 key versionHTTP 时间线不暴露 `secret_ref`
- 2026-07-16把 test/mock/staging 六类事件收口到 `simulation_only` 只读投影生产事件继续完成付款、ERP、归档、Savings 和冲回,生产冲回也不能引用模拟原事件。
- 2026-07-16normalized payload 删除完整 `claim_reference`,历史冗余值由 `0020` 受控脱敏HMAC v2 继续以内容哈希和 tenant/provider/key version/path 证明请求边界。
- 2026-07-16容器内连接器/配置/费用价值链组合 16 项、PostgreSQL 并发 3 项、迁移静态 124 项及全新 PostgreSQL 17 完整升降级循环通过;独立 0019→0020 探针确认版本回填、历史脱敏和 legacy 不确定性标记符合契约。
- 2026-07-16新增显式非生产 adapter以 tenant/config/claim/scenario/request ID 派生稳定签名回执,覆盖成功、失败、乱序、重复、冲突、退款和 ERP 场景HTTP 与服务测试确认 simulation-only 且 Claim、对账、Business Event、ERP 与 Savings 零副作用。
- 2026-07-16新增租户级脱敏可观测性 API 与财务看板面板,现有 config/event/reconciliation 表提供最后成功、失败率、积压和对账异常真实值retry/auth_failure 在 `0022` 运行事件落地前明确显示 unavailable不伪造零值。
- 2026-07-16新增付款证据等级 DTO 和 UI 口径,通过 production-mode 签名契约的回执分类为 `external_cash`,人工确认标为低等级 `internal_manual_payment`,模拟/预发布回执明确不进入核心账;当前本地自签测试不作为真实现金或 provider 接通证据。
- 2026-07-17完成 `0022` 追加式运行事实迁移与服务接入,耐久记录 replay、可信归属后的 auth failure 和 payload conflict请求与外部事件只保存 HMAC 指纹,失败事务回滚后独立补偿提交。
- 2026-07-17可观测性改为返回 `20260716_0022` source revision、明确窗口、真实计数和最近时间同一 candidate 补偿重试幂等,不同 HTTP 尝试分别计数。迁移总头继续串到既有 `0023``0022` 不越界创建后继 AI 表。
- 2026-07-17全新一次性 PostgreSQL 17 完整迁移循环 51 项、连接器并发 4 项、后继盲审并发 7 项通过验证租户复合外键、HMAC 格式、运行事实 append-only 和并发单赢家。