支付系統-收銀台系統總結


支付系統-帳戶系統總結

他人:支付業務與技術架構學習總結(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 說不要存儲.

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM