微信支付 Java后台 安卓和小程序前台(一)

转载请注明出处:https://blog.csdn.net/Strugglein/article/details/81143244

1.先来段开头

最近一段时间一直都是在写微信支付,微信中的坑本来就多,怎么说呢,慢慢的摸爬滚打也就过来了,这篇博主要就是把我这段时间自己的一些总结记录下来,方便以后的查阅,也方便以后身边朋友们需要学习的一个参考.

本篇主要讲述以微信app支付为主,小程序支付为辅,微信支付的其他支付也情况类似,这篇文章看会了其他的也就顺其自然了,在接入微信支付之前我也浏览过一些文章,都是一块一块的,有的就直接一代而过了,有的甚至为了挣那点积分,就让你下载不下不行,

本篇文章将以流程式写下去,包括每个类中引用的jar包也都贴出来,其他什么工具类都给,因为我不是太喜欢那种给一半藏一半的,再要不就是让你去下载的那种的

把代码拷出去自己填上自己的参数就可以使用

本篇博先写微信的支付,退款什么的看我的下一篇或者下下篇博客 >_<


正文

2.准备工作

微信支付首先就从准备工作先开始吧

wx.appid=微信开放平台审核通过的应用APPID
wx.mch_id=微信支付分配的商户号
wx.partnerKey=微信商户平台(pay.weixin.qq.com)-->账户设置-->API安全-->密钥设置
wx.notify_url=接收微信支付异步通知回调地址,通知url必须为直接可访问的url,不能携带参数。

首先这4个参数我认为是必须要有的,没有的话那就先搞到了再来开发吧
appid: 微信开放平台审核通过app时候的appid很醒目的就可以看到
mch_id: 微信商户平台的商户号,为app申请微信支付时会分配的商户号
partnerKey: 商户平台中自己设置的秘钥
notify_url: 这个是自己写的接口,用来进行微信对我们的回调(暂时可以没有,写完这个接口在填)

3.进入代码编写

微信支付:

        //这个properties是一个读取配置文件中的上面四个参数一个util
        HashMap<String, Object> properties = WeChatPayUtil.getProperties();
        //封装请求参数
        SortedMap<Object, Object> parameters = new TreeMap<Object, Object>();
        parameters.put("appid", properties.get("wx.appid"));//appid
        parameters.put("mch_id", properties.get("wx.mch_id"));//商户号
        parameters.put("nonce_str", WeChatPayToolsUtil.CreateNoncestr());//随机字符串
        parameters.put("fee_type", "CNY");
        parameters.put("notify_url", properties.get("wx.notify_url"));//回调地址
        parameters.put("trade_type", "APP");//app支付
        parameters.put("body", "wechatPay");//订单描述
           String ip = "127.0.0.1";//测试使用这个ip
        // String ip = WeChatPayToolsUtil.getRemortIP(request);//实际使用这个获取本机ip
        parameters.put("spbill_create_ip", ip);//ip
        parameters.put("out_trade_no", userOrder.getId() + "");//商户的订单号
        parameters.put("total_fee", String.valueOf(param.getFee()));
         // 设置签名
        String sign = WeChatPayToolsUtil.createSign(properties.get("wx.partnerKey").toString(), parameters);
        parameters.put("sign", sign);

        // 封装请求参数结束
        String requestXML = WeChatPayToolsUtil.getRequestXml(parameters); // 获取xml结果
        logger.debug("封装请求参数是:" + requestXML);

        // 调用统一下单接口
        InputStream in = null;
        try {
            in = WeChatPayToolsUtil.sendXMLDataByPost(GetConstantsUtil.WECHAT_UNDIFYURL, requestXML).getEntity().getContent();
        } catch (IOException e) {
            new IOException(Constants.Exception_Head + "统一下单异常");
        }
         //处理返回结果转为map
        Map<String, String> resultmap = getElementValue(in);
        logger.debug("调用统一下单接口:" + resultmap.toString());

  if (resultmap.get("return_code").equals("SUCCESS")) {
            if (resultmap.get("result_code").equals("SUCCESS")) {
            //这里主要是获取一个叫做prepay_id的字段,如果返回说签名失败或者什么的,根据msg提示操作
            }
        }

上面的处理返回结果转为map

 /**
     * 微信通过返回IO流获取返回信息
     */

    private Map<String, String> getElementValue(InputStream in) {
        SAXReader reader = new SAXReader();
        Document document = null;
        Map<String, String> map = new HashMap<String, String>();
        StringBuffer sb = new StringBuffer();
        try {
            document = reader.read(in);
        } catch (DocumentException e) {
            e.printStackTrace();
        }
        Element root = document.getRootElement();
        List<Element> childElements = root.elements();
        int stant = 0;
        for (Element child : childElements) {
            map.put(child.getName(), child.getStringValue());
        }
        return map;
    }

上面的读取配置文件中那几个参数的工具类;

package top.qujiali.core.util;


import com.alibaba.dubbo.common.logger.LoggerFactory;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Properties;

public final class WeChatPayUtil {

    private static final com.alibaba.dubbo.common.logger.Logger logger = LoggerFactory.getLogger(WeChatPayUtil.class);

    private static final String PATH = "config/weixin.properties";

    private static Properties properties;


    /**
     * @Description: 获取微信 properties文件中的内容
     * @author zhaopeng
     * @email [email protected]
     * @date 2018/5/24 10:25
     */
    public static synchronized HashMap<String, Object> getProperties() {
        logger.info("开始加载" + PATH + "文件内容.......");
        properties = new Properties();
        String strPath = null;//绝对路径
        InputStream in = null;//输入流对象
        HashMap<String, Object> map = new HashMap<>();
        try {
            //获取绝对路径
            strPath = Thread.currentThread().getContextClassLoader().getResource(PATH).getPath();
            //获取properties的输入流对象
            in = new FileInputStream(strPath);
            //加载流
            properties.load(in);
            Enumeration<?> enumeration = properties.propertyNames();
            while (enumeration.hasMoreElements()) {
                String row = enumeration.nextElement() + "";
                String property = properties.getProperty(row);
                map.put(row, property);
            }
        } catch (FileNotFoundException e) {
            logger.error(PATH + "文件未找到");
        } catch (IOException e) {
            logger.error("加载" + PATH + "时出现IOException");
        } finally {
            try {
                if (null != in) {
                    in.close();
                }
            } catch (IOException e) {
                logger.error(PATH + "文件流关闭出现异常");
            }
            logger.info("加载" + PATH + "文件内容完成...........");
            logger.info("properties文件内容:" + properties);
        }
        return map;
    }
}

这个是我把一些其他的工具类都封装到了这个util中了,上面的获取随机数,签名,封装请求参数下单都在里面

package top.qujiali.core.util;

import org.apache.commons.lang.math.RandomUtils;
import org.apache.http.HttpResponse;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.HttpClients;
import org.jdom.Document;
import org.jdom.Element;
import org.jdom.JDOMException;
import org.jdom.input.SAXBuilder;
import org.xml.sax.InputSource;

import javax.servlet.http.HttpServletRequest;
import java.io.*;
import java.security.MessageDigest;
import java.util.*;

public class WeChatPayToolsUtil {


    public static final String TIME = "yyyyMMddHHmmss";

    /**
     *
     * @Description: MD5
     * @author zhaopeng
     *
     */

    /***
     * MD5加码 生成32位md5码
     */
    public static String string2MD5(String str) {
        if (str == null || str.length() == 0) {
            return null;
        }
        char hexDigits[] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
                'a', 'b', 'c', 'd', 'e', 'f'};

        try {
            MessageDigest mdTemp = MessageDigest.getInstance("MD5");
            mdTemp.update(str.getBytes("UTF-8"));

            byte[] md = mdTemp.digest();
            int j = md.length;
            char buf[] = new char[j * 2];
            int k = 0;
            for (int i = 0; i < j; i++) {
                byte byte0 = md[i];
                buf[k++] = hexDigits[byte0 >>> 4 & 0xf];
                buf[k++] = hexDigits[byte0 & 0xf];
            }
            return new String(buf).toUpperCase();
        } catch (Exception e) {
            return null;
        }
    }

    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 String byteArrayToHexString(byte b[]) {
        StringBuffer resultSb = new StringBuffer();
        for (int i = 0; i < b.length; i++)
            resultSb.append(byteToHexString(b[i]));

        return resultSb.toString();
    }

    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 final String hexDigits[] = {"0", "1", "2", "3", "4", "5",
            "6", "7", "8", "9", "a", "b", "c", "d", "e", "f"};


    /**
     *
     * @Description: XML
     * @author zhaopeng
     *
     */

    /**
     * 生成xml
     */
    public static String genXml(Map<String, Object> map) {
        StringBuffer sb = new StringBuffer();
        sb.append("<xml>");
        for (String k : map.keySet()) {
            Object value = map.get(k);
            sb.append("<" + k + ">" + value + "</" + k + ">");
        }
        sb.append("</xml>");
        try {
            return new String(sb.toString().getBytes(), "ISO8859-1");
        } catch (UnsupportedEncodingException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        return null;
    }


    /**
     * 解析xml,返回第一级元素键值对。如果第一级元素有子节点,则此节点的值是子节点的xml数据。
     */
    public static Map doXMLParse(String strxml) throws IOException, JDOMException {
        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 = WeChatPayToolsUtil.getChildrenText(children);
            }

            m.put(k, v);
        }

        //关闭流
        in.close();

        return m;
    }

    /**
     * 获取子结点的xml
     */
    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(WeChatPayToolsUtil.getChildrenText(list));
                }
                sb.append(value);
                sb.append("</" + name + ">");
            }
        }

        return sb.toString();
    }

    /*随机字符串*/
    public static String CreateNoncestr() {
        String chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
        String res = "";
        for (int i = 0; i < 16; i++) {
            Random rd = new Random();
            res += chars.charAt(rd.nextInt(chars.length() - 1));
        }
        return res;
    }


    /**
     * 生成由[A-Z,0-9]生成的随机字符串
     *
     * @param length 欲生成的字符串长度
     */
    public static String getRandomString(int length) {
        Random random = new Random();

        StringBuffer sb = new StringBuffer();

        for (int i = 0; i < length; ++i) {
            int number = random.nextInt(2);
            long result = 0;

            switch (number) {
                case 0:
                    result = Math.round(Math.random() * 25 + 65);
                    sb.append(String.valueOf((char) result));
                    break;
                case 1:

                    sb.append(String.valueOf(new Random().nextInt(10)));
                    break;
            }
        }
        return sb.toString();
    }

    /**
     * 获取本机IP地址
     *
     * @return IP
     */
    public static String getRemortIP(HttpServletRequest request) {
        if (request.getHeader("x-forwarded-for") == null) {
            return request.getRemoteAddr();
        }
        return request.getHeader("x-forwarded-for");
    }

    /**
     * 微信支付签名算法sign
     */
    public static String createSign(String key, SortedMap<Object, Object> 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=" + key);
        String sign = WeChatPayToolsUtil.string2MD5(sb.toString()).toUpperCase();
        return sign;
    }

    /**
     * @param parameters 请求参数
     * @Description:将请求参数转换为xml格式的string
     */
    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)) {
                sb.append("<" + k + ">" + "<![CDATA[" + v + "]]></" + k + ">");
            } else {
                sb.append("<" + k + ">" + v + "</" + k + ">");
            }
        }
        sb.append("</xml>");
        return sb.toString();
    }


    /**
     * 发送xml数据
     *
     * @param url
     * @param xmlData
     * @return
     * @throws ClientProtocolException
     */
    public static HttpResponse sendXMLDataByPost(String url, String xmlData) {
        HttpClient httpClient = HttpClients.createDefault();
        HttpPost httppost = new HttpPost(url);
        HttpResponse response = null;
        try {
            StringEntity entity = new StringEntity(xmlData);
            httppost.setEntity(entity);
            httppost.setHeader("Content-Type", "text/xml;charset=UTF-8");
            response = httpClient.execute(httppost);
        } catch (IOException e) {
            e.printStackTrace();
        }
        return response;
    }

    /**
     * 接收微信的异步通知
     *
     * @throws IOException
     */
    public static String reciverWx(HttpServletRequest request) {
        StringBuffer sb = null;
        InputStream inputStream;
        try {
            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();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return sb.toString();
    }


    /**
     * 是否签名正确,规则是:按参数名称a-z排序,遇到空值的参数不参加签名。
     *
     * @return boolean
     */
    public static boolean isTenpaySign(String characterEncoding, SortedMap<Object, Object> packageParams, String 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=" + key);
        // 算出摘要
        String mysign = WeChatPayToolsUtil.MD5Encode(sb.toString(), characterEncoding).toLowerCase();
        String tenpaySign = ((String) packageParams.get("sign")).toLowerCase();
        // System.out.println(tenpaySign + " " + mysign);
        return tenpaySign.equals(mysign);
    }

    /*------------------------------*/
    public static String getMoney(String amount) {
        if (amount == null) {
            return "";
        }
        // 金额转化为分为单位
        String currency = amount.replaceAll("\\$|\\¥|\\,", "");  //处理包含, ¥ 或者$的金额
        int index = currency.indexOf(".");
        int length = currency.length();
        Long amLong = 0l;
        if (index == -1) {
            amLong = Long.valueOf(currency + "00");
        } else if (length - index >= 3) {
            amLong = Long.valueOf((currency.substring(0, index + 3)).replace(".", ""));
        } else if (length - index == 2) {
            amLong = Long.valueOf((currency.substring(0, index + 2)).replace(".", "") + 0);
        } else {
            amLong = Long.valueOf((currency.substring(0, index + 1)).replace(".", "") + "00");
        }
        return amLong.toString();
    }

    public static String signMd5_2(Map<String, String> packageParams, String merchantKey) {
        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=" + merchantKey);
        String sign = MD5Util.MD5Encode(sb.toString(), "UTF-8").toUpperCase();
        return sign;
    }


    public static String createXML(Map<String, String> map, String s) throws UnsupportedEncodingException {
        StringBuilder xml = new StringBuilder("<xml>");
        for (Map.Entry<String, String> entry : map.entrySet()) {
            String key = entry.getKey();
            String val = entry.getValue();

            xml.append("<").append(key).append(">").append(val).append("</").append(key).append(">");
        }
        xml.append("<").append("sign").append(">").append(s).append("</").append("sign").append(">");
        xml.append("</xml>");
        String xml1 = xml.toString();
        return xml1;
    }

    public static Map parseXmlToMap(String xml) {
        //  Map retMap = new HashMap();
        SortedMap<String, String> retMap = new TreeMap<String, String>();
        try {
            StringReader read = new StringReader(xml);
            // 创建新的输入源SAX 解析器将使用 InputSource 对象来确定如何读取 XML 输入
            InputSource source = new InputSource(read);
            // 创建一个新的SAXBuilder
            SAXBuilder sb = new SAXBuilder();
            // 通过输入源构造一个Document
            Document doc = sb.build(source);
            Element root = (Element) doc.getRootElement();// 指向根节点
            List<Element> es = root.getChildren();
            if (es != null && es.size() != 0) {
                for (Element element : es) {
                    retMap.put(element.getName(), element.getValue());
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return retMap;
    }
}

这时候不出意外就可以获取到prepay_id了,然后交给前段同志,让同志使用sdk唤起微信支付,因为是各自负责自己的模块所以我没有太去研究app唤起,详情就参考https://pay.weixin.qq.com/wiki/doc/api/app/app.php?chapter=8_5

这时候就该我们去提供一个回调的接口了,记住这个接口一定要布在一个外网可以访问的到的地方,也就是你的notify_url一定要可以外网直接访问的到,话不多说上代码了

   public void wxNotify(HttpServletRequest request, HttpServletResponse response) throws IOException {
        String result = WeChatPayToolsUtil.reciverWx(request); // 接收到异步的参数
        logger.debug("接收到的参数是__________________"+result);

       HashMap<String, Object> properties = WeChatPayUtil.getProperties();//这个类在上面已经贴出来了

        Map<String, String> m = new HashMap<String, String>();// 解析xml成map
        if (m != null && !"".equals(m)) {
            try {
                m = WeChatPayToolsUtil.doXMLParse(result);
            } catch (IOException e) {
                e.printStackTrace();
            } catch (JDOMException e) {
                e.printStackTrace();
            }
        }

        // 过滤空 设置 TreeMap
        SortedMap<Object, Object> packageParams = new TreeMap<Object, Object>();
        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 resXml = "";
        String key = (String) properties.get("wx.partnerKey");
        if (WeChatPayToolsUtil.isTenpaySign("UTF-8", packageParams, key)) {
            if ("SUCCESS".equals((String) packageParams.get("return_code"))) {
                // 如果返回成功 商户号,商户订单号,金额,微信支付订单号
                String mch_id = (String) packageParams.get("mch_id");
                String out_trade_no = (String) packageParams.get("out_trade_no");
                String total_fee = (String) packageParams.get("total_fee");
                String transaction_id = (String) packageParams.get("transaction_id");

                //校验商户号,如果正确 直接去以商户的订单号和金额为条件去修改订单字段,根据修改成功与否来判断防篡改
                int updateResult = -1;
                if (properties.get("wx.mch_id").equals(mch_id)) {
                    //如果商户号都一致了
                    //就处理你的业务吧,对商户的业务进行处理,
                    //如果没有啥问题就给人家微信返回success,
                    //如果有啥问题 就返别的 =_=
                }

                if (updateResult == 1) {
                    resXml = "<xml>" + "<return_code><![CDATA[SUCCESS]]></return_code>"
                            + "<return_msg><![CDATA[OK]]></return_msg>" + "</xml> ";
                } else {
                    resXml = "<xml>" + "<return_code><![CDATA[FAIL]]></return_code>"
                            + "<return_msg><![CDATA[参数错误]]></return_msg>" + "</xml> ";
                }
            } else // 如果微信返回支付失败,将错误信息返回给微信
            {
                resXml = "<xml>" + "<return_code><![CDATA[FAIL]]></return_code>"
                        + "<return_msg><![CDATA[交易失败]]></return_msg>" + "</xml> ";
            }
        } else {
            resXml = "<xml>" + "<return_code><![CDATA[FAIL]]></return_code>"
                    + "<return_msg><![CDATA[通知签名验证失败]]></return_msg>" + "</xml> ";
        }
        logger.info("response结果为" + resXml);


        // 处理业务完毕,将业务结果通知给微信
        BufferedOutputStream out = new BufferedOutputStream(response.getOutputStream());
        out.write(resXml.toString().getBytes());//如果toString不好使使用泛型约束
        out.flush();
        out.close();
    }

其实以上的代码就够了 ,了解微信异步回调的其他参数就去参考https://pay.weixin.qq.com/wiki/doc/api/app/app.php?chapter=9_7&index=3

到了这里微信支付就算ok了,微信小程序的支付看下一篇,退款,退款回调什么的看我目录吧;

微信本来就是一个坑很多的一个接入,如果有其他的不了解的地方可以下面评论或者邮箱私信我都可以,一定看到就会回复的,我把所有的包括微信支付,退款,回调,查询,关闭的代码打了个包放到了https://download.csdn.net/download/strugglein/10560640,如果有有土豪玩家或者有不想拷代码的同志可以支持我一下谢谢大家~~~~

本篇完~

虚心的去学习,自信的去工作~

猜你喜欢

转载自blog.csdn.net/Strugglein/article/details/81143244