微信公眾號H5支付-JAVA版


微信開發之微信公眾號H5支付-JAVA版

  引子

  從事JAVA開發一年多了,一直都在看博客園,CSDN的博客,從很多前人哪里學習了很多,突然覺得自己也要盡一份力,寫點博客自己給自己做做記錄,也給要開發微信人提提醒少遇點坑。

  很多人開發微信的時候,總是在抱怨微信的開發文檔很坑,里面的參數和使用方式很含糊,其實有時候自己想想,如果自己去研發API的時候,是否能夠做的比微信更好呢?,大師都有一顆虔誠學徒的心,希望這篇文檔能給予從事微信公眾號H5支付焦頭爛額的朋友,一點幫助。

  一、前言

  先給大家提提從事微信開發,需要做的一些准備條件:1、去微信公共平台和微信商戶平台注冊一個賬號,這里需要給微信交納300塊RMB成為一個開發者擁有開發資質 ,微信公共平台網址 、微信商戶平台網址 2、申請一個服務號詳細流程我就不提醒了,服務號是需要企業認證的(如果沒有企業資格,又想練練手的朋友可以去申請一個微信公眾號測試號,進行一些簡單的開發,微信測試號網址) 。3、注意看文檔,切記重要的事情說三遍!!!,做操作之前仔細理解文檔里面的內容,參數里面的關系,請求的格式之類的,這種錯誤是最難發現的,微信公共號開發文檔網址 ,微信支付文檔

  二、思路

 進行開發之前,先把思路理清楚了解,了解微信支付的類型,這里類型有很多,我們需要的是公眾號支付,通過官網文檔了解,這里需要注冊一個微信商戶,這個我就不解釋了,按照官網的指示擼就行了,這里展示一個官網的業務流程圖。

  支付業務流程圖

  沒有理解到的多看看流程圖少吃很多虧,也可以帶入自己的業務模式,來考慮怎么設計業務。

  這里我們了解到,使用微信統一下單支付接口,前提是需要用戶的openid,這里又要涉及到微信的授權登錄了,我的另一篇文章:  網頁微信授權登錄文章

 好了我們這里提到微信支付流程第一步,統一下單支付接口,這里貼上必填的參數:

字段名 變量名 示例值 類型 描述
公眾賬號ID appid wxd678efh567hg6787 String(32) 微信支付分配的公眾賬號ID(企業號corpid即為此appId)
商戶號 mch_id 1230000109 String(32) 微信支付分配的商戶號
隨機字符串 nonce_str 5K8264ILTKCH16CQ2502SI8ZNMTM67VS String(32) 隨機字符串,長度要求在32位以內。推薦隨機數生成算法
簽名 sign C380BEC2BFD727A4B6845133519F3AD6 String(32) 通過簽名算法計算得出的簽名值,詳見簽名生成算法
商品描述 body 騰訊充值中心-QQ會員充值 String(128) 商品簡單描述,該字段請按照規范傳遞,具體請見參數規定
商戶訂單號 out_trade_no 20150806125346 String(32) 商戶系統內部訂單號,要求32個字符內,只能是數字、大小寫字母_-|* 且在同一個商戶號下唯一。詳見商戶訂單號
標價金額 total_fee 88 Int 訂單總金額,單位為分,詳見支付金額
終端IP spbill_create_ip 123.12.12.123 String(16) APP和網頁支付提交用戶端ip,Native支付填調用微信支付API的機器IP
通知地址 notify_url http://www.weixin.qq.com/wxpay/pay.php String(256) 異步接收微信支付結果通知的回調地址,通知url必須為外網可訪問的url,不能攜帶參數
交易類型 trade_type JSAPI String(16)

JSAPI 公眾號支付

NATIVE 掃碼支付

APP APP支付

說明詳見參數規定

 

  里面要注意:簽名算法,不建議自己手寫,可以用微信的支付工具類(微信官方工具類,里面有很多的函數,例如生成sign,map組裝xml等實用功能,減少開發量),里面有方法可以填入參數就可以生成sign

三、場景代碼 

貼上我自己的代碼:

  

 // 統一下單URL
        String unifiedorder_url = "https://api.mch.weixin.qq.com/pay/unifiedorder";
        IWXPayConfig config = new IWXPayConfig();

   Map<String, String> data = new HashMap<String, String>();
            data.put("appid", config.getAppID());//微信支付分配的公眾賬號ID(企業號corpid即為此appId)
            data.put("mch_id", config.getMchID());//微信支付分配的商戶號
            String  str = WXPayUtil.generateNonceStr();
            data.put("nonce_str", str); // 通過微信工具類生成  隨機字符串
            data.put("body", "超級商品");//商品描述
            data.put("out_trade_no", order.getOrderSn()); // 訂單唯一編號, 不允許重復
            data.put("total_fee", new BigDecimal(100).multiply(order.getAmount()).setScale(0,BigDecimal.ROUND_DOWN).toString()); // 訂單金額, 單位分
            data.put("spbill_create_ip", GetIp.getTrueIpAddr(request)); // 下單ip
            data.put("openid", order.getOpenId()); // 微信公眾號統一標示openid
            data.put("notify_url", "URL這里填寫,你的回調域名"); // 訂單結果通知, 微信主動回調此接口
            data.put("trade_type", "JSAPI"); // 固定填寫
            // 生成帶有 sign 的 XML 格式字符串
            String xmlparam = WXPayUtil.generateSignedXml(data, config.getKey());
            // 發送請求
            String resultStr = HttpRequest.sendPost(unifiedorder_url, xmlparam);
            logger.info("返回消息" + resultStr);

通過post請求微信會回調一個json回調信息,如果里面return_code 回復為SUCCESS而且result_code 也為SUCCESS的話,恭喜你就成功了一半了。這個時候可以放松一下泡杯茶。

  下一步,根據官方提示,需要把返回的json回調信息的,參數取出來進行二次封裝,組裝進map,然后返回前端發起支付請求(這里有一個坑,頁面實際的支付路徑等於微信商戶平台的設置支付目錄的下一級,切記!!!微信錯誤提示不足,導致我這里卡了一下午)

這里貼上我的代碼:

    // 時間戳
                String timeStamp = new Long(WXPayUtil.getCurrentTimestamp()).toString();
                // 創建返回值
                //組裝二次簽名
                Map<String, String> resultMap = new HashMap<String, String>();
                resultMap.put("appId", wxResultMap.get("appid"));
                resultMap.put("timeStamp", timeStamp);
                resultMap.put("nonceStr", str);
                resultMap.put("package", "prepay_id=" + wxResultMap.get("prepay_id"));
                resultMap.put("signType", "MD5");
                // 生成簽名
                String paySign = WXPayUtil.generateSignature(resultMap, config.getKey());
                resultMap.put("paySign", paySign);
                return MsgJson.getmsg(false, resultMap, "簽名生成成功");

注意一般出現錯誤的地方都是參數錯誤,如果前端執行報錯請檢查參數,是否有填寫錯,大小寫是否有問題。

  貼上前端代碼:

  $.ajax({
            type:"post",
            url: "/weixin/pay",
            dataType:"json",
            ontentType : "application/x-www-form-urlencoded",
            data:{orderId:'${orderId}'},
            success:function(result) {
                console.log(result);
                var data  = result;
                //var data = JSON.parse(result);
                if(data.status == '200'){
                    appId = data.data.appId;
                    paySign = data.data.paySign;
                    timeStamp = data.data.timeStamp;
                    nonceStr = data.data.nonceStr;
                    packageStr = data.data.package;
                    signType = data.data.signType;
                    callpay();
                }else{
                    alert("統一下單失敗");
                }
            }
        });
    }

    function onBridgeReady(){
        WeixinJSBridge.invoke(
            'getBrandWCPayRequest', {
                "appId":appId,     //公眾號名稱,由商戶傳入
                "timeStamp":timeStamp, //時間戳,自1970年以來的秒數
                "nonceStr":nonceStr , //隨機串
                "package":packageStr,  //預支付交易會話標識
                "signType":signType,     //微信簽名方式
                "paySign":paySign        //微信簽名
            },
            function(res){
                if(res.err_msg == "get_brand_wcpay_request:ok" ) {
                    alert('支付成功');
                    window.location.replace("/eby/index");
                }else if(res.err_msg == "get_brand_wcpay_request:cancel"){
                    alert('支付取消');
                }else if(res.err_msg == "get_brand_wcpay_request:fail" ){
                    alert('支付失敗');
                } //使用以上方式判斷前端返回,微信團隊鄭重提示:res.err_msg將在用戶支付成功后返回    ok,但並不保證它絕對可靠。
            }
        );
    }
    function callpay(){
        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();
        }

前端做完,這一步順利調出來微信支付的彈框

 

 這里就基本大功告成了,還有差一步處理支付的回調,根據微信官方提示前端返回的支付成功是不靠譜的,需要微信異步回調來驗證,一共有8次注意通知的延遲重復性,通過生成sign比對成功后才可以確保這次支付成功了,下面貼上我的處理回調代碼:

 

 HashMap  map  = new HashMap();
        IWXPayConfig config = new IWXPayConfig();
        try {
            InputStream inStream = request.getInputStream();
            ByteArrayOutputStream outSteam = new ByteArrayOutputStream();
            byte[] buffer = new byte[1024];
            int len = 0;
            while ((len = inStream.read(buffer)) != -1) {
                outSteam.write(buffer, 0, len);
            }
            outSteam.close();
            inStream.close();
            String resultStr = new String(outSteam.toByteArray(), "utf-8");
            Map<String, String> resultMap = WXPayUtil.xmlToMap(resultStr);//將xml轉成排序之后的map
            logger.info("微信支付回調地址請求參數=requst:{}", resultMap.toString());
            String result_code = resultMap.get("result_code");//業務結果
            String is_subscribe = resultMap.get("is_subscribe");//是否關注了微信公眾號
            String out_trade_no = resultMap.get("out_trade_no");//訂單號
            String transaction_id = resultMap.get("transaction_id");//微信支付訂單號  類似於支付寶的交易號
            String sign = resultMap.get("sign");//簽名
            String total_fee = resultMap.get("total_fee");//訂單總金額  單位為 分
            String openid = resultMap.get("openid");//用戶在商戶appid下的唯一標識
            String time_end = resultMap.get("time_end");
            String bank_type = resultMap.get("bank_type");
            //簽名驗證
            resultMap.remove("sign");
            String signStr =  WXPayUtil.generateSignature(resultMap,config.getKey());
            logger.warn("驗證= signStr:{},sign:{}", signStr, sign);
            if (!signStr.equals(sign)) {
                logger.warn("微信支付回調地址請求參數簽名驗證失敗= signStr:{},sign:{}", signStr, sign);
                map.put("return_code", "FAIL");
                map.put("return_msg", "sign不正確");
                return WXPayUtil.mapToXml(map);
            }
     if (result_code.equals("SUCCESS")) {
                logger.info("支付成功= 訂單號:{},交易號:{}", out_trade_no, transaction_id);
                BigDecimal bigDecimal_total_fee = new BigDecimal(total_fee);
                BigDecimal bigDecimal = bigDecimal_total_fee.divide(new BigDecimal(100));
               // 支付成功處理業務邏輯

            }
  //通知微信.異步確認成功.必寫.不然會一直通知后台.八次之后就認為交易失敗了.
            map.put("return_code","SUCCESS");
            map.put("return_msg","OK");

四、總結

總結的來說,微信支付坑還是不少尤其是是地址和參數,多注意一些還是可以避免的,其他的注意點在我在場景代碼里面提出來了,第一次寫文章如果有不好的地方,希望大家指出來,希望這篇文章能幫到大家。

文章純手寫,轉載請帶上作者。

  

  

 


免責聲明!

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



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