微信APP支付

最近公司在開發新項目,須要微信支付   經歷了幾天時間 終於寫出來了  不過微信文檔也不是很容易閱讀  東西不全  不少東西全靠猜php

注意:全部帶加密的都須要帶上微信商戶祕鑰(32位)  即key前端

這邊主要是java服務端代碼   :java

        1.1.首先是統一下單接口git

        一。後端

          微信提供的下單接口 https://api.mch.weixin.qq.com/pay/unifiedorderapi

          經過POST接口訪問微信

          Url是訪問地址   data是微信轉成xml格式的傳輸參數   必須是UTF-8格式的參數app

        

微信要求的格式是這樣     咱們只須要填寫必要參數就好了。dom

<xml>
   <appid>wx2421b1c4370ec43b</appid>
   <attach>支付測試</attach>
   <body>APP支付測試</body>
   <mch_id>10000100</mch_id>
   <nonce_str>1add1a30ac87aa2db72f57a2375d8fec</nonce_str>
   <notify_url>http://wxpay.wxutil.com/pub_v2/pay/notify.v2.php</notify_url>
   <out_trade_no>1415659990</out_trade_no>
   <spbill_create_ip>14.23.150.211</spbill_create_ip>
   <total_fee>1</total_fee>
   <trade_type>APP</trade_type>
   <sign>0CB01533B8C1EF103065174F50BCA001</sign>
</xml>

==============Post 請求封裝方法===================ide

 public static String POST(String url, String data){
         CloseableHttpClient client = HttpClients.createDefault();
         CloseableHttpResponse response = null;
 
         try {
             HttpPost method = new HttpPost(url);
             method.addHeader(HTTP.CONTENT_TYPE, "application/xml");
             StringEntity entity = new StringEntity(data,"UTF-8");
             method.setEntity(entity);
             response  = client.execute(method);
 
             String s = EntityUtils.toString(response.getEntity(), "UTF-8");
             return s;
         } catch (IOException e) {
            e.printStackTrace();
         }finally {
            try
             {
                response.close();
            }
             catch (Exception e)
             {
                e.printStackTrace();
             }
         }
 
 
         return null;
     }

//返回的數據也是xml格式的

     https://pay.weixin.qq.com/wiki/doc/api/app/app.php?chapter=9_1    微信文檔寫的很清楚

 

 

     1.2.生成隨機字符串

        這裏我是經過UUID生成的32位隨機字符串

    

 /**
     * 生成32位隨機數
     * @return
     */
    public static String  getRandomNumberAlgorithm(){
        String uuid = UUID.randomUUID().toString().toUpperCase();
        String replace = uuid.replace("-", "");
        return  replace;
    }

     1.3 將Map 轉換成xml格式的數據字符串

    

 /**
     * @author
     * @Description:將請求參數轉換爲xml格式的string 不包含CDATA標籤
     * @param parameters
     *            請求參數
     * @return
     */
    public static String getRequestXml2(SortedMap<String, Object> parameters) {
        StringBuffer sb = new StringBuffer();
        sb.append("<xml>");
        Set<?> es = parameters.entrySet();
        Iterator<?> it = es.iterator();
        while (it.hasNext()) {
            @SuppressWarnings("rawtypes")
            Map.Entry entry = (Map.Entry) it.next();
            String k = entry.getKey().toString();
            String v = entry.getValue().toString();

                sb.append("<" + k + ">" + v + "</" + k + ">");

        }
        sb.append("</xml>");
        return sb.toString();
    }

    1.4 生成簽名

public static String createSign(SortedMap<String, Object> packageParams, String apiKey) {  
        StringBuffer sb = new StringBuffer();  
        Set<?> es = packageParams.entrySet();  
        Iterator<?> it = es.iterator();  
        while (it.hasNext()) {  
            @SuppressWarnings("rawtypes")
            Map.Entry entry = (Map.Entry) it.next();  
            String k = entry.getKey().toString();  
            String v = entry.getValue().toString();  
            if (null != v && !"".equals(v) && !"sign".equals(k) && !"key".equals(k)) {  
                sb.append(k + "=" + v + "&");  
            }  
        }  
        sb.append("key=" + apiKey);  
        String sign = MD5.encoder(sb.toString()).toUpperCase();  
        return sign;  
    }

    這個時候你將下單返回的參數  傳遞過去  你覺得就成功了?    相信我   幻覺 絕對是幻覺     這個時候瀏覽安卓支付起掉文檔    OK  須要二次加密

ok  咱們按照參數  依次寫進去

    

//返回數據
            CreateWeiXinPayOrderResponse createWeiXinPayOrderResponse = payResult(stringObjectSortedMap);
            //或者封裝到map中將數據
            SortedMap<String, String> secondaryEncryption=new TreeMap<>();
            String appid = stringObjectSortedMap.get("appid").toString();
            secondaryEncryption.put("appid",appid);
            String partnerid = request.getMch_id().toString();
            secondaryEncryption.put("partnerid",partnerid);
            String prepay_id = stringObjectSortedMap.get("prepay_id").toString();
            secondaryEncryption.put("prepayid",prepay_id);
            String nonce_str = stringObjectSortedMap.get("nonce_str").toString();
            secondaryEncryption.put("noncestr",nonce_str);
            secondaryEncryption.put("package","Sign=WXPay");
            secondaryEncryption.put("timestamp",String.valueOf(time/1000));
            String sign1 = createSign("UTF-8", secondaryEncryption, request.getKey());
            stringObjectSortedMap.put("sign",sign1);
            createWeiXinPayOrderResponse.setMap(stringObjectSortedMap);

這個時候  APP 前端有報錯  -1   來咱們看看微信對這個描述是怎麼樣的

 

 坑爹的其餘異常   搞了一天多    然後查看簽名 規則 發現

    

也就是說  無論是簽名幾回  什麼簽名    都須要帶上key  即商戶密匙!!!

 順帶貼上加密 代碼:

public static String createSign(String characterEncoding, SortedMap<String, String> parameters,String key) {
        StringBuffer sb = new StringBuffer();
        Set es = parameters.entrySet();
        Iterator it = es.iterator();
        while (it.hasNext()) {
            Map.Entry entry = (Map.Entry) it.next();
            String k = (String) entry.getKey();
            Object v = entry.getValue();
            if (null != v && !"".equals(v)
                    && !"sign".equals(k) && !"key".equals(k)) {
                sb.append(k + "=" + v + "&");
            }
        }
        sb.append("key=" + key);//最後加密時添加商戶密鑰,因爲key值放在最後,因此不用添加到SortMap裏面去,單獨處理,編碼方式採用UTF-8
        String sign = MD5Encode(sb.toString(), characterEncoding).toUpperCase();
        return sign;
    }

public static String MD5Encode(String origin, String charsetname) {
        String resultString = null;
        try {
            resultString = new String(origin);
            MessageDigest md = MessageDigest.getInstance("MD5");
            if (charsetname == null || "".equals(charsetname))
                resultString = byteArrayToHexString(md.digest(resultString
                        .getBytes()));
            else
                resultString = byteArrayToHexString(md.digest(resultString
                        .getBytes(charsetname)));
        } catch (Exception exception) {
        }
        return resultString;
    }

 private static String byteToHexString(byte b) {
        int n = b;
        if (n < 0)
            n += 256;
        int d1 = n / 16;
        int d2 = n % 16;
        return hexDigits[d1] + hexDigits[d2];
    }
    private static final String hexDigits[] = { "0", "1", "2", "3", "4", "5",
            "6", "7", "8", "9", "a", "b", "c", "d", "e", "f" };

   private static String byteArrayToHexString(byte b[]) {
        StringBuffer resultSb = new StringBuffer();
        for (int i = 0; i < b.length; i++)
            resultSb.append(byteToHexString(b[i]));

        return resultSb.toString();
    }

OK  完事!!!

 

而後是最重要的回調

 回調  

該連接是經過【統一下單API】中提交的參數notify_url設置,若是連接沒法訪問,商戶將沒法接收到微信通知。

通知url必須爲直接可訪問的url,不能攜帶參數。示例:notify_url:「https://pay.weixin.qq.com/wxpay/pay.action」

 

不能帶參數這個回調接口   須要本身提供  須要有證書的    

 這樣的話咱們就只能從二進制流中取得咱們須要的數據了

將數據轉換爲xml格式的數據  後端進行驗籤

@Override
    public String weiXinNotify(HttpServletRequest request, HttpServletResponse response) {
     
        StringBuffer xmlStr = new StringBuffer();
        try {
            BufferedReader reader = request.getReader();
            String line = null;
            while ((line = reader.readLine()) != null) {
                xmlStr.append(line);
            }
            reader.close();
            logger.info("微信支付回調返回參數:" + xmlStr + ", 即將調用支付模塊回調接口");
            //調用支付模塊的回調接口,處理返回出具
            NotifyRequest req = new NotifyRequest();
            req.setXmlStr(xmlStr.toString().trim());
            NotifyResponse resp = paymentClient.weiXinPayNotify(req);
            logger.info("微信支付回調返回給微信端的數據:" + resp.getResultXml());
            BufferedOutputStream out = new BufferedOutputStream(
                    response.getOutputStream());
            out.write(resp.getResultXml().getBytes());
            out.flush();
            out.close();
        } catch (Exception e) {
            logger.error("微信回調出錯: " + e.getMessage());
            throw new WebApiCommonException(WebApiCommonErrorCodeType.PayBackError);
        }

        return xmlStr.toString();
    }

後端取 驗籤  開發時間緊 我就沒封裝了

public NotifyResponse weiXinPayNotify(String xmlStr) {
        NotifyResponse notifyResponse=new NotifyResponse();
        // 獲取微信配置信息並賦值
        QueryAppliactionConfigRequest req = new QueryAppliactionConfigRequest();
        req.setConfigType("weixinPay");
        req.setConfigName("key");
        QueryAppliactionConfigResponse resp = appliactionConfigClient.queryAppliactionConfig(req);
        if (resp.getData()==null||resp.getData().getList().size()<0){
            throw new PayCommonException(PayCommonErrorCodeType.PAY_CONFIG_ERROR);
        }
        try {
            SortedMap<String, Object> map = xmlParse(xmlStr);
            logger.info(xmlStr);
            //返回結果
            String return_code = map.get("return_code").toString();
            if ("SUCCESS".equals(return_code)){
                //簽名
                String sign1 = map.get("sign").toString().toUpperCase();
                String sign = PayCommonUtil.createSign(map, resp.getData().getList().get(0).getConfigVal());
                //回調驗籤
                if(sign.equals(sign1)||runInTest){
                    //業務結果
                    String result_code = map.get("result_code").toString();
                    if ("SUCCESS".equals(return_code)&&"SUCCESS".equals(result_code)){
                        //商戶訂單號
                        String out_trade_no = map.get("out_trade_no").toString();
                        notifyResponse.setTradeNo(out_trade_no);
                        //根據商戶訂單號獲取訂單信息
                        PayOrderInfo payOrderInfo = iPayOrderService.queryPayOrderInfoByTradeNo(out_trade_no);
                        if ("5".equals(payOrderInfo.getCurrencyType())||"4".equals(payOrderInfo.getCurrencyType())){
                            notifyResponse.setResultXml(setXml("FAIL", "該訂單已失敗或超時"));
                            return notifyResponse;
                        }
                        //根據商戶訂單號查到的狀態判斷是否進行下一步驟
                        payOrderInfo.setCurrencyType("1");
                        //交易金額
                        String total_fee = map.get("total_fee").toString();
                        //商戶系統對於支付結果通知的內容必定要作簽名驗證,並校驗返回的訂單金額是否與商戶側的訂單金額一致,防止數據泄漏致使出現「假通知」,形成資金損失。
                        if (Integer.valueOf(total_fee)!= payOrderInfo.getTradeAmount()) {
                            throw new PayCommonException(PayCommonErrorCodeType.PAY_INCORRECT_AMOUNT);
                        }
                        payOrderInfo.setTradeAmount(Integer.valueOf(total_fee));
                        //支付完成時間
                        String time_end = map.get("time_end").toString();
                        if (time_end!=null){
                            payOrderInfo.setTradeEndTime(new Date());
                        }
                        //充值方式
                        payOrderInfo.setTradeChannel(1004);
                        //微信訂單號
                        String transaction_id = map.get("transaction_id").toString();
                        notifyResponse.setTradeNo(transaction_id);
                        payOrderInfo.setTransactionId(transaction_id);
                        //加入訂單信息
                        if(time_end!=null){
                            //成功
                            payOrderInfo.setTradeStatus(3);
                        }
                        iPayOrderService.updatePayOrderByOrderNo(payOrderInfo);
                        logger.info("支付成功!訂單號爲:"+payOrderInfo.getTradeNo()+"-----"+"第三方訂單號:"+payOrderInfo.getTransactionId());
                        notifyResponse.setResultXml(setXml("SUCCESS", "OK"));
                        return notifyResponse;
                    }else {
                        // 支付出錯
                        notifyResponse.setResultXml(setXml("FAIL","支付訂單校驗出錯"));
                    }

                }else {
                    //驗籤失敗
                    notifyResponse.setResultXml(setXml("FAIL","支付驗籤失敗"));
                }
            }else {
                notifyResponse.setResultXml(setXml("FAIL","支付不成功"));
            }


        } catch (Exception e) {
            logger.error("支付回調異常: " + e.getMessage());
            notifyResponse.setResultXml(setXml("FAIL","支付回調異常"));
            return notifyResponse;
        }

        return null;
    }

 

  

搞定

相關文章
相關標籤/搜索