一:前期微信支付扫盲知识
前提条件是已经有申请了微信支付功能的公众号,然后我们需要得到公众号APPID和微信商户号,这个分别在微信公众号和微信支付商家平台上面可以发现。其实在你申请成功支付功能之后,微信会通过邮件把Mail转给你的,有了这些信息之后,我们就可以去微信支付服务支持页面:https://pay.weixin.qq.com/service_provider/index.shtml
打开这个页面,点击右上方的链接【开发文档】会进入到API文档说明页面,看起来如下
选择红色圆圈的扫码支付就是我们要做接入方式,鼠标移动到上面会提示你去查看开发文档,如果这个都不知道怎么查看,可以洗洗睡了,你真的不合适做程序员,地址如下:
https://pay.weixin.qq.com/wiki/doc/api/native.php?chapter=6_1在浏览器中打开之后会看到
一:前期微信支付扫盲知识
前提条件是已经有申请了微信支付功能的公众号,然后我们需要得到公众号APPID和微信商户号,这个分别在微信公众号和微信支付商家平台上面可以发现。其实在你申请成功支付功能之后,微信会通过邮件把Mail转给你的,有了这些信息之后,我们就可以去微信支付服务支持页面:https://pay.weixin.qq.com/service_provider/index.shtml
打开这个页面,点击右上方的链接【开发文档】会进入到API文档说明页面,看起来如下
选择红色圆圈的扫码支付就是我们要做接入方式,鼠标移动到上面会提示你去查看开发文档,如果这个都不知道怎么查看,可以洗洗睡了,你真的不合适做程序员,地址如下:
https://pay.weixin.qq.com/wiki/doc/api/native.php?chapter=6_1在浏览器中打开之后会看到
我们重点要关注和阅读的内容我已经用红色椭圆标注好了,首先阅读【接口规则】里面的协议规范,开玩笑这个都不读你就想做微信支付,这个就好比你要去泡妞,得先收集点基本背景信息,了解对方特点,不然下面还怎么沟通。事实证明只有会泡妞得程序员才是好销售。跑题了我们接下来要看一下【场景介绍】中的案例与规范,只看一下记得一定要微信支付的LOGO下载下来,是为了最后放到我们自己的扫码支付网页上,这样看上去比较专业一点。之后重点关注【模式二】
我们这里就是要采用模式二的方式实现PC端页面扫码支付功能。
微信官方对模式二的解释是这样的“商户后台系统先调用微信支付的统一下单接口,微信后台系统返回链接参数code_url,商户后台系统将code_url值生成二维码图片,用户使用微信客户端扫码后发起支付。注意:code_url有效期为2小时,过期后扫码不能再发起支付”。看明白了吧就是我们首先要调用微信提供统一下单接口,得到一个关键信息code_url(至于这个code_url是什么鬼,我也不知道),然后我们通过自己的程序把这个URL生成一个二维码,生成二维码我这里用了Google的zxing库。然后把这个二维码显示在你的PC端网页上就行啦。这样终端用户一扫码就支付啦,支付就完成啦,看到这里你肯定很激动,发现微信支付如此简单,等等还有个事情我们还不知道,客户知道付钱了,我们服务器端还不知道呢,以微信开发人员的智商他们早就想到这个问题了,所以让你在调用统一下单接口的时候其中有个必填的参数就是回调URL,就是如果客户端付款成功之后微信会通过这个URL向我们自己的服务器提交一些数据,然后我们后台解析这些数据,完成我们自己操作。这样我们才知道客户是否真的已经通过微信付款了。这样整个流程才结束,这个就是模式二。微信用一个时序图示这样表示这个过程的。
表达起来比较复杂,看上去比较吃力,总结一下其实我们服务器该做的事情就如下件:
1. 通过统一下单接口传入正确的参数(当然要包括我们的回调URL)与签名验证,从返回数据中得到code_url的对应数据
2. 根据code_url的数据我们自己生成一个二维码图片,显示在浏览器网页上
3. 在回调的URL中添加我们自己业务逻辑处理。
至此扫盲结束了,你终于知道扫码支付什么个什么样的流程了,下面我们就一起来扒扒它的相关API使用,做好每步处理。
二:开发过程
在开发代码之前,请先准备几件事情。
1. 添加ZXing的maven依赖 也可以直接在前端使用第三方库 生成二维码
2. 添加jdom的maven依赖
3.下载Java版本SDK演示程序,地址在这里
https://pay.weixin.qq.com/wiki/doc/api/native.php?chapter=11_1
我们需要MD5Util.java和XMLUtil.java两个文件
4. 我们使用HttpClient版本是4.5.1,记得添加Maven依赖
上面准备工作做好以后,继续往下看:
首先我们要调用微信的统一下单接口,我们点击【API列表】中的统一下单会看到这样页面:
我们重点要关注和阅读的内容我已经用红色椭圆标注好了,首先阅读【接口规则】里面的协议规范,开玩笑这个都不读你就想做微信支付,这个就好比你要去泡妞,得先收集点基本背景信息,了解对方特点,不然下面还怎么沟通。事实证明只有会泡妞得程序员才是好销售。跑题了我们接下来要看一下【场景介绍】中的案例与规范,只看一下记得一定要微信支付的LOGO下载下来,是为了最后放到我们自己的扫码支付网页上,这样看上去比较专业一点。之后重点关注【模式二】
我们这里就是要采用模式二的方式实现PC端页面扫码支付功能。
微信官方对模式二的解释是这样的“商户后台系统先调用微信支付的统一下单接口,微信后台系统返回链接参数code_url,商户后台系统将code_url值生成二维码图片,用户使用微信客户端扫码后发起支付。注意:code_url有效期为2小时,过期后扫码不能再发起支付”。看明白了吧就是我们首先要调用微信提供统一下单接口,得到一个关键信息code_url(至于这个code_url是什么鬼,我也不知道),然后我们通过自己的程序把这个URL生成一个二维码,生成二维码我这里用了Google的zxing库。然后把这个二维码显示在你的PC端网页上就行啦。这样终端用户一扫码就支付啦,支付就完成啦,看到这里你肯定很激动,发现微信支付如此简单,等等还有个事情我们还不知道,客户知道付钱了,我们服务器端还不知道呢,以微信开发人员的智商他们早就想到这个问题了,所以让你在调用统一下单接口的时候其中有个必填的参数就是回调URL,就是如果客户端付款成功之后微信会通过这个URL向我们自己的服务器提交一些数据,然后我们后台解析这些数据,完成我们自己操作。这样我们才知道客户是否真的已经通过微信付款了。
这样整个流程才结束,这个就是模式二。微信用一个时序图示这样表示这个过程的。
package com........util; import java.util.Iterator; import java.util.Map; import java.util.Set; import java.util.SortedMap; import javax.servlet.http.HttpServletRequest; public class ConstantUtil { /** * 商家可以考虑读取配置文件 */ public static String GATEURL = "https://api.mch.weixin.qq.com/pay/unifiedorder";//获取预支付id的接口url public static String QUERYURL = "https://api.mch.weixin.qq.com/pay/orderquery";//获取微信支付订单支付情况url public static String BODY="";//支付介绍看微信介绍 //微信扫码支付配置--start--微信公众平台APPID public static String WECHAT_APP_ID="";//公司提供 public static String WECHAT_MCH_ID="";//公司提供 public static String WECHAT_APP_KEY="";//公司提供微信公众平台支付商户平台系统内的API密钥 public static String WECHAT_NOTIFY_URL="http://www.*******.com/weixinPayReturn";//微信扫码支付配置 回调路径--end-- public static String createSign(String Encoding, SortedMap<String, String> parameters){ StringBuffer sb = new StringBuffer(); Set es = parameters.entrySet(); Iterator it = es.iterator(); while(it.hasNext()) { Map.Entry entry = (Map.Entry)it.next(); String k = (String)entry.getKey(); Object v = entry.getValue(); if(null != v && !"".equals(v) && !"sign".equals(k) && !"key".equals(k)) { sb.append(k + "=" + v + "&"); } } sb.append("key=" + ConstantUtil.WECHAT_APP_KEY); String sign = MD5Util.MD5Encode(sb.toString(), Encoding).toUpperCase(); return sign; } public static boolean IsNumeric(String str) { return str.matches("\\d *"); } public static String parametersToXml(Map<String, String> parameters) { String xml = "<xml>"; Set es = parameters.entrySet(); Iterator it = es.iterator(); while(it.hasNext()) { Map.Entry entry = (Map.Entry)it.next(); String key = (String)entry.getKey(); String val = (String)entry.getValue(); if(IsNumeric(val)) { xml = xml + "<" + key + ">" + val + "</" + key + ">"; } else { xml = xml + "<" + key + "><![CDATA[" + val + "]]></" + key + ">"; } } xml = xml + "</xml>"; return xml; } public static String generateString(int length) { StringBuffer sb = new StringBuffer(); Random random = new Random(); for (int i = 0; i < length; i++) { sb.append(ALLCHAR.charAt(random.nextInt(ALLCHAR.length()))); } return sb.toString(); } public static String getIpAddress(HttpServletRequest request) { String ip = request.getHeader("x-forwarded-for"); if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { ip = request.getHeader("Proxy-Client-IP"); } if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { ip = request.getHeader("WL-Proxy-Client-IP"); } if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { ip = request.getHeader("HTTP_CLIENT_IP"); } if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { ip = request.getHeader("HTTP_X_FORWARDED_FOR"); } if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { ip = request.getRemoteAddr(); } return ip.equals("0:0:0:0:0:0:0:1")?"127.0.0.1":ip; } } |
先准备下单接口 window.location.href = "weixinPay?Oid=" + data; |
@RequestMapping(value = "/weixinPay", method = RequestMethod.GET, produces = MediaType.ALL_VALUE) public String weixinPay(Model model,String Oid) { System.out.println("\n*****************开启微信扫码界面******\n"); // 付款金额,必填 /*JSONObject jObject =new JSONObject();*/ long date = System.currentTimeMillis(); SortedMap<String, String> sParaTemp = new TreeMap<String, String>(); sParaTemp.put("appid", ConstantUtil.WECHAT_APP_ID); sParaTemp.put("mch_id", ConstantUtil.WECHAT_MCH_ID); sParaTemp.put("nonce_str", date + ConstantUtil.generateString(10));//最好是当前时间在随机数 sParaTemp.put("sign_type", "MD5"); sParaTemp.put("body", ConstantUtil.BODY); sParaTemp.put("out_trade_no", Oid);//此处改成商城orderID sParaTemp.put("total_fee", "1"); //为一分钱 sParaTemp.put("spbill_create_ip", ConstantUtil.getIpAddress(request)); sParaTemp.put("trade_type", "NATIVE"); sParaTemp.put("notify_url", ConstantUtil.WECHAT_NOTIFY_URL);//notify_url String signString = ConstantUtil.createSign("utf-8", sParaTemp); sParaTemp.put("sign", signString); String paramXml = ConstantUtil.parametersToXml(sParaTemp); try {//一下发送请求至微信的下单接口 CloseableHttpClient httpClient = HttpClientBuilder.create().build(); HttpPost post = new HttpPost(ConstantUtil.GATEURL); post.addHeader("Content-Type", "text/xml; charset=UTF-8"); StringEntity xmlEntity = new StringEntity(paramXml, ContentType.APPLICATION_JSON);//UTF-8 post.setEntity(xmlEntity); CloseableHttpResponse httpResponse = httpClient.execute(post); String responseXML = EntityUtils.toString(((org.apache.http.HttpResponse) httpResponse).getEntity(), "UTF-8"); @SuppressWarnings("unchecked") Map<String, String> resultMap = XMLUtil.parseXmlToMap(responseXML); if (resultMap.get("return_code").equals("SUCCESS") && resultMap.get("result_code").equals("SUCCESS")) { String codeurl = resultMap.get("code_url"); if (codeurl != null && !"".equals(codeurl)) { model.addAttribute("data", codeurl);//一切正常返回一个url 页面接收生成二维码 } else { model.addAttribute("data", ""); } } post.releaseConnection(); } catch (Exception e) { e.printStackTrace(); } return "/weixinPay"; } //发送的请求主要是注意验签那块。只要告诉提供的信息没有问题。一切都ok |
页面展示 weixinPay.jsp 获取以上的data页面使用第三方工具库生成二维码 <script src="${pageContext.request.contextPath}/resources/js/jquery-1.12.0.min.js" type="text/javascript"></script> <script src="${pageContext.request.contextPath}/resources/js/jquery.qrcode.min.js" type="text/javascript"></script> $(function(){ var str = toUtf8("${data}");//展示二维码 $("#code").qrcode({ render: "canvas", // 渲染方式有table方式和canvas方式 width: 220, //默认宽度 height: 200, //默认高度 text: str, //二维码内容 typeNumber: -1, //计算模式一般默认为-1 correctLevel: 2, //二维码纠错级别 background: "#ffffff", //背景颜色 foreground: "#000000" //二维码颜色 }); }) |
到最后一步微信的二维码就展示在页面上了。 然后还有扫码回调和订单查询不懂的可以留言 |