</div>
<!--一个博主专栏付费入口-->
<!--一个博主专栏付费入口结束-->
<link rel="stylesheet" href="https://csdnimg.cn/release/phoenix/template/css/ck_htmledit_views-4a3473df85.css">
<link rel="stylesheet" href="https://csdnimg.cn/release/phoenix/template/css/ck_htmledit_views-4a3473df85.css">
<div class="htmledit_views" id="content_views">
<p> </p>
关键词:微信登录、统一下单(支付)、统一下单通知(回调)、统一下单查询、企业付款至零钱、支付查询、获取ACCESS_Token、获取小程序二维码
因为做项目涉及到微信这些接口的调用,尽管看了很多博客,比对了官方文档,仍还是踩了很多很多的坑,这里做一个记录及分享,提醒自己,帮助他人。文章如果有讲的不对得地方,欢迎指正。
首先根据官方文档分析流程,工具类见最后:
一、登录
官方时序图如下:
https://developers.weixin.qq.com/miniprogram/dev/framework/open-ability/login.html
图里其实说的很清楚了,清理下流程:
1.前端调用wx.login()获取code值
2.前端通过调用wx.getUserInfo获取iv、rawData、signature、encryptedData等加密数据,传递给后端
3.服务器通过code请求api换回session_key和openid
4.服务器通过前端给的rawData 加获取的session_key使用sha1加密,计算出signature1
5.比对前端传的signature和自己算出来的signature1是否一致(防止数据不一致)
6.用AES算法解密encryptedData里的敏感数据
7.拿着敏感数据后做自己的逻辑
8.通知前端登陆成功
** 这里如果你只是想拿到用户的openid,则直接1,3就可以做到了。如下:
-
public WeChatUserInfo loginSimple(String code) throws Exception {
-
-
String url =
new StringBuilder().append(WeChatAPIInfo.loginUrl)
-
.append(
"?appid="+ WechatInfo.appid)
-
.append(
"&secret="+WechatInfo.SECRET)
-
.append(
"&js_code="+code)
-
.append(
"&grant_type=authorization_code")
-
.toString();
-
-
String result = HttpClientHelper.doGet(url,
null);
-
if(result ==
null ) {
//请求失败
-
throw
new UnExceptedException(
"获取会话失败");
-
}
-
JSONObject jsonObj = JSON.parseObject(result);
-
String openId = jsonObj.getString(
"openid");
-
WeChatUserInfo weUser =
new WeChatUserInfo();
-
weUser.setOpenId(openId);
-
return weUser;
-
}
-
/**
-
* 登录并验证:验证数据完整性
-
* @param req
-
* @return
-
*/
-
public WeChatUserInfo loginAndSign(WeChatAppLoginReq req) throws Exception {
-
-
//获取 session_key 和 openId
-
-
String url =
new StringBuilder().append(WeChatAPIInfo.loginUrl)
-
.append(
"?appid="+ WechatInfo.appid)
-
.append(
"&secret="+WechatInfo.SECRET)
-
.append(
"&js_code="+req.getCode())
-
.append(
"&grant_type=authorization_code")
-
.toString();
-
-
String result = HttpClientHelper.doGet(url,
null);
-
if(result ==
null ) {
//请求失败
-
throw
new UnExceptedException(
"获取会话失败");
-
}
-
-
JSONObject jsonObj = JSON.parseObject(result);
-
String sessionKey = jsonObj.getString(
"session_key");
-
String str = req.getRawData()+sessionKey;
-
String signature = Algorithm.useSHA1(str);
//用SHA-1算法计算签名
-
-
if(!signature.equals(req.getSignature())){
-
logger.info(
" req signature="+req.getSignature()+
"\n\t\n"+
" java signature="+signature);
-
throw
new CheckSignatureFailException(
"签名无法解析,或被篡改,无法登录");
-
}
-
-
byte[] resultByte =
null;
-
try {
//解密敏感数据
-
resultByte = WeChatUtil.decrypt(Base64.decodeBase64(req.getEncryptedData()),
-
Base64.decodeBase64(sessionKey),
-
Base64.decodeBase64(req.getIv()));
-
}
catch (Exception e) {
-
throw
new DecryptFailedException(
"数据无法解析!");
-
}
-
-
if(
null != resultByte && resultByte.length >
0){
-
try {
-
String userInfoStr =
new String(resultByte,
"UTF-8");
-
WeChatUserInfo weUser = JSON.parseObject(userInfoStr,WeChatUserInfo.class);
-
return weUser;
-
}
catch (UnsupportedEncodingException e){
-
logger.error(
"对象转换错误",e);
-
}
-
}
-
return
null;
-
}
二、
⑴统一下单
官方api: https://pay.weixin.qq.com/wiki/doc/api/wxa/wxa_api.php?chapter=9_1
同样清理下流程:
1.计算signature:把这个文档中提及的必填参数及你需要的通过字典顺序连接(字典顺序就是你查字典时,单词的顺序),然后在最后拼接&key=商户key,然后用MD5计算signature,微信服务器会用这个signature及自己生成的作比对,防止数据被篡改。
2.把你请求的参数转换为xml格式:腾讯接口采用的xml通讯,这个是他要求的格式,没办法,老老实实转吧。内容就是你上面请求的参数+你刚生成的signature,signature腾讯服务器要用来比对的。
3.发起请求,获取prepay_id:这里状态真的炒鸡多的,不过你怎么处理就看自己需要了。
4.返回前段调用支付接口需要的参数,并根据这些参数生成一个新的signature,然后返回给前端,这里的signature是下一次请求腾讯服务器时的签名,和第一个一个作用,一次请求一个。
5.前端拿着返回的参数,发起wx.requestPayment(Object object)
6.微信服务器会进行回调,回调地址是前面请求参数的notify_url,用来通知你支付成功了,然后你可以做相应的逻辑操作,然后告诉微信服务器你知道了,不然他会通知很多次(9次)。
7.支付成功,前端收到通知,继续其他逻辑。
-
/**
-
* 统一下单
-
* @param orderParams
-
* @param resultParse
-
* @return
-
* @throws UnsupportedEncodingException
-
* @throws NoSuchAlgorithmException
-
*/
-
public FlyResponse pay(CreateOrderParams orderParams, WeChatResultParseAbstract resultParse)
-
throws UnsupportedEncodingException, NoSuchAlgorithmException {
-
-
//解析参数
-
String urlParam = WeChatUtil.concatOrderParams(orderParams);
//参数按字典顺序连接起来
-
String sign = WeChatUtil.getSign(urlParam);
//MD5加密形成签名sign,官方文档固定格式
-
orderParams.setSign(sign);
//将生成的签名放入
-
String xmlStr = WeChatUtil.transToXML(orderParams);
//转为xml
-
-
logger.info(
"\t\n微信下单参数转换为xml:\n"+xmlStr);
-
-
resultParse.setUrl(WeChatAPIInfo.Create_Order_Prefix_Url);
-
resultParse.setXmlStr(xmlStr);
-
resultParse.setApiDesc(
"<< 统一下单 >>");
-
-
return resultParse.ResultParse();
-
}
-
/**
-
* @ClassName: WeChatResultParseAbstract
-
* @Description: 结果解析抽象类
-
* @Version: 1.0
-
*/
-
public
abstract
class WeChatResultParseAbstract {
-
private
static Logger logger = LoggerFactory.getLogger(WeChatResultParseAbstract.class);
-
/**
-
* 调用api的描述,如:统一下单
-
*/
-
private String apiDesc;
-
/**
-
* 调用api的url
-
*/
-
private String url;
-
/**
-
* 调用APi需要的xml格式参数
-
*/
-
private String xmlStr;
-
-
public WeChatResultParseAbstract(String apiDesc, String url, String xmlStr) {
-
this.apiDesc = apiDesc;
-
this.url = url;
-
this.xmlStr = xmlStr;
-
}
-
-
public WeChatResultParseAbstract(String apiDesc, String xmlStr) {
-
this.apiDesc = apiDesc;
-
this.xmlStr = xmlStr;
-
}
-
-
public WeChatResultParseAbstract() {
-
}
-
-
public FlyResponse ResultParse(){
-
-
FlyResponse flyResponse =
null;
-
RestTemplate template =
new RestTemplate();
-
template.getMessageConverters().set(
1,
new StringHttpMessageConverter(StandardCharsets.UTF_8));
-
ResponseEntity<String> resp = template.postForEntity(url, xmlStr, String.class);
-
if(resp ==
null || resp.getStatusCode() != HttpStatus.OK) {
-
throw
new UnExceptedException(
"连接通信失败");
-
}
-
Map<String,String> map =
null;
-
try{
-
map = WeChatUtil.transXMLStrToMap(resp.getBody());
-
}
catch (ParserConfigurationException | IOException | SAXException e) {
-
logger.error(apiDesc+
"xml解析异常:"+e.getMessage()+
"\n");
-
return FlyResponse.Fail(
501,apiDesc+
"失败",
null,apiDesc+
"xml解析异常!");
-
}
-
-
if (
"SUCCESS".equals(map.get(
"return_code"))) {
-
if(
"SUCCESS".equals(map.get(
"result_code"))){
-
flyResponse = onSuccess(map);
-
}
else{
-
flyResponse = onFail(map);
-
}
-
-
}
else{
-
flyResponse = onLinkFail(map);
-
}
-
return flyResponse;
-
}
-
-
-
/**
-
* 响应成功,业务状态成功后要做的业务逻辑
-
* @param resultMap
-
* @return
-
*/
-
protected abstract FlyResponse onSuccess(Map<String,String> resultMap);
-
-
-
/**
-
* 业务失败,业务码失败后的逻辑
-
* @param resultMap
-
* @return
-
*/
-
protected abstract FlyResponse onFail(Map<String,String> resultMap);
-
-
-
/**
-
* 响应失败,业务码失败后的逻辑
-
* @param resultMap
-
* @return
-
*/
-
protected FlyResponse onLinkFail(Map<String,String> resultMap){
-
return FlyResponse.Fail(
505,
"通信失败",resultMap,
"通信失败");
-
}
⑵下单回调
官方api: https://pay.weixin.qq.com/wiki/doc/api/wxa/wxa_api.php?chapter=9_7&index=8
流程:
1.接收微信服务器发送的数据,xml格式
2.根据响应结果,完成自己的业务逻辑
3.通知微信服务器接收到通知
-
/**
-
* 支付回调
-
*
-
* @param request
-
* @param response
-
* @return
-
*/
-
public void payback(HttpServletRequest request, HttpServletResponse response) throws Exception {
-
logger.info(
"\n--------------<><><><>开始回调<><><>----------\n");
-
-
BufferedReader br =
new BufferedReader(
new InputStreamReader(request.getInputStream()));
-
String line =
null;
-
StringBuilder sb =
new StringBuilder();
-
while((line = br.readLine()) !=
null){
-
sb.append(line);
-
}
-
br.close();
-
String notityXml = sb.toString();
-
-
String resXml =
"";
-
Map<String,String> map = WeChatUtil.transXMLStrToMap(notityXml);
-
String returnCode = (String) map.get(
"return_code");
-
if(
"SUCCESS".equals(returnCode)){
-
SortedMap<String,String> payInfo =
new TreeMap<>(map);
-
String sign = WeChatUtil.getSignFromMap(payInfo);
-
-
if(sign.equals(map.get(
"sign"))){
//比对签名防止数据篡改
-
//这里写自己的逻辑
-
}
-
//通知微信服务器已经支付成功
-
resXml =
"<xml>" +
"<return_code><![CDATA[SUCCESS]]></return_code>"
-
+
"<return_msg><![CDATA[OK]]></return_msg>" +
"</xml> ";
-
logger.info(
"回调成功!!!!!!!");
-
}
else {
-
resXml =
"<xml>" +
"<return_code><![CDATA[FAIL]]></return_code>"
-
+
"<return_msg><![CDATA[签名不一致]]></return_msg>" +
"</xml> ";
-
logger.warn(
"\n\t微信支付回调失败!签名不一致\n\t");
-
}
-
}
else{
-
resXml =
"<xml>" +
"<return_code><![CDATA[FAIL]]></return_code>"
-
+
"<return_msg><![CDATA[报文为空]]></return_msg>" +
"</xml> ";
-
logger.warn(
"回调失败!!!!!!!");
-
}
-
-
BufferedOutputStream out =
new BufferedOutputStream(
-
response.getOutputStream());
-
out.write(resXml.getBytes());
-
out.flush();
-
out.close();
-
}
⑶下单查询
官方api: https://pay.weixin.qq.com/wiki/doc/api/wxa/wxa_api.php?chapter=9_2
流程:
1.根据请求参数生成signature
2.请求参数转为xml
3.发起请求,判断结果
-
/**
-
* 查询订单情况
-
* @param orderNo
-
* @return
-
*/
-
public FlyResponse checkOrder(String orderNo) throws UnsupportedEncodingException,NoSuchAlgorithmException {
-
-
CheckOrderVO check = WechatVOFactory.createCheck(orderNo);
-
String sign = WeChatUtil.getSign(WeChatUtil.concatOrderParams(check));
//参数按字典顺序连接起来
-
check.setSign(sign);
-
String xml = WeChatUtil.transToXML(check);
-
//调用统计下单接口验证
-
WeChatResultParseAbstract reParse =
new WeChatResultParseAbstract(
"查询订单",Order_check_Url,xml) {
-
@Override
-
protected FlyResponse onSuccess(Map<String, String> resultMap) {
-
if(
"SUCCESS".equalsIgnoreCase(resultMap.get(
"trade_state"))){
-
return FlyResponse.Success(
200,
"支付成功",resultMap);
-
}
else{
-
return FlyResponse.Fail(
501,
"支付失败",resultMap,
"支付失败");
-
}
-
}
-
@Override
-
protected FlyResponse onFail(Map<String, String> resultMap) {
-
return FlyResponse.Fail(
500,
"【查询订单】失败",resultMap,
"微信API调用Fail");
-
}
-
};
-
return reParse.ResultParse();
-
}
三、企业付款至零钱
官方api: https://pay.weixin.qq.com/wiki/doc/api/tools/mch_pay.php?chapter=14_2
企业付款有支付到零钱和银行卡,这里我使用的是支付到零钱。签名什么的大同小异,直接上代码。
-
/**
-
* 企业付款至用户零钱
-
* @param request
-
* @param fee
-
* @param openid
-
* @param payDesc
-
* @return
-
* @throws UnsupportedEncodingException
-
* @throws NoSuchAlgorithmException
-
*/
-
public synchronized FlyResponse CompanyPayToUser(HttpServletRequest request,String fee, String openid,String payDesc)
-
throws Exception {
-
-
FlyResponse flyResponse =
null;
-
final String orderno = createAnTransferOrderNum();
//创建一个唯一的订单号,这个看自己的情况实现
-
CompanyPayVO payVO = WechatVOFactory.createCompanyPayVO(request, orderno, openid, fee, payDesc);
-
String sign = WeChatUtil.getSign(WeChatUtil.concatOrderParams(payVO));
-
payVO.setSign(sign);
-
String xmlStr = WeChatUtil.transToXML(payVO);
-
-
String result = WeChatUtil.HttpsPost(WeChatAPIInfo.Company_Transfer_Url,xmlStr);
-
-
if(result !=
null && result.length()>
0 ){
-
Map<String, String> resultMap = WeChatUtil.transXMLStrToMap(result);
-
-
if (
"SUCCESS".equals(resultMap.get(
"return_code"))) {
-
if(
"SUCCESS".equals(resultMap.get(
"result_code"))){
-
flyResponse = FlyResponse.Success(
200,
"提现成功",resultMap);
-
}
else{
-
flyResponse = FlyResponse.Fail(
500,
"提现失败",resultMap,
"提现失败");
-
}
-
}
else{
-
if(TransgerErrorCode.isUnsureState(resultMap.get(
"err_code"))){
//不确定状态,再查一次
-
try {
-
flyResponse = checkTransfer(orderno);
-
}
catch (UnsupportedEncodingException | NoSuchAlgorithmException e) {
-
e.printStackTrace();
//已经解析过
-
}
-
}
else{
-
flyResponse = FlyResponse.Fail(
500,
"提现失败",resultMap,
"提现失败");
-
}
-
}
-
}
else{
-
flyResponse = FlyResponse.Fail(
505,
"通信失败",
null,
"通信失败");
-
}
-
-
if(flyResponse.getCode() ==
200){
//成功
-
//写自己要的逻辑
-
-
}
else
if(flyResponse.getCode() ==
201){
//处理中
-
//写自己要的逻辑
-
}
-
logger.info(
"返回结果:"+JSON.toJSON(flyResponse));
-
return flyResponse;
-
}
-
/**
-
* 查询企业付款情况
-
* @param orderNo
-
* @return
-
* @throws UnsupportedEncodingException
-
* @throws NoSuchAlgorithmException
-
*/
-
public synchronized FlyResponse checkTransfer(String orderNo) throws Exception {
-
FlyResponse flyResponse =
null;
-
TransferCheckVO check = WechatVOFactory.createTransferCheckVO(orderNo);
-
String sign = WeChatUtil.getSign(WeChatUtil.concatOrderParams(check));
-
check.setSign(sign);
-
String xmlStr = WeChatUtil.transToXML(check);
-
-
String result = WeChatUtil.HttpsPost(WeChatAPIInfo.Transfer_Check_Url,xmlStr);
-
-
if(result !=
null && result.length()>
0 ){
-
Map<String, String> resultMap = WeChatUtil.transXMLStrToMap(result);
-
-
if (
"SUCCESS".equals(resultMap.get(
"return_code"))) {
-
if(
"SUCCESS".equalsIgnoreCase(resultMap.get(
"status"))){
-
return FlyResponse.Success(
200,
"提现成功",resultMap);
-
}
-
if(
"PROCESSING".equalsIgnoreCase(resultMap.get(
"status"))){
//处理中
-
return FlyResponse.Success(
201,
"提现处理中",resultMap);
-
}
else{
-
return FlyResponse.Fail(
501,
"提现失败",resultMap,
"提现失败");
-
}
-
}
else{
-
return FlyResponse.Fail(
500,
"【提现查询】失败",resultMap,
"微信API调用Fail");
-
}
-
}
else{
-
flyResponse = FlyResponse.Fail(
505,
"提现查询通信失败",
null,
"通信失败");
-
}
-
-
if(flyResponse.getCode() ==
200){
-
//查询支付成功了,写自己逻辑
-
}
-
-
return flyResponse;
-
}
四、获取小程序二维码
小程序二维码获取之前得获取accesstoken,然后调用接口获取二维码,我是保存在项目本地,然后返回地址供请求访问,代码如下:
-
/**
-
* 获取小程序二维码
-
* @param qrCode
-
* @param imageName
-
* @return
-
* @throws Exception
-
*/
-
public String getQRCode(QRCodeVO qrCode,String imageName) throws Exception {
-
imageName +=
".jpg";
-
String url =
new StringBuilder().append(WeChatAPIInfo.QRcode)
-
.append(
"?access_token="+getAccessToken().getAccess_token())
-
.toString();
-
//获取web目录
-
String rootpath = ContextLoader.getCurrentWebApplicationContext().getServletContext().getRealPath(
"/");
-
Path path = Paths.get(rootpath,WechatInfo.QRImgRootAddress,imageName);
-
File file = path.toFile();
-
-
File result = HttpClientHelper.doPostToFileSSL_unSafe(url,JSON.toJSONString(qrCode),file);
-
-
return WechatInfo.SourceUrl +imageName;
-
}
-
/**
-
*获取全局Access_Token验证凭证
-
* @return
-
*/
-
public AccessResult getAccessToken() throws Exception {
-
String url =
new StringBuilder().append(WeChatAPIInfo.ACCESS_TOKEN)
-
.append(
"&appid="+ WechatInfo.appid)
-
.append(
"&secret="+WechatInfo.SECRET)
-
.toString();
-
String str = HttpClientHelper.doGet(url,
null);
-
-
if(str ==
null ) {
//请求失败
-
throw
new UnExceptedException(
"获取会话失败");
-
}
-
AccessResult result = JSON.parseObject(str,AccessResult.class);
-
if(result.getAccess_token() ==
null){
-
throw
new UnExceptedException(
"获取accessToken失败:"+result.getErrcode()+
";"+result.getErrmsg());
-
}
-
if(result.getErrcode()==
null){
//没有错误码,成功,替换新的accesstoken
-
//这里可以把accesstoken保存下来,官方说有2小时有效期,我是采用的redis,怎么实现看个人喜好了
-
}
-
return result;
-
}
-
/**
-
* AccessToken结果内部类
-
*/
-
public
static
class AccessResult{
-
private String access_token;
-
private String expires_in;
-
private String errcode;
-
private String errmsg;
-
//get、set
-
、、、、
-
}
嗯。。。基本是这样,接下来是自建的工具类:
*ps: 因为参考过很多人的代码,也修改过很多次,但是 有些地方并没有去调整。比如http相关的工具类的东西,可能有些乱。不过因为可以用,而且很懒,所以就那样吧先。。。。
-
import com.thoughtworks.xstream.XStream;
-
import com.thoughtworks.xstream.XStreamException;
-
import org.apache.http.HttpEntity;
-
import org.apache.http.client.methods.CloseableHttpResponse;
-
import org.apache.http.client.methods.HttpPost;
-
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
-
import org.apache.http.entity.StringEntity;
-
import org.apache.http.impl.client.CloseableHttpClient;
-
import org.apache.http.impl.client.HttpClients;
-
import org.apache.http.ssl.SSLContexts;
-
import org.apache.http.util.EntityUtils;
-
import org.apache.log4j.Logger;
-
import org.bouncycastle.jce.provider.BouncyCastleProvider;
-
import org.springframework.util.StringUtils;
-
import org.w3c.dom.Node;
-
import org.w3c.dom.NodeList;
-
import org.xml.sax.SAXException;
-
-
import javax.crypto.BadPaddingException;
-
import javax.crypto.Cipher;
-
import javax.crypto.IllegalBlockSizeException;
-
import javax.crypto.NoSuchPaddingException;
-
import javax.crypto.spec.IvParameterSpec;
-
import javax.crypto.spec.SecretKeySpec;
-
import javax.net.ssl.SSLContext;
-
import javax.servlet.http.HttpServletRequest;
-
import javax.xml.parsers.DocumentBuilder;
-
import javax.xml.parsers.DocumentBuilderFactory;
-
import javax.xml.parsers.ParserConfigurationException;
-
import java.io.*;
-
import java.lang.reflect.Field;
-
import java.lang.reflect.InvocationTargetException;
-
import java.lang.reflect.Method;
-
import java.security.*;
-
import java.util.*;
-
-
-
-
/**
-
* @ClassName: WeChatUtil
-
* @Description: 微信工具类
-
* @Author: totors
-
* @Version: 1.0
-
*/
-
public
class WeChatUtil {
-
-
private
static Logger logger = Logger.getLogger(WeChatUtil.class);
-
public
static
boolean initialized =
false;
-
-
-
-
/**
-
* 获取一个32位的随机字符串
-
*
-
* @return
-
*/
-
public static String getOneRandomString() {
-
String base =
"abcdefghijklmnopqrstuvwxyz0123456789";
-
Random random =
new Random();
-
StringBuilder sb =
new StringBuilder();
-
for (
int i =
0; i <
31; i++) {
-
int number = random.nextInt(
31);
-
sb.append(base.charAt(number));
-
}
-
return sb.toString();
-
}
-
-
-
/**
-
* 获取真实的ip地址
-
*
-
* @param request
-
* @return
-
*/
-
public static String getIpAddr(HttpServletRequest request) {
-
Enumeration<String> headers = request.getHeaderNames();
-
String ip = request.getHeader(
"X-Forwarded-For");
-
if (!StringUtils.isEmpty(ip) && !
"unKnown".equalsIgnoreCase(ip)) {
-
int index = ip.indexOf(
",");
-
if (index != -
1) {
-
return ip.substring(
0, index);
-
}
else {
-
return ip;
-
}
-
}
-
ip = request.getHeader(
"X-Real-IP");
-
if (!StringUtils.isEmpty(ip) && !
"unKnown".equalsIgnoreCase(ip)) {
-
return ip;
-
}
-
return request.getRemoteAddr();
-
}
-
-
-
/**
-
* 连接订单参数,空则忽略,连接符&
-
* 使用详解:符合条件的参数按字段名称由小到大(字典顺序)排序,并连接
-
*
-
* @param createOrderParams
-
* @return
-
*/
-
public static String concatOrderParams(Object createOrderParams) throws UnExceptedException {
-
-
TreeMap<String, String> tree =
new TreeMap<>();
//用于排序
-
Class clazz = createOrderParams.getClass();
-
Field[] fields = clazz.getDeclaredFields();
-
-
//查找字段
-
for (Field field : fields) {
-
field.setAccessible(
true);
-
String fieldName = field.getName();
-
String methodName = getFiledMethodName(fieldName);
-
try {
-
Method method = clazz.getMethod(methodName);
-
Object value = method.invoke(createOrderParams);
-
if (value !=
null) {
//不为空
-
tree.put(fieldName, value.toString());
-
}
-
}
catch (NoSuchMethodException e) {
-
logger.error(e.getMessage());
-
}
catch (IllegalAccessException e) {
-
logger.error(e.getMessage());
-
}
catch (InvocationTargetException e) {
-
logger.error(e.getMessage());
-
}
-
}
-
if (tree.size() ==
0) {
-
throw
new UnExceptedException(
"No field can be linked ! ");
-
}
-
String str = linkMapKeyValue(tree,
"&");
-
-
return str.substring(
1);
//截取第一个&符号之后的内容
-
}
-
-
-
/**
-
* 从map创建签名
-
* @param parameters
-
* @return
-
*/
-
public static String getSignFromMap(SortedMap<String, String> parameters){
-
StringBuffer sb =
new StringBuffer();
-
Set es = parameters.entrySet();
-
Iterator it = es.iterator();
-
while (it.hasNext())
-
{
-
Map.Entry entry = (Map.Entry) it.next();
-
String k = (String) entry.getKey();
-
Object v = entry.getValue();
-
if (
null != v && !
"".equals(v) && !
"sign".equals(k) && !
"key".equals(k)){
-
sb.append(k +
"=" + v +
"&");
-
}
-
}
-
sb.append(
"key=" + WechatInfo.key);
-
logger.info(
"\t\n由MAP生产的字符串:"+sb.toString());
-
String sign =
null;
-
try {
-
sign = Algorithm.MD5(sb.toString()).toUpperCase();
-
}
catch (NoSuchAlgorithmException | UnsupportedEncodingException e) {
-
logger.error(
"MD5加密失败:"+e.getClass()+
">>>>"+e.getMessage());
-
}
-
return sign;
-
}
-
-
-
-
-
/**
-
* 连接字符串:
-
* * 将map值连接为key = value & key = value……形式
-
* @param map
-
* @param character 连接符号,如:&
-
* @return
-
*/
-
public static String linkMapKeyValue(TreeMap<String, String> map, String character) {
-
if (map ==
null || map.size() ==
0) {
-
return
null;
-
}
-
StringBuilder sb =
new StringBuilder();
-
Set<String> keys = map.keySet();
-
Iterator<String> it = keys.iterator();
-
while (it.hasNext()) {
-
String key = it.next();
-
sb.append(character + key +
"=" + map.get(key));
-
}
-
return sb.toString();
-
}
-
-
-
-
/**
-
* 获取字段方法名称
-
*
-
* @param fieldName
-
* @return
-
*/
-
public static String getFiledMethodName(String fieldName) {
-
char firstChar = fieldName.toCharArray()[
0];
-
return
"get" + String.valueOf(firstChar).toUpperCase() + fieldName.substring(
1, fieldName.length());
-
}
-
-
-
-
-
-
-
/**
-
* 将对象非空参数转化为XML
-
*
-
* @param obj
-
* @return
-
*/
-
public static String transToXML(Object obj) {
-
//解决XStream对出现双下划线的bug
-
// XStream xstream = new XStream(new DomDriver("UTF-8", new XmlFriendlyNameCoder("-_", "_")));
-
XStream xstream = XStreamFactory.getXStream();
-
xstream.alias(
"xml", obj.getClass());
-
return xstream.toXML(obj);
-
}
-
-
-
-
-
-
/**
-
* 将字符串格式的xml内容转化为对象
-
* 注意:该方法存在一个不可避免风险,即:当微信官方文档反馈字段增加或改变时,该方法将不能映射进pojo里。
-
* 因为本地pojo(OrderResult)可能没有做对应调整。
-
* @param str
-
* @return
-
*/
-
public static OrderResult transToObject(String str) throws XStreamException {
-
str = str.replaceAll(
"xml",
"OrderResult");
//将返回结果的<xml>标签替换为返回结果类
-
XStream xstream =
new XStream();
-
xstream.alias(
"OrderResult", OrderResult.class);
-
OrderResult orderResult =
new OrderResult();
-
return (OrderResult) xstream.fromXML(str,orderResult);
-
}
-
-
-
-
-
/**
-
* 将xml字符串解析为map集合,兼容性高
-
* @param xmlStr
-
* @return
-
* @throws ParserConfigurationException
-
*/
-
public static Map<String,String> transXMLStrToMap(String xmlStr) throws ParserConfigurationException,
-
SAXException, IOException {
-
-
Map<String, String> data =
new HashMap<String, String>();
-
DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
-
DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder();
-
-
try(InputStream stream =
new ByteArrayInputStream(xmlStr.getBytes(
"UTF-8"));) {
-
org.w3c.dom.Document doc = documentBuilder.parse(stream);
-
doc.getDocumentElement().normalize();
-
NodeList nodeList = doc.getDocumentElement().getChildNodes();
-
for (
int idx =
0; idx < nodeList.getLength(); ++idx) {
-
Node node = nodeList.item(idx);
-
if (node.getNodeType() == Node.ELEMENT_NODE) {
-
org.w3c.dom.Element element = (org.w3c.dom.Element) node;
-
data.put(element.getNodeName(), element.getTextContent());
-
}
-
}
-
}
catch (UnsupportedEncodingException e) {
-
logger.error(
"解析xml结果失败!字符编码不匹配!");
-
throw e;
-
}
catch (IOException e) {
-
logger.error(
"解析xml结果失败!无法读入流");
-
throw e;
-
}
-
return data;
-
}
-
-
-
-
-
/**
-
* 获取签名
-
* @param signStr
-
* @return
-
*/
-
public static String getSign(String signStr) throws UnsupportedEncodingException, NoSuchAlgorithmException {
-
return Algorithm.MD5(signStr +
"&key="+ WechatInfo.key).toUpperCase();
-
}
-
-
-
-
/**
-
* 敏感数据对称解密
-
* @param content ——被加密的数据
-
* @param keyByte ——加密密匙
-
* @param ivByte ——偏移量
-
* @return
-
* @throws InvalidAlgorithmParameterException
-
*/
-
public
static
byte[] decrypt(
byte[] content,
byte[] keyByte,
byte[] ivByte)
throws InvalidAlgorithmParameterException {
-
initialize();
-
try {
-
Cipher cipher = Cipher.getInstance(
"AES/CBC/PKCS7Padding");
-
Key sKeySpec =
new SecretKeySpec(keyByte,
"AES");
-
-
cipher.init(Cipher.DECRYPT_MODE, sKeySpec, generateIV(ivByte));
// 初始化
-
byte[] result = cipher.doFinal(content);
-
return result;
-
}
-
return
null;
-
}
-
-
/**
-
* 添加算法
-
*/
-
public static void initialize(){
-
if (initialized)
return;
-
Security.addProvider(
new BouncyCastleProvider());
-
initialized =
true;
-
}
-
-
-
/**
-
* @Description 生成iv
-
* @Param [iv]
-
* @return java.security.AlgorithmParameters
-
**/
-
public static AlgorithmParameters generateIV(byte[] iv) throws Exception{
-
AlgorithmParameters params = AlgorithmParameters.getInstance(
"AES");
-
params.init(
new IvParameterSpec(iv));
-
return params;
-
}
-
-
-
public static String HttpsPost(String url,String xmlStr) throws Exception {
-
-
KeyStore keyStore = KeyStore.getInstance(
"PKCS12");
-
try (FileInputStream instream =
new FileInputStream(
new File(WechatInfo.CERTIFICATE_ADDRESS));){
-
keyStore.load(instream, WechatInfo.mch_id.toCharArray());
-
}
-
// Trust own CA and all self-signed certs
-
SSLContext sslcontext = SSLContexts.custom().loadKeyMaterial(keyStore, WechatInfo.mch_id.toCharArray()).build();
-
// Allow TLSv1 protocol only
-
SSLConnectionSocketFactory sslsf =
new SSLConnectionSocketFactory(sslcontext);
-
// SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(sslcontext, new String[]{"TLSv1"}, null, SSLConnectionSocketFactory.BROWSER_COMPATIBLE_HOSTNAME_VERIFIER);
-
CloseableHttpClient httpclient = HttpClients.custom().setSSLSocketFactory(sslsf).build();
-
HttpPost httpPost =
new HttpPost(url);
-
httpPost.addHeader(
"Connection",
"keep-alive");
-
httpPost.addHeader(
"Accept",
"*/*");
-
httpPost.addHeader(
"Content-Type",
"application/x-www-form-urlencoded; charset=UTF-8");
-
httpPost.addHeader(
"Host",
"api.mch.weixin.qq.com");
-
httpPost.addHeader(
"X-Requested-With",
"XMLHttpRequest");
-
httpPost.addHeader(
"Cache-Control",
"max-age=0");
-
// httpPost.addHeader("User-Agent", "Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.0) ");
-
httpPost.setEntity(
new StringEntity(xmlStr,
"UTF-8"));
-
-
logger.info(
"执行请求" + httpPost.getRequestLine());
-
-
CloseableHttpResponse response = httpclient.execute(httpPost);
-
StringBuffer sbf =
new StringBuffer();
-
try {
-
HttpEntity entity = response.getEntity();
-
if (entity !=
null) {
-
BufferedReader bufferedReader =
new BufferedReader(
new InputStreamReader(entity.getContent(),
"UTF-8"));
-
String text;
-
while ((text = bufferedReader.readLine()) !=
null) {
-
sbf.append(text);
-
}
-
}
-
EntityUtils.consume(entity);
-
}
finally {
-
response.close();
-
httpclient.close();
-
}
-
return sbf.toString();
-
}
-
}
算法工具类:
-
public
class Algorithm {
-
-
-
private
static
final
char[] HEX_DIGITS = {
'0',
'1',
'2',
'3',
'4',
'5',
-
'6',
'7',
'8',
'9',
'a',
'b',
'c',
'd',
'e',
'f'};
-
-
-
public static String useSHA1(String str) {
-
if (str ==
null) {
-
return
null;
-
}
-
try {
-
MessageDigest messageDigest = MessageDigest.getInstance(
"SHA1");
-
messageDigest.update(str.getBytes(
"utf-8"));
-
String result = getFormattedText(messageDigest.digest());
-
return result;
-
}
catch (Exception e) {
-
throw
new RuntimeException(e);
-
}
-
}
-
-
private static String getFormattedText(byte[] bytes) {
-
int len = bytes.length;
-
StringBuilder buf =
new StringBuilder(len *
2);
-
// 把密文转换成十六进制的字符串形式
-
for (
int j =
0; j < len; j++) {
-
buf.append(HEX_DIGITS[(bytes[j] >>
4) &
0x0f]);
-
buf.append(HEX_DIGITS[bytes[j] &
0x0f]);
-
}
-
return buf.toString();
-
}
-
-
/**
-
* MD5加密
-
* @param text
-
* @return
-
* @throws NoSuchAlgorithmException
-
* @throws UnsupportedEncodingException
-
*/
-
public static String MD5(String text) throws NoSuchAlgorithmException, UnsupportedEncodingException {
-
return MD5.MD5Encode(text);
-
}
-
}
-
public
class MD5 {
-
private
final
static String[] hexDigits = {
"0",
"1",
"2",
"3",
"4",
"5",
"6",
"7",
-
"8",
"9",
"a",
"b",
"c",
"d",
"e",
"f"};
-
-
/**
-
* 转换字节数组为16进制字串
-
* @param b 字节数组
-
* @return 16进制字串
-
*/
-
public static String byteArrayToHexString(byte[] b) {
-
StringBuilder resultSb =
new StringBuilder();
-
for (
byte aB : b) {
-
resultSb.append(byteToHexString(aB));
-
}
-
return resultSb.toString();
-
}
-
-
/**
-
* 转换byte到16进制
-
* @param b 要转换的byte
-
* @return 16进制格式
-
*/
-
private static String byteToHexString(byte b) {
-
int n = b;
-
if (n <
0) {
-
n =
256 + n;
-
}
-
int d1 = n /
16;
-
int d2 = n %
16;
-
return hexDigits[d1] + hexDigits[d2];
-
}
-
-
/**
-
* MD5编码
-
* @param origin 原始字符串
-
* @return 经过MD5加密之后的结果
-
*/
-
public static String MD5Encode(String origin) {
-
String resultString =
null;
-
try {
-
resultString = origin;
-
MessageDigest md = MessageDigest.getInstance(
"MD5");
-
resultString = byteArrayToHexString(md.digest(resultString.getBytes(
"UTF-8")));
-
}
catch (Exception e) {
-
e.printStackTrace();
-
}
-
return resultString;
-
}
-
}
微信小程序及商户的配置信息,你也可以写在配置文件来引用:
-
public
interface WeChatAPIInfo {
-
/**
-
* 获取access_token
-
*/
-
String ACCESS_TOKEN =
"https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential";
-
-
/**
-
* 登录地址
-
*/
-
String loginUrl =
"https://api.weixin.qq.com/sns/jscode2session";
-
-
/**
-
* 统一下单url
-
*/
-
String Create_Order_Prefix_Url =
"https://api.mch.weixin.qq.com/pay/unifiedorder";
-
-
/**
-
* 订单情况查询url
-
*/
-
String Order_check_Url =
"https://api.mch.weixin.qq.com/pay/orderquery";
-
-
/**
-
* 企业付款到零钱
-
*/
-
String Company_Transfer_Url =
"https://api.mch.weixin.qq.com/mmpaymkttransfers/promotion/transfers";
-
-
/**
-
* 企业付款查询url
-
*/
-
String Transfer_Check_Url =
"https://api.mch.weixin.qq.com/mmpaymkttransfers/gettransferinfo";
-
-
/**
-
* 二维码url
-
*/
-
String QRcode =
"https://api.weixin.qq.com/wxa/getwxacodeunlimit";
-
-
-
String SendTemplateMsg_Url =
"https://api.weixin.qq.com/cgi-bin/message/wxopen/template/send";
-
-
-
-
}
-
public
interface WechatInfo {
-
-
/**
-
* 小程序appid
-
*/
-
String appid =
"";
-
/**
-
* 商户号的Appid
-
*/
-
String mch_appid =
"";
-
/**
-
*商户号
-
*/
-
String mch_id =
"";
-
/**
-
*回调地址
-
*/
-
String notify_url =
"";
-
/**
-
*交易类型
-
*/
-
String trade_type =
"JSAPI";
-
/**
-
* 签名类型
-
*/
-
String sign_type =
"MD5";
-
/**
-
* 商户密匙
-
*/
-
String key =
"";
-
/**
-
* 小程序ApiSecret
-
*/
-
String SECRET =
"";
-
/**
-
* 证书地址
-
*/
-
String CERTIFICATE_ADDRESS =
"";
-
/**
-
* 二维码图片地址
-
*/
-
String QRImgRootAddress =
"";
-
-
/**
-
* 静态资源
-
*/
-
String SourceUrl =
"";
-
}
HTTP方法封装:
-
import org.apache.http.HttpEntity;
-
import org.apache.http.HttpStatus;
-
import org.apache.http.NameValuePair;
-
import org.apache.http.client.config.RequestConfig;
-
import org.apache.http.client.entity.UrlEncodedFormEntity;
-
import org.apache.http.client.methods.CloseableHttpResponse;
-
import org.apache.http.client.methods.HttpGet;
-
import org.apache.http.client.methods.HttpPost;
-
import org.apache.http.client.utils.URIUtils;
-
import org.apache.http.client.utils.URLEncodedUtils;
-
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
-
import org.apache.http.conn.ssl.TrustStrategy;
-
import org.apache.http.entity.ContentType;
-
import org.apache.http.entity.StringEntity;
-
import org.apache.http.entity.mime.MultipartEntityBuilder;
-
import org.apache.http.entity.mime.content.FileBody;
-
import org.apache.http.entity.mime.content.StringBody;
-
import org.apache.http.impl.client.CloseableHttpClient;
-
import org.apache.http.impl.client.HttpClients;
-