微信支付服务端流程:
首先客户端调起服务端接口来进行微信的统一下单(微信统一下单要进行两次签名验证),通过验证后返回处理结果,得到成功状态码后通知客户端,并返回相应的信息。
然后客户端发起支付,调用微信服务端。支付成功后,微信调用服务端的回调函数通知服务端支付结果,然后服务端进行一些后续处理操作。
1--------- 微信支付统一下单及验证签名
/** * 微信支付统一下单及验证签名 * * @param request * 请求信息 * @return 处理结果JSON信息 * @throws 无 */ @RequestMapping(value = "/wxPayinfo", method = RequestMethod.GET) @ResponseBody public ResultTO wxPayinfo(String orderId,String memberId,HttpServletRequest request, HttpServletResponse response) { // 下单详细 Map<String, Object> back = null; // 未输入订单ID if (orderId==null || orderId.isEmpty() || "".equals(orderId)) { // 返回未输入订单ID错误信息 return ResultTO.newFailResultTO("参数orderId不能为空", null); } // 未输入用户ID if (memberId==null || memberId.isEmpty() || "".equals(memberId)) { // 返回未输入用户ID错误信息 return ResultTO.newFailResultTO("参数memberId不能为空", null); } try { // 提交前订单信息 Map<String,Object> map = new HashMap<String,Object>(); map.put("orderId", orderId); Order oldOrder = OrderService.selectObject(map); if (oldOrder != null) { Double amount = oldOrder.getOrderAmount().doubleValue(); TenPayManager tenPayManager = new TenPayManager();
//一次签名验证 back = tenPayManager.weixinPay(oldOrder.getOrderSn().toString(), amount, request, response); } if(back.get("return_code").toString().equals("FAIL")){ String msg = back.get("return_msg").toString(); // 返回登录信息错误信息 return ResultTO.newFailResultTO(msg, null); } else { logger.info("++++++++++++++++++++++++++++++++++ back: "+back); //二次签名验证 TenPayManager tenPayManager = new TenPayManager(); Map<String, Object> back2 = tenPayManager.weixinSignSecond(back); logger.info("++++++++++++++++++++++++++++++++++ back2: "+back2); // 返回处理成功信息 return ResultTO.newSuccessResultTO("微信验证成功", back2); } } catch (Exception e) { // 保存错误LOG logger.error(e.getLocalizedMessage()); // 返回登录信息错误信息 return ResultTO.newFailResultTO("微信验证失败", null); } }
2------微信工具类
package com.util.pay.tenpay; import java.util.HashMap; import java.util.Map; import java.util.Random; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.jboss.logging.Logger; import com.util.AmountUtils; import com.util.DateUtil; import com.util.pay.tenpay.util.HttpPost; import com.util.pay.tenpay.util.MD5Weixin; import com.util.pay.tenpay.util.XmlParser; public class TenPayManager { /** * 发单超过系统次数支付接口 * * @param orderId * @param request * @param response * @return */ public Map<String, Object> weixinPay(String orderSn, double amount, HttpServletRequest request, HttpServletResponse response) { //服务器IP String serverIP = "127.0.0.1"; // if(request != null){ // serverIP = request.getRemoteAddr(); // } String nonce_str = createNonceStr(); String body = "订单:" + orderSn; //注意:微信中金额单位为分,传参值要转化为分 String oAmount = moneyToFen(amount); //要删--用于测试 start //oAmount = "1"; // end //签名验证,拼接字符串 String stringA = "appid=" + Constant.appID + "&body=" + body + "&mch_id=" + Constant.mchID + "&nonce_str=" + nonce_str + "¬ify_url=" + Constant.wxNoticeUrl + "&out_trade_no=" + orderSn + "&spbill_create_ip=" + serverIP + "&total_fee=" + oAmount + "&trade_type=" + Constant.wxTradeType;
//拼接key String stringSignTemp = stringA + "&key=" + Constant.key; System.out.println(stringA); System.out.println(stringSignTemp); // 签名最后全部转为大写 String sign = MD5Weixin.MD5Encode(stringSignTemp).toUpperCase(); System.out.println(sign); // 拼接xml StringBuffer xml = new StringBuffer(); xml.append("<xml>"); xml.append("<appid>").append(Constant.appID).append("</appid>"); xml.append("<body>").append(body).append("</body>"); xml.append("<mch_id>").append(Constant.mchID).append("</mch_id>"); xml.append("<nonce_str>").append(nonce_str).append("</nonce_str>"); xml.append("<notify_url>").append(Constant.wxNoticeUrl) .append("</notify_url>"); xml.append("<out_trade_no>").append(orderSn).append("</out_trade_no>"); xml.append("<spbill_create_ip>").append(serverIP) .append("</spbill_create_ip>"); xml.append("<total_fee>").append(oAmount).append("</total_fee>"); xml.append("<trade_type>").append(Constant.wxTradeType) .append("</trade_type>"); xml.append("<sign><![CDATA[").append(sign).append("]]></sign>"); xml.append("</xml>"); // System.out.println( "----------微信下单-----下单参数----------:" + // xml.toString() ); Map<String, Object> resultMap = new HashMap<String, Object>(); String retXmlStr = null; retXmlStr = HttpPost.sendPostWithXmlPara(Constant.pay_address, new String(xml.toString())); if (null != retXmlStr) { resultMap = XmlParser.xmlStrParser(retXmlStr); resultMap.put("timestamp", DateUtil.intDate()); resultMap.put("pack", "Sign=WXPay"); } return resultMap; } public static String createNonceStr() { String letter[] = { "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "S", "Y", "Z" }; String nonce_str = ""; for (int i = 0; i < 26; i++) { int num = new Random().nextInt(26); nonce_str += letter[num]; } return nonce_str; } /** * 将金额转换成分 * * @param amount * 金额 * @return String 分为单位的金额 */ public static String moneyToFen(double amount) { // 分为单位的金额 String toFenTemp = String.valueOf(amount); // 临时保存用金额 String toFen = AmountUtils.changeY2F(toFenTemp); // 返回分为单位的金额 return toFen; } /** * 微信二次签名 * * @return */ public Map<String, Object> weixinSignSecond(Map<String,Object> back) { String stringA = "appid=" + Constant.appID + "&noncestr=" + back.get("nonce_str") + "&package=Sign=WXPay" + "&partnerid=" + Constant.mchID + "&prepayid=" + back.get("prepay_id") + "×tamp=" + DateUtil.intDate(); System.out.println(stringA); String stringSignTemp = stringA + "&key=" + Constant.key; System.out.println(stringSignTemp); // 签名最后全部转为大写 String sign = MD5Weixin.MD5Encode(stringSignTemp).toUpperCase(); System.out.println(sign); Map<String, Object> resultMap = new HashMap<String, Object>(); resultMap.put("appid", Constant.appID); resultMap.put("partnerid", Constant.mchID); resultMap.put("prepayid", back.get("prepay_id")); resultMap.put("pack", "Sign=WXPay"); resultMap.put("noncestr", back.get("nonce_str")); resultMap.put("timestamp", DateUtil.intDate()); resultMap.put("sign", sign); return resultMap; } }
3------配置类
package com.util.pay.tenpay; public class Constant { public static final String key = "";// 签名算法需要用到的秘钥 public static final String appID = "";// 开放平台AppID public static final String mchID = "100011111";// 商户号 public static final String pwd = "111111";// // public static final String wxBody = "微信test";// public static final String pay_address = "https://api.mch.weixin.qq.com/pay/unifiedorder";// 统一支付API接口地址 public static final String check_url = "https://api.mch.weixin.qq.com/pay/orderquery";// 查询订单API接口地址 public static final String close_url = "https://api.mch.weixin.qq.com/pay/closeorder";// 关闭订单API接口地址 public static final String refund_url = "https://api.mch.weixin.qq.com/secapi/pay/refund"; //退款 //测试用地址 // public static String wxNoticeUrl = "http://xxx.xxx.xxx.xx:8080/api/wxPayReturn/wxPayNotifyReturn"; public static String wxNoticeUrl = "http://xxx.xxx.xxx.xx:8080/api/wxPayReturn/wxPayNotifyReturn";
public static final String wxTradeType = "APP";// 取值如下:JSAPI,NATIVE,APP }
4-----回调函数
package com.controller.api.; import java.io.BufferedOutputStream; import java.io.StringReader; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Date; import java.util.HashMap; import java.util.List; import java.util.Map; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.apache.log4j.Logger; import org.jdom.Document; import org.jdom.Element; import org.jdom.input.SAXBuilder; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.util.StringUtils; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.ResponseBody; import org.xml.sax.InputSource; import com.controller.AbstractAPIController; import com.po.Order; import com.po.Store; import com.service.OrderService; import com.service.StoreService; import com.to.ResultTO; import com.util.RequestParamUtil; import com.util.pay.tenpay.TenPayManager; import com.util.pay.tenpay.bean.WxPayResult; /** * FileName AppCourseController.java * * Version 1.0 * * Create by zy 2016/3/20 * * APP课程业务控制器 */ @Controller @RequestMapping("/api/wxPayReturn") public class WxPayReturnController extends AbstractAPIController { /** 日志文件生成器 */ private static Logger log = Logger.getLogger(WxPayReturnController.class); @Autowired private OrderService OrderService; @Autowired private StoreService StoreService; /** * 微信支付回调接口 * * @author zy * @since Version 1.0 * @param request * 请求信息 * @return 处理结果JSON信息 * @throws 无 */ @RequestMapping(value = "/wxPayNotifyReturn") @ResponseBody public ResultTO wxPayNotifyReturn(HttpServletRequest request, HttpServletResponse response) { log.info("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA-------进入微信回调函数wxPayNotifyReturn!"); try { // 把如下代码贴到的你的处理回调的servlet 或者.do 中即可明白回调操作 String inputLine; String notityXml = ""; String resXml = ""; try { while ((inputLine = request.getReader().readLine()) != null) { notityXml += inputLine; } request.getReader().close(); } catch (Exception e) { e.printStackTrace(); } System.out.println("接收到的报文:" + notityXml); Map<String,String> m = parseXmlToList2(notityXml); WxPayResult wpr = new WxPayResult(); wpr.setAppid(m.get("appid").toString()); wpr.setBankType(m.get("bank_type").toString()); wpr.setCashFee(m.get("cash_fee").toString()); wpr.setFeeType(m.get("fee_type").toString()); wpr.setIsSubscribe(m.get("is_subscribe").toString()); wpr.setMchId(m.get("mch_id").toString()); wpr.setNonceStr(m.get("nonce_str").toString()); wpr.setOpenid(m.get("openid").toString()); wpr.setOutTradeNo(m.get("out_trade_no").toString()); wpr.setResultCode(m.get("result_code").toString()); wpr.setReturnCode(m.get("return_code").toString()); wpr.setSign(m.get("sign").toString()); wpr.setTimeEnd(m.get("time_end").toString()); wpr.setTotalFee(m.get("total_fee").toString()); wpr.setTradeType(m.get("trade_type").toString()); wpr.setTransactionId(m.get("transaction_id").toString()); boolean payFlag = false; log.info("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA-------wpr.getReturnCode():"+wpr.getReturnCode()); if ("SUCCESS".equals(wpr.getReturnCode())) { // 支付成功 resXml = "<xml>" + "<return_code><![CDATA[SUCCESS]]></return_code>" + "<return_msg><![CDATA[OK]]></return_msg>" + "</xml> "; payFlag = true; } else { resXml = "<xml>" + "<return_code><![CDATA[FAIL]]></return_code>" + "<return_msg><![CDATA[报文为空]]></return_msg>" + "</xml> "; } // 支付成功 if (payFlag) { log.info("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA----------------payFlag:"+payFlag); //支付成功后,商户操作处理 } BufferedOutputStream out = new BufferedOutputStream( response.getOutputStream()); out.write(resXml.getBytes()); out.flush(); out.close(); } catch (Exception e) { // 保存错误LOG log.error(e.getLocalizedMessage()); // 返回登录信息错误信息 return ResultTO.newFailResultTO("支付失败!", null); } // 返回处理成功信息以及课程列表 return ResultTO.newSuccessResultTO("支付成功", null); } /** * description: 解析微信通知xml * * @param xml * @return * @author ex_yangxiaoyi * @see */ private static Map<String,String> parseXmlToList2(String xml) { Map<String,String> retMap = new HashMap<String,String>(); try { StringReader read = new StringReader(xml); // 创建新的输入源SAX 解析器将使用 InputSource 对象来确定如何读取 XML 输入 InputSource source = new InputSource(read); // 创建一个新的SAXBuilder SAXBuilder sb = new SAXBuilder(); // 通过输入源构造一个Document Document doc = (Document) sb.build(source); Element root = doc.getRootElement();// 指向根节点 List<Element> es = root.getChildren(); if (es != null && es.size() != 0) { for (Element element : es) { retMap.put(element.getName(), element.getValue()); } } } catch (Exception e) { e.printStackTrace(); } return retMap; }
5、AmountUtils
/** * 将元为单位的转换为分 替换小数点,支持以逗号区分的金额 * * @param amount * @return */ public static String changeY2F(String amount) { String currency = amount.replaceAll("\$|\¥|\,", ""); // 处理包含, ¥ // 或者$的金额 int index = currency.indexOf("."); int length = currency.length(); Long amLong = 0l; if (index == -1) { amLong = Long.valueOf(currency + "00"); } else if (length - index >= 3) { amLong = Long.valueOf((currency.substring(0, index + 3)).replace( ".", "")); } else if (length - index == 2) { amLong = Long.valueOf((currency.substring(0, index + 2)).replace( ".", "") + 0); } else { amLong = Long.valueOf((currency.substring(0, index + 1)).replace( ".", "") + "00"); } return amLong.toString(); }