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.javapackage 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.javapackage 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安全 下面設置。
新聞熱點
疑難解答