对资金交易的一些看法

对资金交易的一些看法

   本来是在论坛上的一个帖子得回复,写的有一点篇幅,所以才做为一篇文章保存在博客里。http://www.iteye.com/topic/1111985

资金管理数据库上面的账户跟资金有关的有3个字段 ‘当前余额balance''未确认credit金额'和‘未确认debit金额'
这里的credit/debit引用了财务上的借贷的概念,credit金额总是负数(消费、取款、转出等),debit金额总是正的(退款、存款、转入等)
为了保证资金不出问题,一个账户的可用额度总是 balance+credit(注意这里credit是负数,相当于当前balnce - abs(credit)。而debit在被确认之前,是不可用的。
交易确认的概念:在交易确认之前,交易的金额被累计在credit/debit字段中,确认之后,create/debit里面的金额被合并到balance中。确认之后的交易不能再改变。(退货/退款或是其他交易在在实际应用中应该被定义成单独的交易类型)。实际的交易系统必需在保证资金安全的前提下,尽量减少消费者的等待时间。
下面说说实际的交易过程:
  跟楼主的应用场景一样,A系统是资金管理系统,B系统是业务交易子系统,分别在不同的网络位置,整个过程的网络通讯可能在任何时刻出现异常。更具体点我们来模拟一下使用储值卡在商场零售交易的情景(类似的系统有相同的特点:交易在客户端发生,而资金在服务器端管理):

同步、实时交易:
1:B系统,商场零售客户端在计算出消费金额后,在本地系统里记录一条记录,然后向资金管理系统发送一个卡消费交易请求。
2:A系统,收到消费交易请求后,判断对应的储值卡的可用余额是否>=消费金额。可用余额的计算方法是当前余额-未确认消费金额。如果可用余额>=消费金额,则记录该消费记录,并在credit(非确认消费金额)字段上累加这笔消费金额,并返回允许刷卡成功的应答。(当然实际系统还会有交易流水号和/或授权号等更多信息,这里就不讲太细了)
3:B系统:(这里是确认交易是否成功的地方,简单地说就是决定是否允许你拿着购买的东西走人)。如果收到一些类似余额不足的非刷卡成功的应答,则取消次交易。
    如果收到刷卡成功的应答,在消费者签名后,则可以确认交易成功,允许客户把购买的东西拿走。如果消费者拒绝签名(当然也没有拿走商品),此交易被取消。但无论是否签名、此交易是否成功,此结果只在本地系统(B系统)里做相应记录后,结束当前的同步交易过程,(消费者只等待到这里)。后续的交易确认、交易取消信息,由后台系统异步完成。(实际可以采取每隔一段时间批量提交的策略)。
   在收到A系统的应答之前的任何一个时刻,如果发生网络异常,资金管理系统(A系统)的逻辑不变,消费子系统(B系统)对次交易记录标记为异常,并取消次交易。此时,A系统的记录里可能有此消费刷卡成功的记录,也可能没有。

    以上过程是在B系统的前台POS机上完成的同步实时交易子过程。该子过程完成后,此次交易已经被确定是否交易成功,或被取消,准确的交易结果/过程被记录在B系统里。此时可能会与A系统的信息不一致,但以B系统为准(因为实际交易是发生在这里的),读者可以自己分析此时上述过程中可能会发生哪些异常,会导致A、B系统的交易信息会有哪些不一致。总之,所有结果最多都是交易失败,而储值卡的可用余额却被扣除的情况。对资金管理系统来说是保证安全的。
    这里要重提一下可用余额的概念,假设账户余额有1000元,此次消费金额是100元,则不管该交易是否成功,除了消费请求根本就没有发送成功的情况,该账户的可用余额都变成了900元,可以用于其它/后续消费。

后台异步、非实时过程
交易确认:
    此过程在B系统通常是后台、异步、延时、批量提交的。B系统把在前台POS机上同步实时交易记录提交到A系统。A系统逐条处理,记录交易记录,在未确认消费字段(credit)减去交易金额,如果交易确认成功,同时在账户余额上减去交易金额。此时,A/B两系统的数据最终一致。如果交易成功,可用余额没有变化,如果交易取消,可用余额会恢复到交易前的数量。
    此过程如果发生网络异常,B系统需要采取保守的策略,不断的尝试发送数据,允许重复,不允许丢失,A系统需要有能力处理重复的数据(这个不难,每个交易都有唯一的流水号和或授权号)。

可靠保证的最后一步:交易清算过程
   通常完成上面的过程后,只要程序没有bug,不管是否发生过网络异常,A/B两端的数据应该是一致的。但对可靠保证更高的系统来说(本人认为只要涉及到钱,还是尽量多做一些补偿措施).
   还有一些灾难性质的情况,比如B系统的数据库宕机,致使数据丢失,此时可能需要根据打印出来的交易单据(POS机上的小票),和有客户签名的刷卡单据,用人工的方式重新在B系统上逐笔核对和录入。如果A系统数据库宕机,则需要从所有的B系统重新传输重上一个恢复点后的交易记录。
    此过程首先核对A、B两系统的当天(通常)的所有交易记录的汇总数据及其校验结果,如果一致,可以认为两端数据已经一致。否则,重新、逐笔从B系统把交易记录传输到A系统。(可能有一些优化措施,可以不用传输所有记录,但要保证不降低可靠性)。
   这是一个数据集成的过程,B系统可以先把交易记录打包、并压缩成一个或多个文件再传输,这样做效率会比较高。

    在过去的有些银行系统里,通常都没有后台传输交易记录的过程,只是在每天晚上的清算过程才传输交易数据。如果你在柜台或者取款机上取现没有成功,却发现账户余额却少了,通常只有等到第二天,这笔钱才会退回到你的账户中。注意,实际上你看到的账户余额是我们上面提到的可用余额,应用系统也一样,不要给用户看到过多细节,他看到的余额就是可用余额。
   
   上面没有提到存款/充值的例子,交易过程类似,但不同的是只有在该交易被确认后,才会反映到可用余额和账户余额中,这是由于资金安全的考虑。为了提高实时性,通常在存款交易的同步过程完成后,B系统可以立即向A系统post确认信息。(post是指发送请求后立即返回,不等待应答,服务器端也无需返回应答,即使发送失败也被允许)。如果该确认信息被A系统收到并处理,此存款会立即有效,否则就要等待后续的确认过程和/或清算过程了。此种方法也可用于交易被取消的情况。

   另外提一个个人对数据事务处理的看法,不要把A/B系统的网络通讯过程纳入到数据库事务当中去。在B系统,总是先在本地数据库记录完后再去跟A系统通讯,在A系统,总是在接收到交易请求后再开始数据库事务,并在数据库事务结束后再返回应答。网络通讯(非局域网)是可能在任何时候发生异常,甚至被阻塞产生长时间延迟,这些都会严重影响数据库性能。

说了一大堆,可能有些复杂。为了提高资金系统的安全性和可靠性,多做一些事情是必要的。

本人非金融交易的专业人士,观点可能是错误或不准确的,欢迎批评指正,但不对此产生的任何后果负责。
如果你有对涉及到资金交易安全的观点和经验,欢迎并感谢交流和分享,我也对此很感兴趣,希望能学习和了解到更多的经验和知识。

 

猜你喜欢

转载自taolei0628.iteye.com/blog/1120335