前置准备
微信的东西比支付宝要复杂一点,要先去注册开发者账号,然后一系列巴拉巴拉……,然后得到下面列出的我们开发需要的参数,下面的参数中TOKEN和CERT_PATH是需要退款和设计到其他功能才需要的,如果只做APP支付功能的同学可以忽略。
- APP_ID : 服务号的应用ID
- APP_SECRET : 服务号的应用密钥
- TOKEN : 服务号的配置token
- MCH_ID : 商户号
- API_KEY : API密钥
- SIGN_TYPE : 签名加密方式
- CERT_PATH : 微信支付证书名称、
支付流程
具体的流程大概就是后台拿到(或生成)自己的订单号后,拿着一堆参数按ASCII码(即abc…)排序后用MD5加密后调用微信的服务器地址拿到一个prepayId,再将prepayId和当前的参数再次排序和加密(二次加密)生成sign后返回前端进行支付,支付完成后,微信的后台会回调我们的后台接口。
Java代码
maven依赖
1 `<!-- 微信支付依赖 --> 2 <dependency> 3 <groupId>org.xmlpull</groupId> 4 <artifactId>xmlpull</artifactId> 5 <version>1.1.3.1 </version> 6 </dependency> 7 <dependency> 8 <groupId>net.sf.json-lib</groupId> 9 <artifactId>json-lib</artifactId> 10 <version>2.3</version> 11 <classifier>jdk15</classifier> 12 </dependency> 13 <!-- https://mvnrepository.com/artifact/com.thoughtworks.xstream/xstream --> 14 <dependency> 15 <groupId>com.thoughtworks.xstream</groupId> 16 <artifactId>xstream</artifactId> 17 <version>1.4.5</version> 18 </dependency> 19 <!-- https://mvnrepository.com/artifact/com.ning/async-http-client --> 20 <dependency> 21 <groupId>com.ning</groupId> 22 <artifactId>async-http-client</artifactId> 23 <version>1.8.13</version> 24 </dependency>`
Java代码
1 @ResponseBody 2 @RequestMapping(value="/wxpaySign",method=RequestMethod.POST) 3 public JsonResult getWXPaySign(double totalAmount,String userId,String out_trade_no,String ip) 4 throws JDOMException, IOException{ 5 //预备参数 6 WXPayDTO wxPayDTO = new WXPayDTO(); 7 String subject = "******"; 8 String body = "******"; 9 String nonceStr = setNonceStr(); 10 long timeStamp = (long)CommonUtil.getSecondTimestamp(new Date()); 11 String packageStr = "Sign=WXPay"; 12 String total_fee = String.valueOf(CommonUtil.double100ToInt(totalAmount)); 13 14 SortedMap<String,String> payMap = genOrderData(PayConstants.WX_APP_ID,body,out_trade_no, 15 nonceStr,packageStr,PayConstants.WX_PARTNER_ID,total_fee,subject,ip); 16 17 //设置WXPayDTO 18 wxPayDTO.setAppId(PayConstants.WX_APP_ID); 19 //商家id 20 wxPayDTO.setPartnerId(PayConstants.WX_PARTNER_ID); 21 wxPayDTO.setPrepayId(out_trade_no); //订单 22 wxPayDTO.setNonceStr(nonceStr); //设置随机串 23 wxPayDTO.setTimeStamp(timeStamp); //设置时间戳 24 wxPayDTO.setPackageStr(packageStr); 25 wxPayDTO.setSign(payMap.get("sign")); 26 wxPayDTO.setPrepayId(payMap.get("prepayid")); 27 return new JsonResult(Message.M2000,wxPayDTO); 28 } 29 30 31 private SortedMap<String, String> genOrderData(String appId, String body, String out_trade_no, 32 String nonceStr, String packageStr, String partnerId,String total_fee,String subject,String ip) 33 throws JDOMException, IOException { 34 SortedMap<String, String> paraMap = new TreeMap<String,String>(); 35 //按照官方文档的要求,这里参数名ASCII码从小到大排序(字典序); 36 paraMap.put("appid", appId); 37 paraMap.put("body",body); 38 paraMap.put("mch_id",partnerId); //微信商户账号 39 paraMap.put("nonce_str",nonceStr); //32位不重复的编号 40 paraMap.put("notify_url", 41 PayConstants.WX_Notify_Url); //设置一个回调的地址 42 paraMap.put("out_trade_no", out_trade_no); //订单号 43 paraMap.put("spbill_create_ip", ip);//前端传回来的ip 44 paraMap.put("total_fee",total_fee); //设置金额 45 paraMap.put("trade_type","APP"); //方式为APP支付 46 //用上面封装的参数生成sign 47 String sign = PayCommonUtil.createSign("UTF-8", paraMap,PayConstants.WX_API_KEY); 48 //这里的sign用二次签名生成的sign 49 paraMap.put("sign",sign); 50 //统一下单地址 51 //将上面集合内容转成xml 52 String xml = getRequestXML(paraMap); 53 String xmlStr = HttpUtil.postData(PayConstants.WX_Notify_Url,xml); 54 //将返回的xml转换成map 55 Map map = XMLUtil.doXMLParse(xmlStr); 56 //预付商品id 57 String prepay_id = (String) map.get("prepay_id"); 58 59 //这里要对参数进行二次签名 60 SortedMap<String, String> secParaMap = new TreeMap<String,String>(); 61 secParaMap.put("appid", appId); 62 secParaMap.put("partnerid",partnerId); 63 secParaMap.put("prepayid", prepay_id); 64 //这里不确定是不是要重新生成一个随机串,再改 65 secParaMap.put("noncestr", nonceStr); 66 secParaMap.put("timestamp", CommonUtil.getTimeStamp()); 67 secParaMap.put("package", "Sign=WXPay"); 68 String secSign = PayCommonUtil.createSign("UTF-8", secParaMap, PayConstants.WX_API_KEY); 69 secParaMap.put("sign", secSign); 70 secParaMap.put("prepayid", prepay_id); 71 72 return secParaMap; 73 }
支付回调
微信的支付回调是用流形式传输的,所以要先对流形式的数据进行解析再得到我们需要的东西。
1 @RequestMapping(value = "payNotifyUrl", produces = "application/json;charset=UTF-8") 2 @ResponseBody 3 public String payNotifyUrl(HttpServletRequest request, HttpServletResponse response) throws Exception { 4 //读取参数 5 6 InputStream inputStream ; 7 StringBuffer sb = new StringBuffer(); 8 inputStream = request.getInputStream(); 9 String s ; 10 BufferedReader in = new BufferedReader(new InputStreamReader(inputStream, "UTF-8")); 11 while ((s = in.readLine()) != null){ 12 sb.append(s); 13 } 14 in.close(); 15 inputStream.close(); 16 17 //解析xml成map 18 Map<String, String> m = new HashMap<String, String>(); 19 m = XMLUtil.doXMLParse(sb.toString()); 20 21 //过滤空 设置 TreeMap 22 SortedMap<Object,Object> packageParams = new TreeMap<Object,Object>(); 23 Iterator it = m.keySet().iterator(); 24 while (it.hasNext()) { 25 String parameter = (String) it.next(); 26 String parameterValue = m.get(parameter); 27 28 String v = ""; 29 if(null != parameterValue) { 30 v = parameterValue.trim(); 31 } 32 packageParams.put(parameter, v); 33 } 34 35 //判断签名是否正确 36 if(PayCommonUtil.isTenpaySign("UTF-8", packageParams,PayConstants.WX_API_KEY)) { 37 //------------------------------ 38 //处理业务开始 39 //------------------------------ 40 String resXml = ""; 41 if("SUCCESS".equals((String)packageParams.get("result_code"))){ 42 // 这里是支付成功 43 //////////执行自己的业务逻辑//////////////// 44 String mch_id = (String)packageParams.get("mch_id"); 45 String openid = (String)packageParams.get("openid"); 46 String is_subscribe = (String)packageParams.get("is_subscribe"); 47 String out_trade_no = (String)packageParams.get("out_trade_no"); 48 49 String total_fee = (String)packageParams.get("total_fee"); 50 51 System.out.println("mch_id:"+mch_id); 52 System.out.println("openid:"+openid); 53 logger.info("is_subscribe:"+is_subscribe); 54 logger.info("out_trade_no:"+out_trade_no); 55 logger.info("total_fee:"+total_fee); 56 57 //////////执行自己的业务逻辑//////////////// 58 59 logger.info("支付成功"); 60 //通知微信.异步确认成功.必写.不然会一直通知后台.八次之后就认为交易失败了. 61 resXml = "<xml>" + "<return_code><![CDATA[SUCCESS]]></return_code>" 62 + "<return_msg><![CDATA[OK]]></return_msg>" + "</xml> "; 63 64 } else { 65 logger.info("支付失败,错误信息:" + packageParams.get("err_code")); 66 resXml = "<xml>" + "<return_code><![CDATA[FAIL]]></return_code>" 67 + "<return_msg><![CDATA[报文为空]]></return_msg>" + "</xml> "; 68 } 69 //------------------------------ 70 //处理业务完毕 71 //------------------------------ 72 BufferedOutputStream out = new BufferedOutputStream( 73 response.getOutputStream()); 74 out.write(resXml.getBytes()); 75 out.flush(); 76 out.close(); 77 } else{ 78 logger.info("通知签名验证失败"); 79 } 80 return "SUCCESS"; 81 }