package com.pj.api.wx.service; import cn.hutool.core.date.DateUtil; import cn.hutool.core.net.URLDecoder; import cn.hutool.core.util.NumberUtil; import cn.hutool.core.util.RandomUtil; import cn.hutool.core.util.StrUtil; import cn.hutool.core.util.XmlUtil; import cn.hutool.crypto.SecureUtil; import cn.hutool.http.HttpUtil; import cn.hutool.json.JSONObject; import cn.hutool.json.JSONUtil; import com.alibaba.fastjson.JSON; import com.pj.api.wx.WxUtils; import com.pj.api.wx.bo.*; import com.pj.api.wx.vo.PrePayVO; import com.pj.current.config.MyConfig; import com.pj.current.config.PartConfig; import com.pj.current.config.WxConfig; import com.pj.project.tb_account.AutomaticPay; import com.pj.project.tb_account.TbAccountService; import com.pj.project.tb_business.TbBusiness; import com.pj.project.tb_business.TbBusinessService; import com.pj.project.tb_business_car.TbBusinessCar; import com.pj.project.tb_business_car.TbBusinessCarService; import com.pj.project.tb_business_item.TbBusinessItem; import com.pj.project.tb_business_item.TbBusinessItemService; import com.pj.project.tb_charge_record.TbChargeRecord; import com.pj.project.tb_fee_details.TbFeeDetails; import com.pj.project.tb_fee_details.TbFeeDetailsService; import com.pj.project.tb_fee_statistics.TbFeeStatisticsService; import com.pj.project.tb_goods.TbGoods; import com.pj.project.tb_goods.TbGoodsService; import com.pj.project.tb_invoice_order.TbInvoiceOrderService; import com.pj.project.tb_item.TbItem; import com.pj.project.tb_order.TbOrder; import com.pj.project.tb_order.TbOrderService; import com.pj.project.wx_send_msg.WxSendMsg; import com.pj.project.wx_send_msg.WxSendMsgService; import com.pj.project4sp.global.BusinessException; import com.pj.utils.cache.RedisUtil; import lombok.extern.slf4j.Slf4j; import org.springframework.context.annotation.Lazy; import org.springframework.scheduling.annotation.Async; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import javax.annotation.Resource; import javax.servlet.http.HttpServletRequest; import java.math.BigDecimal; import java.net.URLEncoder; import java.nio.charset.Charset; import java.util.*; import java.util.stream.Collectors; @Service @Transactional @Slf4j public class WxService { @Resource @Lazy private TbBusinessService tbBusinessService; @Resource WxConfig wxConfig; @Resource MyConfig myConfig; @Resource PartConfig partConfig; @Resource WxUtils wxUtils; @Resource TbBusinessCarService tbBusinessCarService; @Resource @Lazy TbBusinessItemService tbBusinessItemService; @Resource TbFeeStatisticsService tbFeeStatisticsService; @Resource private TbAccountService tbAccountService; @Resource TbFeeDetailsService tbFeeDetailsService; @Resource private TbOrderService tbOrderService; @Resource private TbGoodsService tbGoodsService; @Resource private WxSendMsgService wxSendMsgService; @Resource private TbInvoiceOrderService tbInvoiceOrderService; @Resource private AutomaticPay automaticPay; /** * 统一下单接口 */ public Map prePay(HttpServletRequest request) throws Exception { String tradeType = request.getParameter("tradeType"); String money = request.getParameter("money"); if (Double.valueOf(money) <= 0) { throw new BusinessException("金额不正确"); } String openid = request.getParameter("openid"); String desc = request.getParameter("desc"); // String openid = "oDWvn5w-hVkzUuKeY7OBXBV_l1rU"; String businessId = request.getParameter("b"); String c = request.getParameter("c"); String a = request.getParameter("a"); Attach atchMap = new Attach(); atchMap.setC(c).setB(businessId).setA(a); String out_trade_no = RandomUtil.randomString(32); Map params = new HashMap<>(); params.put("appid", wxConfig.getAppId()); params.put("mch_id", wxConfig.getMachId()); params.put("openid", openid); params.put("nonce_str", RandomUtil.randomString(32)); params.put("body", handlerDesc(desc)); params.put("out_trade_no", out_trade_no); // params.put("attach", JSONUtil.toJsonStr(atchMap)); String total_free = partConfig.isTestEnv() ? "1" : NumberUtil.mul(money, 100 + "").intValue() + ""; log.info("pay free:{}", total_free); params.put("total_fee", total_free); // params.put("total_fee", "1"); params.put("spbill_create_ip", getIpAddress(request)); params.put("notify_url", myConfig.getDomain() + "/wx/notify"); params.put("trade_type", tradeType); params.put("scene_info", SceneInfoBO.getSceneInfo(myConfig.getDomain(), wxConfig.getTitle())); String sign = wxUtils.sign(params, wxConfig.getKey()); log.info("wx pay sign result:{}", sign); params.put("sign", sign); String prePayUrl = "https://api.mch.weixin.qq.com/pay/unifiedorder"; String xmlParam = XmlUtil.mapToXmlStr(params); log.info("wx pay xml params:{}", xmlParam); String result = HttpUtil.createPost(prePayUrl).header("Content-Type", "text/xml").body(xmlParam) .execute().body(); Map resultMap = XmlUtil.xmlToMap(result); log.info("request pre pay url result:{}", resultMap); PrePayVO vo = new PrePayVO(); if ("SUCCESS".equals(resultMap.get("return_code")) && "SUCCESS".equals(resultMap.get("result_code"))) { log.info("生成预支付订单成功,mweb_url:{}", resultMap.get("mweb_url")); Object webUrl = resultMap.get("mweb_url"); Object prePayId = resultMap.get("prepay_id"); String preId = prePayId == null ? "" : prePayId.toString(); vo.setPayUrl(webUrl == null ? "" : webUrl.toString()) .setPrePayId(preId) .setTradeType(tradeType); Map payParams = getPayParams(openid, preId); payParams.put("outTradeNo", out_trade_no); TbOrder tbOrder = new TbOrder(); tbOrder.setAttach(JSONUtil.toJsonStr(atchMap)) .setOpenid(openid) .setOrderTime(new Date()) .setOutTradeNo(out_trade_no).setPrice(money); return payParams; } throw new Exception("支付信息有误"); } public Map getPayParams(String openid, String prePayId) { Map signMap = new HashMap<>(10); signMap.put("appId", wxConfig.getAppId()); signMap.put("timeStamp", System.currentTimeMillis() / 1000 + ""); signMap.put("nonceStr", RandomUtil.randomString(32)); signMap.put("signType", "MD5"); signMap.put("package", "prepay_id=" + prePayId); String sign = wxUtils.sign(signMap, wxConfig.getKey()); signMap.put("paySign", sign); signMap.put("openId", openid); return signMap; } public Map getPayP(String timeStamp, String nonceStr, String openid, String pack) { Map signMap = new HashMap<>(10); signMap.put("appId", wxConfig.getAppId()); signMap.put("timeStamp", timeStamp); signMap.put("nonceStr", nonceStr); signMap.put("signType", "MD5"); signMap.put("package", pack); String sign = wxUtils.sign(signMap, wxConfig.getKey()); signMap.put("paySign", sign); signMap.put("openId", openid); return signMap; } public static String getIpAddress(HttpServletRequest request) { String ip = request.getHeader("x-forwarded-for"); if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { ip = request.getHeader("Proxy-Client-IP"); } if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { ip = request.getHeader("WL-Proxy-Client-IP"); } if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { ip = request.getRemoteAddr(); } if (ip.contains(",")) { return ip.split(",")[0]; } else { return ip; } } public void WxNotify(NotifyBO notifyBO) { String orderStatus = notifyBO.getTradeStatus(); if (!"SUCCESS".equals(orderStatus) && !"FINISH".equals(orderStatus)) { log.error("支付订单回调失败:{}", JSONUtil.toJsonStr(notifyBO)); return; } String outTradeNo = notifyBO.getOutTradeNo(); if (StrUtil.isNotEmpty(RedisUtil.get(outTradeNo))) { log.error("========订单已处理==========:{}", outTradeNo); return; } RedisUtil.setByMINUTES(outTradeNo, DateUtil.now(), 10); TbOrder tbOrder = tbOrderService.findByOutTradeNo(outTradeNo); if (tbOrder == null) { log.error("========订单不存在==========:{}", outTradeNo); return; } String payopenid=tbOrder.getOpenid(); String total_fee = notifyBO.getTotalFee(); BigDecimal money = new BigDecimal(total_fee).divide(new BigDecimal(100), 2, BigDecimal.ROUND_UP); String attachStr = tbOrder.getAttach(); String transactionId = notifyBO.getTransactionId(); Date now = new Date(); String timeEnd = notifyBO.getTimeEnd(); Date payTime = now; if (StrUtil.isNotEmpty(timeEnd)) { payTime = DateUtil.parse(timeEnd, "yyyyMMddHHmmss"); } if (StrUtil.isNotEmpty(attachStr)) { Attach attach = JSONUtil.toBean(attachStr, Attach.class); List cars = JSONUtil.toList(attach.getC(), PriceBO.class); for (PriceBO bo1 : cars) { TbBusinessCar car = tbBusinessCarService.getById(bo1.getId()); BigDecimal price = bo1.getP(); if (price == null || price.doubleValue() <= 0) { log.error("付款金额不正确:{}", JSONUtil.toJsonStr(notifyBO)); continue; } car.setPay(1).setMoney(price).setPayTime(payTime).setPayType(TbBusinessCar.PayTypeEnum.HAS_PAY_TYPE.getType()); tbBusinessCarService.updateById(car); //支付完之后要解绑 automaticPay.unbindRun(car.getCarNo()); } tbFeeDetailsService.chargeParkFee(cars, transactionId, outTradeNo, payTime, TbBusiness.PayType.WX_PAY);//添加cars的收费明细 String businessId = attach.getB(); Date finalPayTime = payTime; if (StrUtil.isNotEmpty(businessId)) { List businessIds = StrUtil.splitTrim(businessId, ","); List businessList = tbBusinessService.listByIds(businessIds); businessList=businessList.stream().filter(tbBusiness -> tbBusiness.getPayStatus()!=TbBusiness.PayStatus.HAS_PAY_CONFIRM.getCode()).collect(Collectors.toList()); businessIds=businessList.stream().map(TbBusiness::getId).collect(Collectors.toList()); List items = tbBusinessItemService.findByBusinessIdList(businessIds); items=items.stream().filter(item -> item.getPayStatus()==0).collect(Collectors.toList()); for (TbBusiness tbBusiness : businessList) { tbBusiness.setPayTime(payTime).setPayType(3).setConfirmInput(1).setConfirmInputTime(payTime) .setPayMoney(tbBusiness.getItemPrice()).setPayOpenid(payopenid) .setPayNo(transactionId); tbBusiness.setPayStatus(TbBusiness.PayStatus.HAS_PAY_CONFIRM.getCode()); tbBusinessService.updateById(tbBusiness); TbGoods tbGoods = tbGoodsService.getById(tbBusiness.getGoodsId()); List carList = tbBusinessCarService.findOtherBusinessCar(businessId); carList.forEach(tbBusinessCar -> { String carType = tbBusinessCar.getCarType(); // if (TbItem.ItemTypeEnum.EMPTY_TYPE.getType().equals(carType) && tbGoods.getChinaCarPay() == 0 || TbItem.ItemTypeEnum.WEIGHT_TYPE.getType().equals(carType) && tbGoods.getVietnamCarPay() == 0) { tbBusinessCar.setPay(1).setPayTime(finalPayTime).setPayOpenid(payopenid); tbBusinessCarService.updateById(tbBusinessCar); } }); } tbFeeDetailsService.chargeBusinessFee(items, transactionId, outTradeNo, payTime, TbBusiness.PayType.WX_PAY);//添加items的收费明细 items.forEach(tbBusinessItem -> tbBusinessItem.setPayStatus(1).setPayTime(finalPayTime)); tbBusinessItemService.updateBatchById(items); } tbFeeStatisticsService.addOrUpdateStatistic(payTime);//更新当前日期的日统计 String a = attach.getA(); if (StrUtil.isNotEmpty(a)) {//充值的======> AccountChargeBO chargeBO = JSONUtil.toBean(a, AccountChargeBO.class); tbAccountService.recharge(chargeBO.getId(), chargeBO.getC(), TbChargeRecord.Ch.WECHAT.getType(), money); } } tbOrder.setOrderStatus(orderStatus) .setTransactionId(notifyBO.getTransactionId()) .setCompleteDate(notifyBO.getTimeEnd()); tbOrderService.updateById(tbOrder); tbInvoiceOrderService.addT(transactionId);//生成开票订单 } public String getRedirectUrl(String path, String state) { String redirectUrl = myConfig.getWebDomain() + path; String encoderUrl = URLEncoder.encode(redirectUrl); String url = wxConfig.getAuthLoginUrl().replace("REDIRECT_URI", encoderUrl); if (StrUtil.isNotEmpty(state)) { url = url.replace("STATE", state); } log.info("get redirect url:{}", url); return url; } public String getOpenidByCode(String code, String openid) { String openidUrl = wxConfig.getOpenidUrl().replace("CODE", code); String resp = HttpUtil.get(openidUrl); JSONObject result = JSONUtil.parseObj(resp); log.info("get openid result:{}", resp); String newOpenid = result.getStr("openid"); if (StrUtil.isNotEmpty(newOpenid)) { return newOpenid; } return openid; } public SortedMap getWxConfig(String url) { Long nowTime = System.currentTimeMillis() / 1000; String jsTicket = RedisUtil.get(wxConfig.getJsApiTicketKey()); SortedMap params = new TreeMap<>(); params.put("noncestr", RandomUtil.randomString(32)); params.put("jsapi_ticket", jsTicket); params.put("timestamp", nowTime); params.put("url", url); String sign = SecureUtil.sha1(params.toString().substring(1, params.toString().lastIndexOf("}")).replaceAll(", ", "&")); params.put("sign", sign); params.put("appId", wxConfig.getAppId()); return params; } @Async public void sendTemplateMsg(String templateId, String openid, MsgDataBO data) { String accessToken = RedisUtil.get(wxConfig.getAccessTokenKey()); BaseTemplate baseTemplate = new BaseTemplate(openid, templateId, data); String json = JSON.toJSONString(baseTemplate); String url = wxConfig.getSendMsgUrl().replace("ACCESS_TOKEN", accessToken); String resp = HttpUtil.post(url, json); log.info("send wx msg{},{},{};return :{}", templateId, openid, JSONUtil.toJsonStr(data), resp); } @Async public void sendTemplateMsg(String templateId, String openid, Object data, String detailUrl) { if (StrUtil.isEmpty(openid)) { return; } String accessToken = RedisUtil.get(wxConfig.getAccessTokenKey()); BaseTemplate baseTemplate = new BaseTemplate(openid, templateId, detailUrl, data); String json = JSON.toJSONString(baseTemplate); String url = wxConfig.getSendMsgUrl().replace("ACCESS_TOKEN", accessToken); String resp = HttpUtil.post(url, json); JSONObject result = JSONUtil.parseObj(resp); int code = result.getInt("errcode"); log.info("send wx msg{},{},{};return :{}", templateId, openid, JSONUtil.toJsonStr(data), resp); WxSendMsg wxSendMsg = wxSendMsgService.findByOpenidAndDetailUrl(openid, detailUrl); if (code == 42001) {//token过期导致 if (wxSendMsg == null) { wxSendMsg = new WxSendMsg(); wxSendMsg.setCreateTime(DateUtil.now()); } wxSendMsg.setCode(code).setDetailUrl(detailUrl) .setMsgData(JSONUtil.toJsonStr(data)).setOpenid(openid).setTemplateId(templateId) .setReturnMsg(result.getStr("errmsg")); wxSendMsgService.saveOrUpdate(wxSendMsg); } else if (wxSendMsg != null) { wxSendMsgService.delete(wxSendMsg.getId()); } } private String handlerDesc(String desc) { if (StrUtil.isEmpty(desc)) { return ""; } if (desc.getBytes(Charset.forName("utf-8")).length > 128) { desc = StrUtil.sub(desc, 0, desc.lastIndexOf("-")); desc = handlerDesc(desc); } return desc; } }