微信APP支付-java后台实现
Posted 你猜怎么着
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了微信APP支付-java后台实现相关的知识,希望对你有一定的参考价值。
不说废话,直接上代码
先是工具类(注意签名时要排序):
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 certs 201 SSLContext sslcontext = SSLContexts.custom().loadKeyMaterial(keyStore, "1313329201".toCharArray()).build(); 202 // Allow TLSv1 protocol only 203 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 * @Description 314 * @Author zhaozhenhua 315 * @date 2018年5月10日 316 * @param pd 317 * @return 318 * @throws Exception 319 */ 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对应的value 41 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 = XXXXX 11 #微信回调地址(自己定义) 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 else 28 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
以上是关于微信APP支付-java后台实现的主要内容,如果未能解决你的问题,请参考以下文章