回眸曾經的專案,與第三方支付相關,所帶來的溝通問題
導讀
筆者在校期間,通過自學java。學校裡也開過這門課,但是,講的都是一些基礎,比如java的表示式、基本型別、自定義型別等等。也都是很基礎的東西,就連lambda表示式都沒有。然而,讓我們交的作業,是用java-web開發出的網站。我當時做的是與圖書共享相關的網站。當時滿腔熱血的想著去創業,但是,因為自身還沒離開學校,社會經驗不是特別足,於是,這件事就擱淺了。
去年六月份畢業,參加了班級的散夥飯後,大家也都各奔東西。但大部分從事軟體開發的行業,有些人進入外包公司,有些人進入了遊戲公司。不管進入到什麼行業,在最初的一段時間中,遇到一個教你的人很重要。這樣,你可以學校到很多東西。當然,你自己也得努力學習。
我依稀記得第一次做專案,那個專案做得真是一塌糊塗,權當我個人練手用了。對於個人來說,成長是非常快的,但是,對於企業來說,這是一種損失。真的是損失。因為,企業讓你來做事,不是讓你來試驗的。自從第一個專案失敗後,也不能說失敗吧,至少做得不夠完美。專案沒有達到鬆散耦合的程度。我在開發的過程中,遇到了各種各樣的問題,在他人的幫助下,慢慢地適應了開發強度。
當時,我給自己定義的是java後端開發工程師,因而,介面都是通過postman來測試。我們的持久層使用的hibernate框架,於是,仿照hibernate寫個框架,參考我的部落格: 模仿hibernate框架,詳解hibernate部分方法設計 ;同時使用spring容器集合該框架,於是,仿照了spring寫個框架,可以參考我的部落格: 模擬spring框架,深入講解spring的物件的建立 ;資料過濾的框架是Apache下的beanutils框架,於是,模擬beanutils寫個框架,參看我的部落格: 只因資料過濾,方可模擬beanutils框架
框架能寫的出來,而這只是java端的,我並不知道前端的一些情況,比如前端和後端是如何進行資料互動的,前端如何在頁面展示資料等等。於是,下載了前端的頁面。我們前端的頁面放置在SVN上,後端程式碼放置在git上。自己慢慢地根據前端頁面去摸索,神奇地是能夠展示出資料。帶我的人看我做的還不錯,於是,教我前後端一起開發。
我們這個是前後端分離的,前端呼叫後端的介面。在學校裡面也學過前端的一些知識,比如CSS3、HTML5,jQuery,JavaScript等等,那時並不是專案開發,有些東西只是自己弄著玩的。但公司專案的開發和自己開發完全不是一回事,需要掌握很多的前端知識。
不久,做了個完整的前端專案,因為有學校的經驗,有些東西很快就掌握了,但是,其他很多東西還不怎麼會,真的很腦大。但隨著專案的深入,才發現自己在學校學的知識,還是遠遠地不夠專案開發所用的。於是,不斷地充電,不斷地向前輩的學習,現在,前端的很多東西也知道了。
現在,回過頭看我寫的前端程式碼,再看前輩給我寫的前端程式碼,我發現我那時寫的真的垃圾,可以用不堪入目這個詞來形容。但是,當時設計的頁面還是蠻酷的,也得到了老闆的認可。這是我值得驕傲的事。
我不斷地嘗試前後端的開發,也從中知道了vue.js,bootstrap,jQuery、echart等前端框架。也知道了本地ip和區域網ip的區別,以及如何用小米球做本地除錯。從而更知道了,如何實現前後端分離。我越來越相信這句話,只要你努力,就會有人幫助你。
隨著時間地推進,老闆接了一個專案,就是圖書共享的專案。我當時聽到這個專案後,我就感覺到有點難過。這和我畢設所做的專案思想是一致的,如果,我當時能夠勇敢一點,也許,我就推廣我的這個專案了。但是,我沒有。
世界就是這麼殘酷,不允許你有絲毫地猶豫。於是,老闆再接下一個專案後,專案名為雲碼兌換平臺。使用者收到某家公司給的代幣(福利),拿到代幣到這個平臺上兌換成錢。錢可以儲存在自己的餘額中,這就相當於微信錢包,餘額可以提現到銀行卡的中。也可以使用餘額來購買商品。這分為企業錢包和個人錢包,企業錢包和個人錢包是不一樣的。
我沒做個類似專案,於是,就接過來做了。老闆說這個專案對我來說,難度係數還是比較大的。我就下定決心把這個專案給做好,因為我知道可以從這個專案學到很多東西。我們的第三方支付平臺是連連支付,杭州的一家公司。
支付
這個專案可以說沒有任何人帶我,完全由我自己參考連連給的文件,邊學習邊嘗試著去開發。這樣,也培養了我呼叫第三方介面,參讀他們文件的能力。可以說,未嘗不是一種收穫呢?
我們老總說過年前夕,只做支付相關的業務,也就是,使用者提交代幣兌換金額的申請,後臺管理員接收到這個申請後,從後臺給使用者打款。如圖所示:
後臺管理員點選批量打款時,服務端會做資料的篩選,晒選出狀態為待轉讓的客戶打款,其他情況下一律不打款。就這麼一個小小的支付按鈕,其內部涉及太多的知識點。
我之所以害怕,是因為對未知的恐懼。一開始做支付非常難,其實,做完之後,也沒有我想像的那麼難。但是,我還是需要做一些準備工作。比如,下載連連支付的SDK。但要考慮到SDK是什麼SDK?SDK分為兩種,一種是應用到java開發中,一種是應用到Android或iOS開發中。因為,連連那邊沒有將其區分開來,從而造成錢包專案的延誤。這個會在下文提到。
下載好連連支付的SDK後,我們通過maven建立專案,使用的是阿里雲的中央倉庫,其並不存在連連SDK。因而,我們需要配置本地的Maven庫。這個網上都有教程的,我在這裡就不細說了。
配置好了SDK後,我們需要了解簽名機制,連連使用的RSA簽名機制,如圖所示:
至於連連為什麼使用RSA簽名,可以參考我的這篇部落格: 支付與簽名原串的那些事,但選擇排序生成簽名原串 。
我們將生成的pkcs8格式的公鑰上傳到連連商戶站,再從連連的商戶站下載其連連公鑰。私鑰用以加簽,公鑰用以驗籤,這用以提高資料的安全性。
私鑰怎麼加簽?每個公司的加簽方式是不一樣的,支付寶有支付寶的加簽方式,微信有微信的加簽方式。這裡,我就說說連連支付的加簽方式。我們在向連連發送支付請求前,需要封裝我們的請求引數,將請求引數以一定格式的方式儲存,這就是簽名原串,如圖所示:
將簽名原串和我們的pkcs8格式的私鑰共同加密,這就呼叫到jdk數字簽名的這幾個包:
import java.security.KeyFactory; import java.security.PrivateKey; import java.security.PublicKey; import java.security.Signature; import java.security.spec.PKCS8EncodedKeySpec; import java.security.spec.X509EncodedKeySpec;
得到加簽後的簽名字串,再呼叫連連支付的 LianLianPaySecurity.encrypt(JSON.toJSONString(preq), PaymentConstant.LIAN_PUBLIC_RSA_KEY);
加密演算法,該演算法有兩個引數,一個是包括簽名的氫氣引數,一個是下載下來的連連公鑰。
如果請求連連支付成功的話,其會返回一段字串,該字串包含連連的簽名原串、私鑰簽名串等其他資訊。連連那邊的簽名串的加簽方式,和我們這邊的是一樣的。我們從連連下載下來的連連公鑰,其公鑰也是其由私鑰生成而來。其將請求引數封裝成簽名原串,將私鑰和簽名原串共同加簽成簽名串。
因而,我們這邊拿到了連連的簽名原串和簽名串之後,就要進行驗簽了。怎麼驗籤呢?我們這個時候,就用到了從連連的商戶站下載的連連公鑰了,如程式碼所示:
/** * Created By zby on 17:28 2018/12/18 * RSA簽名驗證 * <p> * * @param reqObj: The obtained asynchronous notification body * * @param rsa_public: The public key provided by LianLian */ public static boolean checkSignRSA(String response) { logger.info("【響應引數】申請付款的響應引數為:" + response); if (StringUtils.isBlank(response)) { return false; } JSONObject reqObj = JSONObject.parseObject(response); String lianPubkey = PaymentConstant.LIAN_PUBLIC_RSA_KEY; String lianSigned = reqObj.getString("sign"); String lianSignSrc = genSignData(reqObj); ifNullThrow(lianPubkey, ResultCodeEnum.NOT_CONFIG_LIAN_PUBLIC_KEY_VALUE); try { if (TraderRSAUtil.checksign(lianPubkey, lianSigned, lianSignSrc)) { return true; } else { return false; } } catch (Exception e) { return false; } }
底部呼叫的是TraderRSAUtil.checksign這個方法,其內部如程式碼所示:
/** * 簽名驗證 * * @param lianPubkey下載好的連連公鑰 * @param lianSignSrc 連連的簽名原串,也就是將返回串封裝成jsonObject * @param lianSigned連連加密簽名簽名,包括簽名原串+私鑰 * @return */ public static boolean checksign(String lianPubkey, String lianSigned, String lianSignSrc) { try { //【1】獲取公鑰 //獲取KeyFactory,指定RSA演算法 KeyFactory keyFactory = KeyFactory.getInstance(PaymentConstant.SIGN_TYPE); //將BASE64編碼的公鑰字串進行解碼 BASE64Decoder decoder = new BASE64Decoder(); //將BASE64解碼後的位元組陣列,構造成X509EncodedKeySpec物件,生成公鑰物件 PublicKey publicKey = keyFactory.generatePublic(new X509EncodedKeySpec(decoder.decodeBuffer(lianPubkey))); byte[] signed = decoder.decodeBuffer(lianSigned); //【2】使用公鑰,進行驗籤 //獲取Signature例項,指定簽名演算法(與之前一致) Signature signature = Signature.getInstance(PaymentConstant.MD5_WITH_RSA); //載入公鑰 signature.initVerify(publicKey); //更新原資料 signature.update(lianSignSrc.getBytes(BaseConstant.CHARSET)); //公鑰驗籤(true-驗籤通過;false-驗籤失敗) return signature.verify(signed); } catch (Exception e) { e.printStackTrace(); } return false; }
這就是一個支付的流程,不論是任何第三方支付,其都要有加簽和驗籤,因為,這是最基本的安全機制。只不過,各個公司的加簽和驗籤方式有所不同而已。
當然,有些東西就沒必要使用RSA加簽和驗簽了,比如連連那邊的繫結銀行卡的業務,其加簽方式就是用MD5加簽的。但是,支付涉及到了金額,這個,只要與錢相關的業務。都需要謹慎處理,也許,RSA不是最好的加密方式,但就目前而言,其還是比較安全的。
錢包
為什麼要做錢包呢?做任何事都要有其目的。
我們做的是福利平臺。屆時,許多商戶會入駐該平臺,給其使用者提供不同的優惠卡或者錢,比如流量充值卡、話費充值卡,中石油的加油卡等。不同商戶提供不同的(虛擬)商品,不同的商品對應多少錢,比如話費卡30元,流量卡30元等。商戶需要在錢包中充錢(充值),便於使用者兌換。
使用者拿到這些卡消費時,商戶的錢包裡的錢會減少。使用者可以通過代幣兌換這些充值卡,也可以通過代幣兌換錢,商戶的錢就會變少,而使用者的錢包的錢增加,其可以提現到銀行卡(提現),也可以購買其他產品(支付);在購買時,錢包餘額不夠,可以充值到連連錢包(充值)。如果已經購買了,也可以,申請退款(退款)。 同時,也可以檢視提現記錄(提現查詢),支付記錄(支付查詢);
總的來說,電子錢包可以儲存使用者的金額,相當於微信支付中的餘額,也相當於我們現實中的錢包。使用者可以支配錢包金額(餘額),但並非任意支配。因為使用者支配的額度不能超過錢包的額度。
今年都到了三月二十四號了,我們的錢包也已經就開始做了。但是,可能,我們這邊的產品經理和連連那邊的業務沒有溝通好,其所提供給我們的web元件全部是走他們的頁面。我當時問他們的頁面支不支援響應式佈局,他們堅決地回答說不支援。如圖所示:
這在PC端的瀏覽器中看,介面還是可以的,但是,如果用手機端看的話,其會非常的彆扭,如圖所示:
你會發現,這簡直不能使用。我當時和對方說,這種效果的使用者體驗不好。他們說我們這邊提供的了手機端的SDK元件,我看了他們給的SDK元件,其完全是Android開發和iOS開發的SDK元件。他們以為我們用的測試Android開發的,而我們這是微信公眾號,是嵌入在微信中的h5頁面,自然是不能使用的。
這就是產品經理和業務沒有溝通好帶來的誤會,產品經理說他們那邊的業務不懂技術。他們那邊的業務以為SDK就是SDK,不區分是不是Android和iOS,還是服務端java的SDK。業務也沒有問他們那邊的技術。因而,浪費了這一個月的時間。我們這是從開年就開始做錢包了,做到現在就遇到了這個問題。所以,很多東西都要重新做。
對於企業來說,這是一個損失。但對於我個人來說,這是一種成長。不論是技術方面,還是與人溝通方面,都是一種成長。
我們後來和他們說,web元件走不通了,能不能通過API的方式自定義介面,他們說他們這邊也是提供的,不過,是最近剛開放的API的介面。我們這邊的產品經理非常生氣,說你們那邊沒有的話,就早點和我們說嗎,免得浪費我們的時間。
我在開發的起始階段,看了他們的SDK,也有和對方溝通。我說我們這是公眾號開發,你們這個SDK好像有問題,他們那邊說你可以使用web元件,當時,也沒有說到這個API的介面。
溝通問題
出現上述的問題,也不能說是誰的錯。事情已經出了,生氣也沒用。因而,我就從這件事中吸取教訓。凡是,都要先溝通好,比如,就這個SDK的問題。是什麼樣的SDK?嵌入在java中的SDK,還是純安卓的SDK。這個,就需要考慮清楚。溝通是非常重要,如果溝通的不及時,既浪費了人力和物力,也讓雙方都不開心。
因而,以後為人處事,一定要學會溝通。不過,這對於我來說,也是一次小小的成長吧。在成長的過程中,就會摔跟頭的。但站起來之後,回頭看看絆倒自己的是什麼?是石頭,還是香蕉皮?以後,再遇到這樣的事情,能不能及時的溝通。
祝自己在成長的過程,愈挫愈勇吧。