欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

微信原生支付方式二(实现一)

程序员文章站 2022-03-07 08:29:47
...

1.maven引用:

<dependency>
    <groupId>org.jdom</groupId>
    <artifactId>jdom2</artifactId>
    <version>2.0.6</version>
</dependency>

2.配置文件(application.yml)

wx:
  api_key: *** #生成签名信息
  appid: *** #公众账号ID
  mch_id: *** #商户号
  notify_url: 外网访问的回调地址

3.工具类实现文件

package com.common.util.weChat;

import java.security.MessageDigest;

/**
 * @Author FangYN
 * @Date 2020/6/29 9:16
 * @Description MD5工具类
 **/
public class MD5Util {
    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];
    }

    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"};
}
package com.common.util;

import java.text.ParsePosition;
import java.text.SimpleDateFormat;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.Calendar;
import java.util.Date;
import java.util.regex.Pattern;

/**
 * @Author FangYN
 * @Date 2020/6/29 9:32
 * @Description 时间工具类
 **/
public class DateUtil {

    // 格式:年-月-日 小时:分钟:秒
    public static final String FORMAT_ONE = "yyyy-MM-dd HH:mm:ss";
    // 格式:年-月-日 小时:分钟
    public static final String FORMAT_TWO = "yyyy-MM-dd HH:mm";
    // 格式:年月日 小时分钟秒
    public static final String FORMAT_THREE = "yyyyMMdd-HHmmss";
    // 格式:年月日
    public static final String FORMAT_FOUR = "yyyyMMdd";
    // 格式:年-月-日
    public static final String LONG_DATE_FORMAT = "yyyy-MM-dd";
    // 格式:月-日
    public static final String SHORT_DATE_FORMAT = "MM-dd";
    // 格式:小时:分钟:秒
    public static final String LONG_TIME_FORMAT = "HH:mm:ss";
    //格式:年-月
    public static final String MONTH_DATE_FORMAT = "yyyy-MM";
    // 年的加减
    public static final int SUB_YEAR = Calendar.YEAR;
    // 月加减
    public static final int SUB_MONTH = Calendar.MONTH;
    // 天的加减
    public static final int SUB_DAY = Calendar.DATE;
    // 小时的加减
    public static final int SUB_HOUR = Calendar.HOUR;
    // 分钟的加减
    public static final int SUB_MINUTE = Calendar.MINUTE;
    // 秒的加减
    public static final int SUB_SECOND = Calendar.SECOND;

    static final String dayNames[] = {"星期日", "星期一", "星期二", "星期三", "星期四", "星期五", "星期六"};

    public DateUtil() {
    }

    public static LocalDateTime dateToLocalDateTime(Date date){
        ZoneId zoneId = ZoneId.systemDefault();
        LocalDateTime localDateTime = LocalDateTime.ofInstant(date.toInstant(), zoneId);
        return localDateTime;
    }

    /**
     * 把符合日期格式的字符串转换为日期类型
     */
    public static Date stringToDate(String dateStr, String formatStr) {
        Date d = null;
        SimpleDateFormat format = new SimpleDateFormat(formatStr);
        try {
            format.setLenient(false);
            d = format.parse(dateStr);
        } catch (Exception e) {
            d = null;
        }
        return d;
    }

    /**
     * 把符合日期格式的字符串转换为日期类型
     */
    public static Date stringToDate(String dateStr, String formatStr, ParsePosition pos) {
        Date d = null;
        SimpleDateFormat format = new SimpleDateFormat(formatStr);
        try {
            format.setLenient(false);
            d = format.parse(dateStr, pos);
        } catch (Exception e) {
            d = null;
        }
        return d;
    }

    /**
     * 把日期转换为字符串
     */
    public static String dateToString(Date date, String formatStr) {
        String result = "";
        SimpleDateFormat format = new SimpleDateFormat(formatStr);
        try {
            result = format.format(date);
        } catch (Exception e) {
            // log.error(e);
        }
        return result;
    }

    /**
     * 获取当前时间的指定格式
     */
    public static String getCurrDate(String format) {
        return dateToString(new Date(), format);
    }

    /**
     * @param dateKind 例:Calendar.DAY_OF_MONTH
     * @param dateStr  指定日期
     * @param amount   增加(减去)的时间量
     * @return String
     * @Title dateSub
     * @Date 2014-1-9 上午10:44:02
     * @Description 得到指定日期前(后)的日期
     */
    public static String dateSub(int dateKind, String dateStr, int amount) {
        Date date = stringToDate(dateStr, MONTH_DATE_FORMAT);
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(date);
        calendar.add(dateKind, amount);
        return dateToString(calendar.getTime(), FORMAT_ONE);
    }

    /**
     * @return 昨日日期
     */
    public static String yesterdayDate(String dateStr) {
        Date date = stringToDate(dateStr, LONG_DATE_FORMAT);//取时间
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(date);
        calendar.add(SUB_DAY, -1);//把日期往后增加一天.整数往后推,负数往前移动
        //date=calendar.getTime();  //这个时间就是日期往后推一天的结果
        return dateToString(calendar.getTime(), LONG_DATE_FORMAT);
    }

    /**
     * @return 相减得到的秒数 (两个日期相减)
     */
    public static long timeSub(String firstTime, String secTime) {
        long first = stringToDate(firstTime, FORMAT_ONE).getTime();
        long second = stringToDate(secTime, FORMAT_ONE).getTime();
        return (second - first) / 1000;
    }

    /**
     * 两个日期相减
     * 参数地DATE
     * second 两个日期相差的秒
     *
     * @return 相减得到的秒数
     * 后面时间减去前面时间 再减去 相差秒数  如果大于0 返回 FASLE
     */
    public static boolean timeSub(Date firstTime, Date secTime, long secs) {
        long first = firstTime.getTime();
        long second = secTime.getTime();
        // 判断两个时间 是否间隔那么长 secs。
        return (second - first - secs) <= 0;
    }

    /**
     * 两个日期相减
     * 参数地DATE
     *
     * @return 相减得到的秒数
     * 后面时间减去前面时间 如果大于0 返回 false
     */
    public static boolean timeSub(Date firstTime, Date secTime) {
        long first = firstTime.getTime();
        long second = secTime.getTime();
        return (second - first) <= 0;
    }

    /**
     * 获得某月的天数
     */
    public static int getDaysOfMonth(String year, String month) {
        int days;
        if (month.equals("1") || month.equals("3") || month.equals("5")
                || month.equals("7") || month.equals("8") || month.equals("10")
                || month.equals("12")) {
            days = 31;
        } else if (month.equals("4") || month.equals("6") || month.equals("9")
                || month.equals("11")) {
            days = 30;
        } else {
            if ((Integer.parseInt(year) % 4 == 0 && Integer.parseInt(year) % 100 != 0)
                    || Integer.parseInt(year) % 400 == 0) {
                days = 29;
            } else {
                days = 28;
            }
        }

        return days;
    }

    /**
     * 获取某年某月的天数
     */
    public static int getDaysOfMonth(int year, int month) {
        Calendar calendar = Calendar.getInstance();
        calendar.set(year, month - 1, 1);
        return calendar.getActualMaximum(Calendar.DAY_OF_MONTH);
    }

    /**
     * 获得当前日期
     */
    public static int getToday() {
        Calendar calendar = Calendar.getInstance();
        return calendar.get(SUB_DAY);
    }

    /**
     * 获得当前月份
     */
    public static int getToMonth() {
        Calendar calendar = Calendar.getInstance();
        return calendar.get(SUB_MONTH) + 1;
    }

    /**
     * 获得当前年份
     */
    public static int getToYear() {
        Calendar calendar = Calendar.getInstance();
        return calendar.get(SUB_YEAR);
    }

    /**
     * 返回日期的天
     */
    public static int getDay(Date date) {
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(date);
        return calendar.get(SUB_DAY);
    }

    /**
     * 返回日期的年
     */
    public static int getYear(Date date) {
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(date);
        return calendar.get(SUB_YEAR);
    }

    /**
     * 返回日期的月份,1-12
     */
    public static int getMonth(Date date) {
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(date);
        return calendar.get(SUB_MONTH) + 1;
    }

    /**
     * 计算两个日期相差的天数,如果date2 > date1 返回正数,否则返回负数
     */
    public static long dayDiff(Date date1, Date date2) {
        return (date2.getTime() - date1.getTime()) / 86400000;
    }

    /**
     * 比较两个日期的年差
     */
    public static int yearDiff(String before, String after) {
        Date beforeDay = stringToDate(before, LONG_DATE_FORMAT);
        Date afterDay = stringToDate(after, LONG_DATE_FORMAT);
        return getYear(afterDay) - getYear(beforeDay);
    }

    /**
     * 比较指定日期与当前日期的差
     */
    public static int yearDiffCurr(String after) {
        Date beforeDay = new Date();
        Date afterDay = stringToDate(after, LONG_DATE_FORMAT);
        return getYear(beforeDay) - getYear(afterDay);
    }

    /**
     * 获取每月的第一周
     */
    public static int getFirstWeekdayOfMonth(int year, int month) {
        Calendar c = Calendar.getInstance();
        c.setFirstDayOfWeek(Calendar.SATURDAY); // 星期天为第一天
        c.set(year, month - 1, 1);
        return c.get(Calendar.DAY_OF_WEEK);
    }

    /**
     * 获取每月的最后一周
     */
    public static int getLastWeekdayOfMonth(int year, int month) {
        Calendar c = Calendar.getInstance();
        c.setFirstDayOfWeek(Calendar.SATURDAY); // 星期天为第一天
        c.set(year, month - 1, getDaysOfMonth(year, month));
        return c.get(Calendar.DAY_OF_WEEK);
    }

    /**
     * @return 获得当前日期字符串,格式"yyyy-MM-dd HH:mm:ss"
     */
    public static String getNow() {
        Calendar today = Calendar.getInstance();
        return dateToString(today.getTime(), FORMAT_ONE);
    }

    /**
     * @param date YYYY-mm-dd
     * @return 判断日期是否有效, 包括闰年的情况
     */
    public static boolean isDate(String date) {
        StringBuffer reg = new StringBuffer(
                "^((\\d{2}(([02468][048])|([13579][26]))-?((((0?");
        reg.append("[13578])|(1[02]))-?((0?[1-9])|([1-2][0-9])|(3[01])))");
        reg.append("|(((0?[469])|(11))-?((0?[1-9])|([1-2][0-9])|(30)))|");
        reg.append("(0?2-?((0?[1-9])|([1-2][0-9])))))|(\\d{2}(([02468][12");
        reg.append("35679])|([13579][01345789]))-?((((0?[13578])|(1[02]))");
        reg.append("-?((0?[1-9])|([1-2][0-9])|(3[01])))|(((0?[469])|(11))");
        reg.append("-?((0?[1-9])|([1-2][0-9])|(30)))|(0?2-?((0?[");
        reg.append("1-9])|(1[0-9])|(2[0-8]))))))");
        Pattern p = Pattern.compile(reg.toString());
        return p.matcher(date).matches();
    }


    /*****
     * 时间 增加、减少 n个小时以后时间
     * @param d YYYY-mm-dd HH:mm:ss
     * @param num >0 小时
     * @param type 增加和减少标志
     * **/
    public static Date adjustDateByHour(Date d, Integer num, int type) {
        Calendar Cal = Calendar.getInstance();
//        DateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        Cal.setTime(d);
        if (type == 0) {
            Cal.add(SUB_MINUTE, -num);
            // System.out.println("date:"+df.format(Cal.getTime()));

        } else {
            Cal.add(SUB_MINUTE, num);
            //System.out.println("date:"+df.format(Cal.getTime()));
        }
        return Cal.getTime();
    }

    /*****
     * 时间 增加、减少 n个分钟以后时间
     * @param d YYYY-mm-dd HH:mm:ss
     * @param num>0 分钟
     * @param type 增加和减少标志
     * **/
    public static Date adjustDateByMinutes(Date d, Integer num, int type) {
        Calendar Cal = Calendar.getInstance();
//        DateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        Cal.setTime(d);
        if (type == 0) {
            Cal.add(SUB_MINUTE, -num);
            // System.out.println("date:"+df.format(Cal.getTime()));
        } else {
            Cal.add(SUB_MINUTE, num);
            //  System.out.println("date:"+df.format(Cal.getTime()));
        }
        return Cal.getTime();
    }

//    public static void main(String[] args) {
////      String dateStr = DateUtil.yearthDate("2017-05-30");
////      System.out.println(dateStr);
////      long min = DateUtil.timeSub("2017-04-12 00:00:00", "2017-04-13 00:00:00")/60;
////      System.out.println(min);
//        String settlementDate = DateUtil.dateToString(new Date(), "yyyy-MM-dd");
//        long day = DateUtil.dayDiff(DateUtil.stringToDate("2017-06-22", "yyyy-MM-dd"), DateUtil.stringToDate(settlementDate, "yyyy-MM-dd"));
//        if (day >= 0) {
//            System.out.println(day);
//        }
//
//        String goodsArriveTime = "2017-04-02 17:00-18:00";
//        int space_index = goodsArriveTime.indexOf(" ");
//        String arrive_date = goodsArriveTime.substring(0, space_index);
//        String arrive_time = goodsArriveTime.substring(space_index + 1, goodsArriveTime.length());
//
//        System.out.println(arrive_date);
//        System.out.println(arrive_time);
//        String arrive_start_time = arrive_time.substring(0, 2);
//        String arrive_end_time = arrive_time.substring(6, 8);
//
//        System.out.println(arrive_start_time);
//        System.out.println(arrive_end_time);
//
//        String Time = DateUtil.getCurrDate("HH");
//        System.out.println(Time);
//
//        String Time2 = DateUtil.getCurrDate("mm");
//        System.out.println(Time2);
//    }
}
package com.common.util;

import org.springframework.beans.BeanUtils;
import org.springframework.beans.BeanWrapper;
import org.springframework.beans.BeanWrapperImpl;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;

import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

@Component
public class SpringUtil implements ApplicationContextAware {


    /**
     * @param num 数值
     * @return 数值类型前面补零(共13位)
     */
    public static String supplementZeroGenerateThirteen(int num) {
        return String.format("%013d", num);
    }

    /**
     * @param num 数值
     * @return 数值类型前面补零(共16位)
     */
    public static String supplementZeroGenerateSixteen(int num) {
        return String.format("%016d", num);
    }

    /**
     * @param num 数值
     * @return 数值类型前面补零(共3位)
     */
    public static String supplementZeroGenerateThree(int num) {
        return String.format("%03d", num);
    }

    /**
     * @param str 字符串
     * @return 判断字符串是不是double型
     */
    public static boolean isNumeric(String str) {
        Pattern pattern = Pattern.compile("[0-9]+[.]{0,1}[0-9]*[dD]{0,1}");
        Matcher isNum = pattern.matcher(str);
        if (!isNum.matches()) return false;
        return true;
    }

    public static String trim(String str, boolean nullFlag) {
        String tempStr = null;
        if (str != null) tempStr = str.trim();

        if (nullFlag) {
            if ("".equals(tempStr) || "null".equals(tempStr)) tempStr = null;
        } else {
            if (tempStr == null) tempStr = "";
        }
        return tempStr;
    }

    public static String replace(String strSource, String strFrom, String strTo) {
        if (strSource == null) return null;
        int i = 0;
        if ((i = strSource.indexOf(strFrom, i)) >= 0) {
            char[] cSrc = strSource.toCharArray();
            char[] cTo = strTo.toCharArray();
            int len = strFrom.length();
            StringBuffer buf = new StringBuffer(cSrc.length);
            buf.append(cSrc, 0, i).append(cTo);
            i += len;
            int j = i;
            while ((i = strSource.indexOf(strFrom, i)) > 0) {
                buf.append(cSrc, j, i - j).append(cTo);
                i += len;
                j = i;
            }
            buf.append(cSrc, j, cSrc.length - j);
            return buf.toString();
        }
        return strSource;
    }

    public static String deal(String str) {
        str = replace(str, "\\", "\\\\");
        str = replace(str, "'", "\\'");
        str = replace(str, "\r", "\\r");
        str = replace(str, "\n", "\\n");
        str = replace(str, "\"", "\\\"");
        return str;
    }

    public static String GetMapToXML(Map<String, String> param) {
        StringBuffer sb = new StringBuffer();
        sb.append("<xml>");
        for (Map.Entry<String, String> entry : param.entrySet()) {
            sb.append("<" + entry.getKey() + ">");
            sb.append(entry.getValue());
            sb.append("</" + entry.getKey() + ">");
        }
        sb.append("</xml>");
        return sb.toString();
    }

  /*  public static void main(String[] args) {
        //String a = StringUtil.supplementZeroGenerateThirteen(1000);
        double a = 32.;
        System.out.println(isNumeric("32."));
        System.out.println(a);
    }*/
}
package com.common.util.weChat;

import lombok.Data;
import org.apache.http.Consts;
import org.apache.http.HttpEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.ssl.SSLContexts;
import org.apache.http.util.EntityUtils;
import org.jdom2.Document;
import org.jdom2.Element;
import org.jdom2.JDOMException;
import org.jdom2.input.SAXBuilder;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import javax.net.ssl.SSLContext;
import javax.servlet.http.HttpServletRequest;
import java.io.*;
import java.net.ConnectException;
import java.net.HttpURLConnection;
import java.net.URL;
import java.security.KeyStore;
import java.util.*;

/**
 * @Author FangYN
 * @Date 2020/6/29 9:14
 * @Description 微信支付工具类
 **/
@Component
@Data
public class PayCommonUtil {
    //微信参数配置
    @Value("${wx.api_key}")
    public static String API_KEY;
    /**
     * 应用ID
     */
    @Value("${wx.appid}")
    public static String APP_ID;
    /**
     * 商户号
     */
    @Value("${wx.mch_id}")
    public static String MCH_ID;
    /**
     * 回调地址
     */
    @Value("${wx.notify_url}")
    public static String NOTIFY_URL;

    //随机字符串生成
    public static String getRandomString(int length) { //length表示生成字符串的长度
        String base = "abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
        Random random = new Random();
        StringBuffer sb = new StringBuffer();
        for (int i = 0; i < length; i++) {
            int number = random.nextInt(base.length());
            sb.append(base.charAt(number));
        }
        return sb.toString();
    }

    /**
     * @param request 请求对象
     * @return 读取 request body 内容作为字符串
     * @throws IOException
     */
    public static String readRequest(HttpServletRequest request) throws IOException {
        InputStream inStream = request.getInputStream();
        ByteArrayOutputStream outSteam = new ByteArrayOutputStream();
        byte[] buffer = new byte[1024];
        int len = 0;
        while ((len = inStream.read(buffer)) != -1)
            outSteam.write(buffer, 0, len);
        String resultXml = new String(outSteam.toByteArray(), "utf-8");
        outSteam.close();
        inStream.close();
        return resultXml;
    }

    //请求xml组装
    public static String getRequestXml(SortedMap<String, 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 key = (String) entry.getKey();
            String value = (String) entry.getValue();
            if ("attach".equalsIgnoreCase(key) || "body".equalsIgnoreCase(key) || "sign".equalsIgnoreCase(key)) {
                sb.append("<" + key + ">" + "<![CDATA[" + value + "]]></" + key + ">");
            } else {
                sb.append("<" + key + ">" + value + "</" + key + ">");
            }
        }
        sb.append("</xml>");
        return sb.toString();
    }

    //生成签名
    public static String createSign(String characterEncoding, SortedMap<String, 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=" + API_KEY);
        System.out.println(sb.toString());
        String sign = MD5Util.MD5Encode(sb.toString(), characterEncoding).toUpperCase();
        return sign;
    }

    /**
     * @return 验证回调签名
     */
    public static boolean isTenPaySign(Map<String, String> map) {
        String characterEncoding = "utf-8";
        String charset = "utf-8";
        String signFromAPIResponse = map.get("sign");
        if (signFromAPIResponse == null || signFromAPIResponse.equals("")) {
            System.out.println("API返回的数据签名数据不存在,有可能被第三方篡改!!!");
            return false;
        }
        System.out.println("服务器回包里面的签名是:" + signFromAPIResponse);
        //过滤空 设置 TreeMap
        SortedMap<String, String> packageParams = new TreeMap();
        for (String parameter : map.keySet()) {
            String parameterValue = map.get(parameter);
            String v = "";
            if (null != parameterValue) {
                v = parameterValue.trim();
            }
            packageParams.put(parameter, v);
        }

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

        //将API返回的数据根据用签名算法进行计算新的签名,用来跟API返回的签名进行比较
        //算出签名
        String resultSign = "";
        String tobeSign = sb.toString();

        if (null == charset || "".equals(charset)) {
            resultSign = MD5Util.MD5Encode(tobeSign, characterEncoding).toUpperCase();
        } else {
            try {
                resultSign = MD5Util.MD5Encode(tobeSign, characterEncoding).toUpperCase();
            } catch (Exception e) {
                resultSign = MD5Util.MD5Encode(tobeSign, characterEncoding).toUpperCase();
            }
        }

        String tenPaySign = ((String) packageParams.get("sign")).toUpperCase();
        return tenPaySign.equals(resultSign);
    }

    //请求方法
    public static String httpsRequest(String requestUrl, String requestMethod, String outputStr) {
        try {
            URL url = new URL(requestUrl);
            HttpURLConnection conn = (HttpURLConnection) url.openConnection();

            conn.setDoOutput(true);
            conn.setDoInput(true);
            conn.setUseCaches(false);
            // 设置请求方式(GET/POST)
            conn.setRequestMethod(requestMethod);
            conn.setRequestProperty("content-type", "application/x-www-form-urlencoded");
            // 当outputStr不为null时向输出流写数据
            if (null != outputStr) {
                OutputStream outputStream = conn.getOutputStream();
                // 注意编码格式
                outputStream.write(outputStr.getBytes("UTF-8"));
                outputStream.close();
            }
            // 从输入流读取返回内容
            InputStream inputStream = conn.getInputStream();
            InputStreamReader inputStreamReader = new InputStreamReader(inputStream, "utf-8");
            BufferedReader bufferedReader = new BufferedReader(inputStreamReader);
            String str = null;
            StringBuffer buffer = new StringBuffer();
            while ((str = bufferedReader.readLine()) != null) {
                buffer.append(str);
            }
            // 释放资源
            bufferedReader.close();
            inputStreamReader.close();
            inputStream.close();
            inputStream = null;
            conn.disconnect();
            return buffer.toString();
        } catch (ConnectException ce) {
            System.out.println("连接超时:{}" + ce);
        } catch (Exception e) {
            System.out.println("https请求异常:{}" + e);
        }
        return null;
    }

    //退款的请求方法
    public static String httpsRequest2(String requestUrl, String requestMethod, String outputStr) throws Exception {
        KeyStore keyStore = KeyStore.getInstance("PKCS12");
        StringBuilder res = new StringBuilder("");
        FileInputStream inStream = new FileInputStream(new File("/home/apiclient_cert.p12"));
        try {
            keyStore.load(inStream, "".toCharArray());
        } finally {
            inStream.close();
        }

        // Trust own CA and all self-signed certs
        SSLContext sslcontext = SSLContexts.custom()
                .loadKeyMaterial(keyStore, "1313329201".toCharArray())
                .build();
        // Allow TLSv1 protocol only
        SSLConnectionSocketFactory sslCSF = new SSLConnectionSocketFactory(
                sslcontext,
                new String[]{"TLSv1"},
                null,
                SSLConnectionSocketFactory.BROWSER_COMPATIBLE_HOSTNAME_VERIFIER);
        CloseableHttpClient httpClient = HttpClients.custom()
                .setSSLSocketFactory(sslCSF)
                .build();
        try {

            HttpPost httpPost = new HttpPost("https://api.mch.weixin.qq.com/secapi/pay/refund");
            httpPost.addHeader("Connection", "keep-alive");
            httpPost.addHeader("Accept", "*/*");
            httpPost.addHeader("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8");
            httpPost.addHeader("Host", "api.mch.weixin.qq.com");
            httpPost.addHeader("X-Requested-With", "XMLHttpRequest");
            httpPost.addHeader("Cache-Control", "max-age=0");
            httpPost.addHeader("User-Agent", "Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.0) ");
            StringEntity entity2 = new StringEntity(outputStr, Consts.UTF_8);
            httpPost.setEntity(entity2);
            System.out.println("executing request" + httpPost.getRequestLine());
            CloseableHttpResponse response = httpClient.execute(httpPost);

            try {
                HttpEntity entity = response.getEntity();
                System.out.println("----------------------------------------");
                System.out.println(response.getStatusLine());
                if (entity != null) {
                    System.out.println("Response content length: " + entity.getContentLength());
                    BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(entity.getContent()));
                    String text = "";
                    res.append(text);
                    while ((text = bufferedReader.readLine()) != null) {
                        res.append(text);
                        System.out.println(text);
                    }

                }
                EntityUtils.consume(entity);
            } finally {
                response.close();
            }
        } finally {
            httpClient.close();
        }
        return res.toString();
    }

    //xml解析
    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 = getChildrenText(children);
            }
            m.put(k, v);
        }
        in.close();  //关闭流
        return m;
    }

    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(getChildrenText(list));
                }
                sb.append(value);
                sb.append("</" + name + ">");
            }
        }

        return sb.toString();
    }

    public static String getRemoteHost(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.getRemoteAddr();
        return ip.equals("0:0:0:0:0:0:0:1") ? "127.0.0.1" : ip;
    }
}

4.具体实现

package com.service.impl;

import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.common.util.DateUtil;
import com.common.util.SpringUtil;
import com.common.util.UUIDUtils;
import com.common.util.weChat.PayCommonUtil;
import com.domain.WxOrder;
import com.orange.physical.mapper.WxOrderMapper;
import com.service.IWxOrderService;
import com.vo.pay.WeChatPayVo;
import org.apache.commons.io.IOUtils;
import org.jdom2.JDOMException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.ObjectUtils;

import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.math.BigDecimal;
import java.util.HashMap;
import java.util.Map;
import java.util.SortedMap;
import java.util.TreeMap;

/**
 * @Author: FangYN
 * @Date: 2020/6/29
 * @Description: 微信订单支付逻辑实现
 */
@Service
public class **ServiceImpl extends ServiceImpl<**Mapper, **> implements I**Service {
    private static Logger log = LoggerFactory.getLogger(WxOrderServiceImpl.class);
    private String randomString = PayCommonUtil.getRandomString(32);
    private static final String PLACE_URL = "https://api.mch.weixin.qq.com/pay/unifiedorder";// 下单 API 地址
    @Autowired
    private WxPayService wxService;

    // 创建预订单,返回收款二维码
    @Transactional(rollbackFor = Exception.class)
    @Override
    public WeChatPayVo createOrder(HttpServletRequest request) throws Exception {
        BigDecimal totalAmount = new BigDecimal("0.01");// 支付金额
        String tradeNo = UUIDUtils.getUUID();// 订单编号
        String description = request.getParameter("description");// 订单描述
        String productId = "1";
        Map<String, String> map = weChatPrePay(tradeNo, totalAmount, description, productId, productId, request);
        if ("FAIL".equals(map.get("return_code")) || "FAIL".equals(map.get("result_code")))
            throw new Exception("订单创建失败,请重新请求");
        // 保存订单信息
        WxOrder insertOrder = new WxOrder();
        insertOrder.setTradeNo(tradeNo);
        insertOrder.setPrepayId(map.get("prepay_id"));
        insertOrder.setTotalFee(totalAmount);
        insertOrder.setBody(description);
        insertOrder.setDetail(description);
        baseMapper.insert(insertOrder);
        // 返回订单相关数据
//        Map app = new HashMap();
//        app.put("appid", PayCommonUtil.APP_ID);
//        app.put("partnerid",PayCommonUtil.MCH_ID);
//        app.put("prepayid", map.get("prepay_id"));
//        app.put("package", "Sign=WXPay");                   // 固定字段,保留,不可修改
//        app.put("noncestr", map.get("nonce_str"));
//        app.put("timestamp", new Date().getTime() / 1000);  // 时间为秒,JDK 生成的是毫秒,故除以 1000

        WeChatPayVo prePay = new WeChatPayVo();
        prePay.setPrice(totalAmount);
        prePay.setDescription(description);
        prePay.setTradeNo(tradeNo);
        prePay.setOrderId(insertOrder.getOrderId());
        prePay.setPrepayId(insertOrder.getPrepayId());
        prePay.setCodeUrl(map.get("code_url"));
        return prePay;
    }

    /**
     * 统一下单
     * 应用场景:商户系统先调用该接口在微信支付服务后台生成预支付交易单,返回正确的预支付交易回话标识后再在APP里面调起支付。
     *
     * @param tradeNo     系统生成的订单号
     * @param totalAmount 订单金额
     * @param description 订单描述
     * @param openid      用户标识 trade_type=JSAPI时(即JSAPI支付),此参数必传,此参数为微信用户在商户对应appid下的唯一标识
     * @param productId   trade_type=NATIVE时,此参数必传。此参数为二维码中包含的商品ID
     * @param request     请求信息
     * @return 预支付信息
     */
    @SuppressWarnings("unchecked")
    public Map<String, String> weChatPrePay(String tradeNo, BigDecimal totalAmount, String description, String openid, String productId, HttpServletRequest request) throws Exception {
        SortedMap<String, Object> parameterMap = new TreeMap<String, Object>();
        parameterMap.put("appid", PayCommonUtil.APP_ID); // 应用appid
        parameterMap.put("mch_id", PayCommonUtil.MCH_ID); // 商户号
        parameterMap.put("device_info", "WEB");
        parameterMap.put("nonce_str", randomString);
        parameterMap.put("body", description);// 商品名称
        parameterMap.put("out_trade_no", tradeNo);// 当前系统生成的订单编号
        parameterMap.put("fee_type", "CNY");// 默认:人民币
        BigDecimal total = totalAmount.multiply(new BigDecimal(100)); //接口中参数支付金额单位为【分】,参数值不能带小数,所以乘以100
        java.text.DecimalFormat df = new java.text.DecimalFormat("0");
        parameterMap.put("total_fee", df.format(total.intValue()));// 标价金额
        parameterMap.put("spbill_create_ip", PayCommonUtil.getRemoteHost(request));// 终端IP
        parameterMap.put("notify_url", PayCommonUtil.NOTIFY_URL);// 通知地址
        parameterMap.put("trade_type", "NATIVE");// "JSAPI","APP"
        if ("JSAPI".equals(parameterMap.get("trade_type").toString()))
            parameterMap.put("openid", openid);
        if ("NATIVE".equals(parameterMap.get("trade_type").toString()))
            parameterMap.put("product_id", productId);
        parameterMap.put("sign", PayCommonUtil.createSign("UTF-8", parameterMap));
        String requestXML = PayCommonUtil.getRequestXml(parameterMap);
        log.info("微信统一下单:下单构建数据请求内容===>" + requestXML);
        String result = PayCommonUtil.httpsRequest(PLACE_URL, "POST", requestXML);
        log.info("微信统一下单:下单返回数据===>" + result);
        Map<String, String> map;
        try {
            map = PayCommonUtil.doXMLParse(result);
        } catch (JDOMException e) {
            e.printStackTrace();
            throw new Exception("订单创建失败");
        } catch (IOException e) {
            e.printStackTrace();
            throw new Exception("订单创建失败");
        }
        return map;
    }

    // 支付成功回调方法
    @Transactional(rollbackFor = Exception.class)
    @Override
    public String callBack(HttpServletRequest request) throws IOException, JDOMException {
        // 读取参数,解析Xml为map
        Map<String, String> params = PayCommonUtil.doXMLParse(PayCommonUtil.readRequest(request));
        log.info("微信notify:微信支付回调:读取数据result===>" + params);
        if (!PayCommonUtil.isTenPaySign(params) || "FAIL".equals(params.get("return_code"))) {// 签名验证失败
            return fail();
        }
        String tradeNo = params.get("out_trade_no");// 系统订单号
        if ("FAIL".equals(params.get("result_code"))) {// 支付失败
            baseMapper.updateStatusByTradeNo(tradeNo, "2", params.get("err_code_des"));
            return fail();
        }
        log.info("微信notify:微信支付回调:修改的订单===>" + tradeNo);
      
        // 更新订单交易成功相关数据等逻辑
        // 验证订单金额是否一致
        // BigDecimal totalFee = new BigDecimal(params.get("total_fee")).divide(new BigDecimal(100), 2, BigDecimal.ROUND_UNNECESSARY);
        // if (totalFee.compareTo(**.getTotalFee()) != 0)
        //   return fail();
        return success();

    }

    private String fail() {
        Map<String, String> return_data = new HashMap<>();
        return_data.put("return_code", "FAIL");
        return_data.put("return_msg", "return_code不正确");
        return SpringUtil.GetMapToXML(return_data);
    }

    private String success() {
        Map<String, String> return_data = new HashMap<>();
        return_data.put("return_code", "SUCCESS");
        return_data.put("return_msg", "OK");
        return SpringUtil.GetMapToXML(return_data);
    }
}

 

 

相关标签: Java 微信支付