diff --git a/play-admin/pom.xml b/play-admin/pom.xml index 72f433a..0a4847c 100644 --- a/play-admin/pom.xml +++ b/play-admin/pom.xml @@ -94,6 +94,12 @@ jave-nativebin-linux64 + + com.github.binarywang + weixin-java-pay + 4.5.0 + + com.tencentcloudapi tencentcloud-sdk-java-dnspod diff --git a/play-admin/src/main/java/com/starry/admin/modules/weichat/controller/WxPlayController.java b/play-admin/src/main/java/com/starry/admin/modules/weichat/controller/WxPlayController.java index 7697343..eae33a4 100644 --- a/play-admin/src/main/java/com/starry/admin/modules/weichat/controller/WxPlayController.java +++ b/play-admin/src/main/java/com/starry/admin/modules/weichat/controller/WxPlayController.java @@ -3,42 +3,35 @@ package com.starry.admin.modules.weichat.controller; import cn.hutool.core.util.IdUtil; import cn.hutool.core.util.StrUtil; -import cn.hutool.core.util.XmlUtil; -import com.github.wxpay.sdk.WXPayUtil; +import com.alibaba.fastjson2.JSONObject; +import com.github.binarywang.wxpay.bean.notify.WxPayNotifyResponse; +import com.github.binarywang.wxpay.bean.request.WxPayUnifiedOrderRequest; +import com.github.binarywang.wxpay.bean.result.WxPayUnifiedOrderResult; +import com.github.binarywang.wxpay.exception.WxPayException; +import com.github.binarywang.wxpay.service.WxPayService; +import com.github.binarywang.wxpay.util.SignUtils; import com.starry.admin.common.aspect.CustomUserLogin; import com.starry.admin.common.conf.ThreadLocalRequestDetail; import com.starry.admin.common.exception.CustomException; -import com.starry.admin.common.play.wx.WeChatConstants; -import com.starry.admin.common.play.wx.WxCustomPayUtils; -import com.starry.admin.modules.balance.service.IPlayBalanceDetailsInfoService; import com.starry.admin.modules.custom.module.entity.PlayCustomUserInfoEntity; import com.starry.admin.modules.custom.service.IPlayCustomUserInfoService; +import com.starry.admin.modules.order.module.entity.PlayOrderInfoEntity; import com.starry.admin.modules.order.service.IPlayOrderInfoService; import com.starry.admin.modules.platform.entity.SysTenantEntity; import com.starry.admin.modules.platform.service.impl.SysTenantServiceImpl; -import com.starry.admin.modules.weichat.entity.WxPayReturnVo; +import com.starry.admin.modules.weichat.service.WxCustomMpService; import com.starry.admin.utils.SecurityUtils; import com.starry.common.result.R; import com.starry.common.utils.StringUtils; import lombok.extern.slf4j.Slf4j; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RequestParam; -import org.springframework.web.bind.annotation.RestController; +import org.dom4j.Document; +import org.dom4j.DocumentHelper; +import org.dom4j.Element; +import org.springframework.web.bind.annotation.*; import javax.annotation.Resource; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; -import java.io.BufferedReader; -import java.io.InputStream; -import java.io.InputStreamReader; import java.math.BigDecimal; -import java.nio.charset.StandardCharsets; -import java.time.LocalDateTime; -import java.time.ZoneOffset; -import java.util.Map; -import java.util.SortedMap; -import java.util.TreeMap; +import java.util.*; /** * @author admin @@ -48,19 +41,14 @@ import java.util.TreeMap; @RequestMapping("/wx/pay/") public class WxPlayController { - @Resource private SysTenantServiceImpl tenantService; - - @Resource private IPlayCustomUserInfoService customUserInfoService; - - @Resource - private IPlayBalanceDetailsInfoService playBalanceDetailsInfoService; - @Resource private IPlayOrderInfoService orderInfoService; + @Resource + private WxCustomMpService mpService; /** @@ -70,35 +58,67 @@ public class WxPlayController { * @since 2024/5/8 11:25 **/ @GetMapping("/jsCallback") - public void jsCallback(HttpServletRequest request, HttpServletResponse response) throws Exception { - // 读取回调数据 - InputStream inputStream = request.getInputStream(); - StringBuilder sb = new StringBuilder(); - String s; - BufferedReader in = new BufferedReader(new InputStreamReader(inputStream, StandardCharsets.UTF_8)); - while ((s = in.readLine()) != null) { - sb.append(s); - } - in.close(); - inputStream.close(); + public String wxPayNotify(@RequestBody String xmlData) { + log.info("****************接受到微信支付回调:{}", xmlData); + this.dealNotify(xmlData); - // 解析xml成map - Map m = XmlUtil.xmlToMap(sb.toString()); - // 过滤空 设置 TreeMap - SortedMap packageParams = new TreeMap<>(); - for (String parameter : m.keySet()) { - Object parameterValue = m.get(parameter); - String v = ""; - if (null != parameterValue) { - v = parameterValue.toString().trim(); - } - packageParams.put(parameter, v); - } - log.info("packageParams=" + packageParams); - String resXml = ""; - response.getWriter().write(resXml); + return WxPayNotifyResponse.success("成功"); } + private void dealNotify(String xmlData) { + try { + Map orderMap = readStringXmlOut(xmlData); + String outTradeNo = orderMap.get("out_trade_no"); + PlayOrderInfoEntity orderInfoEntity = orderInfoService.getById(outTradeNo); + if (Objects.isNull(orderInfoEntity)) { + log.error("*********未查询到对应的支付记录,订单号:{}", outTradeNo); + return; + } + // TODO如果支付状态不是待支付 + if (!orderInfoEntity.getOrderStatus().equals(RepairStatusEnum.NOT_PAY.name())) { + log.error("*********支付记录状态异常,支付记录:{}", JSONObject.toJSONString(repair)); + return; + } + Date nowDate = new Date(); + repair.setPaySuccessTime(nowDate); + repair.setPayStatus(RepairStatusEnum.PAID.name()); + repair.setStatus(RepairStatusEnum.REPAIRING.name()); + orderInfoService.updateById(orderInfoEntity); + + log.info("*********支付处理完成"); + } catch (Exception e) { + log.error(e.getMessage()); + } + } + //public void jsCallback(HttpServletRequest request, HttpServletResponse response) throws Exception { + // // 读取回调数据 + // InputStream inputStream = request.getInputStream(); + // StringBuilder sb = new StringBuilder(); + // String s; + // BufferedReader in = new BufferedReader(new InputStreamReader(inputStream, StandardCharsets.UTF_8)); + // while ((s = in.readLine()) != null) { + // sb.append(s); + // } + // in.close(); + // inputStream.close(); + // + // // 解析xml成map + // Map m = XmlUtil.xmlToMap(sb.toString()); + // // 过滤空 设置 TreeMap + // SortedMap packageParams = new TreeMap<>(); + // for (String parameter : m.keySet()) { + // Object parameterValue = m.get(parameter); + // String v = ""; + // if (null != parameterValue) { + // v = parameterValue.toString().trim(); + // } + // packageParams.put(parameter, v); + // } + // log.info("packageParams=" + packageParams); + // String resXml = ""; + // response.getWriter().write(resXml); + //} + @CustomUserLogin @GetMapping("/custom/createOrder") @@ -120,18 +140,40 @@ public class WxPlayController { String orderId = IdUtil.fastSimpleUUID(); orderInfoService.createRechargeOrder(orderId, new BigDecimal(totalFee * 1.0 / 100), new BigDecimal(totalFee * 1.0 / 100), customUserInfo.getId()); String body = "树洞充值"; + + WxPayService wxPayService = mpService.getWxPay(); + WxPayUnifiedOrderRequest request = new WxPayUnifiedOrderRequest(); + request.setOpenid(customUserInfo.getOpenid()); + // 订单总金额,单位为分(开发阶段固定设置为支付1分钱) + request.setTotalFee(1); + request.setOutTradeNo(orderId); + request.setTradeType("JSAPI"); + request.setSpbillCreateIp("101.43.206.16"); + request.setNotifyUrl(wxPayService.getConfig().getNotifyUrl()); + request.setBody("船票充值"); + WxPayUnifiedOrderResult orderResult; try { - String nonceStr = WxCustomPayUtils.generateNonceStr(); - Map playRequestParameters = WxCustomPayUtils.getPayRequestParameters(customUserInfo.getOpenid(), entity.getAppId(), entity.getMchId(), orderId, nonceStr, "127.0.0.1", body, SecurityUtils.getTenantId(), totalFee); - log.info(WXPayUtil.mapToXml(playRequestParameters)); - String sign = WxCustomPayUtils.generateSignature(playRequestParameters, entity.getMchKey()); - String prepayId = WxCustomPayUtils.unifiedOrderJsApi(playRequestParameters, sign, entity.getMchKey()); - WxPayReturnVo vo = new WxPayReturnVo(entity.getMchKey(), String.valueOf(LocalDateTime.now().toEpochSecond(ZoneOffset.of("+8"))), nonceStr, WeChatConstants.SignType.MD5.toString(), sign, prepayId, WeChatConstants.NOTIFY_URL); - return R.ok(vo); - } catch (Exception e) { - log.error("创建微信预支付订单失败,error=", e); - throw new CustomException("创建支付订单失败," + e.getMessage()); + orderResult = wxPayService.unifiedOrder(request); + } catch (WxPayException e) { + throw new RuntimeException(e); } + + String prepayId = orderResult.getPrepayId(); + //组合参数构建支付 + Map paySignInfo = new HashMap<>(5); + String timeStamp = String.valueOf(System.currentTimeMillis()); + String nonceStr = "dalfhh241lnandnsklajax"; + paySignInfo.put("appId", wxPayService.getConfig().getAppId()); + paySignInfo.put("nonceStr", nonceStr); + paySignInfo.put("timeStamp", timeStamp); + paySignInfo.put("signType", "MD5"); + paySignInfo.put("package", "prepay_id=" + prepayId); + String[] signInfo = new String[0]; + String paySign = SignUtils.createSign(paySignInfo, "MD5", wxPayService.getConfig().getMchKey(), signInfo); + + //组合支付参数 + JSONObject jsonObject = new JSONObject().fluentPut("appId", wxPayService.getConfig().getAppId()).fluentPut("timeStamp", timeStamp).fluentPut("nonceStr", nonceStr).fluentPut("package", "prepay_id=" + prepayId).fluentPut("signType", "MD5").fluentPut("paySign", paySign); + return R.ok(jsonObject); } @@ -152,7 +194,38 @@ public class WxPlayController { return totalFee; } - public static void main(String[] args) { - + public static Map readStringXmlOut(String xml) { + Map map = new HashMap(); + Document doc; + try { + doc = DocumentHelper.parseText(xml); // 将字符串转为XML + Element rootElt = doc.getRootElement(); // 获取根节点 + List list = rootElt.elements();//获取根节点下所有节点 + for (Element element : list) { //遍历节点 + map.put(element.getName(), element.getText()); //节点的name为map的key,text为map的value + } + } catch (Exception e) { + throw new RuntimeException(e); + } + return map; + // 参数示例 + // + // + // + // + // + // + // + // + // + // + // + // + // + // + // 1 + // + // + // } } diff --git a/play-admin/src/main/java/com/starry/admin/modules/weichat/service/WxCustomMpService.java b/play-admin/src/main/java/com/starry/admin/modules/weichat/service/WxCustomMpService.java index f72cd4c..f144b32 100644 --- a/play-admin/src/main/java/com/starry/admin/modules/weichat/service/WxCustomMpService.java +++ b/play-admin/src/main/java/com/starry/admin/modules/weichat/service/WxCustomMpService.java @@ -1,13 +1,18 @@ package com.starry.admin.modules.weichat.service; import cn.hutool.core.util.StrUtil; +import com.github.binarywang.wxpay.config.WxPayConfig; +import com.github.binarywang.wxpay.service.WxPayService; +import com.github.binarywang.wxpay.service.impl.WxPayServiceImpl; import com.starry.admin.common.exception.CustomException; +import com.starry.admin.common.play.wx.WeChatConstants; import com.starry.admin.modules.platform.entity.SysTenantEntity; import com.starry.admin.modules.platform.service.impl.SysTenantServiceImpl; import com.starry.admin.utils.SecurityUtils; import lombok.extern.slf4j.Slf4j; import me.chanjar.weixin.mp.api.WxMpService; import me.chanjar.weixin.mp.config.impl.WxMpMapConfigImpl; +import org.apache.commons.lang3.StringUtils; import org.springframework.stereotype.Service; import javax.annotation.Resource; @@ -39,5 +44,30 @@ public class WxCustomMpService { return wxMpService.switchoverTo(entity.getAppId()); } + public WxPayService getWxPay() { + String tenantId = SecurityUtils.getTenantId(); + if (StrUtil.isBlankIfStr(tenantId)) { + throw new CustomException("系统错误,租户ID不能为空"); + } + SysTenantEntity entity = tenantService.selectSysTenantByTenantId(tenantId); + if (entity == null) { + throw new CustomException("系统错误,租户ID不能为空"); + } + if (StringUtils.isEmpty(entity.getMchId())) throw new CustomException("商户号不能为空,请联系平台方进行配置"); + WxPayConfig payConfig = new WxPayConfig(); + payConfig.setAppId(StringUtils.trimToNull(entity.getAppId())); + payConfig.setMchId(StringUtils.trimToNull(entity.getMchId())); + payConfig.setMchKey(StringUtils.trimToNull(entity.getMchKey())); + + payConfig.setNotifyUrl(StringUtils.trimToNull(WeChatConstants.NOTIFY_URL)); + payConfig.setTradeType("JSAPI"); + payConfig.setSignType("MD5"); + // 可以指定是否使用沙箱环境 + payConfig.setUseSandboxEnv(false); + WxPayService wxPayService = new WxPayServiceImpl(); + wxPayService.setConfig(payConfig); + return wxPayService; + } + }