1.首先我们先下载微信支付的服务器端demo

微信支付异步回调,带你解决微信支付的深坑-风君雪科技博客


微信支付异步回调,带你解决微信支付的深坑-风君雪科技博客


2.个文件作用介绍

index.jsp  下单  payRequest.jsp  获取微信支付prepay_id等。

重点我说说这个payNotifyUrl.jsp

<%@ page language="java" contentType="text/html; charset=GBK" pageEncoding="GBK"%><%@ page import="com.tenpay.RequestHandler" %><%@ page import="com.tenpay.ResponseHandler" %>   <%@ page import="com.tenpay.client.ClientResponseHandler" %>    <%@ page import="com.tenpay.client.TenpayHttpClient" %><!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"><%//---------------------------------------------------------//财付通支付通知(后台通知)示例,商户按照此文档进行开发即可//---------------------------------------------------------//商户号  String partner = "1900000109"; //密钥String key = "8934e7d15453e97507ef794cf7b0519d"; //创建支付应答对象ResponseHandler resHandler = new ResponseHandler(request, response);resHandler.setKey(key); //判断签名if(resHandler.isTenpaySign()) {		//通知id	String notify_id = resHandler.getParameter("notify_id");		//创建请求对象	RequestHandler queryReq = new RequestHandler(null, null);	//通信对象	TenpayHttpClient httpClient = new TenpayHttpClient();	//应答对象	ClientResponseHandler queryRes = new ClientResponseHandler();		//通过通知ID查询,确保通知来至财付通	queryReq.init();	queryReq.setKey(key);	queryReq.setGateUrl("https://gw.tenpay.com/gateway/verifynotifyid.xml");	queryReq.setParameter("partner", partner);	queryReq.setParameter("notify_id", notify_id);		//通信对象	httpClient.setTimeOut(5);	//设置请求内容	httpClient.setReqContent(queryReq.getRequestURL());	System.out.println("queryReq:" + queryReq.getRequestURL());	//后台调用	if(httpClient.call()) {		//设置结果参数		queryRes.setContent(httpClient.getResContent());		System.out.println("queryRes:" + httpClient.getResContent());		queryRes.setKey(key);								//获取返回参数		String retcode = queryRes.getParameter("retcode");		String trade_state = queryRes.getParameter("trade_state");			String trade_mode = queryRes.getParameter("trade_mode");					//判断签名及结果		if(queryRes.isTenpaySign()&& "0".equals(retcode) && "0".equals(trade_state) && "1".equals(trade_mode)) {			System.out.println("订单查询成功");			//取结果参数做业务处理							System.out.println("out_trade_no:" + queryRes.getParameter("out_trade_no")+					" transaction_id:" + queryRes.getParameter("transaction_id"));			System.out.println("trade_state:" + queryRes.getParameter("trade_state")+					" total_fee:" + queryRes.getParameter("total_fee"));		        //如果有使用折扣券,discount有值,total_fee+discount=原请求的total_fee			System.out.println("discount:" + queryRes.getParameter("discount")+					" time_end:" + queryRes.getParameter("time_end"));			//------------------------------			//处理业务开始			//------------------------------						//处理数据库逻辑			//注意交易单不要重复处理			//注意判断返回金额						//------------------------------			//处理业务完毕			//------------------------------			resHandler.sendToCFT("Success");		}		else{				//错误时,返回结果未签名,记录retcode、retmsg看失败详情。				System.out.println("查询验证签名失败或业务错误");				System.out.println("retcode:" + queryRes.getParameter("retcode")+						" retmsg:" + queryRes.getParameter("retmsg"));		}		} else { 		System.out.println("后台调用通信失败");					System.out.println(httpClient.getResponseCode());		System.out.println(httpClient.getErrInfo());		//有可能因为网络原因,请求已经处理,但未收到应答。	}}else{	System.out.println("通知签名验证失败");} %> 

就是上面的这代码。完全没有用。查看sdk源码才知道 这个异步回调是接收微信发送的所有参数,然后排序  加密 验签。  最坑的是  微信 的参数根本不是通过的参数返回的。而是通过的流。所以这个太坑了。下面我把我修改过的源码发出来 帮助大家解决回调问题。

首先是控制器

/**	 * 异步回调接口	 * @param request	 * @param response	 * @throws Exception	 */	@RequestMapping(value="/weixin_parent_notify.do",produces="text/html;charset=utf-8")	@ResponseBody	public String WeixinParentNotifyPage(HttpServletRequest request,HttpServletResponse response) throws Exception{				ServletInputStream instream = request.getInputStream();		StringBuffer sb = new StringBuffer();		int len = -1;		byte[] buffer = new byte[1024];				while((len = instream.read(buffer)) != -1){			sb.append(new String(buffer,0,len));		}		instream.close();				SortedMap<String,String> map = WXRequestUtil.doXMLParseWithSorted(sb.toString());//接受微信的通知参数		Map<String,String> return_data = new HashMap<String,String>();			//创建支付应答对象		ResponseHandler resHandler = new ResponseHandler(request, response);				resHandler.setAllparamenters(map);		resHandler.setKey(ConstantUtil.PARTNER_KEY[0]);				//判断签名		if(resHandler.isTenpaySign()){			if(!map.get("return_code").toString().equals("SUCCESS")){				return_data.put("return_code", "FAIL");				return_data.put("return_msg", "return_code不正确");			}else{				if(!map.get("result_code").toString().equals("SUCCESS")){					return_data.put("return_code", "FAIL");					return_data.put("return_msg", "result_code不正确");				}																String out_trade_no = map.get("out_trade_no").toString();				String time_end = map.get("time_end").toString();				BigDecimal total_fee = new BigDecimal(map.get("total_fee").toString());				//付款完成后,支付宝系统发送该交易状态通知				System.out.println("交易成功");				Map order = orderdao.PaymentEndGetOrderInfo(out_trade_no);								if(order == null){					System.out.println("订单不存在");					return_data.put("return_code", "FAIL");					return_data.put("return_msg", "订单不存在");					return WXRequestUtil.GetMapToXML(return_data);				}								int order_type = (int) order.get("order_type");				boolean payment_status = (boolean) order.get("payment_status");				int supplier_id = (int) order.get("supplier_id");								BigDecimal p = new BigDecimal("100");  				BigDecimal amount = (BigDecimal) order.get("amount");								amount  = amount.multiply(p);								//如果订单已经支付返回错误				if(payment_status){					System.out.println("订单已经支付");					return_data.put("return_code", "SUCCESS");					return_data.put("return_msg", "OK");					return WXRequestUtil.GetMapToXML(return_data);				}												//如果支付金额不等于订单金额返回错误				if(amount.compareTo(total_fee)!=0){					System.out.println("资金异常");					return_data.put("return_code", "FAIL");					return_data.put("return_msg", "金额异常");					return WXRequestUtil.GetMapToXML(return_data);				}								//更新订单信息				if(orderdao.PaymentEndUpdateOrder(out_trade_no, time_end)){					System.out.println("更新订单成功");					//如果该订单是幼儿产品  并且 存在代理					if(order_type == 2){						if(supplier_id != 0){							Map su = userdao.getSupplierInfo(supplier_id);							String phone = (String) su.get("phone_number");							String nickname = (String) su.get("nickname");							String app_token = (String) su.get("app_token");														String content = "【三盛科创】尊敬的"+ nickname +"您好。您在我们平台出售的商品有新用户下单。请您点击该链接查看发货信息。"+Config.WEB_SERVER+"/order/SupplierOrderInfo.do?order_number="+out_trade_no+"&sid="+app_token+".请您务必妥善包管。";							MessageUtil.SendMessage(phone,content);						}					}else{						orderdao.UpdateOrderStatus(out_trade_no, 3);//更新订单为已完成					}										return_data.put("return_code", "SUCCESS");					return_data.put("return_msg", "OK");					return WXRequestUtil.GetMapToXML(return_data);				}else{					return_data.put("return_code", "FAIL");					return_data.put("return_msg", "更新订单失败");					return WXRequestUtil.GetMapToXML(return_data);				}			}		}else{			return_data.put("return_code", "FAIL");			return_data.put("return_msg", "签名错误");		}				String xml = WXRequestUtil.GetMapToXML(return_data);		return xml;	}

微信工具类WXRequestUtil.java

package com.tenpay.util; import java.io.BufferedReader;import java.io.ByteArrayInputStream;import java.io.File;import java.io.FileInputStream;import java.io.InputStream;import java.io.InputStreamReader;import java.io.OutputStream;import java.net.ConnectException;import java.net.HttpURLConnection;import java.net.InetAddress;import java.net.URL;import java.security.KeyStore;import java.util.Date;import java.util.HashMap;import java.util.Iterator;import java.util.List;import java.util.Map;import java.util.SortedMap;import java.util.TreeMap; import javax.net.ssl.SSLContext; import org.apache.http.Consts;import org.apache.http.HttpEntity;import org.apache.http.client.methods.CloseableHttpResponse;import org.apache.http.client.methods.HttpPost;import org.apache.http.conn.ssl.SSLConnectionSocketFactory;import org.apache.http.conn.ssl.SSLContexts;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 org.glassfish.jersey.internal.util.Base64;import org.jdom.Document;import org.jdom.Element;import org.jdom.input.SAXBuilder; import com.zhiweism.util.MD5;import com.zhiweism.util.Util; /* * 用户发起统一下单请求 * 作者:董志平 * 用于发起微信扫码支付接口 */public class WXRequestUtil {	private static String WXSign = null;//	//测试	public static void main(String[] args) {		//Map<String,String> res = SendPayment("苹果","20170106113325",1,0);	}		/*	 * 发起支付请求	 * body	商品描述	 * out_trade_no	订单号	 * total_fee	订单金额		单位  元	 * product_id	商品ID	 */	public static Map<String,String> SendPayment(String body,String out_trade_no,double total_fee,int app_type){		String url = "https://api.mch.weixin.qq.com/pay/unifiedorder";		String xml = WXParamGenerate(body,out_trade_no,total_fee,app_type);		String res = httpsRequest(url,"POST",xml);		Map<String,String> data = null;		try {			data = doXMLParse(res);		} catch (Exception e) {			data = null;		}		return data;	} 		/**	 * 获取签名	 * @return	 */	public static String getWXSign() {		return WXSign;	} 	/**	 * 	获得随机字符串	 * 	 */	public static String NonceStr(){		String res = Base64.encodeAsString(Math.random()+"::"+new Date().toString()).substring(0, 30);		return res;	}		/**	 * 获取时间戳	 */	public static String GetTimeStamp(){		int t = (int)(System.currentTimeMillis()/1000);		return t+"";	}			/**	 * 	获取用户的ip	 * 	 */	 public static String GetIp() {        InetAddress ia=null;        try {            ia=InetAddress.getLocalHost();            String localip=ia.getHostAddress();            return localip;        } catch (Exception e) {            return null;        }    }	 	 /**	  * 获取签名	  * 	  */	 public static String GetSign(Map<String,String> param,int app_type){		String StringA =  Util.formatUrlMap(param, false, false);		String stringSignTemp = MD5.md5(StringA+"&key="+ConstantUtil.PARTNER_KEY[app_type]).toUpperCase();		return stringSignTemp;	 }	 	 /**	  * 	  * Map转xml数据	  */	 public static String GetMapToXML(Map<String,String> param){		 StringBuffer sb = new StringBuffer();		 sb.append("<xml>");		 for (Map.Entry<String,String> entry : param.entrySet()) { 			    sb.append("<"+ entry.getKey() +">");			    sb.append(entry.getValue());			    sb.append("</"+ entry.getKey() +">");		}  		 sb.append("</xml>");		 return sb.toString();	 }			//微信统一下单参数设置	public static String WXParamGenerate(String description,String out_trade_no,double total_fee,int app_type){		int fee = (int)(total_fee * 100.00);		Map<String,String> param = new HashMap<String,String>();		param.put("appid", ConstantUtil.APP_ID[app_type]);		param.put("mch_id", ConstantUtil.PARTNER[app_type]);		param.put("nonce_str",NonceStr());		param.put("body",description);		param.put("out_trade_no",out_trade_no);		param.put("total_fee", fee+"");		param.put("spbill_create_ip", GetIp());		param.put("notify_url", ConstantUtil.WEIXIN_NOTIFY[app_type]);		param.put("trade_type", "APP");				WXSign = GetSign(param,app_type);				param.put("sign", WXSign);		return GetMapToXML(param);	}		//发起微信支付请求    public static String httpsRequest(String requestUrl, String requestMethod, String outputStr) {        try {            URL url = new URL(requestUrl);            HttpURLConnection conn = (HttpURLConnection) url.openConnection();                      conn.setDoOutput(true);            conn.setDoInput(true);            conn.setUseCaches(false);            // 设置请求方式(GET/POST)            conn.setRequestMethod(requestMethod);            conn.setRequestProperty("content-type", "application/x-www-form-urlencoded");            // 当outputStr不为null时向输出流写数据            if (null != outputStr) {                OutputStream outputStream = conn.getOutputStream();                // 注意编码格式                outputStream.write(outputStr.getBytes("UTF-8"));                outputStream.close();            }            // 从输入流读取返回内容            InputStream inputStream = conn.getInputStream();            InputStreamReader inputStreamReader = new InputStreamReader(inputStream, "utf-8");            BufferedReader bufferedReader = new BufferedReader(inputStreamReader);            String str = null;          StringBuffer buffer = new StringBuffer();            while ((str = bufferedReader.readLine()) != null) {                buffer.append(str);            }            // 释放资源            bufferedReader.close();            inputStreamReader.close();            inputStream.close();            inputStream = null;            conn.disconnect();            return buffer.toString();        } catch (ConnectException ce) {            System.out.println("连接超时:{}"+ ce);        } catch (Exception e) {            System.out.println("https请求异常:{}"+ e);        }        return null;      }            //退款的请求方法      public static String httpsRequest2(String requestMethod, String outputStr) throws Exception {            KeyStore keyStore  = KeyStore.getInstance("PKCS12");            StringBuilder res = new StringBuilder("");            FileInputStream instream = new FileInputStream(new File("/home/apiclient_cert.p12"));            try {                keyStore.load(instream, "".toCharArray());            } finally {                instream.close();            }             // Trust own CA and all self-signed certs            SSLContext sslcontext = SSLContexts.custom()                    .loadKeyMaterial(keyStore, "1313329201".toCharArray())                    .build();             SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(                    sslcontext,                    new String[] { "TLSv1" },                    null,                    SSLConnectionSocketFactory.BROWSER_COMPATIBLE_HOSTNAME_VERIFIER);            CloseableHttpClient httpclient = HttpClients.custom()                    .setSSLSocketFactory(sslsf)                    .build();            try {                 HttpPost httpost = new HttpPost("https://api.mch.weixin.qq.com/secapi/pay/refund");                httpost.addHeader("Connection", "keep-alive");                httpost.addHeader("Accept", "*/*");                httpost.addHeader("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8");                httpost.addHeader("Host", "api.mch.weixin.qq.com");                httpost.addHeader("X-Requested-With", "XMLHttpRequest");                httpost.addHeader("Cache-Control", "max-age=0");                httpost.addHeader("User-Agent", "Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.0) ");                 StringEntity entity2 = new StringEntity(outputStr ,Consts.UTF_8);                 httpost.setEntity(entity2);                System.out.println("executing request" + httpost.getRequestLine());                 CloseableHttpResponse response = httpclient.execute(httpost);                               try {                    HttpEntity entity = response.getEntity();                                        System.out.println("----------------------------------------");                    System.out.println(response.getStatusLine());                    if (entity != null) {                        System.out.println("Response content length: " + entity.getContentLength());                        BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(entity.getContent()));                        String text = "";                      res.append(text);                        while ((text = bufferedReader.readLine()) != null) {                            res.append(text);                            System.out.println(text);                        }                                           }                    EntityUtils.consume(entity);                } finally {                    response.close();                }            } finally {                httpclient.close();            }            return  res.toString();                  }          //xml解析      public static Map<String, String> doXMLParse(String strxml) throws Exception {            strxml = strxml.replaceFirst("encoding=".*"", "encoding="UTF-8"");            if(null == strxml || "".equals(strxml)) {                return null;            }                        Map<String,String> m = new HashMap<String,String>();             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 = getChildrenText(children);                }                                m.put(k, v);            }                        //关闭流            in.close();             return m;      }          //xml解析      public static SortedMap<String, String> doXMLParseWithSorted(String strxml) throws Exception {            strxml = strxml.replaceFirst("encoding=".*"", "encoding="UTF-8"");            if(null == strxml || "".equals(strxml)) {                return null;            }                        SortedMap<String,String> m = new TreeMap<String,String>();             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 = getChildrenText(children);                }                                m.put(k, v);            }                        //关闭流            in.close();             return m;      }            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(getChildrenText(list));                    }                    sb.append(value);                    sb.append("</" + name + ">");                }            }             return sb.toString();      }}

修改微信ResponseHandler.java

package com.tenpay;  import java.io.IOException;import java.io.PrintWriter;import java.io.UnsupportedEncodingException;import java.util.Iterator;import java.util.Map;import java.util.Set;import java.util.SortedMap;import java.util.TreeMap; import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse; import com.tenpay.util.MD5Util;import com.tenpay.util.TenpayUtil; /** *  * @author miklchen * */public class ResponseHandler {  	private String key;	 	private SortedMap parameters; 			private String debugInfo;		private HttpServletRequest request;		private HttpServletResponse response;		private String uriEncoding;		/**	 * 	 * @param request	 * @param response	 */	public ResponseHandler(HttpServletRequest request,			HttpServletResponse response)  {		this.request = request;		this.response = response; 		this.key = "";		this.parameters = new TreeMap();		this.debugInfo = "";				this.uriEncoding = "";	}		/**	*/	public String getKey() {		return key;	} 	/**	*	*/	public void setKey(String key) {		this.key = key;	} 	/**	 * @param parameter 	 * @return String 	 */	public String getParameter(String parameter) {		String s = (String)this.parameters.get(parameter); 		return (null == s) ? "" : s;	}		/**	 * @param parameter	 * @param parameterValueֵ	 */	public void setParameter(String parameter, String parameterValue) {		String v = "";		if(null != parameterValue) {			v = parameterValue.trim();		}				this.parameters.put(parameter, v);	}		/**	 * 	 * @return SortedMap	 */	public SortedMap getAllParameters() {		return this.parameters;	}		public void setAllparamenters(SortedMap map){		this.parameters = map;	}		/**	 * 微信异步回调签名	 * @return boolean	 */	public boolean isTenpaySign() {		StringBuffer sb = new StringBuffer();		Set es = this.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(!"sign".equals(k) && null != v && !"".equals(v)) {				sb.append(k + "=" + v + "&");			}		}				sb.append("key="+this.getKey());		String enc = TenpayUtil.getCharacterEncoding(this.request, this.response);		String sign = MD5Util.MD5Encode(sb.toString(), enc).toLowerCase();				String tenpaySign = this.getParameter("sign").toLowerCase();				System.out.println("sign:"+sign+"      tenpaysign:"+tenpaySign);				return tenpaySign.equals(sign);	}		/**	 * 	 * @throws IOException 	 */	public void sendToCFT(String msg) throws IOException {		String strHtml = msg;		PrintWriter out = this.getHttpServletResponse().getWriter();		out.println(strHtml);		out.flush();		out.close(); 	}		/**	 *	 * @return String	 */	public String getUriEncoding() {		return uriEncoding;	} 	/**	 * @param uriEncoding	 * @throws UnsupportedEncodingException	 */	public void setUriEncoding(String uriEncoding)			throws UnsupportedEncodingException {		if (!"".equals(uriEncoding.trim())) {			this.uriEncoding = uriEncoding; 						String enc = TenpayUtil.getCharacterEncoding(request, response);			Iterator it = this.parameters.keySet().iterator();			while (it.hasNext()) {				String k = (String) it.next();				String v = this.getParameter(k);				v = new String(v.getBytes(uriEncoding.trim()), enc);				this.setParameter(k, v);			}		}	} 	/**		*/	public String getDebugInfo() {		return debugInfo;	}		/**	*	*/	protected void setDebugInfo(String debugInfo) {		this.debugInfo = debugInfo;	}		protected HttpServletRequest getHttpServletRequest() {		return this.request;	}		protected HttpServletResponse getHttpServletResponse() {		return this.response;	}	}

试试是不是已经可以发起异步回调了。记得key  在  微信支付 -》API安全  下面设置。