WechatClient.java 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296
  1. package com.nuliji.tools.third.wechat;
  2. import com.alibaba.fastjson.JSONObject;
  3. import com.nuliji.tools.Tools;
  4. import com.nuliji.tools.third.OauthData;
  5. import org.apache.commons.lang.StringUtils;
  6. import org.slf4j.Logger;
  7. import org.slf4j.LoggerFactory;
  8. import org.springframework.http.HttpEntity;
  9. import org.springframework.http.HttpHeaders;
  10. import org.springframework.http.MediaType;
  11. import org.springframework.stereotype.Component;
  12. import org.springframework.stereotype.Service;
  13. import org.springframework.web.client.RestTemplate;
  14. import java.net.URLEncoder;
  15. import java.sql.Time;
  16. import java.util.*;
  17. public class WechatClient {
  18. private static final Logger logger = LoggerFactory.getLogger(WechatClient.class);
  19. private String appId = "";
  20. private String appSecret = "";
  21. public WechatClient(String appId, String appSecret){
  22. this.appId = appId;
  23. this.appSecret = appSecret;
  24. }
  25. private static Map<String, String> tokenMap = new HashMap<>();
  26. private static Map<String, Date> tokenExpiresIn = new HashMap<>();
  27. private static Map<String, String> jsTicketMap = new HashMap<>();
  28. private static Map<String, Date> jsTicketExpiresIn = new HashMap<>();
  29. /**
  30. * 从微信服务器获取临时访问二维码
  31. *
  32. */
  33. public String getQrcode(int sceneId) {
  34. String accessToken = getAccessToken(appId, appSecret);
  35. String url = "https://api.weixin.qq.com/cgi-bin/qrcode/create?access_token=" + accessToken;
  36. Map<String, Object> body = new HashMap<>();
  37. body.put("expire_seconds", 3600);
  38. body.put("action_name", "QR_SCENE");
  39. Map<String, Object> actionInfo = new HashMap<>();
  40. Map<String, Object> scene = new HashMap<>();
  41. scene.put("scene_id", sceneId);
  42. actionInfo.put("scene", scene);
  43. body.put("action_info", actionInfo);
  44. JSONObject object = execute(url, null, body);
  45. return "https://mp.weixin.qq.com/cgi-bin/showqrcode?ticket=" + object.getString("ticket");
  46. }
  47. public String getOAuthUrl(String redirectUrl ,String state){
  48. return "https://open.weixin.qq.com/connect/qrconnect?appid="+appId+"&redirect_uri="+redirectUrl+"&response_type=code&scope=snsapi_login&state="+state+"#wechat_redirect";
  49. }
  50. /**
  51. * 取微信公众平台accesstoken
  52. *
  53. * @return
  54. */
  55. private static synchronized String getAccessToken(String appid, String secret) {
  56. Date date = new Date();
  57. // 系统刚启动时,或者离上次取accessToken有7180秒以上时,重新取它
  58. if (!tokenMap.containsKey(appid) || date.getTime() / 1000 - tokenExpiresIn.get(appid).getTime() / 1000 >= 7180) {
  59. Map<String, String> params = new HashMap<>();
  60. params.put("grant_type", "client_credential");
  61. params.put("appid", appid);
  62. params.put("secret", secret);
  63. JSONObject object = execute("https://api.weixin.qq.com/cgi-bin/token", params, null);
  64. tokenMap.put(appid, object.getString("access_token"));
  65. tokenExpiresIn.put(appid, date);
  66. logger.info("getAccessToken:" + tokenMap.get(appid));
  67. }
  68. return tokenMap.get(appid);
  69. }
  70. /**
  71. * 取微信公众平台js_ticket
  72. *
  73. * @return
  74. */
  75. private static synchronized String getJSTicket(String appid, String secret) {
  76. String accessToken = getAccessToken(appid, secret);
  77. Date date = new Date();
  78. // 系统刚启动时,或者离上次取accessToken有7180秒以上时,重新取它
  79. if (!jsTicketMap.containsKey(accessToken) || date.getTime() / 1000 - jsTicketExpiresIn.get(accessToken).getTime() / 1000 >= 7180) {
  80. Map<String, String> params = new HashMap<>();
  81. params.put("access_token", accessToken);
  82. params.put("type", "jsapi");
  83. JSONObject object = execute("https://api.weixin.qq.com/cgi-bin/ticket/getticket", params, null);
  84. jsTicketMap.put(accessToken, object.getString("ticket"));
  85. jsTicketExpiresIn.put(accessToken, date);
  86. logger.info("getJSTicket:" + jsTicketMap.get(accessToken));
  87. }
  88. return jsTicketMap.get(accessToken);
  89. }
  90. private JSONObject getWebAccessToken(String code) {
  91. Map<String, String> params = new HashMap<>();
  92. params.put("appid", appId);
  93. params.put("secret", appSecret);
  94. params.put("code", code);
  95. params.put("grant_type", "authorization_code");
  96. JSONObject object = execute("https://api.weixin.qq.com/sns/oauth2/access_token", params, null);
  97. return object;
  98. }
  99. public OauthData webAuthorize(String code) {
  100. JSONObject result = getWebAccessToken(code);
  101. // 从result中解析出openid和session
  102. if (result.get("errcode") != null) {
  103. logger.warn(String.format("jscodeToSession failed: result = %s", result));
  104. throw new RuntimeException(String.format("从微信服务器换取用户登录态信息: 错误码: %d, 错误信息: %s", result.getIntValue("errcode"), result.getString("errmsg")));
  105. }
  106. JSONObject info = getWebUserInfo(result.getString("access_token"), result.getString("openid"));
  107. OauthData data = new OauthData();
  108. data.setRefreshToken(result.getString("refresh_token"));
  109. data.setAccessToken(result.getString("access_token"));
  110. data.setExpiresTime(new Date(new Date().getTime() + result.getInteger("expires_in")));
  111. data.setAvatar(info.getString("avatar"));
  112. data.setNickName(info.getString("nickname"));
  113. data.setUnionId(info.getString("unionid"));
  114. data.setOpenId(info.getString("openid"));
  115. data.setGender(info.getIntValue("sex") == 1 ? "mela":"femela");
  116. return data;
  117. }
  118. public JSONObject getWebUserInfo(String accessToken, String openid) {
  119. Map<String, String> params = new HashMap<>();
  120. params.put("access_token", accessToken);
  121. params.put("openid", openid);
  122. params.put("lang", "zh_CN");
  123. JSONObject object = execute("https://api.weixin.qq.com/sns/userinfo", params, null);
  124. return object;
  125. }
  126. public OauthData refreshWebAccessToken(String refreshToken){
  127. Map<String, String> params = new HashMap<>();
  128. params.put("appid", appId);
  129. params.put("refresh_token", refreshToken);
  130. params.put("grant_type", "refresh_token");
  131. JSONObject result = execute("https://api.weixin.qq.com/sns/oauth2/refresh_token", params, null);
  132. OauthData data = new OauthData();
  133. data.setRefreshToken(result.getString("refresh_token"));
  134. data.setAccessToken(result.getString("access_token"));
  135. data.setExpiresTime(new Date(new Date().getTime() + result.getInteger("expires_in")));
  136. return data;
  137. }
  138. public String jsTicket(){
  139. return getJSTicket(appId, appSecret);
  140. }
  141. /**
  142. * 处理接收到的微信消息
  143. *
  144. * @param request
  145. * @param listener
  146. * @return
  147. */
  148. public void ReceiveMessage(String request, MessageListener listener) {
  149. logger.info("receive -------------------------------------- = " + request);
  150. TreeMap<String, String> map = Tools.parseXml(request);
  151. if (map == null) {
  152. logger.warn("processReceiveWeixinMessage(): request is null");
  153. return ;
  154. }
  155. String openId = map.get("FromUserName");
  156. if (openId == null) {
  157. logger.error("processReceiveWeixinMessage(): FromUserName(openid) error: " + request);
  158. return;
  159. }
  160. // 已关注
  161. // 未关注进行关注
  162. if ("event".equals(map.get("MsgType")) && "SCAN".equals(map.get("Event"))) {
  163. // 后台根据场景值A,查询到对应的用户ID
  164. int sceneId = Integer.valueOf(map.get("EventKey"));
  165. listener.OnScan(openId, sceneId);
  166. } else if ("event".equals(map.get("MsgType")) && "subscribe".equals(map.get("Event"))) {
  167. // 关注事件-普通关注事件
  168. if (StringUtils.isBlank(map.get("EventKey"))) {
  169. listener.OnSubscribe(openId);
  170. }
  171. else if (map.get("EventKey").startsWith("qrscene_")) // 关注事件-扫描带参数二维码事件(内部帐号绑定微信号)
  172. {
  173. // 后台根据场景值A,查询到对应的用户ID
  174. int sceneId = Integer.valueOf(map.get("EventKey").substring("qrscene_".length()));
  175. listener.OnSubscribe(openId);
  176. listener.OnScan(openId, sceneId);
  177. }
  178. }
  179. }
  180. /**
  181. * 公众号发送微信消息给指定商家
  182. *
  183. * @param openid
  184. * @param templateId
  185. * @param dataUrl
  186. * @param dataMap
  187. * @return 返回数据,正常返回的例子: {"errcode":0,"errmsg":"ok","msgid":200228332}
  188. */
  189. public JSONObject sendMessage(String openid, String templateId, String dataUrl, Map<String, String> dataMap) {
  190. String accessToken = getAccessToken(appId, appSecret);
  191. String url = "https://api.weixin.qq.com/cgi-bin/message/template/send";
  192. Map<String, String> params = new HashMap<>();
  193. params.put("access_token", accessToken);
  194. Map<String, Object> body = new HashMap<>();
  195. body.put("touser", openid);
  196. body.put("template_id", templateId);
  197. if (dataUrl != null){
  198. body.put("url", dataUrl);
  199. }
  200. body.put("data", dataMap);
  201. JSONObject object = execute(url, params, body);
  202. return object;
  203. }
  204. public JSONObject sendCustomMessage(String openid, String content) {
  205. String accessToken = getAccessToken(appId, appSecret);
  206. String url = "https://api.weixin.qq.com/cgi-bin/message/custom/send";
  207. Map<String, String> params = new HashMap<>();
  208. params.put("access_token", accessToken);
  209. HttpHeaders headers = new HttpHeaders();
  210. Map<String, Object> body = new HashMap<>();
  211. body.put("touser", openid);
  212. body.put("msgtype", "text");
  213. Map<String, Object> text = new HashMap<>();
  214. text.put("content", content);
  215. body.put("text", text);
  216. body.put("data", null);
  217. JSONObject object = execute(url, params, body);
  218. return object;
  219. }
  220. private static JSONObject execute(String api, Map<String, String> urlParams, Map<String,Object> bodyParams) throws RuntimeException {
  221. try {
  222. String paramStr = "";
  223. if (urlParams != null){
  224. List<String> keys = new ArrayList<>(urlParams.keySet());
  225. Collections.sort(keys);
  226. for (int i = 0; i < keys.size(); i++) {
  227. String key = keys.get(i);
  228. String value = urlParams.get(key);
  229. value = URLEncoder.encode(value, "UTF-8");
  230. if (i == keys.size() - 1) {//拼接时,不包括最后一个&字符
  231. paramStr = paramStr + key + "=" + value;
  232. } else {
  233. paramStr = paramStr + key + "=" + value + "&";
  234. }
  235. }
  236. }
  237. HttpHeaders headers = new HttpHeaders();
  238. MediaType type = MediaType.APPLICATION_JSON;
  239. headers.setContentType(type);
  240. RestTemplate restTemplate = new RestTemplate();
  241. String result ="";
  242. if (bodyParams == null){
  243. result = restTemplate.getForObject(api+"?"+paramStr, String.class);
  244. } else {
  245. HttpEntity<String> formEntity = new HttpEntity<>(JSONObject.toJSONString(bodyParams), headers);
  246. result = restTemplate.postForObject(api+ "?" + paramStr, formEntity, String.class);
  247. }
  248. logger.info("微信返回结果:" + result);
  249. JSONObject a = JSONObject.parseObject(result);
  250. if(a.getInteger("errcode") !=null && a.getInteger("errcode") > 0){
  251. throw new Exception("接口错误"+a.getString("errmsg"));
  252. }
  253. return a;
  254. } catch (Exception e) {
  255. e.printStackTrace();
  256. throw new RuntimeException(e);
  257. }
  258. }
  259. }