WxService.java 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303
  1. package com.pj.api.wx.service;
  2. import cn.hutool.cache.CacheUtil;
  3. import cn.hutool.cache.impl.TimedCache;
  4. import cn.hutool.core.net.URLDecoder;
  5. import cn.hutool.core.util.NumberUtil;
  6. import cn.hutool.core.util.RandomUtil;
  7. import cn.hutool.core.util.StrUtil;
  8. import cn.hutool.core.util.XmlUtil;
  9. import cn.hutool.crypto.SecureUtil;
  10. import cn.hutool.http.HttpUtil;
  11. import cn.hutool.json.JSONObject;
  12. import cn.hutool.json.JSONUtil;
  13. import com.alibaba.fastjson.JSON;
  14. import com.pj.api.wx.WxUtils;
  15. import com.pj.api.wx.bo.*;
  16. import com.pj.api.wx.vo.PrePayVO;
  17. import com.pj.current.config.MyConfig;
  18. import com.pj.current.config.PartConfig;
  19. import com.pj.current.config.WxConfig;
  20. import com.pj.project.tb_account.TbAccountService;
  21. import com.pj.project.tb_business.TbBusiness;
  22. import com.pj.project.tb_business.TbBusinessService;
  23. import com.pj.project.tb_business_car.TbBusinessCar;
  24. import com.pj.project.tb_business_car.TbBusinessCarService;
  25. import com.pj.project.tb_business_item.TbBusinessItem;
  26. import com.pj.project.tb_business_item.TbBusinessItemService;
  27. import com.pj.project.tb_charge_record.TbChargeRecord;
  28. import com.pj.project.tb_costomer.TbCostomer;
  29. import com.pj.project.tb_costomer.TbCostomerService;
  30. import com.pj.project.tb_fee_details.TbFeeDetailsService;
  31. import com.pj.project.tb_fee_statistics.TbFeeStatisticsService;
  32. import com.pj.project.tb_pay_record.TbPayRecord;
  33. import com.pj.project.tb_pay_record.TbPayRecordService;
  34. import com.pj.utils.cache.RedisUtil;
  35. import lombok.extern.slf4j.Slf4j;
  36. import org.springframework.context.annotation.Lazy;
  37. import org.springframework.scheduling.annotation.Async;
  38. import org.springframework.stereotype.Service;
  39. import org.springframework.transaction.annotation.Transactional;
  40. import javax.annotation.Resource;
  41. import javax.servlet.http.HttpServletRequest;
  42. import java.math.BigDecimal;
  43. import java.nio.charset.Charset;
  44. import java.time.LocalDateTime;
  45. import java.time.format.DateTimeFormatter;
  46. import java.util.*;
  47. @Service
  48. @Transactional
  49. @Slf4j
  50. public class WxService {
  51. @Resource
  52. @Lazy
  53. private TbBusinessService tbBusinessService;
  54. @Resource
  55. WxConfig wxConfig;
  56. @Resource
  57. MyConfig myConfig;
  58. @Resource
  59. PartConfig partConfig;
  60. @Resource
  61. WxUtils wxUtils;
  62. @Resource
  63. TbPayRecordService tbPayRecordService;
  64. @Resource
  65. TbCostomerService tbCostomerService;
  66. @Resource
  67. TbBusinessCarService tbBusinessCarService;
  68. @Resource
  69. @Lazy
  70. TbBusinessItemService tbBusinessItemService;
  71. @Resource
  72. TbFeeStatisticsService tbFeeStatisticsService;
  73. @Resource
  74. private TbAccountService tbAccountService;
  75. @Resource
  76. TbFeeDetailsService tbFeeDetailsService;
  77. /**
  78. * 统一下单接口
  79. */
  80. public Map<String, String> prePay(HttpServletRequest request) throws Exception {
  81. String tradeType = request.getParameter("tradeType");
  82. String money = request.getParameter("money");
  83. String openid = request.getParameter("openid");
  84. String desc = request.getParameter("desc");
  85. // String openid = "oDWvn5w-hVkzUuKeY7OBXBV_l1rU";
  86. String businessId = request.getParameter("b");
  87. String c = request.getParameter("c");
  88. String a = request.getParameter("a");
  89. Attach atchMap = new Attach();
  90. atchMap.setC(c).setB(businessId).setA(a);
  91. String out_trade_no = RandomUtil.randomString(32);
  92. Map<String, String> params = new HashMap<>();
  93. params.put("appid", wxConfig.getAppId());
  94. params.put("mch_id", wxConfig.getMachId());
  95. params.put("openid", openid);
  96. params.put("nonce_str", RandomUtil.randomString(32));
  97. params.put("body", desc);
  98. params.put("out_trade_no", out_trade_no);
  99. params.put("attach", JSONUtil.toJsonStr(atchMap));
  100. String total_free = partConfig.isTestEnv() ? "1" : NumberUtil.mul(money, 100 + "").intValue() + "";
  101. log.info("pay free:{}", total_free);
  102. params.put("total_fee", total_free);
  103. // params.put("total_fee", "1");
  104. params.put("spbill_create_ip", getIpAddress(request));
  105. params.put("notify_url", myConfig.getDomain() + "/wx/notify");
  106. params.put("trade_type", tradeType);
  107. params.put("scene_info", SceneInfoBO.getSceneInfo(myConfig.getDomain(), wxConfig.getTitle()));
  108. String sign = wxUtils.sign(params, wxConfig.getKey());
  109. log.info("wx pay sign result:{}", sign);
  110. params.put("sign", sign);
  111. String prePayUrl = "https://api.mch.weixin.qq.com/pay/unifiedorder";
  112. String xmlParam = XmlUtil.mapToXmlStr(params);
  113. log.info("wx pay xml params:{}", xmlParam);
  114. String result = HttpUtil.createPost(prePayUrl).header("Content-Type", "text/xml").body(xmlParam)
  115. .execute().body();
  116. Map<String, Object> resultMap = XmlUtil.xmlToMap(result);
  117. log.info("request pre pay url result:{}", resultMap);
  118. PrePayVO vo = new PrePayVO();
  119. if ("SUCCESS".equals(resultMap.get("return_code")) && "SUCCESS".equals(resultMap.get("result_code"))) {
  120. log.info("生成预支付订单成功,mweb_url:{}", resultMap.get("mweb_url"));
  121. Object webUrl = resultMap.get("mweb_url");
  122. Object prePayId = resultMap.get("prepay_id");
  123. String preId = prePayId == null ? "" : prePayId.toString();
  124. vo.setPayUrl(webUrl == null ? "" : webUrl.toString())
  125. .setPrePayId(preId)
  126. .setTradeType(tradeType);
  127. return getPayParams(openid, preId);
  128. }
  129. throw new Exception("支付信息有误");
  130. }
  131. public Map<String, String> getPayParams(String openid, String prePayId) {
  132. Map<String, String> signMap = new HashMap<>(10);
  133. signMap.put("appId", wxConfig.getAppId());
  134. signMap.put("timeStamp", System.currentTimeMillis() / 1000 + "");
  135. signMap.put("nonceStr", RandomUtil.randomString(32));
  136. signMap.put("signType", "MD5");
  137. signMap.put("package", "prepay_id=" + prePayId);
  138. String sign = wxUtils.sign(signMap, wxConfig.getKey());
  139. signMap.put("paySign", sign);
  140. signMap.put("openId", openid);
  141. return signMap;
  142. }
  143. public Map<String, String> getPayP(String timeStamp, String nonceStr, String openid, String pack) {
  144. Map<String, String> signMap = new HashMap<>(10);
  145. signMap.put("appId", wxConfig.getAppId());
  146. signMap.put("timeStamp", timeStamp);
  147. signMap.put("nonceStr", nonceStr);
  148. signMap.put("signType", "MD5");
  149. signMap.put("package", pack);
  150. String sign = wxUtils.sign(signMap, wxConfig.getKey());
  151. signMap.put("paySign", sign);
  152. signMap.put("openId", openid);
  153. return signMap;
  154. }
  155. public static String getIpAddress(HttpServletRequest request) {
  156. String ip = request.getHeader("x-forwarded-for");
  157. if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
  158. ip = request.getHeader("Proxy-Client-IP");
  159. }
  160. if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
  161. ip = request.getHeader("WL-Proxy-Client-IP");
  162. }
  163. if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
  164. ip = request.getRemoteAddr();
  165. }
  166. if (ip.contains(",")) {
  167. return ip.split(",")[0];
  168. } else {
  169. return ip;
  170. }
  171. }
  172. TimedCache<String, Integer> NotifyCache = CacheUtil.newTimedCache(360000);
  173. @Async
  174. public void WxNotify(NotifyBO notifyBO) throws Exception {
  175. String outTradeNo = notifyBO.getOutTradeNo();
  176. if (NotifyCache.get(outTradeNo) != null) {
  177. return;
  178. }
  179. NotifyCache.put(outTradeNo, 1);
  180. String total_fee = notifyBO.getTotalFee();
  181. BigDecimal money = new BigDecimal(total_fee).divide(new BigDecimal(100), 2, BigDecimal.ROUND_UP);
  182. log.info("wx pay notify;{}", JSONUtil.toJsonStr(notifyBO));
  183. String tradeNo = notifyBO.getOutTradeNo();
  184. tradeNo = StrUtil.subBefore(tradeNo, "_", true);
  185. String attachStr = notifyBO.getAttach();
  186. String transactionId = notifyBO.getTransactionId();
  187. Date now = new Date();
  188. if (StrUtil.isNotEmpty(attachStr)) {
  189. Attach attach = JSONUtil.toBean(attachStr, Attach.class);
  190. List<PriceBO> cars = JSONUtil.toList(attach.getC(), PriceBO.class);
  191. tbFeeStatisticsService.countParkFee(cars);//停车费日统计
  192. for (PriceBO bo1 : cars) {
  193. TbBusinessCar car = tbBusinessCarService.getById(bo1.getId());
  194. BigDecimal price = bo1.getP();
  195. car.setPay(1).setMoney(car.getMoney().add(price)).setPayTime(now);
  196. tbBusinessCarService.updateById(car);
  197. TbBusiness business = tbBusinessService.getById(car.getBusinessId());
  198. if (business != null) {
  199. business.setPayMoney(business.getPayMoney().add(price));
  200. business.setPayStatus(business.getPayMoney().equals(business.getTotalMoney()) ? 3 : 4);
  201. tbBusinessService.updateById(business);
  202. }
  203. }
  204. tbFeeDetailsService.chargeParkFee(cars, transactionId);//添加cars的收费明细
  205. String businessId = attach.getB();
  206. if (StrUtil.isNotEmpty(businessId)) {
  207. List<TbBusinessItem> items = tbBusinessItemService.findByBusinessId(businessId);
  208. tbFeeStatisticsService.countBusinessFee(items, transactionId);//业务费日统计
  209. items.forEach(tbBusinessItem -> tbBusinessItem.setPayStatus(1).setPayTime(now));
  210. tbBusinessItemService.updateBatchById(items);
  211. TbBusiness tbBusiness = tbBusinessService.getById(businessId);
  212. tbBusiness.setPayTime(now).setPayType(3).setConfirmInput(1).setConfirmInputTime(now)
  213. .setPayMoney(tbBusiness.getPayMoney().add(tbBusiness.getItemPrice()))
  214. .setPayNo(transactionId);
  215. tbBusiness.setPayStatus(tbBusiness.getPayMoney().equals(tbBusiness.getTotalMoney()) ? 3 : 4);
  216. tbBusinessService.updateById(tbBusiness);
  217. TbPayRecord payRecord = new TbPayRecord();
  218. TbCostomer tbCostomer = tbCostomerService.getById(tbBusiness.getCustomerId());
  219. payRecord.setCreateTime(now)
  220. .setOutTradeNo(tradeNo)
  221. .setTransactionId(transactionId)
  222. .setPayMoney(money);
  223. if (tbCostomer != null) {
  224. payRecord.setCustomerId(tbCostomer.getId()).setCustomerName(tbCostomer.getName());
  225. }
  226. tbPayRecordService.save(payRecord);
  227. }
  228. String a = attach.getA();
  229. if (StrUtil.isNotEmpty(a)) {
  230. AccountChargeBO chargeBO = JSONUtil.toBean(a, AccountChargeBO.class);
  231. tbAccountService.recharge(chargeBO.getId(), chargeBO.getC(), TbChargeRecord.Ch.WECHAT.getType(), money);
  232. }
  233. }
  234. }
  235. public String getRedirectUrl(String path, String state) {
  236. String redirectUrl = myConfig.getWebDomain() + path;
  237. String encoderUrl = URLDecoder.decode(redirectUrl, Charset.forName("utf-8"));
  238. String url = wxConfig.getAuthLoginUrl().replace("REDIRECT_URI", encoderUrl);
  239. if (StrUtil.isNotEmpty(state)) {
  240. url = url.replace("STATE", state);
  241. }
  242. log.info("get redirect url:{}", url);
  243. return url;
  244. }
  245. public String getOpenidByCode(String code, String openid) {
  246. String openidUrl = wxConfig.getOpenidUrl().replace("CODE", code);
  247. String resp = HttpUtil.get(openidUrl);
  248. JSONObject result = JSONUtil.parseObj(resp);
  249. log.info("get openid result:{}", resp);
  250. String newOpenid = result.getStr("openid");
  251. if (StrUtil.isNotEmpty(newOpenid)) {
  252. return newOpenid;
  253. }
  254. return openid;
  255. }
  256. public SortedMap getWxConfig(String url) {
  257. Long nowTime = System.currentTimeMillis() / 1000;
  258. String jsTicket = RedisUtil.get(wxConfig.getJsApiTicketKey());
  259. SortedMap<String, Object> params = new TreeMap<>();
  260. params.put("noncestr", RandomUtil.randomString(32));
  261. params.put("jsapi_ticket", jsTicket);
  262. params.put("timestamp", nowTime);
  263. params.put("url", url);
  264. String sign = SecureUtil.sha1(params.toString().substring(1, params.toString().lastIndexOf("}")).replaceAll(", ", "&"));
  265. params.put("sign", sign);
  266. params.put("appId", wxConfig.getAppId());
  267. return params;
  268. }
  269. @Async
  270. public void sendTemplateMsg(String templateId, String openid, MsgDataBO data) {
  271. String accessToken = RedisUtil.get(wxConfig.getAccessTokenKey());
  272. BaseTemplate baseTemplate = new BaseTemplate(openid, templateId, data);
  273. String json = JSON.toJSONString(baseTemplate);
  274. String url = wxConfig.getSendMsgUrl().replace("ACCESS_TOKEN", accessToken);
  275. String resp = HttpUtil.post(url, json);
  276. log.info("send wx msg{},{},{};return :{}", templateId, openid, JSONUtil.toJsonStr(data), resp);
  277. }
  278. @Async
  279. public void sendTemplateMsg(String templateId, String openid, MsgDataBO data, String detailUrl) {
  280. String accessToken = RedisUtil.get(wxConfig.getAccessTokenKey());
  281. BaseTemplate baseTemplate = new BaseTemplate(openid, templateId, detailUrl, data);
  282. String json = JSON.toJSONString(baseTemplate);
  283. String url = wxConfig.getSendMsgUrl().replace("ACCESS_TOKEN", accessToken);
  284. String resp = HttpUtil.post(url, json);
  285. log.info("send wx msg{},{},{};return :{}", templateId, openid, JSONUtil.toJsonStr(data), resp);
  286. }
  287. }