微信支付官方文档给的太乱并且还居然有好多错误,导致好多初学者走了好多弯路,甚至摸不着头脑。这节给大家介绍代码+解说并附有源码(文章底部有下载按钮)。
目前微信支付最新版本为V3版本,官方并且有几种语言版本,其中就有java版本,下载地址为:
https://mp.weixin.qq.com/paymch/readtemplate?t=mp/business/course2_tmpl
微信支付成功后 你的邮件会有以下信息:
1、 信息包括:商户ID(mch_id)、申请编号、登录帐号、登录密码、商户API密码(key)
2.、证书包括:商户API证书、证书密钥、CA证书
开发前,我们先登录自己的服务号,点击微信支付----->开发配置。
如果这里的授权路径和下面参数的notify_url不对 调用付款接口时会弹出access_denied。
比如,我的授权目录是http://183.33.212.175/wxweb/config/,那么我对应的notify_url的回调方法必须是String notify_url = "http://183.33.212.175:8016/wxweb/config/pay!paySuccess.action";这样的
二,代码示例
1.请求前的拼包
/ api支付拼包------------------------------------------------------------------------------ RequestHandler reqHandler = new RequestHandler(request, response); // TenpayHttpClient httpClient = new TenpayHttpClient(); // TreeMap<String, String> outParams = new TreeMap<String,String>(); // 初始化 reqHandler.init(); reqHandler.init(APP_ID,APP_SECRET, APP_KEY,PARTNER_KEY); // 当前时间 yyyyMMddHHmmss String currTime = TenpayUtil.getCurrTime(); // 8位日期 String strTime = currTime.substring(8, currTime.length()); // 四位随机数 String strRandom = TenpayUtil.buildRandom(4) + ""; // 10位序列号,可以自行调整。 String strReq = strTime + strRandom; // 订单号,此处用时间加随机数生成,商户根据自己情况调整,只要保持全局唯一就行 String out_trade_no = strReq; // 设置package订单参数 SortedMap<String, String> packageParams = new TreeMap<String, String>(); packageParams.put("bank_type", "WX"); // 支付类型 packageParams.put("body", "商品名称"); // 商品描述 packageParams.put("fee_type", "1"); // 银行币种 packageParams.put("input_charset", "UTF-8"); // 字符集 packageParams.put("notify_url", "通知地址,接收交易结果,并进行业务处理,例如:http://abc.com/buy/buy.do"); // 通知地址 packageParams.put("out_trade_no", out_trade_no); // 商户订单号 packageParams.put("partner", PARTNER_ID); // 设置商户号 packageParams.put("total_fee", "交易金额"); // 商品总金额,以分为单位 packageParams.put("spbill_create_ip", request.getRemoteAddr()); // 订单生成的机器IP,指用户浏览器端IP // 获取package包 String packageValue = reqHandler.genPackage(packageParams); String noncestr = Sha1Util.getNonceStr(); String timestamp = Sha1Util.getTimeStamp(); // 设置支付参数 SortedMap<String, String> signParams = new TreeMap<String, String>(); signParams.put("appid", appid); signParams.put("noncestr", noncestr); signParams.put("package", packageValue); signParams.put("timestamp", timestamp); signParams.put("appkey", appkey); // 生成支付签名,要采用URLENCODER的原始值进行SHA1算法! String sign = Sha1Util.createSHA1Sign(signParams); // 增加非参与签名的额外参数 signParams.put("paySign", sign); signParams.put("signType", "SHA1"); // ------------------------------------微信js // api支付拼包结束------------------------------------ // --------------------------------本地系统生成订单------------------------------------- // // --------------------------------生成完成--------------------------------------------- request.setAttribute("appid", appid); request.setAttribute("timestamp", timestamp); request.setAttribute("noncestr", noncestr); request.setAttribute("package", packageValue); request.setAttribute("paysign", sign); request.setAttribute("czje", czje);
英文全大写带下划线的,各位申请微信支付的时候都会给你的。
注意:微信demo里面RequestHandler 在设置商户key和appkey有点问题,ResponseHandler连postData都没获取到,下面把我修正后的贴出来做参考(注:其它java类没什么问题)
RequestHandler:
package com.zk.common.wxpay; import java.io.UnsupportedEncodingException; import java.net.URLEncoder; 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.zk.common.wxpay.util.MD5Util; import com.zk.common.wxpay.util.TenpayUtil; /* '微信支付服务器签名支付请求请求类 '============================================================================ 'api说明: 'init(app_id, app_secret, partner_key, app_key); '初始化函数,默认给一些参数赋值,如cmdno,date等。 'setKey(key_)'设置商户密钥 'getLasterrCode(),获取最后错误号 'GetToken();获取Token 'getTokenReal();Token过期后实时获取Token 'createMd5Sign(signParams);生成Md5签名 'genPackage(packageParams);获取package包 'createSHA1Sign(signParams);创建签名SHA1 'sendPrepay(packageParams);提交预支付 'getDebugInfo(),获取debug信息 '============================================================================ '*/ public class RequestHandler { /** Token获取网关地址地址 */ private String tokenUrl; /** 预支付网关url地址 */ private String gateUrl; /** 查询支付通知网关URL */ private String notifyUrl; /** 商户参数 */ private String appid; private String appkey; private String partnerkey; private String appsecret; private String key; /** 请求的参数 */ private SortedMap parameters; /** Token */ private String Token; private String charset; /** debug信息 */ private String debugInfo; private String last_errcode; private HttpServletRequest request; private HttpServletResponse response; /** * 初始构造函数。 * * @return */ public RequestHandler(HttpServletRequest request, HttpServletResponse response) { this.last_errcode = "0"; this.request = request; this.response = response; this.charset = "GBK"; this.parameters = new TreeMap(); // 验证notify支付订单网关 notifyUrl = "https://gw.tenpay.com/gateway/simpleverifynotifyid.xml"; } /** * 初始化函数。 */ public void init(String app_id, String app_secret, String app_key, String partner_key) { this.last_errcode = "0"; this.Token = "token_"; this.debugInfo = ""; this.appkey = app_key; this.appid = app_id; this.partnerkey = partner_key; this.key = partner_key;//原demo没有,手动加上 this.appsecret = app_secret; } public void init() { } /** * 获取最后错误号 */ public String getLasterrCode() { return last_errcode; } /** *获取入口地址,不包含参数值 */ public String getGateUrl() { return gateUrl; } /** * 获取参数值 * * @param parameter * 参数名称 * @return String */ public String getParameter(String parameter) { String s = (String) this.parameters.get(parameter); return (null == s) ? "" : s; } //设置密钥 public void setKey(String key) { this.partnerkey = key; } //设置微信密钥 public void setAppKey(String key){ this.appkey = key; } // 特殊字符处理 public String UrlEncode(String src) throws UnsupportedEncodingException { return URLEncoder.encode(src, this.charset).replace("+", "%20"); } // 获取package的签名包 public String genPackage(SortedMap<String, String> packageParams) throws UnsupportedEncodingException { String sign = createSign(packageParams); 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(); sb.append(k + "=" + UrlEncode(v) + "&"); } // 去掉最后一个& String packageValue = sb.append("sign=" + sign).toString(); System.out.println("packageValue=" + packageValue); return packageValue; } /** * 创建md5摘要,规则是:按参数名称a-z排序,遇到空值的参数不参加签名。 */ public String createSign(SortedMap<String, String> packageParams) { 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=" + this.getKey()); System.out.println("md5 sb:" + sb); String sign = MD5Util.MD5Encode(sb.toString(), this.charset) .toUpperCase(); return sign; } /** * 创建package签名 */ public boolean createMd5Sign(String signParams) { 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 + "&"); } } // 算出摘要 String enc = TenpayUtil.getCharacterEncoding(this.request, this.response); String sign = MD5Util.MD5Encode(sb.toString(), enc).toLowerCase(); String tenpaySign = this.getParameter("sign").toLowerCase(); // debug信息 this.setDebugInfo(sb.toString() + " => sign:" + sign + " tenpaySign:" + tenpaySign); return tenpaySign.equals(sign); } //输出XML public String parseXML() { StringBuffer sb = new StringBuffer(); sb.append("<xml>"); 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(null != v && !"".equals(v) && !"appkey".equals(k)) { sb.append("<" + k +">" + getParameter(k) + "</" + k + ">\n"); } } sb.append("</xml>"); return sb.toString(); } /** * 设置debug信息 */ protected void setDebugInfo(String debugInfo) { this.debugInfo = debugInfo; } public void setPartnerkey(String partnerkey) { this.partnerkey = partnerkey; } public String getDebugInfo() { return debugInfo; } public String getKey() { return key; } }
ResponseHandler:
package com.zk.common.wxpay; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.io.PrintWriter; import java.io.Reader; import java.io.UnsupportedEncodingException; import java.util.ArrayList; import java.util.Iterator; import java.util.Map; import java.util.Set; import java.util.SortedMap; import java.util.TreeMap; import com.zk.common.Parameters; import com.zk.common.wxpay.util.Sha1Util; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.dom4j.Document; import org.dom4j.DocumentHelper; import org.dom4j.Element; import org.jdom.JDOMException; import com.sun.org.apache.xalan.internal.xsltc.runtime.Hashtable; import com.zk.common.wxpay.util.MD5Util; import com.zk.common.wxpay.util.TenpayUtil; import com.zk.common.wxpay.util.XMLUtil; /** * 微信支付服务器签名支付请求应答类 api说明: getKey()/setKey(),获取/设置密钥 * getParameter()/setParameter(),获取/设置参数值 getAllParameters(),获取所有参数 * isTenpaySign(),是否财付通签名,true:是 false:否 getDebugInfo(),获取debug信息 */ public class ResponseHandler { private static final String appkey = APP_KEY; /** 密钥 */ private String key; /** 应答的参数 */ private SortedMap parameters; /** debug信息 */ private String debugInfo; private HttpServletRequest request; private HttpServletResponse response; private String uriEncoding; private Hashtable xmlMap; private SortedMap smap; public SortedMap getSmap() { return smap; } private String k; /** * 构造函数 * * @param request * @param response */ public ResponseHandler(HttpServletRequest request, HttpServletResponse response) { this.request = request; this.response = response; this.smap = new TreeMap<String, String>(); this.key = ""; this.parameters = new TreeMap(); this.debugInfo = ""; this.uriEncoding = ""; Map m = this.request.getParameterMap(); Iterator it = m.keySet().iterator(); while (it.hasNext()) { String k = (String) it.next(); String v = ((String[]) m.get(k))[0]; this.setParameter(k, v); } BufferedReader reader =null; try{ reader = new BufferedReader(new InputStreamReader(request.getInputStream())); StringBuilder sb = new StringBuilder(); String line = null; while ((line = reader.readLine()) != null) { sb.append(line); } Document doc = DocumentHelper.parseText(sb.toString()); Element root = doc.getRootElement(); for (Iterator iterator = root.elementIterator(); iterator.hasNext();) { Element e = (Element) iterator.next(); smap.put(e.getName(), e.getText()); } }catch (Exception e) { // TODO: handle exception e.printStackTrace(); } } /** * 获取密钥 */ 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 doParse(String xmlContent) throws JDOMException, IOException { this.parameters.clear(); // 解析xml,得到map Map m = XMLUtil.doXMLParse(xmlContent); // 设置参数 Iterator it = m.keySet().iterator(); while (it.hasNext()) { String k = (String) it.next(); String v = (String) m.get(k); this.setParameter(k, v); } } /** * 是否财付通签名,规则是:按参数名称a-z排序,遇到空值的参数不参加签名。 * * @return boolean */ public boolean isValidSign() { 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 ValidSign = this.getParameter("sign").toLowerCase(); // debug信息 this.setDebugInfo(sb.toString() + " => sign:" + sign + " ValidSign:" + ValidSign); System.out.println("财付通签名:"+this.getDebugInfo()); return ValidSign.equals(sign); } /** * 判断微信签名 */ public boolean isWXsign() { StringBuffer sb = new StringBuffer(); String keys = ""; SortedMap<String, String> signParams = new TreeMap<String, String>(); Set es = this.smap.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 (k != "SignMethod" && k != "AppSignature") { signParams.put(k.toLowerCase(), v); } } signParams.put("appkey", this.appkey); Set set = signParams.entrySet(); Iterator pit = set.iterator(); while (pit.hasNext()) { Map.Entry entry = (Map.Entry) pit.next(); String k = (String) entry.getKey(); String v = (String) entry.getValue(); if (sb.length() == 0) { sb.append(k + "=" + v); } else { sb.append("&" + k + "=" + v); } } String sign = Sha1Util.getSha1(sb.toString()).toString().toLowerCase(); this.setDebugInfo(sb.toString() + " => SHA1 sign:" + sign); return sign.equals(this.smap.get("AppSignature")); } // 判断微信维权签名 public boolean isWXsignfeedback() { StringBuffer sb = new StringBuffer(); Hashtable signMap = new Hashtable(); 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 (k != "SignMethod" && k != "AppSignature") { sb.append(k + "=" + v + "&"); } } signMap.put("appkey", this.appkey); // ArrayList akeys = new ArrayList(); // akeys.Sort(); while (it.hasNext()) { String v = k; if (sb.length() == 0) { sb.append(k + "=" + v); } else { sb.append("&" + k + "=" + v); } } String sign = Sha1Util.getSha1(sb.toString()).toString().toLowerCase(); this.setDebugInfo(sb.toString() + " => SHA1 sign:" + sign); return sign.equals(this.smap.get("AppSignature")); } /** * 返回处理结果给财付通服务器。 * * @param msg * Success or fail * @throws IOException */ public void sendToCFT(String msg) throws IOException { String strHtml = msg; PrintWriter out = this.getHttpServletResponse().getWriter(); out.println(strHtml); out.flush(); out.close(); } /** * 获取uri编码 * * @return String */ public String getUriEncoding() { return uriEncoding; } /** * 设置uri编码 * * @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); } } } /** * 获取debug信息 */ public String getDebugInfo() { return debugInfo; } /** * 设置debug信息 */ protected void setDebugInfo(String debugInfo) { this.debugInfo = debugInfo; } protected HttpServletRequest getHttpServletRequest() { return this.request; } protected HttpServletResponse getHttpServletResponse() { return this.response; } }
2.Jsapi调用
在第一步中已拼好包放入request中了,第二部js调用微信api,看代码:
<script type="text/javascript"> function callpay(){ WeixinJSBridge.invoke('getBrandWCPayRequest',{ "appId" : "${appid}","timeStamp" : "${timestamp}", "nonceStr" : "${noncestr}", "package" : "${package}","signType" : "SHA1", "paySign" : "${paysign}" },function(res){ WeixinJSBridge.log(res.err_msg); if(res.err_msg == "get_brand_wcpay_request:ok"){ alert("微信支付成功"); }else if(res.err_msg == "get_brand_wcpay_request:cancel"){ alert("用户取消支付"); }else{ alert("支付失败"); } }); } </script>
点击支付按钮,调用callpay 即可。
3.微信支付后的通知接口
支付成功了,微信根据你提供的notify_url,来通知你支付成功与否,虽然js里知道了结果,但这里需要正式通知,并处理业务,下面是我的代码片段:
System.out.println("-----------------微信支付来消息啦--------------------------"); ResponseHandler resHandler = new ResponseHandler(request,response); resHandler.setKey(PARTNER_KEY); if (resHandler.isValidSign() == true) { if (resHandler.isWXsign() == true) { // 商户订单号 String out_trade_no = resHandler .getParameter("out_trade_no"); // 财付通订单号 String transaction_id = resHandler .getParameter("transaction_id"); // 金额,以分为单位 String total_fee = resHandler.getParameter("total_fee"); // 如果有使用折扣券,discount有值,total_fee+discount=原请求的total_fee String discount = resHandler.getParameter("discount"); // 支付结果 String trade_state = resHandler .getParameter("trade_state"); System.out.println("支付的订单号:" + out_trade_no); // 判断签名及结果 if ("0".equals(trade_state)) { System.out.println("哈哈,微信支付成功了"); // ------------------------------ // 即时到账处理业务开始 // ------------------------------ // 处理数据库逻辑 // 注意交易单不要重复处理 // 注意判断返回金额 // ------------------------------ // 即时到账处理业务完毕 // ------------------------------ System.out.println("success 后台通知成功"); // 给财付通系统发送成功信息,财付通系统收到此结果后不再进行后续通知 resHandler.sendToCFT("success"); } else {// sha1签名失败 System.out.println("fail -SHA1 failed"); resHandler.sendToCFT("fail"); } } else {// MD5签名失败 System.out.println("fail -Md5 failed"); } }
到了这里,支付的整个流程就结束了,如果读者按照步骤来,是可以能成功做出微信公众号支付的。
感觉本站内容不错,读后有收获?小额赞助,鼓励网站分享出更好的教程