WxService.java 17 KB

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