java实现网站微信扫码支付
程序员文章站
2023-12-22 20:25:52
一、网站微信扫码支付开发并没有现成的java示例,总结一下自己微信扫码支付心得
二、首先去微信公众平台申请账户
**
三、账户开通、开发者认证之后就...
一、网站微信扫码支付开发并没有现成的java示例,总结一下自己微信扫码支付心得
二、首先去微信公众平台申请账户
**
三、账户开通、开发者认证之后就可以进行微信支付开发了
1、微信统一下单接口调用获取预支付id,以及生成二维码所需的codeurl
/** * 保存订单,并生成二维码所需的codeurl * * @param request * @param response * @param notifyurlbuf * @param order * @return * @throws exception */ @override public map<string, string> getwechatorderinfo(string ip, cuser user, string notifyurl, order order) throws exception { map<string, string> resultmap = new hashmap<string, string>(); // 生成并保存订单 order.setuserid(user.getid()); // 支付方式 0:银联 1:支付宝 2:网上银行 3:微信 4:其他 order.setpaytype("3"); // 生成订单号 order.setorderno(ordernogenerator.getorderno()); // 订单类型 1:消费 2:退款 order.setordertype("1"); // 订单创建时间 order.setcreatetime(new date()); // 订单更新时间 order.setupdatetime(new date()); // 订单状态 0: 交易中 1:完成 2:已取消 order.setorderstatus("0"); // 付款状态 0:失败 1:成功 2、待付款 order.setpaystatus("2"); // 设置订单失效时间 calendar calendar = calendar.getinstance(); calendar.add(calendar.hour, 2); order.setexpiretime(calendar.gettime()); integer orderid = this.balancedao.saveorder(order); map<string, string> paypreidmap = new hashmap<>(); paypreidmap = wechatutil.getpaypreid(string.valueof(orderid), "体检报告", notifyurl, ip, string.valueof((order.getmoney().multiply(new bigdecimal(100)).intvalue())), orderid.tostring()); string prepayid = paypreidmap.get("prepay_id"); // 更新 order.setid(orderid); order.setprepayid(prepayid); order.setcodeurl(paypreidmap.get("code_url")); this.balancedao.updateorder(order); // return wechatutil.qrfromgoogle(order.getcodeurl(), 300, 0); resultmap.put("codeurl", order.getcodeurl()); resultmap.put("orderid", string.valueof(order.getid())); return resultmap; }
此方法返回的数据如下
<xml><return_code><![cdata[success]]></return_code> <return_msg><![cdata[ok]]></return_msg> <appid><![cdata[wxaf0b*****8afbf]]></appid> <mch_id><![cdata[1408****02]]></mch_id> <nonce_str><![cdata[zf0vgvdtvycbliwb]]></nonce_str> <sign><![cdata[a2910f16086211153d747058063b3368]]></sign> <result_code><![cdata[success]]></result_code> <prepay_id><![cdata[wx201701191109388037e9a12310276591827]]></prepay_id> <trade_type><![cdata[native]]></trade_type> <code_url><![cdata[weixin://wxpay/bizpayurl?pr=1ujornx]]></code_url> </xml>
2、服务器端接受微信支付结果通知
/** * 保存微信通知结果 * * @param request * @param response * @return * @throws exception */ @override public string savewechatnotify(string notifyinfoxml) throws exception { map<string, string> noticemap = xmlutil.doxmlparse(notifyinfoxml); // 这个其实是订单 的id string outtradeno = noticemap.get("out_trade_no"); order order = this.balancedao.getorderbyid(integer.valueof(outtradeno)); // 如果支付通知信息不为,说明请求已经处理过,直接返回 if (stringutil.isnotempty(order.getnotifyinfo())) { return "success"; } string sign = noticemap.get("sign"); noticemap.remove("sign"); // 验签通过 if (wechatutil.getsignveryfy(noticemap, sign)) { // 通信成功此字段是通信标识,非交易标识,交易是否成功需要查看result_code来判断 if ("success".equals(noticemap.get("return_code"))) { // 交易成功 if ("success".equals(noticemap.get("result_code"))) { // 商户订单号 // 订单更新时间 order.setupdatetime(new date()); // ------------------------------ // 处理业务开始 // ------------------------------ // 是否交易成功,1:成功0:失败 // 微信支付成功 order.setpaystatus("1"); // 订单状态 0: 交易中 1:完成 2:已取消 order.setorderstatus("1"); // 保存通知信息 order.setnotifyinfo(notifyinfoxml); this.balancedao.updateorder(order); // 处理业务完毕 } else { // 错误时,返回结果未签名,记录retcode、retmsg看失败详情。 logger.info("查询验证签名失败或业务错误"); logger.info("retcode:" + noticemap.get("retcode") + " retmsg:" + noticemap.get("retmsg")); } return "success"; } else { logger.info("后台调用通信失败"); } return "success"; } else { logger.info("通知签名验证失败"); } return null; }
3、上面代码用到的工具方法都在wechatutil.java工具类中
package com.caifu.tencent.common; import java.io.ioexception; import java.net.urisyntaxexception; import java.net.urlencoder; import java.util.arraylist; import java.util.collections; import java.util.hashmap; import java.util.linkedlist; import java.util.list; import java.util.map; import java.util.random; import org.apache.commons.httpclient.httpstatus; import org.apache.http.namevaluepair; import org.apache.http.client.config.requestconfig; import org.apache.http.client.methods.closeablehttpresponse; import org.apache.http.client.methods.httppost; import org.apache.http.client.utils.uribuilder; import org.apache.http.entity.stringentity; import org.apache.http.impl.client.closeablehttpclient; import org.apache.http.impl.client.httpclients; import org.apache.http.message.basicnamevaluepair; import org.apache.http.util.entityutils; import org.jdom2.jdomexception; import org.slf4j.logger; import org.slf4j.loggerfactory; import com.caifu.login.utils.xmlutil; public class wechatutil { private static logger logger = loggerfactory.getlogger(wechatutil.class); public static final string tag = "wechat.util"; private static final int timeout = 5000; public static byte[] httppost(string url, string entity) throws urisyntaxexception, ioexception { if (url == null || url.length() == 0) { logger.info(tag, "httppost, url is null"); return null; } closeablehttpclient httpclient = httpclients.createdefault(); uribuilder uribuilder = new uribuilder(url); httppost httppost = new httppost(uribuilder.build()); requestconfig requestconfig = requestconfig.custom().setsockettimeout(timeout).setconnectionrequesttimeout(timeout).setconnecttimeout(timeout).build(); httppost.setconfig(requestconfig); // 避免汉字乱码导致请求失败, httppost.setentity(new stringentity(entity, "utf-8")); closeablehttpresponse resp = null; try { resp = httpclient.execute(httppost); if (resp.getstatusline().getstatuscode() != httpstatus.sc_ok) { logger.info(tag, "httpget fail, status code = " + resp.getstatusline().getstatuscode()); return null; } return entityutils.tobytearray(resp.getentity()); } catch (exception e) { logger.info(tag, "httppost exception, e = " + e.getmessage()); e.printstacktrace(); return null; } finally { if (httpclient != null) { httpclient.close(); } if (resp != null) { resp.close(); } } } /** * 把数组所有元素排序,并按照“参数=参数值”的模式用“&”字符拼接成字符串 * * @param params * 需要排序并参与字符拼接的参数组 * @return 拼接后字符串 */ public static string createlinkstring(map<string, string> params) { list<string> keys = new arraylist<string>(params.keyset()); collections.sort(keys); string prestr = ""; for (int i = 0; i < keys.size(); i++) { string key = keys.get(i); string value = params.get(key); if (i == keys.size() - 1) {// 拼接时,不包括最后一个&字符 prestr = prestr + key + "=" + value; } else { prestr = prestr + key + "=" + value + "&"; } } return prestr; } /** * 根据反馈回来的信息,生成签名结果 * * @param params * 通知返回来的参数数组 * @param sign * 比对的签名结果 * @return 生成的签名结果 */ public static boolean getsignveryfy(map<string, string> params, string sign) { // 过滤空值、sign与sign_type参数 // map<string, string> sparanew = alipaycore.parafilter(params); // 获取待签名字符串 string presignstr = createlinkstring(params); presignstr += "&key=" + configure.getkey(); // 获得签名验证结果 string resultsign = md5.md5encode(presignstr).touppercase(); // string resultsign = md5util.md5encode(presignstr.tostring(), // "utf-8").tolowercase(); if (sign.equals(resultsign)) { return true; } else { return false; } } /** * 装配xml,生成请求prepayid所需参数 * * @param params * @return */ public static string toxml(list<namevaluepair> params) { stringbuilder sb = new stringbuilder(); sb.append("<xml>"); for (int i = 0; i < params.size(); i++) { sb.append("<" + params.get(i).getname() + ">"); sb.append(params.get(i).getvalue()); sb.append("</" + params.get(i).getname() + ">"); } sb.append("</xml>"); return sb.tostring(); } /** * 生成签名 */ public static string genpackagesign(list<namevaluepair> params) { stringbuilder sb = new stringbuilder(); for (int i = 0; i < params.size(); i++) { sb.append(params.get(i).getname()); sb.append('='); sb.append(params.get(i).getvalue()); sb.append('&'); } sb.append("key="); sb.append(configure.getkey()); string packagesign = md5.md5encode(sb.tostring()); return packagesign; } /** * * @param goodorderno * @param body * @param noticeurl * @param ip * @param totalfee * @return */ public static string genproductargs(string goodorderno, string body, string noticeurl, string ip, string totalfee, string productid) { stringbuffer xml = new stringbuffer(); try { string noncestr = getnoncestr(); xml.append("</xml>"); list<namevaluepair> packageparams = new linkedlist<namevaluepair>(); packageparams.add(new basicnamevaluepair("appid", configure.getappid())); packageparams.add(new basicnamevaluepair("body", body)); packageparams.add(new basicnamevaluepair("mch_id", configure.getmchid())); packageparams.add(new basicnamevaluepair("nonce_str", noncestr)); packageparams.add(new basicnamevaluepair("notify_url", noticeurl)); packageparams.add(new basicnamevaluepair("out_trade_no", goodorderno)); packageparams.add(new basicnamevaluepair("product_id", productid)); packageparams.add(new basicnamevaluepair("spbill_create_ip", ip)); packageparams.add(new basicnamevaluepair("total_fee", totalfee)); packageparams.add(new basicnamevaluepair("trade_type", "native")); string sign = genpackagesign(packageparams); packageparams.add(new basicnamevaluepair("sign", sign)); string xmlstring = toxml(packageparams); return xmlstring; } catch (exception e) { logger.info("genproductargs fail, ex = " + e.getmessage()); return null; } } /** * 生成支付签名 * * @param params * @return */ public static string genappsign(list<namevaluepair> params) { stringbuilder sb = new stringbuilder(); for (int i = 0; i < params.size(); i++) { sb.append(params.get(i).getname()); sb.append('='); sb.append(params.get(i).getvalue()); sb.append('&'); } sb.append("key="); sb.append(configure.getkey()); string appsign = md5.md5encode(sb.tostring()).touppercase(); logger.info("orion", appsign); return appsign; } /** * 生成调用微信支付所需参数 * * @param prepayid * @return */ public static map<string, string> genpayreq(string prepayid) { map<string, string> resultmap = new hashmap<string, string>(); string timestamp = gettimestamp(); string noncestr = getnoncestr(); list<namevaluepair> signparams = new linkedlist<namevaluepair>(); signparams.add(new basicnamevaluepair("appid", configure.getappid())); signparams.add(new basicnamevaluepair("noncestr", noncestr)); signparams.add(new basicnamevaluepair("package", "sign=wxpay")); signparams.add(new basicnamevaluepair("partnerid", configure.getmchid())); signparams.add(new basicnamevaluepair("prepayid", prepayid)); signparams.add(new basicnamevaluepair("timestamp", timestamp)); string sign = genappsign(signparams); resultmap.put("appid", configure.getappid()); resultmap.put("noncestr", noncestr); resultmap.put("packagevalue", "sign=wxpay"); resultmap.put("partnerid", configure.getmchid()); resultmap.put("prepayid", prepayid); resultmap.put("timestamp", timestamp); resultmap.put("sign", sign); return resultmap; } /** * 微信支付生成预支付订单 * * @throws ioexception * @throws jdomexception */ public static map<string, string> getpaypreid(string goodorderno, string body, string noticeurl, string ip, string totalfee, string productid) throws exception { string paramsxml = genproductargs(goodorderno, body, noticeurl, ip, totalfee, productid); logger.info("orion", paramsxml); byte[] buf = wechatutil.httppost(configure.unifiedorder_api, paramsxml); string contentxml = new string(buf); map<string, string> resultmap = xmlutil.doxmlparse(contentxml); return resultmap; } public static string getnoncestr() { random random = new random(); return md5.md5encode(string.valueof(random.nextint(10000))); } public static string gettimestamp() { return string.valueof(system.currenttimemillis() / 1000); } /** * 生成支付二维码 * @param request * @param response * @param width * @param height * @param text 微信生成预定id时,返回的codeurl */ public static void getqrcode(httpservletrequest request, httpservletresponse response, integer width, integer height, string text) { if (width == null) { width = 300; } if (height == null) { height = 300; } string format = "jpg"; hashtable hints = new hashtable(); hints.put(encodehinttype.character_set, "utf-8"); bitmatrix bitmatrix; try { bitmatrix = new multiformatwriter().encode(text, barcodeformat.qr_code, width, height, hints); matrixtoimagewriter.writetostream(bitmatrix, format, response.getoutputstream()); } catch (writerexception e) { e.printstacktrace(); } catch (ioexception e) { // todo auto-generated catch block e.printstacktrace(); } } }
生成二维码需要两jar
<!-- google zxing 二维码jar begin --> <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.0</version> </dependency> <!-- google zxing 二维码jar begin -->
4、下面是用到的配置类
package com.caifu.tencent.common; /** * user: rizenguo * date: 2014/10/29 * time: 14:40 * 这里放置各种配置数据 */ public class configure { //这个就是自己要保管好的私有key了(切记只能放在自己的后台代码里,不能放在任何可能被看到源代码的客户端程序中) // 每次自己post数据给api的时候都要用这个key来对所有字段进行签名,生成的签名会放在sign这个字段,api收到post数据的时候也会用同样的签名算法对post过来的数据进行签名和验证 // 收到api的返回的时候也要用这个key来对返回的数据算下签名,跟api的sign数据进行比较,如果值不一致,有可能数据被第三方给篡改 private static string key = "a6gb0dy4dsfdssupcpsdfdshkscdqcr3exs"; private static string appsecret="7584sdfdsfe4f26fadsfsdfs56f10728a"; //微信分配的公众号id(开通公众号之后可以获取到) private static string appid = "wxaf0b86sdfsdf8afbf"; //微信支付分配的商户号id(开通公众号的微信支付功能之后可以获取到) private static string mchid = "14012313702"; //受理模式下给子商户分配的子商户号 private static string submchid = ""; //https证书的本地路径 private static string certlocalpath = ""; //https证书密码,默认密码等于商户号mchid private static string certpassword = ""; //是否使用异步线程的方式来上报api测速,默认为异步模式 private static boolean usethreadtodoreport = true; //机器ip private static string ip = ""; //以下是几个api的路径: //1)被扫支付api public static string unifiedorder_api = "https://api.mch.weixin.qq.com/pay/unifiedorder"; public static string pay_api = "https://api.mch.weixin.qq.com/pay/micropay"; //2)被扫支付查询api public static string pay_query_api = "https://api.mch.weixin.qq.d/pay/orderquery"; //3)退款api public static string refund_api = "https://api.mch.weixin.qq.com/secapi/pay/refund"; //4)退款查询api public static string refund_query_api = "https://api.mch.weixin.qq.com/pay/refundquery"; //5)撤销api public static string reverse_api = "https://api.mch.weixin.qq.com/secapi/pay/reverse"; //6)下载对账单api public static string download_bill_api = "https://api.mch.weixin.qq.com/pay/downloadbill"; //7) 统计上报api public static string report_api = "https://api.mch.weixin.qq.com/payitil/report"; public static boolean isusethreadtodoreport() { return usethreadtodoreport; } public static void setusethreadtodoreport(boolean usethreadtodoreport) { configure.usethreadtodoreport = usethreadtodoreport; } public static string httpsrequestclassname = "com.tencent.common.httpsrequest"; public static void setkey(string key) { configure.key = key; } public static void setappid(string appid) { configure.appid = appid; } public static void setmchid(string mchid) { configure.mchid = mchid; } public static void setsubmchid(string submchid) { configure.submchid = submchid; } public static void setcertlocalpath(string certlocalpath) { configure.certlocalpath = certlocalpath; } public static void setcertpassword(string certpassword) { configure.certpassword = certpassword; } public static void setip(string ip) { configure.ip = ip; } public static string getkey(){ return key; } public static string getappid(){ return appid; } public static string getmchid(){ return mchid; } public static string getsubmchid(){ return submchid; } public static string getcertlocalpath(){ return certlocalpath; } public static string getcertpassword(){ return certpassword; } public static string getip(){ return ip; } public static void sethttpsrequestclassname(string name){ httpsrequestclassname = name; } }
在这里需要注意的配置
private static string key = “a6gb0dy4dsfdssupcpsdfdshkscdqcr3exs”;
这里的key 是登陆 (微信商户平台)设置的api_key
5、xml 解析工具类
package com.caifu.login.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 javax.servlet.http.httpservletrequest; import org.dom4j.io.saxreader; import org.jdom2.document; import org.jdom2.element; import org.jdom2.jdomexception; import org.jdom2.input.saxbuilder; /** * xml工具类 * * @author miklchen * */ 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(); } /** * 将requestxml通知结果转出啊成map * @param request * @return * @throws exception */ public static map<string, string> parsexml(httpservletrequest request) throws exception { // 解析结果存储在hashmap map<string, string> map = new hashmap<string, string>(); inputstream inputstream = request.getinputstream(); // 读取输入流 saxreader reader = new saxreader(); org.dom4j.document document = reader.read(inputstream); // 得到xml根元素 org.dom4j.element root = document.getrootelement(); // 得到根元素的所有子节点 list<org.dom4j.element> elementlist = root.elements(); // 遍历所有子节点 for (org.dom4j.element e : elementlist) map.put(e.getname(), e.gettext()); // 释放资源 inputstream.close(); inputstream = null; return map; } }
6、整个后台服务已经完成,最后关闭页面微信支付二维码,告知用户支付已经完成了
var f; /* 定时任务方法,异步请求去查询订单是否支付*/ function getorder() { var orderid = $('#orderid').val(); if (orderid != '') { $.ajax({ url : "${base}/balance/auth/ispay?orderid=" + orderid, type : "get", async : false, success : function(d) { if (d == "1") { //当获取到微信支付结果时,关闭二维码div $(".weixinpay").css("display", "none"); $("#zhichutankuang").css("display", "block"); ////当获取到微信支付结果时,关闭定时任务 clearinterval(f); // layer.alert('付款成功', { // skin : 'layui-layer-molv', // 样式类名 // closebtn : 0 // }, function() { // location.href = "${base}/balance/auth/presentation?tjno=" + $("#tjno").val(); // }); } } }); } } //异步请求获取生成二维码的url $(".paylast").click(function() { var $paytype = $('input:radio:checked').val(); var $money = $("#money").val(); var $tjreporttype = $("#tjreporttype").val(); var $tjno = $("#tjno").val(); $.ajax({ url : "${base}/balance/auth/wechatinfo", type : "post", async : false, data : { paytype : $paytype, money : $money, tjno : $tjno, tjreporttype : $tjreporttype }, success : function(d) { if (d.resultcode == "1000") { //当请求成功时,设置二维码图片地址 $("#codeimg").attr('src', d.obj); $("#orderid").val(d.attributes.orderid); ////当请求成功时,启动定时任务,每隔3秒去后台查询一次订单是否成功 f = setinterval(getorder, 3000); // getorder(true); } } }); $(".selpaycon").css("display", "none"); $(".weixinpay").css("display", "block"); });
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。