不说废话,直接上代码
先是工具类(注意签名时要排序):
1 import java.io.BufferedReader; 2 import java.io.ByteArrayInputStream; 3 import java.io.File; 4 import java.io.FileInputStream; 5 import java.io.InputStream; 6 import java.io.InputStreamReader; 7 import java.io.OutputStream; 8 import java.net.ConnectException; 9 import java.net.HttpURLConnection; 10 import java.net.URL; 11 import java.security.KeyStore; 12 import java.util.ArrayList; 13 import java.util.Collections; 14 import java.util.Iterator; 15 import java.util.List; 16 import java.util.Map; 17 import java.util.Random; 18 import java.util.Set; 19 import java.util.SortedMap; 20 import java.util.TreeMap; 21 22 import javax.net.ssl.HttpsURLConnection; 23 import javax.net.ssl.SSLContext; 24 25 import org.apache.http.Consts; 26 import org.apache.http.HttpEntity; 27 import org.apache.http.client.methods.CloseableHttpResponse; 28 import org.apache.http.client.methods.HttpPost; 29 import org.apache.http.conn.ssl.SSLConnectionSocketFactory; 30 import org.apache.http.entity.StringEntity; 31 import org.apache.http.impl.client.CloseableHttpClient; 32 import org.apache.http.impl.client.HttpClients; 33 import org.apache.http.ssl.SSLContexts; 34 import org.apache.http.util.EntityUtils; 35 import org.jdom.Document; 36 import org.jdom.Element; 37 import org.jdom.input.SAXBuilder; 38 39 import com.system.property.WXProperty; 40 import com.system.util.Tools; 41 42 public class PayCommonUtil { 43 44 45 /** 46 * post请求并得到返回结果 47 * @param requestUrl 48 * @param requestMethod 49 * @param output 50 * @return 51 */ 52 public static String httpsRequest21(String requestUrl, String requestMethod, String output) { 53 try{ 54 URL url = new URL(requestUrl); 55 HttpsURLConnection connection = (HttpsURLConnection) url.openConnection(); 56 connection.setDoOutput(true); 57 connection.setDoInput(true); 58 connection.setUseCaches(false); 59 connection.setRequestMethod(requestMethod); 60 if (null != output) { 61 OutputStream outputStream = connection.getOutputStream(); 62 outputStream.write(output.getBytes("UTF-8")); 63 outputStream.close(); 64 } 65 // 从输入流读取返回内容 66 InputStream inputStream = connection.getInputStream(); 67 InputStreamReader inputStreamReader = new InputStreamReader(inputStream, "utf-8"); 68 BufferedReader bufferedReader = new BufferedReader(inputStreamReader); 69 String str = null; 70 StringBuffer buffer = new StringBuffer(); 71 while ((str = bufferedReader.readLine()) != null) { 72 buffer.append(str); 73 } 74 bufferedReader.close(); 75 inputStreamReader.close(); 76 inputStream.close(); 77 inputStream = null; 78 connection.disconnect(); 79 return buffer.toString(); 80 }catch(Exception ex){ 81 ex.printStackTrace(); 82 } 83 84 return ""; 85 } 86 87 88 // 随机字符串生成 89 public static String getRandomString(int length) { // length表示生成字符串的长度 90 String base = "abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"; 91 Random random = new Random(); 92 StringBuffer sb = new StringBuffer(); 93 for (int i = 0; i < length; i++) { 94 int number = random.nextInt(base.length()); 95 sb.append(base.charAt(number)); 96 } 97 return sb.toString(); 98 } 99 100 // 请求xml组装101 public static String getRequestXml(SortedMap<String, Object> parameters) {102 StringBuffer sb = new StringBuffer();103 sb.append("<xml>");104 Set es = parameters.entrySet();105 106 Iterator it = es.iterator();107 while (it.hasNext()) {108 Map.Entry entry = (Map.Entry) it.next();109 String key = (String) entry.getKey();110 // String value = (String) entry.getValue();111 Object value = entry.getValue();112 // || "body".equalsIgnoreCase(key) || "sign".equalsIgnoreCase(key)113 if ("attach".equalsIgnoreCase(key) || "body".equalsIgnoreCase(key) || "sign".equalsIgnoreCase(key)) {114 sb.append("<" + key + ">" + "<![CDATA[" + value + "]]></" + key + ">");115 } else {116 sb.append("<" + key + ">" + value + "</" + key + ">");117 }118 }119 sb.append("</xml>");120 return sb.toString();121 }122 123 124 // 生成签名125 public static String createSign(String characterEncoding, SortedMap<String, Object> parameterMap) {126 if (parameterMap == null) {127 return null;128 }129 StringBuffer sb = new StringBuffer();130 List<String> keys = new ArrayList<>(parameterMap.keySet());131 Collections.sort(keys);132 133 for (int i = 0; i < keys.size(); i++) {134 String key = keys.get(i);135 // String value = (String) parameters.get(key);136 Object value = parameterMap.get(key);137 // if (Tools.notEmpty(value)) {138 sb.append((i == 0 ? "" : "&") + key + "=" + value);139 // }140 }141 sb.append("&key=" + WXProperty.get("API_KEY"));142 System.out.println("【生成签名 】" + sb.toString());143 String sign = MD5Util.MD5Encode(sb.toString(), characterEncoding).toUpperCase();144 return sign;145 }146 147 148 // 微信支付请求149 public static String httpsRequest(String requestUrl, String requestMethod, String outputStr) {150 try {151 URL url = new URL(requestUrl);152 HttpURLConnection conn = (HttpURLConnection) url.openConnection();153 conn.setDoOutput(true);154 conn.setDoInput(true);155 conn.setUseCaches(false);156 // 设置请求方式(GET/POST)157 conn.setRequestMethod(requestMethod);158 conn.setRequestProperty("content-type", "application/x-www-form-urlencoded");159 // 当outputStr不为null时向输出流写数据160 if (null != outputStr) {161 OutputStream outputStream = conn.getOutputStream();162 // 注意编码格式163 outputStream.write(outputStr.getBytes("UTF-8"));164 outputStream.close();165 }166 // 从输入流读取返回内容167 InputStream inputStream = conn.getInputStream();168 InputStreamReader inputStreamReader = new InputStreamReader(inputStream, "utf-8");169 BufferedReader bufferedReader = new BufferedReader(inputStreamReader);170 String str;171 StringBuffer buffer = new StringBuffer();172 while ((str = bufferedReader.readLine()) != null) {173 buffer.append(str);174 }175 // 释放资源176 bufferedReader.close();177 inputStreamReader.close();178 inputStream.close();179 conn.disconnect();180 return buffer.toString();181 } catch (ConnectException ce) {182 System.out.println("连接超时:" + ce);183 } catch (Exception e) {184 System.out.println("https请求异常" + e);185 }186 return null;187 }188 189 // 退款的请求方法190 public static String httpsRequest2(String requestUrl, String requestMethod, String outputStr) throws Exception {191 KeyStore keyStore = KeyStore.getInstance("PKCS12");192 StringBuilder res = new StringBuilder("");193 FileInputStream instream = new FileInputStream(new File("/home/apiclient_cert.p12"));194 try {195 keyStore.load(instream, "".toCharArray());196 } finally {197 instream.close();198 }199 200 // Trust own CA and all self-signed certs201 SSLContext sslcontext = SSLContexts.custom().loadKeyMaterial(keyStore, "1313329201".toCharArray()).build();202 // Allow TLSv1 protocol only203 SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(sslcontext, new String[] { "TLSv1" }, null,204 SSLConnectionSocketFactory.BROWSER_COMPATIBLE_HOSTNAME_VERIFIER);205 CloseableHttpClient httpclient = HttpClients.custom().setSSLSocketFactory(sslsf).build();206 try {207 208 HttpPost httpost = new HttpPost("https://api.mch.weixin.qq.com/secapi/pay/refund");209 httpost.addHeader("Connection", "keep-alive");210 httpost.addHeader("Accept", "*/*");211 httpost.addHeader("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8");212 httpost.addHeader("Host", "api.mch.weixin.qq.com");213 httpost.addHeader("X-Requested-With", "XMLHttpRequest");214 httpost.addHeader("Cache-Control", "max-age=0");215 httpost.addHeader("User-Agent", "Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.0) ");216 StringEntity entity2 = new StringEntity(outputStr, Consts.UTF_8);217 httpost.setEntity(entity2);218 System.out.println("executing request" + httpost.getRequestLine());219 220 CloseableHttpResponse response = httpclient.execute(httpost);221 222 try {223 HttpEntity entity = response.getEntity();224 225 System.out.println("----------------------------------------");226 System.out.println(response.getStatusLine());227 if (entity != null) {228 System.out.println("Response content length: " + entity.getContentLength());229 BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(entity.getContent()));230 String text = "";231 res.append(text);232 while ((text = bufferedReader.readLine()) != null) {233 res.append(text);234 System.out.println(text);235 }236 237 }238 EntityUtils.consume(entity);239 } finally {240 response.close();241 }242 } finally {243 httpclient.close();244 }245 return res.toString();246 247 }248 249 // xml解析250 public static SortedMap<String, Object> doXMLParse(String strxml){251 strxml = strxml.replaceFirst("encoding=".*"", "encoding="UTF-8"");252 253 if (null == strxml || "".equals(strxml)) {254 return null;255 }256 257 SortedMap<String, Object> m = new TreeMap<>();258 259 InputStream in;260 try {261 in = new ByteArrayInputStream(strxml.getBytes("UTF-8"));262 SAXBuilder builder = new SAXBuilder();263 Document doc = builder.build(in);264 Element root = doc.getRootElement();265 List list = root.getChildren();266 Iterator it = list.iterator();267 while (it.hasNext()) {268 Element e = (Element) it.next();269 String k = e.getName();270 String v = "";271 List children = e.getChildren();272 if (children.isEmpty()) {273 v = e.getTextNormalize();274 } else {275 v = getChildrenText(children);276 }277 278 m.put(k, v);279 }280 281 // 关闭流282 in.close();283 } catch (Exception e1) {284 e1.printStackTrace();285 }286 287 return m;288 }289 290 public static String getChildrenText(List children) {291 StringBuffer sb = new StringBuffer();292 if (!children.isEmpty()) {293 Iterator it = children.iterator();294 while (it.hasNext()) {295 Element e = (Element) it.next();296 String name = e.getName();297 String value = e.getTextNormalize();298 List list = e.getChildren();299 sb.append("<" + name + ">");300 if (!list.isEmpty()) {301 sb.append(getChildrenText(list));302 }303 sb.append(value);304 sb.append("</" + name + ">");305 }306 }307 308 return sb.toString();309 }310 311 /**312 * 验证微信回调签名313 * @Description314 * @Author zhaozhenhua315 * @date 2018年5月10日316 * @param pd317 * @return318 * @throws Exception319 */320 public static boolean checkWXSign(SortedMap<String, Object> receiveMap) {321 String signFromAPIResponse = (String) receiveMap.get("sign");322 if (Tools.isEmpty(signFromAPIResponse)) {323 System.out.println("API返回的数据签名数据不存在,有可能被第三方篡改!!!");324 return false;325 }326 //清掉返回数据对象里面的Sign数据(不能把这个数据也加进去进行签名),然后用签名算法进行签名327 receiveMap.remove("sign");328 String responseSign = createSign("utf-8", receiveMap);329 if (!responseSign.equals(signFromAPIResponse)) {330 //签名验不过,表示这个API返回的数据有可能已经被篡改了331 System.out.println("API返回的数据签名验证不通过,有可能被第三方篡改!!! responseSign生成的签名为" + responseSign);332 return false;333 }334 335 System.out.println("服务器回包里面的签名是:" + signFromAPIResponse);336 return true;337 }338 339 }
配置文件信息读取:
1 import java.io.FileNotFoundException; 2 import java.io.IOException; 3 import java.io.InputStream; 4 import java.util.Properties; 5 6 import org.slf4j.Logger; 7 import org.slf4j.LoggerFactory; 8 9 public class WXProperty {10 private static final Logger logger = LoggerFactory.getLogger(WXProperty.class);11 12 13 private static Properties props;14 static{15 loadProps();16 }17 18 synchronized static private void loadProps(){19 props = new Properties();20 InputStream in = null;21 22 try {23 in = WXProperty.class.getClassLoader().getResourceAsStream("wxinfo.properties"); 24 props.load(in);25 } catch (FileNotFoundException e) {26 logger.error("payment.properties文件未找到");27 } catch (IOException e) {28 logger.error("出现IOException");29 } finally {30 try {31 if(null != in) {32 in.close();33 }34 } catch (IOException e) {35 logger.error("payment.properties文件流关闭出现异常");36 }37 }38 }39 40 //读取key对应的value41 public static String get(String key){42 if(null == props) {43 loadProps();44 }45 return props.getProperty(key);46 }47 48 }
配置文件(前三个配置为微信申请APP支付通过后给的,而回调地址是自己定义的,在微信统一下单成功后会随着相应信息返回给前端,前端处理成功后执行这个回调接口):
1 #服务号的应用ID 2 APP_ID = XXXXXX 3 #商户号 4 MCH_ID = XXXXXXX 5 #API密钥(前三个为微信申请获得) 6 API_KEY = XXXXXXX 7 #签名加密方式 8 SIGN_TYPE = MD5 9 #微信支付证书名称10 CERT_PATH = XXXXX11 #微信回调地址(自己定义)12 notify_url = XXXXX/success
MD5加密方法:
1 public class MD5Util { 2 3 private static String byteArrayToHexString(byte b[]) { 4 StringBuffer resultSb = new StringBuffer(); 5 for (int i = 0; i < b.length; i++) 6 resultSb.append(byteToHexString(b[i])); 7 8 return resultSb.toString(); 9 }10 11 private static String byteToHexString(byte b) {12 int n = b;13 if (n < 0)14 n += 256;15 int d1 = n / 16;16 int d2 = n % 16;17 return hexDigits[d1] + hexDigits[d2];18 }19 20 public static String MD5Encode(String origin, String charsetname) {21 String resultString = null;22 try {23 resultString = new String(origin);24 MessageDigest md = MessageDigest.getInstance("MD5");25 if (charsetname == null || "".equals(charsetname))26 resultString = byteArrayToHexString(md.digest(resultString.getBytes()));27 else28 resultString = byteArrayToHexString(md.digest(resultString.getBytes(charsetname)));29 } catch (Exception exception) {30 }31 return resultString;32 }33 34 private static final String hexDigits[] = { "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "a", "b", "c", "d", "e", "f" };35 }
微信支付统一下单接口封装的方法:
1 public synchronized static SortedMap<String, Object> weixinPrePay (PageData pd) throws JDOMException, IOException{ 2 Map<String,String> returnMap = new HashMap<>(); 3 String trade_no = (String) pd.get("ORDERNUMBER"); 4 //支付金额 5 BigDecimal totalAmount = new BigDecimal((String)pd.get("ORDERMONEY")); 6 //描述(以商品标题做为描述) 7 String description = "XXXX:"+trade_no; 8 //预支付 9 SortedMap<String, Object> parameterMap = new TreeMap<String, Object>(); 10 parameterMap.put("appid", WXProperty.get("APP_ID")); 11 parameterMap.put("mch_id", WXProperty.get("MCH_ID")); 12 parameterMap.put("nonce_str", PayCommonUtil.getRandomString(32)); 13 parameterMap.put("body", description);14 parameterMap.put("out_trade_no", trade_no);15 parameterMap.put("fee_type", "CNY"); 16 BigDecimal total = totalAmount.multiply(new BigDecimal(100)); 17 //parameterMap.put("total_fee", total.intValue()+""); 18 parameterMap.put("total_fee", 1+""); 19 parameterMap.put("spbill_create_ip", pd.get("spbillCreateIp")); 20 parameterMap.put("notify_url", WXProperty.get("notify_url"));21 parameterMap.put("trade_type", "APP");22 parameterMap.put("sign_type", "MD5");23 parameterMap.put("attach", pd.get("ORDERTYPE"));//附加数据24 String sign = PayCommonUtil.createSign("UTF-8", parameterMap) ;25 parameterMap.put("sign", sign);26 27 String requestXML = PayCommonUtil.getRequestXml(parameterMap);28 29 System.out.println("【转换为xml格式的参数】 "+requestXML); 30 String resultXML = PayCommonUtil.httpsRequest( 31 "https://api.mch.weixin.qq.com/pay/unifiedorder","POST", requestXML); 32 33 System.out.println("【返回的XML】 " + resultXML);34 Map mapResult = PayCommonUtil.doXMLParse(resultXML);35 String returnCode = (String) mapResult.get("return_code");36 System.out.println("【返回内容 】 "+returnCode); 37 if("SUCCESS".equals(returnCode)){38 String resultCode = (String) mapResult.get("result_code");39 if("SUCCESS".equals(resultCode)){40 String prepay_id = (String) mapResult.get("prepay_id");41 42 SortedMap<String, Object> finalpackage = new TreeMap<String, Object>();43 finalpackage.put("appid", WXProperty.get("APP_ID"));44 finalpackage.put("partnerid", WXProperty.get("MCH_ID"));45 finalpackage.put("prepayid", prepay_id);46 String noncestr = PayCommonUtil.getRandomString(32);47 finalpackage.put("noncestr", noncestr);48 String timestamp = String.valueOf(System.currentTimeMillis() / 1000);49 finalpackage.put("timestamp", timestamp);50 finalpackage.put("package", "Sign=WXPay");51 sign = PayCommonUtil.createSign("UTF-8", finalpackage) ;52 SortedMap<String, Object> returnmap = new TreeMap<String, Object>();53 returnmap.put("appid", WXProperty.get("APP_ID"));54 returnmap.put("partnerId", WXProperty.get("MCH_ID"));55 returnmap.put("prepayId", prepay_id);56 returnmap.put("nonceStr", noncestr);57 returnmap.put("timeStamp", timestamp);58 returnmap.put("package", "Sign=WXPay");59 returnmap.put("sign", sign);60 return returnmap;61 }else{62 String errCodeDes = (String) mapResult.get("err_code_des");63 returnMap.put("errCodeDes", errCodeDes);64 }65 }else{66 String returnMsg = (String) mapResult.get("return_msg");67 returnMap.put("returnMsg", returnMsg);68 }69 return null;70 }
微信支付回调地址:
1 @RequestMapping(value = "/success") 2 public String wxpaySucc(HttpServletRequest request){ 3 logger.info("微信支付回调"); 4 5 //商户处理后同步返回给微信参数: 6 String xmlResultSuccess = "<xml>" + "<return_code><![CDATA[SUCCESS]]></return_code>" + "<return_msg><![CDATA[OK]]></return_msg>" + "</xml> "; 7 String xmlResultFailure = "<xml>" + "<return_code><![CDATA[FAIL]]></return_code>" + "<return_msg><![CDATA[报文为空]]></return_msg>" + "</xml> "; 8 9 SortedMap<String, Object> params = null;10 try {11 InputStream inStream = request.getInputStream();12 ByteArrayOutputStream outSteam = new ByteArrayOutputStream();13 byte[] buffer = new byte[1024];14 int len = 0;15 while ((len = inStream.read(buffer)) != -1) {16 outSteam.write(buffer, 0, len);17 }18 String resultxml = new String(outSteam.toByteArray(), "utf-8");19 params = PayCommonUtil.doXMLParse(resultxml);20 logger.info("微信回调的信息:解析XML得到key:value");21 for (Object b : params.keySet()) {22 logger.info("key="+b+":"+"value="+params.get(b));23 }24 outSteam.close();25 inStream.close();26 } catch (Exception e) {27 e.printStackTrace();28 logger.info("回调xml解析失败");29 }30 //验证签名31 boolean signVerified = PayCommonUtil.checkWXSign(params);32 logger.info("验证签名:"+signVerified);33 if (!signVerified) {34 // 支付失败,失败业务逻辑处理35 //将订单状态设置为待支付36 37 return xmlResultFailure;38 } else {39 String return_code = (String) params.get("return_code");//状态40 String out_trade_no = (String) params.get("out_trade_no");//订单号41 String ORDERTYPE = (String) params.get("attach");//商家数据包42 System.out.println("订单号out_trade_no:"+out_trade_no);43 44 //防止微信重复通知45 PageData pd = new PageData();46 pd.put("ORDERNUMBER", out_trade_no);47 try {48 PageData payInfo = alipayInService.getPayInfo(pd);49 if(payInfo != null){50 return xmlResultSuccess;51 }52 } catch (Exception e1) {53 e1.printStackTrace();54 }55 56 if (return_code.equals("SUCCESS")) {57 if (out_trade_no != null) {58 //付款成功业务逻辑处理59 return xmlResultSuccess;60 default:61 break;62 }63 } catch (Exception e) {64 e.printStackTrace();65 }66 }67 }else{68 logger.info("微信手机支付回调失败订单号:{}"+out_trade_no);69 return xmlResultFailure;70 }71 }72 return null;73 }74