JAVA开发(后端):微信小程序API调用详细分析及步骤

时间:2024-04-09 18:52:15

 

关键词:微信登录、统一下单(支付)、统一下单通知(回调)、统一下单查询、企业付款至零钱、支付查询、获取ACCESS_Token、获取小程序二维码

 

因为做项目涉及到微信这些接口的调用,尽管看了很多博客,比对了官方文档,仍还是踩了很多很多的坑,这里做一个记录及分享,提醒自己,帮助他人。文章如果有讲的不对得地方,欢迎指正。

 

首先根据官方文档分析流程,工具类见最后:

一、登录

官方时序图如下:

https://developers.weixin.qq.com/miniprogram/dev/framework/open-ability/login.html

JAVA开发(后端):微信小程序API调用详细分析及步骤

图里其实说的很清楚了,清理下流程:

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

JAVA开发(后端):微信小程序API调用详细分析及步骤

 

同样清理下流程:

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;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.ssl.SSLContextBuilder;
import org.apache.http.ssl.SSLContexts;
import org.apache.http.util.EntityUtils;
import org.apache.log4j.Logger;

import javax.net.ssl.SSLContext;
import java.io.*;
import java.net.URI;
import java.nio.charset.Charset;
import java.security.GeneralSecurityException;
import java.security.KeyStore;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;


public class HttpClientHelper {

    private static Logger logger = Logger.getLogger(HttpClientHelper.class);
    private static PoolingHttpClientConnectionManager connMgr;
    private static RequestConfig requestConfig;
    private static final int MAX_TIMEOUT = 7000;

    static {
        // 设置连接池
        connMgr = new PoolingHttpClientConnectionManager();
        // 设置连接池大小
        connMgr.setMaxTotal(100);
        connMgr.setDefaultMaxPerRoute(connMgr.getMaxTotal());

        RequestConfig.Builder configBuilder = RequestConfig.custom();
        // 设置连接超时
        configBuilder.setConnectTimeout(MAX_TIMEOUT);
        // 设置读取超时
        configBuilder.setSocketTimeout(MAX_TIMEOUT);
        // 设置从连接池获取连接实例的超时
        configBuilder.setConnectionRequestTimeout(MAX_TIMEOUT);
        // 在提交请求之前 测试连接是否可用
        configBuilder.setStaleConnectionCheckEnabled(true);
        requestConfig = configBuilder.build();
    }



    /**
     * 发送GET请求(HTTP),K-V形式
     * @param url
     * @author Charlie.chen;
     * @return
     */
    public static String doGet(String url, Map<String, Object> params) throws Exception {


        URI uri = null;
        // 创建默认的HttpClient实例.
        try(CloseableHttpClient httpclient = HttpClients.createDefault();) {
            if(params != null){
                List<NameValuePair> nameParams = paramsToNameValuePair(params);
                String queryString = URLEncodedUtils.format(nameParams, "utf-8");
                uri = URIUtils.resolve(new URI(url),queryString);
            }else{
                uri = new URI(url);
            }
            // 定义一个get请求方法
            HttpGet httpget = new HttpGet(uri);

            // 执行get请求,返回response服务器响应对象, 其中包含了状态信息和服务器返回的数据
            CloseableHttpResponse httpResponse = httpclient.execute(httpget);

            // 使用响应对象, 获得状态码, 处理内容
            int statusCode = httpResponse.getStatusLine().getStatusCode();
            logger.info("Send a http get request and the response code is :"+statusCode);
            if (statusCode == HttpStatus.SC_OK) {
                // 使用响应对象获取响应实体
                HttpEntity entity = httpResponse.getEntity();
                // 将响应实体转为字符串
                String response = EntityUtils.toString(entity, "utf-8");
                return response;
            }
        } catch (Exception e) {
            logger.info("Send a http get request occurred exception",e);
            throw e;
        }
        return null;
    }

    /**
     * 发送POST请求(HTTP),K-V形式
     * @param url
     * @param params
     * @author Charlie.chen
     * @return  响应结果
     */
    public static String doPost(String url, Map<String,Object> params) throws IOException {

        try(CloseableHttpClient httpclient = HttpClients.createDefault();){// 创建默认的HttpClient实例.

            // 定义一个get请求方法
            HttpPost httppost = new HttpPost(url);
            httppost.setHeader("Content-type","application/json,charset=utf-8");
            httppost.setHeader("Accept", "application/json");

            if (params!=null) {
                List<NameValuePair> list = paramsToNameValuePair(params);
                httppost.setEntity(new UrlEncodedFormEntity(list, "utf-8"));
            }

            // 执行post请求,返回response服务器响应对象, 其中包含了状态信息和服务器返回的数据
            CloseableHttpResponse httpResponse = httpclient.execute(httppost);

            // 使用响应对象, 获得状态码, 处理内容
            int statusCode = httpResponse.getStatusLine().getStatusCode();
            if (statusCode == HttpStatus.SC_OK) {
                // 使用响应对象获取响应实体
                HttpEntity entity = httpResponse.getEntity();
                // 将响应实体转为字符串
                String response = EntityUtils.toString(entity, "utf-8");
                return response;
            }

        } catch (Exception e) {
            logger.info("Send a http get request occurred exception",e);
            throw e;
        }
        return null;
    }


    /**
     * 转换参数
     * @param params
     * @return
     */
    private static List<NameValuePair> paramsToNameValuePair(Map<String,Object> params){
        List<NameValuePair> pairList = new ArrayList<>(params.size());
        for (Map.Entry<String, Object> entry : params.entrySet()) {
            NameValuePair pair = new BasicNameValuePair(entry.getKey(), entry.getValue()==null ? null : entry.getValue().toString());
            pairList.add(pair);
        }
        return pairList;
    }


    /**
     * 发送 不安全的SSL POST请求(信任所有证书)
     * (HTTPS),K-V形式
     * @param url
     * @param params
     * @author Charlie.chen
     */
    public static String doPostSSL_unSafe(String url, Map<String, Object> params,boolean isJson) {
        CloseableHttpClient httpClient = HttpClients.custom()
                .setSSLSocketFactory(createUnsafeSSLConn())
                .setConnectionManager(connMgr)
                .setDefaultRequestConfig(requestConfig)
                .build();
        HttpPost httpPost = new HttpPost(url);
        if(isJson){
            httpPost.addHeader("Content-type","application/json;charset=UTF-8");
            httpPost.addHeader("Accept","application/json");
            httpPost.addHeader("Cache-Control: no-cache","Pragma: no-cache");
        }

        String httpStr = null;

        try {
            httpPost.setConfig(requestConfig);
            if(isJson){
                String paramsStr = JSON.toJSONString(params);
                StringEntity entity = new StringEntity(paramsStr,ContentType.APPLICATION_JSON);
                httpPost.setEntity(entity);
            }
            if(!isJson && params!=null&&params.size()>0){
                List<NameValuePair> pairList = paramsToNameValuePair(params);
                httpPost.setEntity(new UrlEncodedFormEntity(pairList, Charset.forName("utf-8")));
            }

            response = httpClient.execute(httpPost);
            int statusCode = response.getStatusLine().getStatusCode();
            logger.info("Send a PostSSL request and the response code is :"+statusCode);
            if (statusCode != HttpStatus.SC_OK) {
                return null;
            }
            HttpEntity entity = response.getEntity();
            if (entity == null) {
                return null;
            }
            httpStr = EntityUtils.toString(entity, "utf-8");
        } catch (Exception e) {
            logger.error("Http post unexpected exception",e);
        } finally {
            if (response != null) {
                try {
                    EntityUtils.consume(response.getEntity());
                } catch (IOException e) {
                    logger.error("Http client close response exception",e);
                }
            }
        }
        return httpStr;
    }



    public static File doPostToFileSSL_unSafe(String url, String paramJson,File file) throws IOException {
        CloseableHttpClient httpClient = HttpClients.custom()
                .setSSLSocketFactory(createUnsafeSSLConn())
                .setConnectionManager(connMgr)
                .setDefaultRequestConfig(requestConfig)
                .build();
        HttpPost httpPost = new HttpPost(url);
        httpPost.addHeader("Content-type","application/json;charset=UTF-8");
        httpPost.addHeader("Accept","application/json");
        httpPost.addHeader("Cache-Control: no-cache","Pragma: no-cache");

        CloseableHttpResponse response = null;
        String httpStr = null;

        try {
            httpPost.setConfig(requestConfig);
            httpPost.setEntity(entityParam);

            response = httpClient.execute(httpPost);
            int statusCode = response.getStatusLine().getStatusCode();
            logger.info("Send a PostSSL request and the response code is :"+statusCode);
            if (statusCode == HttpStatus.SC_OK) {
                // 使用响应对象获取响应实体
                HttpEntity entity = response.getEntity();
                entity.writeTo(new FileOutputStream(file));
                return file;
            }
        } catch (Exception e) {
            logger.error("Http post unexpected exception",e);
            throw e;
        } finally {
            if (response != null) {
                try {
                    EntityUtils.consume(response.getEntity());
                } catch (IOException e) {
                    logger.error("Http client close response exception",e);
                }
            }
        }
        return null;
    }



    /**
     * 发送安全的SSL POST请求
     *      * (HTTPS),K-V形式
     * @param url
     * @param params
     * @param certificateAddress    证书地址
     * @param certificatePassword   证书密码
     * @return
     */
    public static String doPostSSL_Safe(String url, Map<String, Object> params,String certificateAddress,
                                        String certificatePassword) throws Exception {

        CloseableHttpClient httpClient = HttpClients.custom()
                .setSSLSocketFactory(createSafeSSLConn(certificateAddress,certificatePassword))
                .setConnectionManager(connMgr)
                .setDefaultRequestConfig(requestConfig)
                .build();
        HttpPost httpPost = new HttpPost(url);
        CloseableHttpResponse response = null;
        String httpStr = null;

        try {
            httpPost.setConfig(requestConfig);
            List<NameValuePair> pairList = paramsToNameValuePair(params);
            httpPost.setEntity(new UrlEncodedFormEntity(pairList, Charset.forName("utf-8")));
            response = httpClient.execute(httpPost);
            int statusCode = response.getStatusLine().getStatusCode();
            logger.info("Send a PostSSL request and the response code is :"+statusCode);
            if (statusCode != HttpStatus.SC_OK) {
                return null;
            }
            HttpEntity entity = response.getEntity();
            if (entity == null) {
                return null;
            }
            httpStr = EntityUtils.toString(entity, "utf-8");
        } catch (Exception e) {
            logger.error("Http post unexpected exception",e);
        } finally {
            if (response != null) {
                try {
                    EntityUtils.consume(response.getEntity());
                } catch (IOException e) {
                    logger.error("Http client close response exception",e);
                }
            }
        }
    }



    /**
     * 创建不安全的SSL连接
     * @author Charlie.chen
     * @return
     */
    private static SSLConnectionSocketFactory createUnsafeSSLConn() {
        SSLConnectionSocketFactory sslsf = null;
        try {
            SSLContext sslContext = new SSLContextBuilder().loadTrustMaterial(null, new TrustStrategy() {
                public boolean isTrusted(X509Certificate[] chain, String authType) throws CertificateException {
                    return true;
                }
            }).build();

            sslsf = new SSLConnectionSocketFactory(sslContext);
        } catch (GeneralSecurityException e) {
            e.printStackTrace();
        }
        return sslsf;
    }

    /**
     * 建立给定证书的安全连接
     * @param certificateAddress   证书地址
     * @param password  证书密码
     * @return
     */

        //读取证书
        KeyStore keyStore = KeyStore.getInstance("PKCS12");//PKCS12,java中的公匙加密标准
        try (FileInputStream instream = new FileInputStream(new File(certificateAddress));){
            keyStore.load(instream, password.toCharArray());//加载给定的证书
//            keyStore.load(null,null); //若没有可用的证书的写法
        }

        //建立证书连接工厂
        SSLConnectionSocketFactory sslsf = null;
        try {
            SSLContext sslContext = SSLContexts.custom().loadKeyMaterial(keyStore,password.toCharArray()).build();
            sslsf = new SSLConnectionSocketFactory(sslContext);
        } catch (GeneralSecurityException e) {
            e.printStackTrace();
        }
        return sslsf;
    }
}

 

水平有限,如果有问题,欢迎指正,一起交流学习成长。转载请注明。