吐槽下:

经常有人问我,你不是在某软件公司么?我听说大公司都是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 自己根据需要选择替换。

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();// keyString 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";// 获取发起电脑 ipString 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.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());//终端ipinputObj.SetValue("appid", Configure.getAppid());//公众账号IDinputObj.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通信接口以提交数据到APIWxPayData 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());//公众账号IDinputObj.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通信接口以提交数据到APIWxPayData 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());//公众账号IDinputObj.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通信接口以提交数据到APIWxPayData 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());//公众账号IDinputObj.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通信接口以提交数据到APIWxPayData 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());//公众账号IDinputObj.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通信接口以提交数据到APIWxPayData 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());//公众账号IDinputObj.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通信接口以提交数据到APIWxPayData result = new WxPayData();//若接口调用失败会返回xml格式的结果if (response.startsWith("<xml>")){result.FromXml(response);}//接口调用成功则返回非xml格式的数据elseresult.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());//公众账号IDinputObj.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通信接口以提交数据到APIWxPayData 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未设置,则使用配置文件中的urlif (!inputObj.IsSet("notify_url")){inputObj.SetValue("notify_url", Configure.NOTIFY_URL);//异步通知url}inputObj.SetValue("appid", Configure.getAppid());//公众账号IDinputObj.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通信接口以提交数据到APIWxPayData 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());//公众账号IDinputObj.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通信接口以提交数据到APIWxPayData 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());//公众账号IDinputObj.SetValue("mch_id",Configure.getMchid());//商户号inputObj.SetValue("user_ip",Configure.getIP());//终端ipinputObj.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通信接口以提交数据到APIWxPayData 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.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;elsereturn 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 KEYstr += "&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;public class WxPayException extends Exception {public WxPayException(String string) {super(string);}}

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;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 certsSSLContext sslcontext = SSLContexts.custom().loadKeyMaterial(keyStore, Configure.getCertPassword().toCharArray()).build();// Allow TLSv1 protocol onlySSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(sslcontext, new String[] { "TLSv1" }, null,SSLConnectionSocketFactory.BROWSER_COMPATIBLE_HOSTNAME_VERIFIER);httpClient = HttpClients.custom().setSSLSocketFactory(sslsf).build();// 根据默认超时限制初始化requestConfigrequestConfig = 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 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.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.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 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 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 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;}}
												

java集成微信支付接口(微信V3版)相关推荐

  1. java集成支付宝支付接口

    吐槽一下:支付宝的接口文档真心太难看懂了,乱七八糟,都不知道去哪里找自己要的东西,还要有技术客服,感谢技术客服(┬_┬) 支付宝技术客服https://support.open.alipay.com/ ...

  2. php官方微信接口大全(微信支付、微信红包、微信摇一摇、微信小店)

    微信入口绑定,微信事件处理,微信API全部操作包含在这些文件中. 内容有:微信摇一摇接口/微信多客服接口/微信支付接口/微信红包接口/微信卡券接口/微信小店接口/JSAPI <?php clas ...

  3. 【Day40】php官方微信接口大全(微信支付、微信红包、微信摇一摇、微信小店)

    微信入口绑定,微信事件处理,微信API全部操作包含在这些文件中. 内容有:微信摇一摇接口/微信多客服接口/微信支付接口/微信红包接口/微信卡券接口/微信小店接口/JSAPI <?phpclass ...

  4. java后台提供ios微信支付接口

    提供微信支付接口,需要先在微信开放平台(https://open.weixin.qq.com/)申请账号,进行开发者资格认证通过后创建应用. 创建成功后会有对应应用的AppID和AppSecret这个 ...

  5. java支付接口开发原理_java对接微信支付接口开发的过程是什么?

    java对接微信支付接口开发的过程是什么?以下是小编整理的java对接微信支付接口实现的方法和过程,有需要的朋友可以学习以下的java对接微信支付接口内容. java对接微信支付接口开发的过程是什么? ...

  6. 后端---最全最简单细致的Java接入微信支付接口(Native接入)

    首先,我简单总结一下这几天搞微信支付和支付宝支付接口的体会,通过这几天的动手实操,加强了自己对SSM框架的认识,以及对业务逻辑的思路,明白了许多以前不懂的许多空白. 做支付宝接口的接入完全可以按照支付 ...

  7. Java 微信支付接口(统一下单,异步回调,订单退款,取消订单)

    一.准备工作 APP绑定微信商户平台获取商户id(mchID).证书(商户后台下载).支付签名密钥(商户后台设置api密钥).退款签名密钥(商户后台设置api密钥ipv3)等 1.导入微信支付SDK ...

  8. 微信小程序-JAVA实现微信支付功能(微信支付2.0)

    微信小程序-JAVA实现微信支付功能(微信支付2.0) 一.前言 本博客主要介绍JAVA后台与微信小程序(UNI-APP或者原生微信小程序)的微信支付的实现,如果是APP或者H5的开发暂时不支持,具体 ...

  9. python 微信支付接口 详解_Python支付接口汇总大全(包含微信、支付宝等,长期更新、欢迎补充)...

    wzhifuSDK- 由微信支付SDK 官方PHP Demo移植而来,v3.37下载地址 学习Python中有不明白推荐加入交流群 号:864573496 群里有志同道合的小伙伴,互帮互助, 群里有不 ...

最新文章

  1. 史上最全Android开发中100%会用到的开源框架整理(1/5)
  2. 4.2小时完成数万年计算,比超级计算机快亿亿亿倍!量子计算机“祖冲之号”“九章”再升级...
  3. python urllib.request 爬虫 数据处理-python之爬虫(三) Urllib库的基本使用
  4. Anaconda装OpenCV
  5. 【SQL Server】阿里云CentOS安装SQL Server教程 含破解3.5G内存的限制、win端远程连接
  6. # 睡眠3秒_【for fun】睡眠排序算法
  7. jenkins重启 linux_在Linux中,Jenkins无法启动
  8. java 打包 根目录_java – Maven在jar文件的根目录中打包图像
  9. linux--vi的使用
  10. poj 3468 A Simple Problem with Integers 线段树!!!!
  11. java url链接超时_Java HttpURLConnection超时和IO异常处理
  12. win10为单个网卡配置多个IP地址
  13. 【软技能】完全写作指南--个人博客
  14. 永恒之蓝漏洞紧急应对方案
  15. Excel文本自动换行
  16. 手机查看python代码_30个极简Python代码,拿走即用
  17. 信息安全系统设计基础期中总结
  18. 短距离无线通讯-蓝牙
  19. 辉芒微MCU全系列供应,FT60,61,62
  20. [网鼎杯 2020 玄武组]SSRFMe【redis主从复制ssrf】

热门文章

  1. php将数组转换为JSON中文字符串(兼容中文)
  2. 使用Packstack一键安装openstack-stein
  3. 国内著名大学课件大全]线点播
  4. 网站防火墙,您的请求带有不合法参数,已被网站管理设置拦截
  5. 全国计算机等级考试报名指南~~
  6. HTML---图像标签
  7. VO高级使用技巧之一:可重写的重要的方法
  8. 计算机技术现状及发展,分析计算机技术的应用现状及发展前景
  9. 海铁联运码头的优化挑战和文献综述
  10. 晶体的切割方式对于晶体频率稳定度的影响