您的位置:首页 > 移动开发 > 微信开发

java集成微信支付接口(微信V3版)

2017-07-27 16:53 344 查看
吐槽下:

经常有人问我,你不是在某软件公司么?我听说大公司都是Java,对吗?

我只想回答,那是网上流传的骗人的传说,据我所知公司开发组大部分都是C#,少部分用JAVA。

这不,官方提供那个Demo就是个例证,JAVA版本完全是V1版本,难以使用,反而C#是V3版本,拿来就用。

最近几天我们公司需要做类似的开发,我作为先锋,率先解决Java集成支付宝支付和微信支付接口工作。

我们的工作环境:目前工作的支付宝接口为20160912,微信为V3版本,如遇到版本升级,请联系相关机构的客户服务人员升级。

本文介绍JAVA+微信V3,刚刚已经说过,官方提供那个Demo完全是V1版本,下面是本人原创Java V3版本SDK,

本文的WxPayApi接口完全兼容官方提供C# SDK DEMO WxPayApi类,可以把C# 代码拿来转转就能用。

新手注意:

1、配置有个证书路径,是新版httpclient 连接HTTPS需要目标网站证书,不清楚的话,自己看注释做证书或者度娘一下。

2、用到的Jar:

commons-logging-1.2.jar

fastjson-1.2.7.jar

FastWeixin.jar

httpclient-4.5.2.jar

httpcore-4.4.4.jar

httpmime-4.5.2.jar

xstream-1.4.9.jar

3、使用到UtilDate类,请参考上一篇文章,或者支付宝支付Demo。

4、示例函数:WXPay.weixin_pay(),新手要注意,每种支付所用到参数不尽相同,多看看文档确定使用到哪几个参数,不要想当然。

5、部分通用函数,使用了FastWeixin里面Util 自己根据需要选择替换。

[java] view plain copy print?<pre code_snippet_id=“2067985” snippet_file_name=“blog_20161222_1_5457278” name=“code” class=“java”>package com.luozhuang;

import java.text.SimpleDateFormat;
import java.util.Calendar;
impor
4000
t java.util.Date;
import java.util.Map;
import java.util.SortedMap;
import java.util.TreeMap;

import javax.servlet.http.HttpServletRequest;

import com.luozhuang.util.Configure;
import com.luozhuang.util.HttpsRequest;
import com.luozhuang.util.PayCommonUtil;
import com.luozhuang.util.XMLParser;

/**
* SDK总入口
*/
public class WXPay {
public String getRemortIP(HttpServletRequest request) {
if (request.getHeader(“x-forwarded-for”) == null) {
return request.getRemoteAddr();
}
return request.getHeader(“x-forwarded-for”);
}
/**
* 初始化SDK依赖的几个关键配置
*
* @param key
* 签名算法需要用到的秘钥
* @param appID
* 公众账号ID
* @param mchID
* 商户ID
* @param sdbMchID
* 子商户ID,受理模式必填
* @param certLocalPath
* HTTP证书在服务器中的路径,用来加载证书用
* @param certPassword
* HTTP证书的密码,默认等于MCHID
*/
public static void initSDKConfiguration(String key, String appID, String mchID, String sdbMchID,
String certLocalPath, String certPassword) {
Configure.setKey(key);
Configure.setAppID(appID);
Configure.setMchID(mchID);
Configure.setSubMchID(sdbMchID);
Configure.setCertLocalPath(certLocalPath);
Configure.setCertPassword(certPassword);
}

/**
*
*
* @param out_trade_no
* @return
* @throws Exception
*/
public static String weixin_pay(String out_trade_no) throws Exception {
HttpsRequest httpRequest = new HttpsRequest();
// 账号信息
String appid = Configure.getAppid(); // appid
// String appsecret = PayConfigUtil.APP_SECRET; // appsecret
// 商业号
String mch_id = Configure.getMchid();
// key
String key = Configure.getKey();

String currTime = PayCommonUtil.getCurrTime();
String strTime = currTime.substring(8, currTime.length());
String strRandom = PayCommonUtil.buildRandom(4) + “”;
// 随机字符串
String nonce_str = strTime + strRandom;
// 价格 注意:价格的单位是分
String order_price = ”1”;
// 商品名称
// String body = “luozhuang”;

// 获取发起电脑 ip
String spbill_create_ip = Configure.getIP();
// 回调接口
String notify_url = Configure.NOTIFY_URL;
String product_id=”luozhuang”;
String trade_type = ”NATIVE”;//JSAPI–公众号支付、NATIVE–原生扫码支付、APP–app支付
String time_start = new SimpleDateFormat(“yyyyMMddHHmmss”).format(new Date());
Calendar ca = Calendar.getInstance();
ca.setTime(new Date());
ca.add(Calendar.DATE, 1);
String time_expire = new SimpleDateFormat(“yyyyMMddHHmmss”).format(ca.getTime());
WxPayData packageParams = new WxPayData();
packageParams.put(”appid”, appid);
packageParams.put(”mch_id”, mch_id);
packageParams.put(”nonce_str”, nonce_str);
packageParams.put(”body”, “luozhuang-服务费”);
packageParams.put(”out_trade_no”, out_trade_no);
packageParams.put(”product_id”,product_id);
packageParams.put(”total_fee”, order_price);
packageParams.put(”spbill_create_ip”, spbill_create_ip);
packageParams.put(”notify_url”, notify_url);
packageParams.put(”trade_type”, trade_type);
packageParams.put(”time_start”, time_start);
packageParams.put(”time_expire”, time_expire);
String sign = packageParams.MakeSign();
packageParams.put(”sign”, sign);
WxPayData resXml=WxPayApi.UnifiedOrder(packageParams, 20);
String requestXML = packageParams.ToXml();
System.out.println(”请求xml::::” + requestXML);
System.out.println(”得到xml::::” + resXml.toString());
// String return_code = (String) map.get(“return_code”);
// String prepay_id = (String) map.get(“prepay_id”);
String urlCode = (String) resXml.GetValue(”code_url”);
System.out.println(”打印调用统一下单接口生成二维码url:::::” + urlCode);
return urlCode;
}

}
</pre><br>
<p></p>
<pre></pre>
<br>
<br>
<p></p>
<p></p><pre code_snippet_id=”2067985” snippet_file_name=“blog_20161222_2_3079537” name=“code” class=“java”>package com.luozhuang;

import java.io.IOException;
import java.security.KeyManagementException;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.UnrecoverableKeyException;
import java.util.Calendar;
import java.util.Date;
import java.util.Random;

import javax.xml.parsers.ParserConfigurationException;

import org.xml.sax.SAXException;

import com.fastwixinextend.CommonClass;
import com.luozhuang.util.Configure;
import com.luozhuang.util.HttpsRequest;

public class WxPayApi
{
private static final int REPORT_LEVENL = 0;

/**
* 提交被扫支付API
* 收银员使用扫码设备读取微信用户刷卡授权码以后,二维码或条码信息传送至商户收银台,
* 由商户收银台或者商户后台调用该接口发起支付。
* @param WxPayData inputObj 提交给被扫支付API的参数
* @param int timeOut 超时时间
* @throws WxPayException
* @return 成功时返回调用结果,其他抛异常
* @throws IOException
* @throws KeyStoreException
* @throws NoSuchAlgorithmException
* @throws KeyManagementException
* @throws UnrecoverableKeyException
* @throws SAXException
* @throws ParserConfigurationException
*/
public static WxPayData Micropay(WxPayData inputObj, int timeOut) throws UnrecoverableKeyException, KeyManagementException, NoSuchAlgorithmException, KeyStoreException, IOException, WxPayException, ParserConfigurationException, SAXException
{
String url =Configure.PAY_API;
//检测必填参数
if (!inputObj.IsSet(“body”))
{
throw new WxPayException(“提交被扫支付API接口中,缺少必填参数body!”);
}
else if (!inputObj.IsSet(“out_trade_no”))
{
throw new WxPayException(“提交被扫支付API接口中,缺少必填参数out_trade_no!”);
}
else if (!inputObj.IsSet(“total_fee”))
{
throw new WxPayException(“提交被扫支付API接口中,缺少必填参数total_fee!”);
}
else if (!inputObj.IsSet(“auth_code”))
{
throw new WxPayException(“提交被扫支付API接口中,缺少必填参数auth_code!”);
}

inputObj.SetValue(”spbill_create_ip”, Configure.getIP());//终端ip
inputObj.SetValue(”appid”, Configure.getAppid());//公众账号ID
inputObj.SetValue(”mch_id”, Configure.getMchid());//商户号
inputObj.SetValue(”nonce_str”, UUIDGenerator.getUUID().toString().replace(“-“, “”));//随机字符串
inputObj.SetValue(”sign”, inputObj.MakeSign());//签名
String xml = inputObj.ToXml();

HttpsRequest https=new HttpsRequest();
https.setSocketTimeout(timeOut);
String response = https.sendPost(url, xml);//调用HTTP通信接口以提交数据到API
WxPayData result = new WxPayData();
result.FromXml(response);

return result;
}

/**
*
* 查询订单
* @param WxPayData inputObj 提交给查询订单API的参数
* @param int timeOut 超时时间
* @throws WxPayException
* @return 成功时返回订单查询结果,其他抛异常
* @throws IOException
* @throws KeyStoreException
* @throws NoSuchAlgorithmException
* @throws KeyManagementException
* @throws UnrecoverableKeyException
* @throws SAXException
* @throws ParserConfigurationException
*/
public static WxPayData OrderQuery(WxPayData inputObj, int timeOut) throws UnrecoverableKeyException, KeyManagementException, NoSuchAlgorithmException, KeyStoreException, IOException, WxPayException, ParserConfigurationException, SAXException
{
String url = Configure.PAY_QUERY_API;
//检测必填参数
if (!inputObj.IsSet(“out_trade_no”) && !inputObj.IsSet(“transaction_id”))
{
throw new WxPayException(“订单查询接口中,out_trade_no、transaction_id至少填一个!”);
}

inputObj.SetValue(”appid”, Configure.getAppid());//公众账号ID
inputObj.SetValue(”mch_id”, Configure.getMchid());//商户号
inputObj.SetValue(”nonce_str”, WxPayApi.GenerateNonceStr());//随机字符串
inputObj.SetValue(”sign”, inputObj.MakeSign());//签名

String xml = inputObj.ToXml();

HttpsRequest https=new HttpsRequest();
https.setSocketTimeout(timeOut);
String response = https.sendPost(url, xml);//调用HTTP通信接口以提交数据到API
WxPayData result = new WxPayData();
result.FromXml(response);

return result;
}

/**
*
* 撤销订单API接口
* @param WxPayData inputObj 提交给撤销订单API接口的参数,out_trade_no和transaction_id必填一个
* @param int timeOut 接口超时时间
* @throws WxPayException
* @return 成功时返回API调用结果,其他抛异常
* @throws IOException
* @throws KeyStoreException
* @throws NoSuchAlgorithmException
* @throws KeyManagementException
* @throws UnrecoverableKeyException
* @throws SAXException
* @throws ParserConfigurationException
*/
public static WxPayData Reverse(WxPayData inputObj, int timeOut) throws WxPayException, UnrecoverableKeyException, KeyManagementException, NoSuchAlgorithmException, KeyStoreException, IOException, ParserConfigurationException, SAXException
{
String url =Configure.REVERSE_API;
//检测必填参数
if (!inputObj.IsSet(“out_trade_no”) && !inputObj.IsSet(“transaction_id”))
{
throw new WxPayException(“撤销订单API接口中,参数out_trade_no和transaction_id必须填写一个!”);
}

inputObj.SetValue(”appid”, Configure.getAppid());//公众账号ID
inputObj.SetValue(”mch_id”, Configure.getMchid());//商户号
inputObj.SetValue(”nonce_str”, GenerateNonceStr());//随机字符串
inputObj.SetValue(”sign”, inputObj.MakeSign());//签名
String xml = inputObj.ToXml();

HttpsRequest https=new HttpsRequest();
https.setSocketTimeout(timeOut);
String response = https.sendPost(url, xml);//调用HTTP通信接口以提交数据到API
WxPayData result = new WxPayData();
result.FromXml(response);

return result;
}

/**
*
* 申请退款
* @param WxPayData inputObj 提交给申请退款API的参数
* @param int timeOut 超时时间
* @throws WxPayException
* @return 成功时返回接口调用结果,其他抛异常
* @throws IOException
* @throws KeyStoreException
* @throws NoSuchAlgorithmException
* @throws KeyManagementException
* @throws UnrecoverableKeyException
* @throws SAXException
* @throws ParserConfigurationException
*/
public static WxPayData Refund(WxPayData inputObj, int timeOut) throws WxPayException, UnrecoverableKeyException, KeyManagementException, NoSuchAlgorithmException, KeyStoreException, IOException, ParserConfigurationException, SAXException
{
String url = Configure.REFUND_API;
//检测必填参数
if (!inputObj.IsSet(“out_trade_no”) && !inputObj.IsSet(“transaction_id”))
{
throw new WxPayException(“退款申请接口中,out_trade_no、transaction_id至少填一个!”);
}
else if (!inputObj.IsSet(“out_refund_no”))
{
throw new WxPayException(“退款申请接口中,缺少必填参数out_refund_no!”);
}
else if (!inputObj.IsSet(“total_fee”))
{
throw new WxPayException(“退款申请接口中,缺少必填参数total_fee!”);
}
else if (!inputObj.IsSet(“refund_fee”))
{
throw new WxPayException(“退款申请接口中,缺少必填参数refund_fee!”);
}
else if (!inputObj.IsSet(“op_user_id”))
{
throw new WxPayException(“退款申请接口中,缺少必填参数op_user_id!”);
}

inputObj.SetValue(”appid”, Configure.getAppid());//公众账号ID
inputObj.SetValue(”mch_id”, Configure.getMchid());//商户号
inputObj.SetValue(”nonce_str”, UUIDGenerator.getUUID().toString().replace(“-“, “”));//随机字符串
inputObj.SetValue(”sign”, inputObj.MakeSign());//签名

String xml = inputObj.ToXml();

HttpsRequest https=new HttpsRequest();
https.setSocketTimeout(timeOut);
String response = https.sendPost(url, xml);//调用HTTP通信接口以提交数据到API
WxPayData result = new WxPayData();
result.FromXml(response);

return result;
}

/**
*
* 查询退款
* 提交退款申请后,通过该接口查询退款状态。退款有一定延时,
* 用零钱支付的退款20分钟内到账,银行卡支付的退款3个工作日后重新查询退款状态。
* out_refund_no、out_trade_no、transaction_id、refund_id四个参数必填一个
* @param WxPayData inputObj 提交给查询退款API的参数
* @param int timeOut 接口超时时间
* @throws WxPayException
* @return 成功时返回,其他抛异常
* @throws IOException
* @throws KeyStoreException
* @throws NoSuchAlgorithmException
* @throws KeyManagementException
* @throws UnrecoverableKeyException
* @throws SAXException
* @throws ParserConfigurationException
*/
public static WxPayData RefundQuery(WxPayData inputObj, int timeOut) throws UnrecoverableKeyException, KeyManagementException, NoSuchAlgorithmException, KeyStoreException, IOException, WxPayException, ParserConfigurationException, SAXException
{
String url = Configure.REFUND_QUERY_API;
//检测必填参数
if(!inputObj.IsSet(“out_refund_no”) && !inputObj.IsSet(“out_trade_no”) &&
!inputObj.IsSet(”transaction_id”) && !inputObj.IsSet(“refund_id”))
{
throw new WxPayException(“退款查询接口中,out_refund_no、out_trade_no、transaction_id、refund_id四个参数必填一个!”);
}

inputObj.SetValue(”appid”,Configure.getAppid());//公众账号ID
inputObj.SetValue(”mch_id”,Configure.getMchid());//商户号
inputObj.SetValue(”nonce_str”,GenerateNonceStr());//随机字符串
inputObj.SetValue(”sign”,inputObj.MakeSign());//签名

String xml = inputObj.ToXml();

HttpsRequest https=new HttpsRequest();
https.setSocketTimeout(timeOut);
String response = https.sendPost(url, xml);//调用HTTP通信接口以提交数据到API
WxPayData result = new WxPayData();
result.FromXml(response);

return result;
}

/**
* 下载对账单
* @param WxPayData inputObj 提交给下载对账单API的参数
* @param int timeOut 接口超时时间
* @throws WxPayException
* @return 成功时返回,其他抛异常
* @throws IOException
* @throws NoSuchAlgorithmException
* @throws KeyStoreException
* @throws KeyManagementException
* @throws UnrecoverableKeyException
* @throws SAXException
* @throws ParserConfigurationException
*/
public static WxPayData DownloadBill(WxPayData inputObj, int timeOut) throws UnrecoverableKeyException, KeyManagementException, KeyStoreException, NoSuchAlgorithmException, IOException, WxPayException, ParserConfigurationException, SAXException
{
String url = Configure.DOWNLOAD_BILL_API;
//检测必填参数
if (!inputObj.IsSet(“bill_date”))
{
throw new WxPayException(“对账单接口中,缺少必填参数bill_date!”);
}

inputObj.SetValue(”appid”, Configure.getAppid());//公众账号ID
inputObj.SetValue(”mch_id”, Configure.getMchid());//商户号
inputObj.SetValue(”nonce_str”, GenerateNonceStr());//随机字符串
inputObj.SetValue(”sign”, inputObj.MakeSign());//签名

String xml = inputObj.ToXml();

HttpsRequest https=new HttpsRequest();
https.setSocketTimeout(timeOut);
String response = https.sendPost(url, xml);//调用HTTP通信接口以提交数据到API

WxPayData result = new WxPayData();
//若接口调用失败会返回xml格式的结果
if (response.startsWith(“<xml>”))
{

result.FromXml(response);
}
//接口调用成功则返回非xml格式的数据
else
result.SetValue(”result”, response);

return result;
}

/**
*
* 转换短链接
* 该接口主要用于扫码原生支付模式一中的二维码链接转成短链接(weixin://wxpay/s/XXXXXX),
* 减小二维码数据量,提升扫描速度和精确度。
* @param WxPayData inputObj 提交给转换短连接API的参数
* @param int timeOut 接口超时时间
* @throws WxPayException
* @return 成功时返回,其他抛异常
* @throws IOException
* @throws KeyStoreException
* @throws NoSuchAlgorithmException
* @throws KeyManagementException
* @throws UnrecoverableKeyException
* @throws SAXException
* @throws ParserConfigurationException
*/
public static WxPayData ShortUrl(WxPayData inputObj, int timeOut) throws WxPayException, UnrecoverableKeyException, KeyManagementException, NoSuchAlgorithmException, KeyStoreException, IOException, ParserConfigurationException, SAXException
{
String url = Configure.Shorturl_API;
//检测必填参数
if(!inputObj.IsSet(“long_url”))
{
throw new WxPayException(“需要转换的URL,签名用原串,传输需URL encode!”);
}

inputObj.SetValue(”appid”,Configure.getAppid());//公众账号ID
inputObj.SetValue(”mch_id”,Configure.getMchid());//商户号
inputObj.SetValue(”nonce_str”,GenerateNonceStr());//随机字符串
inputObj.SetValue(”sign”,inputObj.MakeSign());//签名
String xml = inputObj.ToXml();

HttpsRequest https=new HttpsRequest();
https.setSocketTimeout(timeOut);
String response = https.sendPost(url, xml);//调用HTTP通信接口以提交数据到API
WxPayData result = new WxPayData();
result.FromXml(response);

return result;
}

/**
*
* 统一下单
* @param WxPaydata inputObj 提交给统一下单API的参数
* @param int timeOut 超时时间
* @throws WxPayException
* @return 成功时返回,其他抛异常
* @throws IOException
* @throws KeyStoreException
* @throws NoSuchAlgorithmException
* @throws KeyManagementException
* @throws UnrecoverableKeyException
* @throws SAXException
* @throws ParserConfigurationException
*/
public static WxPayData UnifiedOrder(WxPayData inputObj, int timeOut) throws WxPayException, UnrecoverableKeyException, KeyManagementException, NoSuchAlgorithmException, KeyStoreException, IOException, ParserConfigurationException, SAXException
{
String url =Configure.UnifiedOrder_API;
//检测必填参数
if (!inputObj.IsSet(“out_trade_no”))
{
throw new WxPayException(“缺少统一支付接口必填参数out_trade_no!”);
}
else if (!inputObj.IsSet(“body”))
{
throw new WxPayException(“缺少统一支付接口必填参数body!”);
}
else if (!inputObj.IsSet(“total_fee”))
{
throw new WxPayException(“缺少统一支付接口必填参数total_fee!”);
}
else if (!inputObj.IsSet(“trade_type”))
{
throw new WxPayException(“缺少统一支付接口必填参数trade_type!”);
}

//关联参数
if (inputObj.GetValue(“trade_type”).toString() == “JSAPI” && !inputObj.IsSet(“openid”))
{
throw new WxPayException(“统一支付接口中,缺少必填参数openid!trade_type为JSAPI时,openid为必填参数!”);
}
if (inputObj.GetValue(“trade_type”).toString() == “NATIVE” && !inputObj.IsSet(“product_id”))
{
throw new WxPayException(“统一支付接口中,缺少必填参数product_id!trade_type为JSAPI时,product_id为必填参数!”);
}

//异步通知url未设置,则使用配置文件中的url
if (!inputObj.IsSet(“notify_url”))
{
inputObj.SetValue(”notify_url”, Configure.NOTIFY_URL);//异步通知url
}

inputObj.SetValue(”appid”, Configure.getAppid());//公众账号ID
inputObj.SetValue(”mch_id”, Configure.getMchid());//商户号
inputObj.SetValue(”spbill_create_ip”, Configure.getIP());//终端ip
inputObj.SetValue(”nonce_str”, GenerateNonceStr());//随机字符串

//签名
inputObj.SetValue(”sign”, inputObj.MakeSign());
String xml = inputObj.ToXml();

HttpsRequest https=new HttpsRequest();
https.setSocketTimeout(timeOut);
String response = https.sendPost(url, xml);//调用HTTP通信接口以提交数据到API
WxPayData result = new WxPayData();
result.FromXml(response);

return result;
}

/**
*
* 关闭订单
* @param WxPayData inputObj 提交给关闭订单API的参数
* @param int timeOut 接口超时时间
* @throws WxPayException
* @return 成功时返回,其他抛异常
* @throws IOException
* @throws KeyStoreException
* @throws NoSuchAlgorithmException
* @throws KeyManagementException
* @throws UnrecoverableKeyException
* @throws SAXException
* @throws ParserConfigurationException
*/
public static WxPayData CloseOrder(WxPayData inputObj, int timeOut) throws WxPayException, UnrecoverableKeyException, KeyManagementException, NoSuchAlgorithmException, KeyStoreException, IOException, ParserConfigurationException, SAXException
{

String url =Configure.CloseOrder_API;
//检测必填参数
if(!inputObj.IsSet(“out_trade_no”))
{
throw new WxPayException(“关闭订单接口中,out_trade_no必填!”);
}

inputObj.SetValue(”appid”,Configure.getAppid());//公众账号ID
inputObj.SetValue(”mch_id”,Configure.getMchid());//商户号
inputObj.SetValue(”nonce_str”,GenerateNonceStr());//随机字符串
inputObj.SetValue(”sign”,inputObj.MakeSign());//签名
String xml = inputObj.ToXml();

HttpsRequest https=new HttpsRequest();
https.setSocketTimeout(timeOut);
String response = https.sendPost(url, xml);//调用HTTP通信接口以提交数据到API
WxPayData result = new WxPayData();
result.FromXml(response);

return result;
}

/**
*
* 测速上报
* @param String interface_url 接口URL
* @param int timeCost 接口耗时
* @param WxPayData inputObj参数数组
*/
private static void ReportCostTime(String interface_url, int timeCost, WxPayData inputObj)
{
//如果不需要进行上报
if(REPORT_LEVENL == 0)
{
return;
}

//如果仅失败上报
if(REPORT_LEVENL == 1 && inputObj.IsSet(“return_code”) && inputObj.GetValue(“return_code”).toString() == “SUCCESS” &&
inputObj.IsSet(”result_code”) && inputObj.GetValue(“result_code”).toString() == “SUCCESS”)
{
return;
}

//上报逻辑
WxPayData data = new WxPayData();
data.SetValue(”interface_url”,interface_url);
data.SetValue(”execute_time_”,timeCost);
//返回状态码
if(inputObj.IsSet(“return_code”))
{
data.SetValue(”return_code”,inputObj.GetValue(“return_code”));
}
//返回信息
if(inputObj.IsSet(“return_msg”))
{
data.SetValue(”return_msg”,inputObj.GetValue(“return_msg”));
}
//业务结果
if(inputObj.IsSet(“result_code”))
{
data.SetValue(”result_code”,inputObj.GetValue(“result_code”));
}
//错误代码
if(inputObj.IsSet(“err_code”))
{
data.SetValue(”err_code”,inputObj.GetValue(“err_code”));
}
//错误代码描述
if(inputObj.IsSet(“err_code_des”))
{
data.SetValue(”err_code_des”,inputObj.GetValue(“err_code_des”));
}
//商户订单号
if(inputObj.IsSet(“out_trade_no”))
{
data.SetValue(”out_trade_no”,inputObj.GetValue(“out_trade_no”));
}
//设备号
if(inputObj.IsSet(“device_info”))
{
data.SetValue(”device_info”,inputObj.GetValue(“device_info”));
}

}

/**
*
* 测速上报接口实现
* @param WxPayData inputObj 提交给测速上报接口的参数
* @param int timeOut 测速上报接口超时时间
* @throws WxPayException
* @return 成功时返回测速上报接口返回的结果,其他抛异常
* @throws IOException
* @throws KeyStoreException
* @throws NoSuchAlgorithmException
* @throws KeyManagementException
* @throws UnrecoverableKeyException
* @throws SAXException
* @throws ParserConfigurationException
*/
public static WxPayData Report(WxPayData inputObj, int timeOut) throws WxPayException, UnrecoverableKeyException, KeyManagementException, NoSuchAlgorithmException, KeyStoreException, IOException, ParserConfigurationException, SAXException
{
String url = Configure.REPORT_API;
//检测必填参数
if(!inputObj.IsSet(“interface_url”))
{
throw new WxPayException(“接口URL,缺少必填参数interface_url!”);
}
if(!inputObj.IsSet(“return_code”))
{
throw new WxPayException(“返回状态码,缺少必填参数return_code!”);
}
if(!inputObj.IsSet(“result_code”))
{
throw new WxPayException(“业务结果,缺少必填参数result_code!”);
}
if(!inputObj.IsSet(“user_ip”))
{
throw new WxPayException(“访问接口IP,缺少必填参数user_ip!”);
}
if(!inputObj.IsSet(“execute_time_”))
{
throw new WxPayException(“接口耗时,缺少必填参数execute_time_!”);
}

inputObj.SetValue(”appid”,Configure.getAppid());//公众账号ID
inputObj.SetValue(”mch_id”,Configure.getMchid());//商户号
inputObj.SetValue(”user_ip”,Configure.getIP());//终端ip
inputObj.SetValue(”time”,CommonClass.GetCurrentDateTimeTextNosplit());//商户上报时间
inputObj.SetValue(”nonce_str”,GenerateNonceStr());//随机字符串
inputObj.SetValue(”sign”,inputObj.MakeSign());//签名
String xml = inputObj.ToXml();

HttpsRequest https=new HttpsRequest();
https.setSocketTimeout(timeOut);
String response = https.sendPost(url, xml);//调用HTTP通信接口以提交数据到API
WxPayData result = new WxPayData();
result.FromXml(response);

return result;
}

/**
* 根据当前系统时间加随机序列来生成订单号
* @return 订单号
*/
public static String GenerateOutTradeNo()
{
Random ran = new Random();
return String.format(“{0}{1}{2}”, Configure.getMchid(),CommonClass.GetCurrentDateTimeTextNosplit(), ran.nextInt(999));
}

/**
* 生成时间戳,标准北京时间,时区为东八区,自1970年1月1日 0点0分0秒以来的秒数
* @return 时间戳
*/
public static String GenerateTimeStamp()
{
//Java中的getTime方法默认的是从1970 1 1 算起所以可以直接调用
//date.getTime获得的是毫秒数,不是秒,所以最后的结果day应当再除以1000才对。
return String.valueOf(Calendar.getInstance().getTimeInMillis()/1000);
}

/**
* 生成随机串,随机串包含字母或数字
* @return 随机串
*/
public static String GenerateNonceStr()
{
return UUIDGenerator.getUUID().toString().replace(“-“, “”);
}
}
</pre><br>
<pre code_snippet_id=”2067985” snippet_file_name=“blog_20161222_3_3558714” name=“code” class=“java”>package com.luozhuang;

import java.io.IOException;
import java.util.Map;
import java.util.SortedMap;
import java.util.TreeMap;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.xml.parsers.ParserConfigurationException;

import org.slf4j.Logger;
import org.xml.sax.SAXException;

import com.alibaba.fastjson.JSON;
import com.luozhuang.util.Configure;
import com.luozhuang.util.MD5;
import com.luozhuang.util.PayCommonUtil;
import com.luozhuang.util.XMLParser;

/// <summary>
/// 微信支付协议接口数据类,所有的API接口通信都依赖这个数据结构,
/// 在调用接口之前先填充各个字段的值,然后进行接口通信,
/// 这样设计的好处是可扩展性强,用户可随意对协议进行更改而不用重新设计数据结构,
/// 还可以随意组合出不同的协议数据包,不用为每个协议设计一个数据包结构
/// </summary>
public class WxPayData {

private static Logger Log = new Logger();

public WxPayData() {

}

// 采用排序的Dictionary的好处是方便对数据包进行签名,不用再签名之前再做一次排序
private SortedMap<String, Object> m_values = new TreeMap<String, Object>();

public void put(String key, String value) {
SetValue(key, value);

}
/**
* 设置某个字段的值
*
* @param key
* 字段名
* @param value
* 字段值
*/
public void SetValue(String key, Object value) {
m_values.put(key, value);
}

/**
* 根据字段名获取某个字段的值
*
* @param key
* 字段名
* @return key对应的字段值
*/
public Object GetValue(String key) {
Object o = m_values.get(key);
return o;
}

/**
* 判断某个字段是否已设置
*
* @param key
* 字段名
* @return 若字段key已被设置,则返回true,否则返回false
*/
public boolean IsSet(String key) {

Object o = m_values.get(key);
if (null != o)
return true;
else
return false;
}

/**
* @将Dictionary转成xml
* @return 经转换得到的xml串
* @throws WxPayException
**/
public String ToXml() throws WxPayException {
// 数据为空时不能转化为xml格式
if (0 == m_values.size()) {

throw new WxPayException(“WxPayData数据为空!”);
}

/*
* String xml = ”<xml>”; for (Map.Entry<String, Object> pair :
* m_values.entrySet())
*
* { // 字段值不能为null,会影响后续流程 if (pair.getValue() == null) {
*
* throw new WxPayException(“WxPayData内部含有值为null的字段!”); }
*
* if (pair.getValue() instanceof Integer) { xml += ”<” + pair.getKey()
* + ”>” + pair.getValue() + ”</” + pair.getKey() + ”>”; } else if
* (pair.getValue() instanceof String) { xml += ”<” + pair.getKey() +
* ”>” + ”<![CDATA[“ + pair.getValue() + ”]]></” + pair.getKey() + ”>”;
* } else// 除了String和int类型不能含有其他数据类型 {
*
* throw new WxPayException(“WxPayData字段数据类型错误!”); } } xml += ”</xml>”;
*/
return PayCommonUtil.getRequestXml(m_values);
}

/**
* @将xml转为WxPayData对象并返回对象内部的数据
* @param String
* 待转换的xml串
* @return 经转换得到的Dictionary
* @throws WxPayException
* @throws SAXException
* @throws IOException
* @throws ParserConfigurationException
*/
public SortedMap<String, Object> FromXml(String xml)
throws WxPayException, ParserConfigurationException, IOException, SAXException {
if (xml == null) {

throw new WxPayException(“将空的xml串转换为WxPayData不合法!”);
}

m_values = XMLParser.getMapFromXML(xml);

// 2015-06-29 错误是没有签名
if (m_values.get(“return_code”) != “SUCCESS”) {
return m_values;
}
CheckSign();// 验证签名,不通过会抛异常

return m_values;
}

/**
* @throws WxPayException
* @Dictionary格式转化成url参数格式 @ return url格式串, 该串不包含sign字段值
*/
public String ToUrl() throws WxPayException {
String buff = ”“;
for (Map.Entry<String, Object> pair : m_values.entrySet()) {
if (pair.getValue() == null) {
throw new WxPayException(“WxPayData内部含有值为null的字段!”);
}

if (pair.getKey() != “sign” && pair.getValue().toString() != “”) {
buff += pair.getKey() + ”=” + pair.getValue() + “&”;
}
}
buff = sideTrim(buff, ”&”);
return buff;
}

/**
*
*
* 去掉指定字符串的开头和结尾的指定字符
*
*
*
* @param stream
* 要处理的字符串
* @param trimstr
* 要去掉的字符串
* @return 处理后的字符串
*/
public static String sideTrim(String stream, String trimstr) {
// null或者空字符串的时候不处理
if (stream == null || stream.length() == 0 || trimstr == null || trimstr.length() == 0) {
return stream;
}

// 结束位置
int epos = 0;

// 正规表达式
String regpattern = ”[“ + trimstr + “]*+”;
Pattern pattern = Pattern.compile(regpattern, Pattern.CASE_INSENSITIVE);

// 去掉结尾的指定字符
StringBuffer buffer = new StringBuffer(stream).reverse();
Matcher matcher = pattern.matcher(buffer);
if (matcher.lookingAt()) {
epos = matcher.end();
stream = new StringBuffer(buffer.substring(epos)).reverse().toString();
}

// 去掉开头的指定字符
matcher = pattern.matcher(stream);
if (matcher.lookingAt()) {
epos = matcher.end();
stream = stream.substring(epos);
}

// 返回处理后的字符串
return stream;
}

/**
* @Dictionary格式化成Json
* @return json串数据
*/
public String ToJson() {
String jsonStr = JSON.toJSONString(m_values);
return jsonStr;
}

/**
* @throws WxPayException
* @values格式化成能在Web页面上显示的结果(因为web页面上不能直接输出xml格式的字符串)
*/
public String ToPrintStr() throws WxPayException {
String str = ”“;
for (Map.Entry<String, Object> pair : m_values.entrySet()) {
if (pair.getValue() == null) {

throw new WxPayException(“WxPayData内部含有值为null的字段!”);
}

str += String.format(”{0}={1}<br>”, pair.getKey(), pair.getValue().toString());
}

return str;
}

/**
* @生成签名,详见签名生成算法
* @return 签名, sign字段不参加签名
* @throws WxPayException
*/
public String MakeSign() throws WxPayException {
// 转url格式
String str = ToUrl();
// 在String后加入API KEY
str += ”&key=” + Configure.getKey();
// MD5加密
//PayCommonUtil.createSign(“UTF-8”, packageParams,key)
// 所有字符转为大写
return MD5.MD5Encode(str, “UTF-8”).toUpperCase();
}

/**
*
* 检测签名是否正确 正确返回true,错误抛异常
*
* @throws WxPayException
*/
public boolean CheckSign() throws WxPayException {
// 如果没有设置签名,则跳过检测
if (!IsSet(“sign”)) {

throw new WxPayException(“WxPayData签名存在但不合法!”);
}
// 如果设置了签名但是签名为空,则抛异常
else if (GetValue(“sign”) == null || GetValue(“sign”).toString() == “”) {

throw new WxPayException(“WxPayData签名存在但不合法!”);
}

// 获取接收到的签名
String return_sign = GetValue(”sign”).toString();

// 在本地计算新的签名
String cal_sign = MakeSign();

if (cal_sign == return_sign) {
return true;
}

throw new WxPayException(“WxPayData签名验证错误!”);
}

/**
* @获取Dictionary
*/
public SortedMap<String, Object> GetValues() {
return m_values;
}

}</pre><br>
<pre code_snippet_id=”2067985” snippet_file_name=“blog_20161222_4_9512532” name=“code” class=“java”>package com.luozhuang; public class WxPayException extends Exception { public WxPayException(String string) { super(string); } }</pre><br>
<pre code_snippet_id=”2067985” snippet_file_name=“blog_20161222_5_4386556” name=“code” class=“java”>package com.luozhuang.util;

/**
* luozhuang 这里放置各种配置数据
*/
public class Configure {

// 回调地址

public static final String NOTIFY_URL = “http://www.weixin.qq.com/wxpay/pay.php”;

/**
* 这个就是自己要保管好的私有Key了(切记只能放在自己的后台代码里,不能放在任何可能被看到源代码的客户端程序中)
*
* 每次自己Post数据给API的时候都要用这个key来对所有字段进行签名,生成的签名会放在Sign这个字段,API收到Post数据的时候也会用同样的签名算法对Post过来的数据进行签名和验证
* 收到API的返回的时候也要用这个key来对返回的数据算下签名,跟API的Sign数据进行比较,如果值不一致,有可能数据被第三方给篡改
*/
private static String key = “luozhuang”;

/**
* 微信分配的公众号ID(开通公众号之后可以获取到)
*
*/
private static String appID = “luozhuang”;

/**
* 微信支付分配的商户号ID(开通公众号的微信支付功能之后可以获取到)
*
*/
private static String mchID = “luozhuang”;

/**
* 受理模式下给子商户分配的子商户号
*
*/
private static String subMchID = “luozhuang”;

/**
* HTTPS证书的本地路径
*
*/
private static String certLocalPath = “C:\\Users\\luozhuang\\my.store”;

/**
* HTTPS证书密码,默认密码等于商户号MCHID
*
*/
private static String certPassword = “luozhuang”;

/**
* 是否使用异步线程的方式来上报API测速,默认为异步模式
*
*/
private static boolean useThreadToDoReport = true;

/**
* 机器IP
*
*/
private static String ip = “luozhuang”;

// 以下是几个API的路径:
/**
* 1)被扫支付API
*
*/
public static String PAY_API = “https://api.mch.weixin.qq.com/pay/micropay”;

/**
* 2)被扫支付查询API
*
*/
public static String PAY_QUERY_API = “https://api.mch.weixin.qq.com/pay/orderquery”;

/**
* 3)退款API
*
*/
public static String REFUND_API = “https://api.mch.weixin.qq.com/secapi/pay/refund”;

/**
* 4)退款查询API
*
*/
public static String REFUND_QUERY_API = “https://api.mch.weixin.qq.com/pay/refundquery”;

/**
* 5)撤销API
*
*/
public static String REVERSE_API = “https://api.mch.weixin.qq.com/secapi/pay/reverse”;

/**
* 6)下载对账单API
*
*/
public static String DOWNLOAD_BILL_API = “https://api.mch.weixin.qq.com/pay/downloadbill”;

/**
* 7) 统计上报API
*
*/
public static String REPORT_API = “https://api.mch.weixin.qq.com/payitil/report”;

/**
* 转换短链接
*/
public static String Shorturl_API = “https://api.mch.weixin.qq.com/tools/shorturl”;

/**
* 统一下单
*/
public static String UnifiedOrder_API = “https://api.mch.weixin.qq.com/pay/unifiedorder”;

/**
* 关闭订单
*/
public static String CloseOrder_API = “https://api.mch.weixin.qq.com/pay/closeorder”;

public static boolean isUseThreadToDoReport() {
return useThreadToDoReport;
}

public static void setUseThreadToDoReport(boolean useThreadToDoReport) {
Configure.useThreadToDoReport = useThreadToDoReport;
}

public static String HttpsRequestClassName = “com.tencent.common.HttpsRequest”;

public static void setKey(String key) {
Configure.key = key;
}

public static void setAppID(String appID) {
Configure.appID = appID;
}

public static void setMchID(String mchID) {
Configure.mchID = mchID;
}

public static void setSubMchID(String subMchID) {
Configure.subMchID = subMchID;
}

public static void setCertLocalPath(String certLocalPath) {
Configure.certLocalPath = certLocalPath;
}

public static void setCertPassword(String certPassword) {
Configure.certPassword = certPassword;
}

public static void setIp(String ip) {
Configure.ip = ip;
}

public static String getKey() {
return key;
}

public static String getAppid() {
return appID;
}

public static String getMchid() {
return mchID;
}

public static String getSubMchid() {
return subMchID;
}

public static String getCertLocalPath() {
return certLocalPath;
}

public static String getCertPassword() {
return certPassword;
}

public static String getIP() {
return ip;
}

public static void setHttpsRequestClassName(String name) {
HttpsRequestClassName = name;
}

}
</pre><br>
<pre code_snippet_id=”2067985” snippet_file_name=“blog_20161222_6_9293599” name=“code” class=“java”>package com.luozhuang.util;

import com.thoughtworks.xstream.XStream;
import com.thoughtworks.xstream.io.xml.DomDriver;
import com.thoughtworks.xstream.io.xml.XmlFriendlyNameCoder;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.conn.ConnectTimeoutException;
import org.apache.http.conn.ConnectionPoolTimeoutException;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.conn.ssl.SSLContexts;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.net.ssl.SSLContext;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.SocketTimeoutException;
import java.security.*;
import java.security.cert.CertificateException;

/**
* luozhuang
*/
public class HttpsRequest {

public interface ResultListener {

public void onConnectionPoolTimeoutError();

}

private static Logger log = new Logger();

// 表示请求器是否已经做了初始化工作
private boolean hasInit = false;

// 连接超时时间,默认10秒
private int socketTimeout = 10000;

// 传输超时时间,默认30秒
private int connectTimeout = 30000;

// 请求器的配置
private RequestConfig requestConfig;

// HTTP请求器
private CloseableHttpClient httpClient;

public HttpsRequest() throws UnrecoverableKeyException, KeyManagementException, NoSuchAlgorithmException,
KeyStoreException, IOException {
init();
}

/**
* 对安全性有要求的网站一般使用https来加密传输的请求和响应。https离不开证书,关于证书不在多说。Apache的HttpClient支持https,
* 面是官方的样例程序,程序中使用了my.store这个文件,
* 这个文件不是网站的证书,而是一份包含自己密码的自己的证书库。这个文件是需要自己生成的,使用jdk中的keytool命令可以很方便的生成my.store文件。步骤如下(以支付宝为例):
* 浏览器(以chrome为例)访问https://www.alipay.com/,点击域名左侧的小锁,可以查看支付宝的证书信息
*
*
* 将支付包的证书信息导出,证书格式有很多中,der、cer等。随便选择即可。 命令行或者shell执行 keytool -import -alias
* “my alipay cert” -file www.alipay.com.cert -keystore my.store,
* 如果keytool命令不识别,去检查一下jdk的环境变量是否设置正确。”my alipay
* cert”是个别名,随便取。“www.alipay.com.cert”这个文件就是从浏览器中导出的支付宝的证书。
*
* @throws IOException
* @throws KeyStoreException
* @throws UnrecoverableKeyException
* @throws NoSuchAlgorithmException
* @throws KeyManagementException
*/
private void init() throws IOException, KeyStoreException, UnrecoverableKeyException, NoSuchAlgorithmException,
KeyManagementException {
if (Configure.getCertLocalPath() == null || Configure.getCertLocalPath().length() < 1) {

}
KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
FileInputStream instream = new FileInputStream(new File(Configure.getCertLocalPath()));// 加载本地的证书进行https加密传输
try {
keyStore.load(instream, Configure.getCertPassword().toCharArray());// 设置证书密码
} catch (CertificateException e) {
e.printStackTrace();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} finally {
instream.close();
}

// Trust own CA and all self-signed certs
SSLContext sslcontext = SSLContexts.custom()
.loadKeyMaterial(keyStore, Configure.getCertPassword().toCharArray()).build();
// Allow TLSv1 protocol only
SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(sslcontext, new String[] { “TLSv1” }, null,
SSLConnectionSocketFactory.BROWSER_COMPATIBLE_HOSTNAME_VERIFIER);

httpClient = HttpClients.custom().setSSLSocketFactory(sslsf).build();

// 根据默认超时限制初始化requestConfig
requestConfig = RequestConfig.custom().setSocketTimeout(socketTimeout).setConnectTimeout(connectTimeout)
.build();

hasInit = true;
}

/**
* 通过Https往API post xml数据
*
* @param url
* API地址
* @param xmlObj
* 要提交的XML数据对象
* @return API回包的实际数据
* @throws IOException
* @throws KeyStoreException
* @throws UnrecoverableKeyException
* @throws NoSuchAlgorithmException
* @throws KeyManagementException
*/

public String sendPost(String url, String postDataXML) throws IOException, KeyStoreException,
UnrecoverableKeyException, NoSuchAlgorithmException, KeyManagementException {

if (!hasInit) {
init();
}

String result = null;

HttpPost httpPost = new HttpPost(url);

Util.log(”API,POST过去的数据是:”);
Util.log(postDataXML);

// 得指明使用UTF-8编码,否则到API服务器XML的中文不能被成功识别
StringEntity postEntity = new StringEntity(postDataXML, “UTF-8”);
httpPost.addHeader(”Content-Type”, “text/xml”);
httpPost.setEntity(postEntity);

// 设置请求器的配置
httpPost.setConfig(requestConfig);

Util.log(”executing request” + httpPost.getRequestLine());

try {
HttpResponse response = httpClient.execute(httpPost);

HttpEntity entity = response.getEntity();

result = EntityUtils.toString(entity, ”UTF-8”);

} catch (ConnectionPoolTimeoutException e) {
log.error(”http get throw ConnectionPoolTimeoutException(wait time out)”, e);

} catch (ConnectTimeoutException e) {
log.error(”http get throw ConnectTimeoutException”, e);

} catch (SocketTimeoutException e) {
log.error(”http get throw SocketTimeoutException”, e);

} catch (Exception e) {
log.error(”http get throw Exception”, e);

} finally {
httpPost.abort();
}

return result;
}

/**
* 设置连接超时时间
*
* @param socketTimeout
* 连接时长,默认10秒
*/
public void setSocketTimeout(int socketTimeout) {
socketTimeout = socketTimeout;
resetRequestConfig();
}

/**
* 设置传输超时时间
*
* @param connectTimeout
* 传输时长,默认30秒
*/
public void setConnectTimeout(int connectTimeout) {
connectTimeout = connectTimeout;
resetRequestConfig();
}

private void resetRequestConfig() {
requestConfig = RequestConfig.custom().setSocketTimeout(socketTimeout).setConnectTimeout(connectTimeout)
.build();
}

/**
* 允许商户自己做更高级更复杂的请求器配置
*
* @param requestConfig
* 设置HttpsRequest的请求器配置
*/
public void setRequestConfig(RequestConfig requestConfig) {
requestConfig = requestConfig;
}

}
</pre><br>
<pre code_snippet_id=”2067985” snippet_file_name=“blog_20161222_7_5629101” name=“code” class=“java”>package com.luozhuang.util;

import java.security.MessageDigest;

/**
* luozhuang
*/
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 原始字符串
* @param characterEncoding
* @return 经过MD5加密之后的结果
*/
public static String MD5Encode(String origin, String charsetname) {
String resultString = null;
try {
resultString = 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 e) {
e.printStackTrace();
}
return resultString;
}

}
</pre><br>
<pre code_snippet_id=”2067985” snippet_file_name=“blog_20161222_8_7471482” name=“code” class=“java”>package com.luozhuang.util;

import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.SortedMap;

import com.luozhuang.WxPayException;

public class PayCommonUtil {

/**
* 是否签名正确,规则是:按参数名称a-z排序,遇到空值的参数不参加签名。
*
* @return boolean
*/
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 = MD5.MD5Encode(sb.toString(), characterEncoding).toLowerCase();
String tenpaySign = ((String) packageParams.get(”sign”)).toLowerCase();

return tenpaySign.equals(mysign);
}

/**
* @author
* @date 2016-4-22
* @Description:sign签名
* @param characterEncoding
* 编码格式
* @param parameters
* 请求参数
* @return
*/
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)) {
sb.append(k + ”=” + v + “&”);
}
}
sb.append(”key=” + API_KEY);
String sign = MD5.MD5Encode(sb.toString(), characterEncoding).toUpperCase();
return sign;
}

/**
* @author
* @date 2016-4-22
* @Description:将请求参数转换为xml格式的string
* @param parameters
* 请求参数
* @return
* @throws WxPayException
*/
public static String getRequestXml(SortedMap<String, Object> parameters) throws WxPayException {
StringBuffer sb = new StringBuffer();
sb.append(”<xml>”);
Set<Map.Entry<String, Object>> es = parameters.entrySet();
Iterator<Map.Entry<String, Object>> it = es.iterator();
while (it.hasNext()) {
Map.Entry<String, Object> entry = (Map.Entry<String, Object>) it.next();
String k = entry.getKey();
String v = (String) entry.getValue();

// 字段值不能为null,会影响后续流程
if (v == null) {

throw new WxPayException(“WxPayData内部含有值为null的字段!”);
}

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

}
</pre><br>
<pre code_snippet_id=”2067985” snippet_file_name=“blog_20161222_9_7046370” name=“code” class=“java”>package com.luozhuang.util;

import java.util.Random;

/**
* luozhuang
*/
public class RandomStringGenerator {

/**
* 获取一定长度的随机字符串
* @param length 指定字符串长度
* @return 一定长度的字符串
*/
public static String getRandomStringByLength(int length) {
String base = ”abcdefghijklmnopqrstuvwxyz0123456789”;
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();
}

}
</pre><br>
<pre code_snippet_id=”2067985” snippet_file_name=“blog_20161222_10_9968546” name=“code” class=“java”>package com.luozhuang.util;

import org.xml.sax.SAXException;

import javax.xml.parsers.ParserConfigurationException;
import java.io.IOException;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Map;

/**
* luozhuang
*/
public class Signature {
/**
* 签名算法
* @param o 要参与签名的数据对象
* @return 签名
* @throws IllegalAccessException
*/
public static String getSign(Object o) throws IllegalAccessException {
ArrayList<String> list = new ArrayList<String>();
Class cls = o.getClass();
Field[] fields = cls.getDeclaredFields();
for (Field f : fields) {
f.setAccessible(true);
if (f.get(o) != null && f.get(o) != “”) {
list.add(f.getName() + ”=” + f.get(o) + “&”);
}
}
int size = list.size();
String [] arrayToSort = list.toArray(new String[size]);
Arrays.sort(arrayToSort, String.CASE_INSENSITIVE_ORDER);
StringBuilder sb = new StringBuilder();
for(int i = 0; i < size; i ++) {
sb.append(arrayToSort[i]);
}
String result = sb.toString();
result += ”key=” + Configure.getKey();
Util.log(”Sign Before MD5:” + result);
result = MD5.MD5Encode(result,null).toUpperCase();
Util.log(”Sign Result:” + result);
return result;
}

public static String getSign(Map<String,Object> map){
ArrayList<String> list = new ArrayList<String>();
for(Map.Entry<String,Object> entry:map.entrySet()){
if(entry.getValue()!=“”){
list.add(entry.getKey() + ”=” + entry.getValue() + “&”);
}
}
int size = list.size();
String [] arrayToSort = list.toArray(new String[size]);
Arrays.sort(arrayToSort, String.CASE_INSENSITIVE_ORDER);
StringBuilder sb = new StringBuilder();
for(int i = 0; i < size; i ++) {
sb.append(arrayToSort[i]);
}
String result = sb.toString();
result += ”key=” + Configure.getKey();
//Util.log(“Sign Before MD5:” + result);
result = MD5.MD5Encode(result,null).toUpperCase();
//Util.log(“Sign Result:” + result);
return result;
}

/**
* 从API返回的XML数据里面重新计算一次签名
* @param responseString API返回的XML数据
* @return 新鲜出炉的签名
* @throws ParserConfigurationException
* @throws IOException
* @throws SAXException
*/
public static String getSignFromResponseString(String responseString) throws IOException, SAXException, ParserConfigurationException {
Map<String,Object> map = XMLParser.getMapFromXML(responseString);
//清掉返回数据对象里面的Sign数据(不能把这个数据也加进去进行签名),然后用签名算法进行签名
map.put(”sign”,“”);
//将API返回的数据根据用签名算法进行计算新的签名,用来跟API返回的签名进行比较
return Signature.getSign(map);
}

/**
* 检验API返回的数据里面的签名是否合法,避免数据在传输的过程中被第三方篡改
* @param responseString API返回的XML数据字符串
* @return API签名是否合法
* @throws ParserConfigurationException
* @throws IOException
* @throws SAXException
*/
public static boolean checkIsSignValidFromResponseString(String responseString) throws ParserConfigurationException, IOException, SAXException {

Map<String,Object> map = XMLParser.getMapFromXML(responseString);
Util.log(map.toString());

String signFromAPIResponse = map.get(”sign”).toString();
if(signFromAPIResponse==“” || signFromAPIResponse == null){
Util.log(”API返回的数据签名数据不存在,有可能被第三方篡改!!!”);
return false;
}
Util.log(”服务器回包里面的签名是:” + signFromAPIResponse);
//清掉返回数据对象里面的Sign数据(不能把这个数据也加进去进行签名),然后用签名算法进行签名
map.put(”sign”,“”);
//将API返回的数据根据用签名算法进行计算新的签名,用来跟API返回的签名进行比较
String signForAPIResponse = Signature.getSign(map);

if(!signForAPIResponse.equals(signFromAPIResponse)){
//签名验不过,表示这个API返回的数据有可能已经被篡改了
Util.log(”API返回的数据签名验证不通过,有可能被第三方篡改!!!”);
return false;
}
Util.log(”恭喜,API返回的数据签名验证通过!!!”);
return true;
}

}
</pre><br>
<pre code_snippet_id=”2067985” snippet_file_name=“blog_20161222_11_2367335” name=“code” class=“java”>package com.luozhuang.util;

import com.thoughtworks.xstream.XStream;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.*;
import java.lang.reflect.Field;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Map;

/**
* luozhuang
*/
public class Util {

//打log用
private static Logger logger = new Logger();

/**
* 通过反射的方式遍历对象的属性和属性值,方便调试
*
* @param o 要遍历的对象
* @throws Exception
*/
public static void reflect(Object o) throws Exception {
Class cls = o.getClass();
Field[] fields = cls.getDeclaredFields();
for (int i = 0; i < fields.length; i++) {
Field f = fields[i];
f.setAccessible(true);
Util.log(f.getName() + ” -> ” + f.get(o));
}
}

public static byte[] readInput(InputStream in) throws IOException {
ByteArrayOutputStream out = new ByteArrayOutputStream();
int len = 0;
byte[] buffer = new byte[1024];
while ((len = in.read(buffer)) > 0) {
out.write(buffer, 0, len);
}
out.close();
in.close();
return out.toByteArray();
}

public static String inputStreamToString(InputStream is) throws IOException {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
int i;
while ((i = is.read()) != -1) {
baos.write(i);
}
return baos.toString();
}

public static InputStream getStringStream(String sInputString) throws UnsupportedEncodingException {
ByteArrayInputStream tInputStringStream = null;
if (sInputString != null && !sInputString.trim().equals(“”)) {
tInputStringStream = new ByteArrayInputStream(sInputString.getBytes(“UTF-8”));
}
return tInputStringStream;
}

public static Object getObjectFromXML(String xml, Class tClass) {
//将从API返回的XML数据映射到Java对象
XStream xStreamForResponseData = new XStream();
xStreamForResponseData.alias(”xml”, tClass);
xStreamForResponseData.ignoreUnknownElements();//暂时忽略掉一些新增的字段
return xStreamForResponseData.fromXML(xml);
}

public static String getStringFromMap(Map<String, Object> map, String key, String defaultValue) {
if (key == “” || key == null) {
return defaultValue;
}
String result = (String) map.get(key);
if (result == null) {
return defaultValue;
} else {
return result;
}
}

public static int getIntFromMap(Map<String, Object> map, String key) {
if (key == “” || key == null) {
return 0;
}
if (map.get(key) == null) {
return 0;
}
return Integer.parseInt((String) map.get(key));
}

/**
* 打log接口
* @param log 要打印的log字符串
* @return 返回log
*/
public static String log(Object log){
logger.info(log.toString());
//System.out.println(log);
return log.toString();
}

/**
* 读取本地的xml数据,一般用来自测用
* @param localPath 本地xml文件路径
* @return 读到的xml字符串
*/
public static String getLocalXMLString(String localPath) throws IOException {
return Util.inputStreamToString(Util.class.getResourceAsStream(localPath));
}

}
</pre><br>
<pre code_snippet_id=”2067985” snippet_file_name=“blog_20161222_12_1178072” name=“code” class=“java”>package com.luozhuang.util; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.Node; import org.w3c.dom.NodeList; import org.xml.sax.SAXException; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; import java.io.IOException; import java.io.InputStream; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.SortedMap; import java.util.TreeMap; /** * luozhuang */ public class XMLParser { public static SortedMap<String,Object> getMapFromXML(String xmlString) throws ParserConfigurationException, IOException, SAXException { //这里用Dom的方式解析回包的最主要目的是防止API新增回包字段 DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); DocumentBuilder builder = factory.newDocumentBuilder(); InputStream is = Util.getStringStream(xmlString); Document document = builder.parse(is); //获取到document里面的全部结点 NodeList allNodes = document.getFirstChild().getChildNodes(); Node node; SortedMap<String, Object> map = new TreeMap<String, Object>(); int i=0; while (i < allNodes.getLength()) { node = allNodes.item(i); if(node instanceof Element){ map.put(node.getNodeName(),node.getTextContent()); } i++; } return map; } }</pre><br>
<br>
<p></p>

[java] view plain copy print?package com.luozhuang;

import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.Map;
import java.util.SortedMap;
import java.util.TreeMap;

import javax.servlet.http.HttpServletRequest;

import com.luozhuang.util.Configure;
import com.luozhuang.util.HttpsRequest;
import com.luozhuang.util.PayCommonUtil;
import com.luozhuang.util.XMLParser;

/**
* SDK总入口
*/
public class WXPay {
public String getRemortIP(HttpServletRequest request) {
if (request.getHeader("x-forwarded-for") == null) {
return request.getRemoteAddr();
}
return request.getHeader("x-forwarded-for");
}
/**
* 初始化SDK依赖的几个关键配置
*
* @param key
*            签名算法需要用到的秘钥
* @param appID
*            公众账号ID
* @param mchID
*            商户ID
* @param sdbMchID
*            子商户ID,受理模式必填
* @param certLocalPath
*            HTTP证书在服务器中的路径,用来加载证书用
* @param certPassword
*            HTTP证书的密码,默认等于MCHID
*/
public static void initSDKConfiguration(String key, String appID, String mchID, String sdbMchID,
String certLocalPath, String certPassword) {
Configure.setKey(key);
Configure.setAppID(appID);
Configure.setMchID(mchID);
Configure.setSubMchID(sdbMchID);
Configure.setCertLocalPath(certLocalPath);
Configure.setCertPassword(certPassword);
}

/**
*
*
* @param out_trade_no
* @return
* @throws Exception
*/
public static String weixin_pay(String out_trade_no) throws Exception {
HttpsRequest httpRequest = new HttpsRequest();
// 账号信息
String appid = Configure.getAppid(); // appid
// String appsecret = PayConfigUtil.APP_SECRET; // appsecret
// 商业号
String mch_id = Configure.getMchid();
// key
String key = Configure.getKey();

String currTime = PayCommonUtil.getCurrTime();
String strTime = currTime.substring(8, currTime.length());
String strRandom = PayCommonUtil.buildRandom(4) + "";
// 随机字符串
String nonce_str = strTime + strRandom;
// 价格 注意:价格的单位是分
String order_price = "1";
// 商品名称
// String body = "luozhuang";

// 获取发起电脑 ip
String spbill_create_ip = Configure.getIP();
// 回调接口
String notify_url = Configure.NOTIFY_URL;
String product_id="luozhuang";
String trade_type = "NATIVE";//JSAPI--公众号支付、NATIVE--原生扫码支付、APP--app支付
String time_start = new SimpleDateFormat("yyyyMMddHHmmss").format(new Date());
Calendar ca = Calendar.getInstance();
ca.setTime(new Date());
ca.add(Calendar.DATE, 1);
String time_expire = new SimpleDateFormat("yyyyMMddHHmmss").format(ca.getTime());
WxPayData packageParams = new WxPayData();
packageParams.put("appid", appid);
packageParams.put("mch_id", mch_id);
packageParams.put("nonce_str", nonce_str);
packageParams.put("body", "luozhuang-服务费");
packageParams.put("out_trade_no", out_trade_no);
packageParams.put("product_id",product_id);
packageParams.put("total_fee", order_price);
packageParams.put("spbill_create_ip", spbill_create_ip);
packageParams.put("notify_url", notify_url);
packageParams.put("trade_type", trade_type);
packageParams.put("time_start", time_start);
packageParams.put("time_expire", time_expire);
String sign = packageParams.MakeSign();
packageParams.put("sign", sign);
WxPayData resXml=WxPayApi.UnifiedOrder(packageParams, 20);
String requestXML = packageParams.ToXml();
System.out.println("请求xml::::" + requestXML);
System.out.println("得到xml::::" + resXml.toString());
// String return_code = (String) map.get("return_code");
// String prepay_id = (String) map.get("prepay_id");
String urlCode = (String) resXml.GetValue("code_url");
System.out.println("打印调用统一下单接口生成二维码url:::::" + urlCode);
return urlCode;
}

}
package com.luozhuang;

import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.Map;
import java.util.SortedMap;
import java.util.TreeMap;

import javax.servlet.http.HttpServletRequest;

import com.luozhuang.util.Configure;
import com.luozhuang.util.HttpsRequest;
import com.luozhuang.util.PayCommonUtil;
import com.luozhuang.util.XMLParser;

/**
* SDK总入口
*/
public class WXPay {
public String getRemortIP(HttpServletRequest request) {
if (request.getHeader("x-forwarded-for") == null) {
return request.getRemoteAddr();
}
return request.getHeader("x-forwarded-for");
}
/**
* 初始化SDK依赖的几个关键配置
*
* @param key
*            签名算法需要用到的秘钥
* @param appID
*            公众账号ID
* @param mchID
*            商户ID
* @param sdbMchID
*            子商户ID,受理模式必填
* @param certLocalPath
*            HTTP证书在服务器中的路径,用来加载证书用
* @param certPassword
*            HTTP证书的密码,默认等于MCHID
*/
public static void initSDKConfiguration(String key, String appID, String mchID, String sdbMchID,
String certLocalPath, String certPassword) {
Configure.setKey(key);
Configure.setAppID(appID);
Configure.setMchID(mchID);
Configure.setSubMchID(sdbMchID);
Configure.setCertLocalPath(certLocalPath);
Configure.setCertPassword(certPassword);
}

/**
*
*
* @param out_trade_no
* @return
* @throws Exception
*/
public static String weixin_pay(String out_trade_no) throws Exception {
HttpsRequest httpRequest = new HttpsRequest();
// 账号信息
String appid = Configure.getAppid(); // appid
// String appsecret = PayConfigUtil.APP_SECRET; // appsecret
// 商业号
String mch_id = Configure.getMchid();
// key
String key = Configure.getKey();

String currTime = PayCommonUtil.getCurrTime();
String strTime = currTime.substring(8, currTime.length());
String strRandom = PayCommonUtil.buildRandom(4) + "";
// 随机字符串
String nonce_str = strTime + strRandom;
// 价格 注意:价格的单位是分
String order_price = "1";
// 商品名称
// String body = "luozhuang";

// 获取发起电脑 ip
String spbill_create_ip = Configure.getIP();
// 回调接口
String notify_url = Configure.NOTIFY_URL;
String product_id="luozhuang";
String trade_type = "NATIVE";//JSAPI--公众号支付、NATIVE--原生扫码支付、APP--app支付
String time_start = new SimpleDateFormat("yyyyMMddHHmmss").format(new Date());
Calendar ca = Calendar.getInstance();
ca.setTime(new Date());
ca.add(Calendar.DATE, 1);
String time_expire = new SimpleDateFormat("yyyyMMddHHmmss").format(ca.getTime());
WxPayData packageParams = new WxPayData();
packageParams.put("appid", appid);
packageParams.put("mch_id", mch_id);
packageParams.put("nonce_str", nonce_str);
packageParams.put("body", "luozhuang-服务费");
packageParams.put("out_trade_no", out_trade_no);
packageParams.put("product_id",product_id);
packageParams.put("total_fee", order_price);
packageParams.put("spbill_create_ip", spbill_create_ip);
packageParams.put("notify_url", notify_url);
packageParams.put("trade_type", trade_type);
packageParams.put("time_start", time_start);
packageParams.put("time_expire", time_expire);
String sign = packageParams.MakeSign();
packageParams.put("sign", sign);
WxPayData resXml=WxPayApi.UnifiedOrder(packageParams, 20);
String requestXML = packageParams.ToXml();
System.out.println("请求xml::::" + requestXML);
System.out.println("得到xml::::" + resXml.toString());
// String return_code = (String) map.get("return_code");
// String prepay_id = (String) map.get("prepay_id");
String urlCode = (String) resXml.GetValue("code_url");
System.out.println("打印调用统一下单接口生成二维码url:::::" + urlCode);
return urlCode;
}

}




[java] view plain copy print?package com.luozhuang;

import java.io.IOException;
import java.security.KeyManagementException;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.UnrecoverableKeyException;
import java.util.Calendar;
import java.util.Date;
import java.util.Random;

import javax.xml.parsers.ParserConfigurationException;

import org.xml.sax.SAXException;

import com.fastwixinextend.CommonClass;
import com.luozhuang.util.Configure;
import com.luozhuang.util.HttpsRequest;

public class WxPayApi
{
private static final int REPORT_LEVENL = 0;

/**
* 提交被扫支付API
* 收银员使用扫码设备读取微信用户刷卡授权码以后,二维码或条码信息传送至商户收银台,
* 由商户收银台或者商户后台调用该接口发起支付。
* @param WxPayData inputObj 提交给被扫支付API的参数
* @param int timeOut 超时时间
* @throws WxPayException
* @return 成功时返回调用结果,其他抛异常
* @throws IOException
* @throws KeyStoreException
* @throws NoSuchAlgorithmException
* @throws KeyManagementException
* @throws UnrecoverableKeyException
* @throws SAXException
* @throws ParserConfigurationException
*/
public static WxPayData Micropay(WxPayData inputObj, int timeOut) throws UnrecoverableKeyException, KeyManagementException, NoSuchAlgorithmException, KeyStoreException, IOException, WxPayException, ParserConfigurationException, SAXException
{
String url =Configure.PAY_API;
//检测必填参数
if (!inputObj.IsSet(“body”))
{
throw new WxPayException(“提交被扫支付API接口中,缺少必填参数body!”);
}
else if (!inputObj.IsSet(“out_trade_no”))
{
throw new WxPayException(“提交被扫支付API接口中,缺少必填参数out_trade_no!”);
}
else if (!inputObj.IsSet(“total_fee”))
{
throw new WxPayException(“提交被扫支付API接口中,缺少必填参数total_fee!”);
}
else if (!inputObj.IsSet(“auth_code”))
{
throw new WxPayException(“提交被扫支付API接口中,缺少必填参数auth_code!”);
}

inputObj.SetValue(”spbill_create_ip”, Configure.getIP());//终端ip
inputObj.SetValue(”appid”, Configure.getAppid());//公众账号ID
inputObj.SetValue(”mch_id”, Configure.getMchid());//商户号
inputObj.SetValue(”nonce_str”, UUIDGenerator.getUUID().toString().replace(“-“, “”));//随机字符串
inputObj.SetValue(”sign”, inputObj.MakeSign());//签名
String xml = inputObj.ToXml();

HttpsRequest https=new HttpsRequest();
https.setSocketTimeout(timeOut);
String response = https.sendPost(url, xml);//调用HTTP通信接口以提交数据到API
WxPayData result = new WxPayData();
result.FromXml(response);

return result;
}

/**
*
* 查询订单
* @param WxPayData inputObj 提交给查询订单API的参数
* @param int timeOut 超时时间
* @throws WxPayException
* @return 成功时返回订单查询结果,其他抛异常
* @throws IOException
* @throws KeyStoreException
* @throws NoSuchAlgorithmException
* @throws KeyManagementException
* @throws UnrecoverableKeyException
* @throws SAXException
* @throws ParserConfigurationException
*/
public static WxPayData OrderQuery(WxPayData inputObj, int timeOut) throws UnrecoverableKeyException, KeyManagementException, NoSuchAlgorithmException, KeyStoreException, IOException, WxPayException, ParserConfigurationException, SAXException
{
String url = Configure.PAY_QUERY_API;
//检测必填参数
if (!inputObj.IsSet(“out_trade_no”) && !inputObj.IsSet(“transaction_id”))
{
throw new WxPayException(“订单查询接口中,out_trade_no、transaction_id至少填一个!”);
}

inputObj.SetValue(”appid”, Configure.getAppid());//公众账号ID
inputObj.SetValue(”mch_id”, Configure.getMchid());//商户号
inputObj.SetValue(”nonce_str”, WxPayApi.GenerateNonceStr());//随机字符串
inputObj.SetValue(”sign”, inputObj.MakeSign());//签名

String xml = inputObj.ToXml();

HttpsRequest https=new HttpsRequest();
https.setSocketTimeout(timeOut);
String response = https.sendPost(url, xml);//调用HTTP通信接口以提交数据到API
WxPayData result = new WxPayData();
result.FromXml(response);

return result;
}

/**
*
* 撤销订单API接口
* @param WxPayData inputObj 提交给撤销订单API接口的参数,out_trade_no和transaction_id必填一个
* @param int timeOut 接口超时时间
* @throws WxPayException
* @return 成功时返回API调用结果,其他抛异常
* @throws IOException
* @throws KeyStoreException
* @throws NoSuchAlgorithmException
* @throws KeyManagementException
* @throws UnrecoverableKeyException
* @throws SAXException
* @throws ParserConfigurationException
*/
public static WxPayData Reverse(WxPayData inputObj, int timeOut) throws WxPayException, UnrecoverableKeyException, KeyManagementException, NoSuchAlgorithmException, KeyStoreException, IOException, ParserConfigurationException, SAXException
{
String url =Configure.REVERSE_API;
//检测必填参数
if (!inputObj.IsSet(“out_trade_no”) && !inputObj.IsSet(“transaction_id”))
{
throw new WxPayException(“撤销订单API接口中,参数out_trade_no和transaction_id必须填写一个!”);
}

inputObj.SetValue(”appid”, Configure.getAppid());//公众账号ID
inputObj.SetValue(”mch_id”, Configure.getMchid());//商户号
inputObj.SetValue(”nonce_str”, GenerateNonceStr());//随机字符串
inputObj.SetValue(”sign”, inputObj.MakeSign());//签名
String xml = inputObj.ToXml();

HttpsRequest https=new HttpsRequest();
https.setSocketTimeout(timeOut);
String response = https.sendPost(url, xml);//调用HTTP通信接口以提交数据到API
WxPayData result = new WxPayData();
result.FromXml(response);

return result;
}

/**
*
* 申请退款
* @param WxPayData inputObj 提交给申请退款API的参数
* @param int timeOut 超时时间
* @throws WxPayException
* @return 成功时返回接口调用结果,其他抛异常
* @throws IOException
* @throws KeyStoreException
* @throws NoSuchAlgorithmException
* @throws KeyManagementException
* @throws UnrecoverableKeyException
* @throws SAXException
* @throws ParserConfigurationException
*/
public static WxPayData Refund(WxPayData inputObj, int timeOut) throws WxPayException, UnrecoverableKeyException, KeyManagementException, NoSuchAlgorithmException, KeyStoreException, IOException, ParserConfigurationException, SAXException
{
String url = Configure.REFUND_API;
//检测必填参数
if (!inputObj.IsSet(“out_trade_no”) && !inputObj.IsSet(“transaction_id”))
{
throw new WxPayException(“退款申请接口中,out_trade_no、transaction_id至少填一个!”);
}
else if (!inputObj.IsSet(“out_refund_no”))
{
throw new WxPayException(“退款申请接口中,缺少必填参数out_refund_no!”);
}
else if (!inputObj.IsSet(“total_fee”))
{
throw new WxPayException(“退款申请接口中,缺少必填参数total_fee!”);
}
else if (!inputObj.IsSet(“refund_fee”))
{
throw new WxPayException(“退款申请接口中,缺少必填参数refund_fee!”);
}
else if (!inputObj.IsSet(“op_user_id”))
{
throw new WxPayException(“退款申请接口中,缺少必填参数op_user_id!”);
}

inputObj.SetValue(”appid”, Configure.getAppid());//公众账号ID
inputObj.SetValue(”mch_id”, Configure.getMchid());//商户号
inputObj.SetValue(”nonce_str”, UUIDGenerator.getUUID().toString().replace(“-“, “”));//随机字符串
inputObj.SetValue(”sign”, inputObj.MakeSign());//签名

String xml = inputObj.ToXml();

HttpsRequest https=new HttpsRequest();
https.setSocketTimeout(timeOut);
String response = https.sendPost(url, xml);//调用HTTP通信接口以提交数据到API
WxPayData result = new WxPayData();
result.FromXml(response);

return result;
}

/**
*
* 查询退款
* 提交退款申请后,通过该接口查询退款状态。退款有一定延时,
* 用零钱支付的退款20分钟内到账,银行卡支付的退款3个工作日后重新查询退款状态。
* out_refund_no、out_trade_no、transaction_id、refund_id四个参数必填一个
* @param WxPayData inputObj 提交给查询退款API的参数
* @param int timeOut 接口超时时间
* @throws WxPayException
* @return 成功时返回,其他抛异常
* @throws IOException
* @throws KeyStoreException
* @throws NoSuchAlgorithmException
* @throws KeyManagementException
* @throws UnrecoverableKeyException
* @throws SAXException
* @throws ParserConfigurationException
*/
public static WxPayData RefundQuery(WxPayData inputObj, int timeOut) throws UnrecoverableKeyException, KeyManagementException, NoSuchAlgorithmException, KeyStoreException, IOException, WxPayException, ParserConfigurationException, SAXException
{
String url = Configure.REFUND_QUERY_API;
//检测必填参数
if(!inputObj.IsSet(“out_refund_no”) && !inputObj.IsSet(“out_trade_no”) &&
!inputObj.IsSet(”transaction_id”) && !inputObj.IsSet(“refund_id”))
{
throw new WxPayException(“退款查询接口中,out_refund_no、out_trade_no、transaction_id、refund_id四个参数必填一个!”);
}

inputObj.SetValue(”appid”,Configure.getAppid());//公众账号ID
inputObj.SetValue(”mch_id”,Configure.getMchid());//商户号
inputObj.SetValue(”nonce_str”,GenerateNonceStr());//随机字符串
inputObj.SetValue(”sign”,inputObj.MakeSign());//签名

String xml = inputObj.ToXml();

HttpsRequest https=new HttpsRequest();
https.setSocketTimeout(timeOut);
String response = https.sendPost(url, xml);//调用HTTP通信接口以提交数据到API
WxPayData result = new WxPayData();
result.FromXml(response);

return result;
}

/**
* 下载对账单
* @param WxPayData inputObj 提交给下载对账单API的参数
* @param int timeOut 接口超时时间
* @throws WxPayException
* @return 成功时返回,其他抛异常
* @throws IOException
* @throws NoSuchAlgorithmException
* @throws KeyStoreException
* @throws KeyManagementException
* @throws UnrecoverableKeyException
* @throws SAXException
* @throws ParserConfigurationException
*/
public static WxPayData DownloadBill(WxPayData inputObj, int timeOut) throws UnrecoverableKeyException, KeyManagementException, KeyStoreException, NoSuchAlgorithmException, IOException, WxPayException, ParserConfigurationException, SAXException
{
String url = Configure.DOWNLOAD_BILL_API;
//检测必填参数
if (!inputObj.IsSet(“bill_date”))
{
throw new WxPayException(“对账单接口中,缺少必填参数bill_date!”);
}

inputObj.SetValue(”appid”, Configure.getAppid());//公众账号ID
inputObj.SetValue(”mch_id”, Configure.getMchid());//商户号
inputObj.SetValue(”nonce_str”, GenerateNonceStr());//随机字符串
inputObj.SetValue(”sign”, inputObj.MakeSign());//签名

String xml = inputObj.ToXml();

HttpsRequest https=new HttpsRequest();
https.setSocketTimeout(timeOut);
String response = https.sendPost(url, xml);//调用HTTP通信接口以提交数据到API

WxPayData result = new WxPayData();
//若接口调用失败会返回xml格式的结果
if (response.startsWith(“<xml>”))
{

result.FromXml(response);
}
//接口调用成功则返回非xml格式的数据
else
result.SetValue(”result”, response);

return result;
}

/**
*
* 转换短链接
* 该接口主要用于扫码原生支付模式一中的二维码链接转成短链接(weixin://wxpay/s/XXXXXX),
* 减小二维码数据量,提升扫描速度和精确度。
* @param WxPayData inputObj 提交给转换短连接API的参数
* @param int timeOut 接口超时时间
* @throws WxPayException
* @return 成功时返回,其他抛异常
* @throws IOException
* @throws KeyStoreException
* @throws NoSuchAlgorithmException
* @throws KeyManagementException
* @throws UnrecoverableKeyException
* @throws SAXException
* @throws ParserConfigurationException
*/
public static WxPayData ShortUrl(WxPayData inputObj, int timeOut) throws WxPayException, UnrecoverableKeyException, KeyManagementException, NoSuchAlgorithmException, KeyStoreException, IOException, ParserConfigurationException, SAXException
{
String url = Configure.Shorturl_API;
//检测必填参数
if(!inputObj.IsSet(“long_url”))
{
throw new WxPayException(“需要转换的URL,签名用原串,传输需URL encode!”);
}

inputObj.SetValue(”appid”,Configure.getAppid());//公众账号ID
inputObj.SetValue(”mch_id”,Configure.getMchid());//商户号
inputObj.SetValue(”nonce_str”,GenerateNonceStr());//随机字符串
inputObj.SetValue(”sign”,inputObj.MakeSign());//签名
String xml = inputObj.ToXml();

HttpsRequest https=new HttpsRequest();
https.setSocketTimeout(timeOut);
String response = https.sendPost(url, xml);//调用HTTP通信接口以提交数据到API
WxPayData result = new WxPayData();
result.FromXml(response);

return result;
}

/**
*
* 统一下单
* @param WxPaydata inputObj 提交给统一下单API的参数
* @param int timeOut 超时时间
* @throws WxPayException
* @return 成功时返回,其他抛异常
* @throws IOException
* @throws KeyStoreException
* @throws NoSuchAlgorithmException
* @throws KeyManagementException
* @throws UnrecoverableKeyException
* @throws SAXException
* @throws ParserConfigurationException
*/
public static WxPayData UnifiedOrder(WxPayData inputObj, int timeOut) throws WxPayException, UnrecoverableKeyException, KeyManagementException, NoSuchAlgorithmException, KeyStoreException, IOException, ParserConfigurationException, SAXException
{
String url =Configure.UnifiedOrder_API;
//检测必填参数
if (!inputObj.IsSet(“out_trade_no”))
{
throw new WxPayException(“缺少统一支付接口必填参数out_trade_no!”);
}
else if (!inputObj.IsSet(“body”))
{
throw new WxPayException(“缺少统一支付接口必填参数body!”);
}
else if (!inputObj.IsSet(“total_fee”))
{
throw new WxPayException(“缺少统一支付接口必填参数total_fee!”);
}
else if (!inputObj.IsSet(“trade_type”))
{
throw new WxPayException(“缺少统一支付接口必填参数trade_type!”);
}

//关联参数
if (inputObj.GetValue(“trade_type”).toString() == “JSAPI” && !inputObj.IsSet(“openid”))
{
throw new WxPayException(“统一支付接口中,缺少必填参数openid!trade_type为JSAPI时,openid为必填参数!”);
}
if (inputObj.GetValue(“trade_type”).toString() == “NATIVE” && !inputObj.IsSet(“product_id”))
{
throw new WxPayException(“统一支付接口中,缺少必填参数product_id!trade_type为JSAPI时,product_id为必填参数!”);
}

//异步通知url未设置,则使用配置文件中的url
if (!inputObj.IsSet(“notify_url”))
{
inputObj.SetValue(”notify_url”, Configure.NOTIFY_URL);//异步通知url
}

inputObj.SetValue(”appid”, Configure.getAppid());//公众账号ID
inputObj.SetValue(”mch_id”, Configure.getMchid());//商户号
inputObj.SetValue(”spbill_create_ip”, Configure.getIP());//终端ip
inputObj.SetValue(”nonce_str”, GenerateNonceStr());//随机字符串

//签名
inputObj.SetValue(”sign”, inputObj.MakeSign());
String xml = inputObj.ToXml();

HttpsRequest https=new HttpsRequest();
https.setSocketTimeout(timeOut);
String response = https.sendPost(url, xml);//调用HTTP通信接口以提交数据到API
WxPayData result = new WxPayData();
result.FromXml(response);

return result;
}

/**
*
* 关闭订单
* @param WxPayData inputObj 提交给关闭订单API的参数
* @param int timeOut 接口超时时间
* @throws WxPayException
* @return 成功时返回,其他抛异常
* @throws IOException
* @throws KeyStoreException
* @throws NoSuchAlgorithmException
* @throws KeyManagementException
* @throws UnrecoverableKeyException
* @throws SAXException
* @throws ParserConfigurationException
*/
public static WxPayData CloseOrder(WxPayData inputObj, int timeOut) throws WxPayException, UnrecoverableKeyException, KeyManagementException, NoSuchAlgorithmException, KeyStoreException, IOException, ParserConfigurationException, SAXException
{

String url =Configure.CloseOrder_API;
//检测必填参数
if(!inputObj.IsSet(“out_trade_no”))
{
throw new WxPayException(“关闭订单接口中,out_trade_no必填!”);
}

inputObj.SetValue(”appid”,Configure.getAppid());//公众账号ID
inputObj.SetValue(”mch_id”,Configure.getMchid());//商户号
inputObj.SetValue(”nonce_str”,GenerateNonceStr());//随机字符串
inputObj.SetValue(”sign”,inputObj.MakeSign());//签名
String xml = inputObj.ToXml();

HttpsRequest https=new HttpsRequest();
https.setSocketTimeout(timeOut);
String response = https.sendPost(url, xml);//调用HTTP通信接口以提交数据到API
WxPayData result = new WxPayData();
result.FromXml(response);

return result;
}

/**
*
* 测速上报
* @param String interface_url 接口URL
* @param int timeCost 接口耗时
* @param WxPayData inputObj参数数组
*/
private static void ReportCostTime(String interface_url, int timeCost, WxPayData inputObj)
{
//如果不需要进行上报
if(REPORT_LEVENL == 0)
{
return;
}

//如果仅失败上报
if(REPORT_LEVENL == 1 && inputObj.IsSet(“return_code”) && inputObj.GetValue(“return_code”).toString() == “SUCCESS” &&
inputObj.IsSet(”result_code”) && inputObj.GetValue(“result_code”).toString() == “SUCCESS”)
{
return;
}

//上报逻辑
WxPayData data = new WxPayData();
data.SetValue(”interface_url”,interface_url);
data.SetValue(”execute_time_”,timeCost);
//返回状态码
if(inputObj.IsSet(“return_code”))
{
data.SetValue(”return_code”,inputObj.GetValue(“return_code”));
}
//返回信息
if(inputObj.IsSet(“return_msg”))
{
data.SetValue(”return_msg”,inputObj.GetValue(“return_msg”));
}
//业务结果
if(inputObj.IsSet(“result_code”))
{
data.SetValue(”result_code”,inputObj.GetValue(“result_code”));
}
//错误代码
if(inputObj.IsSet(“err_code”))
{
data.SetValue(”err_code”,inputObj.GetValue(“err_code”));
}
//错误代码描述
if(inputObj.IsSet(“err_code_des”))
{
data.SetValue(”err_code_des”,inputObj.GetValue(“err_code_des”));
}
//商户订单号
if(inputObj.IsSet(“out_trade_no”))
{
data.SetValue(”out_trade_no”,inputObj.GetValue(“out_trade_no”));
}
//设备号
if(inputObj.IsSet(“device_info”))
{
data.SetValue(”device_info”,inputObj.GetValue(“device_info”));
}

}

/**
*
* 测速上报接口实现
* @param WxPayData inputObj 提交给测速上报接口的参数
* @param int timeOut 测速上报接口超时时间
* @throws WxPayException
* @return 成功时返回测速上报接口返回的结果,其他抛异常
* @throws IOException
* @throws KeyStoreException
* @throws NoSuchAlgorithmException
* @throws KeyManagementException
* @throws UnrecoverableKeyException
* @throws SAXException
* @throws ParserConfigurationException
*/
public static WxPayData Report(WxPayData inputObj, int timeOut) throws WxPayException, UnrecoverableKeyException, KeyManagementException, NoSuchAlgorithmException, KeyStoreException, IOException, ParserConfigurationException, SAXException
{
String url = Configure.REPORT_API;
//检测必填参数
if(!inputObj.IsSet(“interface_url”))
{
throw new WxPayException(“接口URL,缺少必填参数interface_url!”);
}
if(!inputObj.IsSet(“return_code”))
{
throw new WxPayException(“返回状态码,缺少必填参数return_code!”);
}
if(!inputObj.IsSet(“result_code”))
{
throw new WxPayException(“业务结果,缺少必填参数result_code!”);
}
if(!inputObj.IsSet(“user_ip”))
{
throw new WxPayException(“访问接口IP,缺少必填参数user_ip!”);
}
if(!inputObj.IsSet(“execute_time_”))
{
throw new WxPayException(“接口耗时,缺少必填参数execute_time_!”);
}

inputObj.SetValue(”appid”,Configure.getAppid());//公众账号ID
inputObj.SetValue(”mch_id”,Configure.getMchid());//商户号
inputObj.SetValue(”user_ip”,Configure.getIP());//终端ip
inputObj.SetValue(”time”,CommonClass.GetCurrentDateTimeTextNosplit());//商户上报时间
inputObj.SetValue(”nonce_str”,GenerateNonceStr());//随机字符串
inputObj.SetValue(”sign”,inputObj.MakeSign());//签名
String xml = inputObj.ToXml();

HttpsRequest https=new HttpsRequest();
https.setSocketTimeout(timeOut);
String response = https.sendPost(url, xml);//调用HTTP通信接口以提交数据到API
WxPayData result = new WxPayData();
result.FromXml(response);

return result;
}

/**
* 根据当前系统时间加随机序列来生成订单号
* @return 订单号
*/
public static String GenerateOutTradeNo()
{
Random ran = new Random();
return String.format(“{0}{1}{2}”, Configure.getMchid(),CommonClass.GetCurrentDateTimeTextNosplit(), ran.nextInt(999));
}

/**
* 生成时间戳,标准北京时间,时区为东八区,自1970年1月1日 0点0分0秒以来的秒数
* @return 时间戳
*/
public static String GenerateTimeStamp()
{
//Java中的getTime方法默认的是从1970 1 1 算起所以可以直接调用
//date.getTime获得的是毫秒数,不是秒,所以最后的结果day应当再除以1000才对。
return String.valueOf(Calendar.getInstance().getTimeInMillis()/1000);
}

/**
* 生成随机串,随机串包含字母或数字
* @return 随机串
*/
public static String GenerateNonceStr()
{
return UUIDGenerator.getUUID().toString().replace(“-“, “”);
}
}
package com.luozhuang;

import java.io.IOException;
import java.security.KeyManagementException;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.UnrecoverableKeyException;
import java.util.Calendar;
import java.util.Date;
import java.util.Random;

import javax.xml.parsers.ParserConfigurationException;

import org.xml.sax.SAXException;

import com.fastwixinextend.CommonClass;
import com.luozhuang.util.Configure;
import com.luozhuang.util.HttpsRequest;

public class WxPayApi
{
private static final int REPORT_LEVENL = 0;

/**
* 提交被扫支付API
* 收银员使用扫码设备读取微信用户刷卡授权码以后,二维码或条码信息传送至商户收银台,
* 由商户收银台或者商户后台调用该接口发起支付。
* @param WxPayData inputObj 提交给被扫支付API的参数
* @param int timeOut 超时时间
* @throws WxPayException
* @return 成功时返回调用结果,其他抛异常
* @throws IOException
* @throws KeyStoreException
* @throws NoSuchAlgorithmException
* @throws KeyManagementException
* @throws UnrecoverableKeyException
* @throws SAXException
* @throws ParserConfigurationException
*/
public static WxPayData Micropay(WxPayData inputObj, int timeOut) throws UnrecoverableKeyException, KeyManagementException, NoSuchAlgorithmException, KeyStoreException, IOException, WxPayException, ParserConfigurationException, SAXException
{
String url =Configure.PAY_API;
//检测必填参数
if (!inputObj.IsSet("body"))
{
throw new WxPayException("提交被扫支付API接口中,缺少必填参数body!");
}
else if (!inputObj.IsSet("out_trade_no"))
{
throw new WxPayException("提交被扫支付API接口中,缺少必填参数out_trade_no!");
}
else if (!inputObj.IsSet("total_fee"))
{
throw new WxPayException("提交被扫支付API接口中,缺少必填参数total_fee!");
}
else if (!inputObj.IsSet("auth_code"))
{
throw new WxPayException("提交被扫支付API接口中,缺少必填参数auth_code!");
}

inputObj.SetValue("spbill_create_ip", Configure.getIP());//终端ip
inputObj.SetValue("appid", Configure.getAppid());//公众账号ID
inputObj.SetValue("mch_id", Configure.getMchid());//商户号
inputObj.SetValue("nonce_str", UUIDGenerator.getUUID().toString().replace("-", ""));//随机字符串
inputObj.SetValue("sign", inputObj.MakeSign());//签名
String xml = inputObj.ToXml();

HttpsRequest https=new HttpsRequest();
https.setSocketTimeout(timeOut);
String response = https.sendPost(url, xml);//调用HTTP通信接口以提交数据到API
WxPayData result = new WxPayData();
result.FromXml(response);

return result;
}

/**
*
* 查询订单
* @param WxPayData inputObj 提交给查询订单API的参数
* @param int timeOut 超时时间
* @throws WxPayException
* @return 成功时返回订单查询结果,其他抛异常
* @throws IOException
* @throws KeyStoreException
* @throws NoSuchAlgorithmException
* @throws KeyManagementException
* @throws UnrecoverableKeyException
* @throws SAXException
* @throws ParserConfigurationException
*/
public static WxPayData OrderQuery(WxPayData inputObj, int timeOut) throws UnrecoverableKeyException, KeyManagementException, NoSuchAlgorithmException, KeyStoreException, IOException, WxPayException, ParserConfigurationException, SAXException
{
String url = Configure.PAY_QUERY_API;
//检测必填参数
if (!inputObj.IsSet("out_trade_no") && !inputObj.IsSet("transaction_id"))
{
throw new WxPayException("订单查询接口中,out_trade_no、transaction_id至少填一个!");
}

inputObj.SetValue("appid", Configure.getAppid());//公众账号ID
inputObj.SetValue("mch_id", Configure.getMchid());//商户号
inputObj.SetValue("nonce_str", WxPayApi.GenerateNonceStr());//随机字符串
inputObj.SetValue("sign", inputObj.MakeSign());//签名

String xml = inputObj.ToXml();

HttpsRequest https=new HttpsRequest();
https.setSocketTimeout(timeOut);
String response = https.sendPost(url, xml);//调用HTTP通信接口以提交数据到API
WxPayData result = new WxPayData();
result.FromXml(response);

return result;
}

/**
*
* 撤销订单API接口
* @param WxPayData inputObj 提交给撤销订单API接口的参数,out_trade_no和transaction_id必填一个
* @param int timeOut 接口超时时间
* @throws WxPayException
* @return 成功时返回API调用结果,其他抛异常
* @throws IOException
* @throws KeyStoreException
* @throws NoSuchAlgorithmException
* @throws KeyManagementException
* @throws UnrecoverableKeyException
* @throws SAXException
* @throws ParserConfigurationException
*/
public static WxPayData Reverse(WxPayData inputObj, int timeOut) throws WxPayException, UnrecoverableKeyException, KeyManagementException, NoSuchAlgorithmException, KeyStoreException, IOException, ParserConfigurationException, SAXException
{
String url =Configure.REVERSE_API;
//检测必填参数
if (!inputObj.IsSet("out_trade_no") && !inputObj.IsSet("transaction_id"))
{
throw new WxPayException("撤销订单API接口中,参数out_trade_no和transaction_id必须填写一个!");
}

inputObj.SetValue("appid", Configure.getAppid());//公众账号ID
inputObj.SetValue("mch_id", Configure.getMchid());//商户号
inputObj.SetValue("nonce_str", GenerateNonceStr());//随机字符串
inputObj.SetValue("sign", inputObj.MakeSign());//签名
String xml = inputObj.ToXml();

HttpsRequest https=new HttpsRequest();
https.setSocketTimeout(timeOut);
String response = https.sendPost(url, xml);//调用HTTP通信接口以提交数据到API
WxPayData result = new WxPayData();
result.FromXml(response);

return result;
}

/**
*
* 申请退款
* @param WxPayData inputObj 提交给申请退款API的参数
* @param int timeOut 超时时间
* @throws WxPayException
* @return 成功时返回接口调用结果,其他抛异常
* @throws IOException
* @throws KeyStoreException
* @throws NoSuchAlgorithmException
* @throws KeyManagementException
* @throws UnrecoverableKeyException
* @throws SAXException
* @throws ParserConfigurationException
*/
public static WxPayData Refund(WxPayData inputObj, int timeOut) throws WxPayException, UnrecoverableKeyException, KeyManagementException, NoSuchAlgorithmException, KeyStoreException, IOException, ParserConfigurationException, SAXException
{
String url = Configure.REFUND_API;
//检测必填参数
if (!inputObj.IsSet("out_trade_no") && !inputObj.IsSet("transaction_id"))
{
throw new WxPayException("退款申请接口中,out_trade_no、transaction_id至少填一个!");
}
else if (!inputObj.IsSet("out_refund_no"))
{
throw new WxPayException("退款申请接口中,缺少必填参数out_refund_no!");
}
else if (!inputObj.IsSet("total_fee"))
{
throw new WxPayException("退款申请接口中,缺少必填参数total_fee!");
}
else if (!inputObj.IsSet("refund_fee"))
{
throw new WxPayException("退款申请接口中,缺少必填参数refund_fee!");
}
else if (!inputObj.IsSet("op_user_id"))
{
throw new WxPayException("退款申请接口中,缺少必填参数op_user_id!");
}

inputObj.SetValue("appid", Configure.getAppid());//公众账号ID
inputObj.SetValue("mch_id", Configure.getMchid());//商户号
inputObj.SetValue("nonce_str", UUIDGenerator.getUUID().toString().replace("-", ""));//随机字符串
inputObj.SetValue("sign", inputObj.MakeSign());//签名

String xml = inputObj.ToXml();

HttpsRequest https=new HttpsRequest();
https.setSocketTimeout(timeOut);
String response = https.sendPost(url, xml);//调用HTTP通信接口以提交数据到API
WxPayData result = new WxPayData();
result.FromXml(response);

return result;
}

/**
*
* 查询退款
* 提交退款申请后,通过该接口查询退款状态。退款有一定延时,
* 用零钱支付的退款20分钟内到账,银行卡支付的退款3个工作日后重新查询退款状态。
* out_refund_no、out_trade_no、transaction_id、refund_id四个参数必填一个
* @param WxPayData inputObj 提交给查询退款API的参数
* @param int timeOut 接口超时时间
* @throws WxPayException
* @return 成功时返回,其他抛异常
* @throws IOException
* @throws KeyStoreException
* @throws NoSuchAlgorithmException
* @throws KeyManagementException
* @throws UnrecoverableKeyException
* @throws SAXException
* @throws ParserConfigurationException
*/
public static WxPayData RefundQuery(WxPayData inputObj, int timeOut) throws UnrecoverableKeyException, KeyManagementException, NoSuchAlgorithmException, KeyStoreException, IOException, WxPayException, ParserConfigurationException, SAXException
{
String url = Configure.REFUND_QUERY_API;
//检测必填参数
if(!inputObj.IsSet("out_refund_no") && !inputObj.IsSet("out_trade_no") &&
!inputObj.IsSet("transaction_id") && !inputObj.IsSet("refund_id"))
{
throw new WxPayException("退款查询接口中,out_refund_no、out_trade_no、transaction_id、refund_id四个参数必填一个!");
}

inputObj.SetValue("appid",Configure.getAppid());//公众账号ID
inputObj.SetValue("mch_id",Configure.getMchid());//商户号
inputObj.SetValue("nonce_str",GenerateNonceStr());//随机字符串
inputObj.SetValue("sign",inputObj.MakeSign());//签名

String xml = inputObj.ToXml();

HttpsRequest https=new HttpsRequest();
https.setSocketTimeout(timeOut);
String response = https.sendPost(url, xml);//调用HTTP通信接口以提交数据到API
WxPayData result = new WxPayData();
result.FromXml(response);

return result;
}

/**
* 下载对账单
* @param WxPayData inputObj 提交给下载对账单API的参数
* @param int timeOut 接口超时时间
* @throws WxPayException
* @return 成功时返回,其他抛异常
* @throws IOException
* @throws NoSuchAlgorithmException
* @throws KeyStoreException
* @throws KeyManagementException
* @throws UnrecoverableKeyException
* @throws SAXException
* @throws ParserConfigurationException
*/
public static WxPayData DownloadBill(WxPayData inputObj, int timeOut) throws UnrecoverableKeyException, KeyManagementException, KeyStoreException, NoSuchAlgorithmException, IOException, WxPayException, ParserConfigurationException, SAXException
{
String url = Configure.DOWNLOAD_BILL_API;
//检测必填参数
if (!inputObj.IsSet("bill_date"))
{
throw new WxPayException("对账单接口中,缺少必填参数bill_date!");
}

inputObj.SetValue("appid", Configure.getAppid());//公众账号ID
inputObj.SetValue("mch_id", Configure.getMchid());//商户号
inputObj.SetValue("nonce_str", GenerateNonceStr());//随机字符串
inputObj.SetValue("sign", inputObj.MakeSign());//签名

String xml = inputObj.ToXml();

HttpsRequest https=new HttpsRequest();
https.setSocketTimeout(timeOut);
String response = https.sendPost(url, xml);//调用HTTP通信接口以提交数据到API

WxPayData result = new WxPayData();
//若接口调用失败会返回xml格式的结果
if (response.startsWith("<xml>"))
{

result.FromXml(response);
}
//接口调用成功则返回非xml格式的数据
else
result.SetValue("result", response);

return result;
}

/**
*
* 转换短链接
* 该接口主要用于扫码原生支付模式一中的二维码链接转成短链接(weixin://wxpay/s/XXXXXX),
* 减小二维码数据量,提升扫描速度和精确度。
* @param WxPayData inputObj 提交给转换短连接API的参数
* @param int timeOut 接口超时时间
* @throws WxPayException
* @return 成功时返回,其他抛异常
* @throws IOException
* @throws KeyStoreException
* @throws NoSuchAlgorithmException
* @throws KeyManagementException
* @throws UnrecoverableKeyException
* @throws SAXException
* @throws ParserConfigurationException
*/
public static WxPayData ShortUrl(WxPayData inputObj, int timeOut) throws WxPayException, UnrecoverableKeyException, KeyManagementException, NoSuchAlgorithmException, KeyStoreException, IOException, ParserConfigurationException, SAXException
{
String url = Configure.Shorturl_API;
//检测必填参数
if(!inputObj.IsSet("long_url"))
{
throw new WxPayException("需要转换的URL,签名用原串,传输需URL encode!");
}

inputObj.SetValue("appid",Configure.getAppid());//公众账号ID
inputObj.SetValue("mch_id",Configure.getMchid());//商户号
inputObj.SetValue("nonce_str",GenerateNonceStr());//随机字符串
inputObj.SetValue("sign",inputObj.MakeSign());//签名
String xml = inputObj.ToXml();

HttpsRequest https=new HttpsRequest();
https.setSocketTimeout(timeOut);
String response = https.sendPost(url, xml);//调用HTTP通信接口以提交数据到API
WxPayData result = new WxPayData();
result.FromXml(response);

return result;
}

/**
*
* 统一下单
* @param WxPaydata inputObj 提交给统一下单API的参数
* @param int timeOut 超时时间
* @throws WxPayException
* @return 成功时返回,其他抛异常
* @throws IOException
* @throws KeyStoreException
* @throws NoSuchAlgorithmException
* @throws KeyManagementException
* @throws UnrecoverableKeyException
* @throws SAXException
* @throws ParserConfigurationException
*/
public static WxPayData UnifiedOrder(WxPayData inputObj, int timeOut) throws WxPayException, UnrecoverableKeyException, KeyManagementException, NoSuchAlgorithmException, KeyStoreException, IOException, ParserConfigurationException, SAXException
{
String url =Configure.UnifiedOrder_API;
//检测必填参数
if (!inputObj.IsSet("out_trade_no"))
{
throw new WxPayException("缺少统一支付接口必填参数out_trade_no!");
}
else if (!inputObj.IsSet("body"))
{
throw new WxPayException("缺少统一支付接口必填参数body!");
}
else if (!inputObj.IsSet("total_fee"))
{
throw new WxPayException("缺少统一支付接口必填参数total_fee!");
}
else if (!inputObj.IsSet("trade_type"))
{
throw new WxPayException("缺少统一支付接口必填参数trade_type!");
}

//关联参数
if (inputObj.GetValue("trade_type").toString() == "JSAPI" && !inputObj.IsSet("openid"))
{
throw new WxPayException("统一支付接口中,缺少必填参数openid!trade_type为JSAPI时,openid为必填参数!");
}
if (inputObj.GetValue("trade_type").toString() == "NATIVE" && !inputObj.IsSet("product_id"))
{
throw new WxPayException("统一支付接口中,缺少必填参数product_id!trade_type为JSAPI时,product_id为必填参数!");
}

//异步通知url未设置,则使用配置文件中的url
if (!inputObj.IsSet("notify_url"))
{
inputObj.SetValue("notify_url", Configure.NOTIFY_URL);//异步通知url
}

inputObj.SetValue("appid", Configure.getAppid());//公众账号ID
inputObj.SetValue("mch_id", Configure.getMchid());//商户号
inputObj.SetValue("spbill_create_ip", Configure.getIP());//终端ip
inputObj.SetValue("nonce_str", GenerateNonceStr());//随机字符串

//签名
inputObj.SetValue("sign", inputObj.MakeSign());
String xml = inputObj.ToXml();

HttpsRequest https=new HttpsRequest();
https.setSocketTimeout(timeOut);
String response = https.sendPost(url, xml);//调用HTTP通信接口以提交数据到API
WxPayData result = new WxPayData();
result.FromXml(response);

return result;
}

/**
*
* 关闭订单
* @param WxPayData inputObj 提交给关闭订单API的参数
* @param int timeOut 接口超时时间
* @throws WxPayException
* @return 成功时返回,其他抛异常
* @throws IOException
* @throws KeyStoreException
* @throws NoSuchAlgorithmException
* @throws KeyManagementException
* @throws UnrecoverableKeyException
* @throws SAXException
* @throws ParserConfigurationException
*/
public static WxPayData CloseOrder(WxPayData inputObj, int timeOut) throws WxPayException, UnrecoverableKeyException, KeyManagementException, NoSuchAlgorithmException, KeyStoreException, IOException, ParserConfigurationException, SAXException
{

String url =Configure.CloseOrder_API;
//检测必填参数
if(!inputObj.IsSet("out_trade_no"))
{
throw new WxPayException("关闭订单接口中,out_trade_no必填!");
}

inputObj.SetValue("appid",Configure.getAppid());//公众账号ID
inputObj.SetValue("mch_id",Configure.getMchid());//商户号
inputObj.SetValue("nonce_str",GenerateNonceStr());//随机字符串
inputObj.SetValue("sign",inputObj.MakeSign());//签名
String xml = inputObj.ToXml();

HttpsRequest https=new HttpsRequest();
https.setSocketTimeout(timeOut);
String response = https.sendPost(url, xml);//调用HTTP通信接口以提交数据到API
WxPayData result = new WxPayData();
result.FromXml(response);

return result;
}

/**
*
* 测速上报
* @param String interface_url 接口URL
* @param int timeCost 接口耗时
* @param WxPayData inputObj参数数组
*/
private static void ReportCostTime(String interface_url, int timeCost, WxPayData inputObj)
{
//如果不需要进行上报
if(REPORT_LEVENL == 0)
{
return;
}

//如果仅失败上报
if(REPORT_LEVENL == 1 && inputObj.IsSet("return_code") && inputObj.GetValue("return_code").toString() == "SUCCESS" &&
inputObj.IsSet("result_code") && inputObj.GetValue("result_code").toString() == "SUCCESS")
{
return;
}

//上报逻辑
WxPayData data = new WxPayData();
data.SetValue("interface_url",interface_url);
data.SetValue("execute_time_",timeCost);
//返回状态码
if(inputObj.IsSet("return_code"))
{
data.SetValue("return_code",inputObj.GetValue("return_code"));
}
//返回信息
if(inputObj.IsSet("return_msg"))
{
data.SetValue("return_msg",inputObj.GetValue("return_msg"));
}
//业务结果
if(inputObj.IsSet("result_code"))
{
data.SetValue("result_code",inputObj.GetValue("result_code"));
}
//错误代码
if(inputObj.IsSet("err_code"))
{
data.SetValue("err_code",inputObj.GetValue("err_code"));
}
//错误代码描述
if(inputObj.IsSet("err_code_des"))
{
data.SetValue("err_code_des",inputObj.GetValue("err_code_des"));
}
//商户订单号
if(inputObj.IsSet("out_trade_no"))
{
data.SetValue("out_trade_no",inputObj.GetValue("out_trade_no"));
}
//设备号
if(inputObj.IsSet("device_info"))
{
data.SetValue("device_info",inputObj.GetValue("device_info"));
}

}

/**
*
* 测速上报接口实现
* @param WxPayData inputObj 提交给测速上报接口的参数
* @param int timeOut 测速上报接口超时时间
* @throws WxPayException
* @return 成功时返回测速上报接口返回的结果,其他抛异常
* @throws IOException
* @throws KeyStoreException
* @throws NoSuchAlgorithmException
* @throws KeyManagementException
* @throws UnrecoverableKeyException
* @throws SAXException
* @throws ParserConfigurationException
*/
public static WxPayData Report(WxPayData inputObj, int timeOut) throws WxPayException, UnrecoverableKeyException, KeyManagementException, NoSuchAlgorithmException, KeyStoreException, IOException, ParserConfigurationException, SAXException
{
String url = Configure.REPORT_API;
//检测必填参数
if(!inputObj.IsSet("interface_url"))
{
throw new WxPayException("接口URL,缺少必填参数interface_url!");
}
if(!inputObj.IsSet("return_code"))
{
throw new WxPayException("返回状态码,缺少必填参数return_code!");
}
if(!inputObj.IsSet("result_code"))
{
throw new WxPayException("业务结果,缺少必填参数result_code!");
}
if(!inputObj.IsSet("user_ip"))
{
throw new WxPayException("访问接口IP,缺少必填参数user_ip!");
}
if(!inputObj.IsSet("execute_time_"))
{
throw new WxPayException("接口耗时,缺少必填参数execute_time_!");
}

inputObj.SetValue("appid",Configure.getAppid());//公众账号ID
inputObj.SetValue("mch_id",Configure.getMchid());//商户号
inputObj.SetValue("user_ip",Configure.getIP());//终端ip
inputObj.SetValue("time",CommonClass.GetCurrentDateTimeTextNosplit());//商户上报时间
inputObj.SetValue("nonce_str",GenerateNonceStr());//随机字符串
inputObj.SetValue("sign",inputObj.MakeSign());//签名
String xml = inputObj.ToXml();

HttpsRequest https=new HttpsRequest();
https.setSocketTimeout(timeOut);
String response = https.sendPost(url, xml);//调用HTTP通信接口以提交数据到API
WxPayData result = new WxPayData();
result.FromXml(response);

return result;
}

/**
* 根据当前系统时间加随机序列来生成订单号
* @return 订单号
*/
public static String GenerateOutTradeNo()
{
Random ran = new Random();
return String.format("{0}{1}{2}", Configure.getMchid(),CommonClass.GetCurrentDateTimeTextNosplit(), ran.nextInt(999));
}

/**
* 生成时间戳,标准北京时间,时区为东八区,自1970年1月1日 0点0分0秒以来的秒数
* @return 时间戳
*/
public static String GenerateTimeStamp()
{
//Java中的getTime方法默认的是从1970 1 1 算起所以可以直接调用
//date.getTime获得的是毫秒数,不是秒,所以最后的结果day应当再除以1000才对。
return String.valueOf(Calendar.getInstance().getTimeInMillis()/1000);
}

/**
* 生成随机串,随机串包含字母或数字
* @return 随机串
*/
public static String GenerateNonceStr()
{
return UUIDGenerator.getUUID().toString().replace("-", "");
}
}


[java] view plain copy print?package com.luozhuang;

import java.io.IOException;
import java.util.Map;
import java.util.SortedMap;
import java.util.TreeMap;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.xml.parsers.ParserConfigurationException;

import org.slf4j.Logger;
import org.xml.sax.SAXException;

import com.alibaba.fastjson.JSON;
import com.luozhuang.util.Configure;
import com.luozhuang.util.MD5;
import com.luozhuang.util.PayCommonUtil;
import com.luozhuang.util.XMLParser;

/// <summary>
/// 微信支付协议接口数据类,所有的API接口通信都依赖这个数据结构,
/// 在调用接口之前先填充各个字段的值,然后进行接口通信,
/// 这样设计的好处是可扩展性强,用户可随意对协议进行更改而不用重新设计数据结构,
/// 还可以随意组合出不同的协议数据包,不用为每个协议设计一个数据包结构
/// </summary>
public class WxPayData {

private static Logger Log = new Logger();

public WxPayData() {

}

// 采用排序的Dictionary的好处是方便对数据包进行签名,不用再签名之前再做一次排序
private SortedMap<String, Object> m_values = new TreeMap<String, Object>();

public void put(String key, String value) {
SetValue(key, value);

}
/**
* 设置某个字段的值
*
* @param key
* 字段名
* @param value
* 字段值
*/
public void SetValue(String key, Object value) {
m_values.put(key, value);
}

/**
* 根据字段名获取某个字段的值
*
* @param key
* 字段名
* @return key对应的字段值
*/
public Object GetValue(String key) {
Object o = m_values.get(key);
return o;
}

/**
* 判断某个字段是否已设置
*
* @param key
* 字段名
* @return 若字段key已被设置,则返回true,否则返回false
*/
public boolean IsSet(String key) {

Object o = m_values.get(key);
if (null != o)
return true;
else
return false;
}

/**
* @将Dictionary转成xml
* @return 经转换得到的xml串
* @throws WxPayException
**/
public String ToXml() throws WxPayException {
// 数据为空时不能转化为xml格式
if (0 == m_values.size()) {

throw new WxPayException(“WxPayData数据为空!”);
}

/*
* String xml = ”<xml>”; for (Map.Entry<String, Object> pair :
* m_values.entrySet())
*
* { // 字段值不能为null,会影响后续流程 if (pair.getValue() == null) {
*
* throw new WxPayException(“WxPayData内部含有值为null的字段!”); }
*
* if (pair.getValue() instanceof Integer) { xml += ”<” + pair.getKey()
* + ”>” + pair.getValue() + ”</” + pair.getKey() + ”>”; } else if
* (pair.getValue() instanceof String) { xml += ”<” + pair.getKey() +
* ”>” + ”<![CDATA[“ + pair.getValue() + ”]]></” + pair.getKey() + ”>”;
* } else// 除了String和int类型不能含有其他数据类型 {
*
* throw new WxPayException(“WxPayData字段数据类型错误!”); } } xml += ”</xml>”;
*/
return PayCommonUtil.getRequestXml(m_values);
}

/**
* @将xml转为WxPayData对象并返回对象内部的数据
* @param String
* 待转换的xml串
* @return 经转换得到的Dictionary
* @throws WxPayException
* @throws SAXException
* @throws IOException
* @throws ParserConfigurationException
*/
public SortedMap<String, Object> FromXml(String xml)
throws WxPayException, ParserConfigurationException, IOException, SAXException {
if (xml == null) {

throw new WxPayException(“将空的xml串转换为WxPayData不合法!”);
}

m_values = XMLParser.getMapFromXML(xml);

// 2015-06-29 错误是没有签名
if (m_values.get(“return_code”) != “SUCCESS”) {
return m_values;
}
CheckSign();// 验证签名,不通过会抛异常

return m_values;
}

/**
* @throws WxPayException
* @Dictionary格式转化成url参数格式 @ return url格式串, 该串不包含sign字段值
*/
public String ToUrl() throws WxPayException {
String buff = ”“;
for (Map.Entry<String, Object> pair : m_values.entrySet()) {
if (pair.getValue() == null) {
throw new WxPayException(“WxPayData内部含有值为null的字段!”);
}

if (pair.getKey() != “sign” && pair.getValue().toString() != “”) {
buff += pair.getKey() + ”=” + pair.getValue() + “&”;
}
}
buff = sideTrim(buff, ”&”);
return buff;
}

/**
*
*
* 去掉指定字符串的开头和结尾的指定字符
*
*
*
* @param stream
* 要处理的字符串
* @param trimstr
* 要去掉的字符串
* @return 处理后的字符串
*/
public static String sideTrim(String stream, String trimstr) {
// null或者空字符串的时候不处理
if (stream == null || stream.length() == 0 || trimstr == null || trimstr.length() == 0) {
return stream;
}

// 结束位置
int epos = 0;

// 正规表达式
String regpattern = ”[“ + trimstr + “]*+”;
Pattern pattern = Pattern.compile(regpattern, Pattern.CASE_INSENSITIVE);

// 去掉结尾的指定字符
StringBuffer buffer = new StringBuffer(stream).reverse();
Matcher matcher = pattern.matcher(buffer);
if (matcher.lookingAt()) {
epos = matcher.end();
stream = new StringBuffer(buffer.substring(epos)).reverse().toString();
}

// 去掉开头的指定字符
matcher = pattern.matcher(stream);
if (matcher.lookingAt()) {
epos = matcher.end();
stream = stream.substring(epos);
}

// 返回处理后的字符串
return stream;
}

/**
* @Dictionary格式化成Json
* @return json串数据
*/
public String ToJson() {
String jsonStr = JSON.toJSONString(m_values);
return jsonStr;
}

/**
* @throws WxPayException
* @values格式化成能在Web页面上显示的结果(因为web页面上不能直接输出xml格式的字符串)
*/
public String ToPrintStr() throws WxPayException {
String str = ”“;
for (Map.Entry<String, Object> pair : m_values.entrySet()) {
if (pair.getValue() == null) {

throw new WxPayException(“WxPayData内部含有值为null的字段!”);
}

str += String.format(”{0}={1}<br>”, pair.getKey(), pair.getValue().toString());
}

return str;
}

/**
* @生成签名,详见签名生成算法
* @return 签名, sign字段不参加签名
* @throws WxPayException
*/
public String MakeSign() throws WxPayException {
// 转url格式
String str = ToUrl();
// 在String后加入API KEY
str += ”&key=” + Configure.getKey();
// MD5加密
//PayCommonUtil.createSign(“UTF-8”, packageParams,key)
// 所有字符转为大写
return MD5.MD5Encode(str, “UTF-8”).toUpperCase();
}

/**
*
* 检测签名是否正确 正确返回true,错误抛异常
*
* @throws WxPayException
*/
public boolean CheckSign() throws WxPayException {
// 如果没有设置签名,则跳过检测
if (!IsSet(“sign”)) {

throw new WxPayException(“WxPayData签名存在但不合法!”);
}
// 如果设置了签名但是签名为空,则抛异常
else if (GetValue(“sign”) == null || GetValue(“sign”).toString() == “”) {

throw new WxPayException(“WxPayData签名存在但不合法!”);
}

// 获取接收到的签名
String return_sign = GetValue(”sign”).toString();

// 在本地计算新的签名
String cal_sign = MakeSign();

if (cal_sign == return_sign) {
return true;
}

throw new WxPayException(“WxPayData签名验证错误!”);
}

/**
* @获取Dictionary
*/
public SortedMap<String, Object> GetValues() {
return m_values;
}

}
package com.luozhuang;

import java.io.IOException;
import java.util.Map;
import java.util.SortedMap;
import java.util.TreeMap;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.xml.parsers.ParserConfigurationException;

import org.slf4j.Logger;
import org.xml.sax.SAXException;

import com.alibaba.fastjson.JSON;
import com.luozhuang.util.Configure;
import com.luozhuang.util.MD5;
import com.luozhuang.util.PayCommonUtil;
import com.luozhuang.util.XMLParser;

/// <summary>
/// 微信支付协议接口数据类,所有的API接口通信都依赖这个数据结构,
/// 在调用接口之前先填充各个字段的值,然后进行接口通信,
/// 这样设计的好处是可扩展性强,用户可随意对协议进行更改而不用重新设计数据结构,
/// 还可以随意组合出不同的协议数据包,不用为每个协议设计一个数据包结构
/// </summary>
public class WxPayData {

private static Logger Log = new Logger();

public WxPayData() {

}

// 采用排序的Dictionary的好处是方便对数据包进行签名,不用再签名之前再做一次排序
private SortedMap<String, Object> m_values = new TreeMap<String, Object>();

public void put(String key, String value) {
SetValue(key, value);

}
/**
* 设置某个字段的值
*
* @param key
*            字段名
* @param value
*            字段值
*/
public void SetValue(String key, Object value) {
m_values.put(key, value);
}

/**
* 根据字段名获取某个字段的值
*
* @param key
*            字段名
* @return key对应的字段值
*/
public Object GetValue(String key) {
Object o = m_values.get(key);
return o;
}

/**
* 判断某个字段是否已设置
*
* @param key
*            字段名
* @return 若字段key已被设置,则返回true,否则返回false
*/
public boolean IsSet(String key) {

Object o = m_values.get(key);
if (null != o)
return true;
else
return false;
}

/**
* @将Dictionary转成xml
* @return 经转换得到的xml串
* @throws WxPayException
**/
public String ToXml() throws WxPayException {
// 数据为空时不能转化为xml格式
if (0 == m_values.size()) {

throw new WxPayException("WxPayData数据为空!");
}

/*
* String xml = "<xml>"; for (Map.Entry<String, Object> pair :
* m_values.entrySet())
*
* { // 字段值不能为null,会影响后续流程 if (pair.getValue() == null) {
*
* throw new WxPayException("WxPayData内部含有值为null的字段!"); }
*
* if (pair.getValue() instanceof Integer) { xml += "<" + pair.getKey()
* + ">" + pair.getValue() + "</" + pair.getKey() + ">"; } else if
* (pair.getValue() instanceof String) { xml += "<" + pair.getKey() +
* ">" + "<![CDATA[" + pair.getValue() + "]]></" + pair.getKey() + ">";
* } else// 除了String和int类型不能含有其他数据类型 {
*
* throw new WxPayException("WxPayData字段数据类型错误!"); } } xml += "</xml>";
*/
return PayCommonUtil.getRequestXml(m_values);
}

/**
* @将xml转为WxPayData对象并返回对象内部的数据
* @param String
*            待转换的xml串
* @return 经转换得到的Dictionary
* @throws WxPayException
* @throws SAXException
* @throws IOException
* @throws ParserConfigurationException
*/
public SortedMap<String, Object> FromXml(String xml)
throws WxPayException, ParserConfigurationException, IOException, SAXException {
if (xml == null) {

throw new WxPayException("将空的xml串转换为WxPayData不合法!");
}

m_values = XMLParser.getMapFromXML(xml);

// 2015-06-29 错误是没有签名
if (m_values.get("return_code") != "SUCCESS") {
return m_values;
}
CheckSign();// 验证签名,不通过会抛异常

return m_values;
}

/**
* @throws WxPayException
* @Dictionary格式转化成url参数格式 @ return url格式串, 该串不包含sign字段值
*/
public String ToUrl() throws WxPayException {
String buff = "";
for (Map.Entry<String, Object> pair : m_values.entrySet()) {
if (pair.getValue() == null) {
throw new WxPayException("WxPayData内部含有值为null的字段!");
}

if (pair.getKey() != "sign" && pair.getValue().toString() != "") {
buff += pair.getKey() + "=" + pair.getValue() + "&";
}
}
buff = sideTrim(buff, "&");
return buff;
}

/**
*
*
* 去掉指定字符串的开头和结尾的指定字符
*
*
*
* @param stream
*            要处理的字符串
* @param trimstr
*            要去掉的字符串
* @return 处理后的字符串
*/
public static String sideTrim(String stream, String trimstr) {
// null或者空字符串的时候不处理
if (stream == null || stream.length() == 0 || trimstr == null || trimstr.length() == 0) {
return stream;
}

// 结束位置
int epos = 0;

// 正规表达式
String regpattern = "[" + trimstr + "]*+";
Pattern pattern = Pattern.compile(regpattern, Pattern.CASE_INSENSITIVE);

// 去掉结尾的指定字符
StringBuffer buffer = new StringBuffer(stream).reverse();
Matcher matcher = pattern.matcher(buffer);
if (matcher.lookingAt()) {
epos = matcher.end();
stream = new StringBuffer(buffer.substring(epos)).reverse().toString();
}

// 去掉开头的指定字符
matcher = pattern.matcher(stream);
if (matcher.lookingAt()) {
epos = matcher.end();
stream = stream.substring(epos);
}

// 返回处理后的字符串
return stream;
}

/**
* @Dictionary格式化成Json
* @return json串数据
*/
public String ToJson() {
String jsonStr = JSON.toJSONString(m_values);
return jsonStr;
}

/**
* @throws WxPayException
* @values格式化成能在Web页面上显示的结果(因为web页面上不能直接输出xml格式的字符串)
*/
public String ToPrintStr() throws WxPayException {
String str = "";
for (Map.Entry<String, Object> pair : m_values.entrySet()) {
if (pair.getValue() == null) {

throw new WxPayException("WxPayData内部含有值为null的字段!");
}

str += String.format("{0}={1}<br>", pair.getKey(), pair.getValue().toString());
}

return str;
}

/**
* @生成签名,详见签名生成算法
* @return 签名, sign字段不参加签名
* @throws WxPayException
*/
public String MakeSign() throws WxPayException {
// 转url格式
String str = ToUrl();
// 在String后加入API KEY
str += "&key=" + Configure.getKey();
// MD5加密
//PayCommonUtil.createSign("UTF-8", packageParams,key)
// 所有字符转为大写
return MD5.MD5Encode(str, "UTF-8").toUpperCase();
}

/**
*
* 检测签名是否正确 正确返回true,错误抛异常
*
* @throws WxPayException
*/
public boolean CheckSign() throws WxPayException {
// 如果没有设置签名,则跳过检测
if (!IsSet("sign")) {

throw new WxPayException("WxPayData签名存在但不合法!");
}
// 如果设置了签名但是签名为空,则抛异常
else if (GetValue("sign") == null || GetValue("sign").toString() == "") {

throw new WxPayException("WxPayData签名存在但不合法!");
}

// 获取接收到的签名
String return_sign = GetValue("sign").toString();

// 在本地计算新的签名
String cal_sign = MakeSign();

if (cal_sign == return_sign) {
return true;
}

throw new WxPayException("WxPayData签名验证错误!");
}

/**
* @获取Dictionary
*/
public SortedMap<String, Object> GetValues() {
return m_values;
}

}


[java] view plain copy print?package com.luozhuang; public class WxPayException extends Exception { public WxPayException(String string) { super(string); } }
package com.luozhuang;

public class WxPayException extends Exception {

public WxPayException(String string) {
super(string);
}

}


[java] view plain copy print?package com.luozhuang.util;

/**
* luozhuang 这里放置各种配置数据
*/
public class Configure {

// 回调地址

public static final String NOTIFY_URL = “http://www.weixin.qq.com/wxpay/pay.php”;

/**
* 这个就是自己要保管好的私有Key了(切记只能放在自己的后台代码里,不能放在任何可能被看到源代码的客户端程序中)
*
* 每次自己Post数据给API的时候都要用这个key来对所有字段进行签名,生成的签名会放在Sign这个字段,API收到Post数据的时候也会用同样的签名算法对Post过来的数据进行签名和验证
* 收到API的返回的时候也要用这个key来对返回的数据算下签名,跟API的Sign数据进行比较,如果值不一致,有可能数据被第三方给篡改
*/
private static String key = “luozhuang”;

/**
* 微信分配的公众号ID(开通公众号之后可以获取到)
*
*/
private static String appID = “luozhuang”;

/**
* 微信支付分配的商户号ID(开通公众号的微信支付功能之后可以获取到)
*
*/
private static String mchID = “luozhuang”;

/**
* 受理模式下给子商户分配的子商户号
*
*/
private static String subMchID = “luozhuang”;

/**
* HTTPS证书的本地路径
*
*/
private static String certLocalPath = “C:\\Users\\luozhuang\\my.store”;

/**
* HTTPS证书密码,默认密码等于商户号MCHID
*
*/
private static String certPassword = “luozhuang”;

/**
* 是否使用异步线程的方式来上报API测速,默认为异步模式
*
*/
private static boolean useThreadToDoReport = true;

/**
* 机器IP
*
*/
private static String ip = “luozhuang”;

// 以下是几个API的路径:
/**
* 1)被扫支付API
*
*/
public static String PAY_API = “https://api.mch.weixin.qq.com/pay/micropay”;

/**
* 2)被扫支付查询API
*
*/
public static String PAY_QUERY_API = “https://api.mch.weixin.qq.com/pay/orderquery”;

/**
* 3)退款API
*
*/
public static String REFUND_API = “https://api.mch.weixin.qq.com/secapi/pay/refund”;

/**
* 4)退款查询API
*
*/
public static String REFUND_QUERY_API = “https://api.mch.weixin.qq.com/pay/refundquery”;

/**
* 5)撤销API
*
*/
public static String REVERSE_API = “https://api.mch.weixin.qq.com/secapi/pay/reverse”;

/**
* 6)下载对账单API
*
*/
public static String DOWNLOAD_BILL_API = “https://api.mch.weixin.qq.com/pay/downloadbill”;

/**
* 7) 统计上报API
*
*/
public static String REPORT_API = “https://api.mch.weixin.qq.com/payitil/report”;

/**
* 转换短链接
*/
public static String Shorturl_API = “https://api.mch.weixin.qq.com/tools/shorturl”;

/**
* 统一下单
*/
public static String UnifiedOrder_API = “https://api.mch.weixin.qq.com/pay/unifiedorder”;

/**
* 关闭订单
*/
public static String CloseOrder_API = “https://api.mch.weixin.qq.com/pay/closeorder”;

public static boolean isUseThreadToDoReport() {
return useThreadToDoReport;
}

public static void setUseThreadToDoReport(boolean useThreadToDoReport) {
Configure.useThreadToDoReport = useThreadToDoReport;
}

public static String HttpsRequestClassName = “com.tencent.common.HttpsRequest”;

public static void setKey(String key) {
Configure.key = key;
}

public static void setAppID(String appID) {
Configure.appID = appID;
}

public static void setMchID(String mchID) {
Configure.mchID = mchID;
}

public static void setSubMchID(String subMchID) {
Configure.subMchID = subMchID;
}

public static void setCertLocalPath(String certLocalPath) {
Configure.certLocalPath = certLocalPath;
}

public static void setCertPassword(String certPassword) {
Configure.certPassword = certPassword;
}

public static void setIp(String ip) {
Configure.ip = ip;
}

public static String getKey() {
return key;
}

public static String getAppid() {
return appID;
}

public static String getMchid() {
return mchID;
}

public static String getSubMchid() {
return subMchID;
}

public static String getCertLocalPath() {
return certLocalPath;
}

public static String getCertPassword() {
return certPassword;
}

public static String getIP() {
return ip;
}

public static void setHttpsRequestClassName(String name) {
HttpsRequestClassName = name;
}

}
package com.luozhuang.util;

/**
* luozhuang 这里放置各种配置数据
*/
public class Configure {

// 回调地址

public static final String NOTIFY_URL = "http://www.weixin.qq.com/wxpay/pay.php";

/**
* 这个就是自己要保管好的私有Key了(切记只能放在自己的后台代码里,不能放在任何可能被看到源代码的客户端程序中)
*
* 每次自己Post数据给API的时候都要用这个key来对所有字段进行签名,生成的签名会放在Sign这个字段,API收到Post数据的时候也会用同样的签名算法对Post过来的数据进行签名和验证
* 收到API的返回的时候也要用这个key来对返回的数据算下签名,跟API的Sign数据进行比较,如果值不一致,有可能数据被第三方给篡改
*/
private static String key = "luozhuang";

/**
* 微信分配的公众号ID(开通公众号之后可以获取到)
*
*/
private static String appID = "luozhuang";

/**
* 微信支付分配的商户号ID(开通公众号的微信支付功能之后可以获取到)
*
*/
private static String mchID = "luozhuang";

/**
* 受理模式下给子商户分配的子商户号
*
*/
private static String subMchID = "luozhuang";

/**
* HTTPS证书的本地路径
*
*/
private static String certLocalPath = "C:\\Users\\luozhuang\\my.store";

/**
* HTTPS证书密码,默认密码等于商户号MCHID
*
*/
private static String certPassword = "luozhuang";

/**
* 是否使用异步线程的方式来上报API测速,默认为异步模式
*
*/
private static boolean useThreadToDoReport = true;

/**
* 机器IP
*
*/
private static String ip = "luozhuang";

// 以下是几个API的路径:
/**
* 1)被扫支付API
*
*/
public static String PAY_API = "https://api.mch.weixin.qq.com/pay/micropay";

/**
* 2)被扫支付查询API
*
*/
public static String PAY_QUERY_API = "https://api.mch.weixin.qq.com/pay/orderquery";

/**
* 3)退款API
*
*/
public static String REFUND_API = "https://api.mch.weixin.qq.com/secapi/pay/refund";

/**
* 4)退款查询API
*
*/
public static String REFUND_QUERY_API = "https://api.mch.weixin.qq.com/pay/refundquery";

/**
* 5)撤销API
*
*/
public static String REVERSE_API = "https://api.mch.weixin.qq.com/secapi/pay/reverse";

/**
* 6)下载对账单API
*
*/
public static String DOWNLOAD_BILL_API = "https://api.mch.weixin.qq.com/pay/downloadbill";

/**
* 7) 统计上报API
*
*/
public static String REPORT_API = "https://api.mch.weixin.qq.com/payitil/report";

/**
* 转换短链接
*/
public static String Shorturl_API = "https://api.mch.weixin.qq.com/tools/shorturl";

/**
* 统一下单
*/
public static String UnifiedOrder_API = "https://api.mch.weixin.qq.com/pay/unifiedorder";

/**
* 关闭订单
*/
public static String CloseOrder_API = "https://api.mch.weixin.qq.com/pay/closeorder";

public static boolean isUseThreadToDoReport() {
return useThreadToDoReport;
}

public static void setUseThreadToDoReport(boolean useThreadToDoReport) {
Configure.useThreadToDoReport = useThreadToDoReport;
}

public static String HttpsRequestClassName = "com.tencent.common.HttpsRequest";

public static void setKey(String key) {
Configure.key = key;
}

public static void setAppID(String appID) {
Configure.appID = appID;
}

public static void setMchID(String mchID) {
Configure.mchID = mchID;
}

public static void setSubMchID(String subMchID) {
Configure.subMchID = subMchID;
}

public static void setCertLocalPath(String certLocalPath) {
Configure.certLocalPath = certLocalPath;
}

public static void setCertPassword(String certPassword) {
Configure.certPassword = certPassword;
}

public static void setIp(String ip) {
Configure.ip = ip;
}

public static String getKey() {
return key;
}

public static String getAppid() {
return appID;
}

public static String getMchid() {
return mchID;
}

public static String getSubMchid() {
return subMchID;
}

public static String getCertLocalPath() {
return certLocalPath;
}

public static String getCertPassword() {
return certPassword;
}

public static String getIP() {
return ip;
}

public static void setHttpsRequestClassName(String name) {
HttpsRequestClassName = name;
}

}


[java] view plain copy print?package com.luozhuang.util;

import com.thoughtworks.xstream.XStream;
import com.thoughtworks.xstream.io.xml.DomDriver;
import com.thoughtworks.xstream.io.xml.XmlFriendlyNameCoder;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.conn.ConnectTimeoutException;
import org.apache.http.conn.ConnectionPoolTimeoutException;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.conn.ssl.SSLContexts;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.net.ssl.SSLContext;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.SocketTimeoutException;
import java.security.*;
import java.security.cert.CertificateException;

/**
* luozhuang
*/
public class HttpsRequest {

public interface ResultListener {

public void onConnectionPoolTimeoutError();

}

private static Logger log = new Logger();

// 表示请求器是否已经做了初始化工作
private boolean hasInit = false;

// 连接超时时间,默认10秒
private int socketTimeout = 10000;

// 传输超时时间,默认30秒
private int connectTimeout = 30000;

// 请求器的配置
private RequestConfig requestConfig;

// HTTP请求器
private CloseableHttpClient httpClient;

public HttpsRequest() throws UnrecoverableKeyException, KeyManagementException, NoSuchAlgorithmException,
KeyStoreException, IOException {
init();
}

/**
* 对安全性有要求的网站一般使用https来加密传输的请求和响应。https离不开证书,关于证书不在多说。Apache的HttpClient支持https,
* 面是官方的样例程序,程序中使用了my.store这个文件,
* 这个文件不是网站的证书,而是一份包含自己密码的自己的证书库。这个文件是需要自己生成的,使用jdk中的keytool命令可以很方便的生成my.store文件。步骤如下(以支付宝为例):
* 浏览器(以chrome为例)访问https://www.alipay.com/,点击域名左侧的小锁,可以查看支付宝的证书信息
*
*
* 将支付包的证书信息导出,证书格式有很多中,der、cer等。随便选择即可。 命令行或者shell执行 keytool -import -alias
* “my alipay cert” -file www.alipay.com.cert -keystore my.store,
* 如果keytool命令不识别,去检查一下jdk的环境变量是否设置正确。”my alipay
* cert”是个别名,随便取。“www.alipay.com.cert”这个文件就是从浏览器中导出的支付宝的证书。
*
* @throws IOException
* @throws KeyStoreException
* @throws UnrecoverableKeyException
* @throws NoSuchAlgorithmException
* @throws KeyManagementException
*/
private void init() throws IOException, KeyStoreException, UnrecoverableKeyException, NoSuchAlgorithmException,
KeyManagementException {
if (Configure.getCertLocalPath() == null || Configure.getCertLocalPath().length() < 1) {

}
KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
FileInputStream instream = new FileInputStream(new File(Configure.getCertLocalPath()));// 加载本地的证书进行https加密传输
try {
keyStore.load(instream, Configure.getCertPassword().toCharArray());// 设置证书密码
} catch (CertificateException e) {
e.printStackTrace();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} finally {
instream.close();
}

// Trust own CA and all self-signed certs
SSLContext sslcontext = SSLContexts.custom()
.loadKeyMaterial(keyStore, Configure.getCertPassword().toCharArray()).build();
// Allow TLSv1 protocol only
SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(sslcontext, new String[] { “TLSv1” }, null,
SSLConnectionSocketFactory.BROWSER_COMPATIBLE_HOSTNAME_VERIFIER);

httpClient = HttpClients.custom().setSSLSocketFactory(sslsf).build();

// 根据默认超时限制初始化requestConfig
requestConfig = RequestConfig.custom().setSocketTimeout(socketTimeout).setConnectTimeout(connectTimeout)
.build();

hasInit = true;
}

/**
* 通过Https往API post xml数据
*
* @param url
* API地址
* @param xmlObj
* 要提交的XML数据对象
* @return API回包的实际数据
* @throws IOException
* @throws KeyStoreException
* @throws UnrecoverableKeyException
* @throws NoSuchAlgorithmException
* @throws KeyManagementException
*/

public String sendPost(String url, String postDataXML) throws IOException, KeyStoreException,
UnrecoverableKeyException, NoSuchAlgorithmException, KeyManagementException {

if (!hasInit) {
init();
}

String result = null;

HttpPost httpPost = new HttpPost(url);

Util.log(”API,POST过去的数据是:”);
Util.log(postDataXML);

// 得指明使用UTF-8编码,否则到API服务器XML的中文不能被成功识别
StringEntity postEntity = new StringEntity(postDataXML, “UTF-8”);
httpPost.addHeader(”Content-Type”, “text/xml”);
httpPost.setEntity(postEntity);

// 设置请求器的配置
httpPost.setConfig(requestConfig);

Util.log(”executing request” + httpPost.getRequestLine());

try {
HttpResponse response = httpClient.execute(httpPost);

HttpEntity entity = response.getEntity();

result = EntityUtils.toString(entity, ”UTF-8”);

} catch (ConnectionPoolTimeoutException e) {
log.error(”http get throw ConnectionPoolTimeoutException(wait time out)”, e);

} catch (ConnectTimeoutException e) {
log.error(”http get throw ConnectTimeoutException”, e);

} catch (SocketTimeoutException e) {
log.error(”http get throw SocketTimeoutException”, e);

} catch (Exception e) {
log.error(”http get throw Exception”, e);

} finally {
httpPost.abort();
}

return result;
}

/**
* 设置连接超时时间
*
* @param socketTimeout
* 连接时长,默认10秒
*/
public void setSocketTimeout(int socketTimeout) {
socketTimeout = socketTimeout;
resetRequestConfig();
}

/**
* 设置传输超时时间
*
* @param connectTimeout
* 传输时长,默认30秒
*/
public void setConnectTimeout(int connectTimeout) {
connectTimeout = connectTimeout;
resetRequestConfig();
}

private void resetRequestConfig() {
requestConfig = RequestConfig.custom().setSocketTimeout(socketTimeout).setConnectTimeout(connectTimeout)
.build();
}

/**
* 允许商户自己做更高级更复杂的请求器配置
*
* @param requestConfig
* 设置HttpsRequest的请求器配置
*/
public void setRequestConfig(RequestConfig requestConfig) {
requestConfig = requestConfig;
}

}
package com.luozhuang.util;

import com.thoughtworks.xstream.XStream;
import com.thoughtworks.xstream.io.xml.DomDriver;
import com.thoughtworks.xstream.io.xml.XmlFriendlyNameCoder;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.conn.ConnectTimeoutException;
import org.apache.http.conn.ConnectionPoolTimeoutException;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.conn.ssl.SSLContexts;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.net.ssl.SSLContext;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.SocketTimeoutException;
import java.security.*;
import java.security.cert.CertificateException;

/**
* luozhuang
*/
public class HttpsRequest {

public interface ResultListener {

public void onConnectionPoolTimeoutError();

}

private static Logger log = new Logger();

// 表示请求器是否已经做了初始化工作
private boolean hasInit = false;

// 连接超时时间,默认10秒
private int socketTimeout = 10000;

// 传输超时时间,默认30秒
private int connectTimeout = 30000;

// 请求器的配置
private RequestConfig requestConfig;

// HTTP请求器
private CloseableHttpClient httpClient;

public HttpsRequest() throws UnrecoverableKeyException, KeyManagementException, NoSuchAlgorithmException,
KeyStoreException, IOException {
init();
}

/**
* 对安全性有要求的网站一般使用https来加密传输的请求和响应。https离不开证书,关于证书不在多说。Apache的HttpClient支持https,
* 面是官方的样例程序,程序中使用了my.store这个文件,
* 这个文件不是网站的证书,而是一份包含自己密码的自己的证书库。这个文件是需要自己生成的,使用jdk中的keytool命令可以很方便的生成my.store文件。步骤如下(以支付宝为例):
* 浏览器(以chrome为例)访问https://www.alipay.com/,点击域名左侧的小锁,可以查看支付宝的证书信息
*
*
* 将支付包的证书信息导出,证书格式有很多中,der、cer等。随便选择即可。 命令行或者shell执行 keytool -import -alias
* "my alipay cert" -file www.alipay.com.cert -keystore my.store,
* 如果keytool命令不识别,去检查一下jdk的环境变量是否设置正确。”my alipay
* cert”是个别名,随便取。“www.alipay.com.cert”这个文件就是从浏览器中导出的支付宝的证书。
*
* @throws IOException
* @throws KeyStoreException
* @throws UnrecoverableKeyException
* @throws NoSuchAlgorithmException
* @throws KeyManagementException
*/
private void init() throws IOException, KeyStoreException, UnrecoverableKeyException, NoSuchAlgorithmException,
KeyManagementException {
if (Configure.getCertLocalPath() == null || Configure.getCertLocalPath().length() < 1) {

}
KeyStore keyStore  = KeyStore.getInstance(KeyStore.getDefaultType());
FileInputStream instream = new FileInputStream(new File(Configure.getCertLocalPath()));// 加载本地的证书进行https加密传输
try {
keyStore.load(instream, Configure.getCertPassword().toCharArray());// 设置证书密码
} catch (CertificateException e) {
e.printStackTrace();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} finally {
instream.close();
}

// Trust own CA and all self-signed certs
SSLContext sslcontext = SSLContexts.custom()
.loadKeyMaterial(keyStore, Configure.getCertPassword().toCharArray()).build();
// Allow TLSv1 protocol only
SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(sslcontext, new String[] { "TLSv1" }, null,
SSLConnectionSocketFactory.BROWSER_COMPATIBLE_HOSTNAME_VERIFIER);

httpClient = HttpClients.custom().setSSLSocketFactory(sslsf).build();

// 根据默认超时限制初始化requestConfig
requestConfig = RequestConfig.custom().setSocketTimeout(socketTimeout).setConnectTimeout(connectTimeout)
.build();

hasInit = true;
}

/**
* 通过Https往API post xml数据
*
* @param url
*            API地址
* @param xmlObj
*            要提交的XML数据对象
* @return API回包的实际数据
* @throws IOException
* @throws KeyStoreException
* @throws UnrecoverableKeyException
* @throws NoSuchAlgorithmException
* @throws KeyManagementException
*/

public String sendPost(String url, String postDataXML) throws IOException, KeyStoreException,
UnrecoverableKeyException, NoSuchAlgorithmException, KeyManagementException {

if (!hasInit) {
init();
}

String result = null;

HttpPost httpPost = new HttpPost(url);

Util.log("API,POST过去的数据是:");
Util.log(postDataXML);

// 得指明使用UTF-8编码,否则到API服务器XML的中文不能被成功识别
StringEntity postEntity = new StringEntity(postDataXML, "UTF-8");
httpPost.addHeader("Content-Type", "text/xml");
httpPost.setEntity(postEntity);

// 设置请求器的配置
httpPost.setConfig(requestConfig);

Util.log("executing request" + httpPost.getRequestLine());

try {
HttpResponse response = httpClient.execute(httpPost);

HttpEntity entity = response.getEntity();

result = EntityUtils.toString(entity, "UTF-8");

} catch (ConnectionPoolTimeoutException e) {
log.error("http get throw ConnectionPoolTimeoutException(wait time out)", e);

} catch (ConnectTimeoutException e) {
log.error("http get throw ConnectTimeoutException", e);

} catch (SocketTimeoutException e) {
log.error("http get throw SocketTimeoutException", e);

} catch (Exception e) {
log.error("http get throw Exception", e);

} finally {
httpPost.abort();
}

return result;
}

/**
* 设置连接超时时间
*
* @param socketTimeout
*            连接时长,默认10秒
*/
public void setSocketTimeout(int socketTimeout) {
socketTimeout = socketTimeout;
resetRequestConfig();
}

/**
* 设置传输超时时间
*
* @param connectTimeout
*            传输时长,默认30秒
*/
public void setConnectTimeout(int connectTimeout) {
connectTimeout = connectTimeout;
resetRequestConfig();
}

private void resetRequestConfig() {
requestConfig = RequestConfig.custom().setSocketTimeout(socketTimeout).setConnectTimeout(connectTimeout)
.build();
}

/**
* 允许商户自己做更高级更复杂的请求器配置
*
* @param requestConfig
*            设置HttpsRequest的请求器配置
*/
public void setRequestConfig(RequestConfig requestConfig) {
requestConfig = requestConfig;
}

}


[java] view plain copy print?package com.luozhuang.util;

import java.security.MessageDigest;

/**
* luozhuang
*/
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 原始字符串
* @param characterEncoding
* @return 经过MD5加密之后的结果
*/
public static String MD5Encode(String origin, String charsetname) {
String resultString = null;
try {
resultString = 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 e) {
e.printStackTrace();
}
return resultString;
}

}
package com.luozhuang.util;

import java.security.MessageDigest;

/**
* luozhuang
*/
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 原始字符串
* @param characterEncoding
* @return 经过MD5加密之后的结果
*/
public static String MD5Encode(String origin, String charsetname) {
String resultString = null;
try {
resultString = 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 e) {
e.printStackTrace();
}
return resultString;
}

}


[java] view plain copy print?package com.luozhuang.util;

import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.SortedMap;

import com.luozhuang.WxPayException;

public class PayCommonUtil {

/**
* 是否签名正确,规则是:按参数名称a-z排序,遇到空值的参数不参加签名。
*
* @return boolean
*/
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 = MD5.MD5Encode(sb.toString(), characterEncoding).toLowerCase();
String tenpaySign = ((String) packageParams.get(”sign”)).toLowerCase();

return tenpaySign.equals(mysign);
}

/**
* @author
* @date 2016-4-22
* @Description:sign签名
* @param characterEncoding
* 编码格式
* @param parameters
* 请求参数
* @return
*/
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)) {
sb.append(k + ”=” + v + “&”);
}
}
sb.append(”key=” + API_KEY);
String sign = MD5.MD5Encode(sb.toString(), characterEncoding).toUpperCase();
return sign;
}

/**
* @author
* @date 2016-4-22
* @Description:将请求参数转换为xml格式的string
* @param parameters
* 请求参数
* @return
* @throws WxPayException
*/
public static String getRequestXml(SortedMap<String, Object> parameters) throws WxPayException {
StringBuffer sb = new StringBuffer();
sb.append(”<xml>”);
Set<Map.Entry<String, Object>> es = parameters.entrySet();
Iterator<Map.Entry<String, Object>> it = es.iterator();
while (it.hasNext()) {
Map.Entry<String, Object> entry = (Map.Entry<String, Object>) it.next();
String k = entry.getKey();
String v = (String) entry.getValue();

// 字段值不能为null,会影响后续流程
if (v == null) {

throw new WxPayException(“WxPayData内部含有值为null的字段!”);
}

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.luozhuang.util;

import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.SortedMap;

import com.luozhuang.WxPayException;

public class PayCommonUtil {

/**
* 是否签名正确,规则是:按参数名称a-z排序,遇到空值的参数不参加签名。
*
* @return boolean
*/
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 = MD5.MD5Encode(sb.toString(), characterEncoding).toLowerCase();
String tenpaySign = ((String) packageParams.get("sign")).toLowerCase();

return tenpaySign.equals(mysign);
}

/**
* @author
* @date 2016-4-22
* @Description:sign签名
* @param characterEncoding
*            编码格式
* @param parameters
*            请求参数
* @return
*/
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)) {
sb.append(k + "=" + v + "&");
}
}
sb.append("key=" + API_KEY);
String sign = MD5.MD5Encode(sb.toString(), characterEncoding).toUpperCase();
return sign;
}

/**
* @author
* @date 2016-4-22
* @Description:将请求参数转换为xml格式的string
* @param parameters
*            请求参数
* @return
* @throws WxPayException
*/
public static String getRequestXml(SortedMap<String, Object> parameters) throws WxPayException {
StringBuffer sb = new StringBuffer();
sb.append("<xml>");
Set<Map.Entry<String, Object>> es = parameters.entrySet();
Iterator<Map.Entry<String, Object>> it = es.iterator();
while (it.hasNext()) {
Map.Entry<String, Object> entry = (Map.Entry<String, Object>) it.next();
String k = entry.getKey();
String v = (String) entry.getValue();

// 字段值不能为null,会影响后续流程
if (v == null) {

throw new WxPayException("WxPayData内部含有值为null的字段!");
}

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

}


[java] view plain copy print?package com.luozhuang.util;

import java.util.Random;

/**
* luozhuang
*/
public class RandomStringGenerator {

/**
* 获取一定长度的随机字符串
* @param length 指定字符串长度
* @return 一定长度的字符串
*/
public static String getRandomStringByLength(int length) {
String base = ”abcdefghijklmnopqrstuvwxyz0123456789”;
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();
}

}
package com.luozhuang.util;

import java.util.Random;

/**
* luozhuang
*/
public class RandomStringGenerator {

/**
* 获取一定长度的随机字符串
* @param length 指定字符串长度
* @return 一定长度的字符串
*/
public static String getRandomStringByLength(int length) {
String base = "abcdefghijklmnopqrstuvwxyz0123456789";
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();
}

}


[java] view plain copy print?package com.luozhuang.util;

import org.xml.sax.SAXException;

import javax.xml.parsers.ParserConfigurationException;
import java.io.IOException;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Map;

/**
* luozhuang
*/
public class Signature {
/**
* 签名算法
* @param o 要参与签名的数据对象
* @return 签名
* @throws IllegalAccessException
*/
public static String getSign(Object o) throws IllegalAccessException {
ArrayList<String> list = new ArrayList<String>();
Class cls = o.getClass();
Field[] fields = cls.getDeclaredFields();
for (Field f : fields) {
f.setAccessible(true);
if (f.get(o) != null && f.get(o) != “”) {
list.add(f.getName() + ”=” + f.get(o) + “&”);
}
}
int size = list.size();
String [] arrayToSort = list.toArray(new String[size]);
Arrays.sort(arrayToSort, String.CASE_INSENSITIVE_ORDER);
StringBuilder sb = new StringBuilder();
for(int i = 0; i < size; i ++) {
sb.append(arrayToSort[i]);
}
String result = sb.toString();
result += ”key=” + Configure.getKey();
Util.log(”Sign Before MD5:” + result);
result = MD5.MD5Encode(result,null).toUpperCase();
Util.log(”Sign Result:” + result);
return result;
}

public static String getSign(Map<String,Object> map){
ArrayList<String> list = new ArrayList<String>();
for(Map.Entry<String,Object> entry:map.entrySet()){
if(entry.getValue()!=“”){
list.add(entry.getKey() + ”=” + entry.getValue() + “&”);
}
}
int size = list.size();
String [] arrayToSort = list.toArray(new String[size]);
Arrays.sort(arrayToSort, String.CASE_INSENSITIVE_ORDER);
StringBuilder sb = new StringBuilder();
for(int i = 0; i < size; i ++) {
sb.append(arrayToSort[i]);
}
String result = sb.toString();
result += ”key=” + Configure.getKey();
//Util.log(“Sign Before MD5:” + result);
result = MD5.MD5Encode(result,null).toUpperCase();
//Util.log(“Sign Result:” + result);
return result;
}

/**
* 从API返回的XML数据里面重新计算一次签名
* @param responseString API返回的XML数据
* @return 新鲜出炉的签名
* @throws ParserConfigurationException
* @throws IOException
* @throws SAXException
*/
public static String getSignFromResponseString(String responseString) throws IOException, SAXException, ParserConfigurationException {
Map<String,Object> map = XMLParser.getMapFromXML(responseString);
//清掉返回数据对象里面的Sign数据(不能把这个数据也加进去进行签名),然后用签名算法进行签名
map.put(”sign”,“”);
//将API返回的数据根据用签名算法进行计算新的签名,用来跟API返回的签名进行比较
return Signature.getSign(map);
}

/**
* 检验API返回的数据里面的签名是否合法,避免数据在传输的过程中被第三方篡改
* @param responseString API返回的XML数据字符串
* @return API签名是否合法
* @throws ParserConfigurationException
* @throws IOException
* @throws SAXException
*/
public static boolean checkIsSignValidFromResponseString(String responseString) throws ParserConfigurationException, IOException, SAXException {

Map<String,Object> map = XMLParser.getMapFromXML(responseString);
Util.log(map.toString());

String signFromAPIResponse = map.get(”sign”).toString();
if(signFromAPIResponse==“” || signFromAPIResponse == null){
Util.log(”API返回的数据签名数据不存在,有可能被第三方篡改!!!”);
return false;
}
Util.log(”服务器回包里面的签名是:” + signFromAPIResponse);
//清掉返回数据对象里面的Sign数据(不能把这个数据也加进去进行签名),然后用签名算法进行签名
map.put(”sign”,“”);
//将API返回的数据根据用签名算法进行计算新的签名,用来跟API返回的签名进行比较
String signForAPIResponse = Signature.getSign(map);

if(!signForAPIResponse.equals(signFromAPIResponse)){
//签名验不过,表示这个API返回的数据有可能已经被篡改了
Util.log(”API返回的数据签名验证不通过,有可能被第三方篡改!!!”);
return false;
}
Util.log(”恭喜,API返回的数据签名验证通过!!!”);
return true;
}

}
package com.luozhuang.util;

import org.xml.sax.SAXException;

import javax.xml.parsers.ParserConfigurationException;
import java.io.IOException;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Map;

/**
* luozhuang
*/
public class Signature {
/**
* 签名算法
* @param o 要参与签名的数据对象
* @return 签名
* @throws IllegalAccessException
*/
public static String getSign(Object o) throws IllegalAccessException {
ArrayList<String> list = new ArrayList<String>();
Class cls = o.getClass();
Field[] fields = cls.getDeclaredFields();
for (Field f : fields) {
f.setAccessible(true);
if (f.get(o) != null && f.get(o) != "") {
list.add(f.getName() + "=" + f.get(o) + "&");
}
}
int size = list.size();
String [] arrayToSort = list.toArray(new String[size]);
Arrays.sort(arrayToSort, String.CASE_INSENSITIVE_ORDER);
StringBuilder sb = new StringBuilder();
for(int i = 0; i < size; i ++) {
sb.append(arrayToSort[i]);
}
String result = sb.toString();
result += "key=" + Configure.getKey();
Util.log("Sign Before MD5:" + result);
result = MD5.MD5Encode(result,null).toUpperCase();
Util.log("Sign Result:" + result);
return result;
}

public static String getSign(Map<String,Object> map){
ArrayList<String> list = new ArrayList<String>();
for(Map.Entry<String,Object> entry:map.entrySet()){
if(entry.getValue()!=""){
list.add(entry.getKey() + "=" + entry.getValue() + "&");
}
}
int size = list.size();
String [] arrayToSort = list.toArray(new String[size]);
Arrays.sort(arrayToSort, String.CASE_INSENSITIVE_ORDER);
StringBuilder sb = new StringBuilder();
for(int i = 0; i < size; i ++) {
sb.append(arrayToSort[i]);
}
String result = sb.toString();
result += "key=" + Configure.getKey();
//Util.log("Sign Before MD5:" + result);
result = MD5.MD5Encode(result,null).toUpperCase();
//Util.log("Sign Result:" + result);
return result;
}

/**
* 从API返回的XML数据里面重新计算一次签名
* @param responseString API返回的XML数据
* @return 新鲜出炉的签名
* @throws ParserConfigurationException
* @throws IOException
* @throws SAXException
*/
public static String getSignFromResponseString(String responseString) throws IOException, SAXException, ParserConfigurationException {
Map<String,Object> map = XMLParser.getMapFromXML(responseString);
//清掉返回数据对象里面的Sign数据(不能把这个数据也加进去进行签名),然后用签名算法进行签名
map.put("sign","");
//将API返回的数据根据用签名算法进行计算新的签名,用来跟API返回的签名进行比较
return Signature.getSign(map);
}

/**
* 检验API返回的数据里面的签名是否合法,避免数据在传输的过程中被第三方篡改
* @param responseString API返回的XML数据字符串
* @return API签名是否合法
* @throws ParserConfigurationException
* @throws IOException
* @throws SAXException
*/
public static boolean checkIsSignValidFromResponseString(String responseString) throws ParserConfigurationException, IOException, SAXException {

Map<String,Object> map = XMLParser.getMapFromXML(responseString);
Util.log(map.toString());

String signFromAPIResponse = map.get("sign").toString();
if(signFromAPIResponse=="" || signFromAPIResponse == null){
Util.log("API返回的数据签名数据不存在,有可能被第三方篡改!!!");
return false;
}
Util.log("服务器回包里面的签名是:" + signFromAPIResponse);
//清掉返回数据对象里面的Sign数据(不能把这个数据也加进去进行签名),然后用签名算法进行签名
map.put("sign","");
//将API返回的数据根据用签名算法进行计算新的签名,用来跟API返回的签名进行比较
String signForAPIResponse = Signature.getSign(map);

if(!signForAPIResponse.equals(signFromAPIResponse)){
//签名验不过,表示这个API返回的数据有可能已经被篡改了
Util.log("API返回的数据签名验证不通过,有可能被第三方篡改!!!");
return false;
}
Util.log("恭喜,API返回的数据签名验证通过!!!");
return true;
}

}


[java] view plain copy print?package com.luozhuang.util;

import com.thoughtworks.xstream.XStream;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.*;
import java.lang.reflect.Field;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Map;

/**
* luozhuang
*/
public class Util {

//打log用
private static Logger logger = new Logger();

/**
* 通过反射的方式遍历对象的属性和属性值,方便调试
*
* @param o 要遍历的对象
* @throws Exception
*/
public static void reflect(Object o) throws Exception {
Class cls = o.getClass();
Field[] fields = cls.getDeclaredFields();
for (int i = 0; i < fields.length; i++) {
Field f = fields[i];
f.setAccessible(true);
Util.log(f.getName() + ” -> ” + f.get(o));
}
}

public static byte[] readInput(InputStream in) throws IOException {
ByteArrayOutputStream out = new ByteArrayOutputStream();
int len = 0;
byte[] buffer = new byte[1024];
while ((len = in.read(buffer)) > 0) {
out.write(buffer, 0, len);
}
out.close();
in.close();
return out.toByteArray();
}

public static String inputStreamToString(InputStream is) throws IOException {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
int i;
while ((i = is.read()) != -1) {
baos.write(i);
}
return baos.toString();
}

public static InputStream getStringStream(String sInputString) throws UnsupportedEncodingException {
ByteArrayInputStream tInputStringStream = null;
if (sInputString != null && !sInputString.trim().equals(“”)) {
tInputStringStream = new ByteArrayInputStream(sInputString.getBytes(“UTF-8”));
}
return tInputStringStream;
}

public static Object getObjectFromXML(String xml, Class tClass) {
//将从API返回的XML数据映射到Java对象
XStream xStreamForResponseData = new XStream();
xStreamForResponseData.alias(”xml”, tClass);
xStreamForResponseData.ignoreUnknownElements();//暂时忽略掉一些新增的字段
return xStreamForResponseData.fromXML(xml);
}

public static String getStringFromMap(Map<String, Object> map, String key, String defaultValue) {
if (key == “” || key == null) {
return defaultValue;
}
String result = (String) map.get(key);
if (result == null) {
return defaultValue;
} else {
return result;
}
}

public static int getIntFromMap(Map<String, Object> map, String key) {
if (key == “” || key == null) {
return 0;
}
if (map.get(key) == null) {
return 0;
}
return Integer.parseInt((String) map.get(key));
}

/**
* 打log接口
* @param log 要打印的log字符串
* @return 返回log
*/
public static String log(Object log){
logger.info(log.toString());
//System.out.println(log);
return log.toString();
}

/**
* 读取本地的xml数据,一般用来自测用
* @param localPath 本地xml文件路径
* @return 读到的xml字符串
*/
public static String getLocalXMLString(String localPath) throws IOException {
return Util.inputStreamToString(Util.class.getResourceAsStream(localPath));
}

}
package com.luozhuang.util;

import com.thoughtworks.xstream.XStream;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.*;
import java.lang.reflect.Field;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Map;

/**
* luozhuang
*/
public class Util {

//打log用
private static Logger logger = new Logger();

/**
* 通过反射的方式遍历对象的属性和属性值,方便调试
*
* @param o 要遍历的对象
* @throws Exception
*/
public static void reflect(Object o) throws Exception {
Class cls = o.getClass();
Field[] fields = cls.getDeclaredFields();
for (int i = 0; i < fields.length; i++) {
Field f = fields[i];
f.setAccessible(true);
Util.log(f.getName() + " -> " + f.get(o));
}
}

public static byte[] readInput(InputStream in) throws IOException {
ByteArrayOutputStream out = new ByteArrayOutputStream();
int len = 0;
byte[] buffer = new byte[1024];
while ((len = in.read(buffer)) > 0) {
out.write(buffer, 0, len);
}
out.close();
in.close();
return out.toByteArray();
}

public static String inputStreamToString(InputStream is) throws IOException {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
int i;
while ((i = is.read()) != -1) {
baos.write(i);
}
return baos.toString();
}

public static InputStream getStringStream(String sInputString) throws UnsupportedEncodingException {
ByteArrayInputStream tInputStringStream = null;
if (sInputString != null && !sInputString.trim().equals("")) {
tInputStringStream = new ByteArrayInputStream(sInputString.getBytes("UTF-8"));
}
return tInputStringStream;
}

public static Object getObjectFromXML(String xml, Class tClass) {
//将从API返回的XML数据映射到Java对象
XStream xStreamForResponseData = new XStream();
xStreamForResponseData.alias("xml", tClass);
xStreamForResponseData.ignoreUnknownElements();//暂时忽略掉一些新增的字段
return xStreamForResponseData.fromXML(xml);
}

public static String getStringFromMap(Map<String, Object> map, String key, String defaultValue) {
if (key == "" || key == null) {
return defaultValue;
}
String result = (String) map.get(key);
if (result == null) {
return defaultValue;
} else {
return result;
}
}

public static int getIntFromMap(Map<String, Object> map, String key) {
if (key == "" || key == null) {
return 0;
}
if (map.get(key) == null) {
return 0;
}
return Integer.parseInt((String) map.get(key));
}

/**
* 打log接口
* @param log 要打印的log字符串
* @return 返回log
*/
public static String log(Object log){
logger.info(log.toString());
//System.out.println(log);
return log.toString();
}

/**
* 读取本地的xml数据,一般用来自测用
* @param localPath 本地xml文件路径
* @return 读到的xml字符串
*/
public static String getLocalXMLString(String localPath) throws IOException {
return Util.inputStreamToString(Util.class.getResourceAsStream(localPath));
}

}


[java] view plain copy print?package com.luozhuang.util; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.Node; import org.w3c.dom.NodeList; import org.xml.sax.SAXException; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; import java.io.IOException; import java.io.InputStream; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.SortedMap; import java.util.TreeMap; /** * luozhuang */ public class XMLParser { public static SortedMap<String,Object> getMapFromXML(String xmlString) throws ParserConfigurationException, IOException, SAXException { //这里用Dom的方式解析回包的最主要目的是防止API新增回包字段 DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); DocumentBuilder builder = factory.newDocumentBuilder(); InputStream is = Util.getStringStream(xmlString); Document document = builder.parse(is); //获取到document里面的全部结点 NodeList allNodes = document.getFirstChild().getChildNodes(); Node node; SortedMap<String, Object> map = new TreeMap<String, Object>(); int i=0; while (i < allNodes.getLength()) { node = allNodes.item(i); if(node instanceof Element){ map.put(node.getNodeName(),node.getTextContent()); } i++; } return map; } }
package com.luozhuang.util;

import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.SortedMap;
import java.util.TreeMap;

/**
* luozhuang
*/
public class XMLParser {

public static SortedMap<String,Object> getMapFromXML(String xmlString) throws ParserConfigurationException, IOException, SAXException {

//这里用Dom的方式解析回包的最主要目的是防止API新增回包字段
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = factory.newDocumentBuilder();
InputStream is =  Util.getStringStream(xmlString);
Document document = builder.parse(is);

//获取到document里面的全部结点
NodeList allNodes = document.getFirstChild().getChildNodes();
Node node;
SortedMap<String, Object> map = new TreeMap<String, Object>();
int i=0;
while (i < allNodes.getLength()) {
node = allNodes.item(i);
if(node instanceof Element){
map.put(node.getNodeName(),node.getTextContent());
}
i++;
}
return map;

}

}


内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: