# 财务连接器与支付对账闭环 概念文档

更新时间：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 version，HTTP 时间线不暴露 `secret_ref`。
- 2026-07-16：把 test/mock/staging 六类事件收口到 `simulation_only` 只读投影；生产事件继续完成付款、ERP、归档、Savings 和冲回，生产冲回也不能引用模拟原事件。
- 2026-07-16：normalized 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 和并发单赢家。
