You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

428 lines
20 KiB

  1. package com.iformall.controller;
  2. import com.google.gson.Gson;
  3. import com.google.gson.GsonBuilder;
  4. import com.iformall.common.Result;
  5. import com.iformall.common.ResultData;
  6. import com.iformall.domain.po.*;
  7. import com.iformall.enums.*;
  8. import com.iformall.mp.manager.WxMpManager;
  9. import com.iformall.mp.utils.MpUtil;
  10. import com.iformall.service.*;
  11. import com.iformall.service.wechat.WxOpenService;
  12. import io.swagger.annotations.Api;
  13. import io.swagger.annotations.ApiOperation;
  14. import me.chanjar.weixin.common.error.WxErrorException;
  15. import me.chanjar.weixin.mp.api.WxMpService;
  16. import me.chanjar.weixin.mp.bean.message.WxMpXmlMessage;
  17. import me.chanjar.weixin.mp.bean.message.WxMpXmlOutMessage;
  18. import me.chanjar.weixin.open.bean.message.WxOpenXmlMessage;
  19. import me.chanjar.weixin.open.bean.result.WxOpenAuthorizerInfoResult;
  20. import me.chanjar.weixin.open.bean.result.WxOpenPlatformResult;
  21. import me.chanjar.weixin.open.bean.result.WxOpenQueryAuthResult;
  22. import me.chanjar.weixin.open.bean.result.WxOpenResult;
  23. import org.apache.commons.lang3.StringUtils;
  24. import org.slf4j.Logger;
  25. import org.slf4j.LoggerFactory;
  26. import org.springframework.beans.factory.annotation.Autowired;
  27. import org.springframework.stereotype.Controller;
  28. import org.springframework.web.bind.annotation.*;
  29. import javax.servlet.http.HttpServletRequest;
  30. import java.io.ByteArrayOutputStream;
  31. import java.io.IOException;
  32. import java.io.InputStream;
  33. import java.nio.charset.Charset;
  34. import java.text.SimpleDateFormat;
  35. import java.util.Date;
  36. @Controller
  37. @RequestMapping("/wxOpen")
  38. @Api(description = "微信第三方开放平台回调相关接口")
  39. public class WechatCalllbackController extends BaseController {
  40. private final Logger logger = LoggerFactory.getLogger(this.getClass());
  41. @Autowired
  42. protected WxOpenService openService;
  43. @Autowired
  44. private WxComponentVerifyTicketService componentVerifyTicketService;
  45. @Autowired
  46. private WxAuthorizerInfoService authorizerInfoService;
  47. @Autowired
  48. private WxWeappAuditStatusService weappAuditStatusService;
  49. @Autowired
  50. private WxAppinfoService appinfoService;
  51. @Autowired
  52. private MailService mailService;
  53. @Autowired
  54. private WxMpManager mpManager;
  55. @RequestMapping(value = "/notify", method = RequestMethod.POST)
  56. @ApiOperation("接收ticket回调")
  57. public Object receiveTicket(HttpServletRequest request) throws IOException {
  58. logger.info("[" + getIpAddr() + "]接收微信回调 /wxOpen/notify");
  59. InputStream inStream = request.getInputStream();
  60. ByteArrayOutputStream outSteam = new ByteArrayOutputStream();
  61. byte[] buffer = new byte[1024];
  62. int len = 0;
  63. while ((len = inStream.read(buffer)) != -1) {
  64. outSteam.write(buffer, 0, len);
  65. }
  66. String resultxml = new String(outSteam.toByteArray(), Charset.forName("UTF-8"));
  67. logger.info(resultxml);
  68. outSteam.close();
  69. inStream.close();
  70. String timestamp = request.getParameter("timestamp");
  71. String nonce = request.getParameter("nonce");
  72. String signature = request.getParameter("signature");
  73. String encType = request.getParameter("encrypt_type");
  74. String msgSignature = request.getParameter("msg_signature");
  75. /*
  76. // 测试数据
  77. String timestamp = "1545708083";
  78. String nonce = "1052028699";
  79. String signature = "b5fa8296b46c726cbed35f744944f20f682776a6";
  80. String encType = "aes";
  81. String msgSignature= "54bec4c513f447cb312fd091bea48326f87aedde";
  82. String resultxml = "<xml>\n" +
  83. " <AppId><![CDATA[wx897e4673286c915d]]></AppId>\n" +
  84. " <Encrypt><![CDATA[92gl+PCgxooLh85YZ74VX8gNPV3tq2w8br4tuBl2UIc9uJuJiSNBgohFxgS7tRXSMUtr5t+75MiLKlaMKvv4FH5iF3WdYbxjF6ojnA1+MBzD7mf9fo6KkWUdDq+1V0OtrooP4s8iuAC0Lx3elCtWUZXYGnCeP5Y/2A8ZZBwpvcBz73d7n2Kv8QVvLsw2dmrej1b8KGdmRAEbm/oYoVthlAkmoTTAAx+Gj10XTMem1wp1vT/a0mGxN88oJxNZoHdFvAbmc24A7VD4i5VDOt9FqwerqlGXnBX5pBAmOx8ZUsndM8yuf3I54GE9hEPRvdWBiyLb96Lb8ZnjEBlvc9kvs7PpFOSQhkbGMNQKWGW1os8UImr7O3+IiKLuBv2G1pJBb2/kIeZn5v9NuxYLOsXQmb2XijHYMXwCNHCI3Eji22k9XDiiuK1miHlUuL8Gz8X7lN9JuVcE24jd7Ty4H0pThQ==]]></Encrypt>\n" +
  85. "</xml>";
  86. */
  87. logger.info("\n接收微信回调:[signature=[{}], encType=[{}], msgSignature=[{}],"
  88. + " timestamp=[{}], nonce=[{}], requestBody=[\n{}\n] ",
  89. signature, encType, msgSignature, timestamp, nonce, resultxml);
  90. if (!StringUtils.equalsIgnoreCase("aes", encType)
  91. || !openService.getWxOpenComponentService().checkSignature(timestamp, nonce, signature)) {
  92. throw new IllegalArgumentException("非法请求,可能属于伪造的请求!");
  93. }
  94. // aes加密的消息
  95. WxOpenXmlMessage inMessage = WxOpenXmlMessage.fromEncryptedXml(resultxml,
  96. openService.getWxOpenConfigStorage(), timestamp, nonce, msgSignature);
  97. this.logger.info("接收微信回调,消息解密后内容为:{} ", inMessage.toString());
  98. if (inMessage.getInfoType().equals(EnumWxAuthorizationInfoType.COMPONENT_VERIFY_TICKET.getMessage())) {
  99. // 保存component_verify_ticket
  100. componentVerifyTicketService.saveOrUpdate(new WxComponentVerifyTicket() {
  101. {
  102. setComponentVerifyTicket(inMessage.getComponentVerifyTicket());
  103. setCreateTime(new Date(Long.valueOf(inMessage.getCreateTime() * 1000)));
  104. setComponentAppid(inMessage.getAppId());
  105. setDeadline(new Date(Long.valueOf(System.currentTimeMillis() + (60 * 60 * 1000))));
  106. }
  107. });
  108. } else if (inMessage.getInfoType().equals(EnumWxAuthorizationInfoType.AUTHORIZED.getMessage()) ||
  109. inMessage.getInfoType().equals(EnumWxAuthorizationInfoType.UPDATEAUTHORIZED.getMessage())
  110. ) {
  111. try {
  112. WxOpenQueryAuthResult queryAuthResult = openService.getWxOpenComponentService().getQueryAuth(inMessage.getAuthorizationCode());
  113. logger.info("getQueryAuth", queryAuthResult);
  114. WxOpenAuthorizerInfoResult openAuthorizerInfoResult = openService.getWxOpenComponentService().getAuthorizerInfo(
  115. queryAuthResult.getAuthorizationInfo().getAuthorizerAppid());
  116. logger.info(openAuthorizerInfoResult.toString());
  117. // save auth info
  118. authorizerInfoService.saveOrUpdate(new WxAuthorizerInfo() {
  119. {
  120. setAuthorizerAppid(openAuthorizerInfoResult.getAuthorizationInfo().getAuthorizerAppid());
  121. setAlias(openAuthorizerInfoResult.getAuthorizerInfo().getAlias());
  122. setHeadImg(openAuthorizerInfoResult.getAuthorizerInfo().getHeadImg());
  123. setQrcodeUrl(openAuthorizerInfoResult.getAuthorizerInfo().getQrcodeUrl());
  124. setAuthorizationStatus(EnumWxAuthorizationStatus.AUTHORIZED.getCode());
  125. setAuthTime(new Date());
  126. }
  127. });
  128. } catch (WxErrorException e) {
  129. logger.error(e.getMessage());
  130. }
  131. } else if (inMessage.getInfoType().equals(EnumWxAuthorizationInfoType.UNAUTHORIZED.getMessage())) {
  132. authorizerInfoService.saveOrUpdate(new WxAuthorizerInfo() {
  133. {
  134. setAuthorizerAppid(inMessage.getAuthorizerAppid());
  135. setAuthorizationStatus(EnumWxAuthorizationStatus.UNAUTHORIZED.getCode());
  136. setAuthTime(new Date());
  137. }
  138. });
  139. }
  140. try {
  141. String out = openService.getWxOpenComponentService().route(inMessage);
  142. this.logger.info("组装回复信息:{}", out);
  143. } catch (WxErrorException e) {
  144. this.logger.error("notify", e);
  145. }
  146. return "success";
  147. }
  148. @RequestMapping(value = "/{appId}/callback")
  149. @ApiOperation("消息回调")
  150. @ResponseBody
  151. public String callback(@PathVariable("appId") String appId, HttpServletRequest request) throws IOException {
  152. logger.info("[" + getIpAddr() + "]接收微信APP回调 /{appId}/callback");
  153. String timestamp = request.getParameter("timestamp");
  154. String nonce = request.getParameter("nonce");
  155. String signature = request.getParameter("signature");
  156. String encType = request.getParameter("encrypt_type");
  157. String openid = request.getParameter("openid");
  158. String msgSignature = request.getParameter("msg_signature");
  159. InputStream inStream = request.getInputStream();
  160. ByteArrayOutputStream outSteam = new ByteArrayOutputStream();
  161. byte[] buffer = new byte[1024];
  162. int len = 0;
  163. while ((len = inStream.read(buffer)) != -1) {
  164. outSteam.write(buffer, 0, len);
  165. }
  166. String resultxml = new String(outSteam.toByteArray(), Charset.forName("UTF-8"));
  167. logger.info(resultxml);
  168. outSteam.close();
  169. inStream.close();
  170. logger.info("接收微信APP回调:[appId=[{}], openid=[{}], signature=[{}], encType=[{}], msgSignature=[{}],"
  171. + " timestamp=[{}], nonce=[{}], requestBody=[\n{}\n] ",
  172. appId, openid, signature, encType, msgSignature, timestamp, nonce, resultxml);
  173. // 第三方平台一定接收的是加密的消息
  174. if (!StringUtils.equalsIgnoreCase("aes", encType)
  175. || !openService.getWxOpenComponentService().checkSignature(timestamp, nonce, signature)) {
  176. throw new IllegalArgumentException("非法请求,可能属于伪造的请求!");
  177. }
  178. WxAppinfo appinfo = appinfoService.getByAppId(appId);
  179. String out = "";
  180. // aes加密的消息
  181. WxMpXmlMessage inMessage = WxOpenXmlMessage.fromEncryptedMpXml(resultxml,
  182. openService.getWxOpenConfigStorage(), timestamp, nonce, msgSignature);
  183. logger.info("接收微信APP回调,消息解密后内容为:\n{}", inMessage.toString());
  184. // 全网发布测试用例
  185. /*
  186. if (StringUtils.equalsAnyIgnoreCase(appId, "wx570bc396a51b8ff8", "wxd101a85aa106f53e")) {
  187. try {
  188. if (StringUtils.equals(inMessage.getMsgType(), "text")) {
  189. if (StringUtils.equals(inMessage.getContent(), "TESTCOMPONENT_MSG_TYPE_TEXT")) {
  190. out = WxOpenXmlMessage.wxMpOutXmlMessageToEncryptedXml(
  191. WxMpXmlOutMessage.TEXT().content("TESTCOMPONENT_MSG_TYPE_TEXT_callback")
  192. .fromUser(inMessage.getToUser())
  193. .toUser(inMessage.getFromUser())
  194. .build(),
  195. openService.getWxOpenConfigStorage()
  196. );
  197. } else if (StringUtils.startsWith(inMessage.getContent(), "QUERY_AUTH_CODE:")) {
  198. String msg = inMessage.getContent().replace("QUERY_AUTH_CODE:", "") + "_from_api";
  199. WxMpKefuMessage kefuMessage = WxMpKefuMessage.TEXT().content(msg).toUser(inMessage.getFromUser()).build();
  200. openService.getWxOpenComponentService().getWxMpServiceByAppid(appId).getKefuService().sendKefuMessage(kefuMessage);
  201. }
  202. } else if (StringUtils.equals(inMessage.getMsgType(), "event")) {
  203. WxMpKefuMessage kefuMessage = WxMpKefuMessage.TEXT().content(inMessage.getEvent() + "from_callback").toUser(inMessage.getFromUser()).build();
  204. openService.getWxOpenComponentService().getWxMpServiceByAppid(appId).getKefuService().sendKefuMessage(kefuMessage);
  205. }
  206. } catch (WxErrorException e) {
  207. logger.error("callback", e);
  208. }
  209. }
  210. */
  211. if(appinfo.getType().equals(EnumAppType.B.getCode()) || appinfo.getType().equals(EnumAppType.C.getCode())) {
  212. // 小程序
  213. /**判断消息类型,调用对应的方法*/
  214. switch (inMessage.getMsgType()) {
  215. case "event": {
  216. switch (inMessage.getEvent()) {
  217. case "weapp_audit_success": { // 审核成功
  218. updateAuditStatus(appId, true, "");
  219. break;
  220. }
  221. case "weapp_audit_fail": { // 审核失败
  222. updateAuditStatus(appId, false, inMessage.getReason());
  223. break;
  224. }
  225. default:
  226. break;
  227. }
  228. return "success";
  229. }
  230. case "text":
  231. WxMpXmlOutMessage outMessage = openService.getWxOpenMessageRouter().route(inMessage, appId);
  232. if (outMessage != null) {
  233. out = WxOpenXmlMessage.wxMpOutXmlMessageToEncryptedXml(outMessage, openService.getWxOpenConfigStorage());
  234. logger.info(out);
  235. }
  236. else {
  237. out = "success";
  238. }
  239. break;
  240. default:
  241. return "success";
  242. }
  243. } else {
  244. // 公众号
  245. WxMpService mpService = MpUtil.getMpService(appinfo);
  246. try {
  247. WxMpXmlOutMessage outMessage = mpManager.getRouter(mpService).route(inMessage);
  248. if (outMessage == null) {
  249. this.logger.info("2 return: success");
  250. return "success";
  251. }
  252. out = outMessage.toEncryptedXml(mpService.getWxMpConfigStorage());
  253. } catch (Exception e) {
  254. logger.error("路由消息时出现异常!", e);
  255. }
  256. }
  257. return out;
  258. }
  259. private void updateAuditStatus(String appId, boolean success, String reason) {
  260. WxWeappAuditStatus auditStatus = weappAuditStatusService.getByAppId(appId);
  261. if (auditStatus != null) {
  262. if (success) {
  263. auditStatus.setAuditStatus(EnumWeappAuditStatus.SUCCESS.getCode());
  264. auditStatus.setAuditErrCode("");
  265. } else {
  266. auditStatus.setAuditStatus(EnumWeappAuditStatus.FAIL.getCode());
  267. auditStatus.setAuditErrCode(reason);
  268. }
  269. auditStatus.setAuditTime(new Date());
  270. weappAuditStatusService.updateStatus(auditStatus);
  271. }
  272. // 发邮件
  273. WxAppinfo appinfo = appinfoService.getByAppId(appId);
  274. if (appinfo != null) {
  275. SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");//设置日期格式
  276. StringBuilder titleSB = new StringBuilder();
  277. if (appinfo.getType().equals(EnumAppType.C.getCode())) {
  278. titleSB.append("C端小程序");
  279. } else if (appinfo.getType().equals(EnumAppType.B.getCode())) {
  280. titleSB.append("B端小程序");
  281. } else if (appinfo.getType().equals(EnumAppType.MP_S.getCode())) {
  282. titleSB.append("公众号-服务号");
  283. } else if (appinfo.getType().equals(EnumAppType.MP_P.getCode())) {
  284. titleSB.append("公众号-订阅号");
  285. }
  286. titleSB.append(appinfo.getName());
  287. if (success) {
  288. titleSB.append("审核通过");
  289. } else {
  290. titleSB.append("审核未通过");
  291. }
  292. StringBuilder sb = new StringBuilder();
  293. sb.append(df.format(new Date())).append("\n");
  294. if (success) {
  295. sb.append("审核通过\n");
  296. } else {
  297. sb.append("审核未通过\n").append("原因").append(reason);
  298. }
  299. String[] receivers = new String[]{"wangmei@iformall.com", "wuguoqiang@iformall.com", "huanghui@iformall.com", "sunmingming@iformall.com"};
  300. //发送邮件
  301. mailService.sendSimpleMail(receivers, titleSB.toString(), sb.toString());
  302. }
  303. }
  304. @GetMapping(value = "/createOpenPlatform")
  305. @ApiOperation("创建微信开放平台")
  306. public ResultData createOpenPlatform(@RequestParam(value = "appId") String appId) {
  307. Gson gson = new GsonBuilder().create();
  308. try {
  309. String responseStr = openService.getWxOpenComponentService().getWxMpServiceByAppid(appId).createOpenPlatform(appId);
  310. logger.info(responseStr);
  311. WxOpenPlatformResult openPlatformResult = gson.fromJson(responseStr, WxOpenPlatformResult.class);
  312. if (openPlatformResult.isSuccess()) {
  313. WxAuthorizerInfo authorizerInfo = authorizerInfoService.getByAppId(appId);
  314. if (authorizerInfo != null) {
  315. authorizerInfo.setId(authorizerInfo.getId());
  316. authorizerInfo.setOpenAppid(openPlatformResult.getOpenAppid());
  317. authorizerInfoService.updateOpenAppid(authorizerInfo);
  318. }
  319. }
  320. return new ResultData(responseStr);
  321. } catch (WxErrorException e) {
  322. logger.error(e.getMessage());
  323. return new ResultData(Result.ERROR, e.getMessage());
  324. }
  325. }
  326. @GetMapping(value = "/bindOpenPlatform")
  327. @ApiOperation("绑定微信开放平台")
  328. public ResultData bindOpenPlatform(@RequestParam(value = "appId") String appId, @RequestParam(value = "openAppId") String openAppId) {
  329. Gson gson = new GsonBuilder().create();
  330. try {
  331. String responseStr = openService.getWxOpenComponentService().getWxMpServiceByAppid(appId).bindOpenPlatform(appId, openAppId);
  332. logger.info(responseStr);
  333. WxOpenResult openResult = gson.fromJson(responseStr, WxOpenResult.class);
  334. if (openResult.isSuccess()) {
  335. return new ResultData();
  336. }
  337. return new ResultData(Result.ERROR, responseStr);
  338. } catch (WxErrorException e) {
  339. logger.error(e.getMessage());
  340. return new ResultData(Result.ERROR, e.getMessage());
  341. }
  342. }
  343. @GetMapping(value = "/unbindOpenPlatform")
  344. @ApiOperation("解绑微信开放平台")
  345. public ResultData unbindOpenPlatform(@RequestParam(value = "appId") String appId, @RequestParam(value = "openAppId") String openAppId) {
  346. Gson gson = new GsonBuilder().create();
  347. try {
  348. String responseStr = openService.getWxOpenComponentService().getWxMpServiceByAppid(appId).unbindOpenPlatform(appId, openAppId);
  349. logger.info(responseStr);
  350. WxOpenResult openResult = gson.fromJson(responseStr, WxOpenResult.class);
  351. if (openResult.isSuccess()) {
  352. return new ResultData();
  353. }
  354. return new ResultData(Result.ERROR, responseStr);
  355. } catch (WxErrorException e) {
  356. logger.error(e.getMessage());
  357. return new ResultData(Result.ERROR, e.getMessage());
  358. }
  359. }
  360. @GetMapping(value = "/getOpenPlatform")
  361. @ApiOperation("获取微信开放平台appId")
  362. public ResultData getOpenPlatform(@RequestParam(value = "appId") String appId) {
  363. Gson gson = new GsonBuilder().create();
  364. try {
  365. String responseStr = openService.getWxOpenComponentService().getWxMpServiceByAppid(appId).getBindOpenPlatform(appId);
  366. logger.info(responseStr);
  367. WxOpenPlatformResult openPlatformResult = gson.fromJson(responseStr, WxOpenPlatformResult.class);
  368. if (openPlatformResult.isSuccess()) {
  369. return new ResultData(openPlatformResult.getOpenAppid());
  370. }
  371. return new ResultData(Result.ERROR, responseStr);
  372. } catch (WxErrorException e) {
  373. logger.error(e.getMessage());
  374. return new ResultData(Result.ERROR, e.getMessage());
  375. }
  376. }
  377. @GetMapping(value = "/clearQuota")
  378. @ApiOperation("代公众号清零quota,每个公众号每个月有10次清零机会,包括在微信公众平台上的清零以及调用API进行清零")
  379. public ResultData clearQuota(@RequestParam(value = "appId") String appId) {
  380. try {
  381. openService.getWxOpenComponentService().getWxMpServiceByAppid(appId).clearQuota(appId);
  382. return new ResultData();
  383. } catch (WxErrorException e) {
  384. logger.error(e.getMessage());
  385. return new ResultData(Result.ERROR, e.getMessage());
  386. }
  387. }
  388. }