一、扫码支付介绍

扫码支付可分为两种模式,商户根据支付场景选择相应模式。

【模式一】:

商户后台系统根据微信支付规则链接生成二维码,链接中带固定参数productid(可定义为产品标识或订单号)。用户扫码后,微信支付系统将productid和用户唯一标识(openid)回调商户后台系统(需要设置支付回调URL),商户后台系统根据productid生成支付交易,最后微信支付系统发起用户支付流程。

springboot 支付宝登陆 springboot支付功能_微信


  商户支付回调URL设置指引:进入商户平台–>产品中心–>开发配置,进行配置和修改,如下图所示。

  【模式二】:

  商户后台系统调用微信支付【统一下单API】生成预付交易,将接口返回的链接生成二维码,用户扫码后输入密码完成支付交易。注意:该模式的预付单有效期为2小时,过期后无法支付,不依赖设置的回调支付URL。

  流程图:

  

springboot 支付宝登陆 springboot支付功能_微信扫码支付_02


  业务流程说明:

(1)商户后台系统根据用户选购的商品生成订单。

(2)用户确认支付后调用微信支付【统一下单API】生成预支付交易;

(3)微信支付系统收到请求后生成预支付交易单,并返回交易会话的二维码链接code_url。

(4)商户后台系统根据返回的code_url生成二维码

(5)用户打开微信“扫一扫”扫描二维码,微信客户端将扫码内容发送到微信支付系统。

(6)微信支付系统收到客户端请求,验证链接有效性后发起用户支付,要求用户授权。

(7)用户在微信客户端输入密码,确认支付后,微信客户端提交授权。

(8)微信支付系统根据用户授权完成支付交易。

(9)微信支付系统完成支付交易后给微信客户端返回交易结果,并将交易结果通过短信、微信消息提示用户。微信客户端展示支付交易结果页面。

(10)微信支付系统通过发送异步消息通知商户后台系统支付结果。商户后台系统需回复接收情况,通知微信后台系统不再发送该单的支付通知。

(11)未收到支付通知的情况,商户后台系统调用【查询订单API】。

(12)商户确认订单已支付后给用户发货。

二、准备工作
包括申请微信商户、账号的配置,这里暂不详述。
三、java代码
1.controller层
/**
* 微信支付模式二(统一下单)返回微信订单二维码url。前端使用二维码库生成二维码(qrcode.min.js)
* @param request
* @param response
* @return
*/
@ResponseBody
@RequestMapping("/weiXinPay")
public JsonResult weiXinPay(HttpServletRequest request, HttpServletResponse response) {
return wxPayService.weiXinPay(request,response);
}

2.service层

    //统一下单
    UnifiedOrder ufo = new UnifiedOrder();
    //appid
    String appid = "";
    //商户id
    String mch_id ="";
    //商户秘钥
    String mch_key = "";
    String nonce_str = UUID.randomUUID().toString().substring(0,20).replace("-","1");
    //价格(单位:分)
    String total_fee = "";
    //商品名称
    String body = "会员充值";
    //日期
    String date = String.valueOf(System.currentTimeMillis()/ 1000);
    String randomA = String.valueOf((int)(Math.random()*100000000));
    String randomB = String.valueOf((int)(Math.random()*100000000));
    String randomC = String.valueOf((int)(Math.random()*100000000));
    String str = (randomA+randomB+randomC).substring(0,15);
    //订单号
    String out_trade_no = date+str;
    //获取当前电脑的ip
    String spbill_create_ip = request.getRemoteAddr();
    //回调地址(公众号上面配置的支付回调地址)
    String notify_url= "";
    String trade_type = "NATIVE";
    ufo.setAppid(appid);
    ufo.setBody(body);
    ufo.setMch_id(mch_id);
    ufo.setNonce_str(nonce_str);
    ufo.setNotify_url(notify_url);
    ufo.setOut_trade_no(out_trade_no);
    ufo.setSpbill_create_ip(spbill_create_ip);
    ufo.setTotal_fee(total_fee);
    ufo.setTrade_type(trade_type);
    ufo.setOpenid("");
    //获取预支付Map 10个参数
    Map<String,String> unifiedMap = ufo.toMap();
    log.info("unifiedMap is::"+unifiedMap);
    //获取预支付签名
    String estimateSign = SignUtil.getSign(unifiedMap,mch_key);
    ufo.setSign(estimateSign);
    try {
    	//这里使用的是weixin4j框架,具体可查看 http://www.weixin4j.org/
        UnifiedOrderResult ufor = weixin.pay().payUnifiedOrder(ufo);
        ufor.setDevice_info(out_trade_no+":"+total_fee);
        //根据业务需要记录自己的信息
        
        json = new JsonResult(ufor);
    } catch (WeixinException e) {
        json = new JsonResult(new ServiceException("微信认证失败"));
        e.printStackTrace();
    }
    return json;
}

/***
* 微信支付回调
* @param res
* @param rep
* @return
*/

public JsonResult payResult(HttpServletRequest res,HttpServletResponse rep) {
    JsonResult json = null;
    try {
        InputStream inputStream;
        StringBuffer sb = new StringBuffer();
        inputStream = res.getInputStream();
        String s;
        BufferedReader in = new BufferedReader(new InputStreamReader(inputStream, "UTF-8"));
        while ((s = in.readLine()) != null) {
            sb.append(s);
        }
        Map<String, String> map = new HashMap<String, String>();
        //判定是支付成功的返回结果
        if(sb.toString().indexOf("xml")!=-1) {
            //將xml转化为map
            Document doc;
            doc = DocumentHelper.parseText(sb.toString());
            Element root = doc.getRootElement();
            List children = root.elements();
            if (children != null && children.size() > 0) {
                for (int i = 0; i < children.size(); i++) {
                    Element child = (Element) children.get(i);
                    map.put(child.getName(), child.getTextTrim());
                }
            }
        }
        //商户秘钥
        String mch_key = "";
        //微信返回的签名
        String sign = map.get("sign");
        map.remove("sign");
        //自动生成的sign
        String nsign = SignUtil.getSign(map,mch_key);
        //验证签名通过
        if(nsign.equals(sign)){
            String resXml = "";
            if(map.get("return_code").equals("SUCCESS")) {
                log.info("返回结果:: "+ map.get("openid") +"用户支付成功");
                resXml = "<xml>" + "<return_code><![CDATA[SUCCESS]]></return_code>"
                        + "<return_msg><![CDATA[OK]]></return_msg>" + "</xml> ";
                String create_time = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
                //获取系统当前时间
                map.put("create_time",create_time);
                //商户号
                String mch_id = map.get("mch_id");
                //商户订单号
                String out_trade_no = map.get("out_trade_no");
                //查询订单信息
                Map<String,Object> vipOrder = wxPayMapper.getPayList(out_trade_no,mch_id);
                String state = vipOrder.get("state")==null?"":vipOrder.get("state").toString();
                //订单中的金额
                String total_fee = vipOrder.get("total_fee")==null?"":vipOrder.get("total_fee").toString();
                //订单id
                String order_id =vipOrder.get("order_id")==null?"":vipOrder.get("order_id").toString();
                //会员id
                String member_id = vipOrder.get("member_id")==null?"":vipOrder.get("member_id").toString();
                //微信返回信息中的支付金额
                String wxtotal_fee = map.get("total_fee");
                //无订单信息 ,订单状态为已支付,支付金额不正确,返回微信支付成功!
      		   if(null==vipOrder||!state.equals("0")||!total_fee.equals(wxtotal_fee)){
               
                }else{
                    //实现自己的业务
                }
            }else {
                log.info("支付失败,错误信息:" + map.get("err_code"));
                resXml = "<xml>" + "<return_code><![CDATA[FAIL]]></return_code>"
                        + "<return_msg><![CDATA[报文为空]]></return_msg>" + "</xml> ";
            }
            BufferedOutputStream out = new BufferedOutputStream(rep.getOutputStream());
            out.write(resXml.getBytes());
            out.flush();
            out.close();
            log.info("返回结果 map::" + map);
            //json = new JsonResult(map);
            //关闭连接
            in.close();
            inputStream.close();
        }
    } catch (Exception e) {
        log.info("返回结果 Exception is::"+ e);
        //json = new JsonResult(new Exception(e));
    }
    return json;
}



/**主动查询**/
@Override
public JsonResult checkPayResult(String out_trade_no) {
    if(null==out_trade_no||"".equals(out_trade_no)){
        return new JsonResult(new ServiceException("订单号不能为空!"));
    }
    //appid
    String appid = "";
    //商户id
    String mch_id ="";
    //商户秘钥
    String mch_key = "";
    String nonce_str = UUID.randomUUID().toString().substring(0,20).replace("-","1");
    OrderQuery orderQuery = new OrderQuery();
    orderQuery.setAppid(appid);
    orderQuery.setMch_id(mch_id);
    orderQuery.setNonce_str(nonce_str);
    orderQuery.setOut_trade_no(out_trade_no);
    //获取预支付Map 10个参数
    Map<String,String> unifiedMap = orderQuery.toMap();
    //获取预支付签名
    String estimateSign = SignUtil.getSign(unifiedMap,mch_key);
    orderQuery.setSign(estimateSign);
    try {
        OrderQueryResult odr =  weixin.pay().payOrderQuery(orderQuery);
        return new JsonResult(odr);
    } catch (WeixinException e) {
        e.printStackTrace();
    }
    return null;
}

3.前端页面:js

var interval;
$(function(){
    $(".recharge-qrcode1").hide();
    doGetObjects();
    interval = setInterval(doCheckObjects,4000);
})
//计数
var count = 0;

function doGetObjects(){
    $.ajax({
        type:'post',
        url:'/wxPay/weiXinPay',
        success:function(result){
            if(result.state ==1){
                var out_trade_no =result.data.device_info.split(":")[0];
                $("#out_trade_no").val(out_trade_no);
                $("#recharge-num").html("订单编号:"+out_trade_no);
                $("#recharge-type").html("订单类型:"+result.data.trade_type);
                //二维码大小
                var qrcode = new QRCode(document.getElementById("qrcode"), {
                    width : 150,
                    height : 150
                });
                qrcode.makeCode(result.data.code_url);
            }else{
                alert(result.message);
            }
        }
    });
}

订单主动查询
利用定时器的方式实现主动查询订单支付状态
在我们对接微信支付的时候可能会出现用户支付了,但我们系统业务支付状态并没有 改变的情况,这是因为微信推送支付数据流的时候是后台通知交互时,
如果微信收到商户的应答不符合规范或超时,微信会判定本次通知失败,重新发送通知,直到成功为止(在通知一直不成功的情况下,微信总共会发起10次通知,
通知频率为15s/15s/30s/3m/10m/20m/30m/30m/30m/60m/3h/3h/3h/6h/6h - 总计 24h4m),但微信不保证通知最终一定能成功。 所以这里就需要我们自己主动去查询:接口详细说明地址

function doCheckObjects(){
    count++;
    if(count > 20){
        clearInterval(interval);
        //发起关闭订单
        CloseOrder();
    }
    var out_trade_no = $("#out_trade_no").val();
    var params = {"out_trade_no":out_trade_no};
    $.ajax({
        type:'post',
        url:'/wxPay/checkPayResult',
        data:params,
        success:function(result){
            //跳转支付成功页面
            if(result.data.trade_state == "SUCCESS"){
                clearInterval(interval);
                window.location.href="/wxPay/goToSuccess?out_trade_no="+out_trade_no;
            }

        }
    });
}

//关闭订单
function CloseOrder(){
    $(".recharge-qrcode").hide();
    $(".recharge-qrcode1").show();
}