一般,在微信公眾號中的商城都是需要支持微信支付和支付寶支付的,當然,較大的公司對於鵝廠和阿里的站隊就不說了,所以這里簡單記錄一下支付寶支付和微信支付的主要流程。說是簡單介紹,這是因為確實不難,因為前端在這方面,包括微信授權登陸這一塊需要做的都不是很多,而主要的工作量都在后端部分。
支付寶支付
無論是支付寶支付還是微信支付,最開始的步驟當然是將商品列表、商家相關信息、用戶remark、運費、總價等等支付需要的信息通過post請求向后端傳遞,這里介紹支付寶支付,所以假設用戶選擇的是支付寶支付,那么后端返回的就是一個阿里的url,我們通過這個url來進行支付寶的支付。 當然, 首先,我們應該進入一個支付寶引導頁面,即指導用戶在瀏覽器中打開,這是因為微信是不支持支付寶進行付款的,所以支付寶需要單獨在網頁中打開進行支付。注意: 如果真的希望在微信上使用支付寶付款,可以使用微信開發者工具v0.7.0 , 經過測試,這個至少是可以的。 用戶在這個支付寶的url中支付完成之后,最終再跳轉到公眾號的頁面上即可。 至於判斷微信瀏覽器的方法,可以使用下面這種:
util.isWeChat = function () { var browserInfo = navigator.userAgent.toLowerCase(); var weChatPattern = /MicroMessenger/i; if (weChatPattern.test(browserInfo)) { return true; } else { return false; } }
微信支付
微信支付較之於支付寶支付稍顯復雜,公眾號支付開發者文檔對此做了詳細的說明。此文檔的閱讀對象當然就是一些涉及微信支付的研發工程師、運維工程師等等了。並且微信支付從2014年開始到目前2017年6月都在不斷地迭代更新。
支付方式
為了對微信支付有更深層次的了解,我們需要知道下面幾種支付方式:
- 刷卡支付 --- 即微信里我的錢包中的二維碼、條形碼的支付方式,主要用在線下的面對面的收銀場景。
- 掃碼支付 --- 即商戶根據微信支付協議生成的二維碼,用戶通過掃一掃來進行支付。
- 公眾號支付 --- 這也就是用戶在H5頁面中使用JSAPI進行支付的場景。這也是使用比較多的場景。
- APP支付 --- 即在app中使用的支付方式。
支付賬戶
如果商戶希望使用上面的支付方式的其中一種,就需要向微信進行申請,申請成功之后,微信會發來一封郵件,這個郵件上會記錄關於這個支付賬戶相關的信息,即提供給商戶一些API供其調用來完成公眾號內的支付功能。郵件中的參數與將會調用的API有下面的對應關系:

即一個公眾號就是一個應用,它就有唯一的appid,這是在申請公眾號的時候就會有的,而mch_id是微信支付商戶號,這必須是在申請了微信支付功能之后才會有的賬號,用於給商戶分配收款賬號。 key就是api秘鑰。 Appsecret是appid對應的接口密碼,通過這個接口密碼可以使用access_token調用api了,然后通過OAuth2.0接口獲取openid,openid是每一個用戶在一個公眾號中特有的標識,也就是用於標識一個公眾號下不同的用戶。
接口規則
如果希望使用微信支付接口,那就就要遵守它所指定的一些規則: 比如使用https協議,采用post方式提交相應數據,並且這些數據必須是XML的格式,簽名算法是MD5,申請退款、取消訂單也是需要證書的。
並且在調用接口的時候,當然需要傳遞必要的訂單、商品、商鋪等相關的參數。 如交易金額默認單位為分,並且不能有小數。
交易類型也有多種,比如 JSAPI對應的是公眾號支付,NATIVE對應的時原生掃碼支付,APP為app支付,MICROPY是刷卡支付等等。
貨幣類型是CNY,即china yuan。
我們采用的時間都是標准的北京時間。
時間戳就是js中的1970年1月1日0點0分0秒到目前的秒數。
訂單號是由商戶自己定義的不得重復的數字。 重新發起一筆支付要是用原訂單號,避免重復支付。
緊接着就是不同的銀行類型。
安全規范
這一部分是非常重要的一部分。
1. 簽名算法
首先,舍所有發送或者接收的數據為集合M,將集合M中非空參數值的參數(也是鍵值對)按照參數名ASCII碼從小到大排序(字典序),使用URL鍵值對的格式(即key1=value1&key2=value2)的形式拼接成字符串 stringA。 特別注意下面的規則:
- 參數名ASCII碼從小到大進行排序(字典序)。
- 如果參數值為空則不參與簽名。
- 參數名區分大小寫。
- 驗證調用返回或微信主動通知簽名時所傳遞的sign參數不參與簽名,將生成的簽名與該sign值作校驗。
- 微信接口可能增加字段(即微信自己增加的,不是我們傳遞的),驗證簽名時必須支持增加的擴展字段。
緊接着在stringA后面拼接上key(即秘鑰)得到stringSignTemp字符串,然后對stringSignTemp進行MD5運算。 將得到的字符串所有字符轉化為大寫,得到sign值signValue。
舉例如下:
假設傳送的參數如下:
appid: wxd930ea5d5a258f4f mch_id: 10000100 device_info: 1000 body: test nonce_str: ibuaiVcKdpRxkhJA
第一步就是按照key=value的方式按照參數名的ASCII字典序進行排序:
stringA="appid=wxd930ea5d5a258f4f&body=test&device_info=1000&mch_id=10000100&nonce_str=ibuaiVcKdpRxkhJA";
然后拼接api秘鑰並使用md5運算:
stringSignTemp=stringA+"&key=192006250b4c09247ec02edce69f6a2d" sign=MD5(stringSignTemp).toUpperCase()="9A0A8659F005D6984697E2CA0A9CF3B7"
最后就可以得到要發送的數據了:
<xml> <appid>wxd930ea5d5a258f4f</appid> <mch_id>10000100</mch_id> <device_info>1000<device_info> <body>test</body> <nonce_str>ibuaiVcKdpRxkhJA</nonce_str> <sign>9A0A8659F005D6984697E2CA0A9CF3B7</sign> </xml>
這個就做到了加密。
2. 生成隨機數算法
即在微信支付的api接口協議中包含字段nonce_str, 主要是保證簽名不可預測,推薦生成隨機數算法如下: 調用隨機數函數生成,將得到的數值轉化為字符串。
3. 商戶證書
資金退款、撤銷接口需要用到商戶證書,商家在申請微信支付成功后,收到的相應郵件后,可以按照指引下載api證書。 商戶證書不能放在web服務器虛擬目錄, 應該放在有訪問權限控制的目錄中,防止被他人下載。在普通的網絡環境下,http請求存在DNS劫持、運營商插入廣告、數據被竊取、正常數據被修改等安全風險。 用戶回調接口使用HTTPs協議可以保證數據傳輸的安全性。 所以微信支付建議商戶提供微信支付的各種回調采用HTTPs協議。
4. 獲取openid
在關注者與公眾號產生了消息交互之后,公眾號可以獲得關注着的OpenID(加密后的微信號,每個用戶對每個公眾號的OpenID是唯一的。對於不同的公眾號,同一用戶的openid不同)。
公眾號可以通過接口來獲取用戶的openid,還可以獲得用戶的昵稱、頭像、性別、所在城市、語言和關注時間,但是這些都是需要用戶授權。
公眾號支付
場景介紹
用戶選擇商鋪,挑選商品,點擊購買,調起微信支付控件,用戶開始輸入支付密碼,密碼驗證通過,支付成功,返回用戶頁面,顯示購買成功(商戶自定義),當然,用戶也可以吧商品網頁的鏈接生成二維碼, 用戶掃一掃打開后即可完成購買支付。
以下是重要的邏輯
- 用戶選購商品后需要發起支付,前端通過JavaScript調用getBrandWCPayRequest(get-獲取、brand-新的、WC-wechat、payRequest-支付請求)接口,發起微信支付請求,用戶進入支付流程。
- 用戶支付成功並點擊完成按鈕后,商戶的前端會收到JavaScript的返回值,商戶可以直接跳轉到支付成功的靜態頁面(即商戶自定義的靜態頁面,可以嵌入相應的返回值)進行展示。
- 商戶后台收到來自微信開放平台的支付成功回調通知, 標志着該筆訂單成功支付。
值得注意的時,后兩者顯然是不會保證嚴格的觸發時序的,如果用戶不點擊完成,那么后台當然也會接收到相應的支付成功回調通知。JS API返回值作為觸發商戶網頁跳轉的標志,但是商戶后台應該只是在收到微信后台的支付成功回調通知后,才做真正的支付成功的處理。
開發步驟
首先需要設置支付目錄。
接着設置授權域名 --- 在統一下單接口中要求必須傳入用戶的openid, 而獲取openid需要在公眾平台上獲取openid的域名,只有被設置過的域名才是一個有效的獲取openid的域名,否則將獲取失敗。
獲取微信版本號
微信5.0版本后才加入微信支付模塊,所以低於微信5.0的使用用戶將無法使用支付功能。
微信內H5調起支付
在微信路藍旗中打開H5頁面執行JS調起支付,接口輸入輸出數據格式為JSON。
(注意)在微信中有一個 WeixinJSBridge 內置對象,而其他瀏覽器中是不存在的。
(注意)在列表中的參數名區分大小寫,大小寫錯誤簽名驗證會失敗。


注:JS API的返回結果get_brand_wcpay_request:ok僅在用戶成功完成支付時返回。由於前端交互復雜,get_brand_wcpay_request:cancel或者get_brand_wcpay_request:fail可以統一處理為用戶遇到錯誤或者主動放棄,不必細化區分。
示例代碼如下所示:
function onBridgeReady(){ WeixinJSBridge.invoke( 'getBrandWCPayRequest', { "appId":"wx2421b1c4370ec43b", //公眾號名稱,由商戶傳入 "timeStamp":"1395712654", //時間戳,自1970年以來的秒數 "nonceStr":"e61463f8efa94090b1f366cccfbbb444", //隨機串 "package":"prepay_id=u802345jgfjsdfgsdg888", "signType":"MD5", //微信簽名方式: "paySign":"70EA570631E4BB79628FBCA90534C63FF7FADD89" //微信簽名 }, function(res){ if(res.err_msg == "get_brand_wcpay_request:ok" ) {} // 使用以上方式判斷前端返回,微信團隊鄭重提示:res.err_msg將在用戶支付成功后返回 ok,但並不保證它絕對可靠。 } ); } if (typeof WeixinJSBridge == "undefined"){ if( document.addEventListener ){ document.addEventListener('WeixinJSBridgeReady', onBridgeReady, false); }else if (document.attachEvent){ document.attachEvent('WeixinJSBridgeReady', onBridgeReady); document.attachEvent('onWeixinJSBridgeReady', onBridgeReady); } }else{ onBridgeReady(); }
其中最后if判斷的代碼是用於支持瀏覽器不能實現WeixinJSBridge對象的瀏覽器支持的,而else明顯是在調用函數的。
前端所需要的只是大概就這么多了,更多請參考官方文檔。
支付寶支付問題
this.commitOrder(postObj).then(function (response) { that.$loading.close(); switch(response.data.code){ // 支付寶支付方式 case 74: that.$router.push({ path: '/commodity/payment/AlipayHint', query: { alipay: encodeURIComponent(response.data.data) }}) break;
vue中支付的接口如上所示,提交訂單之后,如果返回值為74,那么用戶選擇的就是支付寶支付,由於支付寶不能在微信中使用,所以push到支付寶的提示頁面,提示頁面代碼如下所示:
<template> <div class="alipay-hint"> <img src="../assets/img/alipay-hint.png" alt=""> <MenuFooter></MenuFooter> </div> </template> <style lang="less" scoped> div.alipay-hint { background: #eeefef; img { width: 100%; height: 100%; } } </style> <script> import MenuFooter from "@components/menu" import util from '../utils/js/util' export default { data: function () { return { name: 'alipay-hint' } }, created () { if (!util.isWeChat()) { window.location.href = decodeURIComponent(this.$route.query.alipay); } }, components: { MenuFooter } } </script>
即首先進入這個頁面,然后判斷當前的瀏覽器是否是微信,如果是微信,那么就什么都不做,顯示的是提示在瀏覽器中打開的圖片; 如果不是微信,也就是說在其他瀏覽器打開,就導航到阿里支付的頁面,這樣就可以進行支付了。 現在的問題在於:后端返回的數據(即阿里支付的鏈接)是沒有問題的,但是一旦選擇在瀏覽器中打開,並沒有打開當前的阿里提示支付的頁面,所以也就沒有打開阿里支付頁面,而是跳轉到了首頁,於是支付寶支付一直是有問題的。
問題總結:
從微信中復制到的鏈接的查詢字符串是否含有阿里支付的鏈接,如果有,那么在瀏覽器打開之后,在判斷了不是微信瀏覽器之后,就應該打開了支付寶支付,為什么沒有打開,甚至即使打不開,也應該打開阿里支付的圖片啊,為什么回到了首頁?????
問題猜測以及解決嘗試方法:
- 是否是因為后端返回的數據根本就不含阿里支付的鏈接,所以才打不開?
驗證方法 --- 對於每次返回的數據,alert出來,看看是否是阿里的支付鏈接?
驗證結果 --- 返回的結果的確是阿里支付的鏈接,所以說並不是因為后端沒有返回正確的數據,此問題猜測排除。
2. 當我進入到支付寶支付頁面時,講到里我通過decodeURIComponent應該是可以拿到傳遞過來的阿里支付鏈接的參數,是否是因為這個鏈接沒有正確拿到,所以即使在瀏覽器中打開,也是打開不了的。
驗證方法 --- 進入支付寶支付頁面的時候(在微信中),alert出來 decodeURIComponent 阿里支付鏈接的值,看是否正常拿到了這個值?
驗證結果 --- 在支付寶支付頁面中,alert時, decodeURIComponent的值的確就是后端返回的數據 --- 阿里支付的鏈接, 此問題猜測排除。
3. 經過2可以知道,數據(鏈接)確實是准確的拿到了的,那么就應該在檢測到瀏覽器不是微信的時候就進入支付鏈接。那是不是因為在微信中選擇在瀏覽器打開的時候它復制到的鏈接和實際的鏈接不同呢?
驗證方法 --- 進入支付寶支付頁面的時候,先alert出當前的location.href, 然后再親自復制鏈接,然后粘貼到別的地方,進行比對。
驗證結果 --- alert出來的當前的url和copy出來的url差別很大!!! 在實際的url中,是http://xxx.xxx.com/index.html?84654685465#/dfjlajfoasjfoa8653465fdasl; 在copy得到的url中,只copy到了前面一部分,http://xxx.xxx.com/index.html?84654685465, 把錨點之后的省略了,並且用=來代替錨點之后的所有內容。
問題 --- 也就是說微信中在copy的時候並沒有copy到所有的url,而是截取了一部分,把錨點給刪除了!
再次驗證 --- 把vue中的其他頁面(微信中)在瀏覽器打開 --- 問題同樣 --- 即打開后都沒有得到對應的url,而是都把錨點去掉了,在瀏覽器打開時得到的都是相同的index.html, 而沒有復制到錨點。
4. 對於3的問題追蹤
但是對於其他的頁面是否也是相同的問題呢? 我嘗試着打開了vuejs官網,然后進入了api頁面,發現這里面的每一個知識點也是使用錨點定位的? 所以問題並沒有出在這里。 那么為什么沒有copy上呢?
經過嘗試,發現了這樣的一個問題,對於下面的鏈接:
www.biangou.cn/hat2/index.html?495452482fbv56a3dc2130aba8b32fbv549479711
進入之后,然后點擊不同的路由,即這時應該是帶有錨點的, 但是在copy之后,發現錨點並沒有被復制下來。 但是如果我們使用下面的鏈接進入。
www.biangou.cn/hat2/index.html?495452482fbv56a3dc2130aba8b32fbv549479711#
這樣,如果在使用錨點,那么復制的時候,錨點就可以復制下來了。
微信中的bug
在微信中, 我們可以嘗試輸入 vuejs.org ,然后進入頁面之后找到api頁面,然后點擊一個標題,這時url中應該是添加了一個#的,即錨點定位,但是實際上卻沒有。在除了微信之外的瀏覽器中這樣的做法是可以實現的。
但是我們如果想要准確記錄下微信中的錨點,即copy到並且可以在瀏覽器中打開,那么解決這個問題是非常必要的,我們只要在url的最后添加一個#即可,這樣,帶着#號進入就可以解決到所有的問題了。
