一、微信支付太坑爹,废话不说了,下面是我的服务端微信支付开发过程和代码记录

二、首先去微信申请账户,这里有两个平台 

1、微信公众平台
2、微信开放平台(https://open.weixin.qq.com)这里选择第二个

三、账户开通、开发者认证之后就可以进行微信支付开发了

1、微信统一下单接口调用获取预支付id


1. /**
2.      * 获取微信支付所需信息(统一下单接口调用)
3.      * @param backURL
4.      * @param mDealerOrderEntity
5.      * @param mCarInfoEntity
6.      * @return
7.      * @throws JSONException 
8.      * @throws IOException 
9.      * @throws JDOMException 
10.      */
11. public Map<String, String> getWechatOrderInfo(String notifyUrl, MDealerOrderEntity mDealerOrderEntity, String body, HttpServletRequest request, HttpServletResponse response) throws
12. new
13. //生成payPreId
14. int)(mDealerOrderEntity.getMoney()*100)));  
15. "prepay_id");  
16. if(StringUtils.isNotEmpty(prePayId)) {  
17. //生成调用微信APP参数
18.             resultMap =  WechatUtil.genPayReq(prePayId);  
19.         }  
20. return
21.     }


此方法返回的数据如下


{
        "appid": "123132131",
        "noncestr": "416e5cf0acb7e553a880b7647903da6e",
        "packageValue ": "Sign=WXPay",
        "partnerid ": "1276000000",
        "prepayid ": "wx2015101611341514a3cbbbf90572184370",
        "timestamp ": "1444966497",
        "sign": "1DD72B07607B0B41D2827954150D89E9" 
    }


2、服务器端接受微信支付结果通知



1. /**
2.      * 处理微信支付通知
3.      * @param request
4.      * @return
5.      * @throws Exception
6.      */
7. public String saveWechatNotify(HttpServletRequest request, HttpServletResponse response) throws
8.         Map<String, String> noticeMap = XMLUtil.parseXml(request);  
9. "transaction_id");  
10. this.findEntityByProperty(MWechatInfoEntity.class, "transactionId", transactionId);  
11. //如果wechatInfoEntity存在,说明请求已经处理过,直接返回
12. if(wechatInfoEntity != null) {  
13. return "SUCCESS";  
14.         }  
15. "sign");  
16. "sign");  
17. // 验签通过
18. if
19. // 通信成功此字段是通信标识,非交易标识,交易是否成功需要查看result_code来判断
20. if ("SUCCESS".equals(noticeMap.get("return_code"))) {  
21. // 交易成功
22. if ("SUCCESS".equals(noticeMap.get("result_code"))) {  
23. // 商户订单号
24. "out_trade_no");  
25. this.findEntityByProperty(MDealerOrderEntity.class, "goodorderno", goodorderno);  
26. this.get(MCarInfoEntity.class, mDealerOrderEntity.getCarid());  
27. // 订单更新时间
28. new
29. // ------------------------------
30. // 处理业务开始
31. // ------------------------------
32. // 这里写自己业务相关
33. // ------------------------------
34. // 处理业务完毕
35. // ------------------------------
36. "sign", sign);  
37. this.common99Service.saveWechatInfo(noticeMap, mDealerOrderEntity.getId());  
38. else
39. // 错误时,返回结果未签名,记录retcode、retmsg看失败详情。
40. "查询验证签名失败或业务错误");  
41. "retcode:" + noticeMap.get("retcode") + " retmsg:" + noticeMap.get("retmsg"));  
42.                 }  
43. return "SUCCESS";  
44. else
45. "后台调用通信失败");  
46.             }  
47. return "SUCCESS";  
48. else
49. "通知签名验证失败");  
50.         }  
51. return null;  
52.     }


3、上面代码用到的工具方法都在WechatUtil.java工具类中

1. package
2.   
3. import
4. import
5. import
6. import
7. import
8. import
9. import
10. import
11. import
12.   
13. import
14. import
15. import
16. import
17. import
18. import
19. import
20. import
21. import
22. import
23. import
24. import
25. import
26. import
27.   
28. public class
29.   
30. private static Logger logger = LoggerFactory.getLogger(WechatUtil.class);  
31. public static final String TAG = "Wechat.Util";  
32. private static final int timeout = 5000;  
33.   
34. public static byte[] httpPost(String url, String entity) throws
35. if (url == null || url.length() == 0) {  
36. "httpPost, url is null");  
37. return null;  
38.         }  
39.         CloseableHttpClient httpClient = HttpClients.createDefault();  
40. new
41. new
42.         RequestConfig requestConfig = RequestConfig.custom().setSocketTimeout(timeout).setConnectionRequestTimeout(timeout).setConnectTimeout(timeout).build();  
43.         httpPost.setConfig(requestConfig);  
44. // 避免汉字乱码导致请求失败,
45. new StringEntity(entity, "UTF-8"));  
46. null;  
47. try
48.             resp = httpClient.execute(httpPost);  
49. if
50. "httpGet fail, status code = "
51. return null;  
52.             }  
53. return
54. catch
55. "httpPost exception, e = "
56.             e.printStackTrace();  
57. return null;  
58. finally
59. if (httpClient != null) {  
60.                 httpClient.close();  
61.             }  
62. if (resp != null) {  
63.                 resp.close();  
64.             }  
65.         }  
66.     }  
67.   
68. /**
69.      * 把数组所有元素排序,并按照“参数=参数值”的模式用“&”字符拼接成字符串
70.      * 
71.      * @param params
72.      *            需要排序并参与字符拼接的参数组
73.      * @return 拼接后字符串
74.      */
75. public static
76.   
77. new
78.         Collections.sort(keys);  
79.   
80. "";  
81.   
82. for (int i = 0; i < keys.size(); i++) {  
83.             String key = keys.get(i);  
84.             String value = params.get(key);  
85.   
86. if (i == keys.size() - 1) {// 拼接时,不包括最后一个&字符
87. "="
88. else
89. "=" + value + "&";  
90.             }  
91.         }  
92.   
93. return
94.     }  
95.   
96. /**
97.      * 根据反馈回来的信息,生成签名结果
98.      * 
99.      * @param Params
100.      *            通知返回来的参数数组
101.      * @param sign
102.      *            比对的签名结果
103.      * @return 生成的签名结果
104.      */
105. public static boolean
106. // 过滤空值、sign与sign_type参数
107. // Map<String, String> sParaNew = AlipayCore.paraFilter(Params);
108. // 获取待签名字符串
109.         String preSignStr = createLinkString(Params);  
110. "&key="
111. // 获得签名验证结果
112.         String resultSign = MD5.getMessageDigest(preSignStr.getBytes()).toUpperCase();  
113. // String resultSign = MD5Util.MD5Encode(preSignStr.toString(), "UTF-8").toLowerCase();
114. if
115. return true;  
116. else
117. return false;  
118.         }  
119.     }  
120.   
121. /**
122.      * 装配xml,生成请求prePayId所需参数
123.      * 
124.      * @param params
125.      * @return
126.      */
127. public static
128. new
129. "<xml>");  
130. for (int i = 0; i < params.size(); i++) {  
131. "<" + params.get(i).getName() + ">");  
132.             sb.append(params.get(i).getValue());  
133. "</" + params.get(i).getName() + ">");  
134.         }  
135. "</xml>");  
136. return
137.     }  
138.   
139. /**
140.      * 生成签名
141.      */
142. public static
143. new
144. for (int i = 0; i < params.size(); i++) {  
145.             sb.append(params.get(i).getName());  
146. '=');  
147.             sb.append(params.get(i).getValue());  
148. '&');  
149.         }  
150. "key=");  
151.         sb.append(ConstantUtil.API_KEY);  
152.         String packageSign = MD5.getMessageDigest(sb.toString().getBytes()).toUpperCase();  
153. return
154.     }  
155.   
156. /**
157.      * 
158.      * @param goodOrderNo
159.      * @param body
160.      * @param noticeUrl
161.      * @param ip
162.      * @param totalFee
163.      * @return
164.      */
165. public static
166. new
167. try
168.             String nonceStr = getNonceStr();  
169. "</xml>");  
170. new
171. new BasicNameValuePair("appid", ConstantUtil.APP_ID));  
172. new BasicNameValuePair("body", body));  
173. new BasicNameValuePair("mch_id", ConstantUtil.MCH_ID));  
174. new BasicNameValuePair("nonce_str", nonceStr));  
175. new BasicNameValuePair("notify_url", noticeUrl));  
176. new BasicNameValuePair("out_trade_no", goodOrderNo));  
177. new BasicNameValuePair("spbill_create_ip", ip));  
178. new BasicNameValuePair("total_fee", totalFee));  
179. new BasicNameValuePair("trade_type", "APP"));  
180.             String sign = genPackageSign(packageParams);  
181. new BasicNameValuePair("sign", sign));  
182.             String xmlstring = toXml(packageParams);  
183. return
184. catch
185. "genProductArgs fail, ex = "
186. return null;  
187.         }  
188.     }  
189.   
190. /**
191.      * 生成app支付签名
192.      * 
193.      * @param params
194.      * @return
195.      */
196. public static
197. new
198. for (int i = 0; i < params.size(); i++) {  
199.             sb.append(params.get(i).getName());  
200. '=');  
201.             sb.append(params.get(i).getValue());  
202. '&');  
203.         }  
204. "key=");  
205.         sb.append(ConstantUtil.API_KEY);  
206.         String appSign = MD5.getMessageDigest(sb.toString().getBytes()).toUpperCase();  
207. "orion", appSign);  
208. return
209.     }  
210.   
211. /**
212.      * 生成调用微信app支付所需参数
213.      * 
214.      * @param prepayId
215.      * @return
216.      */
217. public static
218. new
219.         String timeStamp = getTimeStamp();  
220.         String nonceStr = getNonceStr();  
221. new
222. new BasicNameValuePair("appid", ConstantUtil.APP_ID));  
223. new BasicNameValuePair("noncestr", nonceStr));  
224. new BasicNameValuePair("package", "Sign=WXPay"));  
225. new BasicNameValuePair("partnerid", ConstantUtil.MCH_ID));  
226. new BasicNameValuePair("prepayid", prepayId));  
227. new BasicNameValuePair("timestamp", timeStamp));  
228.         String sign = genAppSign(signParams);  
229. "appid", ConstantUtil.APP_ID);  
230. "noncestr", nonceStr);  
231. "packageValue", "Sign=WXPay");  
232. "partnerid", ConstantUtil.MCH_ID);  
233. "prepayid", prepayId);  
234. "timestamp", timeStamp);  
235. "sign", sign);  
236. return
237.     }  
238.   
239. /**
240.      * 微信支付生成预支付订单
241.      * 
242.      * @throws IOException
243.      * @throws JDOMException
244.      */
245. public static Map<String, String> getPayPreId(String goodOrderNo, String body, String noticeUrl, String ip, String totalFee) throws
246.         String paramsXml = genProductArgs(goodOrderNo, body, noticeUrl, ip, totalFee);  
247. "orion", paramsXml);  
248. byte[] buf = WechatUtil.httpPost(ConstantUtil.URL, paramsXml);  
249. new
250.         Map<String, String> resultMap = XMLUtil.doXMLParse(contentXml);  
251. return
252.     }  
253.   
254. public static
255. new
256. return MD5.getMessageDigest(String.valueOf(random.nextInt(10000)).getBytes());  
257.     }  
258.   
259. public static
260. return String.valueOf(System.currentTimeMillis() / 1000);  
261.     }  
262. }


4、下面是用到的配置类

1. package
2.   
3. public class
4. /**
5.      * 商家可以考虑读取配置文件
6.      */
7.       
8. //初始化
9. public static String APP_ID = "wxsdfsdfsf5fdbc";//微信开发平台应用id
10. public static String APP_SECRET = "aab95csdfsdfsffdcsdfsfs0df34";//应用对应的凭证
11. //商户号
12. public static String MCH_ID = "1233312201";  
13. public static String PARTNER = "1233312201";//财付通商户号
14. public static String API_KEY = "KgjyjirmjajdfjsdjfsjffVpT6RMbrB";  
15. public static String PARTNER_KEY = "KgjyjirmjajdfjsdjfsjffVpT6RMbrB";//商户号对应的密钥
16. public static String URL = "https://api.mch.weixin.qq.com/pay/unifiedorder";//获取预支付id的接口url  
17. }


5、xml 解析工具类




    1. package
    2.   
    3. public class
    4. /**
    5.      * 商家可以考虑读取配置文件
    6.      */
    7.       
    8. //初始化
    9. public static String APP_ID = "wxsdfsdfsf5fdbc";//微信开发平台应用id
    10. public static String APP_SECRET = "aab95csdfsdfsffdcsdfsfs0df34";//应用对应的凭证
    11. //商户号
    12. public static String MCH_ID = "1233312201";  
    13. public static String PARTNER = "1233312201";//财付通商户号
    14. public static String API_KEY = "KgjyjirmjajdfjsdjfsjffVpT6RMbrB";  
    15. public static String PARTNER_KEY = "KgjyjirmjajdfjsdjfsjffVpT6RMbrB";//商户号对应的密钥
    16. public static String URL = "https://api.mch.weixin.qq.com/pay/unifiedorder";//获取预支付id的接口url  
    17. }



    5、xml 解析工具类




    1. package
    2.   
    3. import
    4. import
    5. import
    6. import
    7. import
    8. import
    9. import
    10.   
    11. import
    12.   
    13. import
    14. import
    15. import
    16. import
    17. import
    18.   
    19. /**
    20.  * xml工具类
    21.  * 
    22.  * @author miklchen
    23.  *
    24.  */
    25. public class
    26.   
    27. /**
    28.      * 解析xml,返回第一级元素键值对。如果第一级元素有子节点,则此节点的值是子节点的xml数据。
    29.      * 
    30.      * @param strxml
    31.      * @return
    32.      * @throws JDOMException
    33.      * @throws IOException
    34.      */
    35. public static Map doXMLParse(String strxml) throws
    36. "encoding=\".*\"", "encoding=\"UTF-8\"");  
    37.   
    38. if (null == strxml || "".equals(strxml)) {  
    39. return null;  
    40.         }  
    41.   
    42. new
    43.   
    44. new ByteArrayInputStream(strxml.getBytes("UTF-8"));  
    45. new
    46.         Document doc = builder.build(in);  
    47.         Element root = doc.getRootElement();  
    48.         List list = root.getChildren();  
    49.         Iterator it = list.iterator();  
    50. while
    51.             Element e = (Element) it.next();  
    52.             String k = e.getName();  
    53. "";  
    54.             List children = e.getChildren();  
    55. if
    56.                 v = e.getTextNormalize();  
    57. else
    58.                 v = XMLUtil.getChildrenText(children);  
    59.             }  
    60.   
    61.             m.put(k, v);  
    62.         }  
    63.   
    64. // 关闭流
    65.         in.close();  
    66.   
    67. return
    68.     }  
    69.   
    70. /**
    71.      * 获取子结点的xml
    72.      * 
    73.      * @param children
    74.      * @return String
    75.      */
    76. public static
    77. new
    78. if
    79.             Iterator it = children.iterator();  
    80. while
    81.                 Element e = (Element) it.next();  
    82.                 String name = e.getName();  
    83.                 String value = e.getTextNormalize();  
    84.                 List list = e.getChildren();  
    85. "<" + name + ">");  
    86. if
    87.                     sb.append(XMLUtil.getChildrenText(list));  
    88.                 }  
    89.                 sb.append(value);  
    90. "</" + name + ">");  
    91.             }  
    92.         }  
    93.   
    94. return
    95.     }  
    96.   
    97. /**
    98.      * 将requestxml通知结果转出啊成map
    99.      * @param request
    100.      * @return
    101.      * @throws Exception
    102.      */
    103. public static Map<String, String> parseXml(HttpServletRequest request) throws
    104. // 解析结果存储在HashMap
    105. new
    106.         InputStream inputStream = request.getInputStream();  
    107. // 读取输入流
    108. new
    109.         org.dom4j.Document document = reader.read(inputStream);  
    110. // 得到xml根元素
    111.         org.dom4j.Element root = document.getRootElement();  
    112. // 得到根元素的所有子节点
    113.         List<org.dom4j.Element> elementList = root.elements();  
    114. // 遍历所有子节点
    115. for
    116.             map.put(e.getName(), e.getText());  
    117. // 释放资源
    118.         inputStream.close();  
    119. null;  
    120. return
    121.     }  
    122.   
    123. }