微信公衆號中的支付寶支付與微信支付 && 支付寶支付問題(微信bug)

  通常,在微信公衆號中的商城都是須要支持微信支付和支付寶支付的,固然,較大的公司對於鵝廠和阿里的站隊就不說了,因此這裏簡單記錄一下支付寶支付和微信支付的主要流程。說是簡單介紹,這是由於確實不難,由於前端在這方面,包括微信受權登錄這一塊須要作的都不是不少,而主要的工做量都在後端部分。 php

  

支付寶支付

  不管是支付寶支付仍是微信支付,最開始的步驟固然是將商品列表、商家相關信息、用戶remark、運費、總價等等支付須要的信息經過post請求向後端傳遞,這裏介紹支付寶支付,因此假設用戶選擇的是支付寶支付,那麼後端返回的就是一個阿里的url,咱們經過這個url來進行支付寶的支付。 固然, 首先,咱們應該進入一個支付寶引導頁面,即指導用戶在瀏覽器中打開,這是由於微信是不支持支付寶進行付款的,因此支付寶須要單獨在網頁中打開進行支付。注意: 若是真的但願在微信上使用支付寶付款,可使用微信開發者工具v0.7.0 , 通過測試,這個至少是能夠的。  用戶在這個支付寶的url中支付完成以後,最終再跳轉到公衆號的頁面上便可。 至於判斷微信瀏覽器的方法,可使用下面這種:html

util.isWeChat = function () {
  var browserInfo = navigator.userAgent.toLowerCase();
  var weChatPattern = /MicroMessenger/i;
  if (weChatPattern.test(browserInfo)) {
    return true;
  } else {
    return false;
  }
}

 

 

 

微信支付

  微信支付較之於支付寶支付稍顯複雜,公衆號支付開發者文檔對此作了詳細的說明。此文檔的閱讀對象固然就是一些涉及微信支付的研發工程師、運維工程師等等了。而且微信支付從2014年開始到目前2017年6月都在不斷地迭代更新。前端

 

支付方式vue

   爲了對微信支付有更深層次的瞭解,咱們須要知道下面幾種支付方式:web

  • 刷卡支付 --- 即微信裏個人錢包中的二維碼、條形碼的支付方式,主要用在線下的面對面的收銀場景。
  • 掃碼支付 --- 即商戶根據微信支付協議生成的二維碼,用戶經過掃一掃來進行支付。
  • 公衆號支付 --- 這也就是用戶在H5頁面中使用JSAPI進行支付的場景。這也是使用比較多的場景。
  • APP支付 --- 即在app中使用的支付方式。

  

支付帳戶算法

  若是商戶但願使用上面的支付方式的其中一種,就須要向微信進行申請,申請成功以後,微信會發來一封郵件,這個郵件上會記錄關於這個支付帳戶相關的信息,即提供給商戶一些API供其調用來完成公衆號內的支付功能。郵件中的參數與將會調用的API有下面的對應關係:後端

 即一個公衆號就是一個應用,它就有惟一的appid,這是在申請公衆號的時候就會有的,而mch_id是微信支付商戶號,這必須是在申請了微信支付功能以後纔會有的帳號,用於給商戶分配收款帳號。 key就是api祕鑰。 Appsecret是appid對應的接口密碼,經過這個接口密碼可使用access_token調用api了,而後經過OAuth2.0接口獲取openid,openid是每個用戶在一個公衆號中特有的標識,也就是用於標識一個公衆號下不一樣的用戶。api

 

接口規則瀏覽器

  若是但願使用微信支付接口,那就就要遵照它所指定的一些規則: 好比使用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>

 

即首先進入這個頁面,而後判斷當前的瀏覽器是不是微信,若是是微信,那麼就什麼都不作,顯示的是提示在瀏覽器中打開的圖片; 若是不是微信,也就是說在其餘瀏覽器打開,就導航到阿里支付的頁面,這樣就能夠進行支付了。 如今的問題在於:後端返回的數據(即阿里支付的連接)是沒有問題的,可是一旦選擇在瀏覽器中打開,並無打開當前的阿里提示支付的頁面,因此也就沒有打開阿里支付頁面,而是跳轉到了首頁,因而支付寶支付一直是有問題的。 

問題總結:

  從微信中複製到的連接的查詢字符串是否含有阿里支付的連接,若是有,那麼在瀏覽器打開以後,在判斷了不是微信瀏覽器以後,就應該打開了支付寶支付,爲何沒有打開,甚至即便打不開,也應該打開阿里支付的圖片啊,爲何回到了首頁?????

問題猜想以及解決嘗試方法:

  1. 是不是由於後端返回的數據根本就不含阿里支付的連接,因此纔打不開? 

 驗證方法 --- 對於每次返回的數據,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的最後添加一個#便可,這樣,帶着#號進入就能夠解決到全部的問題了。