微信扫码支付——模式二
代码:
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包。