支付工具类PayUtil 主要用来获取生成微信调取支付页面的参数
package com.systek.scenic.utils.wechatApp;
import java.io.BufferedInputStream;
import java.io.ByteArrayOutputStream;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import javax.servlet.http.HttpServletRequest;
import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.HttpStatus;
import org.apache.commons.httpclient.methods.PostMethod;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.DocumentHelper;
import org.dom4j.Element;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class PayUtil {
private final static String appId = "xxxxxxxxxx";//自己的配置appid
private final static String mchId = "xxxxxxxxxx"; //微信支付分配的商户号
private final static String key = "xxxxxxxxxxxxxxxxxxxxxxx"; //微信支付分配的商户号key
private final static String spbill_create_ip = "xx.xxx.xxx.xx"; //调用微信支付API的机器IP 本机IP地址
private final static String notify_url = "xxxxxxxxxxxxxxxxxxxxxxxxx"; //异步接收微信支付结果通知的回调地址,通知url必须为外网可访问的url,不能携带参数。
/*
*
* @description:
* @author: YinZengXiang
* @date: 2020/3/5 11:04
* @param: openid //微信用户身份标识
* @param: body //支付才用的参数
* @param: total_fee //支付总价格分
* @param: out_trade_no //订单号
* @return: java.util.Map<java.lang.String,java.lang.Object>
*/
public static Map<String, Object> takeOrder(String openid,String body,String total_fee,String out_trade_no) throws Exception {
String url = "https://api.mch.weixin.qq.com/pay/unifiedorder";
String nonce_str = UUID.randomUUID().toString().substring(0, 32).replace("-","");//生成随机数,可直接用系统提供的方法 ; //随机字符串,长度要求在32位以内。推荐随机数生成算法
Map<String,Object> params = new HashMap<>();
params.put("appid",appId);
params.put("mch_id",mchId);
params.put("device_info",total_fee);
params.put("nonce_str",nonce_str);
params.put("sign_type","MD5"); //签名方式选用md5
params.put("body",body);
params.put("out_trade_no",out_trade_no); //商户系统内部订单号,要求32个字符内,只能是数字、大小写字母_-|*且在同一个商户号下唯一。
params.put("total_fee",Integer.valueOf(total_fee)); //订单总金额,单位为分,
params.put("spbill_create_ip",spbill_create_ip); //回调IP地址,
params.put("notify_url",notify_url); //回调函数
params.put("trade_type","JSAPI"); //交易类型 小程序取值如下:JSAPI
params.put("openid",openid); //交易类型 小程序取值如下:JSAPI
String sign = OrderUtil.sign(params, key);//生成签名
params.put("sign",sign); // 签名一定要放在最后面!!!!!!!
String xmlResult = OrderUtil.ArrayToXml(params);//将map集合转成xml,供回调接口使用
//发送一次签名
String result = null;
result = post(url,xmlResult);
//解析一次签名获得的数据
Map<String ,Object> signMap = new HashMap<>();
Map<String,Object> bean = readStringXmlOut(result);
System.out.println("==========签名返回参数===============");
System.out.println(bean);
System.out.println("==========签名返回参数===============");
String prepayId = bean.get("prepay_id").toString();
signMap.put("timeStamp", System.currentTimeMillis() / 1000 + "");
signMap.put("nonceStr", nonce_str);
signMap.put("package", "prepay_id="+prepayId);
signMap.put("signType", "MD5");
signMap.put("appId", appId);
String paySign2 = OrderUtil.sign(signMap, key);
signMap.put("paySign", paySign2);
return signMap ;
}
@SuppressWarnings("deprecation")
public static String post(String url,String xmlFileName){
//关闭
System.setProperty("org.apache.commons.logging.Log", "org.apache.commons.logging.impl.SimpleLog");
System.setProperty("org.apache.commons.logging.simplelog.showdatetime", "true");
System.setProperty("org.apache.commons.logging.simplelog.log.org.apache.commons.httpclient", "stdout");
//创建httpclient工具对象
HttpClient client = new HttpClient();
//创建post请求方法
PostMethod myPost = new PostMethod(url);
//设置请求超时时间
client.setConnectionTimeout(300*1000);
String responseString = null;
try{
//设置请求头部类型
myPost.setRequestHeader("Content-Type","text/xml");
myPost.setRequestHeader("charset","utf-8");
myPost.setRequestBody(xmlFileName);
int statusCode = client.executeMethod(myPost);
if(statusCode == HttpStatus.SC_OK){
BufferedInputStream bis = new BufferedInputStream(myPost.getResponseBodyAsStream());
byte[] bytes = new byte[1024];
ByteArrayOutputStream bos = new ByteArrayOutputStream();
int count = 0;
while((count = bis.read(bytes))!= -1){
bos.write(bytes, 0, count);
}
byte[] strByte = bos.toByteArray();
responseString = new String(strByte,0,strByte.length,"utf-8");
bos.close();
bis.close();
}
}catch (Exception e) {
e.printStackTrace();
}
myPost.releaseConnection();
return responseString;
}
public static Map<String, Object> readStringXmlOut(String xml) {
Map<String, Object> map = new HashMap<String, Object>();
Document doc = null;
try {
doc = DocumentHelper.parseText(xml); // 将字符串转为XML
Element rootElt = doc.getRootElement(); // 获取根节点
@SuppressWarnings("unchecked")
List<Element> list = rootElt.elements();// 获取xx根节点下所有节点
for (Element element : list) { // 遍历节点
map.put(element.getName(), element.getText()); // 节点的name为map的key,text为map的value
}
} catch (DocumentException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
return map;
}
}
OrderUtil 此工具主要用来生成签名
package com.systek.scenic.utils.wechatApp;
import java.net.URLEncoder;
import java.security.MessageDigest;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
/*
*
* @description:此工具类用来生成支付所需的签名
* @author: YinZengXiang
* @date: 2020/3/5 11:11
* @param: null
* @return:
*/
public class OrderUtil {
/**
* sha1加密
* @param str
* @return
*/
public static String sha1(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("SHA1");
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);
} catch (Exception e) {
return null;
}
}
/**
* 生成签名sign
* 第一步:非空参数值的参数按照参数名ASCII码从小到大排序,按照键值对的形式。生成字符串StringA
* stringA="appid=wxd930ea5d5a258f4f&body=test&device_info=1000&mch_id=10000100&nonce_str=ibuaiVcKdpRxkhJA";
* 第二部:拼接API密钥,这里的秘钥是微信商户平台的秘钥,是自己设置的,不是公众号的秘钥
* stringSignTemp="stringA&key=192006250b4c09247ec02edce69f6a2d"
* 第三部:MD5加密
* sign=MD5(stringSignTemp).toUpperCase()="9A0A8659F005D6984697E2CA0A9CF3B7"
*
* @param map 不包含空字符串的map
* @return
* @throws Exception
*/
public static String sign(Map<String, Object> map,String key) throws Exception {
String bizString = FormatBizQueryParaMap(map, false);
return MD5.sign(bizString, key);
}
public static String FormatBizQueryParaMap(Map<String, Object> paraMap, boolean urlencode) throws Exception {
String buff = "";
try {
List<Map.Entry<String, Object>> infoIds = new ArrayList<Map.Entry<String, Object>>(paraMap.entrySet());
Collections.sort(infoIds,
new Comparator<Map.Entry<String, Object>>() {
public int compare(Map.Entry<String, Object> o1, Map.Entry<String, Object> o2) {
return (o1.getKey()).toString().compareTo(o2.getKey());
}
});
for (int i = 0; i < infoIds.size(); i++) {
Map.Entry<String, Object> item = infoIds.get(i);
//System.out.println(item.getKey());
if (item.getKey() != "") {
String key = item.getKey();
String val = item.getValue().toString();
if (urlencode) {
val = URLEncoder.encode(val, "utf-8");
}
buff += key + "=" + val + "&";
}
}
if (buff.isEmpty() == false) {
buff = buff.substring(0, buff.length() - 1);
}
} catch (Exception e) {
throw new Exception(e.getMessage());
}
return buff;
}
/**
* 返回key的值
* @param map
* @param key
* @return
*/
private static String getParamString(Map<String,Object> map, String key) {
String buf = "";
if (map.get(key) instanceof String[]) {
buf = ((String[]) map.get(key))[0];
} else {
buf = (String) map.get(key);
}
return buf;
}
/**
* 字符串列表从大到小排序
* @param data
* @return
*/
@SuppressWarnings("unused")
private static List<String> sort(List<String> data) {
Collections.sort(data, new Comparator<String>() {
public int compare(String obj1, String obj2) {
return obj1.compareTo(obj2);
}
});
return data;
}
/**
* Map转Xml
* @param arr
* @return
*/
public static String MapToXml(Map<String, String> arr) {
String xml = "<xml>";
Iterator<Entry<String, String>> iter = arr.entrySet().iterator();
while (iter.hasNext()) {
Entry<String, String> entry = iter.next();
String key = entry.getKey();
String val = entry.getValue();
if (IsNumeric(val)) {
xml += "<" + key + ">" + val + "</" + key + ">";
} else
xml += "<" + key + "><![CDATA[" + val + "]]></" + key + ">";
}
xml += "</xml>";
return xml;
}
private static boolean IsNumeric(String str) {
if (str.matches("\\d *")) {
return true;
} else {
return false;
}
}
public static String ArrayToXml(Map<String, Object> arr) {
String xml = "<xml>";
Iterator<Entry<String, Object>> iter = arr.entrySet().iterator();
while (iter.hasNext()) {
Entry<String, Object> entry = iter.next();
String key = entry.getKey();
String val = entry.getValue().toString();
if (IsNumeric(val)) {
xml += "<" + key + ">" + val + "</" + key + ">";
} else
xml += "<" + key + "><![CDATA[" + val + "]]></" + key + ">";
}
xml += "</xml>";
return xml;
}
/**
* 对参数列表进行排序,并拼接key=value&key=value形式
* @param map
* @return
*/
public static String sortParameters(Map<String, Object> map) {
Set<String> keys = map.keySet();
List<String> paramsBuf = new ArrayList<String>();
for (String k : keys) {
paramsBuf.add((k + "=" + getParamString(map, k)));
}
// 对参数排序
Collections.sort(paramsBuf);
String result="";
int count=paramsBuf.size();
for(int i=0;i<count;i++){
if(i<(count-1)){
result+=paramsBuf.get(i)+"&";
}else {
result+=paramsBuf.get(i);
}
}
return result;
}
}
MD5 工具类 使用MD5算法获取加密后的参数 主要用于生成sign
package com.systek.scenic.utils.wechatApp;
import java.security.MessageDigest;
public class MD5 {
private final static String[] hexDigits = {"0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "A", "B", "C", "D", "E", "F"};
/**
* 转换字节数组为16进制字串
*
* @param b
* 字节数组
* @return 16进制字串
*/
public static String byteArrayToHexString(byte[] b) {
StringBuilder resultSb = new StringBuilder();
for (byte aB : b) {
resultSb.append(byteToHexString(aB));
}
return resultSb.toString();
}
/**
* 转换byte到16进制
*
* @param b
* 要转换的byte
* @return 16进制格式
*/
private static String byteToHexString(byte b) {
int n = b;
if (n < 0) {
n = 256 + n;
}
int d1 = n / 16;
int d2 = n % 16;
return hexDigits[d1] + hexDigits[d2];
}
/**
* MD5编码
*
* @param origin
* 原始字符串
* @return 经过MD5加密之后的结果
*/
public static String MD5Encode(String origin) {
String resultString = null;
try {
resultString = origin;
MessageDigest md = MessageDigest.getInstance("MD5");
md.update(resultString.getBytes("UTF-8"));
resultString = byteArrayToHexString(md.digest());
} catch (Exception e) {
e.printStackTrace();
}
return resultString;
}
/**
* 微信支付获取prepay_id
* @param content
* @param key
* @return
* @throws Exception
*/
public static String sign(String content, String key) throws Exception{
String signStr = "";
signStr = content + "&key=" + key;
return MD5COVER(signStr).toUpperCase();
}
/**
* 微信支付获取prepay_id加密使用
*
* @param s
* @return
*/
public final static String MD5COVER(String s) {
char hexDigits[] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
try {
byte[] btInput = s.getBytes();
MessageDigest mdInst = MessageDigest.getInstance("MD5");
mdInst.update(btInput);
byte[] md = mdInst.digest();
int j = md.length;
char str[] = new char[j * 2];
int k = 0;
for (int i = 0; i < j; i++) {
byte byte0 = md[i];
str[k++] = hexDigits[byte0 >>> 4 & 0xf];
str[k++] = hexDigits[byte0 & 0xf];
}
return new String(str);
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
}
获取支付参数的Service
public Map<String, Object> takeOrder(AppUser appUser, PayOrder payOrder) throws Exception {
//创建订单
//String openid,String body,String total_fee,String out_trade_no
String openid = appUser.getWeChatOpenId();
String body = payOrder.getBody();
double totalFeeD = payOrder.getTotalFee();
int s = (int) (totalFeeD*100);
String totalFee = String.valueOf(s);
String prepayId = "";
//生成订单号
String outTradeNo = StringUtil.getUuidNoneHyphen();
//需要返回的字段
Map<String, Object> stringObjectMap = PayUtil.takeOrder(openid, body, totalFee, outTradeNo);
//获取prepayId
System.out.println(stringObjectMap);
return stringObjectMap;
}
小程序调用方式
wx.request({
url: 'https://systek.imdo.co/appsrv/api/PayOrder/takeOrder',
data:
{
weChatOpenId: res.data.weChatOpenId,
body: "systek",
totalFee: 0.01
},
// url: 'https://systek.imdo.co/appsrv/pay/wx/request_payment',
// data:
// {
// app_user_id: res.data.id,
// access_token: res.data.accessToken,
// vip_type:3,
// pay_mode:1,
// scenic_code: 4103000001
// },
method: 'GET',
success: function (res) {
console.log(res.data.data)
var date = new Date()
wx.requestPayment({
'timeStamp': res.data.data.timeStamp,
'nonceStr': res.data.data.nonceStr,
'package': res.data.data.package,
'signType': 'MD5',
'paySign': res.data.data.paySign,
'success': function (res) {
console.log("============成功==============")
console.log(res)
},
'fail': function (res) {
console.log("============失败==============")
console.log(res)
},
'complete': function (res) {
console.log("============完成==============")
console.log(res)
}
})
},
})
}
});