• 支付系统-收银台系统总结


    支付系统-帐户系统总结

    他人:支付业务与技术架构学习总结(6)——对账系统的设计 

    原则:

        支付系统思考思路: 先考虑正常case, 先考虑并发[重复支付, 押金退款,提现], 再考虑事务中断.[重复支付]

        能幂等先幂等(需要采用数据库事务), 不能幂等优先选择资金安全的方案. [退款和支付的不同处理]

        避免事务中断,尽量采用面向对象嵌套法,每个内部子模块获取自己的数据,开头都需要对需要判断子模块实体的状态,外部系统写操作放在最后.

        对账对出事务中断的部分: 修改状态后人工修复.

        角色,用例,模块,依赖, 抽象,导致. [优惠模块为什么放置到订单之前,又双向依赖订单,而不是放置再支付中的原因]

        从业务抽死剥茧出头疼地并发场景.

        架构重构,千万不要盲目抽到新的模块. 没想明白之前,循环依赖会搞死你,虽然可以依赖倒置的概念,但maven不支持循环依赖. 测试驱动重构.

     看得懂,讲明白下面的这些文章就出师了:

    Aggregate Framework是为方便开发人员运用DDD和CQRS思想来构建复杂的、可扩展的Java企业应用系统而提供的Java技术框架。该框架提供了Aggregate、Repository、Domain Event等构建块的实现;

    重构:  支付宝鲁肃:支付宝全局架构重构实践 http://www.infoq.com/cn/presentations/cl-refactor-system-arch/

    CAP theorem 理论在 多副本存储 和 分布式存储中理解

    微服务架构的分布式事务解决方案 龙果学院 含源代码

      tcc github源代码 https://github.com/search?utf8=%E2%9C%93&q=TCC&type=

    TCC是一个理念,其由Atomikos公司的创始人提出,如果想了解其具体内容直接到其官网下载个白皮书看下就好了,任何时候都是看官方文档才能更准确的获知答案。不过TCC只是分布式事务中的一个选项,且并非最优选项,这里有篇文章介绍https://github.com/QNJR-GROUP...

    https://github.com/QNJR-GROUP/EasyTransaction 含多种模式 和 复合

    https://github.com/prontera/spring-cloud-rest-tcc

    try 就是冻结. 冻结资金,冻结券,或者直接修改订单状态. confirm时啥都不做. tcc三个步骤都需要保证幂等,否则问题很大. 如何做到自动幂等?

    wsba oasis

    通过几天的资料查找,对解决分布式事务的方法有两阶段提交、支付宝分享的TCCtry-confirm-cancel)和基于消息的最终一致解决方案,其中第一条和第二条虽然也能解决问题,但普遍对第三种基于消息队列的最终一致解决方案推荐多比较高,所以第一条和第二条可以参考使用。 from 分布式事务方案整合

    订单处理:本地事务

    资金账户加款、积分账户增加积分:TCC型事务(或两阶段提交型事务),实时性要求比较高,数据必须可靠。

    会计记账:异步确保型事务(基于可靠消息的最终一致性,可以异步,但数据绝对不能丢,而且一定要记账成功)

    看看别人对资金安全的总结 http://www.infoq.com/cn/presentations/correctness-ensure-of-funds-in-internet-financialsystem

    商户通知:最大努力通知型事务(按规律进行通知,不保证数据一定能通知成功,但会提供可查询操作接口进行核对)

    业务 方案1 方案2_黑ma
    支付

    订单支付

    押金支付

    出行卡支付

    月卡充值

    充值支付

     bill引擎,回调各业务实体.

     getFee 时新建实体,还是 success 时新建实体

     支付和费用变更

     费用不一致就退回.

     问题,券不可退.

     多退,少补的原则. 记录费用详情. (拆分同一个费用项 id)

     退款和打款  

    退款记录再退款表

    打款记录在提现表

    退款和打款都记录在订单表.

    打款依赖提现表.

    提现表有两种类型: 司机提现, 退款打款.

    费用,优惠记录

    静态字段标识. 券 id.

    后来优惠越来越多,开始意识到需要抽象出 支付表: 含支付类型,支付 id (券 id)等

    抽象出支付表

    会计

    无会计统计

    基于帐户地变动去进行会计统计. [ ]

    记住: 借代表增加,贷代表减少.

    业务收入

    1、原借款时:借:其他应收款   2200
                    贷:库存现金  2200
    2、报销差旅费时:借:管理费用—差旅费   2000
                         库存现金  200
                        贷:其他应收款       2200

    结合会计

    • 记账
      • 复式记账.
      • 会计凭证和账单.
        •  司乘分离的差额. 需要记录. 不能放在 pay 表里.
        •  多个收据对应一笔帐户变动
    • 何时冻结费用变更
      • 方案一 尝试支付后不可更改费用. 冻结费用最早进行.
        • 缺点明显,一旦尝试支付过就不能变更费用.
      • 方案二 冻结费用在网关层回调业务方. 先冻结,然后修改支付成功.
        • 缺点:方案复杂,解决万分之一的问题
      • 方案三 不冻结. 修改费用对主体状态的变更(还需支付,退款) 和 支付成功通知对主体状态的变更 需要并发加锁
        •  如果支付通知的费用和 现有费用不一致, 状态仍然为待支付. 判断期间费用不可变更
        •  变更费用后,仍需支付.那么就还需要支付. 如果需要退款.那么就退款.
    • 支付和退款退的那部分费用是什么?
      • 需要和费用模块联动.费用历史.
        • 方案一  费用模块用version, 每次变更保存新的费用项和增加 version. 费用模块保存 versionList
        • 方案二  支付保存费用项 list, 每次自己做计算.
        • 方案三  不用关心支付的费用是什么? 只关心总金额. 缺点是不满足有时候退款,需要根据费用来确定渠道退款顺序.(押金等)
    • 如何重复支付判断?
      • 方案1: 实体支付成功,我方未成功. 即重复支付.
      • 考虑并发情况.
        • 两笔支付都修改实体为支付成功,
    • 如何支付溢出判断?
      • 支付成功时,
      • 锁定实体.
      • 计算已支付金额..
      • 修改自己状态. 此时其他流水无法修改自己状态. 已经分布式锁住了唯一健. 有并发竞争的复杂 case. 表面上只改一个整体的状态,但实际上是对计算逻辑有竞争.
      • 是否支付溢出,那么就退款
      • 释放实体
      • 第二笔锁定实体. 计算已支付金额和已退款金额.
    • 代码能处理正常业务逻辑, 但是能否处理事务中断后的重试? 如何面对 mq 或者 dubbo 的重试? 
      • 要做到幂等(见下面条目)
      • 如果做不到幂等,那么就选一种没有资金损失的方案?
      • 案例1 重复支付判断
        • 原有逻辑
          • 实体未支付, 属于正常支付成功case. 修改实体为已支付,修改账单未已支付
          • 实体已支付, 属于重复支付case ,退款. 计算下多支付了多少钱, 多付了哪些费用项, 把多的进行退款.
        • 问题: 假设后续流程出错,整个流程重试, 实体已支付 ,认为属于重复支付 case,贸然退款就有问题,出现资金损失.
        • 解决方案1: 将自账单和实体统一起来判断.
            • 先判断:  判断实体已支付,我方已支付. 略过. 实体已支付,我方未支付. 重复支付. 实体已支付,我方支付. 已经修改过状态
        • 缺点: 假设正常case下修改实体为已支付后, 事务中断. 然后被 dubbo 或者 mq 重试.
        • 问题: 实体已支付,账单未支付. 变成重复支付,退款.资金损失.
        • 解决方案2:  重试最好导致的问题是本来要退款的变成了不退款. 案例思考: 先改账单支付,再改实体支付. 事务中断, 刚好此时实体被另外一笔支付支付. 然后被 dubbo 重试.
        • 问题: 账单已支付,实体已支付. 略过进行下一步. 本来要被退款的被忽略了. 乘客自己会来打电话,或者对账系统能对出来. 支付的金额(减去退款) !=订单的总费用
      • 案例2 帐户加款
    • 幂等. 只有做到了幂等,才能进行重试. 但是有些情况下,重试是mq,或者 dubbo 自动控制的, 如果没有肯定出现问题,选择一种不资损的方案.
      • 方案一: 流水+事务 [能够抵抗微服务拆分,分库]
      • 方案二: 状态前置判断法,
        • 1.设置事务: 内部操作流水+状态修改处于一个事务中,写外部系统无法,可以放在最后?
          • 缺点:不能够抵抗微服务拆分,分库. 例如:重复支付判断?
        • 2.不设置事务: 相信内部操作流水+状态修改处于一个系统,会尽可能得处于一个事务中. (要使用面向对象编程法: 形参传入最上层的 beanWrapper,内部包含了所有对象,所有外部数据都先获取好,组装好. (更新除外) 注意: 不要按需获取,避免调用外部挂掉. ) 否则中间随意调用外部系统,写操作除外,可以放在最后?
          • 缺点:肯定会出现事务中断,要选择一个没有资金损失的方案.
      • 双实体幂等控制(多对1操作) 都在同一个系统和数据库下,相信极大情况下都是一个事务内的. 可以通过对多和1的 双状态判断来进行幂等控制. [ 例如: 多笔账单对实体的支付成功逻辑, 基本上不会在实体侧保存一份支付成功流水,因为同一个数据库. 但如果微服务拆分后就不一样了,微服务拆分引发的幂等重试, 所以这种最好是幂等略过的方式 ,这样虽然数据会不一致,但是不会引发问题, 重复退款等.]
        •  多方支付成功,1方支付成功. 略过 .
        •  多方支付未成功,1方支付成功. 说明已经成功 (不考虑中断在中间的情况, 因为不采用分布式事务,也无从考证)
        • 多方
      • 双实体幂等控制(多对1操作变更)幂等判断, 两个实体在不同的系统中,必须要求1方记录多方的操作流水号. 例如 多笔支付重复支付和幂等判断 , 帐户的变更幂等判断.
    • 回调时金额不一致的处理. (有了支付溢出专题讨论后,这里就简单很多了)
      • 极端场景描述:
        •    乘客需支付10元
        •    唤起 app 尝试支付10元.未回调
        •    改费用,改成了9元.
        •    支付9元
        •    10元回调回来.
        •    9元回调回来.
      • 两个方案
        • 方案一,先设置为支付成功,再由业务方来确定是否全额退款,还是部分退款.
          • 如果是全额退款的. 就会照成问题. 和正常支付成功且部分退款的如何区分? 本质上不用区分.就需要看下未退款金额即可.

            业务上需要判断是否已支付. 不能单纯地看成功支付的笔数,而是要看总金额.

          • 如果是采用多退少补的方式. 如何明确退款对应的费用? 费用 version_from version_to. 押金多少都不动.已账单为主.
              •    乘客需支付10元
              •    唤起 app 尝试支付10元.未回调
              •    改费用,改成了9元.
              •    支付9元
              •    10元回调回来. ( 设置实体为支付成功,设置为支付成功,并且退1元. )
              •    9元回调回来.  ( 设置为成功,设置实体支付成功失败, 全额退款. 不退.)
        • 方案二 及早询问业务方是否费用变更. 同时冻结费用(越早冻结越好),如果变更如何应对.
          •  这样就会出现
            • 多退
              •  如果是多退部分,支付成功并部分退款
              •  如果是多退全部,支付关闭并全额退款. 这部分退款不需要对应着费用. 做一个标记,是全额原路退,还是业务退款.
            • 少补
              •  少的部分需要重新交. 交的那部分费用是什么,用 费用项表示. 负数
            • 少退
              • 如果是少退全部, 支付关闭并全额退款. 这部分退款不需要对应着费用. 做一个标记,是全额原路退,还是业务退款.
    • 支付有哪些抽象概念?
      • 费用:
      • 优惠 (最开始单一的券,就简单的用字段描述,后面发现优惠多了,只能用关联表):
        • 权益
        • 包月
        • 积分
    • 哪些是应收款项
      • 优惠的部分
    • 哪些是显性成本:
      • 优惠的金额. 首单减免,包月.
      • 优惠券
      • 权益折算成的减免时间,最终和原时间的差价
    • 哪些是隐形成本:
    • 哪些是隐形收入:
      • 包月部分
      • 余额部分
    • 哪些是用户关心的支付.
      • 余额+线上支付等
    • 关闭全额退款还是二次支付?
    • 费用是可变的,会导致实体状态是并发修改的? 回调流程,何时冻结? 是否需要提早冻结,层次很多的情况下.最底层网关层回调时是否就需要冻结?
      • 像滴滴, 黑马一层(可变,有并发),收银台一层(可变,有并发,一旦并发,就看谁先锁住,回调先锁住,状态无法回溯的话,那么就生成新的实体.最好是如此.),phoenix 一层

    1.  抽象不同的阶段. ( 引擎模式,更让人思考, 每个业务增加一个流程点,  越上游收集越好,例子是 回调回来费用变更.)

       不同的阶段,分类主体不同. 比如计费阶段,根据业务分. 渠道阶段,根据渠道分.

    2. pay 表 只保存乘客支付金额和优惠总额. 1 对多 优惠表

    3. 优惠表里保存优惠类型,支付帐户,总金额.

    4. 差额表里保存差额凭证 

    业务和统一收银台的交互 ,和 业务和 phoenix 帐户系统的交互没什么区别. 但是区别在数据上,都利用了签名,利用客户端 以及 签名的把两个服务端的交互解耦掉了.(微信就没解耦,微信和网络影响服务器资源)

    区别: 1. 统一收银台不仅传递了支付金额,还传递了各项费用. 面向的 orderId. 一个 orderId 对应多笔支付. 内部封装掉.

    这种做法比较巧妙. 比较适合用在通用组件上.(需要回调业务方. 客户端回调,解耦服务端)

    表结构设计:

      函数依赖要存储.因为可能会变. 老的订单会有问题. 卢peng 说不要存储.

  • 相关阅读:
    【转+补充】在OpenCV for Android 2.4.5中使用SURF(nonfree module)
    Delphi StarOffice Framework Beta 1.0 发布
    Angular ngIf相关问题
    angularjs文档下载
    公众号微信支付开发
    公众号第三方平台开发 教程六 代公众号使用JS SDK说明
    公众号第三方平台开发 教程五 代公众号处理消息和事件
    公众号第三方平台开发 教程四 代公众号发起网页授权说明
    公众号第三方平台开发 教程三 微信公众号授权第三方平台
    公众号第三方平台开发 教程二 component_verify_ticket和accessToken的获取
  • 原文地址:https://www.cnblogs.com/fei33423/p/7494573.html
Copyright © 2020-2023  润新知