支付系統(tǒng)設(shè)計(jì):對(duì)賬處理(二)

可以說(shuō),對(duì)賬是支付系統(tǒng)最頭疼的事情。每一筆交易,都要做到各參與者的記錄能夠吻合,沒有偏差。對(duì)賬系統(tǒng)的工作,是發(fā)現(xiàn)有差異的記錄,即軋帳;然后通過(guò)人工或者自動(dòng)的方式,解決這些差異,即平帳。
對(duì)電商系統(tǒng)來(lái)說(shuō),每一筆交易,在所有相關(guān)主體側(cè)都要能對(duì)得上:
- 交易主體,如果發(fā)起人是個(gè)人,必須能夠從個(gè)人交易歷史記錄中找到這筆交易。但大部分人不會(huì)保留電子記錄,所以一般是提供可以下載的賬單或交易記錄,讓用戶自己對(duì)去。
- 交易對(duì)手,一般是商戶。商戶側(cè)對(duì)賬處理同用戶側(cè),也僅僅提供對(duì)賬單。
- 交易渠道側(cè),這是對(duì)賬的重點(diǎn),一是核實(shí)交易流水,二是核實(shí)交易傭金,畢竟是租用人家通道做結(jié)算的。
那有哪些記錄需要對(duì)賬? 目前主要是兩個(gè):一個(gè)是交易記錄;一個(gè)是退款記錄。
對(duì)賬處理流程
一般來(lái)說(shuō),對(duì)賬流程涉及到如下步驟: 渠道對(duì)賬單下載、本地交易記錄準(zhǔn)備、軋賬、平賬。
渠道對(duì)賬單下載
銀行,第三方支付,銀聯(lián)等,基本都會(huì)提供對(duì)賬單下載的功能。不過(guò)也有少數(shù)工作做不到位或者太到位的銀行,只提供賬單查詢后臺(tái),不提供對(duì)賬單下載功能。
對(duì)開發(fā)人員來(lái)說(shuō),這里有幾個(gè)坑:
- 對(duì)賬單格式不一。文本,XML,csv的都有。為了后續(xù)能夠統(tǒng)一處理,在賬單下載完成后,需要進(jìn)行標(biāo)準(zhǔn)化處理。
- 下載方式不一,HTTP,HTTPS,F(xiàn)TP的,都有。下載程序需要按照渠道的協(xié)議來(lái)處理。
- 下載時(shí)間不一,一般是凌晨1點(diǎn)后,到中午12才能用的也有。如果在預(yù)定的時(shí)間取不到數(shù)據(jù),需要注意重試讀取。
- 穩(wěn)定性差。FTP服務(wù)器出問(wèn)題那是常有的事。渠道側(cè)解決方案往往就是重啟。所以重試機(jī)制是必要的。
看一下第三方支付的對(duì)賬單情況:
銀行直連的對(duì)賬情況:
技術(shù)選型上,HTTP(S)用apache httpclient即可實(shí)現(xiàn)鏈接池和斷點(diǎn)續(xù)傳, FTP也可以使用Apache Commons Net API。 但不管是哪一個(gè),都需要設(shè)置重試次數(shù)和鏈接超時(shí)間。重試次數(shù)和間隔的設(shè)置需要小心,重試太頻繁,容易把服務(wù)器打死.;時(shí)間間隔太大,又會(huì)阻塞后續(xù)處理步驟。5~10分鐘是一個(gè)合適的重試間隔區(qū)間。
鏈接超時(shí)指在服務(wù)器出現(xiàn)問(wèn)題時(shí),連接在指定時(shí)間內(nèi)獲取不到數(shù)據(jù)即自動(dòng)斷開。這個(gè)很容易被忽略。我們有一次系統(tǒng)出問(wèn)題,是渠道側(cè)的FTP假死后重啟,導(dǎo)致我們的客戶端掛住,一直在等待重新鏈接。
渠道對(duì)賬單標(biāo)準(zhǔn)化
找個(gè)例子大家看看, 比如微信的對(duì)賬單,他是csv格式的,包括如下信息:
- 交易時(shí)間:這是在微信側(cè)的支付完成的時(shí)間。 這個(gè)時(shí)間會(huì)成為一個(gè)陷阱。
- 公眾賬號(hào)ID,商戶號(hào),子商戶號(hào),設(shè)備號(hào): 這些信息需要做驗(yàn)證,確保是自己的單子,不要讓微信把老王家的單子也給發(fā)過(guò)來(lái)了;
- 微信訂單號(hào),商戶訂單號(hào): 這兩個(gè)是對(duì)單的核心。前者是微信側(cè)產(chǎn)生的訂單號(hào),在微信支付接口返回值中有。但是萬(wàn)一收不到這個(gè)返回值,那在本地記錄中可能就空了。 后者是我們發(fā)送給微信的訂單號(hào),一般用這個(gè)來(lái)做對(duì)單依據(jù)。兩邊的數(shù)據(jù)中都會(huì)有這個(gè)值。
- 用戶標(biāo)識(shí),交易類型,交易狀態(tài),付款銀行,貨幣種類,總金額,企業(yè)紅包金額: 這幾個(gè)就是對(duì)單的核心字段,必須確保雙方是一致的。
- 商品名稱,商戶數(shù)據(jù)包,手續(xù)費(fèi),費(fèi)率:這些是可選驗(yàn)證。
而某寶的對(duì)賬單,是文本格式的,用空格隔開。他們家的就簡(jiǎn)單很多,只有商戶訂單號(hào),交易流水號(hào),交易時(shí)間,支付時(shí)間,付款方,交易金額,交易類型,交易狀態(tài)這些字段。
由于每個(gè)渠道的賬單格式都不盡相同, 在得到賬單后,下一步是對(duì)賬單做標(biāo)準(zhǔn)化處理,這樣軋帳以及后續(xù)工作就可以統(tǒng)一處理了。 標(biāo)準(zhǔn)化后的賬單數(shù)據(jù)可以放在文件系統(tǒng)或者數(shù)據(jù)庫(kù)中。這取決于交易數(shù)據(jù)量。每天百萬(wàn)以上的量,還是使用文件系統(tǒng),比較合適。數(shù)據(jù)庫(kù)操作相對(duì)比較慢,也浪費(fèi)資源。
基于文件系統(tǒng)的標(biāo)準(zhǔn)化涉及如下內(nèi)容:
- 文件格式標(biāo)準(zhǔn)化:統(tǒng)一使用csv或者json或者xml格式。如果是使用hadoop或者spark來(lái)對(duì)賬,使用csv是個(gè)不錯(cuò)的選擇。
- 文件存儲(chǔ)統(tǒng)一化:文件目錄,文件名都需要遵循統(tǒng)一命名規(guī)范。
為了加快處理速度,我們使用hdfs作為文件系統(tǒng),有利于后續(xù)的對(duì)賬的處理。
本地交易記錄準(zhǔn)備
本地交易記錄的準(zhǔn)備,總的來(lái)說(shuō)有如下方法: – 啥都不做,直接用原始數(shù)據(jù)。鑒于大部分系統(tǒng)使用的是mysql,這也意味著在MySQL上做對(duì)賬。對(duì)賬時(shí)需要大量的數(shù)據(jù)查找工作,必然會(huì)影響線上業(yè)務(wù)。在數(shù)據(jù)規(guī)模較大,比如超過(guò)100萬(wàn)時(shí),就不太合適了。
- 當(dāng)然,還有一個(gè)選擇是使用備庫(kù)來(lái)執(zhí)行對(duì)賬,這樣既簡(jiǎn)單,也不影響線上業(yè)務(wù)。這是典型的空間換時(shí)間的做法。
- 如果業(yè)務(wù)大到需要分表分庫(kù)才能處理,那對(duì)賬數(shù)據(jù)準(zhǔn)備也不一樣。使用分庫(kù)也不現(xiàn)實(shí),因?yàn)榉謳?kù)一般是按照主體id,而不是渠道id,來(lái)分庫(kù),這樣對(duì)賬就需要在多個(gè)庫(kù)上進(jìn)行,效率反而降低了。而對(duì)分表分庫(kù)建立從庫(kù)也非常耗費(fèi)資源。這種情況下,需要同步一份數(shù)據(jù)到(hdfs)文件系統(tǒng)中,或者NOSQL數(shù)據(jù)庫(kù)上。
由于交易記錄是支付系統(tǒng)核心數(shù)據(jù),有大量的應(yīng)用,如信用、風(fēng)控等,都需要交易記錄數(shù)據(jù)。這些應(yīng)用對(duì)交易記錄的需求還不完全一致,為了提升性能, 交易記錄會(huì)使用異步的方式來(lái)將數(shù)據(jù)投遞給使用方。 交易記錄在入庫(kù)時(shí),投遞消息到消息系統(tǒng)中。使用方監(jiān)聽這個(gè)消息,一旦收到新消息,則從交易記錄庫(kù)中查詢數(shù)據(jù),獲取數(shù)據(jù)并更新到庫(kù)中。關(guān)于此類數(shù)據(jù)同步的文章不少,這里就不詳細(xì)介紹。
軋帳
軋帳是按照客戶訂單號(hào)來(lái)比較本地交易記錄和渠道交易記錄是否一致。從算法角度,是計(jì)算兩個(gè)數(shù)組的差異。在單機(jī)運(yùn)行時(shí),可以采用的算法不少,這里不詳細(xì)介紹。 我們推薦采用mapreduce來(lái)軋帳,這有個(gè)優(yōu)勢(shì),可以按照訂單號(hào)將渠道提供的記錄和本地記錄shuffle到同一個(gè)reduce處理上,這樣就可以很容易進(jìn)行數(shù)據(jù)比對(duì)。 軋帳中最大的坑,莫過(guò)于切分點(diǎn)的問(wèn)題。
比如以整0點(diǎn)為切分點(diǎn),那存在一個(gè)問(wèn)題,本地23:59發(fā)起的交易,到了渠道側(cè),可能會(huì)在00:01處理,這一筆交易變成第二天的帳了。實(shí)際處理中,一筆交易在渠道側(cè)處理,花上幾分鐘都有可能。 對(duì)于切分點(diǎn)附近無(wú)法確認(rèn)的帳,做一個(gè)時(shí)間窗,在時(shí)間窗內(nèi)的數(shù)據(jù),留待第二天對(duì)賬時(shí)繼續(xù)處理。
平帳
發(fā)現(xiàn)兩邊不一致的數(shù)據(jù),那應(yīng)該如何處理?數(shù)據(jù)量不大時(shí),記錄起來(lái),人工甄別就行。但如果數(shù)據(jù)量很大,每天上千條,人工處理就成本太高了。這個(gè)沒有統(tǒng)一的處理方法,需要根據(jù)有問(wèn)題的數(shù)據(jù),做個(gè)分析,然后做自動(dòng)處理。 針對(duì)交易記錄的對(duì)賬的處理,主要有如下情況:
- 本地未支付,支付渠道已支付。這主要是本地未正確接收到渠道下發(fā)的異步通知導(dǎo)致。 一般處理是將本地狀態(tài)修改為已支付,并做響應(yīng)的后續(xù)處理,比如通知業(yè)務(wù)方等。
- 本地已支付,支付渠道已支付,但是金額不同,這個(gè)需要人工核查。
- 本地已支付,但是支付渠道中無(wú)記錄;或者本地?zé)o記錄,支付渠道有記錄。在排除跨日因素外,這種情況非常少見,需要了解具體原因后做處理。
針對(duì)退款的對(duì)賬處理,主要有如下情況:
- 本地未退款,支付渠道已退款,則以支付渠道為準(zhǔn),修改本地為已退款狀態(tài),并觸發(fā)后續(xù)處理。
- 本地已退款、支付渠道已退款,但是金額不同,需要人工核查;
- 本地已退款,但是支付渠道無(wú)記錄;或者支付渠道有記錄,但是本地沒有。 在排除跨日因素外, 這種情況非常少見,需要了解具體原因后做處理。
總之,對(duì)賬工作,即復(fù)雜也不復(fù)雜。需要細(xì)心,對(duì)業(yè)務(wù)要有深入的了解,并選擇合適的架構(gòu)。
相關(guān)閱讀
支付系統(tǒng)設(shè)計(jì):支付系統(tǒng)的賬戶模型(一)
作者:鳳凰牌老熊,程序員 & 架構(gòu)師,來(lái)自中科大的本科,研究生在軟件所學(xué)習(xí)。先后在中科輔龍、三星(中國(guó))研究院和國(guó)內(nèi)一些大型的互聯(lián)網(wǎng)公司呆過(guò)。在中科輔龍公司負(fù)責(zé)電子政務(wù)內(nèi)容管理系統(tǒng)建設(shè),負(fù)責(zé)研發(fā)龍馭系列產(chǎn)品的研發(fā),這款產(chǎn)品最終實(shí)施到2000多個(gè)電子政務(wù)網(wǎng)站上,期間也參與了一些支付反洗錢以及支付系統(tǒng)的建設(shè)。之后在三星中國(guó)研究院,負(fù)責(zé)自然語(yǔ)言處理(NLP)以及智能家居相關(guān)項(xiàng)目。智能家居項(xiàng)目在2014CES消費(fèi)電子展上作為三星重點(diǎn)項(xiàng)目推介。2014年開始加入愛奇藝公司,負(fù)責(zé)數(shù)據(jù)倉(cāng)庫(kù)和支付系統(tǒng)的建設(shè)。
本文由@鳳凰牌老熊(微信公眾號(hào):shamphone) 原創(chuàng)發(fā)布于人人都是產(chǎn)品經(jīng)理 。未經(jīng)許可,禁止轉(zhuǎn)載。
消費(fèi)券的發(fā)放和使用
1. 在收到支付成功回調(diào)后保留渠道支付成功時(shí)間,對(duì)賬時(shí)采用渠道時(shí)間來(lái)進(jìn)行對(duì)賬;
2. 如果渠道側(cè)不提供成功時(shí)間,對(duì)賬時(shí)采用平臺(tái)時(shí)間與渠道時(shí)間進(jìn)行對(duì)賬,對(duì)賬時(shí)平臺(tái)長(zhǎng)款時(shí)間在接近日切點(diǎn)時(shí)可放到第二日進(jìn)行對(duì)賬,如渠道長(zhǎng)款可根據(jù)渠道ID到我平臺(tái)支付表中查詢,進(jìn)行差錯(cuò)狀態(tài)細(xì)化,有可能因?yàn)闀r(shí)間的差異此筆交易已成功;
3. 如果交易數(shù)據(jù)量大,可采用分批對(duì)賬,MAPA – MAPB 流水排序交易,已10萬(wàn)數(shù)據(jù)分頁(yè)查詢?yōu)槔认耐甑臄?shù)據(jù)進(jìn)行下次分頁(yè)查詢,差異先統(tǒng)一存儲(chǔ),所有數(shù)據(jù)對(duì)賬后進(jìn)行整體處理;
4. 對(duì)賬產(chǎn)生的差異應(yīng)針對(duì)渠道或平臺(tái)賬戶進(jìn)行資金凍結(jié),來(lái)保證對(duì)賬數(shù)據(jù)與賬戶數(shù)據(jù)一致性;
很有條理 很有用!干活滿滿
請(qǐng)問(wèn)現(xiàn)在對(duì)賬思路都是 人工上傳賬單或者自動(dòng)獲取賬單,然后找到業(yè)務(wù)訂單去對(duì)賬(賬單-訂單對(duì)賬),是否還有別的對(duì)賬思路呢
請(qǐng)問(wèn),現(xiàn)在第三方支付都要統(tǒng)一接入網(wǎng)聯(lián),這些功能是否還需要系統(tǒng)自己實(shí)現(xiàn)呢
簡(jiǎn)書上有人轉(zhuǎn)載你的文章,有標(biāo)明轉(zhuǎn)載地址。是你自己么?還是朋友?http://www.jianshu.com/u/897526abf799
感謝 感謝,良心干貨篇,多謝
對(duì)于一個(gè)即將轉(zhuǎn)入支付系統(tǒng)的程序猿, 看到你的文章如沐春風(fēng), 感覺業(yè)務(wù)思路上清楚多了,多謝講解
我也是支付行業(yè),很是喜歡你的筆記和感悟,對(duì)一個(gè)踏入新行業(yè)不久的新人來(lái)說(shuō),是一件值得慶幸的事情。
我想咨詢下,像這種交易單的下載通過(guò)支付寶,微信,銀聯(lián)借口就能直接下載獲取嗎?還是說(shuō)是需要財(cái)務(wù)人員到相關(guān)的平臺(tái)將相關(guān)的賬單導(dǎo)出放到FTP,程序再定時(shí)獲取與本地的訂單數(shù)據(jù)來(lái)做核對(duì)呢?
請(qǐng)教一下:交易流水表,如何按照交易流水號(hào)來(lái)做分庫(kù)、分表的依據(jù)。這樣需要查詢某個(gè)用戶的所有交易流水,由于是按照交易流水切分,數(shù)據(jù)會(huì)分散在多個(gè)庫(kù)、多個(gè)表。展示起來(lái),就需要跨庫(kù)、跨表查詢了。尤其是,要分頁(yè)展示某個(gè)用戶所有的交易流水。在支付寶上有。求教作者這方面,怎么處理?
按照用戶來(lái)分表分庫(kù),流水號(hào)根據(jù)用戶ID來(lái)生成。
對(duì)于切分點(diǎn)附近無(wú)法確認(rèn)的帳,做一個(gè)時(shí)間窗,在時(shí)間窗內(nèi)的數(shù)據(jù),留待第二天對(duì)賬時(shí)繼續(xù)處理。
–請(qǐng)教一下,這個(gè)時(shí)間窗如何設(shè)置?能大概講下?謝謝
點(diǎn)贊,好文。
“軋帳中最大的坑,莫過(guò)于切分點(diǎn)的問(wèn)題”,這個(gè)對(duì)于沒做過(guò)的人,真的不見得注意到這個(gè)坑;
想起我們做通信的時(shí)候,一次正常的呼叫,有很多信令記錄點(diǎn),比如起呼是一個(gè)信令記錄點(diǎn),在23:59分發(fā)起; 接通是一個(gè)信令點(diǎn),在00:01分發(fā)生; 這樣,起呼就在上一個(gè)時(shí)間點(diǎn),接通在下一個(gè)時(shí)間點(diǎn),當(dāng)我們按小時(shí)來(lái)統(tǒng)計(jì)接通率的時(shí)候,這就悲劇了;
這個(gè)怎么統(tǒng)計(jì)比較好啊
前輩,解決切分點(diǎn)問(wèn)題的時(shí)間窗是指什么呢?
好文!但是發(fā)現(xiàn)一個(gè)小小的瑕疵,“本地未退款,支付渠道已退款,則以支付渠道為準(zhǔn),修改本地為已退款狀態(tài),并出發(fā)后續(xù)處理?!边@個(gè)地方是不是應(yīng)該為“觸發(fā)”。
謝謝, 是筆誤。
以表敬意
太棒了,居然有一系列的分享,大神就是這樣
看到以前自己天天面對(duì)的業(yè)務(wù),哈哈,還有些懷念呢。
現(xiàn)在面對(duì)什么業(yè)務(wù)
時(shí)髦的O2O,線下智能POS與線上結(jié)合
好文,希望能寫的更深入點(diǎn),多舉一些實(shí)際場(chǎng)景 ??
良心文章
謝謝。