java實現微信h5支付

    前段時間給了了需求對公司網站添加微信支付,由於之前沒接觸過,簡單的東西愣是寫了好幾天。話不多少,直接開始。

    首先你需要先看微信的官方文檔https://pay.weixin.qq.com/wiki/doc/api/H5.php?chapter=15_1,這裏面幾乎介紹了全部流程了。等你瞭解大概流程之後,需要在微信公衆平臺和微信商戶平臺拿到或者配置一下參數

    appid:公衆平臺的appid

    商戶號:公衆平臺的商戶號

    appsecrut:商戶平臺裏的簽名密鑰(17年8月開始移到商戶平臺了,這個要保存好,現在不支持查看,忘了就得重新配置了)

    

   最後在商戶平臺上圖這個位置配置h5支付的域名(需要通過備案的域名,外網能直接訪問)

    拿到以上數據後,就開始寫代碼吧。首先下單

   

​
	@ResponseBody
	@RequestMapping(value = "/pay" ,produces = { "application/json;charset=UTF-8" })
	public String weixinPayWap(HttpServletRequest request, HttpServletResponse response, ModelMap model) {
		String APPID = "你的apid";
                String MERID = "你的商戶號";
                String SIGNKEY = "你的商戶密鑰";
		String spbill_create_ip = getIpAddr(request);//生產
		System.out.println("spbill_create_ip="+spbill_create_ip);
		//String spbill_create_ip = "";//測試地址,也就是本地真是ip,用於本地測試用
		String scene_info = "{\"h5_info\": {\"type\":\"Wap\",\"wap_url\": \"這裏寫在h5支付配置的那個域名\",\"wap_name\": \"信息認證\"}}";//我這裏是網頁入口,app入口參考文檔的安卓和ios寫法
		String tradeType = "MWEB";//H5支付標記
		String MD5 = "MD5";//雖然官方文檔不是必須參數,但是不送有時候會驗籤失敗
		JSONObject result = new JSONObject();
		String subject = request.getParameter("subject");//前端上送的支付主題
		String total_amount = request.getParameter("totalAmount");//前端上送的支付金額
		String timestamp = String.valueOf(System.currentTimeMillis() / 1000);
		//金額轉化爲分爲單位 微信支付以分爲單位
	    String finalmoney = StringUtils.getMoney(total_amount);
		int randomNum  = (int) (Math.random() * 1999+5000);
		String out_trade_no = TimeUtils.getSysTime("yyyyMMddHHmmss") + randomNum;
	    //隨機數 
	    String nonce_str= MD5Utils.getMessageDigest(String.valueOf(new Random().nextInt(10000)).getBytes());
	    //簽名數據
	    StringBuilder sb = new StringBuilder();
	    sb.append("appid="+APPID);
	    sb.append("&body="+subject);
	    sb.append("&mch_id="+MERID);
	    sb.append("&nonce_str="+nonce_str);
	    sb.append("&notify_url="+"這裏寫回調地址");
	    sb.append("&out_trade_no="+out_trade_no);
	    sb.append("&scene_info="+scene_info);
	    sb.append("&sign_type="+"MD5");
	    sb.append("&spbill_create_ip="+spbill_create_ip);
	    sb.append("&total_fee="+finalmoney);
	    sb.append("&trade_type="+tradeType);
	    sb.append("&key="+SIGNKEY);
	    System.out.println("sb="+sb);
	    //簽名MD5加密
	    String sign = "把sb.toString()做MD5操作並且toUpperCase()一下,至於怎麼MD5,百度一下或者看官方文檔";
	    System.out.println("sign="+sign);
	    log.info("簽名數據:"+sign);
	    //封裝xml報文
	    String xml="<xml>"+
	            "<appid>"+ APPID+"</appid>"+
	            "<mch_id>"+ MERID+"</mch_id>"+
	            "<nonce_str>"+nonce_str+"</nonce_str>"+
	            "<sign>"+sign+"</sign>"+
	            "<body>"+subject+"</body>"+//
	            "<out_trade_no>"+out_trade_no+"</out_trade_no>"+
	            "<total_fee>"+finalmoney+"</total_fee>"+//
	            "<trade_type>"+tradeType+"</trade_type>"+
	            "<notify_url>"+"這裏寫回調地址"+"</notify_url>"+
	            "<sign_type>MD5</sign_type>"+
	            "<scene_info>"+scene_info+"</scene_info>"+
	            "<spbill_create_ip>"+spbill_create_ip+"</spbill_create_ip>"+
	            "</xml>";
	    
	    String createOrderURL = "https://api.mch.weixin.qq.com/pay/unifiedorder";//微信統一下單接口
	    String mweb_url = "";
	    Map map = new HashMap();
	    try {
	    	//預下單 獲取接口地址
	    	map = WebUtils.getMwebUrl(createOrderURL, xml);
    		String return_code  = (String) map.get("return_code");
	        String return_msg = (String) map.get("return_msg");
	        if("SUCCESS".equals(return_code) && "OK".equals(return_msg)){
	        	 mweb_url = (String) map.get("mweb_url");//調微信支付接口地址
	        	 System.out.println("mweb_url="+mweb_url);
	        }else{
	        	System.out.println("統一支付接口獲取預支付訂單出錯");
	        	result.put("msg", "支付錯誤");
	        	return result.toString();
	        }
	    } catch (Exception e) {
	    	System.out.println("統一支付接口獲取預支付訂單出錯");
	        result.put("msg", "支付錯誤");
	        return result.toString();
	    }
	    result.put("mwebUrl",mweb_url);
	    
		//添加微信支付記錄日誌等操作
	    
		
		result.put("msg", "success");
		return result.toString();
	}
        
    	/**
	 * 獲取用戶實際ip
	 * @param request
	 * @return
	 */
	 public String getIpAddr(HttpServletRequest request){  
        String ipAddress = request.getHeader("x-forwarded-for");  
            if(ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {  
                ipAddress = request.getHeader("Proxy-Client-IP");  
            }  
            if(ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {  
                ipAddress = request.getHeader("WL-Proxy-Client-IP");  
            }  
            if(ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {  
                ipAddress = request.getRemoteAddr();  
                if(ipAddress.equals("127.0.0.1") || ipAddress.equals("0:0:0:0:0:0:0:1")){  
                    //根據網卡取本機配置的IP  
                    InetAddress inet=null;  
                    try {  
                        inet = InetAddress.getLocalHost();  
                    } catch (UnknownHostException e) {  
                        e.printStackTrace();  
                    }  
                    ipAddress= inet.getHostAddress();  
                }  
            }  
            //對於通過多個代理的情況,第一個IP爲客戶端真實IP,多個IP按照','分割  
            if(ipAddress!=null && ipAddress.length()>15){ //"***.***.***.***".length() = 15  
                if(ipAddress.indexOf(",")>0){  
                    ipAddress = ipAddress.substring(0,ipAddress.indexOf(","));  
                }  
            }  
            return ipAddress;   
	  }

​

    如果簽名驗證失敗就在https://pay.weixin.qq.com/wiki/tools/signverify/調試下簽名 記住上送xml裏的全都要進行簽名,並且排列順序要按ASCLL碼的順序從小到大(好像是這樣,可以看官方文檔的簽名方式)

  接下來是回調

	@RequestMapping(value = "/notify")
	public void weixinPayNotify(HttpServletRequest request, HttpServletResponse response) throws Exception {
		BufferedReader reader = request.getReader();
        String line = "";
        Map map = new HashMap();
        String xml = "<xml><return_code><![CDATA[FAIL]]></xml>";;
        JSONObject dataInfo = new JSONObject();
        StringBuffer inputString = new StringBuffer();
        while ((line = reader.readLine()) != null) {
            inputString.append(line);
        }
        request.getReader().close();
        System.out.println("----接收到的報文---"+inputString.toString());
        if(inputString.toString().length()>0){
        	 map = XMLUtils.parseXmlToList(inputString.toString());
        }else{
        	System.out.println("接受微信報文爲空");
        }
        System.out.println("map="+map);
        if(map!=null && "SUCCESS".equals(map.get("result_code"))){
            //成功的業務。。。
        	xml = "<xml><return_code><![CDATA[SUCCESS]]></return_code><return_msg><![CDATA[OK]]></return_msg></xml>";
        }else{
        	//失敗的業務。。。
        }
        //告訴微信端已經確認支付成功
        response.getWriter().write(xml);
	}

  接下來是工具類

public class WebUtils {

    	public static Map getMwebUrl(String url,String xmlParam){
		  String jsonStr = null;
	      HttpClient httpClient = new HttpClient();
	      Map map = new HashMap();
	      try {
	    	 	PostMethod method = null;
				RequestEntity reqEntity = new StringRequestEntity(xmlParam,"text/json","UTF-8");
				method = new PostMethod(url);
				method.setRequestEntity(reqEntity);
				method.addRequestHeader("Content-Type","application/json;charset=utf-8");
				httpClient.executeMethod(method);
				StringBuffer resBodyBuf = new StringBuffer();
				byte[] responseBody = new byte[1024];
				int readCount = 0;
				BufferedInputStream is = new BufferedInputStream(method.getResponseBodyAsStream());
				while((readCount = is.read(responseBody,0,responseBody.length))!=-1){
					resBodyBuf.append(new String(responseBody,0,readCount,"utf-8"));
				}
				jsonStr = resBodyBuf.toString();
	        System.out.println(jsonStr);
	        map = XMLUtils.parseXmlToList(jsonStr);
	    } catch (Exception e) {
	        e.printStackTrace();
	    }
	    return map;
	  }
}


 

public class XMLUtils {
    
    	/**
     * description: 解析微信通知xml
     * 
     * @param xml
     * @return
     * @author ex_yangxiaoyi
     * @see
     */
    @SuppressWarnings({ "unused", "rawtypes", "unchecked" })
    public static Map parseXmlToList(String xml) {
        Map retMap = new HashMap();
        try {
            StringReader read = new StringReader(xml);
            // 創建新的輸入源SAX 解析器將使用 InputSource 對象來確定如何讀取 XML 輸入
            InputSource source = new InputSource(read);
            // 創建一個新的SAXBuilder
            SAXBuilder sb = new SAXBuilder();
            // 通過輸入源構造一個Document
            Document doc = (Document) sb.build(source);
            Element root = doc.getRootElement();// 指向根節點
            List<Element> es = root.getChildren();
            if (es != null && es.size() != 0) {
                for (Element element : es) {
                    retMap.put(element.getName(), element.getValue());
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return retMap;
	
    }
}
public class TimeUtils {

     /**
	 * 取得系統時間
	 * @param pattern eg:yyyy-MM-dd HH:mm:ss,SSS
	 * @return
	 */
    public static String getSysTime(String pattern) {

        return formatSysTime(new SimpleDateFormat(pattern));
    }

     /**
     * 格式化系統時間
     * @param format
     * @return
     */
    private static String formatSysTime(SimpleDateFormat format) {

        String str = format.format(Calendar.getInstance().getTime());
        return str;
    }
}

 

public class StringUtils {
    
    	      /**
         * 元轉換成分
         * @param money
         * @return
         */
        public static String getMoney(String amount) {
            if(amount==null){
                return "";
            }
            // 金額轉化爲分爲單位
            String currency =  amount.replaceAll("\\$|\\¥|\\,", "");  //處理包含, ¥ 或者$的金額  
            int index = currency.indexOf(".");  
            int length = currency.length();  
            Long amLong = 0l;  
            if(index == -1){  
                amLong = Long.valueOf(currency+"00");  
            }else if(length - index >= 3){  
                amLong = Long.valueOf((currency.substring(0, index+3)).replace(".", ""));  
            }else if(length - index == 2){  
                amLong = Long.valueOf((currency.substring(0, index+2)).replace(".", "")+0);  
            }else{  
                amLong = Long.valueOf((currency.substring(0, index+1)).replace(".", "")+"00");  
            }  
            return amLong.toString(); 
        }
}

 

public class MD5Utils {

       public final static String getMessageDigest(byte[] buffer) {
        char hexDigits[] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };
        try {
            MessageDigest mdTemp = MessageDigest.getInstance("MD5");
            mdTemp.update(buffer);
            byte[] md = mdTemp.digest();
            int j = md.length;
            char str[] = new char[j * 2];
            int k = 0;
            for (int i = 0; i < j; i++) {
                byte byte0 = md[i];
                str[k++] = hexDigits[byte0 >>> 4 & 0xf];
                str[k++] = hexDigits[byte0 & 0xf];
            }
            return new String(str);
        } catch (Exception e) {
            return null;
        }
    }
}

前端頁面調用返回的地址就行了

 location.href = data.mwebUrl;//這應該看的懂吧  ajax發微信下單請求,返回一個地址直接調用,注意要在手機裏調用,不然會報商戶格式錯誤。

      整個微信h5支付就這樣了,應該沒有遺漏了吧。如果會的話代碼其實很簡單,親測可以。本地測試最多能到下單成功返回mwebUrl這步,支付和回調必須線上測試,如果怕出問題可以寫一個模擬頁面(只有你知道地址)打到線上真是環境測試。

      下篇再寫了公衆號支付的文章,公衆號支付和h5支付大體相同,就是多了獲取用戶openid和前端需要調用微信js接口。

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章