java微信扫码支付详细说明

最近由于业务需要,参考别人的代码,然后加上自己整合,总算写出来了,在这里得跟大家一起分享下。

这是官方开发文档模式二的地址:https://pay.weixin.qq.com/wiki/doc/api/native.php?chapter=6_5

大家可以仔细看看这里面,里面的内容我就不写了,大家可以自己看。

先看看我的项目结构:
java微信扫码支付详细说明_第1张图片
pom文件的依赖:



    4.0.0
    
        org.springframework.boot
        spring-boot-starter-parent
        2.1.4.RELEASE
         
    
    com.wky
    wechat
    0.0.1-SNAPSHOT
    wechat
    Demo project for Spring Boot

    
        1.8
    

    

        
            org.springframework.boot
            spring-boot-starter-web
        

        
        
            com.google.zxing
            core
            3.2.1
        
        
            commons-logging
            commons-logging
            1.1.1
        
        
            net.sf.ezmorph
            ezmorph
            1.0.6
        
        
        
            org.apache.tomcat.embed
            tomcat-embed-jasper
            provided
        

        
            net.sf.json-lib
            json-lib
            2.2.3
            jdk15
        
        
            com.github.wxpay
            wxpay-sdk
            0.0.3
        
        
        
            com.alibaba
            fastjson
            1.2.24
        

        
        
            com.google.code.gson
            gson
            2.8.2
        
        
            org.apache.tomcat.embed
            tomcat-embed-core
        
        
            org.springframework.boot
            spring-boot-starter-test
            test
        
        
            com.thoughtworks.xstream
            xstream
            1.4.4
        
        
            commons-httpclient
            commons-httpclient
            3.0
        

        
            dom4j
            dom4j
            1.6.1
        
        
            org.jdom
            jdom
            1.1
        
        
            com.orhanobut
            logger
            1.14
        
        
            log4j
            log4j
            1.2.17
        
        
            org.slf4j
            slf4j-api
            1.7.25
        
        
            org.slf4j
            slf4j-log4j12
            1.7.25
            test
        
        
            org.slf4j
            slf4j-simple
            1.7.25
            test
        
        
        
            org.springframework.boot
            spring-boot-starter-tomcat
            compile
        
        
            org.apache.httpcomponents
            httpclient
            4.4
        

    

    
        
            
                org.springframework.boot
                spring-boot-maven-plugin
            
        
    



先来看看util包下面的几个工具类代码:

MD5Util:

package com.wky.wechat.Util;

import java.security.MessageDigest;

public class MD5Util {
      //md5加密
       private static String byteToHexString(byte b) {
            int n = b;
             if (n < 0)
                     n += 256;
            int d1 = n / 16;
             int d2 = n % 16;
             return hexDigits[d1] + hexDigits[d2];
         }

       private static String byteArrayToHexString(byte b[]) {
        StringBuffer resultSb = new StringBuffer();
        for (int i = 0; i < b.length; i++)
            resultSb.append(byteToHexString(b[i]));

        return resultSb.toString();
      }

       public static String MD5Encode(String origin, String charsetname) {
        String resultString = null;
        try {
            resultString = new String(origin);
            MessageDigest md = MessageDigest.getInstance("MD5");
            if (charsetname == null || "".equals(charsetname))

                resultString = byteArrayToHexString(md.digest(resultString.getBytes()));

            else
                resultString = byteArrayToHexString(md.digest(resultString.getBytes(charsetname)));

        } catch (Exception exception) {
        }
        return resultString;
     }

       private static final String hexDigits[] = { "0", "1", "2", "3", "4", "5",
                        "6", "7", "8", "9", "a", "b", "c", "d", "e", "f" };


}

XMLUtil:

package com.wky.wechat.Util;

import org.jdom.Document;
import org.jdom.Element;
import org.jdom.JDOMException;
import org.jdom.input.SAXBuilder;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

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;
            }

    /**
     59      * 获取子结点的xml
     60      * @param children
     61      * @return String
     62      */

    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("");
                            }
                    }

                return sb.toString();
            }
}

HttpUtil:

package com.wky.wechat.Util;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.net.URL;
import java.net.URLConnection;

public class HttpUtil {
//用http连接提交支付信息参数
    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;
             }
}

GetWeixinUrlUtil:

package com.wky.wechat.Util;

import com.google.zxing.BarcodeFormat;
import com.google.zxing.MultiFormatWriter;
import com.google.zxing.WriterException;
import com.google.zxing.common.BitMatrix;
import org.apache.commons.lang.StringUtils;

import javax.imageio.ImageIO;
import javax.servlet.http.HttpServletResponse;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.HashMap;
import java.util.Map;
import java.util.SortedMap;
import java.util.TreeMap;

public class GetWeixinUrlUtil {

    private static final int BLACK = 0xff000000;
    private static final int WHITE = 0xFFFFFFFF;

    //获取二维码url
    public static String weixin_pay(String ip) 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 body = "商品名称";   // 商品名称
                 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 packageParams = new TreeMap();
                 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);
                //生成签名
                 String sign = PayCommonUtil.createSign("UTF-8", packageParams,key);
                 packageParams.put("sign", sign);

                 String requestXML = PayCommonUtil.getRequestXml(packageParams);
                 System.out.println(requestXML);

                 String resXml = HttpUtil.postData(PayConfigUtil.UFDODER_URL, requestXML);
                 Map map = XMLUtil.doXMLParse(resXml);
                 String urlCode = (String) map.get("code_url");
                 System.out.println(urlCode);
                 return urlCode;
             }

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

    /**
     * 将路径生成二维码图片
     * @author xiaoloutingyu
     * @param content
     * @param response
     */
    @SuppressWarnings({ "unchecked", "rawtypes" })
    public static void encodeQrcode(String content, HttpServletResponse response){

        if(StringUtils.isBlank(content))
            return;
        MultiFormatWriter multiFormatWriter = new MultiFormatWriter();
        Map hints = new HashMap();
        BitMatrix bitMatrix = null;
        try {
            bitMatrix = multiFormatWriter.encode(content, BarcodeFormat.QR_CODE, 250, 250,hints);
            BufferedImage image = toBufferedImage(bitMatrix);
            //输出二维码图片流
            try {
                ImageIO.write(image, "png", response.getOutputStream());
            } catch (IOException e) {
                e.printStackTrace();
            }
        } catch (WriterException e1) {
            e1.printStackTrace();
        }
    }

    /**
     * 类型转换
     * @author xiaoloutingyu
     * @param matrix
     * @return
     */
    public static BufferedImage toBufferedImage(BitMatrix matrix) {
        int width = matrix.getWidth();
        int height = matrix.getHeight();
        BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
        for (int x = 0; x < width; x++) {
            for (int y = 0; y < height; y++) {
                image.setRGB(x, y, matrix.get(x, y) == true ? BLACK : WHITE);
            }
        }
        return image;
    }
}

PayCommonUtil:

package com.wky.wechat.Util;

import java.text.SimpleDateFormat;
import java.util.*;

public class PayCommonUtil {

    /**
          * 是否签名正确,规则是:按参数名称a-z排序,遇到空值的参数不参加签名。
          * @return boolean
          */
    public static boolean isTenpaySign(String characterEncoding, SortedMap 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(!"sign".equals(k) && null != v && !"".equals(v)) {
                               sb.append(k + "=" + v + "&");
                            }
                     }

               sb.append("key=" + API_KEY);

        //算出摘要
                String mysign = MD5Util.MD5Encode(sb.toString(), characterEncoding).toLowerCase();
                 String tenpaySign = ((String)packageParams.get("sign")).toLowerCase();

                 //System.out.println(tenpaySign + "    " + mysign);
                 return tenpaySign.equals(mysign);
             }

    /**
           * @author  xiaoloutingyu
           * @date 2016-4-22
           * @Description:sign签名
           * @param characterEncoding
           *            编码格式
           * @param
           *
           * @return
           */

    public static String createSign(String characterEncoding, SortedMap 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;
            }
    /**
     65      * @author  xiaoloutingyu
     66      * @date 2016-4-22
     67      * @Description:将请求参数转换为xml格式的string
     68      * @param parameters
     69      *            请求参数
     70      * @return
     71      */

    public static String getRequestXml(SortedMap parameters) {
                 StringBuffer sb = new StringBuffer();
                 sb.append("");
                 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 + ">" + "");
                             } else {
                                 sb.append("<" + k + ">" + v + "");
                             }
                     }
                 sb.append("");
                return sb.toString();
             }
    /**
     92      * 取出一个指定长度大小的随机正整数.
     93      *
     94      * @param length
     95      *            int 设定所取出随机数的长度。length小于11
     96      * @return int 返回生成的随机数。
     97      */
    public static int buildRandom(int length) {
                 int num = 1;
                double random = Math.random();
                 if (random < 0.1) {
                         random = random + 0.1;
                     }
                 for (int i = 0; i < length; i++) {
                         num = num * 10;
                     }
                 return (int) ((random * num));
             }

    /**
     111      * 获取当前时间 yyyyMMddHHmmss
     112      *
     113      * @return String
     114      */
    public static String getCurrTime() {
                Date now = new Date();
                 SimpleDateFormat outFormat = new SimpleDateFormat("yyyyMMddHHmmss");
                 String s = outFormat.format(now);
                 return s;
             }

}

PayConfigUtil:

package com.wky.wechat.Util;

import javax.servlet.http.HttpServletRequest;

public class PayConfigUtil {
//这里的必要参数填写自己的,另外回调地址必须是外网能访问的,我这里是用netapp开通了一个隧道,免费的,大家测试的时候也可以申请个,可以百度netapp教程几分钟学习一下就可以
      public static final String APP_ID = "";//微信开发平台应用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 = "";//回调地址
      public static final String UFDODER_URL = "https://api.mch.weixin.qq.com/pay/unifiedorder";//微信统一下单接口
      public static final String APP_SECRET = "4b438be89862ab64a2cb2960a8f696bd";//应用对应的凭证(在公众号里面)

            //获取ip
            public static String getIP(HttpServletRequest request)
     {
                 String ip = request.getRemoteAddr();
                return ip;
             }
}

service层:
WechatService:

package com.wky.wechat.Service;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public interface WechatService {

  void  weixin_notify(HttpServletRequest request, HttpServletResponse response) throws Exception;

  ;
}  

看看实现类的代码:
package com.wky.wechat.Service.impl;

import com.wky.wechat.Service.WechatService;
import com.wky.wechat.Util.PayCommonUtil;
import com.wky.wechat.Util.PayConfigUtil;
import com.wky.wechat.Util.XMLUtil;
import org.springframework.stereotype.Service;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.BufferedOutputStream;
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.*;

@Service
public class WechatServiceImpl implements WechatService {

    @Override
    public void weixin_notify(HttpServletRequest request, HttpServletResponse response) throws Exception {

        System.out.println("调用回调方法");
                 //读取参数
                 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 m = new HashMap();
                 m = XMLUtil.doXMLParse(sb.toString());

                 //过滤空 设置 TreeMap
                 SortedMap 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

                 //logger.info(packageParams);
                 System.out.println(packageParams);
                 //判断签名是否正确
                 if(PayCommonUtil.isTenpaySign("UTF-8", packageParams,key)) {
                         //------------------------------
                         //处理业务开始
                         //------------------------------
                        String resXml = "";
                        if("SUCCESS".equals((String) packageParams.get("result_code"))){
                                 // 这里是支付成功
                                //////////执行自己的业务逻辑////////////////
                                 String mch_id = (String) packageParams.get("mch_id");
                                String openid = (String) packageParams.get("openid");
                                 String is_subscribe = (String) packageParams.get("is_subscribe");
                                String out_trade_no = (String) packageParams.get("out_trade_no");
                                 String total_fee = (String) packageParams.get("total_fee");
                                 String cash_fee_s = (String) packageParams.get("cash_fee");
                                 String cash_fee = String.valueOf(Integer.parseInt(cash_fee_s) / 100);
                                 //String time_end = MctsUtils.numberDateToDate((String) packageParams.get("time_end"));
                                 String transaction_id = (String) packageParams.get("transaction_id");

                                     //////////执行自己的业务逻辑(报存订单信息到数据库)////////////////
                                     System.out.println("支付成功 ,处理业务成功");
                                     //logger.info("支付成功");
                                     //通知微信.异步确认成功.必写.不然会一直通知后台.八次之后就认为交易失败了.
                                     resXml = "" + "" + "" + " ";
                                   //------------------------------
                                     //处理业务完毕
                                     //------------------------------
                     //向微信服务器发送确认信息,若不发送,微信服务器会间隔不同的时间调用回调方法
                                     BufferedOutputStream out = new BufferedOutputStream(
                                                     response.getOutputStream());
                                    out.write(resXml.getBytes());
                                     out.flush();
                                    out.close();
                                     System.out.println("通知微信.异步确认成功");
                                 } else {
                               System.out.println("支付失败,错误信息:" + packageParams.get("err_code"));
                                // logger.info("支付失败,错误信息:" + packageParams.get("err_code"));
                                resXml = "" + ""
                                         + "" + " ";

                                 BufferedOutputStream out = new BufferedOutputStream(
                                                 response.getOutputStream());
                                 out.write(resXml.getBytes());
                                 out.flush();
                                 out.close();
                                 System.out.println("执行回调函数失败");
                            }
                    } else{
                         //logger.info("通知签名验证失败");
                        System.out.println("通知签名验证失败");
                     }
    }



}


controller:

@RequestMapping(value="/image",method = RequestMethod.GET)
    public void payWexin(@RequestParam String ip, HttpServletResponse response)throws Exception{
        String url=GetWeixinUrlUtil.weixin_pay(ip);
        GetWeixinUrlUtil.encodeQrcode(url,response);

    }


![在这里插入图片描述](https://img-blog.csdnimg.cn/20190507105259911.jpg)

你可能感兴趣的:(工作总结)