项目状态:持续开发中 · 项目性质:技术学习与模拟环境(Sandbox) · 不涉及真实资金交易 项目代码: GitHub — GlobalPay Lab · dev 分支
写在前面
在日常生活中,付款似乎只需要几个步骤:填写金额、确认收款人、点击支付,然后等待成功通知。但当付款场景从个人消费变成企业跨境业务时,背后其实是一整套复杂的系统:支付指令、账户余额、异步通道、交易状态、账务分录、结算、对账,以及出现异常后的追踪与处理。
GlobalPay Lab 是我正在开发的金融科技工程项目。我希望以一个 B2B 跨境支付平台为场景,亲自设计并实现这些核心能力,理解支付业务与软件架构之间的关系。
项目在产品层面参考了 Airwallex 和 Stripe 等支付基础设施平台的部分设计思想,但它不是这些产品的复刻版,更不是实际提供跨境汇款服务的持牌机构。现阶段的所有账户、资金和支付通道都是模拟环境。
这篇文章是一份持续更新的项目介绍与系统设计记录。我会同时说明计划中的目标架构和目前实际完成的功能,避免把技术蓝图误认为已交付的产品。
一、产品定位:GlobalPay 想解决什么问题?
我为项目设定的核心场景是:一家位于澳大利亚的中小企业,需要向海外供应商付款,并希望在同一个平台追踪支付、管理资金记录以及核对交易结果。
例如,一家澳大利亚企业要向日本供应商发起一笔付款。在未来的 GlobalPay 模拟平台中,用户可以创建出款指令,查看模拟手续费与报价,提交支付请求,追踪其处理状态,并最终核对平台账务记录和通道结算文件。
这里有几个需要明确区分的概念:
Payment / Payout:支付与出款不是完全相同的业务。项目首个 MVP 聚焦模拟企业出款(Payout),不急于同时实现银行卡收单。
Ledger:记录资金变动的复式记账系统,不能只靠一张交易状态表替代。
Wallet:面向用户的账户余额与可用资金视图,其数值需要能够与底层账务核对。
Reconciliation:将平台内部记录与模拟支付通道的结算记录进行匹配,发现缺失、重复或金额不一致的问题。
MVP 的边界
首个 MVP 要做 | 首个 MVP 暂时不做 |
|---|---|
单币种模拟企业出款 | 真实银行及卡组织清算 |
复式记账与账户余额 | 实际托管或转移客户资金 |
模拟通道及支付状态管理 | 真实外汇交易 |
CSV 结算文件自动对账 | 正式持牌支付服务 |
核心交易监控与异常追踪 | 覆盖全球所有支付方式 |
首版先从 AUD 单币种出款 + 可靠 Ledger 开始,再逐步扩展到多币种账户和 FX。这是刻意控制复杂度,而不是放弃完整的产品愿景。
二、总体系统架构
我选择 Modular Monolith(模块化单体) 作为项目现阶段的架构:整个后端先作为一个 Spring Boot 应用部署,但内部按照清晰的业务边界划分模块。
这样既方便在 PostgreSQL 中维护本地事务一致性,也避免在业务模型尚未稳定时就引入微服务、分布式事务和过多运维成本。

图 1:GlobalPay 目标架构。图中的模块不代表目前均已实现。
这张架构图可以按四层理解:
Web 应用层:商户查看付款、余额与记录;运营人员处理对账和异常。
API 层:负责对外提供 REST API,以及认证、鉴权、输入校验等共性能力。
业务层:Payment 负责流程编排,Ledger 负责财务入账,Wallet 提供余额视图,Reconciliation 负责核对数据。
基础设施层:PostgreSQL 保留持久化状态,模拟通道用于验证异步成功、失败、超时和重复通知等情况。
未来会考虑 Transactional Outbox 和异步 Worker;但在早期,我不会仅为了“架构先进”就引入 Kafka 或把每个模块拆成一个微服务。
三、核心业务模块
模块 | 主要职责 | 优先级 |
|---|---|---|
Merchant / Identity | 企业、用户、权限与数据隔离 | 基础能力 |
Payment / Payout | 支付指令、状态机、幂等、通道交互 | MVP 核心 |
Ledger | 账务账户、借贷分录、入账规则、余额核算 | MVP 核心 |
Wallet | 可用余额、预留资金、余额视图 | MVP 核心 |
Reconciliation | 结算文件匹配、差异识别、异常处理 | MVP 核心 |
Events / Webhooks | 事件投递、签名验证、重试与日志 | 后续迭代 |
FX | 模拟报价、币种转换和跨币种账务 | 后续迭代 |
Risk | 限额检查、规则与人工审核 | 后续迭代 |
产品层面,我希望逐步形成三个界面:面向企业用户的 Merchant Portal、面向内部团队的 Operations Console,以及面向 API 接入方的 Developer Portal。不过首版只实现必要的界面,不会同时做三个完整后台。
四、一笔跨境出款如何完成?
支付处理并不是“调用一次接口,然后把状态改成成功”。尤其在对接外部通道时,请求可能超时,但通道实际上已经处理成功;同一个回调也可能被发送多次。
.png)
图 2:简化后的 Payout 生命周期。未知结果不能直接当作失败处理。
这一流程至少要解决以下工程问题:
Idempotency(幂等):同一个请求反复提交,不能创建重复的付款。
State Machine(状态机):状态只能按照合法路径转换;迟到的通知不能随意覆盖终态。
Concurrency Control(并发控制):两笔并发出款不能重复占用同一份余额。
Transactional Consistency(事务一致性):交易状态与账务写入之间需要清晰、一致的提交规则。
Failure Recovery(故障恢复):外部通道超时、重复通知以及本地重试都需要可恢复的策略。
五、Ledger:为什么要先做复式记账?
对支付系统来说,交易成功和账务正确是两件不同的事。
如果系统只有 payment.status = SUCCESS,却不能准确解释资金从哪个账户转移到了哪个账户,那么它仍然缺少重要的金融基础设施能力。
我计划以 LedgerAccount、JournalEntry 和 JournalLine 作为最基本的账务模型。
.png)
图 3:核心 Ledger 概念模型;具体数据库结构后续以 Flyway Migration 为准。
复式记账最重要的约束之一是:对于同一笔已入账的 Journal,借方金额与贷方金额必须相等。多币种情况下还必须按每一种货币分别校验,而不能把 AUD 与 JPY 金额直接相加。
例如在两个 AUD 账务账户之间转移 100 澳元,会生成一条借方 100 AUD、另一条贷方 100 AUD 的分录。两条分录必须在同一个数据库事务中成功提交;任何一步失败,都不能留下半笔账。
另一个关键设计原则是:已入账分录不可随意修改。如需纠错,应当通过明确的冲正或调整交易保留审计轨迹。
目前已经做到哪里?
在本次检查的 GitHub dev 分支中,我已经建立了 LedgerAccount、JournalEntry、JournalLine 等领域类,并编写了一些单元测试,包括借贷平衡、非平衡拒绝、无效金额和入账后限制修改。
但这还只是领域模型的起点。当前检查的代码中尚未发现完整的 Flyway 数据库迁移、Repository、入账 Application Service 和 Ledger REST API。这也是接下来优先实现的部分。
六、Reconciliation:为什么支付成功后还需要对账?
支付平台内部记录和外部支付通道记录,可能因为通知延迟、系统故障或金额不一致而出现差异。仅仅依赖支付成功通知,无法确保双方记录始终一致。
.png)
图 4:自动对账的基本流程。
MVP 会使用模拟通道生成的 CSV 结算数据,以内部交易号、通道交易号、金额、币种和状态等信息进行匹配,并识别缺失、重复及金额不一致的记录。
之后的 Operations Console 将提供异常列表,方便定位问题并记录处理过程。
七、技术栈与选型
层次 | 选型 | 当前情况 |
|---|---|---|
Backend | Java 21、Spring Boot | 项目已初始化 |
Frontend | React、TypeScript、Vite | 已有脚手架 |
Database | PostgreSQL 16 | Docker Compose 已配置 |
Database Migration | Flyway | 已引入依赖,迁移文件待实现 |
Testing | JUnit、PostgreSQL 集成测试 | 已有部分领域单测 |
API | REST / OpenAPI | 业务 API 待实现 |
Async Processing | Transactional Outbox + Worker | 规划中 |
Deployment | Docker Compose | 目前主要用于本地数据库 |
关于架构选型,我目前有三条比较明确的原则。
第一,先做模块化单体,而不是微服务。 这样能集中精力保证交易和账务一致性,降低早期部署、调试与运维成本。
第二,先接入 Mock Provider,而不是真实支付通道。 这样可以重复制造成功、失败、超时和重复回调等场景,方便验证系统是否真的可靠。
第三,先完成 Ledger,再丰富 Dashboard。 前端界面可以快速迭代,但底层账务规则如果一开始就不可靠,后期修正的代价会很高。
八、开发路线与最终交付
我希望 GlobalPay 的每一个版本都具有可运行、可测试、可演示的成果,而不是仅仅增加模块数量。
阶段 | 核心交付成果 |
|---|---|
Phase 1 — Ledger Foundation | 账户模型、数据库迁移、原子入账、余额查询、集成测试 |
Phase 2 — Payment MVP | 幂等出款、状态机、资金预留、模拟通道和支付回执 |
Phase 3 — Reconciliation MVP | CSV 结算导入、匹配规则、异常队列与运营查询 |
Phase 4 — Portfolio Release | API 文档、完整测试、可复现部署、产品演示 |
Phase 5 — Exploration | FX、多币种账户、Webhook、风控、更多真实场景 |
MVP 的验收不能仅以“接口能够返回成功”为标准。我尤其希望验证两个边界条件:重复请求或重复回调不会造成重复记账,以及失败的账务操作不会留下部分分录。
九、下一步:把领域模型变成真正的 Ledger 服务
当前最实际的工作,是在已有领域模型基础上补全数据持久化与事务边界:
使用 Flyway 创建账务账户、Journal 与分录表。
实现 Repository 和事务性的 Journal Posting Service。
校验账户存在性、币种一致性、借贷平衡和入账不可变性。
提供账户查询、创建与 Journal Posting API。
在 PostgreSQL 上运行集成测试,验证提交、回滚及错误处理。
只有把 Ledger 的正确性建立起来,后续的支付编排、资金预留和自动对账才有可靠的基础。
结语
对我来说,GlobalPay Lab 不只是一个用来练习 Spring Boot 的项目。它是我理解金融业务、支付基础设施和后端系统设计的一种实践方式。
我希望最终能够独立回答这样的问题:一笔付款到底经历了什么?什么才算资金结算完成?系统怎样避免重复扣款?账本为什么可信?当平台和通道的数据发生冲突时,应该如何发现并解释?
我会在持续开发过程中同步更新这篇文章,让文档、架构图和代码逐渐保持一致。
说明:本文展示的是 GlobalPay Lab 的持续演进架构。项目属于个人技术学习与模拟环境,不处理真实客户资金,不提供实际支付服务,文中的规划功能不代表已经上线。