微信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后台实现的主要内容,如果未能解决你的问题,请参考以下文章

微信APP支付-Java后台实现

.Net后台实现微信APP支付

微信app支付(android端+java后台)

APP支付微信支付,Java后台开发

微信APP支付(Java后台生成签名具体步骤)

.Net后台实现支付宝APP支付