最近在负责公司的微信扫码支付,网上的扫码支付文章写的一言难尽,所以想自己写一篇心得,帮助需要的人。
开发之前呢,必须要去看看微信的开发api,弄明白了流程剩下的就好办了。废话少说,接下来就来Coding吧
- 第一步是引入依赖:Native支付除了基本的依赖之外还需要的依赖有以下这些
<dependency> <groupId>com.google.zxing</groupId> <artifactId>core</artifactId> <version>3.3.0</version> </dependency> <dependency> <groupId>com.google.zxing</groupId> <artifactId>javase</artifactId> <version>3.3.3</version> </dependency> <dependency> <groupId>cn.hutool</groupId> <artifactId>hutool-all</artifactId> <version>5.0.3</version> </dependency> <dependency> <groupId>org.jdom</groupId> <artifactId>jdom</artifactId> <version>1.1</version> </dependency>
<dependency> <groupId>org.apache.httpcomponents</groupId> <artifactId>httpclient</artifactId> <version>4.3.5</version> </dependency> <dependency> <groupId>com.alibaba</groupId> <artifactId>fastjson</artifactId> <version>1.2.45</version>
2. 创建WeChatConfig类
/** * 微信支付配置文件 * @author lrh * */ public class WeChatConfig { /** * 微信服务号APPID */ public static String APPID="xxxxxx"; /** * 微信支付的商户号 */ public static String MCHID="xxxxxx"; /** * 微信支付的API密钥 */ public static String APIKEY="xxxxxx"; /** * 微信支付成功之后的回调地址【注意:当前回调地址必须是公网能够访问的地址】 * 这里我是用了natapp来对本地进行穿透,本地调试的时候记得修改 */ public static String WECHAT_NOTIFY_URL_PC="http://68jhuv.natappfree.cc/chevip_bms/app/zcp/wechat_notify_url_pc"; /** * 微信统一下单API地址 */ public static String UFDODER_URL="https://api.mch.weixin.qq.com/pay/unifiedorder"; }
3. 创建微信统一下单api下的其他字段的entity-->
import lombok.Data; /** * 微信支付需要的入参 * * @author lrh */ @Data public class WeChatParams { public String total_fee;//订单金额:以分钱为单位 public String body;//商品名称 public String out_trade_no;//商户订单号 public String attach;//附加参数 }
4. 接下来就是根据微信的api文档,转换成对应的格式来进行HttpClient请求。工具类我放在最后面提供
import java.awt.image.BufferedImage; import java.util.Map; import java.util.SortedMap; import java.util.TreeMap; import com.chevip.bms.modules.Service.wechat.config.WeChatConfig; import com.chevip.bms.modules.Service.wechat.entity.WeChatDto; import com.chevip.bms.modules.Service.wechat.utils.HttpClientUtil; import com.chevip.bms.modules.Service.wechat.utils.PayForUtil; import com.chevip.bms.modules.Service.wechat.utils.XMLUtil; import com.chevip.framework.utils.PgsApiUtils; import lombok.extern.slf4j.Slf4j; import com.google.zxing.common.BitMatrix; @Slf4j public class WeixinPay { private static final int BLACK = 0xff000000; private static final int WHITE = 0xFFFFFFFF; /** * 获取微信支付的二维码地址 * * @return * @throws Exception * @author lrh */ public static String getCodeUrl(WeChatDto ps) throws Exception { /** * 账号信息 */ String appid = WeChatConfig.APPID;//微信服务号的appid String mch_id = WeChatConfig.MCHID; //微信支付商户号 String key = WeChatConfig.APIKEY; // 微信支付的API密钥 String notify_url = WeChatConfig.WECHAT_NOTIFY_URL_PC;//回调地址【注意,这里必须要使用外网的地址】 String ufdoder_url = WeChatConfig.UFDODER_URL;//微信下单API地址 String trade_type = "NATIVE"; //类型【网页扫码支付】 /** * 时间字符串 */ String currTime = PayForUtil.getCurrTime(); String strTime = currTime.substring(8, currTime.length()); String strRandom = PayForUtil.buildRandom(4) + ""; String nonce_str = strTime + strRandom; /** * 参数封装 */ SortedMap<Object, Object> packageParams = new TreeMap<Object, Object>(); packageParams.put("appid", appid); packageParams.put("mch_id", mch_id); packageParams.put("nonce_str", nonce_str);//随机字符串 packageParams.put("body", ps.getBody());//支付的商品名称 packageParams.put("out_trade_no", ps.getOut_trade_no());//交易订单号->我用的是时间戳 packageParams.put("total_fee", ps.getTotal_fee());//支付金额 packageParams.put("spbill_create_ip", PayForUtil.localIp());//客户端主机 packageParams.put("notify_url", notify_url); packageParams.put("trade_type", trade_type); packageParams.put("attach", ps.getAttach()); String sign = PayForUtil.createSign("UTF-8", packageParams, key); //获取签名 packageParams.put("sign", sign); String requestXML = PayForUtil.getRequestXml(packageParams);//将请求参数转换成String类型 log.info("微信支付请求参数的报文" + requestXML); String resXml = HttpClientUtil.postData(ufdoder_url, requestXML); //解析请求之后的xml参数并且转换成String类型 Map map = XMLUtil.doXMLParse(resXml); log.info("微信支付响应参数的报文" + resXml); String urlCode = (String) map.get("code_url"); return urlCode; } }
上面的代码请求返回的是url_code ,其中需要将字段转换成微信所需要的xml格式。
微信是不支持直接返回二维码图片的 ,所以需要将url_code使用zxing转换为二维码图片
5.工具类提供:
package com.chevip.bms.modules.Service.wechat.utils; import com.alibaba.fastjson.JSON; import org.apache.http.client.methods.CloseableHttpResponse; import org.apache.http.client.methods.HttpPost; import org.apache.http.entity.StringEntity; import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClients; import org.apache.http.util.EntityUtils; import java.io.IOException; /** * @author lrh */ public class HttpClientUtil { public static String postData(String url, String data) { //创建HttpClient对象 CloseableHttpClient httpClient = HttpClients.createDefault(); //创建response对象 CloseableHttpResponse response = null; //创建POST请求 HttpPost httpPost = new HttpPost(url); //将data转换为JSON对象 StringEntity stringEntity = new StringEntity(JSON.toJSONString(data), "UTF-8"); //将JSON对象保存到Entity中 httpPost.setEntity(stringEntity); //设置请求头 httpPost.setHeader("Content-Type", "application/json;charset=utf-8"); try { response = httpClient.execute(httpPost); if (response != null) { return EntityUtils.toString(response.getEntity()); } } catch (IOException e) { e.printStackTrace(); }finally { try { if (httpClient != null) { httpClient.close(); } if (response != null) { response.close(); } } catch (IOException e) { e.printStackTrace(); } } return null; } }
package com.chevip.bms.modules.Service.wechat.utils; import com.alibaba.fastjson.JSON; import org.apache.http.client.methods.CloseableHttpResponse; import org.apache.http.client.methods.HttpPost; import org.apache.http.entity.StringEntity; import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClients; import org.apache.http.util.EntityUtils; import java.io.IOException; /** * @author lrh */ public class HttpClientUtil { public static String postData(String url, String data) { //创建HttpClient对象 CloseableHttpClient httpClient = HttpClients.createDefault(); //创建response对象 CloseableHttpResponse response = null; //创建POST请求 HttpPost httpPost = new HttpPost(url); //将data转换为JSON对象 StringEntity stringEntity = new StringEntity(JSON.toJSONString(data), "UTF-8"); //将JSON对象保存到Entity中 httpPost.setEntity(stringEntity); //设置请求头 httpPost.setHeader("Content-Type", "application/json;charset=utf-8"); try { response = httpClient.execute(httpPost); if (response != null) { return EntityUtils.toString(response.getEntity()); } } catch (IOException e) { e.printStackTrace(); }finally { try { if (httpClient != null) { httpClient.close(); } if (response != null) { response.close(); } } catch (IOException e) { e.printStackTrace(); } } return null; } }
package com.chevip.bms.modules.Service.wechat.utils; import lombok.extern.slf4j.Slf4j; import java.net.Inet4Address; import java.net.InetAddress; import java.net.InterfaceAddress; import java.net.NetworkInterface; import java.net.SocketException; import java.text.SimpleDateFormat; import java.util.Date; import java.util.Enumeration; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; import java.util.SortedMap; /** * 支付通用公共类 * * @author lrh */ @Slf4j public class PayForUtil { /** * 是否签名正确,规则是:按参数名称a-z排序,遇到空值的参数不参加签名。 * @return boolean */ public static boolean isTenpaySign(String characterEncoding, SortedMap<Object, Object> packageParams, String API_KEY) { StringBuffer sb = new StringBuffer(); Set es = packageParams.entrySet(); Iterator it = es.iterator(); while(it.hasNext()) { Map.Entry entry = (Map.Entry)it.next(); String k = (String)entry.getKey(); String v = (String)entry.getValue(); if(!"sign".equals(k) && null != v && !"".equals(v)) { sb.append(k + "=" + v + "&"); } } sb.append("key=" + API_KEY); //算出摘要 String mysign = MD5Util.MD5Encode(sb.toString(), characterEncoding).toLowerCase(); String tenpaySign = ((String)packageParams.get("sign")).toLowerCase(); return tenpaySign.equals(mysign); } /** * @author chenp * @Description:sign签名 * @param characterEncoding * 编码格式 * @param * * @return */ public static String createSign(String characterEncoding, SortedMap<Object, Object> packageParams, String API_KEY) { StringBuffer sb = new StringBuffer(); Set es = packageParams.entrySet(); Iterator it = es.iterator(); while (it.hasNext()) { Map.Entry entry = (Map.Entry) it.next(); String k = (String) entry.getKey(); String v = (String) entry.getValue(); if (null != v && !"".equals(v) && !"sign".equals(k) && !"key".equals(k)) { sb.append(k + "=" + v + "&"); } } sb.append("key=" + API_KEY); String sign = MD5Util.MD5Encode(sb.toString(), characterEncoding).toUpperCase(); return sign; } /** * @author chenp * @Description:将请求参数转换为xml格式的string * @param parameters * 请求参数 * @return */ public static String getRequestXml(SortedMap<Object, Object> parameters) { StringBuffer sb = new StringBuffer(); sb.append("<xml>"); Set es = parameters.entrySet(); Iterator it = es.iterator(); while (it.hasNext()) { Map.Entry entry = (Map.Entry) it.next(); String k = (String) entry.getKey(); String v = (String) entry.getValue(); if ("attach".equalsIgnoreCase(k) || "body".equalsIgnoreCase(k) || "sign".equalsIgnoreCase(k)) { sb.append("<" + k + ">" + "<![CDATA[" + v + "]]></" + k + ">"); } else { sb.append("<" + k + ">" + v + "</" + k + ">"); } } sb.append("</xml>"); return sb.toString(); } /** * 取出一个指定长度大小的随机正整数. * * @param length * int 设定所取出随机数的长度。length小于11 * @return int 返回生成的随机数。 */ public static int buildRandom(int length) { int num = 1; double random = Math.random(); if (random < 0.1) { random = random + 0.1; } for (int i = 0; i < length; i++) { num = num * 10; } return (int) ((random * num)); } /** * 获取当前时间 yyyyMMddHHmmss * @author chenp * @return String */ public static String getCurrTime() { Date now = new Date(); SimpleDateFormat outFormat = new SimpleDateFormat("yyyyMMddHHmmss"); String s = outFormat.format(now); return s; } /** * 获取本机IP地址 * @author chenp * @return */ public static String localIp(){ String ip = null; Enumeration allNetInterfaces; try { allNetInterfaces = NetworkInterface.getNetworkInterfaces(); while (allNetInterfaces.hasMoreElements()) { NetworkInterface netInterface = (NetworkInterface) allNetInterfaces.nextElement(); List<InterfaceAddress> InterfaceAddress = netInterface.getInterfaceAddresses(); for (InterfaceAddress add : InterfaceAddress) { InetAddress Ip = add.getAddress(); if (Ip != null && Ip instanceof Inet4Address) { ip = Ip.getHostAddress(); } } } } catch (SocketException e) { log.warn("获取本机Ip失败:异常信息:"+e.getMessage()); } return ip; } }
package com.chevip.bms.modules.Service.wechat.utils; import com.google.zxing.*; import com.google.zxing.client.j2se.BufferedImageLuminanceSource; import com.google.zxing.client.j2se.MatrixToImageConfig; import com.google.zxing.client.j2se.MatrixToImageWriter; import com.google.zxing.common.BitMatrix; import com.google.zxing.common.HybridBinarizer; import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel; import org.apache.commons.lang.StringUtils; import javax.imageio.ImageIO; import javax.servlet.http.HttpServletResponse; import java.awt.*; import java.awt.geom.RoundRectangle2D; import java.awt.image.BufferedImage; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.IOException; import java.util.HashMap; import java.util.Map; public class QRUtil { private static final int WIDTH = 222; // 二维码宽 private static final int HEIGHT = 222; // 二维码高 private static final int WORDHEIGHT = 235; // 加文字二维码高 /** * 生成二维码 */ public static String QREncode(String content, HttpServletResponse response ,String fee,String sLicense,String typeName ) throws WriterException, IOException { int width = 200; // 图像宽度 int height = 200; // 图像高度 double money = Double.parseDouble(fee) / 100; Map<EncodeHintType, Object> hints = new HashMap<>(); //内容编码格式 hints.put(EncodeHintType.CHARACTER_SET, "UTF-8"); // 指定纠错等级 hints.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.H); //设置二维码边的空度,非负数 hints.put(EncodeHintType.MARGIN, 1); BitMatrix bitMatrix = new MultiFormatWriter().encode(content, BarcodeFormat.QR_CODE, width, height, hints); // MatrixToImageWriter.writeToPath(bitMatrix, format, new File("C:\\pic\\zxing.gif").toPath());// 输出原图片 MatrixToImageConfig matrixToImageConfig = new MatrixToImageConfig(0xFF000001, 0xFFFFFFFF); BufferedImage bufferedImage = LogoMatrix(MatrixToImageWriter.toBufferedImage(bitMatrix,matrixToImageConfig), new File("C:\\pic\\icon_weipu.png")); // BufferedImage bufferedImage = LogoMatrix(toBufferedImage(bitMatrix), new File("D:\\logo.png")); // ImageIO.write(bufferedImage, "gif", new File("C:\\pic\\"+out_trade_no+".gif"));//输出带logo图片 BufferedImage image = insertWords(bufferedImage, sLicense+typeName+"金额为"+money+"元"); System.out.println("输出成功."); ImageIO.write(image, "png", response.getOutputStream()); //图片转换为base64格式 ByteArrayOutputStream os = new ByteArrayOutputStream(); ImageIO.write(image, "png", os); String base64img = safeUrlBase64Encode(os.toByteArray()); // org.apache.commons.codec.binary.Base64.encodeBase64String(os.toByteArray()); // Base64 base64 = new Base64(); // String base64img = new String(base64.encode(bytes)); return base64img; } public static String safeUrlBase64Encode(byte[] data){ String encodeBase64 = org.apache.commons.codec.binary.Base64.encodeBase64String(data); String safeBase64Str = encodeBase64.replace('+', '-'); safeBase64Str = safeBase64Str.replace('/', '_'); // safeBase64Str = safeBase64Str.replaceAll("=", ""); return safeBase64Str; } /** * 识别二维码 */ public static void QRReader(File file) throws IOException, NotFoundException { MultiFormatReader formatReader = new MultiFormatReader(); //读取指定的二维码文件 BufferedImage bufferedImage =ImageIO.read(file); BinaryBitmap binaryBitmap= new BinaryBitmap(new HybridBinarizer(new BufferedImageLuminanceSource(bufferedImage))); //定义二维码参数 Map hints= new HashMap<>(); hints.put(EncodeHintType.CHARACTER_SET, "utf-8"); Result result = formatReader.decode(binaryBitmap, hints); //输出相关的二维码信息 System.out.println("解析结果:"+result.toString()); System.out.println("二维码格式类型:"+result.getBarcodeFormat()); System.out.println("二维码文本内容:"+result.getText()); bufferedImage.flush(); } /** * 二维码添加logo * @param matrixImage 源二维码图片 * @param logoFile logo图片 * @return 返回带有logo的二维码图片 */ public static BufferedImage LogoMatrix(BufferedImage matrixImage, File logoFile) throws IOException{ /** * 读取二维码图片,并构建绘图对象 */ Graphics2D g2 = matrixImage.createGraphics(); int matrixWidth = matrixImage.getWidth(); int matrixHeigh = matrixImage.getHeight(); /** * 读取Logo图片 */ BufferedImage logo = ImageIO.read(logoFile); //开始绘制图片 g2.drawImage(logo,matrixWidth/5*2,matrixHeigh/5*2, matrixWidth/5, matrixHeigh/5, null);//绘制 BasicStroke stroke = new BasicStroke(5,BasicStroke.CAP_ROUND,BasicStroke.JOIN_ROUND); g2.setStroke(stroke);// 设置笔画对象 //指定弧度的圆角矩形 RoundRectangle2D.Float round = new RoundRectangle2D.Float(matrixWidth/5*2, matrixHeigh/5*2, matrixWidth/5, matrixHeigh/5,20,20); g2.setColor(Color.white); g2.draw(round);// 绘制圆弧矩形 //设置logo 有一道灰色边框 BasicStroke stroke2 = new BasicStroke(1,BasicStroke.CAP_ROUND,BasicStroke.JOIN_ROUND); g2.setStroke(stroke2);// 设置笔画对象 RoundRectangle2D.Float round2 = new RoundRectangle2D.Float(matrixWidth/5*2+2, matrixHeigh/5*2+2, matrixWidth/5-4, matrixHeigh/5-4,20,20); g2.setColor(new Color(128,128,128)); g2.draw(round2);// 绘制圆弧矩形 g2.dispose(); matrixImage.flush() ; return matrixImage ; } /** * 把带logo的二维码下面加上文字 * @param image * @param words * @return */ private static BufferedImage insertWords(BufferedImage image,String words){ // 新的图片,把带logo的二维码下面加上文字 if (StringUtils.isNotEmpty(words)) { //创建一个带透明色的BufferedImage对象 BufferedImage outImage = new BufferedImage(WIDTH, WORDHEIGHT, BufferedImage.TYPE_INT_ARGB); Graphics2D outg = outImage.createGraphics(); setGraphics2D(outg); // 画二维码到新的面板 outg.drawImage(image, 0, 0, image.getWidth(), image.getHeight(), null); // 画文字到新的面板 Color color=new Color(183,183,183); outg.setColor(color); // 字体、字型、字号 outg.setFont(new Font("微软雅黑", Font.PLAIN, 14)); //文字长度 int strWidth = outg.getFontMetrics().stringWidth(words); //总长度减去文字长度的一半 (居中显示) int wordStartX=(WIDTH - strWidth) / 2; //height + (outImage.getHeight() - height) / 2 + 12 int wordStartY=HEIGHT+10; // 画文字 outg.drawString(words, wordStartX, wordStartY); outg.dispose(); outImage.flush(); return outImage; } return null; } /** * 设置 Graphics2D 属性 (抗锯齿) * @param graphics2D */ private static void setGraphics2D(Graphics2D graphics2D){ graphics2D.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); graphics2D.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_DEFAULT); Stroke s = new BasicStroke(1, BasicStroke.CAP_ROUND, BasicStroke.JOIN_MITER); graphics2D.setStroke(s); } }
package com.chevip.bms.modules.Service.wechat.utils; import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import org.jdom.Document; import org.jdom.Element; import org.jdom.JDOMException; import org.jdom.input.SAXBuilder; public class XMLUtil { /** * 解析xml,返回第一级元素键值对。如果第一级元素有子节点,则此节点的值是子节点的xml数据。 * @param strxml * @return * @throws JDOMException * @throws IOException */ public static Map doXMLParse(String strxml) throws JDOMException, IOException { strxml = strxml.replaceFirst("encoding=\".*\"", "encoding=\"UTF-8\""); if(null == strxml || "".equals(strxml)) { return null; } Map m = new HashMap(); InputStream in = new ByteArrayInputStream(strxml.getBytes("UTF-8")); SAXBuilder builder = new SAXBuilder(); Document doc = builder.build(in); Element root = doc.getRootElement(); List list = root.getChildren(); Iterator it = list.iterator(); while(it.hasNext()) { Element e = (Element) it.next(); String k = e.getName(); String v = ""; List children = e.getChildren(); if(children.isEmpty()) { v = e.getTextNormalize(); } else { v = XMLUtil.getChildrenText(children); } m.put(k, v); } //关闭流 in.close(); return m; } /** * 获取子结点的xml * @param children * @return String */ public static String getChildrenText(List children) { StringBuffer sb = new StringBuffer(); if(!children.isEmpty()) { Iterator it = children.iterator(); while(it.hasNext()) { Element e = (Element) it.next(); String name = e.getName(); String value = e.getTextNormalize(); List list = e.getChildren(); sb.append("<" + name + ">"); if(!list.isEmpty()) { sb.append(XMLUtil.getChildrenText(list)); } sb.append(value); sb.append("</" + name + ">"); } } return sb.toString(); } }
6. 最后是回调,微信的回调是直接Post我们的接口,所以必须要公网能访问到的接口。
本地测试的时候可以用内网穿透软件
package com.chevip.bms.modules.Service.wechat.service; import com.chevip.auction_order.service.ServiceOrderService; import com.chevip.bms.modules.Service.wechat.config.WeChatConfig; import com.chevip.bms.modules.Service.wechat.dao.WeChatDao; import com.chevip.bms.modules.Service.wechat.entity.WeChatResultEntity; import com.chevip.bms.modules.Service.wechat.utils.PayForUtil; import com.chevip.bms.modules.Service.wechat.utils.XMLUtil; import lombok.extern.slf4j.Slf4j; import org.jdom.JDOMException; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.*; import java.util.*; /** * 微信回调,通过流进行IO * * @author lrh */ @Slf4j @Service public class NotifyService { @Autowired ServiceOrderService serviceOrderService; @Autowired WeChatDao weChatDao; @Transactional public void wxnotify(HttpServletRequest request, HttpServletResponse response) throws IOException, JDOMException { // InputStream inputStream ; // 获取前端传来的请求参数 // inputStream = request.getInputStream(); BufferedReader in = request.getReader(); // 将字节流转化成字符流 // BufferedReader in = new BufferedReader(new InputStreamReader(inputStream, "UTF-8")); StringBuffer sb = new StringBuffer(); String s; while ((s = in.readLine()) != null) { sb.append(s); } in.close(); // inputStream.close(); //解析xml成map Map<String, String> m = new HashMap<String, String>(); m = XMLUtil.doXMLParse(sb.toString()); //过滤空 设置 TreeMap SortedMap<Object, Object> packageParams = new TreeMap<Object, Object>(); Iterator<String> it = m.keySet().iterator(); while (it.hasNext()) { String parameter = it.next(); String parameterValue = m.get(parameter); String v = ""; if (null != parameterValue) { v = parameterValue.trim(); } packageParams.put(parameter, v); } // 微信支付的API密钥 String key = WeChatConfig.APIKEY; // key log.info("微信支付返回回来的参数:" + packageParams); //判断签名是否正确 if (PayForUtil.isTenpaySign("UTF-8", packageParams, key)) { String resXml = ""; if ("SUCCESS".equals((String) packageParams.get("result_code"))) { String app_id = (String) packageParams.get("appid"); String mch_id = (String) packageParams.get("mch_id"); String openid = (String) packageParams.get("openid"); String is_subscribe = (String) packageParams.get("is_subscribe");//是否关注公众号 //商户订单号 String out_trade_no = (String) packageParams.get("out_trade_no"); //付款金额【以分为单位】 String total_fee = (String) packageParams.get("total_fee"); //微信生成的交易订单号 String transaction_id = (String) packageParams.get("transaction_id");//微信支付订单号 //支付完成时间 String time_end = (String) packageParams.get("time_end"); String attach = (String) packageParams.get("attach"); String result_code = (String) packageParams.get("result_code"); // 处理自己的回调业务 。。。// 保存微信支付订单 。。。// 微信支付成功后更改支付状态 。。。 log.info("支付成功"); //通知微信.异步确认成功.必写.不然会一直通知后台.八次之后就认为交易失败了. resXml = "<xml>" + "<return_code><![CDATA[SUCCESS]]></return_code>" + "<return_msg><![CDATA[OK]]></return_msg>" + "</xml> "; } else { log.info("支付失败,错误信息:" + packageParams.get("err_code")); resXml = "<xml>" + "<return_code><![CDATA[FAIL]]></return_code>" + "<return_msg><![CDATA[报文为空]]></return_msg>" + "</xml> "; } //------------------------------ //处理业务完毕 //------------------------------ BufferedOutputStream out = new BufferedOutputStream(response.getOutputStream()); out.write(resXml.getBytes()); out.flush(); out.close(); } else { log.info("通知签名验证失败"); } } }