微信公眾號支付流程(Node實現)


前言

花費了一天時間,調通了微信公眾號支付。作下記錄,方便以后再次填坑。先聲明,微信公眾號支付,不同於微信H5支付,這點在本文結束時再詳細說明。

微信配置

設置測試目錄

在微信公眾平台設置,欄目見下圖。支付測試狀態下,設置測試目錄,測試人的微信號添加到白名單,發起支付的頁面目錄必須與設置的精確匹配。並將支付鏈接發到對應的公眾號會話窗口中才能正常發起支付測試。注意正式目錄一定不能與測試目錄設置成一樣,否則支付會出錯。

設置正式支付目錄

根據圖中欄目順序進入修改欄目,勾選JSAPI網頁支付開通該權限,並配置好支付授權目錄,該目錄必須是發起支付的頁面的精確目錄,子目錄下無法正常調用支付。具體界面如圖所示:

交互流程

業務流程時序圖

首先看下微信官方給出的交互流程:

服務器交互

微信公眾號支付的流程,簡而言之只有兩步。第一步,下單,拿到prepay_id等信息;第二步,利用第一步拿到的prepay_id等信息進行支付。

代碼

Node端

// 微信支付
exports.weixinpay = function(req, res){
  var orderId = req.params.orderId;
  var selectSum = req.params.selectSum;
  var token = req.params.token;
  // 獲取code
  if(!req.query.code){
    var r_url = 'http://' +config.host+'/artist/weixinpay/'+orderId+'/'+selectSum+'/'+token;
    var url = 'https://open.weixin.qq.com/connect/oauth2/authorize?appid='+config.weixin.appid+'&redirect_uri='+urlencode(r_url)+'&response_type=code&scope=snsapi_userinfo&state=111#wechat_redirect';
    res.redirect(url);
  }else{
    var code = req.query.code;
    var state = req.query.state;

    var ep = new eventproxy();
    ep.all('userInfo',function(userInfoData){
      var userInfoData = JSON.parse(userInfoData);
      res.render('artist/weixinpay',{
        // 其他數據
        openid: userInfoData.openid,
        orderId: orderId,
        selectSum: selectSum,
        token: token
      });
    });

    // 獲取用戶信息userInfo
  }
}

exports.weixinpay2 = function(req, res){
  var wxParamUrl = config.apihost + '/order/getH5SubOrderforWechat';
  var param = {
    orderId: req.query.orderId,
    selectSum: req.query.selectSum,
    accessToken: req.query.token,
    openId: req.query.openid
  };
  request.post({url: wxParamUrl,form: JSON.stringify(param)},function(error, response, body){    
    res.render('artist/weixinpay2',{
      // 其他數據
      wxParam: JSON.parse(response.body)
    });
  });
}

上面的代碼看起來很奇怪,為什么需要一個weixinpay2函數?代碼全部放在weixinpay函數里面,然后在weixinpay.html里調用微信支付接口,不是很好么?這個,就涉及到支付目錄的問題了。weixinpay函數中,微信授權獲取用戶信息后的回調url,目錄太深,而且不確定,因為我們是通過目錄來傳遞參數的。這樣,就無法在微信公眾號上面設置支付授權目錄。所以,我們不能在weixinpay.html這個頁面發起支付請求,只能把參數轉發給下一個weixinpay2.html頁面,在weixinpap2.html中發起支付請求。

那么,為什么獲取用戶信息后的回調url目錄太深?直接把orderId等信息當做參數放在回調url后面不就可以了嗎?很遺憾,回調url無法帶入你的自定義參數。因此,只能把參數當做回調url的一部分,也就是目錄的一部分。

綜上,weixinpay函數負責獲取用戶的openid,weixinpay.html負責跳轉weixinpay2.html;weixinpay2函數負責獲取調用微信JSAPI的參數,weixinpay2.html負責調用微信JSAPI。

html

<!--weixinpay.html-->
<!DOCTYPE html>
<html lang="zh">
<head>
    <meta charset="UTF-8">
    <title>微信支付</title>
</head>
<body>
    <a id="weixin-link" href="/artist/weixinpay2?orderId=<%= orderId%>&selectSum=<%= selectSum%>&token=<%= token%>&openid=<%= openid%>" style="display: none">微信支付</a>

<script>
    var link = document.getElementById('weixin-link');
    link.click();
</script>
</body>
</html>
<!--weixinpay2.html-->
<!DOCTYPE html>
<html lang="zh">
<head>
    <meta charset="UTF-8">
    <title>微信支付</title>
</head>
<body>
    <input id="appId" type="hidden" value="<%= wxParam.obj.appid%>">
    <input id="timeStamp" type="hidden" value="<%= wxParam.obj.timestamp%>">
    <input id="nonceStr" type="hidden" value="<%= wxParam.obj.noncestr%>">
    <input id="package" type="hidden" value="<%= wxParam.obj.packagestr%>">
    <input id="signType" type="hidden" value="MD5">
    <input id="paySign" type="hidden" value="<%= wxParam.obj.sign%>">

<script>
function onBridgeReady(){
    var appId = document.getElementById('appId').value;
    var timeStamp = document.getElementById('timeStamp').value;
    var nonceStr = document.getElementById('nonceStr').value;
    var package = document.getElementById('package').value;
    var signType = document.getElementById('signType').value;
    var paySign = document.getElementById('paySign').value;
    WeixinJSBridge.invoke(
        'getBrandWCPayRequest', {
            "appId": appId,     //公眾號名稱,由商戶傳入     
            "timeStamp":timeStamp,         //時間戳,自1970年以來的秒數     
            "nonceStr": nonceStr, //隨機串     
            "package": package,     
            "signType": signType,         //微信簽名方式:     
            "paySign": paySign //微信簽名 
        },
        function(res){
            WeixinJSBridge.log(res.err_msg);
            //alert(res.err_code + res.err_desc + res.err_msg);
            if (res.err_msg == "get_brand_wcpay_request:ok") {  
                window.location.href = '/artist/alipayresult?trade_status=TRADE_SUCCESS';
                // 執行跳轉頁面....  
            } else if (res.err_msg == "get_brand_wcpay_request:cancel") {  
                alert ("用戶取消支付!");  
            } else {  
                alert ("支付失敗!");  
            }  
            
        }
    ); 
}

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();
}
</script>
</body>
</html>

為了加快執行速度,這里的兩個頁面中,都使用原生的js來實現頁面邏輯。

錯誤

支付驗證簽名失敗


很明顯,支付驗證簽名失敗。怎么判斷算出的簽名是否正確?使用微信支付接口簽名校驗工具
校驗方式選擇自定義參數,把需要簽名的參數名稱和參數值輸入頁面,點擊生成簽名即可。

當前頁面的URL未注冊

不好意思,這個錯誤忘記截圖了。借來了一張圖,湊合着看。

這個問題,是由於調用支付接口的url不對!那么,怎樣是對的呢?舉個栗子:如果調用支付接口的url為http://wx.voidking.com/pay/weixinpay,那么微信公眾平台上的支付授權目錄應該設置為http://wx.voidking.com/pay/。也就是說,支付授權目錄應該設置為調用支付接口的url的上一級目錄。

支付成功


后記

微信公眾號支付,兩個缺點,一是必須在微信瀏覽器使用,二是必須有一個拉取用戶信息的步驟。但是我們發現,很多網站,在其他瀏覽器也可以使用微信支付,也不需要拉取用戶信息,這是怎么回事?

因為,這是兩種不同的支付方式!今天我們討論的,叫做微信公眾號支付;而在其他瀏覽器中調用微信支付,叫做微信H5支付!

更多關於微信H5支付的內容,請參考微信H5支付官方文檔

書簽

微信支付開發文檔——公眾號支付
https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=7_1

微信支付接口簽名校驗工具
https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=20_1

非微信內置瀏覽器中的網頁調起微信支付的方案研究
http://blog.csdn.net/ahence/article/details/51317814

微信支付|商戶平台開發者文檔
https://pay.weixin.qq.com/wiki/doc/api/wap.php?chapter=15_1

【微信支付V2.0】H5支付實例
http://wxpay.weixin.qq.com/pub_v2/pay/wap.v2.php


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM