WxService.java 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377
  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.codec.Base64;
  5. import cn.hutool.core.date.DateUtil;
  6. import cn.hutool.core.net.URLDecoder;
  7. import cn.hutool.core.util.NumberUtil;
  8. import cn.hutool.core.util.RandomUtil;
  9. import cn.hutool.core.util.StrUtil;
  10. import cn.hutool.core.util.XmlUtil;
  11. import cn.hutool.crypto.SecureUtil;
  12. import cn.hutool.http.HttpUtil;
  13. import cn.hutool.json.JSONObject;
  14. import cn.hutool.json.JSONUtil;
  15. import com.alibaba.fastjson.JSON;
  16. import com.pj.api.wx.WxUtils;
  17. import com.pj.api.wx.bo.*;
  18. import com.pj.api.wx.vo.PrePayVO;
  19. import com.pj.current.config.MyConfig;
  20. import com.pj.current.config.PartConfig;
  21. import com.pj.current.config.WxConfig;
  22. import com.pj.project.tb_account.TbAccountService;
  23. import com.pj.project.tb_business.TbBusiness;
  24. import com.pj.project.tb_business.TbBusinessService;
  25. import com.pj.project.tb_business_car.TbBusinessCar;
  26. import com.pj.project.tb_business_car.TbBusinessCarService;
  27. import com.pj.project.tb_business_item.TbBusinessItem;
  28. import com.pj.project.tb_business_item.TbBusinessItemService;
  29. import com.pj.project.tb_charge_record.TbChargeRecord;
  30. import com.pj.project.tb_costomer.TbCostomer;
  31. import com.pj.project.tb_costomer.TbCostomerService;
  32. import com.pj.project.tb_fee_details.TbFeeDetailsService;
  33. import com.pj.project.tb_fee_statistics.TbFeeStatisticsService;
  34. import com.pj.project.tb_goods.TbGoods;
  35. import com.pj.project.tb_goods.TbGoodsService;
  36. import com.pj.project.tb_item.TbItem;
  37. import com.pj.project.tb_order.TbOrder;
  38. import com.pj.project.tb_order.TbOrderService;
  39. import com.pj.project.tb_pay_record.TbPayRecord;
  40. import com.pj.project.tb_pay_record.TbPayRecordService;
  41. import com.pj.project.wx_send_msg.WxSendMsg;
  42. import com.pj.project.wx_send_msg.WxSendMsgService;
  43. import com.pj.project4sp.global.BusinessException;
  44. import com.pj.utils.cache.RedisUtil;
  45. import lombok.extern.slf4j.Slf4j;
  46. import org.springframework.context.annotation.Lazy;
  47. import org.springframework.scheduling.annotation.Async;
  48. import org.springframework.stereotype.Service;
  49. import org.springframework.transaction.annotation.Transactional;
  50. import javax.annotation.Resource;
  51. import javax.servlet.http.HttpServletRequest;
  52. import java.math.BigDecimal;
  53. import java.nio.charset.Charset;
  54. import java.util.*;
  55. @Service
  56. @Transactional
  57. @Slf4j
  58. public class WxService {
  59. @Resource
  60. @Lazy
  61. private TbBusinessService tbBusinessService;
  62. @Resource
  63. WxConfig wxConfig;
  64. @Resource
  65. MyConfig myConfig;
  66. @Resource
  67. PartConfig partConfig;
  68. @Resource
  69. WxUtils wxUtils;
  70. @Resource
  71. TbBusinessCarService tbBusinessCarService;
  72. @Resource
  73. @Lazy
  74. TbBusinessItemService tbBusinessItemService;
  75. @Resource
  76. TbFeeStatisticsService tbFeeStatisticsService;
  77. @Resource
  78. private TbAccountService tbAccountService;
  79. @Resource
  80. TbFeeDetailsService tbFeeDetailsService;
  81. @Resource
  82. private TbOrderService tbOrderService;
  83. @Resource
  84. private TbGoodsService tbGoodsService;
  85. @Resource
  86. private WxSendMsgService wxSendMsgService;
  87. /**
  88. * 统一下单接口
  89. */
  90. public Map<String, String> prePay(HttpServletRequest request) throws Exception {
  91. String tradeType = request.getParameter("tradeType");
  92. String money = request.getParameter("money");
  93. if (Double.valueOf(money) <= 0) {
  94. throw new BusinessException("金额不正确");
  95. }
  96. String openid = request.getParameter("openid");
  97. String desc = request.getParameter("desc");
  98. // String openid = "oDWvn5w-hVkzUuKeY7OBXBV_l1rU";
  99. String businessId = request.getParameter("b");
  100. String c = request.getParameter("c");
  101. String a = request.getParameter("a");
  102. Attach atchMap = new Attach();
  103. atchMap.setC(c).setB(businessId).setA(a);
  104. String out_trade_no = RandomUtil.randomString(32);
  105. Map<String, String> params = new HashMap<>();
  106. params.put("appid", wxConfig.getAppId());
  107. params.put("mch_id", wxConfig.getMachId());
  108. params.put("openid", openid);
  109. params.put("nonce_str", RandomUtil.randomString(32));
  110. params.put("body", handlerDesc(desc));
  111. params.put("out_trade_no", out_trade_no);
  112. // params.put("attach", JSONUtil.toJsonStr(atchMap));
  113. String total_free = partConfig.isTestEnv() ? "1" : NumberUtil.mul(money, 100 + "").intValue() + "";
  114. log.info("pay free:{}", total_free);
  115. params.put("total_fee", total_free);
  116. // params.put("total_fee", "1");
  117. params.put("spbill_create_ip", getIpAddress(request));
  118. params.put("notify_url", myConfig.getDomain() + "/wx/notify");
  119. params.put("trade_type", tradeType);
  120. params.put("scene_info", SceneInfoBO.getSceneInfo(myConfig.getDomain(), wxConfig.getTitle()));
  121. String sign = wxUtils.sign(params, wxConfig.getKey());
  122. log.info("wx pay sign result:{}", sign);
  123. params.put("sign", sign);
  124. String prePayUrl = "https://api.mch.weixin.qq.com/pay/unifiedorder";
  125. String xmlParam = XmlUtil.mapToXmlStr(params);
  126. log.info("wx pay xml params:{}", xmlParam);
  127. String result = HttpUtil.createPost(prePayUrl).header("Content-Type", "text/xml").body(xmlParam)
  128. .execute().body();
  129. Map<String, Object> resultMap = XmlUtil.xmlToMap(result);
  130. log.info("request pre pay url result:{}", resultMap);
  131. PrePayVO vo = new PrePayVO();
  132. if ("SUCCESS".equals(resultMap.get("return_code")) && "SUCCESS".equals(resultMap.get("result_code"))) {
  133. log.info("生成预支付订单成功,mweb_url:{}", resultMap.get("mweb_url"));
  134. Object webUrl = resultMap.get("mweb_url");
  135. Object prePayId = resultMap.get("prepay_id");
  136. String preId = prePayId == null ? "" : prePayId.toString();
  137. vo.setPayUrl(webUrl == null ? "" : webUrl.toString())
  138. .setPrePayId(preId)
  139. .setTradeType(tradeType);
  140. Map<String, String> payParams = getPayParams(openid, preId);
  141. payParams.put("outTradeNo", out_trade_no);
  142. TbOrder tbOrder = new TbOrder();
  143. tbOrder.setAttach(JSONUtil.toJsonStr(atchMap))
  144. .setOpenid(openid)
  145. .setOrderTime(new Date())
  146. .setOutTradeNo(out_trade_no).setPrice(money);
  147. return payParams;
  148. }
  149. throw new Exception("支付信息有误");
  150. }
  151. public Map<String, String> getPayParams(String openid, String prePayId) {
  152. Map<String, String> signMap = new HashMap<>(10);
  153. signMap.put("appId", wxConfig.getAppId());
  154. signMap.put("timeStamp", System.currentTimeMillis() / 1000 + "");
  155. signMap.put("nonceStr", RandomUtil.randomString(32));
  156. signMap.put("signType", "MD5");
  157. signMap.put("package", "prepay_id=" + prePayId);
  158. String sign = wxUtils.sign(signMap, wxConfig.getKey());
  159. signMap.put("paySign", sign);
  160. signMap.put("openId", openid);
  161. return signMap;
  162. }
  163. public Map<String, String> getPayP(String timeStamp, String nonceStr, String openid, String pack) {
  164. Map<String, String> signMap = new HashMap<>(10);
  165. signMap.put("appId", wxConfig.getAppId());
  166. signMap.put("timeStamp", timeStamp);
  167. signMap.put("nonceStr", nonceStr);
  168. signMap.put("signType", "MD5");
  169. signMap.put("package", pack);
  170. String sign = wxUtils.sign(signMap, wxConfig.getKey());
  171. signMap.put("paySign", sign);
  172. signMap.put("openId", openid);
  173. return signMap;
  174. }
  175. public static String getIpAddress(HttpServletRequest request) {
  176. String ip = request.getHeader("x-forwarded-for");
  177. if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
  178. ip = request.getHeader("Proxy-Client-IP");
  179. }
  180. if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
  181. ip = request.getHeader("WL-Proxy-Client-IP");
  182. }
  183. if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
  184. ip = request.getRemoteAddr();
  185. }
  186. if (ip.contains(",")) {
  187. return ip.split(",")[0];
  188. } else {
  189. return ip;
  190. }
  191. }
  192. public void WxNotify(NotifyBO notifyBO) {
  193. String orderStatus=notifyBO.getTradeStatus();
  194. if (!"SUCCESS".equals(orderStatus) && !"FINISH".equals(orderStatus)) {
  195. log.error("支付订单回调失败:{}",JSONUtil.toJsonStr(notifyBO));
  196. return;
  197. }
  198. String outTradeNo = notifyBO.getOutTradeNo();
  199. if (StrUtil.isNotEmpty(RedisUtil.get(outTradeNo))) {
  200. log.error("========订单已处理==========:{}", outTradeNo);
  201. return;
  202. }
  203. RedisUtil.setByMINUTES(outTradeNo, DateUtil.now(), 10);
  204. TbOrder tbOrder = tbOrderService.findByOutTradeNo(outTradeNo);
  205. if (tbOrder == null) {
  206. log.error("========订单不存在==========:{}", outTradeNo);
  207. return;
  208. }
  209. if (StrUtil.equals("SUCCESS", tbOrder.getOrderStatus())) {
  210. log.error("========订单已支付==========:{}", outTradeNo);
  211. return;
  212. }
  213. String total_fee = notifyBO.getTotalFee();
  214. BigDecimal money = new BigDecimal(total_fee).divide(new BigDecimal(100), 2, BigDecimal.ROUND_UP);
  215. String attachStr = tbOrder.getAttach();
  216. String transactionId = notifyBO.getTransactionId();
  217. Date now = new Date();
  218. String timeEnd=notifyBO.getTimeEnd();
  219. Date payTime=now;
  220. if (StrUtil.isNotEmpty(timeEnd)){
  221. payTime=DateUtil.parse(timeEnd,"yyyyMMddHHmmss");
  222. }
  223. if (StrUtil.isNotEmpty(attachStr)) {
  224. Attach attach = JSONUtil.toBean(attachStr, Attach.class);
  225. List<PriceBO> cars = JSONUtil.toList(attach.getC(), PriceBO.class);
  226. tbFeeStatisticsService.countParkFee(cars);//停车费统计
  227. for (PriceBO bo1 : cars) {
  228. TbBusinessCar car = tbBusinessCarService.getById(bo1.getId());
  229. BigDecimal price = bo1.getP();
  230. if (price == null || price.doubleValue() <= 0) {
  231. log.error("付款金额不正确:{}", JSONUtil.toJsonStr(notifyBO));
  232. continue;
  233. }
  234. car.setPay(1).setMoney(car.getMoney().add(price)).setPayTime(payTime).setPayType(TbBusinessCar.PayTypeEnum.HAS_PAY_TYPE.getType());
  235. tbBusinessCarService.updateById(car);
  236. }
  237. tbFeeDetailsService.chargeParkFee(cars, transactionId, outTradeNo);//添加cars的收费明细
  238. String businessId = attach.getB();
  239. Date finalPayTime = payTime;
  240. if (StrUtil.isNotEmpty(businessId)) {
  241. List<String> businessIds = StrUtil.splitTrim(businessId, ",");
  242. List<TbBusinessItem> items = tbBusinessItemService.findByBusinessIdList(businessIds);
  243. List<TbBusiness> businessList = tbBusinessService.listByIds(businessIds);
  244. for (TbBusiness tbBusiness : businessList) {
  245. tbBusiness.setPayTime(payTime).setPayType(3).setConfirmInput(1).setConfirmInputTime(payTime)
  246. .setPayMoney(tbBusiness.getItemPrice())
  247. .setPayNo(transactionId);
  248. tbBusiness.setPayStatus(3);
  249. tbBusinessService.updateById(tbBusiness);
  250. TbGoods tbGoods = tbGoodsService.getById(tbBusiness.getGoodsId());
  251. List<TbBusinessCar> carList = tbBusinessCarService.findOtherBusinessCar(businessId);
  252. carList.forEach(tbBusinessCar -> {
  253. String carType = tbBusinessCar.getCarType();
  254. //
  255. if (TbItem.ItemTypeEnum.EMPTY_TYPE.getType().equals(carType) && tbGoods.getChinaCarPay() == 0
  256. || TbItem.ItemTypeEnum.WEIGHT_TYPE.getType().equals(carType) && tbGoods.getVietnamCarPay() == 0) {
  257. tbBusinessCar.setPay(1).setPayTime(finalPayTime);
  258. tbBusinessCarService.updateById(tbBusinessCar);
  259. }
  260. });
  261. }
  262. tbFeeStatisticsService.countBusinessFee(items, transactionId, outTradeNo);//业务费统计
  263. items.forEach(tbBusinessItem -> tbBusinessItem.setPayStatus(1).setPayTime(finalPayTime));
  264. tbBusinessItemService.updateBatchById(items);
  265. }
  266. String a = attach.getA();
  267. if (StrUtil.isNotEmpty(a)) {//充值的======>
  268. AccountChargeBO chargeBO = JSONUtil.toBean(a, AccountChargeBO.class);
  269. tbAccountService.recharge(chargeBO.getId(), chargeBO.getC(), TbChargeRecord.Ch.WECHAT.getType(), money);
  270. }
  271. }
  272. tbOrder.setOrderStatus("SUCCESS")
  273. .setTransactionId(notifyBO.getTransactionId())
  274. .setCompleteDate(notifyBO.getTimeEnd());
  275. tbOrderService.updateById(tbOrder);
  276. }
  277. public String getRedirectUrl(String path, String state) {
  278. String redirectUrl = myConfig.getWebDomain() + path;
  279. String encoderUrl = URLDecoder.decode(redirectUrl, Charset.forName("utf-8"));
  280. String url = wxConfig.getAuthLoginUrl().replace("REDIRECT_URI", encoderUrl);
  281. if (StrUtil.isNotEmpty(state)) {
  282. url = url.replace("STATE", state);
  283. }
  284. log.info("get redirect url:{}", url);
  285. return url;
  286. }
  287. public String getOpenidByCode(String code, String openid) {
  288. String openidUrl = wxConfig.getOpenidUrl().replace("CODE", code);
  289. String resp = HttpUtil.get(openidUrl);
  290. JSONObject result = JSONUtil.parseObj(resp);
  291. log.info("get openid result:{}", resp);
  292. String newOpenid = result.getStr("openid");
  293. if (StrUtil.isNotEmpty(newOpenid)) {
  294. return newOpenid;
  295. }
  296. return openid;
  297. }
  298. public SortedMap getWxConfig(String url) {
  299. Long nowTime = System.currentTimeMillis() / 1000;
  300. String jsTicket = RedisUtil.get(wxConfig.getJsApiTicketKey());
  301. SortedMap<String, Object> params = new TreeMap<>();
  302. params.put("noncestr", RandomUtil.randomString(32));
  303. params.put("jsapi_ticket", jsTicket);
  304. params.put("timestamp", nowTime);
  305. params.put("url", url);
  306. String sign = SecureUtil.sha1(params.toString().substring(1, params.toString().lastIndexOf("}")).replaceAll(", ", "&"));
  307. params.put("sign", sign);
  308. params.put("appId", wxConfig.getAppId());
  309. return params;
  310. }
  311. @Async
  312. public void sendTemplateMsg(String templateId, String openid, MsgDataBO data) {
  313. String accessToken = RedisUtil.get(wxConfig.getAccessTokenKey());
  314. BaseTemplate baseTemplate = new BaseTemplate(openid, templateId, data);
  315. String json = JSON.toJSONString(baseTemplate);
  316. String url = wxConfig.getSendMsgUrl().replace("ACCESS_TOKEN", accessToken);
  317. String resp = HttpUtil.post(url, json);
  318. log.info("send wx msg{},{},{};return :{}", templateId, openid, JSONUtil.toJsonStr(data), resp);
  319. }
  320. @Async
  321. public void sendTemplateMsg(String templateId, String openid, Object data, String detailUrl) {
  322. if (StrUtil.isEmpty(openid)){
  323. return;
  324. }
  325. String accessToken = RedisUtil.get(wxConfig.getAccessTokenKey());
  326. BaseTemplate baseTemplate = new BaseTemplate(openid, templateId, detailUrl, data);
  327. String json = JSON.toJSONString(baseTemplate);
  328. String url = wxConfig.getSendMsgUrl().replace("ACCESS_TOKEN", accessToken);
  329. String resp = HttpUtil.post(url, json);
  330. JSONObject result = JSONUtil.parseObj(resp);
  331. int code = result.getInt("errcode");
  332. log.info("send wx msg{},{},{};return :{}", templateId, openid, JSONUtil.toJsonStr(data), resp);
  333. WxSendMsg wxSendMsg = wxSendMsgService.findByOpenidAndDetailUrl(openid, detailUrl);
  334. if (code != 0) {
  335. if (wxSendMsg == null) {
  336. wxSendMsg = new WxSendMsg();
  337. wxSendMsg.setCreateTime(DateUtil.now());
  338. }
  339. wxSendMsg.setCode(code).setDetailUrl(detailUrl)
  340. .setMsgData(JSONUtil.toJsonStr(data)).setOpenid(openid).setTemplateId(templateId)
  341. .setReturnMsg(result.getStr("errmsg"));
  342. wxSendMsgService.saveOrUpdate(wxSendMsg);
  343. }else if (wxSendMsg != null){
  344. wxSendMsgService.delete(wxSendMsg.getId());
  345. }
  346. }
  347. private String handlerDesc(String desc){
  348. if (StrUtil.isEmpty(desc)){
  349. return "";
  350. }
  351. if (desc.getBytes(Charset.forName("utf-8")).length>128){
  352. desc=StrUtil.sub(desc,0,desc.lastIndexOf("-"));
  353. handlerDesc(desc);
  354. }
  355. return desc;
  356. }
  357. }