微信APP支付-java後臺實現

不說廢話,直接上代碼前端

先是工具類(注意簽名時要排序):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 }

配置文件信息讀取:git

 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加密方法:apache

 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 }

 

微信支付統一下單接口封裝的方法:api

 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
相關文章
相關標籤/搜索