Add tenant-safe value, telemetry, connector, commercial, and production-readiness foundations.
21 KiB
21 KiB
财务连接器与支付对账闭环 概念文档
更新时间: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:拒绝错误回执并记录原因。- 连接器配置管理接口仅平台管理员可用,业务确认接口仅财务角色可用,两类权限不互相继承。
匹配算法
- 验证 tenant/provider/key version/timestamp/signature 和事件类型;HMAC canonical request 同时绑定固定 HTTP method/path,禁止共享密钥跨 provider 或 key version 重放。
- 以 canonical JSON 生成 payload hash;按外部事件 ID 与幂等键检查首次请求。
- 通过受控引用解析 Claim,不允许仅凭模糊姓名或备注自动匹配。
- 校验 Claim 已完成审批且处于待付款;比较金额、币种、外部业务引用和事件方向。
- 只有
production_verified且完全一致时才自动 matched;非生产来源只生成模拟投影,任何生产差异进入 exception 且不改变 Claim。 - matched 事件在同事务调用付款动作、记录 Business Event,并把外部事件哈希作为 Savings 证据。
- 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 秒。
算法与公式
本能力不做概率预测,核心是确定性门禁:
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。
运行指标使用确定性窗口聚合,不从应用日志估算:
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_0022source revision、明确窗口、真实计数和最近时间;同一 candidate 补偿重试幂等,不同 HTTP 尝试分别计数。迁移总头继续串到既有0023,0022不越界创建后继 AI 表。 - 2026-07-17:全新一次性 PostgreSQL 17 完整迁移循环 51 项、连接器并发 4 项、后继盲审并发 7 项通过;验证租户复合外键、HMAC 格式、运行事实 append-only 和并发单赢家。