Frank
发布于 2026-10-22 / 8 阅读
0
0

GlobalPay Lab:从0到1设计一个 B2B 跨境支付平台

项目状态:持续开发中 · 项目性质:技术学习与模拟环境(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 中维护本地事务一致性,也避免在业务模型尚未稳定时就引入微服务、分布式事务和过多运维成本。

mermaid-diagram.png

图 1:GlobalPay 目标架构。图中的模块不代表目前均已实现。

这张架构图可以按四层理解:

  1. Web 应用层:商户查看付款、余额与记录;运营人员处理对账和异常。

  2. API 层:负责对外提供 REST API,以及认证、鉴权、输入校验等共性能力。

  3. 业务层:Payment 负责流程编排,Ledger 负责财务入账,Wallet 提供余额视图,Reconciliation 负责核对数据。

  4. 基础设施层: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。不过首版只实现必要的界面,不会同时做三个完整后台。

四、一笔跨境出款如何完成?

支付处理并不是“调用一次接口,然后把状态改成成功”。尤其在对接外部通道时,请求可能超时,但通道实际上已经处理成功;同一个回调也可能被发送多次。

mermaid-diagram (1).png

图 2:简化后的 Payout 生命周期。未知结果不能直接当作失败处理。

这一流程至少要解决以下工程问题:

  • Idempotency(幂等):同一个请求反复提交,不能创建重复的付款。

  • State Machine(状态机):状态只能按照合法路径转换;迟到的通知不能随意覆盖终态。

  • Concurrency Control(并发控制):两笔并发出款不能重复占用同一份余额。

  • Transactional Consistency(事务一致性):交易状态与账务写入之间需要清晰、一致的提交规则。

  • Failure Recovery(故障恢复):外部通道超时、重复通知以及本地重试都需要可恢复的策略。

五、Ledger:为什么要先做复式记账?

对支付系统来说,交易成功和账务正确是两件不同的事。

如果系统只有 payment.status = SUCCESS,却不能准确解释资金从哪个账户转移到了哪个账户,那么它仍然缺少重要的金融基础设施能力。

我计划以 LedgerAccount、JournalEntry 和 JournalLine 作为最基本的账务模型。

mermaid-diagram (2).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:为什么支付成功后还需要对账?

支付平台内部记录和外部支付通道记录,可能因为通知延迟、系统故障或金额不一致而出现差异。仅仅依赖支付成功通知,无法确保双方记录始终一致。

mermaid-diagram (3).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 服务

当前最实际的工作,是在已有领域模型基础上补全数据持久化与事务边界:

  1. 使用 Flyway 创建账务账户、Journal 与分录表。

  2. 实现 Repository 和事务性的 Journal Posting Service。

  3. 校验账户存在性、币种一致性、借贷平衡和入账不可变性。

  4. 提供账户查询、创建与 Journal Posting API。

  5. 在 PostgreSQL 上运行集成测试,验证提交、回滚及错误处理。

只有把 Ledger 的正确性建立起来,后续的支付编排、资金预留和自动对账才有可靠的基础。

结语

对我来说,GlobalPay Lab 不只是一个用来练习 Spring Boot 的项目。它是我理解金融业务、支付基础设施和后端系统设计的一种实践方式。

我希望最终能够独立回答这样的问题:一笔付款到底经历了什么?什么才算资金结算完成?系统怎样避免重复扣款?账本为什么可信?当平台和通道的数据发生冲突时,应该如何发现并解释?

我会在持续开发过程中同步更新这篇文章,让文档、架构图和代码逐渐保持一致。


说明:本文展示的是 GlobalPay Lab 的持续演进架构。项目属于个人技术学习与模拟环境,不处理真实客户资金,不提供实际支付服务,文中的规划功能不代表已经上线。


评论