需要用到的工具類 PayCommonUtil
package com.thinkgem.jeesite.common.wechat;
import java.text.SimpleDateFormat;
import java.util.*;
public class PayCommonUtil {
/**
* 是否簽名正確,規則是:按參數名稱a-z排序,遇到空值的參數不參加簽名。
* @return boolean
*/
@SuppressWarnings("unchecked")
public static boolean isTenpaySign(String characterEncoding, SortedMap<Object, Object> packageParams, String API_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=" + API_KEY);
//算出摘要
String mysign = MD5Util.MD5Encode(sb.toString(), characterEncoding).toLowerCase();
String tenpaySign = ((String)packageParams.get("sign")).toLowerCase();
//System.out.println(tenpaySign + " " + mysign);
return tenpaySign.equals(mysign);
}
/**
* @Description:sign簽名
* @param characterEncoding
* 編碼格式
* @param packageParams
* 請求參數
* @param API_KEY
* @return
*/
@SuppressWarnings("unchecked")
public static String createSign(String characterEncoding, SortedMap<Object, Object> packageParams, String API_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 (null != v && !"".equals(v) && !"sign".equals(k) && !"key".equals(k) && !"notify_url".equals(k)&& !"out_trade_no".equals(k)&& !"spbill_create_ip".equals(k)&& !"total_fee".equals(k)&& !"trade_type".equals(k)) {
if (null != v && !"".equals(v) && !"sign".equals(k) && !"key".equals(k) ) {
sb.append(k + "=" + v + "&");
}
}
sb.append("key=" + API_KEY);
// String stringA="appid=wxc7b425229b570867&mch_id=1406330002&nonce_str=1702585759&key=ab42e0b7aa6bce35164a2d14855d7264";
String sign = MD5Util.MD5Encode(sb.toString(), characterEncoding).toUpperCase();
// String sign = MD5Util.MD5Encode(stringA, characterEncoding).toUpperCase();
// System.out.println(sign);
// System.out.println(MD5Util.MD5Encode("appid=wxd930ea5d5a258f4f&body=test&device_info=1000&mch_id=10000100&nonce_str=ibuaiVcKdpRxkhJA&key=192006250b4c09247ec02edce69f6a2d", characterEncoding).toUpperCase());
// System.out.println(sign);
return sign;
}
/**
* @author
* @date 2016-4-22
* @Description:將請求參數轉換爲xml格式的string
* @param parameters
* 請求參數
* @return
*/
@SuppressWarnings("unchecked")
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) || "sign".equalsIgnoreCase(k)) {
sb.append("<" + k + ">" + "<![CDATA[" + v + "]]></" + k + ">");
} else {
sb.append("<" + k + ">" + v + "</" + k + ">");
}
}
sb.append("</xml>");
return sb.toString();
}
/**
* 取出一個指定長度大小的隨機正整數.
*
* @param length
* int 設定所取出隨機數的長度。length小於11
* @return int 返回生成的隨機數。
*/
public static int buildRandom(int length) {
int num = 1;
double random = Math.random();
if (random < 0.1) {
random = random + 0.1;
}
for (int i = 0; i < length; i++) {
num = num * 10;
}
return (int) ((random * num));
}
/**
* 獲取當前時間 yyyyMMddHHmmss
*
* @return String
*/
public static String getCurrTime() {
Date now = new Date();
SimpleDateFormat outFormat = new SimpleDateFormat("yyyyMMddHHmmss");
String s = outFormat.format(now);
return s;
}
}
package com.thinkgem.jeesite.common.wechat;
import com.thinkgem.jeesite.common.config.Global;
import java.text.DecimalFormat;
import java.util.*;
/**
* 微信支付
* Created by yuhaiming on 2016/11/18 0018.
*/
public class WeChatUtil {
/**
* 基本常量設置
*/
/**
* 統一下單請求路徑
*/
public static String UFDODER_URL = "https://api.mch.weixin.qq.com/pay/unifiedorder";
/**
* 退款請求
*/
public static String REFUND_URL = "https://api.mch.weixin.qq.com/secapi/pay/refund";
/**
* APPID
*/
public static String APP_ID = Global.getConfig("wechat.appid");
public static String APP_SECRET = Global.getConfig("wechat.appsecret");
/**
* 微信支付商戶號
*/
public static String MCH_ID = Global.getConfig("wechat.mch_id");
/**
* 密匙
*/
public static String API_KEY = Global.getConfig("wechat.key");
/**
* 發起支付IP
*/
public static String CREATE_IP = "112.117.94.77";
/**
* 回調url
*/
public static String NOTIFY_URL = "http://tonyyule.ngrok.wendal.cn/app/home/paySeccess";
/**
* 生成微信簽名
*
* @param order_id 訂單ID
* @param body 描述
* @param order_price 價格
* @return
*/
public static String GetWeChatXML(String order_id, String body, double order_price,String openid) {
String currTime = PayCommonUtil.getCurrTime();
String strTime = currTime.substring(8, currTime.length());
String strRandom = PayCommonUtil.buildRandom(4) + "";
//隨機字符串
String nonce_str = strTime + strRandom;//UUID.randomUUID().toString();
// 獲取發起電腦 ip
String spbill_create_ip = WeChatUtil.CREATE_IP;
//交易類型
String trade_type = "JSAPI";
//微信價格最小單位分 轉換爲整數
DecimalFormat df = new DecimalFormat("#######.##");
order_price = order_price * 100;
order_price = Math.ceil(order_price);
String price = df.format(order_price);
SortedMap<Object, Object> packageParams = new TreeMap<Object, Object>();
packageParams.put("appid", APP_ID);
packageParams.put("body", body);
packageParams.put("mch_id", MCH_ID);
packageParams.put("nonce_str", nonce_str);
packageParams.put("notify_url", NOTIFY_URL);// 回調接口
packageParams.put("out_trade_no", order_id);
packageParams.put("spbill_create_ip", spbill_create_ip);
packageParams.put("total_fee", price);
packageParams.put("trade_type", trade_type);
packageParams.put("openid", openid);//o_6gNwi23RLC97cSPfHE-DEg3OLA
String sign = PayCommonUtil.createSign("UTF-8", packageParams, API_KEY);
packageParams.put("sign", sign);
String requestXML = PayCommonUtil.getRequestXml(packageParams);
// System.out.println(requestXML);
return requestXML;
}
/**
* 微信退款初始化數據
* @param out_trade_no
* @param total_fee
* @param refund_fee
* @return
*/
public static String RefundInit(String out_trade_no,Double total_fee,Double refund_fee){
String refundid = UUID.randomUUID().toString();
refundid = refundid.substring(0, 32);
String currTime = PayCommonUtil.getCurrTime();
String strTime = currTime.substring(8, currTime.length());
String strRandom = PayCommonUtil.buildRandom(4) + "";
//隨機字符串
String nonce_str = strTime + strRandom;
//價格轉換
DecimalFormat df = new DecimalFormat("#######.##");
total_fee = total_fee * 100;
total_fee = Math.ceil(total_fee);
String total_price = df.format(total_fee);
refund_fee = refund_fee * 100;
refund_fee = Math.ceil(refund_fee);
String refund_price = df.format(refund_fee);
/*----- 1.生成預支付訂單需要的的package數據-----*/
SortedMap<Object, Object> packageParams = new TreeMap<Object, Object>();
packageParams.put("appid", APP_ID);
packageParams.put("mch_id", MCH_ID);
packageParams.put("nonce_str", nonce_str);
packageParams.put("op_user_id", MCH_ID);//操作員帳號, 默認爲商戶號
packageParams.put("out_trade_no", out_trade_no);
packageParams.put("out_refund_no", refundid);
packageParams.put("total_fee", total_price);
packageParams.put("refund_fee", refund_price);
/*----2.根據package生成簽名sign---- */
String sign = PayCommonUtil.createSign("UTF-8", packageParams, API_KEY);
packageParams.put("sign", sign);
/*----3.拼裝需要提交到微信的數據xml---- */
String xml = PayCommonUtil.getRequestXml(packageParams);
return xml;
}
/**
*
* 查詢訂單初始化數據
* @param out_trade_no
* @return
*/
public static String OrderQuery(String out_trade_no){
String currTime = PayCommonUtil.getCurrTime();
String strTime = currTime.substring(8, currTime.length());
String strRandom = PayCommonUtil.buildRandom(4) + "";
//隨機字符串
String nonce_str = strTime + strRandom;
SortedMap<Object, Object> packageParams = new TreeMap<Object, Object>();
packageParams.put("appid", APP_ID);
packageParams.put("mch_id", MCH_ID);
packageParams.put("out_trade_no", out_trade_no);
packageParams.put("nonce_str", nonce_str);
/*----2.根據package生成簽名sign---- */
String sign = PayCommonUtil.createSign("UTF-8", packageParams, API_KEY);
packageParams.put("sign", sign);
/*----3.拼裝需要提交到微信的數據xml---- */
String xml = PayCommonUtil.getRequestXml(packageParams);
return xml;
}
}
/**
* 微信退款
* @param out_trade_no
* @param total_fee
* @param refund_fee
* @return
*/
public static Map<String, String> wxRefund(String out_trade_no,Double total_fee,Double refund_fee) {
//證書文件路徑
String path = Global.getProjectPath() + "/pp.p12";
Map<String, String> result = new HashMap<String, String>();
//初始化數據
String data = WeChatUtil.RefundInit(out_trade_no,total_fee,refund_fee);
try {
/*----4.讀取證書文件,這一段是直接從微信支付平臺提供的demo中copy的,所以一般不需要修改---- */
KeyStore keyStore = KeyStore.getInstance("PKCS12");
FileInputStream instream = new FileInputStream(new File(path));
try {
//證書密碼(初始是商戶 ID)
keyStore.load(instream, WeChatUtil.MCH_ID.toCharArray());
} finally {
instream.close();
}
SSLContext sslcontext = SSLContexts.custom().loadKeyMaterial(keyStore, WeChatUtil.MCH_ID.toCharArray()).build();
SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(sslcontext,new String[] { "TLSv1" },null,
SSLConnectionSocketFactory.BROWSER_COMPATIBLE_HOSTNAME_VERIFIER);
CloseableHttpClient httpclient = HttpClients.custom() .setSSLSocketFactory(sslsf) .build();
HttpPost httpost = new HttpPost(WeChatUtil.REFUND_URL);
httpost.addHeader("Connection", "keep-alive");
httpost.addHeader("Accept", "*/*");
httpost.addHeader("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8");
httpost.addHeader("Host", "api.mch.weixin.qq.com");
httpost.addHeader("X-Requested-With", "XMLHttpRequest");
httpost.addHeader("Cache-Control", "max-age=0");
httpost.addHeader("User-Agent", "Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.0) ");
httpost.setEntity(new StringEntity(data, "UTF-8"));
CloseableHttpResponse response = httpclient.execute(httpost);
HttpEntity entity = response.getEntity();
String jsonStr = EntityUtils.toString(response.getEntity(), "UTF-8");
EntityUtils.consume(entity);
result = XMLUtil.doXMLParse(jsonStr);
} catch (Exception e) {
result.put("returncode", "error");
result.put("returninfo", "退款失敗");
}
return result;
}