WxNotifyController.java 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349
  1. package com.miaxis.app.controller.wx;
  2. import com.alibaba.fastjson.JSONObject;
  3. import com.miaxis.common.config.WxpayConfig;
  4. import com.miaxis.common.constant.Constants;
  5. import com.miaxis.common.exception.CustomException;
  6. import com.miaxis.common.utils.AesUtil;
  7. import com.miaxis.common.utils.DateUtils;
  8. import com.miaxis.newgzpt.domain.GzptUserInfo;
  9. import com.miaxis.newgzpt.domain.GzptVideoVip;
  10. import com.miaxis.newgzpt.dto.GzptVideoVipDTO;
  11. import com.miaxis.newgzpt.service.IGzptUserInfoService;
  12. import com.miaxis.newgzpt.service.IGzptVideoVipService;
  13. import com.miaxis.wx.domain.RefundRecord;
  14. import com.miaxis.wx.domain.WxJsOrder;
  15. import com.miaxis.wx.domain.WxOrder;
  16. import com.miaxis.wx.dto.*;
  17. import com.miaxis.wx.service.IRefundRecordService;
  18. import com.miaxis.wx.service.IWxJsOrderService;
  19. import com.wechat.pay.contrib.apache.httpclient.auth.AutoUpdateCertificatesVerifier;
  20. import io.swagger.annotations.Api;
  21. import io.swagger.annotations.ApiOperation;
  22. import lombok.Data;
  23. import lombok.RequiredArgsConstructor;
  24. import lombok.extern.slf4j.Slf4j;
  25. import org.joda.time.DateTime;
  26. import org.springframework.beans.factory.annotation.Autowired;
  27. import org.springframework.beans.factory.annotation.Value;
  28. import org.springframework.transaction.annotation.Transactional;
  29. import org.springframework.util.Base64Utils;
  30. import org.springframework.web.bind.annotation.PostMapping;
  31. import org.springframework.web.bind.annotation.RequestBody;
  32. import org.springframework.web.bind.annotation.RequestMapping;
  33. import org.springframework.web.bind.annotation.RestController;
  34. import javax.servlet.http.HttpServletRequest;
  35. import java.io.BufferedReader;
  36. import java.io.IOException;
  37. import java.nio.charset.StandardCharsets;
  38. import java.security.*;
  39. import java.security.cert.X509Certificate;
  40. import java.util.Date;
  41. @RestController
  42. @RequiredArgsConstructor
  43. @RequestMapping(Constants.OPEN_PREFIX+"/wx/notify")
  44. @Api(tags = {"【APP-微信回调】"})
  45. @Slf4j
  46. public class WxNotifyController {
  47. @Autowired
  48. private WxpayConfig wxpayConfig;
  49. @Autowired
  50. private IWxJsOrderService wxJsOrderService;
  51. @Autowired
  52. private IRefundRecordService refundRecordService;
  53. @Autowired
  54. private AutoUpdateCertificatesVerifier verifier;
  55. @Autowired
  56. private IGzptUserInfoService userInfoService;
  57. @Autowired
  58. private IGzptVideoVipService videoVipService;
  59. /**
  60. * 微信支付回调接口
  61. */
  62. @PostMapping(value = "/wxpay")
  63. @ApiOperation("微信支付回调")
  64. public WxNotifyReturnDTO wxpayNotify(@RequestBody WxpayNotifyDTO wxpayNotifyDTO, HttpServletRequest request) throws GeneralSecurityException, IOException {
  65. String bodyString = getBodyString(request);
  66. if (!validate(request,bodyString)){
  67. throw new CustomException("签名失败");
  68. }
  69. String resourceString = getSourString(wxpayNotifyDTO);
  70. log.info(resourceString);
  71. JSONObject jsonObject = JSONObject.parseObject(resourceString);
  72. //将回调数据写入数据库
  73. writeNotifyDataToDb(jsonObject);
  74. WxNotifyReturnDTO wxNotifyReturnDTO = new WxNotifyReturnDTO();
  75. wxNotifyReturnDTO.setCode("SUCCESS");
  76. wxNotifyReturnDTO.setMessage("成功");
  77. return wxNotifyReturnDTO;
  78. }
  79. private String getBodyString(HttpServletRequest request) {
  80. BufferedReader br = null;
  81. StringBuilder sb = new StringBuilder("");
  82. try
  83. {
  84. br = request.getReader();
  85. String str;
  86. while ((str = br.readLine()) != null)
  87. {
  88. sb.append(str);
  89. }
  90. br.close();
  91. }
  92. catch (IOException e)
  93. {
  94. e.printStackTrace();
  95. }
  96. finally
  97. {
  98. if (null != br)
  99. {
  100. try
  101. {
  102. br.close();
  103. }
  104. catch (IOException e)
  105. {
  106. e.printStackTrace();
  107. }
  108. }
  109. }
  110. return sb.toString();
  111. }
  112. private Boolean validate(HttpServletRequest request, String bodyString) throws NoSuchAlgorithmException, InvalidKeyException, SignatureException {
  113. String sign = request.getHeader("Wechatpay-Signature");
  114. String timestamp = request.getHeader("Wechatpay-Timestamp");
  115. String nonce = request.getHeader("Wechatpay-Nonce");
  116. StringBuffer sb = new StringBuffer();
  117. sb.append(timestamp + "\n");
  118. sb.append(nonce + "\n");
  119. sb.append(bodyString + "\n");
  120. X509Certificate validCertificate = verifier.getValidCertificate();
  121. // 进行签名服务
  122. Signature signature = Signature.getInstance("SHA256withRSA");
  123. // 用微信平台公钥对签名器进行初始化
  124. signature.initVerify(validCertificate);
  125. // 把我们构造的验签名串更新到签名器中
  126. signature.update(sb.toString().getBytes(StandardCharsets.UTF_8));
  127. Boolean result = signature.verify(Base64Utils.decodeFromString(sign));
  128. log.info("微信支付回调验签:"+result.toString());
  129. return result;
  130. }
  131. /**
  132. * 微信退款回调
  133. */
  134. @PostMapping(value = "/refund")
  135. @ApiOperation("微信退款回调")
  136. public WxNotifyReturnDTO refundNotify(@RequestBody WxpayNotifyDTO wxpayNotifyDTO, HttpServletRequest request) throws GeneralSecurityException, IOException {
  137. String bodyString = getBodyString(request);
  138. if (!validate(request,bodyString)){
  139. throw new CustomException("签名失败");
  140. }
  141. String resourceString = getSourString(wxpayNotifyDTO);
  142. log.info(resourceString);
  143. JSONObject jsonObject = JSONObject.parseObject(resourceString);
  144. //将回调数据写入数据库
  145. writeRefundNotifyDataToDb(jsonObject);
  146. WxNotifyReturnDTO wxNotifyReturnDTO = new WxNotifyReturnDTO();
  147. wxNotifyReturnDTO.setCode("SUCCESS");
  148. wxNotifyReturnDTO.setMessage("成功");
  149. return wxNotifyReturnDTO;
  150. }
  151. private void writeRefundNotifyDataToDb(JSONObject jsonObject) {
  152. String refundId = jsonObject.getString("refund_id");
  153. RefundRecord refundRecord = refundRecordService.getByRefundId(refundId);
  154. if (refundRecord == null) {
  155. log.error("该退款订单不存在");
  156. return;
  157. }
  158. refundRecord.setTransactionId(jsonObject.getString("transaction_id"));
  159. refundRecord.setUserReceivedAccount(jsonObject.getString("user_received_account"));
  160. refundRecord.setStatus(jsonObject.getString("refund_status"));
  161. refundRecordService.updateById(refundRecord);
  162. }
  163. @Transactional
  164. public void writeNotifyDataToDb(JSONObject jsonObject) {
  165. System.out.println(jsonObject);
  166. String outTradeNo = jsonObject.getString("out_trade_no");
  167. WxJsOrder wxJsOrder = wxJsOrderService.getByOutTradeNo(outTradeNo);
  168. if (wxJsOrder == null) {
  169. log.error("该订单不存在");
  170. return;
  171. }
  172. if("SUCCESS".equals(wxJsOrder.getTradeState())){
  173. log.info("订单号为"+wxJsOrder.getOutTradeNo()+"的订单已完成操作,无法重复操作!");
  174. return;
  175. }
  176. wxJsOrder.setTransactionId(jsonObject.getString("transaction_id"));
  177. JSONObject amount = jsonObject.getJSONObject("amount");
  178. wxJsOrder.setPayerTotal(amount.getInteger("payer_total"));
  179. wxJsOrder.setTotal(amount.getInteger("total"));
  180. wxJsOrder.setCurrency(amount.getString("currency"));
  181. wxJsOrder.setPayerCurrency(amount.getString("payer_currency"));
  182. wxJsOrder.setTradeState(jsonObject.getString("trade_state"));
  183. wxJsOrder.setBankType(jsonObject.getString("bank_type"));
  184. DateTime dateTime = new DateTime(jsonObject.getString("success_time"));
  185. wxJsOrder.setSuccessTime(dateTime.toDate());
  186. wxJsOrder.setTradeStateDesc(jsonObject.getString("trade_state_desc"));
  187. wxJsOrder.setTradeType(jsonObject.getString("trade_type"));
  188. wxJsOrder.setAttach(jsonObject.getString("attach"));
  189. //插入VIP信息
  190. writeVipDataToDb(wxJsOrder);
  191. wxJsOrderService.updateById(wxJsOrder);
  192. }
  193. @Transactional
  194. public void writeVipDataToDb(WxJsOrder wxJsOrder) {
  195. long oneYearLong = 1000*60*60*24*365l;
  196. Date now = new Date();
  197. if ("科目二视频".equals(wxJsOrder.getGoodsName())){
  198. GzptVideoVipDTO gzptVideoVipDTO = new GzptVideoVipDTO();
  199. gzptVideoVipDTO.setUserId(wxJsOrder.getUserId());
  200. GzptVideoVip gv = videoVipService.getGzptVideoVipByUserId(gzptVideoVipDTO);
  201. if(gv!=null) {
  202. //修改会员信息
  203. Date km2Date = gv.getSubject2();
  204. if(km2Date!=null) { //存在会员时间
  205. if(km2Date.compareTo(now)<0) { //会员已过期
  206. long x = now.getTime() + oneYearLong;
  207. Date km2oneYear = new Date(x);
  208. gv.setSubject2(km2oneYear);
  209. videoVipService.updateGzptVideoVipByUserId(gv);
  210. } else { //会员时间延长
  211. long x = km2Date.getTime() + oneYearLong;
  212. Date km2oneYear = new Date(x);
  213. gv.setSubject2(km2oneYear);
  214. videoVipService.updateGzptVideoVipByUserId(gv);
  215. }
  216. }
  217. } else {
  218. //新增会员信息
  219. GzptVideoVip videoVip = new GzptVideoVip();
  220. videoVip.setUserId(wxJsOrder.getUserId());
  221. videoVip.setUserName(wxJsOrder.getUserName());
  222. long x = now.getTime() + oneYearLong;
  223. Date km2oneYear = new Date(x);
  224. videoVip.setSubject2(km2oneYear);
  225. System.out.println(videoVip);
  226. videoVipService.saveGzptVideoVip(videoVip);
  227. }
  228. } else if ("科目三视频".equals(wxJsOrder.getGoodsName())) {
  229. GzptVideoVipDTO gzptVideoVipDTO = new GzptVideoVipDTO();
  230. gzptVideoVipDTO.setUserId(wxJsOrder.getUserId());
  231. GzptVideoVip gv = videoVipService.getGzptVideoVipByUserId(gzptVideoVipDTO);
  232. if(gv!=null) {
  233. //修改会员信息
  234. Date km3Date = gv.getSubject3();
  235. if(km3Date!=null) { //存在会员时间
  236. if(km3Date.compareTo(now)<0) { //会员已过期
  237. long x = now.getTime() + oneYearLong;
  238. Date km3oneYear = new Date(x);
  239. gv.setSubject3(km3oneYear);
  240. videoVipService.updateGzptVideoVipByUserId(gv);
  241. } else { //会员时间延长
  242. long x = km3Date.getTime() + oneYearLong;
  243. Date km3oneYear = new Date(x);
  244. gv.setSubject3(km3oneYear);
  245. videoVipService.updateGzptVideoVipByUserId(gv);
  246. }
  247. }
  248. } else {
  249. //新增会员信息
  250. GzptVideoVip videoVip = new GzptVideoVip();
  251. videoVip.setUserId(wxJsOrder.getUserId());
  252. videoVip.setUserName(wxJsOrder.getUserName());
  253. long x = now.getTime() + oneYearLong;
  254. Date km3oneYear = new Date(x);
  255. videoVip.setSubject3(km3oneYear);
  256. videoVipService.saveGzptVideoVip(videoVip);
  257. }
  258. } else if ("全套实操视频".equals(wxJsOrder.getGoodsName())) {
  259. GzptVideoVipDTO gzptVideoVipDTO = new GzptVideoVipDTO();
  260. gzptVideoVipDTO.setUserId(wxJsOrder.getUserId());
  261. GzptVideoVip gv = videoVipService.getGzptVideoVipByUserId(gzptVideoVipDTO);
  262. if(gv!=null) {
  263. //修改会员信息
  264. Date km2Date = gv.getSubject2();
  265. Date km3Date = gv.getSubject3();
  266. if(km2Date!=null) { //科目二处理
  267. long x = km2Date.getTime() + oneYearLong;
  268. Date km2oneYear = new Date(x);
  269. gv.setSubject2(km2oneYear);
  270. } else {
  271. long x = now.getTime() + oneYearLong;
  272. Date km2oneYear = new Date(x);
  273. gv.setSubject2(km2oneYear);
  274. }
  275. if(km3Date!=null) { //科目三处理
  276. long x = km3Date.getTime() + oneYearLong;
  277. Date km3oneYear = new Date(x);
  278. gv.setSubject3(km3oneYear);
  279. } else {
  280. long x = now.getTime() + oneYearLong;
  281. Date km3oneYear = new Date(x);
  282. gv.setSubject3(km3oneYear);
  283. }
  284. videoVipService.updateGzptVideoVipByUserId(gv);
  285. } else {
  286. //新增会员信息
  287. GzptVideoVip videoVip = new GzptVideoVip();
  288. videoVip.setUserId(wxJsOrder.getUserId());
  289. videoVip.setUserName(wxJsOrder.getUserName());
  290. long x = now.getTime() + oneYearLong;
  291. Date oneYear = new Date(x);
  292. videoVip.setSubject2(oneYear);
  293. videoVip.setSubject3(oneYear);
  294. videoVipService.saveGzptVideoVip(videoVip);
  295. }
  296. }
  297. }
  298. private String getSourString(WxpayNotifyDTO wxpayNotifyDTO) throws GeneralSecurityException, IOException {
  299. AesUtil aesUtil = new AesUtil(wxpayConfig.getV3key().getBytes());
  300. WxpayNotifyDTO.WxpaySource wxpaySource = wxpayNotifyDTO.getResource();
  301. return aesUtil.decryptToString(wxpaySource.getAssociated_data().getBytes(), wxpaySource.getNonce().getBytes(), wxpaySource.getCiphertext());
  302. }
  303. @Data
  304. public class FilmNotifyReturnDTO {
  305. String code;
  306. String message;
  307. Boolean success;
  308. }
  309. }