支付系统设计:对账处理(二)
可以说,对账是支付系统最头疼的事情。每一笔交易,都要做到各参与者的记录能够吻合,没有偏差。对账系统的工作,是发现有差异的记录,即轧帐;然后通过人工或者自动的方式,解决这些差异,即平帐。
对电商系统来说,每一笔交易,在所有相关主体侧都要能对得上:
- 交易主体,如果发起人是个人,必须能够从个人交易历史记录中找到这笔交易。但大部分人不会保留电子记录,所以一般是提供可以下载的账单或交易记录,让用户自己对去。
- 交易对手,一般是商户。商户侧对账处理同用户侧,也仅仅提供对账单。
- 交易渠道侧,这是对账的重点,一是核实交易流水,二是核实交易佣金,毕竟是租用人家通道做结算的。
那有哪些记录需要对账? 目前主要是两个:一个是交易记录;一个是退款记录。
对账处理流程
一般来说,对账流程涉及到如下步骤: 渠道对账单下载、本地交易记录准备、轧账、平账。
渠道对账单下载
银行,第三方支付,银联等,基本都会提供对账单下载的功能。不过也有少数工作做不到位或者太到位的银行,只提供账单查询后台,不提供对账单下载功能。
对开发人员来说,这里有几个坑:
- 对账单格式不一。文本,XML,csv的都有。为了后续能够统一处理,在账单下载完成后,需要进行标准化处理。
- 下载方式不一,HTTP,HTTPS,FTP的,都有。下载程序需要按照渠道的协议来处理。
- 下载时间不一,一般是凌晨1点后,到中午12才能用的也有。如果在预定的时间取不到数据,需要注意重试读取。
- 稳定性差。FTP服务器出问题那是常有的事。渠道侧解决方案往往就是重启。所以重试机制是必要的。
看一下第三方支付的对账单情况:
银行直连的对账情况:
技术选型上,HTTP(S)用apache httpclient即可实现链接池和断点续传, FTP也可以使用Apache Commons Net API。 但不管是哪一个,都需要设置重试次数和链接超时间。重试次数和间隔的设置需要小心,重试太频繁,容易把服务器打死.;时间间隔太大,又会阻塞后续处理步骤。5~10分钟是一个合适的重试间隔区间。
链接超时指在服务器出现问题时,连接在指定时间内获取不到数据即自动断开。这个很容易被忽略。我们有一次系统出问题,是渠道侧的FTP假死后重启,导致我们的客户端挂住,一直在等待重新链接。
渠道对账单标准化
找个例子大家看看, 比如微信的对账单,他是csv格式的,包括如下信息:
- 交易时间:这是在微信侧的支付完成的时间。 这个时间会成为一个陷阱。
- 公众账号ID,商户号,子商户号,设备号: 这些信息需要做验证,确保是自己的单子,不要让微信把老王家的单子也给发过来了;
- 微信订单号,商户订单号: 这两个是对单的核心。前者是微信侧产生的订单号,在微信支付接口返回值中有。但是万一收不到这个返回值,那在本地记录中可能就空了。 后者是我们发送给微信的订单号,一般用这个来做对单依据。两边的数据中都会有这个值。
- 用户标识,交易类型,交易状态,付款银行,货币种类,总金额,企业红包金额: 这几个就是对单的核心字段,必须确保双方是一致的。
- 商品名称,商户数据包,手续费,费率:这些是可选验证。
而某宝的对账单,是文本格式的,用空格隔开。他们家的就简单很多,只有商户订单号,交易流水号,交易时间,支付时间,付款方,交易金额,交易类型,交易状态这些字段。
由于每个渠道的账单格式都不尽相同, 在得到账单后,下一步是对账单做标准化处理,这样轧帐以及后续工作就可以统一处理了。 标准化后的账单数据可以放在文件系统或者数据库中。这取决于交易数据量。每天百万以上的量,还是使用文件系统,比较合适。数据库操作相对比较慢,也浪费资源。
基于文件系统的标准化涉及如下内容:
- 文件格式标准化:统一使用csv或者json或者xml格式。如果是使用hadoop或者spark来对账,使用csv是个不错的选择。
- 文件存储统一化:文件目录,文件名都需要遵循统一命名规范。
为了加快处理速度,我们使用hdfs作为文件系统,有利于后续的对账的处理。
本地交易记录准备
本地交易记录的准备,总的来说有如下方法: – 啥都不做,直接用原始数据。鉴于大部分系统使用的是mysql,这也意味着在MySQL上做对账。对账时需要大量的数据查找工作,必然会影响线上业务。在数据规模较大,比如超过100万时,就不太合适了。
- 当然,还有一个选择是使用备库来执行对账,这样既简单,也不影响线上业务。这是典型的空间换时间的做法。
- 如果业务大到需要分表分库才能处理,那对账数据准备也不一样。使用分库也不现实,因为分库一般是按照主体id,而不是渠道id,来分库,这样对账就需要在多个库上进行,效率反而降低了。而对分表分库建立从库也非常耗费资源。这种情况下,需要同步一份数据到(hdfs)文件系统中,或者NOSQL数据库上。
由于交易记录是支付系统核心数据,有大量的应用,如信用、风控等,都需要交易记录数据。这些应用对交易记录的需求还不完全一致,为了提升性能, 交易记录会使用异步的方式来将数据投递给使用方。 交易记录在入库时,投递消息到消息系统中。使用方监听这个消息,一旦收到新消息,则从交易记录库中查询数据,获取数据并更新到库中。关于此类数据同步的文章不少,这里就不详细介绍。
轧帐
轧帐是按照客户订单号来比较本地交易记录和渠道交易记录是否一致。从算法角度,是计算两个数组的差异。在单机运行时,可以采用的算法不少,这里不详细介绍。 我们推荐采用mapreduce来轧帐,这有个优势,可以按照订单号将渠道提供的记录和本地记录shuffle到同一个reduce处理上,这样就可以很容易进行数据比对。 轧帐中最大的坑,莫过于切分点的问题。
比如以整0点为切分点,那存在一个问题,本地23:59发起的交易,到了渠道侧,可能会在00:01处理,这一笔交易变成第二天的帐了。实际处理中,一笔交易在渠道侧处理,花上几分钟都有可能。 对于切分点附近无法确认的帐,做一个时间窗,在时间窗内的数据,留待第二天对账时继续处理。
平帐
发现两边不一致的数据,那应该如何处理?数据量不大时,记录起来,人工甄别就行。但如果数据量很大,每天上千条,人工处理就成本太高了。这个没有统一的处理方法,需要根据有问题的数据,做个分析,然后做自动处理。 针对交易记录的对账的处理,主要有如下情况:
- 本地未支付,支付渠道已支付。这主要是本地未正确接收到渠道下发的异步通知导致。 一般处理是将本地状态修改为已支付,并做响应的后续处理,比如通知业务方等。
- 本地已支付,支付渠道已支付,但是金额不同,这个需要人工核查。
- 本地已支付,但是支付渠道中无记录;或者本地无记录,支付渠道有记录。在排除跨日因素外,这种情况非常少见,需要了解具体原因后做处理。
针对退款的对账处理,主要有如下情况:
- 本地未退款,支付渠道已退款,则以支付渠道为准,修改本地为已退款状态,并触发后续处理。
- 本地已退款、支付渠道已退款,但是金额不同,需要人工核查;
- 本地已退款,但是支付渠道无记录;或者支付渠道有记录,但是本地没有。 在排除跨日因素外, 这种情况非常少见,需要了解具体原因后做处理。
总之,对账工作,即复杂也不复杂。需要细心,对业务要有深入的了解,并选择合适的架构。
相关阅读
作者:凤凰牌老熊,程序员 & 架构师,来自中科大的本科,研究生在软件所学习。先后在中科辅龙、三星(中国)研究院和国内一些大型的互联网公司呆过。在中科辅龙公司负责电子政务内容管理系统建设,负责研发龙驭系列产品的研发,这款产品最终实施到2000多个电子政务网站上,期间也参与了一些支付反洗钱以及支付系统的建设。之后在三星中国研究院,负责自然语言处理(NLP)以及智能家居相关项目。智能家居项目在2014CES消费电子展上作为三星重点项目推介。2014年开始加入爱奇艺公司,负责数据仓库和支付系统的建设。
本文由@凤凰牌老熊(微信公众号:shamphone) 原创发布于人人都是产品经理 。未经许可,禁止转载。
消费券的发放和使用
1. 在收到支付成功回调后保留渠道支付成功时间,对账时采用渠道时间来进行对账;
2. 如果渠道侧不提供成功时间,对账时采用平台时间与渠道时间进行对账,对账时平台长款时间在接近日切点时可放到第二日进行对账,如渠道长款可根据渠道ID到我平台支付表中查询,进行差错状态细化,有可能因为时间的差异此笔交易已成功;
3. 如果交易数据量大,可采用分批对账,MAPA – MAPB 流水排序交易,已10万数据分页查询为例先消耗完的数据进行下次分页查询,差异先统一存储,所有数据对账后进行整体处理;
4. 对账产生的差异应针对渠道或平台账户进行资金冻结,来保证对账数据与账户数据一致性;
很有条理 很有用!干活满满
请问现在对账思路都是 人工上传账单或者自动获取账单,然后找到业务订单去对账(账单-订单对账),是否还有别的对账思路呢
请问,现在第三方支付都要统一接入网联,这些功能是否还需要系统自己实现呢
简书上有人转载你的文章,有标明转载地址。是你自己么?还是朋友?http://www.jianshu.com/u/897526abf799
感谢 感谢,良心干货篇,多谢
对于一个即将转入支付系统的程序猿, 看到你的文章如沐春风, 感觉业务思路上清楚多了,多谢讲解
我也是支付行业,很是喜欢你的笔记和感悟,对一个踏入新行业不久的新人来说,是一件值得庆幸的事情。
我想咨询下,像这种交易单的下载通过支付宝,微信,银联借口就能直接下载获取吗?还是说是需要财务人员到相关的平台将相关的账单导出放到FTP,程序再定时获取与本地的订单数据来做核对呢?
请教一下:交易流水表,如何按照交易流水号来做分库、分表的依据。这样需要查询某个用户的所有交易流水,由于是按照交易流水切分,数据会分散在多个库、多个表。展示起来,就需要跨库、跨表查询了。尤其是,要分页展示某个用户所有的交易流水。在支付宝上有。求教作者这方面,怎么处理?
按照用户来分表分库,流水号根据用户ID来生成。
对于切分点附近无法确认的帐,做一个时间窗,在时间窗内的数据,留待第二天对账时继续处理。
–请教一下,这个时间窗如何设置?能大概讲下?谢谢
点赞,好文。
“轧帐中最大的坑,莫过于切分点的问题”,这个对于没做过的人,真的不见得注意到这个坑;
想起我们做通信的时候,一次正常的呼叫,有很多信令记录点,比如起呼是一个信令记录点,在23:59分发起; 接通是一个信令点,在00:01分发生; 这样,起呼就在上一个时间点,接通在下一个时间点,当我们按小时来统计接通率的时候,这就悲剧了;
这个怎么统计比较好啊
前辈,解决切分点问题的时间窗是指什么呢?
好文!但是发现一个小小的瑕疵,“本地未退款,支付渠道已退款,则以支付渠道为准,修改本地为已退款状态,并出发后续处理。”这个地方是不是应该为“触发”。
谢谢, 是笔误。
以表敬意
太棒了,居然有一系列的分享,大神就是这样
看到以前自己天天面对的业务,哈哈,还有些怀念呢。
现在面对什么业务
时髦的O2O,线下智能POS与线上结合
好文,希望能写的更深入点,多举一些实际场景 😉
良心文章
谢谢。