Pārlūkot izejas kodu

微信支付基础组间

小柒2012 8 gadi atpakaļ
vecāks
revīzija
3b739d392a

+ 237 - 0
src/main/java/com/itstyle/common/utils/AddressUtils.java

@@ -0,0 +1,237 @@
+package com.itstyle.common.utils;
+import java.io.BufferedReader;
+import java.io.DataOutputStream;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.UnsupportedEncodingException;
+import java.net.HttpURLConnection;
+import java.net.URL;
+
+import javax.servlet.http.HttpServletRequest;
+
+import org.apache.commons.lang.StringUtils;
+/**
+ * 根据IP地址获取详细的地域信息
+ * 创建者 科帮网
+ * 创建时间	2017年7月31日
+ *
+ */
+public class AddressUtils { 
+	/**
+	 *
+	 * @param content
+	 *            请求的参数 格式为:name=xxx&pwd=xxx
+	 * @param encoding
+	 *            服务器端请求编码。如GBK,UTF-8等
+	 * @return
+	 * @throws UnsupportedEncodingException
+	 */
+	public static String getAddresses(String ip) throws UnsupportedEncodingException {
+		String urlStr ="http://ip.taobao.com/service/getIpInfo.php";
+		String returnStr = getResult(urlStr, ip);
+		if (returnStr != null) {
+			// 处理返回的省市区信息
+			String[] temp = returnStr.split(",");
+			if (temp.length < 3) {
+				return "0";// 无效IP,局域网测试
+			}
+			String region = (temp[5].split(":"))[1].replaceAll("\"", "");
+			region = decodeUnicode(region);// 省份
+
+			String country = "";
+			String area = "";
+			// String region = "";
+			String city = "";
+			String county = "";
+			String isp = "";
+			for (int i = 0; i < temp.length; i++) {
+				switch (i) {
+				case 1:
+					country = (temp[i].split(":"))[2].replaceAll("\"", "");
+					country = decodeUnicode(country);// 国家
+					break;
+				case 3:
+					area = (temp[i].split(":"))[1].replaceAll("\"", "");
+					area = decodeUnicode(area);// 地区
+					break;
+				case 5:
+					region = (temp[i].split(":"))[1].replaceAll("\"", "");
+					region = decodeUnicode(region);// 省份
+					break;
+				case 7:
+					city = (temp[i].split(":"))[1].replaceAll("\"", "");
+					city = decodeUnicode(city);// 市区
+					break;
+				case 9:
+					county = (temp[i].split(":"))[1].replaceAll("\"", "");
+					county = decodeUnicode(county);// 地区
+					break;
+				case 11:
+					isp = (temp[i].split(":"))[1].replaceAll("\"", "");
+					isp = decodeUnicode(isp); // ISP公司
+					break;
+				}
+			}
+			String address = region+city;
+			if(StringUtils.isBlank(address)){
+				address = "地球村";
+			}
+			return address;
+		}
+		return null;
+	}
+
+	/**
+	 * @param urlStr
+	 *            请求的地址
+	 * @param content
+	 *            请求的参数 格式为:name=xxx&pwd=xxx
+	 * @param encoding
+	 *            服务器端请求编码。如GBK,UTF-8等
+	 * @return
+	 */
+	private static String getResult(String urlStr, String ip) {
+		URL url = null;
+		HttpURLConnection connection = null;
+		try {
+			url = new URL(urlStr);
+			connection = (HttpURLConnection) url.openConnection();// 新建连接实例
+			/**
+			 * 超时错误 由 2s改为5s
+			 */
+			connection.setConnectTimeout(5000);// 设置连接超时时间,单位毫秒
+			connection.setReadTimeout(5000);// 设置读取数据超时时间,单位毫秒
+			connection.setDoOutput(true);// 是否打开输出流 true|false
+			connection.setDoInput(true);// 是否打开输入流true|false
+			connection.setRequestMethod("POST");// 提交方法POST|GET
+			connection.setUseCaches(false);// 是否缓存true|false
+			connection.connect();// 打开连接端口
+			DataOutputStream out = new DataOutputStream(connection.getOutputStream());// 打开输出流往对端服务器写数据
+			out.writeBytes("ip="+ip);// 写数据,也就是提交你的表单 name=xxx&pwd=xxx
+			out.flush();// 刷新
+			out.close();// 关闭输出流
+			BufferedReader reader = new BufferedReader(new InputStreamReader(connection.getInputStream(), "utf-8"));// 往对端写完数据对端服务器返回数据
+			// ,以BufferedReader流来读取
+			StringBuffer buffer = new StringBuffer();
+			String line = "";
+			while ((line = reader.readLine()) != null) {
+				buffer.append(line);
+			}
+			reader.close();
+			return buffer.toString();
+		} catch (IOException e) {
+			e.printStackTrace();
+		} finally {
+			if (connection != null) {
+				connection.disconnect();// 关闭连接
+			}
+		}
+		return null;
+	}
+
+	/**
+	 * unicode 转换成 中文
+	 * @param theString
+	 * @return
+	 */
+	public static String decodeUnicode(String theString) {
+		char aChar;
+		int len = theString.length();
+		StringBuffer outBuffer = new StringBuffer(len);
+		for (int x = 0; x < len;) {
+			aChar = theString.charAt(x++);
+			if (aChar == '\\') {
+				aChar = theString.charAt(x++);
+				if (aChar == 'u') {
+					int value = 0;
+					for (int i = 0; i < 4; i++) {
+						aChar = theString.charAt(x++);
+						switch (aChar) {
+						case '0':
+						case '1':
+						case '2':
+						case '3':
+						case '4':
+						case '5':
+						case '6':
+						case '7':
+						case '8':
+						case '9':
+							value = (value << 4) + aChar - '0';
+							break;
+						case 'a':
+						case 'b':
+						case 'c':
+						case 'd':
+						case 'e':
+						case 'f':
+							value = (value << 4) + 10 + aChar - 'a';
+							break;
+						case 'A':
+						case 'B':
+						case 'C':
+						case 'D':
+						case 'E':
+						case 'F':
+							value = (value << 4) + 10 + aChar - 'A';
+							break;
+						default:
+							throw new IllegalArgumentException("Malformed      encoding.");
+						}
+					}
+					outBuffer.append((char) value);
+				} else {
+					if (aChar == 't') {
+						aChar = '\t';
+					} else if (aChar == 'r') {
+						aChar = '\r';
+					} else if (aChar == 'n') {
+						aChar = '\n';
+					} else if (aChar == 'f') {
+						aChar = '\f';
+					}
+					outBuffer.append(aChar);
+				}
+			} else {
+				outBuffer.append(aChar);
+			}
+		}
+		return outBuffer.toString();
+	}
+	/**
+	 * 获取IP地址
+	 * @Author  科帮网
+	 * @param request
+	 * @return  String
+	 * @Date	2017年7月31日
+	 * 更新日志
+	 * 2017年7月31日  科帮网 首次创建
+	 *
+	 */
+    public static String getIpAddr(HttpServletRequest request)
+    {
+        String ip = request.getHeader("X-Real-IP");
+        if(!StringUtils.isBlank(ip) && !"unknown".equalsIgnoreCase(ip))
+            return ip;
+        ip = request.getHeader("X-Forwarded-For");
+        if(!StringUtils.isBlank(ip) && !"unknown".equalsIgnoreCase(ip))
+        {
+            int index = ip.indexOf(',');
+            if(index != -1)
+               return ip.substring(0, index);
+            else
+                return ip;
+        }
+        if(ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip))
+            ip = request.getHeader("Proxy-Client-IP");
+        if(ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip))
+            ip = request.getHeader("WL-Proxy-Client-IP");
+        if(ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip))
+            ip = request.getHeader("HTTP_CLIENT_IP");
+        if(ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip))
+            ip = request.getHeader("HTTP_X_FORWARDED_FOR");
+        if(ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip))
+            ip = request.getRemoteAddr();
+        return ip;
+    }
+}  

+ 285 - 0
src/main/java/com/itstyle/common/utils/DateUtil.java

@@ -0,0 +1,285 @@
+package com.itstyle.common.utils;
+import java.sql.Timestamp;
+import java.text.DateFormat;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.Calendar;
+import java.util.Date;
+
+import org.apache.commons.lang.StringUtils;
+/**
+ * 日期操作类
+ * 创建者 科帮网
+ * 创建时间	2017年7月31日
+ *
+ */
+public class DateUtil {
+	private final static SimpleDateFormat sdfYear = new SimpleDateFormat("yyyy");
+
+	private final static SimpleDateFormat sdfDay = new SimpleDateFormat("yyyy-MM-dd");
+
+	private final static SimpleDateFormat sdfDays = new SimpleDateFormat("yyyyMMdd");
+
+	private final static SimpleDateFormat sdfTime = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
+
+	/**
+	 * 获取YYYY格式
+	 *
+	 * @return
+	 */
+	public static String getYear() {
+		return sdfYear.format(new Date());
+	}
+
+	Timestamp timestamp = new Timestamp(new Date().getTime());
+
+	/**
+	 * 获取YYYY-MM-DD格式
+	 *
+	 * @return
+	 */
+	public static String getDay() {
+		return sdfDay.format(new Date());
+	}
+
+	/**
+	 * 获取YYYYMMDD格式
+	 *
+	 * @return
+	 */
+	public static String getDays() {
+		return sdfDays.format(new Date());
+	}
+
+	/**
+	 * 获取YYYY-MM-DD hh:mm:ss格式
+	 *
+	 * @return
+	 */
+	public static String getTime() {
+		return sdfTime.format(new Date());
+	}
+
+	/**
+	* @Title: compareDate
+	* @Description:(日期比较,如果s>=e 返回true 否则返回false)
+	* @param s
+	* @param e
+	* @return boolean
+	* @throws
+	* @author luguosui
+	 */
+	public static boolean compareDate(String s, String e) {
+		if (fomatDate(s) == null || fomatDate(e) == null) {
+			return false;
+		}
+		return fomatDate(s).getTime() >= fomatDate(e).getTime();
+	}
+
+	/**
+	 * 格式化日期
+	 *
+	 * @return
+	 */
+	public static Date fomatDate(String date) {
+		DateFormat fmt = new SimpleDateFormat("yyyy-MM-dd");
+		try {
+			return fmt.parse(date);
+		} catch (ParseException e) {
+			e.printStackTrace();
+			return null;
+		}
+	}
+
+	/**
+	 * 校验日期是否合法
+	 *
+	 * @return
+	 */
+	public static boolean isValidDate(String s) {
+		DateFormat fmt = new SimpleDateFormat("yyyy-MM-dd");
+		try {
+			fmt.parse(s);
+			return true;
+		} catch (Exception e) {
+			// 如果throw java.text.ParseException或者NullPointerException,就说明格式不对
+			return false;
+		}
+	}
+
+	public static int getDiffYear(String startTime, String endTime) {
+		DateFormat fmt = new SimpleDateFormat("yyyy-MM-dd");
+		try {
+			int years = (int) (((fmt.parse(endTime).getTime() - fmt.parse(startTime).getTime()) / (1000 * 60 * 60 * 24)) / 365);
+			return years;
+		} catch (Exception e) {
+			// 如果throw java.text.ParseException或者NullPointerException,就说明格式不对
+			return 0;
+		}
+	}
+
+	/**
+	* <li>功能描述:时间相减得到天数
+	* @param beginDateStr
+	* @param endDateStr
+	* @return
+	* long
+	* @author Administrator
+	*/
+	public static long getDaySub(String beginDateStr, String endDateStr) {
+		long day = 0;
+		java.text.SimpleDateFormat format = new java.text.SimpleDateFormat("yyyy-MM-dd");
+		java.util.Date beginDate = null;
+		java.util.Date endDate = null;
+
+		try {
+			beginDate = format.parse(beginDateStr);
+			endDate = format.parse(endDateStr);
+		} catch (ParseException e) {
+			e.printStackTrace();
+		}
+		day = (endDate.getTime() - beginDate.getTime()) / (24 * 60 * 60 * 1000);
+		// System.out.println("相隔的天数="+day);
+
+		return day;
+	}
+
+	/**
+	 * 得到n天之后的日期
+	 * @param days
+	 * @return
+	 */
+	public static String getAfterDayDate(String days) {
+		int daysInt = Integer.parseInt(days);
+
+		Calendar canlendar = Calendar.getInstance(); // java.util包
+		canlendar.add(Calendar.DATE, daysInt); // 日期减 如果不够减会将月变动
+		Date date = canlendar.getTime();
+
+		SimpleDateFormat sdfd = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
+		String dateStr = sdfd.format(date);
+
+		return dateStr;
+	}
+	/**
+	 * 得到n天之前的日期
+	 * @param days
+	 * @return
+	 */
+	public static String getBeforeDayDate(String days) {
+		int daysInt = Integer.parseInt(days);
+
+		Calendar canlendar = Calendar.getInstance(); // java.util包
+		canlendar.add(Calendar.DATE, daysInt); // 日期减 如果不够减会将月变动
+		Date date = canlendar.getTime();
+
+		String dateStr = sdfDays.format(date);
+
+		return dateStr;
+	}
+
+	/**
+	 * 得到n天之后是周几
+	 * @param days
+	 * @return
+	 */
+	public static String getAfterDayWeek(String days) {
+		int daysInt = Integer.parseInt(days);
+
+		Calendar canlendar = Calendar.getInstance(); // java.util包
+		canlendar.add(Calendar.DATE, daysInt); // 日期减 如果不够减会将月变动
+		Date date = canlendar.getTime();
+
+		SimpleDateFormat sdf = new SimpleDateFormat("E");
+		String dateStr = sdf.format(date);
+
+		return dateStr;
+	}
+
+	/**
+	 * 按照yyyy-MM-dd HH:mm:ss的格式,日期转字符串
+	 * @param date
+	 * @return yyyy-MM-dd HH:mm:ss
+	 */
+	public static String date2Str(Date date) {
+		return date2Str(date, "yyyy-MM-dd");
+	}
+
+	/**
+	 * 按照yyyy-MM-dd HH:mm:ss的格式,字符串转日期
+	 * @param date
+	 * @return
+	 */
+	public static Date str2Date(String date) {
+		if (StringUtils.isNotBlank(date)) {
+			SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
+			try {
+				return sdf.parse(date);
+			} catch (ParseException e) {
+				e.printStackTrace();
+			}
+			return new Date();
+		} else {
+			return null;
+		}
+	}
+
+	/**
+	 * 按照参数format的格式,日期转字符串
+	 * @param date
+	 * @param format
+	 * @return
+	 */
+	public static String date2Str(Date date, String format) {
+		if (null == format) {
+			format = "yyyy-MM-dd";
+		}
+		if (date != null) {
+			SimpleDateFormat sdf = new SimpleDateFormat(format);
+			return sdf.format(date);
+		} else {
+			return "";
+		}
+	}
+
+	/**
+	 * 把时间根据时、分、秒转换为时间段
+	 * @param StrDate
+	 */
+	public static String getTimes(String StrDate) {
+		String resultTimes = "";
+
+		SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
+		java.util.Date now;
+
+		try {
+			now = new Date();
+			java.util.Date date = df.parse(StrDate);
+			long times = now.getTime() - date.getTime();
+			long day = times / (24 * 60 * 60 * 1000);
+			long hour = (times / (60 * 60 * 1000) - day * 24);
+			long min = ((times / (60 * 1000)) - day * 24 * 60 - hour * 60);
+			long sec = (times / 1000 - day * 24 * 60 * 60 - hour * 60 * 60 - min * 60);
+
+			StringBuffer sb = new StringBuffer();
+			// sb.append("发表于:");
+			if (hour > 0) {
+				sb.append(hour + "小时前");
+			} else if (min > 0) {
+				sb.append(min + "分钟前");
+			} else {
+				sb.append(sec + "秒前");
+			}
+
+			resultTimes = sb.toString();
+		} catch (ParseException e) {
+			e.printStackTrace();
+		}
+
+		return resultTimes;
+	}
+
+	public static String getTimestamp() {
+		return String.valueOf(System.currentTimeMillis() / 1000);
+	}
+}

+ 184 - 0
src/main/java/com/itstyle/modules/weixinpay/controller/WeixinMobilePayController.java

@@ -0,0 +1,184 @@
+package com.itstyle.modules.weixinpay.controller;
+
+import java.io.BufferedOutputStream;
+import java.util.Map;
+import java.util.SortedMap;
+import java.util.TreeMap;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.stereotype.Controller;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestMethod;
+
+import com.itstyle.common.utils.AddressUtils;
+import com.itstyle.common.utils.DateUtil;
+import com.itstyle.modules.weixinpay.util.ConfigUtil;
+import com.itstyle.modules.weixinpay.util.HttpUtil;
+import com.itstyle.modules.weixinpay.util.PayCommonUtil;
+import com.itstyle.modules.weixinpay.util.XMLUtil;
+import com.itstyle.modules.weixinpay.util.mobile.MobileUtil;
+/**
+ * 微信H5支付
+ * 创建者 科帮网
+ * 创建时间	2017年7月31日
+ *
+ */
+@Controller
+@RequestMapping(value = "weixinMobile")
+public class WeixinMobilePayController {
+	private static final Logger logger = LoggerFactory.getLogger(WeixinMobilePayController.class);
+	
+	/**
+	 * 预下单(对于已经产生的订单)
+	 * @Author  科帮网
+	 * @param request
+	 * @param response
+	 * @return
+	 * @throws Exception  String
+	 * @Date	2017年7月31日
+	 * 更新日志
+	 * 2017年7月31日  科帮网 首次创建
+	 *
+	 */
+	@SuppressWarnings("rawtypes")
+	@RequestMapping(value = "dopay")
+	public String dopay(HttpServletRequest request, HttpServletResponse response) throws Exception {
+		String state = request.getParameter("state");
+		String[] split = state.split("/");
+		String orderNo = split[0];
+		String totalFee = split[1];
+		//获取code 这个在微信支付调用时会自动加上这个参数 无须设置
+		String code = request.getParameter("code");
+		//获取用户openID(JSAPI支付必须传openid)
+		String openId = MobileUtil.getOpenId(code);
+		String pay_url = "http://blog.52itstyle.com";
+		String notify_url =pay_url+"/weixinMobile/WXPayBack";//回调接口
+		String trade_type = "JSAPI";// 交易类型H5支付
+		SortedMap<Object, Object> packageParams = new TreeMap<Object, Object>();
+		ConfigUtil.commonParams(packageParams);
+		packageParams.put("body","报告");// 商品描述
+		packageParams.put("out_trade_no", orderNo);// 商户订单号
+		packageParams.put("total_fee", totalFee);// 总金额
+		packageParams.put("spbill_create_ip", AddressUtils.getIpAddr(request));// 发起人IP地址
+		packageParams.put("notify_url", notify_url);// 回调地址
+		packageParams.put("trade_type", trade_type);// 交易类型
+		packageParams.put("openid", openId);//用户openID
+		String sign = PayCommonUtil.createSign("UTF-8", packageParams,ConfigUtil.API_KEY);
+		packageParams.put("sign", sign);// 签名
+		String requestXML = PayCommonUtil.getRequestXml(packageParams);
+		String resXml = HttpUtil.postData(ConfigUtil.UNIFIED_ORDER_URL, requestXML);
+		Map map = XMLUtil.doXMLParse(resXml);
+		String returnCode = (String) map.get("return_code");
+		String returnMsg = (String) map.get("return_msg");
+		StringBuffer url = new StringBuffer();
+		if("SUCCESS".equals(returnCode)){
+			String resultCode = (String) map.get("result_code");
+			String errCodeDes = (String) map.get("err_code_des");
+			if("SUCCESS".equals(resultCode)){
+				//获取预支付交易会话标识
+				String prepay_id = (String) map.get("prepay_id");
+				String prepay_id2 = "prepay_id=" + prepay_id;
+				String packages = prepay_id2;
+				SortedMap<Object, Object> finalpackage = new TreeMap<Object, Object>();
+				String timestamp = DateUtil.getTimestamp();
+				String nonceStr = packageParams.get("nonce_str").toString();
+				finalpackage.put("appId",  ConfigUtil.APP_ID);
+				finalpackage.put("timeStamp", timestamp);
+				finalpackage.put("nonceStr", nonceStr);
+				finalpackage.put("package", packages);  
+				finalpackage.put("signType", "MD5");
+				//这里很重要  参数一定要正确 狗日的腾讯 参数到这里就成大写了
+				//可能报错信息(支付验证签名失败 get_brand_wcpay_request:fail)
+				sign = PayCommonUtil.createSign("UTF-8", finalpackage,ConfigUtil.API_KEY);
+				url.append("redirect:/weixinMobile/pay?");
+				url.append("timeStamp="+timestamp+"&nonceStr=" + nonceStr + "&package=" + packages);
+				url.append("&signType=MD5" + "&paySign=" + sign+"&appid="+ ConfigUtil.APP_ID);
+				url.append("&orderNo="+orderNo+"&totalFee="+totalFee);
+			}else{
+				logger.info("订单号:{}错误信息:{}",orderNo,errCodeDes);
+				url.append("redirect:/weixinMobile/error?code=0&orderNo="+orderNo);//该订单已支付
+			}
+		}else{
+			logger.info("订单号:{}错误信息:{}",orderNo,returnMsg);
+			url.append("redirect:/weixinMobile/error?code=1&orderNo="+orderNo);//系统错误
+		}
+		return url.toString();
+	}
+	/**
+	 * 支付请求
+	 * @Author  科帮网
+	 * @param request
+	 * @param response
+	 * @param orderNo
+	 * @param totalFee
+	 * @return
+	 * @throws Exception  String
+	 * @Date	2017年7月31日
+	 * 更新日志
+	 * 2017年7月31日  科帮网 首次创建
+	 *
+	 */
+	@RequestMapping(value = "pay/{orderNo}/{totalFee}", method=RequestMethod.GET)
+	public String pay(HttpServletRequest request, HttpServletResponse response,
+			          @PathVariable String orderNo,@PathVariable String totalFee) throws Exception {
+		StringBuffer url = new StringBuffer();
+		String pay_url = "http://blog.52itstyle.com";
+		url.append("http://open.weixin.qq.com/connect/oauth2/authorize?");
+		url.append("appid="+ConfigUtil.APP_ID);
+		url.append("&redirect_uri="+pay_url+"/weixinMobile/dopay");
+		url.append("&response_type=code&scope=snsapi_base&state=");
+		url.append(orderNo+"/"+totalFee);//订单号/金额(单位是分)
+		url.append("#wechat_redirect");
+		return "redirect:"+url.toString();
+	}
+	/**
+	 * 手机支付完成回调
+	 * @Author  科帮网
+	 * @param request
+	 * @param response
+	 * @param platForm  void
+	 * @Date	2017年7月31日
+	 * 更新日志
+	 * 2017年7月31日  科帮网 首次创建
+	 *
+	 */
+	@RequestMapping(value = "WXPayBack")
+	public void WXPayBack(HttpServletRequest request, HttpServletResponse response){
+		String resXml = "";
+		try {
+			//解析XML
+			Map<String, String> map = MobileUtil.parseXml(request);
+	        String return_code = map.get("return_code");//状态
+	        String out_trade_no = map.get("out_trade_no");//订单号
+			if (return_code.equals("SUCCESS")) {
+				if (out_trade_no != null) {
+					//处理订单逻辑
+					logger.info("微信手机支付回调成功订单号:{}",out_trade_no);
+					resXml = "<xml>" + "<return_code><![CDATA[SUCCESS]]></return_code>" + "<return_msg><![CDATA[OK]]></return_msg>" + "</xml> ";
+				}
+			}else{
+				logger.info("微信手机支付回调失败订单号:{}",out_trade_no);
+				resXml = "<xml>" + "<return_code><![CDATA[FAIL]]></return_code>" + "<return_msg><![CDATA[报文为空]]></return_msg>" + "</xml> ";
+			}
+		} catch (Exception e) {
+			logger.error("手机支付回调通知失败",e);
+			 resXml = "<xml>" + "<return_code><![CDATA[FAIL]]></return_code>" + "<return_msg><![CDATA[报文为空]]></return_msg>" + "</xml> ";
+		}
+		try {
+			// ------------------------------
+			// 处理业务完毕
+			// ------------------------------
+			BufferedOutputStream out = new BufferedOutputStream(response.getOutputStream());
+			out.write(resXml.getBytes());
+			out.flush();
+			out.close();
+		} catch (Exception e) {
+			e.printStackTrace();
+		}
+	}
+}

+ 107 - 0
src/main/java/com/itstyle/modules/weixinpay/controller/WeixinPayController.java

@@ -0,0 +1,107 @@
+package com.itstyle.modules.weixinpay.controller;
+import java.io.BufferedOutputStream;
+import java.io.BufferedReader;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.SortedMap;
+import java.util.TreeMap;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.stereotype.Controller;
+import org.springframework.web.bind.annotation.RequestMapping;
+
+import com.itstyle.modules.weixinpay.util.ConfigUtil;
+import com.itstyle.modules.weixinpay.util.PayCommonUtil;
+import com.itstyle.modules.weixinpay.util.XMLUtil;
+
+/**
+ * 微信二维码支付
+ * 创建者 科帮网
+ * 创建时间	2017年7月31日
+ */
+@Controller
+@RequestMapping(value = "weixin")
+public class WeixinPayController {
+	private static final Logger logger = LoggerFactory.getLogger(WeixinPayController.class);
+	/**
+	 * 支付后台回调
+	 * @Author  科帮网
+	 * @param request
+	 * @param response
+	 * @throws Exception  void
+	 * @Date	2017年7月31日
+	 * 更新日志
+	 * 2017年7月31日  科帮网 首次创建
+	 *
+	 */
+	@SuppressWarnings({ "unchecked", "rawtypes" })
+	@RequestMapping(value = "pay")
+	public void weixin_notify(HttpServletRequest request, HttpServletResponse response) throws Exception {
+		// 读取参数
+		InputStream inputStream = request.getInputStream();
+		StringBuffer sb = new StringBuffer();
+		String s;
+		BufferedReader in = new BufferedReader(new InputStreamReader(inputStream, "UTF-8"));
+		while ((s = in.readLine()) != null) {
+			sb.append(s);
+		}
+		in.close();
+		inputStream.close();
+
+		// 解析xml成map
+		Map<String, String> m = new HashMap<String, String>();
+		m = XMLUtil.doXMLParse(sb.toString());
+
+		// 过滤空 设置 TreeMap
+		SortedMap<Object, Object> packageParams = new TreeMap<Object, Object>();
+		Iterator it = m.keySet().iterator();
+		while (it.hasNext()) {
+			String parameter = (String) it.next();
+			String parameterValue = m.get(parameter);
+
+			String v = "";
+			if (null != parameterValue) {
+				v = parameterValue.trim();
+			}
+			packageParams.put(parameter, v);
+		}
+		// 账号信息
+		String key = ConfigUtil.API_KEY; // key
+		// 判断签名是否正确
+		if (PayCommonUtil.isTenpaySign("UTF-8", packageParams, key)) {
+			logger.info("微信支付成功回调");
+			// ------------------------------
+			// 处理业务开始
+			// ------------------------------
+			String resXml = "";
+			if ("SUCCESS".equals((String) packageParams.get("result_code"))) {
+				// 这里是支付成功
+				String orderNo = (String) packageParams.get("out_trade_no");
+				logger.info("微信订单号{}付款成功",orderNo);
+				//这里 根据实际业务场景 做相应的操作
+				// 通知微信.异步确认成功.必写.不然会一直通知后台.八次之后就认为交易失败了.
+				resXml = "<xml>" + "<return_code><![CDATA[SUCCESS]]></return_code>" + "<return_msg><![CDATA[OK]]></return_msg>" + "</xml> ";
+			} else {
+				logger.info("支付失败,错误信息:{}",packageParams.get("err_code"));
+				resXml = "<xml>" + "<return_code><![CDATA[FAIL]]></return_code>" + "<return_msg><![CDATA[报文为空]]></return_msg>" + "</xml> ";
+			}
+			// ------------------------------
+			// 处理业务完毕
+			// ------------------------------
+			BufferedOutputStream out = new BufferedOutputStream(response.getOutputStream());
+			out.write(resXml.getBytes());
+			out.flush();
+			out.close();
+		} else {
+			logger.info("通知签名验证失败");
+		}
+
+	}
+}

+ 61 - 0
src/main/java/com/itstyle/modules/weixinpay/service/IWeixinPayService.java

@@ -0,0 +1,61 @@
+package com.itstyle.modules.weixinpay.service;
+
+import com.itstyle.common.model.Product;
+
+
+public interface IWeixinPayService {
+	/**
+	 * 微信支付下单
+	 * @Author  科帮网
+	 * @param product
+	 * @return  String
+	 * @Date	2017年7月31日
+	 * 更新日志
+	 * 2017年7月31日  科帮网 首次创建
+	 *
+	 */
+	String weixinPay(Product product);
+    /**
+     * 微信支付退款
+     * @Author  科帮网
+     * @param product
+     * @return  String
+     * @Date	2017年7月31日
+     * 更新日志
+     * 2017年7月31日  科帮网 首次创建
+     *
+     */
+	String weixinRefund(Product product);
+	/**
+	 * 关闭订单
+	 * @Author  科帮网
+	 * @param product
+	 * @return  String
+	 * @Date	2017年7月31日
+	 * 更新日志
+	 * 2017年7月31日  科帮网 首次创建
+	 *
+	 */
+	String weixinCloseorder(Product product);
+	/**
+	 * 下载微信账单
+	 * @Author  科帮网  void
+	 * @Date	2017年7月31日
+	 * 更新日志
+	 * 2017年7月31日  科帮网 首次创建
+	 *
+	 */
+	void saveBill();
+    /**
+     * 微信手机支付返回一个url地址
+     * @Author  科帮网
+     * @param product
+     * @return  String
+     * @Date	2017年7月31日
+     * 更新日志
+     * 2017年7月31日  科帮网 首次创建
+     *
+     */
+	String weixinPayMobile(Product product);
+	
+}

+ 225 - 0
src/main/java/com/itstyle/modules/weixinpay/service/impl/WeixinPayServiceImpl.java

@@ -0,0 +1,225 @@
+package com.itstyle.modules.weixinpay.service.impl;
+
+import java.util.Map;
+import java.util.SortedMap;
+import java.util.TreeMap;
+import net.sf.json.JSONObject;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.stereotype.Service;
+import com.alipay.demo.trade.utils.ZxingUtils;
+import com.itstyle.common.constants.Constants;
+import com.itstyle.common.model.Product;
+import com.itstyle.modules.weixinpay.service.IWeixinPayService;
+import com.itstyle.modules.weixinpay.util.ClientCustomSSL;
+import com.itstyle.modules.weixinpay.util.ConfigUtil;
+import com.itstyle.modules.weixinpay.util.HttpUtil;
+import com.itstyle.modules.weixinpay.util.PayCommonUtil;
+import com.itstyle.modules.weixinpay.util.XMLUtil;
+@Service("weixinPayService")
+public class WeixinPayServiceImpl implements IWeixinPayService {
+	private static final Logger logger = LoggerFactory.getLogger(WeixinPayServiceImpl.class);
+	@SuppressWarnings("rawtypes")
+	@Override
+	public String weixinPay(Product product) {
+		logger.info(product.getAttach()+"(订单号:"+product.getOutTradeNo()+"生成微信支付码)");
+		String  message = Constants.SUCCESS;
+		try {
+			String imgPath= "D:\\"+product.getOutTradeNo()+".png";
+			// 账号信息
+			String key = ConfigUtil.API_KEY; // key
+			// 回调接口
+			String notify_url = Constants.PAY_URL.get("weixin_pc_notify_url");
+			String trade_type = "NATIVE";// 交易类型 原生扫码支付
+
+			SortedMap<Object, Object> packageParams = new TreeMap<Object, Object>();
+			ConfigUtil.commonParams(packageParams);
+			packageParams.put("product_id", product.getProductId());// 商品ID
+			packageParams.put("body", product.getBody());// 商品描述
+			packageParams.put("out_trade_no", product.getOutTradeNo());// 商户订单号
+			String totalFee = product.getTotalFee();
+			if(totalFee.indexOf(".")>0){//去除小数点 invalide total_fee  报错
+				totalFee = totalFee.substring(0, totalFee.indexOf("."));
+			}
+			packageParams.put("total_fee", totalFee);// 总金额
+			packageParams.put("spbill_create_ip", product.getSpbillCreateIp());// 发起人IP地址
+			packageParams.put("notify_url", notify_url);// 回调地址
+			packageParams.put("trade_type", trade_type);// 交易类型
+			String sign = PayCommonUtil.createSign("UTF-8", packageParams, key);
+			packageParams.put("sign", sign);// 签名
+
+			String requestXML = PayCommonUtil.getRequestXml(packageParams);
+			String resXml = HttpUtil.postData(ConfigUtil.UNIFIED_ORDER_URL, requestXML);
+			Map map = XMLUtil.doXMLParse(resXml);
+			String returnCode = (String) map.get("return_code");
+			if("SUCCESS".equals(returnCode)){
+				String resultCode = (String) map.get("result_code");
+				if("SUCCESS".equals(resultCode)){
+					logger.info(product.getAttach()+"(订单号:"+product.getOutTradeNo()+"生成微信支付码成功)");
+					String urlCode = (String) map.get("code_url");
+					ConfigUtil.shorturl(urlCode);//转换为短链接
+					ZxingUtils.getQRCodeImge(urlCode, 256, imgPath);// 生成二维码
+				}else{
+					String errCodeDes = (String) map.get("err_code_des");
+					logger.info(product.getAttach()+"(订单号:"+product.getOutTradeNo()+"生成微信支付码(系统)失败["+errCodeDes+"])");
+					message = Constants.FAIL;
+				}
+			}else{
+				String returnMsg = (String) map.get("return_msg");
+				logger.info(product.getAttach()+"(订单号:"+product.getOutTradeNo()+"生成微信支付码(通信)失败["+returnMsg+"])");
+				message = Constants.FAIL;
+			}
+		} catch (Exception e) {
+			logger.error(product.getAttach()+"(订单号:"+product.getOutTradeNo()+"生成微信支付码失败(系统异常))", e);
+			message = Constants.FAIL;
+		}
+		return message;
+	}
+	@SuppressWarnings("rawtypes")
+	@Override
+	public String weixinRefund(Product product) {
+		logger.info("订单号:"+product.getOutTradeNo()+"微信退款");
+		String  message = Constants.SUCCESS;
+		try {
+			// 账号信息
+			String mch_id = ConfigUtil.MCH_ID; // 商业号
+			String key = ConfigUtil.API_KEY; // key
+			
+			SortedMap<Object, Object> packageParams = new TreeMap<Object, Object>();
+			ConfigUtil.commonParams(packageParams);
+			packageParams.put("out_trade_no", product.getOutTradeNo());// 商户订单号
+			packageParams.put("out_refund_no", product.getOutTradeNo());//商户退款单号
+			String totalFee = product.getTotalFee();
+			if(totalFee.indexOf(".")>0){//去除小数点 invalide total_fee  报错
+				totalFee = totalFee.substring(0, totalFee.indexOf("."));
+			}
+			packageParams.put("total_fee", totalFee);// 总金额
+			packageParams.put("refund_fee", totalFee);//退款金额
+			packageParams.put("op_user_id", mch_id);//操作员帐号, 默认为商户号
+			String sign = PayCommonUtil.createSign("UTF-8", packageParams, key);
+			packageParams.put("sign", sign);// 签名
+			String requestXML = PayCommonUtil.getRequestXml(packageParams);
+			String weixinPost = ClientCustomSSL.doRefund(ConfigUtil.REFUND_URL, requestXML).toString(); 
+			Map map = XMLUtil.doXMLParse(weixinPost);
+			String returnCode = (String) map.get("return_code");
+			if("SUCCESS".equals(returnCode)){
+				String resultCode = (String) map.get("result_code");
+				if("SUCCESS".equals(resultCode)){
+					logger.info("订单号:"+product.getOutTradeNo()+"微信退款成功并删除二维码");
+				}else{
+					String errCodeDes  = (String) map.get("err_code_des");
+					logger.info("订单号:"+product.getOutTradeNo()+"微信退款失败("+errCodeDes+")");
+					message = Constants.FAIL;
+				}
+			}else{
+				String returnMsg = (String) map.get("return_msg");
+				logger.info("订单号:"+product.getOutTradeNo()+"微信退款失败("+returnMsg+")");
+				message = Constants.FAIL;
+			}
+		} catch (Exception e) {
+			logger.error("订单号:"+product.getOutTradeNo()+"微信支付失败(系统异常)", e);
+			message = Constants.FAIL;
+		}
+		return message;
+	}
+
+	@SuppressWarnings("rawtypes")
+	@Override
+	public String weixinCloseorder(Product product) {
+		logger.info("订单号:"+product.getOutTradeNo()+"微信关闭订单");
+		String  message = Constants.SUCCESS;
+		try {
+			String key = ConfigUtil.API_KEY; // key
+			SortedMap<Object, Object> packageParams = new TreeMap<Object, Object>();
+			ConfigUtil.commonParams(packageParams);
+			packageParams.put("out_trade_no", product.getOutTradeNo());// 商户订单号
+			String sign = PayCommonUtil.createSign("UTF-8", packageParams, key);
+			packageParams.put("sign", sign);// 签名
+			String requestXML = PayCommonUtil.getRequestXml(packageParams);
+			String resXml = HttpUtil.postData(ConfigUtil.CLOSE_ORDER_URL, requestXML);
+			Map map = XMLUtil.doXMLParse(resXml);
+			String returnCode = (String) map.get("return_code");
+			if("SUCCESS".equals(returnCode)){
+				String resultCode =  (String) map.get("result_code");
+				if("SUCCESS".equals(resultCode)){
+					logger.info("订单号:"+product.getOutTradeNo()+"微信关闭订单成功");
+				}else{
+					String errCode =  (String) map.get("err_code");
+					String errCodeDes =  (String) map.get("err_code_des");
+					if("ORDERNOTEXIST".equals(errCode)||"ORDERCLOSED".equals(errCode)){//订单不存在或者已经关闭
+						logger.info("订单号:"+product.getOutTradeNo()+"微信关闭订单("+errCodeDes+")");
+					}else{
+						logger.info("订单号:"+product.getOutTradeNo()+"微信关闭订单失败("+errCodeDes+")");
+						message = Constants.FAIL;
+					}
+				}
+			}else{
+				String returnMsg = (String) map.get("return_msg");
+				logger.info("订单号:"+product.getOutTradeNo()+"微信关闭订单失败"+returnMsg);
+				message = Constants.FAIL;
+			}
+		} catch (Exception e) {
+			logger.error("订单号:"+product.getOutTradeNo()+"微信关闭订单失败(系统异常)", e);
+			message = Constants.FAIL;
+		}
+		return message;
+	}
+	/**
+	 * 商户可以通过该接口下载历史交易清单。比如掉单、系统错误等导致商户侧和微信侧数据不一致,通过对账单核对后可校正支付状态。
+		注意:
+		1、微信侧未成功下单的交易不会出现在对账单中。支付成功后撤销的交易会出现在对账单中,跟原支付单订单号一致,bill_type为REVOKED;
+		2、微信在次日9点启动生成前一天的对账单,建议商户10点后再获取;
+		3、对账单中涉及金额的字段单位为“元”。
+		
+		4、对账单接口只能下载三个月以内的账单。
+	 */
+	@SuppressWarnings("rawtypes")
+	@Override
+	public void saveBill() {
+		try {
+			String key = ConfigUtil.API_KEY; // key
+			//获取两天以前的账单
+			//String billDate = DateUtil.getBeforeDayDate("2");
+			SortedMap<Object, Object> packageParams = new TreeMap<Object, Object>();
+			ConfigUtil.commonParams(packageParams);//公用部分
+			packageParams.put("bill_type", "ALL");//ALL,返回当日所有订单信息,默认值SUCCESS,返回当日成功支付的订单REFUND,返回当日退款订单
+			//packageParams.put("tar_type", "GZIP");//压缩账单
+			packageParams.put("bill_date", "20161206");//账单日期
+			String sign = PayCommonUtil.createSign("UTF-8", packageParams, key);
+			packageParams.put("sign", sign);// 签名
+			String requestXML = PayCommonUtil.getRequestXml(packageParams);
+			String resXml = HttpUtil.postData(ConfigUtil.DOWNLOAD_BILL_URL, requestXML);
+            if(resXml.startsWith("<xml>")){
+            	Map map = XMLUtil.doXMLParse(resXml);
+    			String returnMsg = (String) map.get("return_msg");
+    			logger.info("微信查询订单失败("+returnMsg+")");
+			}else{
+				 //入库
+			}
+		} catch (Exception e) {
+			e.printStackTrace();
+			logger.error("微信查询订单异常", e);
+		}
+		
+	}
+	public static void main(String[] args) {
+		JSONObject obj = new JSONObject();
+		obj.put("attach","11");
+	}
+	@Override
+	public String weixinPayMobile(Product product) {
+		StringBuffer url = new StringBuffer();
+		String totalFee = product.getTotalFee();
+		if(totalFee.indexOf(".")>0){//去除小数点 invalide total_fee  报错
+			totalFee = totalFee.substring(0, totalFee.indexOf("."));
+		}
+		String pay_url = Constants.PAY_URL.get("pay_url");
+		url.append("http://open.weixin.qq.com/connect/oauth2/authorize?");
+		url.append("appid="+ConfigUtil.APP_ID);
+		url.append("&redirect_uri="+pay_url+"/weixinMobile/dopay");
+		url.append("&response_type=code&scope=snsapi_base&state=");
+		url.append(product.getOutTradeNo()+"/"+totalFee);//订单号/金额(单位是分)
+		url.append("#wechat_redirect");
+		return  url.toString();
+	}
+}