本篇文章主要聊聊如何设计千万级别的对账系统。其他一些业务问题可以参看对账清分设计总概览。
目前系统运行的对账总情况:对账日交易量两千万,交易金额50亿,对账时间5分钟以内
本篇文章分两个模块 第一个模块主要文字描述下目前系统对账的流程,第二个模块主要讲述系统对账的发展历程
第一个模块:
支付系统对账主要干些什么事情呢,其实同样的对账系统差不多,每家公司业务不一样,可能引申出来的含义不一样但总体对账流程大同小异。
支付对账系统的设计时,一般将对账系统分为五个模块,每个模块负责自己的职能。
文件下载模块:下载第三方通道的对账单文件,目前主要是微信支付宝银联对账单文件
文件解析模块:创建不同的解析器,根据第三方对账文件解析适配成平台可以处理的对账记录
对账处理模块:对账的业务逻辑处理,根据解析到的第三方对账单与支付订单逐笔比对,这个是核心 第二个模块会详细介绍
差错处理模块:处理上述比对过程的差错数据,针对不同的类型订单进行冲正通知补单等
数据汇总模块: 根据业务将对账数据汇总,比如按商户支付类型汇总做报表查询,同时默认生成一些大商户渠道等对账单文件以方便快速下载
整个功能图:
1、文件下载
目前系统每天定时设置通道任务(任务建立优先大通道 同时任务时间尽量错开),
设置通道任务的目的有两个 一个防止重复对账单下载 另一个需要在对账结束的时候将对账的结果信息保存 防止重复对账,所以这里通道任务状态的修改可以使用数据库乐观锁实现。根据不同的通道(通道主要保存了第三方的信息比如下载方式如ftp/sftp/http),技术实现上使用apache开源工具即可,重试次数和间隔的设置需要小心,重试太频繁,容易把服务器打死.;时间间隔太大,又会阻塞后续处理步骤。5~10分钟是一个合适的重试间隔区间。下载的时候可以使用多线程技术(java可以使用fork/join模型)
文件下载流程:
定时任务在凌晨创建下载任务(优先大通道,时间错开),一般都是生成前一日的对账任务,根据任务下载第三方对账单,下载对账单之前判断本地sftp是否已有第三方对账单,如果有直接从sftp下载读取,如果没有,则根据任务批量多线程方式下载第三方对账单,保存到本地sftp,验签成功后,解析对账单...
2、文件解析
文件解析主要是将下载的第三方对账文件解析成我们可以对账的数据类型并且入库。一般对账文件都是压缩且加密的,所以需要根据不同的对账单做不同的解析,因此也可以设计成不同的解析模板,使用工厂模式将不同格式的文件解析成可以对账的统一数据类型。
对账文件中包含的主要信息有:商户订单号、交易流水号、交易时间、支付时间、付款方、交易金额、交易类型、交易状态这些字段。
4、对账处理
对账处理也是对账的核心逻辑,具体分为以下的几个步骤来实现:
A.查询平台交易的订单
B.查询第三方支付对账单
C.以银行订单为基准对账逻辑:以银行的交易数据为基准,遍历所有平台的交易(包括未成功的订单),找出订单号相同但支付状态不一致的订单,在进行对比金额存入差错池。如果没有在平台的交易中找到此订单,再从缓存池中遍历查找,找到对应的平台订单验证金额是否一致,不一致进入差错池。如果在缓存池汇中依然没有找到对应的订单,直接进入差错池,记录平台漏单。同时统计对账相关金额和订单数。
注意系统设计时 需要考虑对账的可扩展性,比如使用以第三方平台订单为准对账 或者只用本地对账单对账(在第三方对账单没到账之前 临时对账特别有用) 具体来说可以通过通道任务来设置
需要对账:比金额====一致 获取对平的订单的对账结果 --》 分发队列 Queue分发
比金额====不一致 进入异常订单
平台有第三方没有 进入异常订单 平台挂账
本地对账:获取对平的订单的对账结果 --》 分发队列 Queue分发
第三方有,本地没有:代表平台改天没有数据或平台库里都没这笔订单:
成功订单,调用第三方接口去同步订单的状态,时间 金额 并写入数据库继续比对
记录平台少订单的异常记录
5、对账统计
根据对账处理中,统计的相关信息包括:对账完成时间、对账是否成功、平账的金额和订单数、差错的金额和订单数、商户日结数据,商户品牌费等其他信息。
对账统计完成后还需生成本系统对账单文件供平台使用者下载
如何生成平台对账单:根据优先常用的方式。具体方式为通过数据分析针对大商户大渠道取优先下载,商户经常下载的取优先下载, 可以使用redis队列zsort排序,定时器捞出redis队列,提前生成好对账单,如果商户在平台下载对账单时此时没有对账单可以通过异步方式后台下载,下载好后保存到sftp,下载后返回给前端,同时redis队列针对该商户打一分,再第二天对账时根据改队列重新优先级提前生成好当日的对账单文件,这样使用该方式可以有效的减少平台对账单生成过程中对系统的影响
6、差错处理
在一般系统中,差错处理分为两种,一种人工来处理,一种系统自动来处理。
主要有如下情况:
本地未支付,第三方已支付。这主要是本地未正确接收到第三方下发的异步通知导致。 一般处理是将本地状态修改为已支付,mq通知业务方即可。
本地已支付,第三方支付已支付,但是金额不同,这个需要人工核查。
本地已支付,但是第三方支付中无记录;或者本地无记录,第三方支付有记录。前者需要挂账处理 后者是销账处理,具体原因需具体分析
支付订单挂帐:第三方账单今天没有推送这笔订单(这笔订单也不会结算钱给我们),我们就要把这个订单挂帐,并且是不结算(第三方没个我们钱,我们也不能结算给商户)
支付订单销账:第三方账单第N天推送了这笔订单(这笔订单今天的钱给我们了),我们就要把这个订单销账,并且要结算(第三方给了我们钱,我们就要结算给商户、渠道)
第二个模块 讲述本系统从无到有 ,以及一些变更 优化的点
|
当时情况 |
优缺点 |
目标 |
V1.0初期 |
当时考虑就是快速上线 基本考虑的是纯内存+多线程方式 |
优点是快速上线满足目前业务 缺点是针对交易量大时 内存占用过多 |
快速上线 支持订单百万级 交易金额十亿内 对账时间半个钟头 |
V2.0 中期 线上 |
采用通道+商户hash+redis缓存+jdbc分时订单获取方式 |
优点通过通道和商户hash将数据隔离 可以快速扩机多台 缺点是对账时 第三方对账单解析后纯内存 内存还是占用过高 |
支持订单千万级 交易金额五十亿内 对账时间五分钟 |
V3.0 规划 |
解决v2.0问题 同时优化数据库查询 |
减少内存消耗 |
订单通知改用mysql,订单表和订单明细表合二为一 去掉不必要索引 |