今天要比昨天吃苦的能力更强

微信支付 全套流程看完就会 pc扫码支付

一:前期微信支付扫盲知识

前提条件是已经有申请了微信支付功能的公众号,然后我们需要得到公众号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生成一个二维码,生成二维码我这里用了Googlezxing库。然后把这个二维码显示在你的PC端网页上就行啦。这样终端用户一扫码就支付啦,支付就完成啦,看到这里你肯定很激动,发现微信支付如此简单,等等还有个事情我们还不知道,客户知道付钱了,我们服务器端还不知道呢,以微信开发人员的智商他们早就想到这个问题了,所以让你在调用统一下单接口的时候其中有个必填的参数就是回调URL,就是如果客户端付款成功之后微信会通过这个URL向我们自己的服务器提交一些数据,然后我们后台解析这些数据,完成我们自己操作。这样我们才知道客户是否真的已经通过微信付款了。这样整个流程才结束,这个就是模式二。微信用一个时序图示这样表示这个过程的。

 

表达起来比较复杂,看上去比较吃力,总结一下其实我们服务器该做的事情就如下件:

 

1. 通过统一下单接口传入正确的参数(当然要包括我们的回调URL)与签名验证,从返回数据中得到code_url的对应数据

 

2. 根据code_url的数据我们自己生成一个二维码图片,显示在浏览器网页上

 

3. 在回调的URL中添加我们自己业务逻辑处理。

 

至此扫盲结束了,你终于知道扫码支付什么个什么样的流程了,下面我们就一起来扒扒它的相关API使用,做好每步处理。

 

二:开发过程

 

在开发代码之前,请先准备几件事情。

 

1. 添加ZXingmaven依赖   也可以直接在前端使用第三方库  生成二维码

 

2. 添加jdommaven依赖

 

3.下载Java版本SDK演示程序,地址在这里

 

https://pay.weixin.qq.com/wiki/doc/api/native.php?chapter=11_1

 

我们需要MD5Util.javaXMLUtil.java两个文件

 

4. 我们使用HttpClient版本是4.5.1,记得添加Maven依赖

 

上面准备工作做好以后,继续往下看:

 

首先我们要调用微信的统一下单接口,我们点击【API列表】中的统一下单会看到这样页面:

 

 

 

我们重点要关注和阅读的内容我已经用红色椭圆标注好了,首先阅读【接口规则】里面的协议规范,开玩笑这个都不读你就想做微信支付,这个就好比你要去泡妞,得先收集点基本背景信息,了解对方特点,不然下面还怎么沟通。事实证明只有会泡妞得程序员才是好销售。跑题了我们接下来要看一下【场景介绍】中的案例与规范,只看一下记得一定要微信支付的LOGO下载下来,是为了最后放到我们自己的扫码支付网页上,这样看上去比较专业一点。之后重点关注【模式二】

我们这里就是要采用模式二的方式实现PC端页面扫码支付功能。

微信官方对模式二的解释是这样的商户后台系统先调用微信支付的统一下单接口,微信后台系统返回链接参数code_url,商户后台系统将code_url值生成二维码图片,用户使用微信客户端扫码后发起支付。注意:code_url有效期为2小时,过期后扫码不能再发起支付。看明白了吧就是我们首先要调用微信提供统一下单接口,得到一个关键信息code_url(至于这个code_url是什么鬼,我也不知道),然后我们通过自己的程序把这个URL生成一个二维码,生成二维码我这里用了Googlezxing库。然后把这个二维码显示在你的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"  //二维码颜色

});

 

 

})

到最后一步微信的二维码就展示在页面上了。  然后还有扫码回调和订单查询不懂的可以留言
posted @ 2017-11-24 17:58  野程序猿小刘  阅读(10123)  评论(0编辑  收藏  举报