微信原生支付方式二(实现一)
程序员文章站
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);
}
}
上一篇: Python如何测试stdout输出