关键词:微信登录、统一下单(支付)、统一下单通知(回调)、统一下单查询、企业付款至零钱、支付查询、获取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 和 openIdString 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);//转为xmllogger.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) {@Overrideprotected FlyResponse onSuccess(Map<String, String> resultMap) {if("SUCCESS".equalsIgnoreCase(resultMap.get("trade_state"))){return FlyResponse.Success(200,"支付成功",resultMap);}else{return FlyResponse.Fail(501,"支付失败",resultMap,"支付失败");}}@Overrideprotected 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 certsSSLContext sslcontext = SSLContexts.custom().loadKeyMaterial(keyStore, WechatInfo.mch_id.toCharArray()).build();// Allow TLSv1 protocol onlySSLConnectionSocketFactory 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;}
}

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

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

  1. 微信小程序——api调用(糗事百科例子)

    糗事百科api分析 糗事百科 http://m2.qiushibaike.com/article/list/{type}?type=refresh&page={page}&count= ...

  2. 前后端微信小程序开发

    随着移动互联网的迅猛发展,微信小程序已经成为了一种非常流行的开发方式,它不仅可以节约用户空间,提高用户体验,而且开发成本也相对较低.因此,前后端微信小程序开发技术也日渐成熟. 本篇文章将介绍前后端微信 ...

  3. 小程序服务器七牛云,基于七牛云 API 开发的微信小程序 SDK

    概述 Qiniu-wxapp-SDK 是七牛云在小程序上的实现,网络功能依赖于微信小程序 API.您可以基于 SDK 方便的在小程序中上传文件至七牛云. Qiniu-wxapp-SDK  为客户端 S ...

  4. java后端微信小程序登录与注册

    java后端微信小程序登录与注册&微信登录授权 分析: 微信小程序用户表 的字段来源于微信服务器 , 必须想办法去获取到对应的用户信息 找到微信开放平台: 微信开放平台 以下是微信开放平台给出 ...

  5. 微信小程序api接口调用用封装

    微信小程序api接口调用用封装 1. 在 02-项目 下新建request目录及index.js文件 1.1 02-项目\request\index.js 1.2.index.js // 同时发送异步 ...

  6. 微信小程序api调起微信提供的功能-网络、媒体、文件、数据存储、位置、设备、界面、开发接口

    微信小程序-API 框架提供丰富的微信原生API,可以方便的调起微信提供的能力,如获取用户信息,本地存储,支付功能等. 说明: wx.on 开头的 API 是监听某个事件发生的API接口,接受一个 C ...

  7. 基于云开发的微信小程序:个人相册ByUestcXiye

    基于云开发的微信小程序:个人相册ByUestcXiye 作业要求 第一次作业 第二次作业 结课作业 小程序开发入门 开发前的准备 注册微信小程序 新建一个模板小程序 开通云开发服务 第一次作业 参考资 ...

  8. 真的简单,单手用Spring Boot 开发一个微信小程序

    前言   嗨,大家好,现在微信使用的用户很多,作为开发人员也可以建立一个自己的微信小程序,本期与大家分享一下作者建立微信小程序的开发流程. 申请   百度搜索微信公众号平台,然后扫码登录注册一个微信公 ...

  9. 云开发(微信-小程序)笔记(五)----云函数,就这(上)

    云开发(微信-小程序)笔记(四)---- 还有吗?再来点 云函数 云函数即在云端(服务器端)运行的函数.在物理设计上,一个云函数可由多个文件组成,占用一定量的 CPU 内存等计算资源:各云函数完全独立 ...

最新文章

  1. 经典!工业界深度推荐系统与CTR预估必读的论文汇总
  2. shiro受权时没有走对应的realm的授权解决方案
  3. pytorch指定用多张显卡训练_Pytorch多GPU训练
  4. Lua初学习 9-12 基础
  5. SQL --几张表公有字段的查询
  6. Linux系统磁盘阵列创建教程----------(better late than never. 只要开始,虽晚不迟。)...
  7. Python实训第一天--基础知识
  8. C语言入门基础知识有哪些?
  9. Spring AOP之静态代理
  10. redhat7 安装telnet服务
  11. 办公软件 -- Office 365免费下载安装
  12. Monaco Editor教程(十七):代码信息指示器CodeLens配置详解
  13. 【资料分享】正念书单:7大经典著作,有效释放压力、缓解焦虑、治疗抑郁!
  14. 王道408思维导图 marginnote 【4门科齐全】Xmind+脑图原件可下载 - 在线分享
  15. CSP 201609-3 炉石传说
  16. 智慧零售产业应用实战,30分钟上手的高精度商品识别
  17. 求负片灰度扩展幂律变换
  18. 一度智信:新开的电商店铺销量低?如何快速提升
  19. Facebook 的开源静态分析工具Infer
  20. AVFrame相关api内存管理

热门文章

  1. 【MATLAB】绘制阶梯图、枝干图
  2. 松耦合(Loose coupling)笔记
  3. x86: perf_events内核初始化
  4. ESP8266固件烧录教程
  5. JS 数组删除 splice和delete
  6. 如何使用Microsoft PowerPoint制作海报
  7. 背完这10个经典面试答案,80%的面试都能喜提offer
  8. 命名空间“Microsoft.Office”中不存在类型或命名空间名称“Interop”(是缺少程序集引用吗?)...
  9. 用byte数组表示RGB颜色
  10. 应用程序正常初始化(0xc000012d)失败的问题