手機沒網(wǎng)了,卻還能支付,這是什么原理?
編輯導(dǎo)讀:在一些信號很差的地方,手機沒網(wǎng)了卻依然還能支付,這是什么原理呢?本文將從四個方面對這個問題展開分析,希望對你有幫助。
現(xiàn)在生活已經(jīng)離不開微信/支付寶電子支付,平常出去吃飯、購物只要帶個手機,就可以解決一切,以致于現(xiàn)在已經(jīng)好久沒摸過真?了。有一次出去吃飯,排著隊付錢,等著過程非常無聊,準(zhǔn)備拔出手機來把荒野亂斗,卻發(fā)現(xiàn)這個地方竟然連不上網(wǎng)。
看著手機明明信號滿格,但是就是顯示網(wǎng)絡(luò)無連接,蘋果手機用戶痛,誰用誰知道。
由于沒有網(wǎng)絡(luò),而我又沒帶錢,所以就怕付錢的時候因為手機沒網(wǎng),沒辦法使用支付寶扣款。正想著時,已經(jīng)排到了我,不管三七二十一,先用下支付寶試試,實在不行就不吃了。
不過沒想到,當(dāng)商家用掃碼搶掃描支付寶上付款碼支付以后,雖然我的手機最終沒有彈出支付成功的頁面,但是商家端顯示支付成功,并成功打印出了小票,過了一會,我的手機收到支付寶扣款短信。
因為我最近的工作對都是與微信/支付寶有關(guān),整體支付流程還是比較清楚,但是付款碼為什么能離線支付確實不是很清楚,所以研究了一番,于是有了今天的文章。
一、科普支付方式
在聊付款碼離線原理之前,我們先給不熟悉支付寶/微信支付方式同學(xué)先科普一下常見的兩種支付方式。
微信、支付寶線下支付常用支付方式有兩種,一種是我們打開手機,主動掃描商家提供碼牌,這種支付方式一般稱為主掃支付(用戶主動掃碼)。
以支付寶為例,付款流程如圖所示:
圖片來自支付寶官網(wǎng)
第二種則是我們打開手機,展示我們的付款碼,然后商家使用掃碼槍等工具獲取付款碼完成支付,這種支付方式一般稱為被掃支付(用戶被掃碼)。
以支付寶為例,付款流程如圖所示:
圖片來自支付寶官網(wǎng)
對于第一種方式,需要手機端 APP 掃碼,然后彈窗確認(rèn)付款,這種方式是沒有辦法在手機沒有網(wǎng)絡(luò)的情況完成支付,所以我們上文說的沒有網(wǎng)絡(luò)的情況特指付款碼支付的場景。
二、付款碼付款流程
在聊付款碼離線支付的前提前,我們先來來看下付款碼的整體流程,以超市購物為例,一次付款碼的支付信息流如圖所示:
參考知乎@天順
這個過程商家后臺系統(tǒng)是需要調(diào)用的支付寶條碼支付的接口,完成支付。
「由于商家后臺需要在線聯(lián)網(wǎng)與支付寶后臺通訊,所以說付款碼的離線支付,指的是客戶端沒有的網(wǎng)絡(luò)的情況,商家端其實必須實時聯(lián)網(wǎng)在線?!?/p>
一次付款碼接口調(diào)用流程如圖所示:
來自支付寶官網(wǎng)
通過上面兩張圖,我們整體了解付款碼交互流程。
付款碼的技術(shù)方案其實可以分為客戶端在線與離線的兩種情況,下面我們來看下兩種方案具體實現(xiàn)方式。
三、在線碼方案
客戶端在線碼的方案,這個應(yīng)該比較容易想到,只要支付寶/微信在登錄的情況下,點擊付款按鈕,客戶端調(diào)用后臺系統(tǒng)的申請付款碼接口。
后臺系統(tǒng)受到請求之后,生成一個付款碼,然后在數(shù)據(jù)庫保存付款碼與用戶的關(guān)系,并且返回給客戶端。
只要客戶端在有效期內(nèi)展示該付款碼,就可以完成支付,否則該二維碼就將會過期。
使用這種方案,相對來說比較安全,因為每次都是服務(wù)端生成碼,服務(wù)端可以控制冪等,沒有客戶端偽造的風(fēng)險的。
另外即使需要對付款碼規(guī)則調(diào)整,比如付款碼位數(shù)增加一位,我們只要調(diào)整服務(wù)端代碼即可,客戶端都無需升級。
「不過這種方案缺點也比較明顯,客戶端必須實時在線聯(lián)網(wǎng),沒有網(wǎng)絡(luò)則無法獲取付款碼。」
另外,現(xiàn)在有一些智能設(shè)備也開始支持支付寶支付,這些設(shè)備中很大一部分是沒有聯(lián)網(wǎng)的功能(比如小米手環(huán)四),那這種情況是沒辦法使用在線碼方案。
基于這種情況,所以開始有了離線碼方案。
四、離線碼方案
說起離線碼大家可能比較陌生,但是實際上你如果仔細(xì)觀察,其實很多場景都用到了離線碼。
比如說以前去黑網(wǎng)吧玩夢幻西游的時候,賬號總是被盜。
沒辦法,花了一筆重資買了一個網(wǎng)易將軍令,每次登錄的時候,除了輸入用戶名與密碼以外,還需要輸入動態(tài)口令。從此賬號就很少被盜了。
又比如說每次網(wǎng)易支付的時候,我們除了輸入銀行卡密碼以外,還需要輸入網(wǎng)銀盾上動態(tài)碼,這樣才能完成支付。
畫外音:這里又要吐槽一下,網(wǎng)銀盾以前真的超難用,動不動就驅(qū)動不兼容。還記得當(dāng)初用網(wǎng)銀充值黃鉆,搞了一下午都沒有成功!
當(dāng)然上面這些可能已經(jīng)是老古董了,很多人都可能沒用過,現(xiàn)在比較流行是「手機驗證器APP」,比如 「Google Authenticator」 等。
這種令牌器,動態(tài)產(chǎn)生一次性口令(「OTP, One-time Password」),可以防止密碼被盜用引發(fā)的安全風(fēng)險。
其實付款碼離線方案技術(shù)原型就是基于這種方案,所以下面我們就基于 Google Authenticator,來了解一下這其中的原理。
1. 動態(tài)口令技術(shù)原理
首先如果我們需要使用 「Google Authenticator」,我們需要在網(wǎng)站上開啟二次驗證功能,以 Google 賬號為例,在設(shè)置兩步驗證的地方可以找到如下設(shè)置:
當(dāng)我們點擊設(shè)置,將會彈出一個二維碼,然后使用 「Google Authenticator」 APP 掃碼綁定。
當(dāng)我們綁定之后, 「Google Authenticator」 APP 將會展示動態(tài)碼。
我們來解析一下這個二維碼,對應(yīng)下面這個字符串:
otpauth://totp/Google%3Ayourname@gmail.com?secret=xxxx&issuer=Google
上面的字符串中,最重要就是這一串密鑰 secret,這個是一個經(jīng)過 「BASE32」 編碼之后的字符串,真正使用時需要將其使用「BASE32」 解碼,處理偽碼如下:
original_secret=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxsecret=BASE32_DECODE(TO_UPPERCASE(REMOVE_SPACES(original_secret)))
「這個密鑰客戶端與服務(wù)端將會同時保存一份,兩端將會同樣的算法計算,以此用來比較動態(tài)碼的正確性?!?/p>
我們以客戶端為例,生成一個動態(tài)碼,首先我們需要經(jīng)過一個簽名函數(shù),這里 **Google Authenticator **采用的 「HMAC-SHA1」,這是一種基于哈希的消息驗證碼,可以用比較安全的單向哈希函數(shù)(如 SHA1)來產(chǎn)生簽名。
簽名函數(shù)偽碼如下:
hmac=SHA1(secret+SHA1(secret+input))
上面函數(shù)中的,input 使用當(dāng)前時間整除 30 的值。
input=CURRENT_UNIX_TIME()/30
這里時間就充當(dāng)一個動態(tài)變參,這樣可以源源不斷產(chǎn)生動態(tài)碼。
「另外這里整除 30,是為了賦予驗證碼一個 30 秒的有效期?!?/p>
這樣對于用戶輸入來講,可以有充足時間準(zhǔn)備輸入這個動態(tài)碼,另外一點客戶端與服務(wù)端可能存在時間偏差,30 秒的間隔可以很大概率的屏蔽這種差異。
畫外音:這個有效時間其實很考量,如果比較長,安全性就差。
如果比較短,用戶體驗就很差,不容易輸入準(zhǔn)備。
經(jīng)過 「HMAC-SHA1」 簽名函數(shù)以后,我們得到一個長度為 40 的字符串,我們還需要將其轉(zhuǎn)化為 6 位數(shù)字,方便用戶輸入。處理的偽碼如下:
four_bytes=hmac[LAST_BYTE(hmac):LAST_BYTE(hmac)+4]large_integer=INT(four_bytes)small_integer=large_integer%1,000,000
完整的算法偽碼如下:
original_secret=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxsecret=BASE32_DECODE(TO_UPPERCASE(REMOVE_SPACES(original_secret)))input=CURRENT_UNIX_TIME()/30hmac=SHA1(secret+SHA1(secret+input))four_bytes=hmac[LAST_BYTE(hmac):LAST_BYTE(hmac)+4]large_integer=INT(four_bytes)small_integer=large_integer%1,000,000
當(dāng)客戶端將動態(tài)碼上傳給服務(wù)端,服務(wù)端查詢數(shù)據(jù)庫獲取到用戶對應(yīng)的密鑰,然后使用同樣的算法進(jìn)行處理生成一個動態(tài)碼,最后比較客戶端上傳動態(tài)碼與服務(wù)端生成是否一致。
2. 付款碼離線方案
上面我們了解了動態(tài)口令的實現(xiàn)方案,付款碼生成原理其實也大致如此。
不過付款碼離線方案采用動態(tài)密鑰的方式(「全局唯一」),定時請求服務(wù)端更換密鑰,以此保證更高的安全性。
另外在一次性動態(tài)口令方案,需要雙方基于同樣的秘鑰,所以服務(wù)端需要明確知道這「背后正確用戶」。以上面的登錄場景為例,登錄過程輸入用戶名,服務(wù)端就可以根據(jù)這個在數(shù)據(jù)庫中查詢相應(yīng)的密鑰。
但是在付款碼的支付場景中,支付過程僅僅傳遞一個付款碼,就可以向相應(yīng)的用戶扣款。不用想,這個付款碼這串?dāng)?shù)字一定包含相應(yīng)的用戶信息。
所以付款碼的相應(yīng)的算法相比動態(tài)碼會更加復(fù)雜,這樣才可以有效保證安全性。
看到這里,不知道你們是否急切想了解這套算法那?
這種算法豈能是我們能掌握的?
支付寶核心算法咱不知道,但是我們可以從其他人公開設(shè)計方案了解一個皮毛。
這里小黑哥給你一個知乎網(wǎng)友@反方向的鐘回答的離線二維碼實現(xiàn)方式,給你 look look。
來自:https://www.zhihu.com/que
stion/49811134/answer/135886638
3. 付款碼離線碼的劣勢
最后我們來看下付款碼離線方案的劣勢:
第一,算法調(diào)整不靈活,如果相關(guān)算法較大的調(diào)整,可能需要升級客戶端,并且這個期間服務(wù)端還需要兼容新老算法產(chǎn)生的付款碼。
第二,安全性問題,正常的情況相關(guān)密鑰無法被普通用戶獲取,但是架不住有有心之人。他們可能通過獲取手機用戶 Root 權(quán)限或者越獄手機,利用惡意程序獲取密鑰,然后隨意生成付款碼。
看到這一點,大家可能會擔(dān)心自己的錢包安全了。不過這一點,我覺得不過過分擔(dān)心,螞蟻集團(tuán)這么多大神,不是吃干飯的,他們肯定有很多措施保證支付安全。
第三數(shù)據(jù)碰撞問題,A 用戶生成付款碼算出來與 B 用戶一致,這就 Hash 算法一樣,再怎么優(yōu)秀的算法,也有概率才生一樣的額 Hash 值。
這就導(dǎo)致原本是扣用戶 A 的錢,最后卻扣了 B 用戶。這樣一來,確實很烏龍,對于 B 用戶來講,莫名其妙被扣錢了。
不過放心,這種事放到放到現(xiàn)在,我覺得還是比買彩票中獎低,所以這種事還是不用過分擔(dān)心了。
即使真被誤扣了,放心,支付寶這么大體量肯定會跟客戶賠錢的。
五、最后
最后總結(jié)一下,我們平常使用付款碼支付,其實原理就是商家端獲取我們手機 APP 付款碼(「其實就是一串?dāng)?shù)字」),然后后臺調(diào)用支付寶支付接口完成扣款。
這個流程商家端后臺程序必須聯(lián)網(wǎng)在線,但是對于我們客戶端來講可以在線,也可以離線。
如果我們客戶端在線,那就可以通過服務(wù)端向客戶端發(fā)送付款碼,這種方式更加安全,靈活,但是對于弱網(wǎng)環(huán)境下,體驗就很差。
如果我們客戶端沒網(wǎng),那就通過客戶端通過一定算法生成付款碼,服務(wù)端收到經(jīng)過相關(guān)校驗,確認(rèn)是哪個用戶,確認(rèn)碼有效性,并且完成扣款。這種方式,適合客戶端沒有網(wǎng)絡(luò)的情況,不過相對不靈活,且安全性稍差。
嘿嘿,了解原理,有沒有覺得還是挺有意思的~
下次排隊付款錢,如果手機沒網(wǎng),不要擔(dān)心尷尬,放心拿出手機付錢~
對了,看完記得一鍵三連,這個對我真的很重要。
六、參考
- https://www.zhihu.com/question/49811134/answer/135886638
- https://garbagecollected.org/2014/09/14/how-google-authenticator-works/
作者:樓下小黑哥;微信公號@程序通事,支付行業(yè),后端技術(shù)
本文由 @樓下小黑哥 原創(chuàng)發(fā)布于人人都是產(chǎn)品經(jīng)理。未經(jīng)許可,禁止轉(zhuǎn)載
題圖來自Pexels,基于CC0協(xié)議
如果手機長時間處于無網(wǎng)狀態(tài),收不到密鑰,是不是離線支付也沒用了
從收款場景處理離線支付其實很崩潰,去年做的時候支付寶的離線支付并不給系統(tǒng)實時返回支付成功,導(dǎo)致大量主動查詢補通知,甚至對未支付的支付寶訂單多次輪詢,非常占資源
為什么離線支付沒有實時返回通知呢?
團(tuán)隊
講得好!
認(rèn)同