本周的工作安排就是在网站添加支付功能,包括支付宝、微信、财付通等,今天这篇文章先说一下微信支付吧,支付宝的太容易了基本看一下都会了
1、首先准备工作,开通了支付功能的账号,具体怎么开通的我也不熟悉因为我做的时候都是使用开通好的账号(目前微信支付好像只支持商家不支持个人吧?)
2、按照我的性格当然是百度一下啦,但是微信支付的文档说明事例实在是太坑了,完全不是我这种小白可以一下看懂的,在这夸奖一下支付宝,支付宝的事例文档做的太好了基本做的时候没什么坑
准备工作结束开工:
我的开发环境是java,所以去官方找代码不得不说太TM坑了 java版本的好像是维护的最不好的,人家其他的那么多
你看java的 不说了,没有扫码支付的,但是我这次使用的肯定是扫码啊 ,只好继续百度了。
找到了这个项目,是git上一个人维护的,我看了一下基本符合我的需求,就按照这个改吧
WxPayDto.java是微信发送消息的实体类
WxPayResult.java是接收到回调消息的实体类
具体的操作看下面:
具体的操作之前需要先得到几个必须的参数
//微信支付商户开通后 微信会提供appid和appsecret和商户号partner private static String appid = ""; private static String appsecret = ""; private static String partner = ""; //这个参数partnerkey是在商户后台配置的一个32位的key,微信商户平台-账户设置-安全设置-api安全 private static String partnerkey = ""; //openId 是微信用户针对公众号的标识,授权的部分这里不解释 private static String openId = ""; //微信支付成功后通知地址 必须要求80端口并且地址不能带参数 private static String notifyurl = "";
参数什么的都是直接客户提供给我的,你们如果是自己开发获取这些参数的时候可能不知道具体位置,我建议你百度一下因为如果参数都不知道怎么获取还谈什么开发接口啊
这里有几点需要说明一下的
openid我没设置 不知道是干什么 随便填了成了客户商户ID反正也没报错。
notifyurl 是交易成功后回调的地址,需要在公众账号的后台设置一下,否则即使交易成功返回信息也是错误的。 微信支付的步骤是 获取交易的二维码,这个二维码是通过你传入的商品ID 价格等信息 微信生成的,用户扫码后启动微信的支付客户端,支付成功后回调你设置的回调地址那第一步获取微信支付的二维码看代码:
//扫码支付 WxPayDto tpWxPay1 = new WxPayDto(); tpWxPay1.setBody(gp.getPackname()); tpWxPay1.setOrderId(of.getOrder_id()); tpWxPay1.setSpbillCreateIp(request.getRemoteAddr()); tpWxPay1.setTotalFee(gp.getFee()); mv.addObject("txcode", getCodeurl(tpWxPay1));
public String getCodeurl(WxPayDto tpWxPayDto){ // 1 参数 // 订单号 String orderId = tpWxPayDto.getOrderId(); // 附加数据 原样返回 String attach = ""; // 总金额以分为单位,不带小数点 String totalFee = getMoney(tpWxPayDto.getTotalFee()); // 订单生成的机器 IP String spbill_create_ip = tpWxPayDto.getSpbillCreateIp(); // 这里notify_url是 支付完成后微信发给该链接信息,可以判断会员是否支付成功,改变订单状态等。 String notify_url = notifyurl; String trade_type = "NATIVE"; // 商户号 String mch_id = partner; // 随机字符串 String nonce_str = getNonceStr(); // 商品描述根据情况修改 String body = tpWxPayDto.getBody(); // 商户订单号 String out_trade_no = orderId; SortedMap<String, String> packageParams = new TreeMap<String, String>(); packageParams.put("appid", appid); packageParams.put("mch_id", mch_id); packageParams.put("nonce_str", nonce_str); packageParams.put("body", body); packageParams.put("attach", attach); packageParams.put("out_trade_no", out_trade_no); // 这里写的金额为1 分到时修改 packageParams.put("total_fee", totalFee); packageParams.put("spbill_create_ip", spbill_create_ip); packageParams.put("notify_url", notify_url); packageParams.put("trade_type", trade_type); RequestHandler reqHandler = new RequestHandler(null, null); reqHandler.init(appid, appsecret, partnerkey); String sign = reqHandler.createSign(packageParams); String xml = "<xml>" + "<appid>" + appid + "</appid>" + "<mch_id>" + mch_id + "</mch_id>" + "<nonce_str>" + nonce_str + "</nonce_str>" + "<sign>" + sign + "</sign>" + "<body><![CDATA[" + body + "]]></body>" + "<out_trade_no>" + out_trade_no + "</out_trade_no>" + "<attach>" + attach + "</attach>" + "<total_fee>" + totalFee + "</total_fee>" + "<spbill_create_ip>" + spbill_create_ip + "</spbill_create_ip>" + "<notify_url>" + notify_url + "</notify_url>" + "<trade_type>" + trade_type + "</trade_type>" + "</xml>"; String code_url = ""; String createOrderURL = "https://api.mch.weixin.qq.com/pay/unifiedorder"; code_url = new GetWxOrderno().getCodeUrl(createOrderURL, xml); System.out.println("code_url----------------"+code_url); return code_url; }
设置好参数后直接调用getCodeurl()函数就能获取到微信的二维码了,如果返回的信息错误,可以断点跟踪一下看看
code_url = new GetWxOrderno().getCodeUrl(createOrderURL, xml); 这句返回的具体错误信息。 如果这部成功的话,你应该会得到一个类似这样的地址:weixin://wxpay/bizpayurl?pr=eYaNVZF 在前台直接使用qrcode.js将这个地址生成二维码显示在页面上,我的前台页面代码是这样的
<!doctype html> <html> <head> <title>扫码支付前台demo</title> <meta name="keywords" content="关键字"> <meta name="description" content="描述"> <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/> <script src=""></script> <script src="http://localhost:8080/shopping/resources/newstyle/js/qrcode.js"></script> </head> <body> <div align="center" id="qrcode"> <p > 扫码商品二维码付款 <br><br> </p> </div> </body> <script> //这个地址是Demo.java生成的code_url,这个很关键 var url = "weixin://wxpay/bizpayurl?pr=eYaNVZF"; //参数1表示图像大小,取值范围1-10;参数2表示质量,取值范围‘L‘,‘M‘,‘Q‘,‘H‘ var qr = qrcode(10, ‘M‘); qr.addData(url); qr.make(); var dom=document.createElement(‘DIV‘); dom.innerHTML = qr.createImgTag(); var element=document.getElementById("qrcode"); element.appendChild(dom); </script> </html>
接下来到了一个小技巧的提供时间了 ,由于微信的回调地址要求必须是80端口还必须是是外网的,咱们开发都是局域网而且也没有域名啊 ,怎么办?当然有办法了神器登场 ngrok如果你用的是原版的ngrok 他每次自动生成的二级域名都是随机生成的 ,这样会很麻烦因为每次都要修改微信的后台,后来又找到了一个国内的,不但不用FQ了还可以定制指定的二级域名http://natapp.cn/ 去这网址,绝对比原版的好用这是我本机的参数配置,zy是二级域名,8080端口是我的resin的启动地址,这样我就可以使用http://zy.ngrok.natapp.cn这样的域名来访问我的本机网站了ngrok -config ngrok.cfg -subdomain zy 8080回调地址直接配置成:http://zy.ngrok.natapp.cn/你的处理回调页面.jsp这样就可以实现在本机的调试,这东西我是找了好几个小时才发现的 ,真心的好用啊以后微信的相关接口开发都可以使用这个啊 接下来就是接受微信的回调页面了可以说的不多直接上代码
@RequestMapping({"/notifyurl.htm"}) public void notifyurl(HttpServletRequest request, HttpServletResponse response) throws IOException { //把如下代码贴到的你的处理回调的servlet 或者.do 中即可明白回调操作 System.out.print("微信支付回调数据开始"); //示例报文 //String xml = "<xml><appid><![CDATA[wxb4dc385f953b356e]]></appid><bank_type><![CDATA[CCB_CREDIT]]></bank_type><cash_fee><![CDATA[1]]></cash_fee><fee_type><![CDATA[CNY]]></fee_type><is_subscribe><![CDATA[Y]]></is_subscribe><mch_id><![CDATA[1228442802]]></mch_id><nonce_str><![CDATA[1002477130]]></nonce_str><openid><![CDATA[o-HREuJzRr3moMvv990VdfnQ8x4k]]></openid><out_trade_no><![CDATA[jdg_16042915230001]]></out_trade_no><result_code><![CDATA[SUCCESS]]></result_code><return_code><![CDATA[SUCCESS]]></return_code><sign><![CDATA[1269E03E43F2B8C388A414EDAE185CEE]]></sign><time_end><![CDATA[20150324100405]]></time_end><total_fee>1</total_fee><trade_type><![CDATA[JSAPI]]></trade_type><transaction_id><![CDATA[1009530574201503240036299496]]></transaction_id></xml>"; String inputLine; String notityXml="" ; String resXml = ""; try { while ((inputLine = request.getReader().readLine()) != null) { notityXml += inputLine; } request.getReader().close(); } catch (Exception e) { e.printStackTrace(); } System.out.println("接收到的报文:" + notityXml); Map m = parseXmlToList2(notityXml); WxPayResult wpr = new WxPayResult(); wpr.setAppid(m.get("appid").toString()); wpr.setBankType(m.get("bank_type").toString()); wpr.setCashFee(m.get("cash_fee").toString()); wpr.setFeeType(m.get("fee_type").toString()); wpr.setIsSubscribe(m.get("is_subscribe").toString()); wpr.setMchId(m.get("mch_id").toString()); wpr.setNonceStr(m.get("nonce_str").toString()); wpr.setOpenid(m.get("openid").toString()); wpr.setOutTradeNo(m.get("out_trade_no").toString()); wpr.setResultCode(m.get("result_code").toString()); wpr.setReturnCode(m.get("return_code").toString()); wpr.setSign(m.get("sign").toString()); wpr.setTimeEnd(m.get("time_end").toString()); wpr.setTotalFee(m.get("total_fee").toString()); wpr.setTradeType(m.get("trade_type").toString()); wpr.setTransactionId(m.get("transaction_id").toString()); if("SUCCESS".equals(wpr.getResultCode())){ //支付成功 //加入支付成功业务逻辑 if(this.isczok(wpr.getOutTradeNo()))//判断该笔订单是否在商户网站中已经做过处理 { if(this.addusergoldandlog(wpr.getOutTradeNo()))//增加用户金币写入订单记录 { if(setorderstate(wpr.getOutTradeNo(),1)) { resXml = "<xml>" + "<return_code><![CDATA[SUCCESS]]></return_code>" + "<return_msg><![CDATA[OK]]></return_msg>" + "</xml> "; } } else { resXml = "<xml>" + "<return_code><![CDATA[FAIL]]></return_code>" + "<return_msg><![CDATA[增加金币失败]]></return_msg>" + "</xml> "; } }else{ resXml = "<xml>" + "<return_code><![CDATA[FAIL]]></return_code>" + "<return_msg><![CDATA[订单已经处理过了]]></return_msg>" + "</xml> "; } } else { resXml = "<xml>" + "<return_code><![CDATA[FAIL]]></return_code>" + "<return_msg><![CDATA[微信返回结果错误]]></return_msg>" + "</xml> "; setorderstate(wpr.getOutTradeNo(),2);//讲订单状态更改成支付失败 } System.out.println("微信支付回调数据结束"); BufferedOutputStream out = new BufferedOutputStream( response.getOutputStream()); out.write(resXml.getBytes()); out.flush(); out.close(); } /** * description: 解析微信通知xml * * @param xml * @return * @author ex_yangxiaoyi * @see */ @SuppressWarnings({ "unused", "rawtypes", "unchecked" }) private static Map parseXmlToList2(String xml) { Map retMap = new HashMap(); try { StringReader read = new StringReader(xml); // 创建新的输入源SAX 解析器将使用 InputSource 对象来确定如何读取 XML 输入 InputSource source = new InputSource(read); // 创建一个新的SAXBuilder SAXBuilder sb = new SAXBuilder(); // 通过输入源构造一个Document Document doc = (Document) sb.build(source); Element root = doc.getRootElement();// 指向根节点 List<Element> es = root.getChildren(); if (es != null && es.size() != 0) { for (Element element : es) { retMap.put(element.getName(), element.getValue()); } } } catch (Exception e) { e.printStackTrace(); } return retMap; }
注意:接受到的都是xml格式的数据,腾讯的坑爹团队,都什么年代了还用xml
其中需要注意的是,如果交易成功后要在页面中直接返回
"<xml>" + "<return_code><![CDATA[SUCCESS]]></return_code>" + "<return_msg><![CDATA[OK]]></return_msg>" + "</xml> "我这个代码中的好像没成功,我准备再修改一下,你们谁知道应该怎么写的可以私信我想我这么写虽然交易成功,但是微信一直没收到你的反馈会一直异步请求你的页面,具体会请求多久我没看到文档里写,不过一般其他支付的也就是请求几次 这篇文章写的非常仓促,很多细节都没说到,等接下来有时间一定将文章好好整理一下
时间: 2024-10-15 02:17:47