Java微信扫码支付(模式二) spring boot

  微信NATIVE支付,用Spring boot写的,这里来分享一下。

首先付一张官方流程图:
在这里插入图片描述
这里简述一下:
基本上就是,把扫码支付所需要的参数准备好,
请求统一下单接口,把返回的二维码链接codeUrl,返回在前端通过js生产二维码,
js延时定时器,请求后台查询订单,确认支付成功,
执行自己的业务逻辑,流程结束。
下面开始贴代码:

一、基本参数配置

/**
 * @author heyonghao
 * @Title: PayConfigUtil
 * @ProjectName xlkb2b
 * @Description: 支付基础参数
 * @date 2019/1/28 002811:46
 */
public class PayConfigUtil {

    public static final String APP_ID = "xxx";//微信开发平台应用ID(公众号ID)
    public static final String MCH_ID = "..";//商户号(商户号ID)
    public static final String API_KEY = "..";//API key(商户号里面的)
    public static final String CREATE_IP = "";//发起支付的ip
    public static final String NOTIFY_URL = "http://...";//回调地址
    public static final String UFDODER_URL = "https://api.mch.weixin.qq.com/pay/unifiedorder";//微信统一下单接口
    public static final String ORDERQUERY_URL = "https://api.mch.weixin.qq.com/pay/orderquery";//微信查询订单接口
    public static final String APP_SECRET = "...";//应用对应的凭证(在公众号里面)
    //获取ip
    public static String getIP(HttpServletRequest request)
    {
        String ip = request.getRemoteAddr();
        return ip;
    }
}

这里要配置自己的参数,基本就这些!

二、controller

/**
     * 用户支付,余额支付,微信、支付宝,扫码支付
     * @param payMethod 支付方式
     * @param totalPrice 支付价格
     * @param ordersId  订单id集合 ,分割
     * @param model model
     * @param request request
     * @return 微信扫码支付二维码链接
     */
    @RequestMapping(value = {"/toPay"},method = RequestMethod.POST)
    public String GetWeixinPayUrl(@RequestParam("payMethod") String payMethod,@RequestParam("totalPrice") Double totalPrice,@RequestParam("ordersId") String ordersId,Model model, HttpServletRequest request){
        //判读用户选择的支付方式
        if(payMethod.equals("weChat")){
            try {
                //获取客户端ip
                String ip = PayConfigUtil.getIP(request);
                //微信扫码支付链接 codeUrl
                Map<String, String> map = GetWeixinUrlUtil.weixin_pay(ip, "收款金额", ordersId);
                model.addAttribute("codeUrl",map.get("urlCode"));
                model.addAttribute("out_trade_no",map.get("out_trade_no"));
                model.addAttribute("nonce_str",map.get("nonce_str"));
                return "/backstage/business/my_pay.html";
            }catch (Exception e){
                e.printStackTrace();
            }
        }
        return "";
    }

!!这里的out_trade_no订单号跟nonce_str,是为了后面的查询订单准备的.

获取codeUrl

public class GetWeixinUrlUtil {

    /**
     * 获取微信扫码支付链接
     * @param ip        客户端ip
     * @param body      商品描述
     * @param ordersId  平台客户订单id
     * @return 返回一个包含,订单号,随机字符串,二维码链接的map
     * @throws Exception
     */
    public static Map<String,String> weixin_pay(String ip, String body, String ordersId) throws Exception {
        // 账号信息
        String appid = PayConfigUtil.APP_ID;  // appid
        String mch_id = PayConfigUtil.MCH_ID; // 商户号
        String key = PayConfigUtil.API_KEY; // key
        String currTime = PayCommonUtil.getCurrTime();
        String strTime = currTime.substring(8, currTime.length());
        String strRandom = PayCommonUtil.buildRandom(4) + "";
        String nonce_str = strTime + strRandom;
        int order_price = 1; // 商品价格   注意:价格的单位是分
        String out_trade_no = PayCommonUtil.getCurrTime() + PayCommonUtil.buildRandom(4); // 商户订单号
        // 获取发起电脑 ip
        String spbill_create_ip = ip;
        // 回调接口
        String notify_url = PayConfigUtil.NOTIFY_URL;
        String trade_type = "NATIVE";  //交易类型
        SortedMap<Object,Object> packageParams = new TreeMap<Object,Object>();
        packageParams.put("appid", appid);
        packageParams.put("mch_id", mch_id);
        packageParams.put("nonce_str", nonce_str);
        packageParams.put("body", body);
        packageParams.put("out_trade_no", out_trade_no);
        packageParams.put("total_fee", String.valueOf(order_price));
        packageParams.put("spbill_create_ip", spbill_create_ip);
        packageParams.put("notify_url", notify_url);
        packageParams.put("trade_type", trade_type);
        packageParams.put("attach",ordersId );//附加参数
        //生成签名
        String sign = PayCommonUtil.createSign("UTF-8", packageParams,key);
        packageParams.put("sign", sign);
        //转换成xml格式
        String requestXML = PayCommonUtil.getRequestXml(packageParams);

        String resXml = HttpUtil.postData(PayConfigUtil.UFDODER_URL, requestXML);
        Map map = XMLUtil.doXMLParse(resXml);
        String urlCode = (String) map.get("code_url");
        Map<String,String> resultMap = new HashMap<>();
        resultMap.put("urlCode",urlCode);
        resultMap.put("out_trade_no",out_trade_no);
        resultMap.put("nonce_str",nonce_str);
        return  resultMap;
    }

    // 特殊字符处理
    public static String UrlEncode(String src)  throws UnsupportedEncodingException {
        return URLEncoder.encode(src, "UTF-8").replace("+", "%20");
    }

}

里面用到的三个方法:

/**
     * @author
     * @date 2016-4-22
     * @Description:sign签名
     * @param characterEncoding  编码格式
     *          parameters  请求参数
     * @return
     */
    public static String createSign(String characterEncoding, SortedMap<Object, Object> packageParams, String API_KEY) {
        StringBuffer sb = new StringBuffer();
        Set es = packageParams.entrySet();
        Iterator it = es.iterator();
        while (it.hasNext()) {
            Map.Entry entry = (Map.Entry) it.next();
            String k = (String) entry.getKey();
            String v = (String) entry.getValue();
            if (null != v && !"".equals(v) && !"sign".equals(k) && !"key".equals(k)) {
                sb.append(k + "=" + v + "&");
            }
        }
        sb.append("key=" + API_KEY);
        String sign = MD5Util.MD5Encode(sb.toString(), characterEncoding).toUpperCase();
        return sign;
    }

–这个主要是生成微信所需要的签名格式

/**
     * @author
     * @date 2016-4-22
     * @Description:将请求参数转换为xml格式的string
     * @param parameters
     *            请求参数
     * @return
     */
    public static String getRequestXml(SortedMap<Object, Object> parameters) {
        StringBuffer sb = new StringBuffer();
        sb.append("<xml>");
        Set es = parameters.entrySet();
        Iterator it = es.iterator();
        while (it.hasNext()) {
            Map.Entry entry = (Map.Entry) it.next();
            String k = (String) entry.getKey();
            String v = (String) entry.getValue();
            if ("attach".equalsIgnoreCase(k) || "body".equalsIgnoreCase(k) || "sign".equalsIgnoreCase(k)) {
                sb.append("<" + k + ">" + "<![CDATA[" + v + "]]></" + k + ">");
            } else {
                sb.append("<" + k + ">" + v + "</" + k + ">");
            }
        }
        sb.append("</xml>");
        return sb.toString();
    }

–这个呢是所需要xml格式.

/**
 * @author heyonghao
 * @Title: HttpUtil
 * @ProjectName xlkb2b
 * @Description: 用http连接提交支付信息参数
 * @date 2019/1/28 002811:43
 */
public class HttpUtil {

    private static final Log logger = LogFactory.getLog("org.apache.catalina.tribes.MESSAGES" );

    private final static int CONNECT_TIMEOUT = 5000; // in milliseconds

    private final static String DEFAULT_ENCODING = "UTF-8";

    public static String postData(String urlStr, String data){
        return postData(urlStr, data, null);
    }

    public static String postData(String urlStr, String data, String contentType){
        BufferedReader reader = null;
        try {
            URL url = new URL(urlStr);
            URLConnection conn = url.openConnection();
            conn.setDoOutput(true);
            conn.setConnectTimeout(CONNECT_TIMEOUT);
            conn.setReadTimeout(CONNECT_TIMEOUT);
            if(contentType != null)
                conn.setRequestProperty("content-type", contentType);
            OutputStreamWriter writer = new OutputStreamWriter(conn.getOutputStream(), DEFAULT_ENCODING);
            if(data == null)
                data = "";
            writer.write(data);
            writer.flush();
            writer.close();

            reader = new BufferedReader(new InputStreamReader(conn.getInputStream(), DEFAULT_ENCODING));
            System.out.println(reader.toString());
            StringBuilder sb = new StringBuilder();
            String line = null;
            while ((line = reader.readLine()) != null) {
                sb.append(line);
                sb.append("\r\n");
            }
            return sb.toString();
        } catch (IOException e) {
            logger.error("Error connecting to " + urlStr + ": " + e.getMessage());
        } finally {
            try {
                if (reader != null)
                    reader.close();
            } catch (IOException e) {
            }
        }
        return null;
    }



}

–这个呢是请求工具类

/**
 * @author heyonghao
 * @Title: XMLUtil
 * @ProjectName xlkb2b
 * @Description: 将支付参数转换为xml格式 工具包
 * @date 2019/1/28 002811:23
 */
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();
    }


}

这个呢是常用xml工具包。

三、在页面生成二维码

<div id="output"></div>
 <span id="code" style="display: none;" th:text="${codeUrl}"></span>

 $(function(){
          var content=$("#code").text();
          $('#output').qrcode(content);
      })

这里用到的是 jquery.qrcode.min.js //(编辑器操作不太熟)

附带一个我的订单查询的js,所需要的参数前面就准备好了,这里获取一下就行了。

 $(function(){
          setInterval(refreshCount,5000*2);//毫秒单位
          //getLoc();
      });
          function refreshCount() {
              var outTradeno=$("#out_trade_no").text();//商户订单号
              var nonceStr=$("#nonce_str").text();//随机字符串
              $.post("orderQuery",{"outTradeno": outTradeno,"nonceStr":nonceStr},function (data) {
                  //返回结构json
                  //支付状态
                  var message=data.payStatus;
                  console.log(message);
                  if(message =="SUCCESS"){
                      swal("支付成功", "", "success");
                      window.location.href = "/searchUserOrder?orderStatus=2";
                  }
              });
          }

四、支付回调

/**
     * 微信扫码支付回调
     * @param request request
     * @param response response
     * @param model model
     * @throws Exception
     */
    @RequestMapping(value = {"/weChatNotify"},method = RequestMethod.POST)
    public String weixin_notify(HttpServletRequest request, HttpServletResponse response,Model model) throws Exception{
        //读取参数
        InputStream inputStream ;
        StringBuffer sb = new StringBuffer();
        inputStream = request.getInputStream();
        String s ;
        BufferedReader in = new BufferedReader(new InputStreamReader(inputStream, "UTF-8"));
        while ((s = in.readLine()) != null){
            sb.append(s);
        }
        in.close();
        inputStream.close();
        //解析xml成map
        Map<String, String> m = new HashMap<>();
        m = XMLUtil.doXMLParse(sb.toString());
        //过滤空 设置 TreeMap
        SortedMap<Object,Object> packageParams = new TreeMap<>();
        Iterator it = m.keySet().iterator();
        while (it.hasNext()) {
            String parameter = (String) it.next();
            String parameterValue = m.get(parameter);
            String v = "";
            if(null != parameterValue) {
                v = parameterValue.trim();
            }
            packageParams.put(parameter, v);
        }
        // 账号信息
        String key = PayConfigUtil.API_KEY; // key
        //判断签名是否正确
        if(PayCommonUtil.isTenpaySign("UTF-8", packageParams,key)) {
            //处理业务开始
            String resXml = "";
            if("SUCCESS".equals((String) packageParams.get("result_code"))){
                String attach = (String) packageParams.get("attach");//订单id集合
                String[] split = attach.split(",");
                int i=0;
                for (String s1 : split) {
                    i= orderService.UpdateUserOrder(s1, "2");//2为已支付
                }
                //执行自己的业务逻辑(报存订单信息到数据库)
                if(i>0){
                    //支付成功后跳转页面
                    //通知微信.异步确认成功.必写.不然会一直通知后台.八次之后就认为交易失败了.
                    resXml = "<xml>" + "<return_code><![CDATA[SUCCESS]]></return_code>"
                            + "<return_msg><![CDATA[OK]]></return_msg>" + "</xml> ";
                    //------------------------------
                    //处理业务完毕
                    //------------------------------
                    //向微信服务器发送确认信息,若不发送,微信服务器会间隔不同的时间调用回调方法
                    BufferedOutputStream out = new BufferedOutputStream(response.getOutputStream());
                    out.write(resXml.getBytes());
                    out.flush();
                    out.close();
                }else{
                    System.out.println("处理业务失败");
                }
            } else {
                model.addAttribute("message","支付失败,错误信息:"+ packageParams.get("err_code"));
                resXml = "<xml>" + "<return_code><![CDATA[FAIL]]></return_code>"
                        + "<return_msg><![CDATA[报文为空]]></return_msg>" + "</xml> ";
                BufferedOutputStream out = new BufferedOutputStream(response.getOutputStream());
                out.write(resXml.getBytes());
                out.flush();
                out.close();
            }
        } else{
            model.addAttribute("message","通知签名验证失败");
        }
        return "";
    }

五、订单查询

/**
     * 查询订单信息
     * @param outTradeno 商户订单号
     * @param nonceStr   所用随机字符串
     * @return SUCCESS—支付成功 REFUND—转入退款 NOTPAY—未支付 CLOSED—已关闭 REVOKED—已撤销(刷卡支付)
     *         USERPAYING--用户支付中 PAYERROR--支付失败(其他原因,如银行返回失败)
     */
    @ResponseBody
    @RequestMapping(value = "/orderQuery" ,method = RequestMethod.POST)
    public JSON queryNotifyResult(@RequestParam("outTradeno") String outTradeno,@RequestParam("nonceStr") String nonceStr) throws Exception{
        CommonJson commonJson = new CommonJson();//公共json类
        // 账号信息
        String appid = PayConfigUtil.APP_ID;  // appid
        String mch_id = PayConfigUtil.MCH_ID; // 商户号
        String key = PayConfigUtil.API_KEY; // key
        Map<String, String> resultMap = new HashMap<>();

        SortedMap<Object,Object> packageParams = new TreeMap<Object,Object>();
        packageParams.put("appid", appid);
        packageParams.put("mch_id", mch_id);
        packageParams.put("nonce_str", nonceStr);//  生成二维码的随机字符串
        packageParams.put("out_trade_no", outTradeno);// 订单号

        //生成签名
        String sign = PayCommonUtil.createSign("UTF-8", packageParams,key);
        packageParams.put("sign", sign);
        //转换成xml格式
        String requestXML = PayCommonUtil.getRequestXml(packageParams);
        String resXml = HttpUtil.postData(PayConfigUtil.ORDERQUERY_URL, requestXML);
        Map m = XMLUtil.doXMLParse(resXml);
        String resultCode = m.get("result_code").toString();
        if ("SUCCESS".equals(resultCode)) {
            String tradeState = m.get("trade_state").toString();
            commonJson.setPayStatus(tradeState);
            JSONObject jsonObject = JSONObject.fromObject(commonJson);
            return jsonObject;
        } else {
            commonJson.setPayStatus("ERROR");
            JSONObject jsonObject = JSONObject.fromObject(commonJson);
            return jsonObject;
        }
    }

OK! !希望对大家有帮助…(如果有jar没有的,maven上搜搜就行了)http://mvnrepository.com/artifact/org.apache.spark/spark-streaming-kafka_2.11/1.6.3

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值