本文档从“支付产品技术交流群”的2017年5月26日聊天记录整理出来,感谢@happycoral 同学的整理工作。

一、 主题分享:支付账务系统设计

大家好,先简单自我介绍下,我一直做支付,现在在公司已经7年多了,主要是负责开发、架构、团队等方面的工作。今天就自己理解的账务系统做一个简单的分享,这也是结合自身业务特点的一家之言,比较粗浅,总体而言我觉得账务会计、会员体系、清分对账等是比较复杂的。

1.1 账务系统的设计思路

我们账务系统和会计系统是分开的,账务是基于我们开展的业务而设计,根据支付业务进行账务处理;而会计系统是面向于财务的复式记账。先简单说明下账务系统的上下文环境及系统边界:
20170526_193649

这里面,业务系统不仅包括支付业务的处理系统,还包括会员系统。会员系统在领域模型设计上,主要就是三户模型,当然每个公司的的三户模型可能也不一样。这里表达的意思是会员系统里的账户和账务系统是有个映射关系的,会员的账户对外暴露。

之所以这样设计,还得结合整个支付系统的总体架构,整个支付系统在设计上总体体现了“以账务为核心”的思想,这也是账务系统在整个支付系统的定位。

以账务为核心,最重点的是所有的出入金交易必须记账,记账成功的交易才算成功;记账不成功的,即使调用银行通道接口成功了发生了真实的资金流转也不能算成功,但这就要分情况处理

以账务为核心,还有一个细节是每一笔交易有一个账务成功时间,以账务成功时间来表示哪些交易纳入到清结算范围内。

账务系统在设计的时候,正因为它是资金处理的核心,以账务为准,所以必须要高性能、系统稳定;也是因为这种定位,所以我们设计账务的时候,业务逻辑要尽量简单清晰,单一职责原则,从而保证系统的稳定,不易出错。

大家搞开发的都知道,复杂不可控,因此账务系统是单边记账,而复式记账还得做数据库事务,强一致性怕性能会不理想。处于性能及巨大业务量的考虑我们做出了让步。
有些公司的账务系统设计是完全双边记账;有些则是先记录单边,事后生成另外一边。

账务记录成功了之后,会异步调用会计复式记账,给会计系统提供原始凭证;会计系统根据会计分录进行记账。

账务技术上采用异步回调的方式去调用会计,通过回调函数取得会计的处理结果,来判断会计是否处理成功,从而来尽量的确保会计处理是成功的;如果会计响应失败,比如通信异常之类的,我们会后续将所有异常情况批量处理。

没用mq,因为mq不能返回会计系统的响应结果,或者mq还得弄2个队列来处理,比较麻烦。微服务的设计上,还是将账务会计分开,也是根据账务系统的定位做了取舍。

顺便说下会计系统,会计核算体系里设计了三级会计科目结构;但是会计系统并未包含非支付业务的资金活动,那个不是会计系统的考虑范畴,只包含备付金变化的资金活动,。比如说公司今天采购了100w的服务器设备且已经付款,


借:固定资产 100w,  
贷:银行存款 100w,  

会计系统不管这个,因为和支付业务无关。

1.2 账务系统的具体功能

其实总体思路是最重要的,定好了之后,后续都是细节上的具体实现了。

1.系统目标

为客户提供电子货币的支付结算服务、支撑内部核算、提供充值,提现,转账,冻结,解冻,开户等原子交易及少量非原子操作、
提供对账系统数据来源,并有日切功能……等等吧,但最重要的就是上述功能

2.账务系统

记录的关键信息包括:

3. 账户管理

账户本身的开户销户冻结解冻就不说了,根据自身的业务规则来设计这几个的逻辑,比如说可用余额不为0,是否允许销户一般的功能就不说了,主要是说下我觉得比较重点的功能:

4.转账

比如账户A向账户B转账,我们的做法是将A与B账户用悲观锁锁行,然后分别更新两个账户的金额。

5.组合记账

比如说账户先解冻,再支付,是个组合的操作。由于这个操作都是在一个系统里,连接的是一个数据库,直接是数据库事务了,不用考虑分布式事务或柔性处理等。

6.热点账户

基本上解决办法有2种,缓冲记账法、二级子账户法。我们采用缓冲。对于入金类的交易,有热点账户的用缓冲记账是没问题的;对于出金类的交易,就要慎重了,可能会造成资金损失。

那么如果某个商户信用度较好,出金的缓冲超出额度,后续商户户会补回来,或者在商户信用度范围内,那么出金缓冲也没问题。

我们现在出金,比如代付类交易,没用缓冲记账,就还是采用悲观锁来做。

7.账务日切

每天日终,日切job按日切日统计系统所有账户的流水,根据流水分别算出每个账户的期初余额与期末余额。主要思想就是A账户T日的期初余额=A账户T-1日的期末余额


二、 Q & A

Q:转账那个,有多少几率会死锁??

A:我们自己测试过,采用悲观锁锁行记录,其实性能还可以,但是测试报告的具体指标我不记得了,不用太担心。

Q:转账功能能否出账账户采用悲观锁,入金账户采用缓冲入账呢

A:悲观锁,我的理解是,认为任何时间内,这个资源都会有其他的线程来抢夺我的资源。所以,我就先占用这个资源,把资源给锁住,这样别人就抢不走了。我用完这个资源,再释放。


Q:热点账户:二级子账户法 这种方式能说下吗?

A:二级子账户,比如把一个主账户,分解为10个小的子账户,那可用余额也分为10份。并发访问的时候,把并发分散到每个小的子账户去处理。但是余额就比较难以控制。

Q:请问发散是随机发散么,10个子账户有区别么

A:10个子账户的账户结构就是一样的,没有区别。至于分散压力的方式,就可以根据业务来设计,比如随机也可以吧。

Q:会计和账务系统分开是出于什么考虑?

==待回复==


Q:单笔入账处理时长大约多久

A:账务的响应时间得设计是毫秒级的,起码要小于100毫秒。账务就是快和稳定。两者分开,主要是定位不同。这个是仁者见仁智者见智的问题。


Q:为什么账务报会计分录还要通过回调知道结果呢,这样给账务增加了不少负担, 假如通过异步消息 可以通过多种手段来核实分录是否都落成功的,并且到日切时候才需要校验分录 也不需要很及时?

A:其实我觉得这2种方式都行


Q:商户缓冲出账和缓冲入账这块是需要和商户签订协议的吗?你们现在有使用这种方式吗?

(分享内容引用)那么如果某个商户信用度较好,出金的缓冲超出额度,后续商户户会补回来,或者在商户信用度范围内,那么出金缓冲也没问题

A:出金缓冲,要签协议。不过我是建议,毕竟也这么做。

Q:出金缓冲?是因为D0么?

A:我们没出金缓冲,这种业务不多。


Q:账务系统到会计系统,会计系统产生的记账凭证会不会出现多借多贷的情况?

A:系统面向财务,能够反映资金在途,通过会计稽核起到资金的监控作用。还可以为备付金报送提供支持。央行的备付金报送的报表,和会计科目挺像的。

A:账务是业务记账,会计是经营核算,没有会计,业务照样转

Q:客户的资金账户在会计系统里头有体现么

A1:有,负债类科目

A2:每个客户的资金在账务里是分开的,叫分户账,在会计里就合一块。分户说的也是这个意思

A3:分户账是有额度限制的吧

Q:账务系统记的是客户的账,会计系统记的是公司本身的账这么理解对吧?客户的资产,对公司平台来说就是负债

A:对


Q:请教下热点账户的问题,热点账户是账务系统的,还是会计系统 ?

A:会计的主体是机构总账,会计的主体是机构总账

Q:10个客户每个1万,在会计系统里记一笔10万就行了吧?记在负债类科目,还是把10笔负债分开记

A:会计系统,内部户余额更新,是通过账务明细做为一批一次更新


Q:记账加锁 A—> B B—>C C—>A 这种情况的死锁呢?

A1:我们现在对于这种情况,做了锁粒度的拆分,这样搞肯定会死锁的

A2:其实那样的情况 可以用排序来做不会死锁,锁力度拆分会引入复杂度

A3: 死锁是尽量要辟免的,加锁顺序

Q:排序这种方案性能如何?

A:排序性能不需要考虑的,在查询用户时候 先按2个userId排序 再去做处理


Q:记账 使用冻结解冻 当遇到 a->b 同时 b->c 你们的事务处理会有失败的吗 ?假如遇到分布式事务 就更不说了

A1:并发情况下两个商户来回转帐死锁几率很大,但是业务使用场景上是不会出现的

A2:记账加锁 A—> B B—>C C—>A 如果注册用户有千万级,建议分布式事务最终一致性解决,我们现在线上已经在用了(@Alive—杭州—金百仕支付)

A3:一般区分业务,用户端的账户一般立即处理,商户最终一致的比较多,还有平台,这东西各种方法都能实现 我一般都用最简单的

Q:如果是千万级注册用户的量呢,A-B转账的问题

A1:千万级是没问题的,扣减先做 增加后做

A2:最终一致性方案简单,按用户分组是没问题的,不垫款财务上是允许

A3:商户账户减钱的都实时,加钱的都可以缓冲,同一笔会计分录也可以。中间账户加减都可以缓冲,延迟不超过1分钟。

A4:涉及商户的缓冲记账,都要签订协议。


Q:按用户分组?如果用户不在一个库呢

A:最终一致性,也就是柔性事务,就是为了解决分布式数据


Q:会计缓冲是可以的,如果是账务 对于商户这种热点账户怎么解决?

A:热点账户 只有出款的情况,比如 for update wait 3 , 系统自动重试。出款就需要削峰填谷。。


Q:悲观锁方案的性能如何?

A:==待回复==


Q:二级子账号 数值拆分思路很新颖 只是一个帐号上产生并发的机会很小吧?即使并发也不会很大吧?

A1:看拆分的粒度,可以使用缓冲记账+二级子账号,如果账务系统做分库分表拆分的话

A2:子账号按业务切是可以的,否则会带来管理复杂度,我们也在用


Q:拿不到锁 不成功就不出款吗

A:拿不到锁过一段时间在重试啊,会计分录不变就没事。实时转账的话,就直接失败,出款没必要保证100%实时返回结果吧?

Q:实时转账这样体验不好吧,而且一般不是实时的

Q:出款要100%保证实时返回结果,否则有可能会造成资损吧

Q: 跟会计分录是两码事资金变动 不要会计分录?

A1:账务和会计分开,账务实时记账,会计异步记分录

A2:理解或设计上不一样吧。 我们是通过会计分录,来变动资金明细的。缓冲记账就先落地会计分录,1分钟批处理一次。

A3:话说我们的账务系统是先分录再记资金变动明细

Q:那如何保证出账是一定成功的呢?(出账是指 生成资金变动明细?)

A:会计分录没问题,就不会有问题的。商户帐只有+钱缓冲,不会有问题的,顶多系统中间过渡户可能会有问题 ,因为加减都缓冲的。(@雪鹰,有遇到过资金明细按时间、序号排序错乱的么……oracle RAC多节点写数据)

++oracle排序不稳定的问题我遇到过,最后我们的解决办法是再加上主键来排序,排序就稳定了。order by 业务字段1、业务字段2、主键。(@张鑫—)
oracle RAC多节点写数据取的是各节点的时间,不是写入数据文件时的时间…++


Q:请问内部账户或者平台账户是放在账务系统,还是会计系统,还是独立的系统,能否分享一下相关优缺点吗,谢谢

A1:内部账户如果有业务方主动变动 是在账务,如果只是统计类的账务 会计里面就行,比如预算户

A2:内部账户如果参与了支付业务的话,是要放账务系统的。这是我的理解。然后再归属到某类会计科目上。

Q:比如担保账户理论上是涉及到业务,那种账户是放在账务系统了?

A:是的


Q:账户的数据要不要发生业务有效性的验证?

A:使用中间过渡户,看过渡户余额就知道有无交易有问题了。

Q:结算中吗?结算的时候再与业务数据对?比如A给B转账,B余额收入的这条单子的有效检验

A:A-C,C-B 看C就行了,有问题C会挂账的,否则没问题。

Q:C是平台?

==待回复==


三、上期主题问题补充

以下讨论是针对5月25日分享主题渠道管理和渠道接入的Q&A

Q:这个状态机你们是怎么做的?规则引擎+流程引擎做的么?我在考虑微引擎,实在不行自己写一个。
20170525_221354
A:类似流程引擎的,也是代码实现的

Q:代码写也比较简单,也就那有限的状态切换再加上行为调用,商户通知也是在这层消息驱动的吧?

A:paid、settled都发消息的

Q:关于回调地址你们是存在订单系统还是在创建订单时候直接拆分到了通知系统?我在规划交易平台微服务化,每个模型都想精简下。每个订单对应一个回调地址。

A:订单系统,要发通知去取回调地址。商户回调地址可能每笔都不一样,一般校验域名或IP。


Q:渠道路由流程这个图是用了责任链模式去实现的?

A:责任链-过滤器,分流多个通道分配不同权重,比如三个通道权重100:100:50,就是250笔交易的话分别有100笔,100笔,50笔。


Q3:在高并发下,渠道电如何去分流的?

==待回复==


四、自由讨论

4.1 现代金控事件

1. 由现代金控清算事件引发的关于订单重复支付、调整等问题的分析和讨论
行业资讯 《独家:关于现代金控清算异常事件的通报!2次事件重复到账涉及4.5亿多!》

2.对现代金控清算异常事件的几点看法
观点1:这就是因为技术不严谨问题造成的

观点2:专业的事一定要专业人去做,对于金融一定要敬畏

观点3:关于现代金控,超时处理机制有问题。首先超时不能就认为失败!我理解2个概念重发,重路由。

4.2 订单重复支付问题

Q:问一下银行一般有频次限制,这里流控怎么弄呢?

==待回复==


Q:对于商户的同一个商户订单号发起多次支付,订单系统里的订单是多笔吗?(含重复支付解决方案探讨) 问题补充说明:银行那边的订单号不能重复,如果上次未完成支付,网银这种情况,如果再次支付的话,重新生成一条支付记录, 在提交到银行,但是商户提交过来的订单就只有一条。

A:订单可以是一笔,但每发起一次支付都要生成一条支付记录

• 多次支付,支付订单应该是多笔,对于交易的订单是一笔,交易订单1:N支付订单
• 入款你换订单号没关系的,顶多退款。前面说的针对出款的,出款那样会资损的。 单号不一样就是2笔。一笔交易多次支付,后面的自动退款就好了。

• 一笔交易的单号包括:交易订单,支付订单,机构订单(银行)。一笔交易可以多次支付,支付订单正常来说和机构订单1VS1,重路由的话1VS多。

• 嗯,我这边发生过重复支付的情况, 第一次支付(网银),支付完成后未收到回执, 支付订单还是未支付状态,这时用户在此支付,就出现重复支付了。 原本我是在第二次支付之前,查询前一次支付的支付状态,发现效率很低。我对接的银行,无论是否成功,订单号都不能重复。所以每次发起都要生成一条支付记录。

• 收银台需要引导用户不能做重复支付,否则用户会疯掉,例如 支付宝 app支付,只有后台的通知,这种情况下,后台通知延迟的情况下,用户有可能会重复支付。一般这种同一交易单,多支付单情况,支付系统内部需要对晚通知的支付单做异常退款,且发送异常退款消息给到用户。

• 重复支付的情况在收银台要做下控制,用户切换支付方式重新支付,要查询下之前一笔第三方的支付状态,明确返回失败,才允许用户继续支付,否则提示支付处理中。

另外,例如买票的行为,在用户下单的时候,订单会有一个锁定期,比如15min,在锁定期内支付就行。


Q:网银不是有前台通知和后台通知吗,前台通知也慢?

A1:目前只有前台通知,要么主动查询。前台通知也有不靠谱的,银行支付成功后,出现一个页面倒计时5秒之后回通知网关,这时页面被关闭了,就通知不到了。这种情况下,前端系统最好给用户提示一下。

A2:后端主动查询网银,前端轮询交易订单状态


Q:重复支付这个问题,假设存在支付宝、微信俩支付方式,用户请求支付宝,但是不做支付。由于支付宝未支付,且未支付失败,那么这个时候,用户使用微信支付就不行了,针对这种情况,各位是如何做的呢?这是一个正常的用户行为,比如用户一开始使用支付宝,后来发现想用微信来支付。

A:如果是偶发,怎么处理都行,比如后台自动换通道。

A:直接支付没有问题。同笔交易快捷支付前一笔状态处理中,我们限制3分钟不能在支付。网银我们不做限制,尝试下来还行,只要渠道别大量长时间掉单


关于重复支付处理的其它方法:

4.3 调账问题

调账的几种说法

Q:如果说电商后台对账模块一般是怎么做有没有调账这个功能?


Q:电商类型的对账后台需要调账功能吗?我目前这个系统不需要所谓的会记凭证。系统目前只是要知道交易成功或者失败,然后跟第三方核对一下这个需不需要有类似于支付机构那种差错帐处理?


Q:主要是目前要做的这个系统其实没有多退少补这概念说白了就是一个记录,就是知道交易到底有没有成功。接的是第三方支付所以不清楚要不要这个。
是指内部交易系统与第三方系统对账之后的处理?

A1:正常情况下是不区分电商不电商的,别人欠自己的钱,还的钱对不对,这就是对账。你说的这个是比较简单的流水勾兑,或者叫台账。

A2: 流水沟兑只是信息流,你的资金对不对?要知道资金有没有交易成功有的资金没有交易状态,这就是需要对帐。以某一方为准


Q:内部交易系统跟第三方系统对账 资金对账要怎么对?

A:无非就是对状态


Q:那需要差错对账吗?就是把勾兑没成功的挂起 然后跟第三方支付校核对订单状态进行人工操作?

A: 账有两方面对,一个是看每一笔的金额是不是对,总额是不是对,另一个是要看对方打到银行账户上的钱,是不是和总额对得上。你会计系统都没有,这些可以不要,只要能将差错核销掉就行。


关于调账处理方法

调账,在我们这是作为一个应急功能。我举一个我们目前现实的例子,大家看看用得对不对,买卖双方,原本双方线下商量好,以1000W,成交,最后因为输入错误,输入了100W。这是由交易双方,提交申请,可以进行调账。调账有两种方式,一种是调资金帐,相当于由买房直接向卖方支付900W。或者调资产账,将交易的资产由原来的数量,调整到1000W的价值,再由双方再做一笔交易。

Q:订单层面上记录下来的就是100w吗?我的感觉也是做退款,再下个单。

A: 对,记录的就是100W。

Q:那退款是做一个反向交易来实现么?资产回到卖方,资金回到买方?

A:如果是普通支付系统我就建议退款或者撤销。

Q: 假设你们做逆向交易,会不会带来额外的问题?

A:我们如果做反向交易的话,系统里头就会出现两个结算通知单,跟业务人员讨论过,觉得不合适。我们每笔交易,都会出具一份加盖公章的结算通知单,表明资金和资产的过户。但我觉得还是系统设计层面的问题,因为系统是AS400,RPG做的,不太敢动交易的各种状态,不然应该会比较好做。

Q:那如果直接调账,订单层面上还是100w,还是直接改成1000w,人工介入做订单的订正

A:调账就是直接改帐了,订单层面不改,就是一个资产或者资金的划拨过程

Q:加一个机器校验环节类似注册时输入两次密码 提交前让程序参与比对?

A: 机器没法比对啊,因为客户输的就是1000000000,但是他以为他输入的10000000000。我觉得加一个数字到中文的转换会比较合适。人数0,容易数错,但是中文一看就明白,比如说自动识别他输入的一千万

Q:调账属于人工发起的行为不能系统发起吧?

A:对,人工发起双人复核,如果没有进入结算环节,我们也直接改过数据库改订单信息

Q:支付多少系统不能预先知道吗或者1’000’000?

A:预先知道的是客户输入的价格啊,客户数错了。我们的交易是非标准化的,没有固定的价格,客户线下商量好价格,再来做,多少都行

Q:调账之后订单和账务怎么对账呢?900万和谁去对? 他的情况下。要在做一次交易,交易900万

A1:你肯定要客户再做一次900万的交易的,我目前系统跟你们那个大型债券系统其实有点类似,你有空就说说你们这块怎么弄的吧
A2: 不一定,因为资产也是托管在我们这的,我们直接对资产调账也行。
A3: 补一笔调账交易就行

4.4 组合支付

Q:请问下 如果用户在商户有余额,在使用快捷支付的时候,应不应该支持组合支付呢?这个快捷支付卡,有第一次绑定做支付的,也有之前完成过支付的卡。我看京东是支持现有快捷卡+余额的组合支付的,但是没有支持新绑卡+余额的组合支付。而且收银台的逻辑会变的很复杂。

A:理论上可以,但是貌似大家都不做支持。这种组合支付,以外部渠道为先,例如快捷支付完成之后,才会做余额扣款。

技术层面上没有问题,但是提供组合支付对业务的帮助并不大,很多公司应该都砍掉这块的支持

做肯定能做,但会把交易拉长

4.5 异步通知

Q:大家的支付系统中的异步通知一般是多长时间通知多少次啊?

A:一天8次

4.6 Lambda架构

关于Lambda详细介绍参考:数据系统架构——Lambda architecture(Lambda架构)

lambda

Q:有没有哪位已经应用到系统中的,谈谈有没有什么坑,我打算用在风控系统中用这个架构

A:Netflix不是有一套基于lambda的推荐架构吗,阿里 现在用的这个

20170526_152513

4.7 认证支付和快捷支付的区别?

IMG_5625.JPG
IMG_5626.JPG

Q:认证支付和快捷支付到底有什么区别啊,我看用户填写身份信息和银行卡信息是一样的

A1:验证的要素不一样

A2:认证支付现在都是预绑卡吧,感觉跟快捷支付没区别啊


Q:不过在用户端填写的信息一样不少啊?第一张图是认证支付,第二张是快捷支付

A1:认证是 姓名+身份证+银行卡+预留手机 送到渠道校验

A2:认证只全要素,快捷第一次也是全要素


Q:感觉用户要填的东西一样没少,可能是第三方支付传给银行的数据没那么多?

A:连连的认证是四要素全验 交易时送到各个渠道去校验,都通过了才成功


Q:认证支付现在都是预绑卡吧,感觉跟快捷支付没区别啊?我记得连连好像是不必同卡进出的

A:因为有的银行校验的要素不是四要素,比如有的银行只校验身份证和银行卡 成功了就签约成功了

A:第一次快捷要这么多,完成了签约,认证是每次都要,是没有协议的,认证就是全要素快捷

A:认证预绑卡也只是留简单信息,但没有协议,快捷属于协议支付。协议是为了免责

A:认证和快捷都是无密支付

A:历次支付可以协议支付的,用协议号


4.8 资金存管

Q:你们的资金存管业务现在怎么样,p2p不让接了,其他商家呢?还有哪些业务可以使用资金存管啊?电商这种算吗?
A:目前已经合作的有 新网 北京 浙商 华瑞 微商 恒丰 其他的都在对接。就p2p要求严一点吧,不然很多p2p会死掉的,不过商户对接的话也耗时间


Q:是不是只有存管这一种情况,需要用户在第三方支付平台开通账户

A:应该是在银行开立账户,不然就不是存管了,关键是资金银行,所有的支付指令都是投资人和借款人发起

A:存管只需要在银行注册了

A:存管用户都是在银行开通账户,第三方支付平台会开平台的手续费账户,收费需要

A:(平安信托)我们的存管是在银行开账户是存管,第三方支付账户开商户账户用来结算资金,有些基金支付通路需要开通第三方支付的个人账户


本文档来自支付产品技术交流群的聊天记录整理,由志愿者整理并发布到本网站。如需要及时收到来自支付产品技术交流群的最新消息,请扫码关注“凤凰牌老熊”的微信公众号。 本群面向支付行业的有经验(2年以上)的产品经理、软件工程师、架构师等,提供交流平台。如想加入本群,请在本文评论中留言(不公开),说明所在的公司、负责的工作、入群分享的主题和时间。