微信掃碼支付——模式二
代碼:
PayCommonUtil.java 微信支付常用方法
1 import java.text.SimpleDateFormat; 2 import java.util.Date; 3 import java.util.Iterator; 4 import java.util.Map; 5 import java.util.Set; 6 import java.util.SortedMap; 7 8 public class PayCommonUtil 9 { 10 /** 11 * 是否簽名正確,規則是:按參數名稱a-z排序,遇到空值的參數不參加簽名。 12 * @return boolean 13 */ 14 public static boolean isTenpaySign(String characterEncoding, SortedMap<Object, Object> packageParams, String API_KEY) { 15 StringBuffer sb = new StringBuffer(); 16 Set es = packageParams.entrySet(); 17 Iterator it = es.iterator(); 18 while(it.hasNext()) { 19 Map.Entry entry = (Map.Entry)it.next(); 20 String k = (String)entry.getKey(); 21 String v = (String)entry.getValue(); 22 if(!"sign".equals(k) && null != v && !"".equals(v)) { 23 sb.append(k + "=" + v + "&"); 24 } 25 } 26 27 sb.append("key=" + API_KEY); 28 29 //算出摘要 30 String mysign = MD5Util.MD5Encode(sb.toString(), characterEncoding).toLowerCase(); 31 String tenpaySign = ((String)packageParams.get("sign")).toLowerCase(); 32 33 //System.out.println(tenpaySign + " " + mysign); 34 return tenpaySign.equals(mysign); 35 } 36 37 /** 38 * @author 39 * @date 2016-4-22 40 * @Description:sign簽名 41 * @param characterEncoding 42 * 編碼格式 43 * @param parameters 44 * 請求參數 45 * @return 46 */ 47 public static String createSign(String characterEncoding, SortedMap<Object, Object> packageParams, String API_KEY) { 48 StringBuffer sb = new StringBuffer(); 49 Set es = packageParams.entrySet(); 50 Iterator it = es.iterator(); 51 while (it.hasNext()) { 52 Map.Entry entry = (Map.Entry) it.next(); 53 String k = (String) entry.getKey(); 54 String v = (String) entry.getValue(); 55 if (null != v && !"".equals(v) && !"sign".equals(k) && !"key".equals(k)) { 56 sb.append(k + "=" + v + "&"); 57 } 58 } 59 sb.append("key=" + API_KEY); 60 String sign = MD5Util.MD5Encode(sb.toString(), characterEncoding).toUpperCase(); 61 return sign; 62 } 63 64 /** 65 * @author 66 * @date 2016-4-22 67 * @Description:將請求參數轉換為xml格式的string 68 * @param parameters 69 * 請求參數 70 * @return 71 */ 72 public static String getRequestXml(SortedMap<Object, Object> parameters) { 73 StringBuffer sb = new StringBuffer(); 74 sb.append("<xml>"); 75 Set es = parameters.entrySet(); 76 Iterator it = es.iterator(); 77 while (it.hasNext()) { 78 Map.Entry entry = (Map.Entry) it.next(); 79 String k = (String) entry.getKey(); 80 String v = (String) entry.getValue(); 81 if ("attach".equalsIgnoreCase(k) || "body".equalsIgnoreCase(k) || "sign".equalsIgnoreCase(k)) { 82 sb.append("<" + k + ">" + "<![CDATA[" + v + "]]></" + k + ">"); 83 } else { 84 sb.append("<" + k + ">" + v + "</" + k + ">"); 85 } 86 } 87 sb.append("</xml>"); 88 return sb.toString(); 89 } 90 91 /** 92 * 取出一個指定長度大小的隨機正整數. 93 * 94 * @param length 95 * int 設定所取出隨機數的長度。length小於11 96 * @return int 返回生成的隨機數。 97 */ 98 public static int buildRandom(int length) { 99 int num = 1; 100 double random = Math.random(); 101 if (random < 0.1) { 102 random = random + 0.1; 103 } 104 for (int i = 0; i < length; i++) { 105 num = num * 10; 106 } 107 return (int) ((random * num)); 108 } 109 110 /** 111 * 獲取當前時間 yyyyMMddHHmmss 112 * 113 * @return String 114 */ 115 public static String getCurrTime() { 116 Date now = new Date(); 117 SimpleDateFormat outFormat = new SimpleDateFormat("yyyyMMddHHmmss"); 118 String s = outFormat.format(now); 119 return s; 120 } 121 }
XMLUtil.java 將支付參數轉換為xml格式
1 import java.io.ByteArrayInputStream; 2 import java.io.IOException; 3 import java.io.InputStream; 4 import java.util.HashMap; 5 import java.util.Iterator; 6 import java.util.List; 7 import java.util.Map; 8 9 import org.jdom.Document; 10 import org.jdom.Element; 11 import org.jdom.JDOMException; 12 import org.jdom.input.SAXBuilder; 13 14 public class XMLUtil 15 { 16 /** 17 * 解析xml,返回第一級元素鍵值對。如果第一級元素有子節點,則此節點的值是子節點的xml數據。 18 * @param strxml 19 * @return 20 * @throws JDOMException 21 * @throws IOException 22 */ 23 public static Map doXMLParse(String strxml) throws JDOMException, IOException { 24 strxml = strxml.replaceFirst("encoding=\".*\"", "encoding=\"UTF-8\""); 25 26 if(null == strxml || "".equals(strxml)) { 27 return null; 28 } 29 30 Map m = new HashMap(); 31 32 InputStream in = new ByteArrayInputStream(strxml.getBytes("UTF-8")); 33 SAXBuilder builder = new SAXBuilder(); 34 Document doc = builder.build(in); 35 Element root = doc.getRootElement(); 36 List list = root.getChildren(); 37 Iterator it = list.iterator(); 38 while(it.hasNext()) { 39 Element e = (Element) it.next(); 40 String k = e.getName(); 41 String v = ""; 42 List children = e.getChildren(); 43 if(children.isEmpty()) { 44 v = e.getTextNormalize(); 45 } else { 46 v = XMLUtil.getChildrenText(children); 47 } 48 49 m.put(k, v); 50 } 51 52 //關閉流 53 in.close(); 54 55 return m; 56 } 57 58 /** 59 * 獲取子結點的xml 60 * @param children 61 * @return String 62 */ 63 public static String getChildrenText(List children) { 64 StringBuffer sb = new StringBuffer(); 65 if(!children.isEmpty()) { 66 Iterator it = children.iterator(); 67 while(it.hasNext()) { 68 Element e = (Element) it.next(); 69 String name = e.getName(); 70 String value = e.getTextNormalize(); 71 List list = e.getChildren(); 72 sb.append("<" + name + ">"); 73 if(!list.isEmpty()) { 74 sb.append(XMLUtil.getChildrenText(list)); 75 } 76 sb.append(value); 77 sb.append("</" + name + ">"); 78 } 79 } 80 81 return sb.toString(); 82 } 83 }
HttpUtil.java 用http連接提交支付信息參數
1 import java.io.BufferedReader; 2 import java.io.IOException; 3 import java.io.InputStreamReader; 4 import java.io.OutputStreamWriter; 5 import java.net.URL; 6 import java.net.URLConnection; 7 8 import org.apache.commons.logging.Log; 9 import org.apache.commons.logging.LogFactory; 10 11 12 public class HttpUtil 13 { 14 private static final Log logger = LogFactory.getLog("org.apache.catalina.tribes.MESSAGES" ); 15 16 private final static int CONNECT_TIMEOUT = 5000; // in milliseconds 17 18 private final static String DEFAULT_ENCODING = "UTF-8"; 19 20 public static String postData(String urlStr, String data){ 21 return postData(urlStr, data, null); 22 } 23 24 public static String postData(String urlStr, String data, String contentType){ 25 BufferedReader reader = null; 26 try { 27 URL url = new URL(urlStr); 28 URLConnection conn = url.openConnection(); 29 conn.setDoOutput(true); 30 conn.setConnectTimeout(CONNECT_TIMEOUT); 31 conn.setReadTimeout(CONNECT_TIMEOUT); 32 if(contentType != null) 33 conn.setRequestProperty("content-type", contentType); 34 OutputStreamWriter writer = new OutputStreamWriter(conn.getOutputStream(), DEFAULT_ENCODING); 35 if(data == null) 36 data = ""; 37 writer.write(data); 38 writer.flush(); 39 writer.close(); 40 41 reader = new BufferedReader(new InputStreamReader(conn.getInputStream(), DEFAULT_ENCODING)); 42 System.out.println(reader.toString()); 43 StringBuilder sb = new StringBuilder(); 44 String line = null; 45 while ((line = reader.readLine()) != null) { 46 sb.append(line); 47 sb.append("\r\n"); 48 } 49 return sb.toString(); 50 } catch (IOException e) { 51 logger.error("Error connecting to " + urlStr + ": " + e.getMessage()); 52 } finally { 53 try { 54 if (reader != null) 55 reader.close(); 56 } catch (IOException e) { 57 } 58 } 59 return null; 60 } 61 }
MD5Util.java md5加密
1 private static String byteToHexString(byte b) { 2 int n = b; 3 if (n < 0) 4 n += 256; 5 int d1 = n / 16; 6 int d2 = n % 16; 7 return hexDigits[d1] + hexDigits[d2]; 8 } 9 10 public static String MD5Encode(String origin, String charsetname) { 11 String resultString = null; 12 try { 13 resultString = new String(origin); 14 MessageDigest md = MessageDigest.getInstance("MD5"); 15 if (charsetname == null || "".equals(charsetname)) 16 resultString = byteArrayToHexString(md.digest(resultString 17 .getBytes())); 18 else 19 resultString = byteArrayToHexString(md.digest(resultString 20 .getBytes(charsetname))); 21 } catch (Exception exception) { 22 } 23 return resultString; 24 } 25 26 private static final String hexDigits[] = { "0", "1", "2", "3", "4", "5", 27 "6", "7", "8", "9", "a", "b", "c", "d", "e", "f" }; 28 }
GetWeixinUrlUtil.java 獲取訂單生成的二維碼URL
1 import java.io.UnsupportedEncodingException; 2 import java.net.URLEncoder; 3 import java.util.Map; 4 import java.util.SortedMap; 5 import java.util.TreeMap; 6 7 public class GetWeixinUrlUtil 8 { 9 //獲取二維碼url 10 public static String weixin_pay(String ip, VcWeixinPay vcWeixinPay) throws Exception { 11 // 賬號信息 12 String appid = PayConfigUtil.APP_ID; // appid 13 String mch_id = PayConfigUtil.MCH_ID; // 商戶號 14 String key = PayConfigUtil.API_KEY; // key 15 String currTime = PayCommonUtil.getCurrTime(); 16 String strTime = currTime.substring(8, currTime.length()); 17 String strRandom = PayCommonUtil.buildRandom(4) + ""; 18 String nonce_str = strTime + strRandom; 19 int order_price = 1; // 商品價格 注意:價格的單位是分 20 String body = "商品名稱"; // 商品名稱 21 String out_trade_no = PayCommonUtil.getCurrTime() + PayCommonUtil.buildRandom(4); // 商戶訂單號 22 // 獲取發起電腦 ip 23 String spbill_create_ip = ip; 24 // 回調接口 25 String notify_url = PayConfigUtil.NOTIFY_URL; 26 String trade_type = "NATIVE"; //交易類型 27 SortedMap<Object,Object> packageParams = new TreeMap<Object,Object>(); 28 packageParams.put("appid", appid); 29 packageParams.put("mch_id", mch_id); 30 packageParams.put("nonce_str", nonce_str); 31 packageParams.put("body", body); 32 packageParams.put("out_trade_no", out_trade_no); 33 packageParams.put("total_fee", String.valueOf(order_price)); 34 packageParams.put("spbill_create_ip", spbill_create_ip); 35 packageParams.put("notify_url", notify_url); 36 packageParams.put("trade_type", trade_type); 37 //生成簽名 38 String sign = PayCommonUtil.createSign("UTF-8", packageParams,key); 39 packageParams.put("sign", sign); 40 41 String requestXML = PayCommonUtil.getRequestXml(packageParams); 42 System.out.println(requestXML); 43 44 String resXml = HttpUtil.postData(PayConfigUtil.UFDODER_URL, requestXML); 45 Map map = XMLUtil.doXMLParse(resXml); 48 String urlCode = (String) map.get("code_url"); 49 50 return urlCode; 51 } 52 53 // 特殊字符處理 54 public static String UrlEncode(String src) throws UnsupportedEncodingException { 55 return URLEncoder.encode(src, "UTF-8").replace("+", "%20"); 56 } 57 }
PayConfigUtil.java 微信支付參數
1 import javax.servlet.http.HttpServletRequest; 2 3 public class PayConfigUtil 4 { 5 public static final String APP_ID = "";//微信開發平台應用ID(公眾號ID) 6 public static final String MCH_ID = "";//商戶號(商戶號ID) 7 public static final String API_KEY = "";//API key(商戶號里面的) 8 public static final String CREATE_IP = "";//發起支付的ip 9 public static final String NOTIFY_URL = "";//回調地址 10 public static final String UFDODER_URL = "https://api.mch.weixin.qq.com/pay/unifiedorder";//微信統一下單接口 11 public static final String APP_SECRET = "";//應用對應的憑證(在公眾號里面) 12 13 //獲取ip 14 public static String getIP(HttpServletRequest request) 15 { 16 String ip = request.getRemoteAddr(); 17 return ip; 18 } 19
支付回調方法
1 public void weixin_notify(HttpServletRequest request,HttpServletResponse response) throws Exception 2 { 3 System.out.println("調用回調方法"); 4 //讀取參數 5 InputStream inputStream ; 6 StringBuffer sb = new StringBuffer(); 7 inputStream = request.getInputStream(); 8 String s ; 9 BufferedReader in = new BufferedReader(new InputStreamReader(inputStream, "UTF-8")); 10 while ((s = in.readLine()) != null){ 11 sb.append(s); 12 } 13 in.close(); 14 inputStream.close(); 15 16 //解析xml成map 17 Map<String, String> m = new HashMap<String, String>(); 18 m = XMLUtil.doXMLParse(sb.toString()); 19 20 //過濾空 設置 TreeMap 21 SortedMap<Object,Object> packageParams = new TreeMap<Object,Object>(); 22 Iterator it = m.keySet().iterator(); 23 while (it.hasNext()) { 24 String parameter = (String) it.next(); 25 String parameterValue = m.get(parameter); 26 27 String v = ""; 28 if(null != parameterValue) { 29 v = parameterValue.trim(); 30 } 31 packageParams.put(parameter, v); 32 } 33 34 // 賬號信息 35 String key = PayConfigUtil.API_KEY; // key 36 37 logger.info(packageParams); 38 //判斷簽名是否正確 39 if(PayCommonUtil.isTenpaySign("UTF-8", packageParams,key)) { 40 //------------------------------ 41 //處理業務開始 42 //------------------------------ 44 String resXml = ""; 45 if("SUCCESS".equals((String) packageParams.get("result_code"))){ 46 // 這里是支付成功 47 //////////執行自己的業務邏輯//////////////// 48 String mch_id = (String) packageParams.get("mch_id"); 49 String openid = (String) packageParams.get("openid"); 50 String is_subscribe = (String) packageParams.get("is_subscribe"); 51 String out_trade_no = (String) packageParams.get("out_trade_no"); 52 String total_fee = (String) packageParams.get("total_fee"); 53 String cash_fee_s = (String) packageParams.get("cash_fee"); 54 String cash_fee = String.valueOf(Integer.parseInt(cash_fee_s) / 100); 55 String time_end = MctsUtils.numberDateToDate((String) packageParams.get("time_end")); 56 String transaction_id = (String) packageParams.get("transaction_id"); 57 58 //////////執行自己的業務邏輯(報存訂單信息到數據庫)//////////////// 59 System.out.println("支付成功 ,處理業務成功"); 60 logger.info("支付成功"); 61 //通知微信.異步確認成功.必寫.不然會一直通知后台.八次之后就認為交易失敗了. 62 resXml = "<xml>" + "<return_code><![CDATA[SUCCESS]]></return_code>" 63 + "<return_msg><![CDATA[OK]]></return_msg>" + "</xml> "; 64 //------------------------------ 65 //處理業務完畢 66 //------------------------------
//向微信服務器發送確認信息,若不發送,微信服務器會間隔不同的時間調用回調方法 67 BufferedOutputStream out = new BufferedOutputStream( 68 response.getOutputStream()); 69 out.write(resXml.getBytes()); 70 out.flush(); 71 out.close(); 73 System.out.println("通知微信.異步確認成功"); 74 } else { 93 logger.info("支付失敗,錯誤信息:" + packageParams.get("err_code")); 94 resXml = "<xml>" + "<return_code><![CDATA[FAIL]]></return_code>" 95 + "<return_msg><![CDATA[報文為空]]></return_msg>" + "</xml> "; 96 97 BufferedOutputStream out = new BufferedOutputStream( 98 response.getOutputStream()); 99 out.write(resXml.getBytes()); 100 out.flush(); 101 out.close(); 102 System.out.println("執行回調函數失敗"); 103 } 105 } else{ 106 logger.info("通知簽名驗證失敗"); 107 } 108 }
根據GetWeixinUrlUtil.java獲取到微信支付訂單信息的二維碼url,下載一個解析二維碼的js插件(jquery.qrcode.min.js),把獲取到二維碼url在前端頁面用jquery.qrcode.min.js解析,就能顯示二維碼了。
前端頁面簡略代碼:
1 <script type="text/javascript" src="js/jquery.qrcode.min.js"></script> 2 <script language="javascript"> 3 $(function(){ 4 var codeUrl = ${erWeiMa};//erWeiMa是后端傳的二維碼url 5 $("#code").qrcode({ 6 render: "canvas", //table方式 7 width: 240, //寬度 8 height:240, //高度 9 text: codeUrl //任意內容 10 }); 11 }); 12 </script> 13 14 <div id="code" style="width: 240px;height: 240px;margin: 0px auto;"></div>
其中用到的jar包當時忘記記錄了,不知道的可以根據報錯信息搜下缺少的jar包。