微信APP支付-java后台实现

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

微信支付回调地址:
1@RequestMapping(value = "https://www.songbingjia.com/success") 2public String wxpaySucc(HttpServletRequest request){ 3logger.info("微信支付回调"); 4 5//商户处理后同步返回给微信参数: 6String xmlResultSuccess = "< xml> " + "< return_code> < ![CDATA[SUCCESS]]> < /return_code> " + "< return_msg> < ![CDATA[OK]]> < /return_msg> " + "< /xml> "; 7String xmlResultFailure = "< xml> " + "< return_code> < ![CDATA[FAIL]]> < /return_code> " + "< return_msg> < ![CDATA[报文为空]]> < /return_msg> " + "< /xml> "; 8 9SortedMap< String, Object> params = null; 10try { 11InputStream inStream = request.getInputStream(); 12ByteArrayOutputStream outSteam = new ByteArrayOutputStream(); 13byte[] buffer = new byte[1024]; 14int len = 0; 15while ((len = inStream.read(buffer)) != -1) { 16outSteam.write(buffer, 0, len); 17} 18String resultxml = new String(outSteam.toByteArray(), "utf-8"); 19params =PayCommonUtil.doXMLParse(resultxml); 20logger.info("微信回调的信息:解析XML得到key:value"); 21for (Object b : params.keySet()) { 22logger.info("key="+b+":"+"value="https://www.songbingjia.com/android/+params.get(b)); 23} 24outSteam.close(); 25inStream.close(); 26} catch (Exception e) { 27e.printStackTrace(); 28logger.info("回调xml解析失败"); 29} 30//验证签名 31boolean signVerified = PayCommonUtil.checkWXSign(params); 32logger.info("验证签名:"+signVerified); 33if (!signVerified) { 34// 支付失败,失败业务逻辑处理 35//将订单状态设置为待支付 36 37return xmlResultFailure; 38} else { 39String return_code = (String) params.get("return_code"); //状态 40String out_trade_no = (String) params.get("out_trade_no"); //订单号 41String ORDERTYPE = (String) params.get("attach"); //商家数据包 42System.out.println("订单号out_trade_no:"+out_trade_no); 43 44//防止微信重复通知 45PageData pd = new PageData(); 46pd.put("ORDERNUMBER", out_trade_no); 47try { 48PageData payInfo = alipayInService.getPayInfo(pd); 49if(payInfo != null){ 50return xmlResultSuccess; 51} 52} catch (Exception e1) { 53e1.printStackTrace(); 54} 55 56if (return_code.equals("SUCCESS")) { 57if (out_trade_no != null) { 58//付款成功业务逻辑处理 59return xmlResultSuccess; 60default: 61break; 62} 63} catch (Exception e) { 64e.printStackTrace(); 65} 66} 67}else{ 68logger.info("微信手机支付回调失败订单号:{}"+out_trade_no); 69return xmlResultFailure; 70} 71} 72return null; 73} 74

【微信APP支付-java后台实现】 

    推荐阅读