准备工作:
登录微信开放平台创建应用开通支付功能支付功能
获得AppID、商户ID、 秘钥
import com.alibaba.fastjson.JSON;
import lombok.Data;
import org.apache.commons.lang3.StringUtils;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.DocumentHelper;
import org.dom4j.Element;
import java.io.*;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.URL;
import java.net.URLConnection;
import java.nio.ByteBuffer;
import java.util.*;
import org.apache.commons.codec.binary.Base64;
import org.springframework.util.DigestUtils;
import java.nio.charset.StandardCharsets;
public class WxPayUtils {
@Data
public static class PayInfo<T> {
private String orderId;
private String subscriptionId;
private boolean ifSandboxOrder;
private T result;
private String resultJson;
private String tradeNo;
private String tradeType;
private String paymentType;
private String payerType;
private String recAccount;
private String payTime;
private String orderStatus = "0";
}
@Data
public class WxPayAppPayResponse {
//返回状态码
private String return_code;
//返回信息
private String return_msg;
//以下字段在return_code为SUCCESS的时候有返回
//应用APPID
private String appid;
//商户号
private String mch_id;
//设备号
private String device_info;
//随机字符串
private String nonce_str;
//签名
private String sign;
//业务结果
private String result_code;
//错误代码
private String err_code;
//错误代码描述
private String err_code_des;
//以下字段在return_code 和result_code都为SUCCESS的时候有返回
//交易类型
private String trade_type;
//预支付交易会话标识
private String prepay_id;
}
private static final String WXPayUnifiedorder = "https://api.mch.weixin.qq.com/pay/unifiedorder";
// APPID ,接入时生成
private static final String appId = "XXXXXXX";
// 商户ID,接入时生成
private static final String mchId = "XXXXXXX";
// appSecret密钥
private static final String appSecret = "1636e***********a57222";
private static final String FIELD_SIGN = "sign";
private static int connectTimeout = 3000;
/**
* 微信支付加签
*
* @param body 业务主题
* @param price 交易金额
* @param notifyUrl 异步通知URL
* @return
*/
public WxPayUtils.PayInfo createOrder(String body, String price, String notifyUrl) {
WxPayUtils.PayInfo<String> payInfo = new WxPayUtils.PayInfo<>();
Map<String, String> paramMap = new HashMap<>();
paramMap.put("appid", appId);
paramMap.put("mch_id", mchId);
paramMap.put("nonce_str", getRandom(16));
paramMap.put("body", body);
paramMap.put("out_trade_no", getBaseUUid());
paramMap.put("total_fee", price);
paramMap.put("fee_type", "CNY"); // 交易币种 默认人民币CNY
paramMap.put("spbill_create_ip", "127.0.0.1"); // 终端ip
paramMap.put("notify_url", notifyUrl);
paramMap.put("trade_type", "APP"); // 支付场景 APP 微信app支付 NATIVE 扫码支付
paramMap.put("sign", createMd5Sign(paramMap, appSecret));
String xmlStr = sendPost(WXPayUnifiedorder, toWxStrXml(paramMap));
WxPayAppPayResponse rstObj = strXmlToEntity(xmlStr, WxPayAppPayResponse.class);
if (rstObj != null && StringUtils.isNotBlank(rstObj.getSign()) && StringUtils.isNotBlank(rstObj.getPrepay_id())) {
payInfo.setResult(JSON.toJSONString(strXmlToMap(xmlStr)));
paramMap = new HashMap<>();
paramMap.put("appid", appId);
paramMap.put("partnerid", mchId);
paramMap.put("prepayid", rstObj.getPrepay_id());
paramMap.put("package", "Sign=WXPay");
paramMap.put("noncestr", rstObj.getNonce_str());
paramMap.put("timestamp", System.currentTimeMillis() / 1000 + "");
paramMap.put("sign", createMd5Sign(paramMap, appSecret)); // 生成二次签名,APP端调起支付客户端时无需再次签名
payInfo.setResultJson(JSON.toJSONString(paramMap));
return payInfo;
}
return payInfo;
}
public static String getBaseUUid() {
ByteBuffer bb = ByteBuffer.wrap(new byte[16]);
UUID uuid = UUID.randomUUID();
bb.putLong(uuid.getMostSignificantBits());
bb.putLong(uuid.getLeastSignificantBits());
return Base64.encodeBase64URLSafeString(bb.array());
}
public static String getRandom(int len) {
String[] tempRandom = {"0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z",};
Random a = new Random();
int nextInt = a.nextInt(len);
nextInt += 16;
StringBuffer stringBuffer = new StringBuffer();
for (int i = 0; i < nextInt; i++) {
int nextInt2 = a.nextInt(36);
stringBuffer.append(tempRandom[nextInt2]);
}
String currentTimeMillis = System.currentTimeMillis() + "";
int length = stringBuffer.length();
if (length < 32) {
if ((currentTimeMillis.length() - 32) > 0) {
stringBuffer.append(currentTimeMillis.substring(currentTimeMillis.length() - 32 + length));
}
}
return stringBuffer.toString();
}
/**
* 拼接wx xml字符串
*
* @param paramMap
* @return
*/
public static String toWxStrXml(Map<String, String> paramMap) {
StringBuilder sb = new StringBuilder();
sb.append("" );
for (String key : paramMap.keySet()) {
String value = paramMap.get(key);
if (value == null) {
value = "";
}
value = value.trim();
sb.append("<").append(key).append(">").append(").append(value).append("]]>").append("").append(key).append(">");
}
sb.append("");
return sb.toString();
}
public static Map<String, String> strXmlToMap(String strXml) {
if (StringUtils.isBlank(strXml)) {
return null;
}
try {
Document document = DocumentHelper.parseText(strXml);
Element root = document.getRootElement();
Iterator<?> iter = root.elementIterator();
//利用反射机制,调用set方法
//获取该实体的元类型
Map<String, String> msg = new HashMap<>();//创建这个实体的对象
while (iter.hasNext()) {//对xml文件进行解析
Element ele = (Element) iter.next();
msg.put(ele.getName(), ele.getText());
}
return msg;
} catch (DocumentException e) {
e.printStackTrace();
}
return null;
}
/**
* 创建md5 数字签证
*
* @param paramMap
* @param secret
* @return
*/
public static String createMd5Sign(Map<String, String> paramMap, String secret) {
Set<String> keySet = paramMap.keySet();
String[] keyArray = keySet.toArray(new String[0]);
Arrays.sort(keyArray);
StringBuilder sb = new StringBuilder();
for (String key : keyArray) {
if (key.equals(FIELD_SIGN)) {
continue;
}
if (paramMap.get(key) != null && paramMap.get(key).trim().length() > 0) { // 参数值为空,则不参与签名
sb.append(key).append("=").append(paramMap.get(key).trim()).append("&");
}
}
sb.append("key=").append(secret);
return DigestUtils.md5DigestAsHex(sb.toString().getBytes(StandardCharsets.UTF_8)).toUpperCase();
}
public static String sendPost(String url, String param) {
try {
URLConnection conn = null;
URL realUrl = new URL(url);
conn = realUrl.openConnection();
conn.setRequestProperty("accept", "*/*");
conn.setRequestProperty("connection", "Keep-Alive");
conn.setRequestProperty("user-agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)");
conn.setDoOutput(true);
conn.setDoInput(true);
conn.setConnectTimeout(connectTimeout);
PrintWriter out = new PrintWriter(new OutputStreamWriter(conn.getOutputStream(), StandardCharsets.UTF_8));
Throwable var5 = null;
try {
out.print(param);
out.flush();
BufferedReader in = new BufferedReader(new InputStreamReader(conn.getInputStream(), StandardCharsets.UTF_8));
Throwable var7 = null;
try {
StringBuffer result = new StringBuffer();
String line;
while((line = in.readLine()) != null) {
result.append(line);
}
String var10 = result.toString();
return var10;
} catch (Throwable var35) {
var7 = var35;
throw var35;
} finally {
if (in != null) {
if (var7 != null) {
try {
in.close();
} catch (Throwable var34) {
var7.addSuppressed(var34);
}
} else {
in.close();
}
}
}
} catch (Throwable var37) {
var5 = var37;
throw var37;
} finally {
if (out != null) {
if (var5 != null) {
try {
out.close();
} catch (Throwable var33) {
var5.addSuppressed(var33);
}
} else {
out.close();
}
}
}
} catch (IOException var39) {
var39.printStackTrace();
return null;
}
}
public static <T> T strXmlToEntity(String strXml, Class<T> c) {
if (StringUtils.isBlank(strXml)) {
return null;
}
try {
Document document = DocumentHelper.parseText(strXml);
Element root = document.getRootElement();
Iterator<?> iter = root.elementIterator();
//利用反射机制,调用set方法
//获取该实体的元类型
T msg = c.newInstance();//创建这个实体的对象
while (iter.hasNext()) {//对xml文件进行解析
Element ele = (Element) iter.next();
Field field = null;
try {
field = c.getDeclaredField(ele.getName());
} catch (NoSuchFieldException | SecurityException ignored) {
}
if (field != null) {
//获取set方法,field.getType())获取它的参数数据类型
String name = ele.getName().substring(0, 1).toUpperCase() + ele.getName().substring(1);
Method method = c.getDeclaredMethod("set" + name, field.getType());
//调用set方法
method.invoke(msg, ele.getText());
}
}
return msg;
} catch (DocumentException | IllegalAccessException | InvocationTargetException | NoSuchMethodException | InstantiationException e) {
e.printStackTrace();
}
return null;
}
}