项目接入微信支付的准备工作:
- 注册成为开发者,进行资质认证,这里是需要300元的审核费用的;
- 在微信商户平台创建应用,提交等待审核(大致需要5-7个工作日);
- 应用审核通过之后,进入应用,开通微信支付,提交审核(大致需要2-3个工作日);
- 审核通过之后,微信会给注册的邮箱发送商户号,用户和密码等信息。
接入App支付的业务流程主要如下图所示:
接下来,就描述一下接入项目的详细步骤:
- 基础信息配置文件:WeiChartConfig.java
- 调用API接口的工具类:WeiChartUtil.java
- 对外接口:WeiChartController.java
WeiChartConfig.java
此文件主要配置接入微信支付需要的基础信息,例如:商户号、AppId、密钥、异步通知地址等信息,以及微信支付Api地址等信息。
1 import com.erenju.util.GlobleConfig; 2 3 /** 4 * 微信支付基础信息配置 5 * 沙箱测试地址前边添加:sandboxnew 例如:https://api.mch.weixin.qq.com/sandboxnew/pay/orderquery 6 * @author rxn 7 * @date 2018/04/23 8 * 9 */ 10 public class WeiChartConfig { 11 12 /** 13 * 预支付请求接口(统一下单) 14 */ 15 public static final String PrePayUrl = "https://api.mch.weixin.qq.com/pay/unifiedorder"; 16 // public static final String PrePayUrl = "https://api.mch.weixin.qq.com/sandboxnew/pay/unifiedorder"; 17 18 /** 19 * 异步通知回调地址 ,外网可访问 20 */ 21 public static final String notify_url = GlobleConfig.getProperty("PayNotifyUrl")+"/wxpay/notify.json"; 22 23 /** 24 * 查询订单地址 25 */ 26 public static final String OrderUrl = "https://api.mch.weixin.qq.com/pay/orderquery"; 27 // public static final String OrderUrl = "https://api.mch.weixin.qq.com/sandboxnew/pay/orderquery"; 28 29 /** 30 * 关闭订单地址 31 */ 32 public static final String CloseOrderUrl = "https://api.mch.weixin.qq.com/pay/closeorder"; 33 // public static final String CloseOrderUrl = "https://api.mch.weixin.qq.com/sandboxnew/pay/closeorder"; 34 35 /** 36 * 申请退款地址 37 */ 38 public static final String RefundUrl = "https://api.mch.weixin.qq.com/secapi/pay/refund"; 39 // public static final String RefundUrl = "https://api.mch.weixin.qq.com/sandboxnew/secapi/pay/refund"; 40 41 /** 42 * 查询退款地址 43 */ 44 public static final String RefundQueryUrl = "https://api.mch.weixin.qq.com/pay/refundquery"; 45 // public static final String RefundQueryUrl = "https://api.mch.weixin.qq.com/sandboxnew/pay/refundquery"; 46 47 /** 48 * 商户AppId 49 */ 50 public static final String AppId = "wx****************"; 51 52 /** 53 * 商户号,10位数字 54 */ 55 public static final String MchId = "**********"; 56 57 /** 58 * 商户密钥 (32位),正式环境和沙箱环境的商户密钥是不同的 59 */ 60 public static final String AppSercret = "*****";//正式 61 /** 62 * Api Key(32位) 63 */ 64 public static final String ApiKey = "*****"; 65 66 /** 67 * 商品描述 68 */ 69 public static final String body = "****"; 70 71 /** 72 * 退款需要证书文件,证书文件的地址 73 */ 74 public static final String refund_file_path = ""; 75 }
WeiChartUtil.java
将业务逻辑和调用Api接口的逻辑分开写了,这样业务逻辑没有那么复杂,还增加了代码的复用率。
主要涉及的API如下图所示:(详细的API,请参考微信支付开放平台https://pay.weixin.qq.com/wiki/doc/api/app/app.php?chapter=9_1)
1 package com.xhgx.web.pay.weiChart; 2 3 import java.net.InetAddress; 4 import java.net.UnknownHostException; 5 import java.text.SimpleDateFormat; 6 import java.util.Arrays; 7 import java.util.Calendar; 8 import java.util.HashMap; 9 import java.util.Iterator; 10 import java.util.Map; 11 import java.util.Random; 12 import java.util.Set; 13 14 import org.dom4j.Document; 15 import org.dom4j.DocumentException; 16 import org.dom4j.DocumentHelper; 17 import org.dom4j.Element; 18 19 20 import com.xhgx.util.HttpClientUtil; 21 import com.xhgx.util.StringUtil; 22 23 import common.Logger; 24 25 /** 26 * 微信支付调用API接口 27 * @author rxn 28 * @date 2018/04/23 29 * 30 */ 31 public class WeiChartUtil { 32 33 public static SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmss"); 34 35 public static Logger log = Logger.getLogger(WeiChartUtil.class); 36 37 public static void main(String[] args) { 38 39 Map map = new HashMap<>(); 40 map.put("mch_id", WeiChartConfig.MchId); 41 map.put("nonce_str", getRandomString()); 42 String sign = getSign(map); 43 map.put("sign", sign); 44 System.out.println(map.get("mch_id")); 45 System.out.println(map.get("nonce_str")); 46 System.out.println(sign); 47 48 String string = HttpClientUtil.sendHttpPost("http://api.mch.weixin.qq.com/sandboxnew/pay/getsignkey", map); 49 System.out.println(string); 50 } 51 52 /** 53 * 请求获取预支付订单 54 * @param orderId 订单id 55 * @param totalFee 总金额(单位:分) 56 * @return 57 */ 58 public static Map<String, Object> getPrepayOrderInfo(String orderId,String totalFee){ 59 Map<String, Object> m = new HashMap<String, Object>(); 60 try { 61 for(int i=0;i<1;i++){ 62 if(StringUtil.isEmpty(orderId)){ 63 m.put("code", -1); 64 m.put("message", "订单号不能为空"); 65 break; 66 } 67 if(Double.parseDouble(totalFee)<=0){ 68 m.put("code", -1); 69 m.put("message", "订单金额有误"); 70 break; 71 } 72 73 Map<String,String> resultMap = getPreyId(orderId, totalFee, "义龙出行"); 74 75 System.out.println("resultMap:"+resultMap); 76 77 String return_code = resultMap.get("return_code");//返回状态码 78 String return_msg = resultMap.get("return_msg");//返回信息 79 //签名失败 80 /*if(!StringUtil.isEmpty(return_msg)){ 81 m.put("code", -1); 82 m.put("message", return_msg); 83 break; 84 }*/ 85 86 if("SUCCESS".equals(return_code)){ 87 // String appid = resultMap.get("appid");//应用APPID 88 String mch_id = resultMap.get("mch_id");//商户号 89 // String device_info = resultMap.get("device_info");//设备号 90 String nonce_str = resultMap.get("nonce_str");//随机字符串 91 String sign = resultMap.get("sign");//签名 92 String result_code = resultMap.get("result_code");//业务结果 93 94 if("SUCCESS".equals(result_code)){ 95 String trade_type = resultMap.get("trade_type");//交易类型:APP 96 String prepay_id = resultMap.get("prepay_id");//预支付交易会话标识,有效期为两个小时 97 98 //重新生成sign 99 Map<String,String> signMap = new HashMap<String,String>(); 100 String timeStamp = getTenTimes(); 101 // signMap.put("appid", appid); 102 signMap.put("appid", WeiChartConfig.AppId); 103 signMap.put("partnerid", mch_id); 104 signMap.put("prepayid", prepay_id); 105 signMap.put("package", "Sign=WXPay"); 106 signMap.put("noncestr", nonce_str); 107 signMap.put("timestamp", timeStamp);//时间戳:10位 108 109 String newSign = getSign(signMap); 110 // System.out.println("String:"+newSign); 111 System.out.println(creatXml(signMap)); 112 m.put("sign", newSign); 113 // m.put("sign", sign); 114 m.put("appid", WeiChartConfig.AppId); 115 m.put("mch_id", mch_id); 116 m.put("nonce_str", nonce_str); 117 m.put("trade_type", trade_type); 118 m.put("prepay_id", prepay_id); 119 m.put("package", "WXPay"); 120 m.put("timestamp", timeStamp); 121 122 m.put("code", 0); 123 m.put("message", "请求成功"); 124 break; 125 }else{ 126 String err_code = resultMap.get("err_code");//错误代码 127 String err_code_des = resultMap.get("err_code_des");//错误代码描述 128 129 m.put("code", -1); 130 m.put("err_code", err_code); 131 m.put("message", err_code_des); 132 break; 133 } 134 135 }else{ 136 m.put("code", -1); 137 m.put("message", "请求失败"); 138 break; 139 } 140 141 } 142 } catch (Exception e) { 143 e.printStackTrace(); 144 m.put("code", -1); 145 m.put("message", "程序异常"); 146 } 147 return m; 148 } 149 150 /** 151 * 统一下单 152 * @param orderId 订单id 153 * @param totalFee 总金额(分) 154 * @param schoolLabel 155 * @return 156 */ 157 public static Map<String, String> getPreyId(String orderId,String totalFee,String schoolLabel){ 158 Map<String,String> m = new HashMap<String,String>(); 159 m.put("appid", WeiChartConfig.AppId);//应用id 160 m.put("body", "【"+schoolLabel+"】"+WeiChartConfig.body);//商品描述 161 m.put("mch_id", WeiChartConfig.MchId);//商户号 162 m.put("nonce_str", getRandomString());//随机字符串 163 m.put("notify_url", WeiChartConfig.notify_url);//通知地址 164 m.put("out_trade_no", orderId);//订单号 165 m.put("spbill_create_ip", getHostIp());//用户端实际ip 166 Calendar cal = Calendar.getInstance(); 167 cal.add(Calendar.HOUR_OF_DAY, 2);//有效时间 168 m.put("time_expire", sdf.format(cal.getTime()));//交易结束时间,非必填 169 Calendar cal1 = Calendar.getInstance(); 170 m.put("time_start", sdf.format(cal1.getTime()));//交易起始时间,非必填 171 // m.put("total_fee", totalFee);//总金额 172 m.put("total_fee", 1+"");//总金额 173 m.put("trade_type", "APP");//交易类型 174 m.put("sign", getSign(m));//签名 175 176 String reqStr = creatXml(m); 177 String retStr = HttpClientUtil.postHtpps(WeiChartConfig.PrePayUrl, reqStr); 178 179 System.out.println("微信调起统一下单接口返回结果:"+retStr); 180 return getInfoByXml(retStr); 181 } 182 183 /** 184 * 查询订单 185 * @param orderId 186 * @return 187 */ 188 public static Map<String, String> getOrder(String orderId){ 189 Map<String, String> m = new HashMap<String,String>(); 190 m.put("appid", WeiChartConfig.AppId); 191 m.put("mch_id", WeiChartConfig.MchId); 192 m.put("nonce_str", getRandomString()); 193 m.put("out_trade_no", orderId);//商户订单号 194 m.put("sign", getSign(m)); 195 196 String reqStr = creatXml(m); 197 String retStr = HttpClientUtil.postHtpps(WeiChartConfig.OrderUrl, reqStr); 198 return getInfoByXml(retStr); 199 } 200 201 /** 202 * 关闭订单 203 * @param orderId 204 * @return 205 */ 206 public static Map<String, String> closeOrder(String orderId){ 207 Map<String, String> m = new HashMap<String,String>(); 208 m.put("appid", WeiChartConfig.AppId); 209 m.put("mch_id", WeiChartConfig.MchId); 210 m.put("out_trade_no", orderId);//商户订单号 211 m.put("nonce_str", getRandomString()); 212 m.put("sign", getSign(m)); 213 214 String reqStr = creatXml(m); 215 String retStr = HttpClientUtil.postHtpps(WeiChartConfig.CloseOrderUrl, reqStr); 216 return getInfoByXml(retStr); 217 } 218 219 /** 220 * 退款 221 * @param orderId 订单号 222 * @param refundId 退款单号 223 * @param totalFee 订单金额(分) 224 * @param refundFee 退款金额(分) 225 * @return 226 */ 227 public static Map<String, String> refund(String orderId,String refundId,String totalFee,String refundFee){ 228 Map<String, String> m = new HashMap<String,String>(); 229 m.put("appid", WeiChartConfig.AppId); 230 m.put("mch_id", WeiChartConfig.MchId); 231 m.put("nonce_str", getRandomString()); 232 m.put("out_trade_no", orderId);//商户订单号 233 m.put("out_refund_no", refundId);//退款单号 234 m.put("total_fee", totalFee);//订单金额 235 m.put("refund_fee", refundFee);//退款金额 236 m.put("sign", getSign(m)); 237 238 String reqStr = creatXml(m); 239 String retStr = ""; 240 try { 241 retStr = HttpClientUtil.postHttplientNeedSSL(WeiChartConfig.RefundUrl, reqStr, WeiChartConfig.refund_file_path, WeiChartConfig.MchId); 242 } catch (Exception e) { 243 // TODO Auto-generated catch block 244 e.printStackTrace(); 245 return null; 246 } 247 return getInfoByXml(retStr); 248 } 249 /** 250 * 查询退款 251 * @param orderId 252 * @return 253 */ 254 public static Map<String, String> getOrderRefundQueryInfo(String orderId){ 255 Map<String,String> m = new HashMap<String,String>(); 256 m.put("appid", WeiChartConfig.AppId); 257 m.put("mch_id", WeiChartConfig.MchId); 258 m.put("nonce_str", getRandomString()); 259 m.put("out_trade_no", orderId);//订单号 260 m.put("sign", getSign(m)); 261 262 String reqStr = creatXml(m); 263 String retStr = HttpClientUtil.postHtpps(WeiChartConfig.RefundQueryUrl, reqStr); 264 return getInfoByXml(retStr); 265 } 266 267 /** 268 * 得到随机字符串 269 * 270 * @param length 271 * @return 272 */ 273 public static String getRandomString() { 274 int length = 32; 275 String str = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; 276 Random random = new Random(); 277 StringBuffer sb = new StringBuffer(); 278 279 for (int i = 0; i < length; ++i) { 280 int number = random.nextInt(62);// [0,62) 281 sb.append(str.charAt(number)); 282 } 283 return sb.toString(); 284 } 285 286 /** 287 * 得到本地机器的IP 288 * 289 * @return 290 */ 291 private static String getHostIp() { 292 String ip = ""; 293 try { 294 ip = InetAddress.getLocalHost().getHostAddress(); 295 } catch (UnknownHostException e) { 296 e.printStackTrace(); 297 } 298 return ip; 299 } 300 /** 301 * 生成为头为XML的xml字符串,例如:<xml><key>123</key></xml> 302 * @param reqMap 303 * @return 304 */ 305 public static String creatXml(Map<String, String> reqMap){ 306 Set<String> set = reqMap.keySet(); 307 StringBuffer b = new StringBuffer(); 308 b.append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>"); 309 b.append("<xml>"); 310 for(String key : set){ 311 b.append("<"+key+">").append(reqMap.get(key)).append("</"+key+">"); 312 } 313 b.append("</xml>"); 314 return b.toString(); 315 } 316 317 /** 318 * 生成签名 319 * 320 * @param map 321 * @return 322 */ 323 public static String getSign(Map<String, String> map) { 324 String[] keys = map.keySet().toArray(new String[0]); 325 Arrays.sort(keys); 326 StringBuffer reqStr = new StringBuffer(); 327 for (String key : keys) { 328 String v = map.get(key); 329 if (v != null && !v.equals("")) { 330 reqStr.append(key).append("=").append(v).append("&"); 331 } 332 } 333 reqStr.append("key").append("=").append(WeiChartConfig.ApiKey); 334 335 return getMd5ByOrder(reqStr.toString()).toUpperCase(); 336 } 337 338 /** 339 * 是否签名正确,规则是:按参数名称a-z排序,遇到空值的参数不参加签名。 340 * @return boolean 341 */ 342 public static boolean isTenpaySign(Map<String, String> map) { 343 344 String sign = map.get("sign");//获取微信返回的sign,进行比较 345 map.remove("sign");//生成签名时,去除sign 346 347 String[] keys = map.keySet().toArray(new String[0]); 348 Arrays.sort(keys); 349 StringBuffer reqStr = new StringBuffer(); 350 for (String key : keys) { 351 String v = map.get(key); 352 if (v != null && !v.equals("")) { 353 reqStr.append(key).append("=").append(v).append("&"); 354 } 355 } 356 reqStr.append("key").append("=").append(WeiChartConfig.ApiKey); 357 358 //算出摘要 359 String mysign = getMd5ByOrder(reqStr.toString()).toLowerCase(); 360 String tenpaySign = sign.toLowerCase(); 361 362 // System.out.println(tenpaySign + " " + mysign); 363 return tenpaySign.equals(mysign); 364 } 365 366 /** 367 * 解析xml 368 * 369 * @param xmlStr 370 * @return 371 */ 372 public static Map<String, String> getInfoByXml(String xmlStr) { 373 try { 374 Map<String, String> m = new HashMap<String, String>(); 375 Document d = DocumentHelper.parseText(xmlStr); 376 Element root = d.getRootElement(); 377 for (Iterator<?> i = root.elementIterator(); i.hasNext();) { 378 Element element = (Element) i.next(); 379 String name = element.getName(); 380 if (!element.isTextOnly()) { 381 // 不是字符串 跳过。确定了微信放回的xml只有根目录 382 continue; 383 } else { 384 m.put(name, element.getTextTrim()); 385 } 386 } 387 Map<String,String> map = new HashMap<String,String>(); 388 map.putAll(m); 389 // 对返回结果做校验.去除sign 字段再去加密 390 String retSign = m.get("sign"); 391 m.remove("sign"); 392 String rightSing = getSign(m); 393 if (rightSing.equals(retSign)) { 394 return map; 395 } 396 } catch (DocumentException e) { 397 // TODO Auto-generated catch block 398 e.printStackTrace(); 399 } 400 return null; 401 } 402 403 /** 404 * 获取10位的时间戳 405 * @return 406 */ 407 public static String getTenTimes(){ 408 String time = System.currentTimeMillis()+""; 409 return time.substring(0, time.length()-3); 410 } 411 /** 412 * 将文本转排序后,换成MD5加密后的字符串 413 * @param s 414 * @return 415 * @author Rxn 416 * @date 2018-05-08 417 */ 418 public static String getMd5ByOrder(String s){ 419 MessageDigest md; 420 StringBuilder sStringBuilder = new StringBuilder(); 421 try { 422 md = MessageDigest.getInstance("MD5"); 423 md.reset(); 424 try { 425 md.update(s.getBytes("utf-8")); 426 } catch (UnsupportedEncodingException e) { 427 e.printStackTrace(); 428 } 429 byte[] digest = md.digest(); 430 sStringBuilder.setLength(0); 431 432 for (int i = 0; i < digest.length; ++i) { 433 int b = digest[i] & 255; 434 if (b < 16) { 435 sStringBuilder.append('0'); 436 } 437 438 sStringBuilder.append(Integer.toHexString(b)); 439 } 440 441 return sStringBuilder.toString().toUpperCase(); 442 443 } catch (NoSuchAlgorithmException e) { 444 e.printStackTrace(); 445 } 446 447 return null; 448 } 449 } 450
WeiChartController.java
这里主要涉及一些业务处理,代码中我主要粘了对外的异步通知接口(主要作用是App支付成功后,会有一个支付成功的返回信息告知商户),其他需要的接口已经在WeiChartUtil.java中处理过了,直接调用即可。
1 import java.io.BufferedOutputStream; 2 import java.io.BufferedReader; 3 import java.io.IOException; 4 import java.io.InputStream; 5 import java.io.InputStreamReader; 6 import java.math.BigDecimal; 7 import java.text.ParseException; 8 import java.text.SimpleDateFormat; 9 import java.util.Date; 10 import java.util.HashMap; 11 import java.util.Iterator; 12 import java.util.Map; 13 import javax.servlet.http.HttpServletRequest; 14 import javax.servlet.http.HttpServletResponse; 15 import org.springframework.beans.factory.annotation.Autowired; 16 import org.springframework.stereotype.Controller; 17 import org.springframework.web.bind.annotation.RequestMapping; 18 import org.springframework.web.bind.annotation.ResponseBody; 19 import com.xhgx.util.StringUtil; 20 import com.xhgx.web.SpringContextUtil; 21 import common.Logger; 22 23 /** 24 * 25 * @author rxn 26 * @data 2018/05/03 27 * 28 */ 29 @Controller 30 public class WeiChartController{ 31 32 public static Logger log = Logger.getLogger(WeiChartController.class); 33 34 public static SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); 35 public static SimpleDateFormat ymdhms = new SimpleDateFormat("yyyyMMddHHmmss"); 36 37 /** 38 * 异步通知,回调方法 39 * @param request 40 * @param response 41 * @throws IOException 42 */ 43 @ResponseBody 44 @RequestMapping("/wxpay/notify.json") 45 public static void getNotifyInfo(HttpServletRequest request, HttpServletResponse response) throws IOException{ 46 System.out.println("微信APP异步通知,回调方法"); 47 //读取参数 48 InputStream inputStream ; 49 StringBuffer sb = new StringBuffer(); 50 inputStream = request.getInputStream(); 51 String s ; 52 BufferedReader in = new BufferedReader(new InputStreamReader(inputStream, "UTF-8")); 53 while ((s = in.readLine()) != null){ 54 sb.append(s); 55 } 56 in.close(); 57 inputStream.close(); 58 59 String reqStr = new String(sb); 60 //将xml解析成map 61 Map<String, String> reqMap = WeiChartUtil.getInfoByXml(reqStr); 62 63 //过滤空 64 Map<String,String> paramMap = new HashMap<String,String>(); 65 Iterator it = reqMap.keySet().iterator(); 66 while (it.hasNext()) { 67 String name = (String) it.next(); 68 String value = reqMap.get(name); 69 70 String v = ""; 71 if(value!=null){ 72 v = value.trim(); 73 } 74 paramMap.put(name, v); 75 } 76 77 String resXml="";//响应数据 78 //创建一个对象,保存通知信息,可以根据业务需求自己定义 79 PayNotifyTbl payNotifyTbl = new PayNotifyTbl(); 80 payNotifyTbl.setPay_type(1);//支付类型: 1微信 2支付宝 81 payNotifyTbl.setCreate_dt(new Date()); 82 //验证签名 83 if(WeiChartUtil.isTenpaySign(paramMap)){ 84 String return_code = paramMap.get("return_code");//返回状态码 85 String return_msg = paramMap.get("return_msg");//返回信息 86 String result_code = paramMap.get("result_code"); 87 String err_code = paramMap.get("err_code"); 88 String err_code_des = paramMap.get("err_code_des"); 89 90 payNotifyTbl.setReturn_code(return_code); 91 payNotifyTbl.setReturn_msg(return_msg); 92 payNotifyTbl.setResult_code(result_code); 93 payNotifyTbl.setErr_code(err_code); 94 payNotifyTbl.setErr_code_des(err_code_des); 95 96 if("SUCCESS".equals(return_code)&&"SUCCESS".equals(result_code)){ 97 String appid = paramMap.get("appid"); 98 String mch_id = paramMap.get("mch_id"); 99 String openid = paramMap.get("openid"); 100 String trade_type = paramMap.get("trade_type"); 101 String total_fee = paramMap.get("total_fee");//单位:分 102 String transaction_id = paramMap.get("transaction_id");//微信支付订单号 103 String out_trade_no = paramMap.get("out_trade_no");//商户订单号 104 String time_end = paramMap.get("time_end");//支付完成时间 105 106 payNotifyTbl.setAppid(appid); 107 payNotifyTbl.setOpenid(openid); 108 payNotifyTbl.setOrder_id(out_trade_no); 109 payNotifyTbl.setTrade_type(trade_type); 110 payNotifyTbl.setTotal_fee(Double.parseDouble(total_fee)/100.00); 111 payNotifyTbl.setTransaction_id(transaction_id); 112 try { 113 payNotifyTbl.setTime_end(sdf.format(ymdhms.parse(time_end))); 114 } catch (ParseException e) { 115 e.printStackTrace(); 116 } 117 118 119 //业务处理,查询订单 120 OrderTbl order = orderTblService.get(Integer.parseInt(out_trade_no)); 121 //安全起见,添加金额作比较 122 if(!WeiChartConfig.MchId.equals(mch_id) || order==null || Double.parseDouble(total_fee)/100!=getIntRound(order.getPrice().doubleValue()){ 123 payNotifyTbl.setPay_result("参数错误"); 124 log.info("支付失败,错误信息:" + "参数错误"); 125 resXml = "<xml>" + "<return_code><![CDATA[FAIL]]></return_code>" 126 + "<return_msg><![CDATA[参数错误]]></return_msg>" + "</xml> "; 127 }else{ 128 if(order.getOrder_status()==7){ 129 //逻辑处理,支付完成,修改订单状态 130 order.setOrder_status(OrderStatus.STEP_08.getNodeId());//订单完成 131 order.setTotal_price(Double.parseDouble(total_fee)/100);//支付费用 132 order.setPay_way(1);//微信支付 133 order.setPay_time(new Date());//支付时间 134 order = ycOrderTblService.save(order); 135 136 payNotifyTbl.setPay_result("支付成功"); 137 resXml = "<xml>" + "<return_code><![CDATA[SUCCESS]]></return_code>" 138 + "<return_msg><![CDATA[OK]]></return_msg>" + "</xml> "; 139 }else{ 140 payNotifyTbl.setPay_result("订单已经被处理"); 141 log.info("订单已经被处理"); 142 resXml = "<xml>" + "<return_code><![CDATA[SUCCESS]]></return_code>" 143 + "<return_msg><![CDATA[OK]]></return_msg>" + "</xml> "; 144 } 145 146 } 147 148 }else{ 149 payNotifyTbl.setPay_result(paramMap.get("return_msg")); 150 log.info("微信支付异步通知返回的错误信息:"+paramMap.get("return_msg")); 151 resXml = "<xml>" + "<return_code><![CDATA[FAIL]]></return_code>" 152 + "<return_msg><![CDATA[报文为空]]></return_msg>" + "</xml> "; 153 } 154 155 }else{ 156 payNotifyTbl.setPay_result("微信支付异步通知签名验证失败"); 157 log.info("微信支付异步通知签名验证失败"); 158 resXml = "<xml>" + "<return_code><![CDATA[FAIL]]></return_code>" 159 + "<return_msg><![CDATA[通知签名验证失败]]></return_msg>" + "</xml> "; 160 } 161 162 if(payNotifyTblService==null){ 163 payNotifyTblService= (PayNotifyTblService) SpringContextUtil.getBean(PayNotifyTblService.class); 164 } 165 166 payNotifyTbl = payNotifyTblService.save(payNotifyTbl); 167 //返回微信支付系统告知成功接收 168 BufferedOutputStream out = new BufferedOutputStream( 169 response.getOutputStream()); 170 out.write(resXml.getBytes()); 171 out.flush(); 172 out.close(); 173 } 174 175 } 176
在接入微信支付时,入了很多坑,特别注意:生成签名时传入参数的顺序,不同的顺序会导致生成的签名与微信返回的签名不一致。尤其注意调用统一下单接口后,重新生成签名的顺序,一定要按照appid,partnerid,prepayid,noncestr,timestamp,package的顺序,否则App会调起支付页面失败,报签名异常的错误。另外注意:异步通知地址一定是外网可以访问的,而且和微信平台上配置的异步通知地址一致。