소스 검색

fix(payment): 修复支付金额类型及泛型使用问题

- 将amount_fee方法返回类型由Integer改为String,解决金额精度问题
- 修改amount_fee内部实现,避免金额向下取整为整数
- 更新WFTOrderService中总金额设置逻辑,调用amount_fee获取正确金额字符串
- 修正PayUtill中Map泛型使用,移除参数化类型以匹配需求
- 调整返回结果中状态码的设置方式,统一使用字符串形式"200"
wzq 2 일 전
부모
커밋
f0c1e20219

+ 36 - 68
src/main/java/com/zsElectric/boot/business/controller/applet/AppletWFTOrderController.java

@@ -2,8 +2,6 @@ package com.zsElectric.boot.business.controller.applet;
 
 import com.zsElectric.boot.business.model.form.applet.AppLevelOrderForm;
 import com.zsElectric.boot.business.model.form.applet.AppUserPayForm;
-import com.zsElectric.boot.business.model.vo.applet.WFTCloseOrderVO;
-import com.zsElectric.boot.business.model.vo.applet.WFTOrderVO;
 import com.zsElectric.boot.business.service.WFTOrderService;
 import com.zsElectric.boot.common.annotation.Log;
 import com.zsElectric.boot.common.enums.LogModuleEnum;
@@ -12,7 +10,6 @@ import com.zsElectric.boot.core.web.Result;
 import io.swagger.v3.oas.annotations.Operation;
 import io.swagger.v3.oas.annotations.Parameter;
 import io.swagger.v3.oas.annotations.tags.Tag;
-import jakarta.servlet.ServletException;
 import jakarta.servlet.http.HttpServletRequest;
 import jakarta.validation.Valid;
 import lombok.RequiredArgsConstructor;
@@ -116,11 +113,8 @@ public class AppletWFTOrderController {
      */
     @Operation(summary = "订单查询")
     @GetMapping("/query/{orderNo}")
-    public Result<Boolean> queryOrder(
-            @Parameter(description = "商户订单号") @PathVariable String orderNo) {
-
+    public Result<Boolean> queryOrder(@Parameter(description = "商户订单号") @PathVariable String orderNo) {
         Boolean result = wftOrderService.queryOrder(orderNo);
-
         return Result.success(result);
     }
 
@@ -136,67 +130,41 @@ public class AppletWFTOrderController {
     public Result<Boolean> closeOrder(@Parameter(description = "商户订单号") @PathVariable String orderNo) {
         return Result.success(wftOrderService.closeOrder(orderNo));
     }
-//
-//    /**
-//     * 申请退款
-//     *
-//     * 业务功能:
-//     * 商户针对某一个已经成功支付的订单发起退款,操作结果在同一会话中同步返回。
-//     *
-//     * 退款方式:
-//     * 目前只支持原路返回退款。退到银行卡则是非实时的,每个银行的处理速度不同,
-//     * 一般发起退款后1-3个工作日内到账。
-//     *
-//     * 重要说明:
-//     * 1. 一笔交易单可以多次退款,只要退款累计金额不超过交易单支付总额
-//     * 2. 退款申请单号唯一确定一次退款,商户必须保证退款申请单的唯一性
-//     * 3. 一笔退款失败后重新提交,请使用原商户退款单号,不要更换
-//     * 4. result_code为0表示退款申请接收成功,实际的退款结果根据退款查询接口查询
-//     * 5. 微信支付订单的部分退款次数不能超过50次
-//     *
-//     * @param form 退款请求表单
-//     * @return 退款结果
-//     */
-//    @Operation(summary = "申请退款")
-//    @PostMapping("/refund")
-//    @Log(value = "申请退款", module = LogModuleEnum.APP_ORDER)
-//    public Result<WFTRefundVO> refundOrder(@Valid @RequestBody WFTRefundForm form) {
-//
-//        WFTRefundVO result = wftOrderService.refundOrderVO(
-//                form.getOutTradeNo(),
-//                form.getTransactionId(),
-//                form.getOutRefundNo(),
-//                form.getTotalFee(),
-//                form.getRefundFee(),
-//                form.getOpUserId(),
-//                form.getAttach());
-//
-//        return Result.success(result);
-//    }
-//
-//    /**
-//     * 查询退款
-//     *
-//     * 业务功能:
-//     * 提交退款申请后,通过调用该接口查询退款状态。
-//     * 退款有一定延时,请在3个工作日后重新查询退款状态。
-//     *
-//     * 参数优先级:
-//     * refund_id > out_refund_no > transaction_id > out_trade_no
-//     *
-//     * 退款状态:
-//     * - SUCCESS:退款成功
-//     * - FAIL:退款失败
-//     * - PROCESSING:退款处理中
-//     * - CHANGE:转入代发,退款到银行发现用户的卡作废或者冻结了,导致原路退款银行卡失败,
-//     *          资金回流到商户的现金帐号,需要商户人工干预,通过线下或者平台转账的方式进行退款
-//     *
-//     * @param outTradeNo 商户订单号(与transactionId/outRefundNo/refundId四选一)
-//     * @param transactionId 平台订单号(与outTradeNo/outRefundNo/refundId四选一)
-//     * @param outRefundNo 商户退款单号(与outTradeNo/transactionId/refundId四选一)
-//     * @param refundId 平台退款单号(与outTradeNo/transactionId/outRefundNo四选一,优先级最高)
-//     * @return 退款查询结果
-//     */
+
+    /**
+     * 账户退款
+     * @return
+     */
+    @Operation(summary = "账户退款")
+    @PutMapping("/refundOrder")
+    @Log(value = "账户退款", module = LogModuleEnum.APP_ORDER)
+    public Result<String> refundOrder() throws Exception {
+        return Result.success(wftOrderService.refundOrder());
+    }
+
+    /**
+     * 查询退款
+     *
+     * 业务功能:
+     * 提交退款申请后,通过调用该接口查询退款状态。
+     * 退款有一定延时,请在3个工作日后重新查询退款状态。
+     *
+     * 参数优先级:
+     * refund_id > out_refund_no > transaction_id > out_trade_no
+     *
+     * 退款状态:
+     * - SUCCESS:退款成功
+     * - FAIL:退款失败
+     * - PROCESSING:退款处理中
+     * - CHANGE:转入代发,退款到银行发现用户的卡作废或者冻结了,导致原路退款银行卡失败,
+     *          资金回流到商户的现金帐号,需要商户人工干预,通过线下或者平台转账的方式进行退款
+     *
+     * @param outTradeNo 商户订单号(与transactionId/outRefundNo/refundId四选一)
+     * @param transactionId 平台订单号(与outTradeNo/outRefundNo/refundId四选一)
+     * @param outRefundNo 商户退款单号(与outTradeNo/transactionId/refundId四选一)
+     * @param refundId 平台退款单号(与outTradeNo/transactionId/outRefundNo四选一,优先级最高)
+     * @return 退款查询结果
+     */
 //    @Operation(summary = "查询退款")
 //    @GetMapping("/refund/query")
 //    public Result<WFTRefundQueryVO> queryRefund(

+ 1 - 2
src/main/java/com/zsElectric/boot/business/model/form/applet/AppInvokeChargeForm.java

@@ -31,12 +31,11 @@ public class AppInvokeChargeForm implements Serializable {
     private Long couponId;
 
     @Schema(description = "设备认证流水号")
-    @NotBlank(message = "设备认证流水号不能为空")
     private String equipAuthSeq;
 
     @Schema(description = "充电设备接口编码")
     @NotBlank(message = "充电设备接口编码不能为空")
-    private Long connectorInfoId;
+    private String connectorInfoId;
 
     @Schema(description = "充电设备接口编码")
     @NotBlank(message = "充电设备接口编码不能为空")

+ 270 - 60
src/main/java/com/zsElectric/boot/business/service/WFTOrderService.java

@@ -1,12 +1,12 @@
 package com.zsElectric.boot.business.service;
 
+import cn.hutool.core.collection.CollUtil;
 import cn.hutool.core.util.ObjectUtil;
 import com.baomidou.mybatisplus.core.toolkit.Wrappers;
-import com.zsElectric.boot.business.mapper.RechargeLevelMapper;
-import com.zsElectric.boot.business.mapper.UserInfoMapper;
-import com.zsElectric.boot.business.mapper.UserOrderInfoMapper;
-import com.zsElectric.boot.business.model.entity.RechargeLevel;
-import com.zsElectric.boot.business.model.entity.UserOrderInfo;
+import com.google.gson.Gson;
+import com.google.gson.JsonObject;
+import com.zsElectric.boot.business.mapper.*;
+import com.zsElectric.boot.business.model.entity.*;
 import com.zsElectric.boot.business.model.form.applet.AppLevelOrderForm;
 import com.zsElectric.boot.business.model.form.applet.AppUserPayForm;
 import com.zsElectric.boot.business.model.vo.applet.WFTOrderVO;
@@ -14,6 +14,7 @@ import com.zsElectric.boot.business.model.vo.applet.WechatPayParamsVO;
 import com.zsElectric.boot.common.constant.SystemConstants;
 import com.zsElectric.boot.core.exception.BusinessException;
 import com.zsElectric.boot.core.pay.WFT.WFTConstants;
+import com.zsElectric.boot.core.pay.WechatUrlConstants;
 import com.zsElectric.boot.core.pay.swiftpass.config.SwiftpassConfig;
 import com.zsElectric.boot.core.pay.swiftpass.util.PayUtill;
 import com.zsElectric.boot.core.pay.swiftpass.util.SignUtil;
@@ -32,6 +33,8 @@ import java.io.IOException;
 import java.math.BigDecimal;
 import java.math.RoundingMode;
 import java.text.SimpleDateFormat;
+import java.time.LocalDateTime;
+import java.time.format.DateTimeFormatter;
 import java.util.*;
 
 /**
@@ -57,6 +60,15 @@ public class WFTOrderService {
     @Resource
     protected SwiftpassConfig swiftpassConfig;
 
+    @Resource
+    private UserAccountService userAccountService;
+
+    @Resource
+    private ChargeOrderInfoMapper chargeOrderInfoMapper;
+
+    @Resource
+    private UserRefundsOrderInfoMapper userRefundsOrderInfoMapper;
+
 
     /**
      * 创建商户订单号
@@ -89,17 +101,35 @@ public class WFTOrderService {
         return cny.multiply(b2).setScale(0, RoundingMode.DOWN).toString();
     }
 
+    /**
+     * 金额分转元
+     *
+     * @param fen 分
+     * @return 元(保留两位小数)
+     */
+    public BigDecimal fee_amount(Integer fen) {
+        if (fen == null) {
+            return BigDecimal.ZERO;
+        }
+        return new BigDecimal(fen).divide(new BigDecimal("100"), 2, RoundingMode.HALF_UP);
+    }
+
 
     public AppUserPayForm createOrder(@Valid AppLevelOrderForm appLevelOrderForm) {
         Long userId = SecurityUtils.getUserId();
         String userOpenId = userInfoMapper.getAppletUserInfo(userId).getOpenid();
         String orderNo = createOrderNo("SP", userId);
+
+        //查询档位
+        RechargeLevel level = rechargeLevelMapper.selectById(appLevelOrderForm.getLevelId());
+
         //创建订单
         UserOrderInfo orderInfo = new UserOrderInfo();
         orderInfo.setUserId(userId);
         orderInfo.setOrderNo(orderNo);
         orderInfo.setLevelId(appLevelOrderForm.getLevelId());
         orderInfo.setOpenid(userOpenId);
+        orderInfo.setOrderMoney(level.getMoney());
 
         userOrderInfoMapper.insert(orderInfo);
 
@@ -107,9 +137,6 @@ public class WFTOrderService {
         AppUserPayForm payForm = new AppUserPayForm();
         payForm.setOrderId(orderInfo.getId()).setOrderNo(orderNo);
 
-        //查询档位
-        RechargeLevel level = rechargeLevelMapper.selectById(appLevelOrderForm.getLevelId());
-
         PayUtill wx = new PayUtill();
 
         /**
@@ -234,39 +261,6 @@ public class WFTOrderService {
         }
     }
 
-    /**
-     * 将威富通支付返回的Map转换为WechatPayParamsVO
-     *
-     * @param wxMap 威富通返回的支付参数Map
-     * @return WechatPayParamsVO
-     */
-    private WechatPayParamsVO convertMapToWechatPayParamsVO(Map<String, Object> wxMap) {
-        WechatPayParamsVO vo = new WechatPayParamsVO();
-        // 威富通返回的字段名可能不同,需要根据实际返回调整
-        if (wxMap.get("appId") != null ) {
-            vo.setAppId(wxMap.get("appId").toString());
-        }
-        if (wxMap.get("timeStamp") != null ) {
-            vo.setTimeStamp(wxMap.get("timeStamp").toString());
-        }
-        if (wxMap.get("nonceStr") != null ) {
-            vo.setNonceStr(wxMap.get("nonceStr").toString());
-        }
-        if (wxMap.get("package") != null ) {
-            vo.setPackageValue(wxMap.get("package").toString());
-        }
-        if (wxMap.get("paySign") != null ) {
-            vo.setPaySign(wxMap.get("paySign").toString());
-        }
-        if (wxMap.get("signType") != null ) {
-            vo.setSignType(wxMap.get("signType").toString());
-        }
-        if (wxMap.get("out_trade_no") != null) {
-            vo.setOutTradeNo(wxMap.get("out_trade_no").toString());
-        }
-        return vo;
-    }
-
     public Boolean closeOrder(String orderNo) {
         UserOrderInfo userOrderInfo = userOrderInfoMapper.selectOne(Wrappers.lambdaQuery(UserOrderInfo.class).eq(UserOrderInfo::getOrderNo, orderNo).last("limit 1"));
         if(ObjectUtil.isNull(userOrderInfo)){
@@ -297,6 +291,7 @@ public class WFTOrderService {
      * @param request HTTP请求对象
      * @return "success" 表示处理成功,"fail" 表示处理失败
      */
+    @Transactional(rollbackFor = Exception.class)
     public String handlePayNotify(HttpServletRequest request) {
         log.info("========== 开始处理威富通支付通知 ==========");
 
@@ -355,10 +350,14 @@ public class WFTOrderService {
             }
 
             // 7. 提取订单相关信息
-            String outTradeNo = notifyData.get("out_trade_no");        // 商户订单号
+            String orderNo = notifyData.get("out_trade_no"); //商户订单号
+            String outTradeNo = notifyData.get("out_transaction_id");        // 第三方订单号
             String transactionId = notifyData.get("transaction_id");    // 平台订单号
             String totalFeeStr = notifyData.get("total_fee");           // 总金额(分)
+            BigDecimal payMoney = fee_amount(Integer.parseInt(totalFeeStr));
             String timeEnd = notifyData.get("time_end");                // 支付完成时间
+            //转localDateTime
+            LocalDateTime payTime = LocalDateTime.parse(timeEnd, DateTimeFormatter.ofPattern("yyyyMMddHHmmss"));
             String openid = notifyData.get("openid");                   // 用户标识
             String attach = notifyData.get("attach");                   // 附加信息
 
@@ -374,12 +373,12 @@ public class WFTOrderService {
             // 8. 查询订单并加锁(防止并发)
             UserOrderInfo orderInfo = userOrderInfoMapper.selectOne(
                     Wrappers.lambdaQuery(UserOrderInfo.class)
-                            .eq(UserOrderInfo::getOrderNo, outTradeNo)
+                            .eq(UserOrderInfo::getOrderNo, orderNo)
                             .last("FOR UPDATE")  // 数据库行锁
             );
 
             if (orderInfo == null) {
-                log.error("订单不存在: out_trade_no={}", outTradeNo);
+                log.error("订单不存在: out_trade_no={}", orderNo);
                 return "fail";
             }
 
@@ -391,13 +390,7 @@ public class WFTOrderService {
             }
 
             // 10. 验证订单金额
-            RechargeLevel level = rechargeLevelMapper.selectById(orderInfo.getLevelId());
-            if (level == null) {
-                log.error("档位不存在: level_id={}", orderInfo.getLevelId());
-                return "fail";
-            }
-
-            String expectedFee = amount_fee(level.getMoney());
+            String expectedFee = amount_fee(orderInfo.getOrderMoney());
             if (!expectedFee.equals(totalFeeStr)) {
                 log.error("订单金额不匹配: expected={}, actual={}", expectedFee, totalFeeStr);
                 return "fail";
@@ -406,8 +399,9 @@ public class WFTOrderService {
             // 11. 更新订单状态
             orderInfo.setOrderStatus(SystemConstants.STATUS_TWO);  // 已支付
             orderInfo.setTransactionId(transactionId);              // 平台订单号
-            orderInfo.setPayMoney(level.getMoney());                // 支付金额
-            orderInfo.setOutTradeNo(transactionId);                 // 第三方订单号
+            orderInfo.setPayMoney(payMoney); // 支付金额
+            orderInfo.setOutTradeNo(outTradeNo);                 // 第三方订单号
+            orderInfo.setPayTime(payTime);
             // 解析支付时间
             if (timeEnd != null && timeEnd.length() == 14) {
                 try {
@@ -428,10 +422,8 @@ public class WFTOrderService {
             log.info("订单支付成功: out_trade_no={}, transaction_id={}, total_fee={}",
                     outTradeNo, transactionId, totalFeeStr);
 
-            // TODO: 这里可以添加业务逻辑,例如:
-            // - 发送支付成功通知
-            // - 更新用户余额/会员等级
-            // - 记录支付日志
+            //支付成功业务处理
+            successPayOrder(orderInfo, transactionId, payTime, payMoney);
 
             return "success";
 
@@ -443,6 +435,95 @@ public class WFTOrderService {
         }
     }
 
+    /**
+     * 支付成功业务处理
+     *
+     * @param orderInfo
+     * @param transactionId
+     * @param payTime
+     * @param orderMoney
+     * @throws Exception
+     */
+    @Transactional(rollbackFor = Exception.class)
+    protected void successPayOrder(UserOrderInfo orderInfo, String transactionId, LocalDateTime payTime, BigDecimal orderMoney) throws Exception {
+        //修改订单状态为已支付
+        orderInfo.setOrderStatus(SystemConstants.STATUS_TWO);
+        orderInfo.setPayTime(payTime);
+        orderInfo.setTransactionId(transactionId);
+        orderInfo.setPayMoney(orderMoney);
+        userOrderInfoMapper.updateById(orderInfo);
+
+        //账户变动及日志记录
+        Long userId = orderInfo.getUserId();
+        UserAccount userAccount = userAccountService.updateAccountBalanceAndLog(
+                userId,
+                orderMoney,
+                SystemConstants.CHANGE_TYPE_ADD,
+                SystemConstants.ACCOUNT_LOG_PAY_NOTE,
+                orderInfo.getId()
+        );
+
+        //平订单超充金额(使用更新后的账户信息)
+        orderBackTax(userId, userAccount);
+    }
+
+    private void orderBackTax(Long userId, UserAccount userAccount) {
+        //查询超充订单
+        List<ChargeOrderInfo> chargeOrderInfoList = chargeOrderInfoMapper.selectList(Wrappers.<ChargeOrderInfo>lambdaQuery()
+                .eq(ChargeOrderInfo::getUserId, userId)
+                .eq(ChargeOrderInfo::getMaspStatus, SystemConstants.STATUS_ONE)
+                .orderByAsc(ChargeOrderInfo::getCreateTime)
+        );
+        if (CollUtil.isNotEmpty(chargeOrderInfoList)) {
+            //余额减去超充金额(使用更新后的余额)
+            BigDecimal balance = userAccount.getBalance();
+            for (ChargeOrderInfo chargeOrderInfo : chargeOrderInfoList) {
+
+                if (balance.compareTo(BigDecimal.ZERO) > 0) {
+                    BigDecimal deductAmount; // 本次扣除金额
+
+                    if (balance.compareTo(chargeOrderInfo.getTotalMaspMoney()) < 0) {
+                        //余额不足,部分补缴
+                        deductAmount = balance;
+                        chargeOrderInfo.setAlreadyMaspMoney(deductAmount);
+                        chargeOrderInfo.setMaspDesc("部分补缴");
+                        chargeOrderInfo.setMaspStatus(SystemConstants.STATUS_TWO);
+                        chargeOrderInfoMapper.updateById(chargeOrderInfo);
+
+                        //账户变动及日志记录
+                        userAccountService.updateAccountBalanceAndLog(
+                                userId,
+                                deductAmount.negate(),
+                                SystemConstants.CHANGE_TYPE_REDUCE,
+                                SystemConstants.ACCOUNT_LOG_BACK_TAX_NOTE,
+                                chargeOrderInfo.getId()
+                        );
+
+                        balance = BigDecimal.ZERO;
+                        continue;
+                    }
+                    //余额足够,完成补缴
+                    deductAmount = chargeOrderInfo.getTotalMaspMoney();
+                    balance = balance.subtract(deductAmount);
+                    chargeOrderInfo.setAlreadyMaspMoney(deductAmount);
+                    chargeOrderInfo.setMaspDesc("完成补缴");
+                    chargeOrderInfo.setMaspStatus(SystemConstants.STATUS_TWO);
+                    chargeOrderInfoMapper.updateById(chargeOrderInfo);
+
+                    //账户变动及日志记录
+                    userAccountService.updateAccountBalanceAndLog(
+                            userId,
+                            deductAmount.negate(),
+                            SystemConstants.CHANGE_TYPE_REDUCE,
+                            SystemConstants.ACCOUNT_LOG_BACK_TAX_NOTE,
+                            chargeOrderInfo.getId()
+                    );
+                }
+            }
+        }
+
+    }
+
     /**
      * 订单是否支付成功查询
      *
@@ -494,7 +575,7 @@ public class WFTOrderService {
             // 5. 调用威富通订单查询API
             Map<String, String> queryResult = queryOrderFromWFT(queryParams);
 
-            if (queryResult == null || !"200".equals(queryResult.get("status"))) {
+            if (!"200".equals(queryResult.get("status"))) {
                 log.error("查询订单失败: orderNo={}, result={}", orderNo, queryResult);
                 return false;
             }
@@ -514,9 +595,15 @@ public class WFTOrderService {
             // 8. 如果支付成功但本地订单状态未更新,同步更新订单状态
             if (isPaid && !SystemConstants.STATUS_TWO.equals(orderInfo.getOrderStatus())) {
                 log.info("同步更新订单支付状态: orderNo={}", orderNo);
-                syncOrderStatus(orderInfo, queryResult);
+                String transactionId = queryResult.get("transaction_id");    // 平台订单号
+                String totalFeeStr = queryResult.get("total_fee");           // 总金额(分)
+                BigDecimal payMoney = fee_amount(Integer.parseInt(totalFeeStr));
+                String timeEnd = queryResult.get("time_end");                // 支付完成时间
+                //转localDateTime
+                LocalDateTime payTime = LocalDateTime.parse(timeEnd, DateTimeFormatter.ofPattern("yyyyMMddHHmmss"));
+                //支付成功业务处理
+                successPayOrder(orderInfo, transactionId, payTime, payMoney);
             }
-
             return isPaid;
 
         } catch (Exception e) {
@@ -680,4 +767,127 @@ public class WFTOrderService {
             throw e;
         }
     }
+
+    /**
+     * 账户退款
+     *
+     * @return
+     */
+    @Transactional(rollbackFor = Exception.class)
+    public String refundOrder() throws Exception {
+
+        //查询账户余额
+        UserAccount userAccount =
+                userAccountService.getOne(Wrappers.<UserAccount>lambdaQuery().eq(UserAccount::getUserId, SecurityUtils.getUserId()).last("limit 1"));
+        if (userAccount.getBalance().compareTo(BigDecimal.ZERO) == 0) {
+            return  "账户余额为 0";
+        }
+        BigDecimal refundMoney = userAccount.getBalance();
+
+        //查询一年内已支付的所有券订单
+        List<UserOrderInfo> userOrderInfoList = userOrderInfoMapper.selectList(Wrappers.<UserOrderInfo>lambdaQuery()
+                .eq(UserOrderInfo::getOrderStatus, SystemConstants.STATUS_TWO)
+                .between(UserOrderInfo::getCreateTime, LocalDateTime.now().minusYears(1), LocalDateTime.now())
+        );
+
+        if (CollUtil.isEmpty(userOrderInfoList)) {
+            log.info("当前用户一年内未支付任何券订单,无法进行退款操作!");
+            throw new BusinessException("无法进行退款操作,请联系客服处理!");
+        }
+        for (UserOrderInfo userOrderInfo : userOrderInfoList) {
+            if(refundMoney.compareTo(BigDecimal.ZERO) == 0){
+                break;
+            }
+            if ((userOrderInfo.getOrderMoney().subtract(userOrderInfo.getRefundMoney())).compareTo(refundMoney) > 0) {
+                //退款金额大于订单金额,则直接退退款金额
+                refundOrder(userOrderInfo,refundMoney, "账户退款", SystemConstants.STATUS_ONE);
+                //账户变动及日志记录
+                userAccountService.updateAccountBalanceAndLog(
+                        SecurityUtils.getUserId(),
+                        refundMoney,
+                        SystemConstants.CHANGE_TYPE_REDUCE,
+                        SystemConstants.ACCOUNT_LOG_REFUND_NOTE,
+                        userOrderInfo.getId()
+                );
+                //修改订单状态
+                userOrderInfo.setOrderStatus(SystemConstants.STATUS_FOUR);
+                userOrderInfoMapper.updateById(userOrderInfo);
+                refundMoney = BigDecimal.ZERO;
+                break;
+            }
+            if ((userOrderInfo.getOrderMoney().subtract(userOrderInfo.getRefundMoney())).compareTo(refundMoney) < 0) {
+                //退款金额小于订单金额,则先退订单金额
+                refundOrder(userOrderInfo,userOrderInfo.getOrderMoney().subtract(userOrderInfo.getRefundMoney()), "账户退款", SystemConstants.STATUS_ONE);
+                //账户变动及日志记录
+                userAccountService.updateAccountBalanceAndLog(
+                        SecurityUtils.getUserId(),
+                        userOrderInfo.getOrderMoney().subtract(userOrderInfo.getRefundMoney()),
+                        SystemConstants.CHANGE_TYPE_REDUCE,
+                        SystemConstants.ACCOUNT_LOG_REFUND_NOTE,
+                        userOrderInfo.getId()
+                );
+                //修改订单状态
+                userOrderInfo.setOrderStatus(SystemConstants.STATUS_FOUR);
+                userOrderInfoMapper.updateById(userOrderInfo);
+                refundMoney = refundMoney.subtract(userOrderInfo.getOrderMoney());
+            }
+        }
+        return "账户退款,预计3个工作日内分一笔或多笔退还!如未收到,请联系客服!";
+    }
+
+    public void refundOrder(UserOrderInfo userOrderInfo, BigDecimal refundAmount, String reason, Integer type) throws Exception {
+        log.info("进入退款接口------>");
+        log.info("执行操作的 原支付交易对应的商户订单号:{}", userOrderInfo.getOrderNo());
+
+        //退款单号
+        String out_refund_no = createOrderNo("TK",userOrderInfo.getId());
+        // 创建退款订单
+        UserRefundsOrderInfo userRefundsOrderInfo = new UserRefundsOrderInfo();
+        userRefundsOrderInfo.setOrderId(userOrderInfo.getId());
+        userRefundsOrderInfo.setOrderNo(userOrderInfo.getOrderNo());
+        userRefundsOrderInfo.setOutRefundNo(out_refund_no);
+        userRefundsOrderInfo.setReason(reason);
+        userRefundsOrderInfo.setAmount(refundAmount);
+        userRefundsOrderInfo.setType(type);
+        userRefundsOrderInfo.setCreateTime(LocalDateTime.now());
+
+        SortedMap<String,String> params = new TreeMap<>();
+
+        params.put("out_trade_no", userOrderInfo.getOrderNo());//商户订单号
+        params.put("out_refund_no", out_refund_no);//商户退款单号
+        params.put("attach", reason);//退款原因
+        params.put("total_fee", amount_fee(userOrderInfo.getOrderMoney()));//原订单金额
+        params.put("refund_fee", amount_fee(refundAmount));//退款金额
+        params.put("sign_type", "RSA_1_256");
+        PayUtill payUtill = new PayUtill();
+
+        Map<String, String> refund = payUtill.refund(params, swiftpassConfig);
+
+        log.info("最终拿到的微信支付通知数据:" + refund);
+        if (Objects.equals(refund.get("status"), "0")){
+
+            log.info("退款调用成功!");
+            if(Objects.equals(refund.get("result_code"), "0")){
+                log.info("退款成功!");
+                log.info("订单:{},退款成功!原因:{}", userOrderInfo.getOrderNo(), reason);
+                //修改订单状态
+                userOrderInfo.setOrderStatus(SystemConstants.STATUS_FIVE);
+                userOrderInfo.setRefundMoney(userOrderInfo.getRefundMoney().add(refundAmount));
+                userOrderInfo.setRefundTime(LocalDateTime.now());
+                userOrderInfoMapper.updateById(userOrderInfo);
+            } else{
+                log.info("退款处理中");
+                //修改订单状态
+                userOrderInfo.setRefundMoney(userOrderInfo.getRefundMoney().add(refundAmount));
+                userOrderInfoMapper.updateById(userOrderInfo);
+            }
+            //退款信息补充
+            userRefundsOrderInfo.setRefundId(refund.get("refund_id"));
+            userRefundsOrderInfo.setNotifyRequest(refund.toString());
+            userRefundsOrderInfo.setTransactionId(userOrderInfo.getTransactionId());
+            userRefundsOrderInfo.setAcceptedTime(LocalDateTime.now());
+            userRefundsOrderInfoMapper.insert(userRefundsOrderInfo);
+        }
+
+    }
 }

+ 4 - 5
src/main/java/com/zsElectric/boot/business/service/impl/ChargeOrderInfoServiceImpl.java

@@ -172,6 +172,10 @@ public class ChargeOrderInfoServiceImpl extends ServiceImpl<ChargeOrderInfoMappe
             Long count = this.baseMapper.selectCount(Wrappers.lambdaQuery(ChargeOrderInfo.class).eq(ChargeOrderInfo::getUserId, userId).in(ChargeOrderInfo::getStatus, SystemConstants.STATUS_ZERO, SystemConstants.STATUS_ONE, SystemConstants.STATUS_TWO));
             Assert.isTrue(count == 0, "您有正在进行中的订单,请等待完成!");
 
+            //生成系统充电订单号及互联互通充电订单号
+            assert userId != null;
+            String chargeOrderNo = generateNo(ORDER_NO_PREFIX, userId);
+            String startChargeSeq = ConnectivityConstants.OPERATOR_ID + chargeOrderNo;
 
             //请求设备认证
             EquipmentAuthResponseVO equipmentAuthResponseVO = chargingBusinessService.queryEquipAuth(formData.getEquipAuthSeq(),
@@ -181,11 +185,6 @@ public class ChargeOrderInfoServiceImpl extends ServiceImpl<ChargeOrderInfoMappe
                 throw new BusinessException("设备认证失败");
             }
 
-
-            //生成系统充电订单号及互联互通充电订单号
-            assert userId != null;
-            String chargeOrderNo = generateNo(ORDER_NO_PREFIX, userId);
-            String startChargeSeq = ConnectivityConstants.OPERATOR_ID + chargeOrderNo;
             //生成设备流水号(格式"运营商ID+唯一编号")
             String equipAuthSeq = ConnectivityConstants.OPERATOR_ID + generateNo(EQUIPMENT_NO_PREFIX, userId);
 

+ 2 - 1
src/main/java/com/zsElectric/boot/business/service/impl/UserAccountServiceImpl.java

@@ -1,5 +1,6 @@
 package com.zsElectric.boot.business.service.impl;
 
+import com.baomidou.mybatisplus.core.toolkit.Wrappers;
 import com.zsElectric.boot.business.mapper.UserAccountLogMapper;
 import com.zsElectric.boot.business.model.entity.UserAccountLog;
 import com.zsElectric.boot.common.constant.SystemConstants;
@@ -119,7 +120,7 @@ public class UserAccountServiceImpl extends ServiceImpl<UserAccountMapper, UserA
     public UserAccount updateAccountBalanceAndLog(Long userId, BigDecimal changeAmount,
                                                   Integer changeType, String changeNote, Long changeId) {
         // 查询用户账户
-        UserAccount userAccount = this.getById(userId);
+        UserAccount userAccount = this.getOne(Wrappers.lambdaQuery(UserAccount.class).eq(UserAccount::getUserId, userId).last("limit 1"));
         
         // 创建账户变动日志
         UserAccountLog accountLog = new UserAccountLog();

+ 10 - 9
src/main/java/com/zsElectric/boot/core/pay/swiftpass/util/PayUtill.java

@@ -1,5 +1,6 @@
 package com.zsElectric.boot.core.pay.swiftpass.util;
 
+import cn.hutool.core.util.RandomUtil;
 import com.zsElectric.boot.core.pay.swiftpass.config.SwiftpassConfig;
 import jakarta.servlet.ServletException;
 import jakarta.servlet.http.HttpServletRequest;
@@ -262,22 +263,26 @@ public class PayUtill {
         }
         return result;
     }
+
     /**
      * 退款
-     * @throws ServletException
+     *
+     * @param map
+     * @param sws
+     * @return
      * @throws IOException
-     * @see [类、类#方法、类#成员]
      */
-    public Map refund(SortedMap<String, String> map,SwiftpassConfig sws) throws ServletException, IOException{
+
+    public Map<String,String> refund(SortedMap<String, String> map,SwiftpassConfig sws) throws IOException {
         log.debug("发起微信退款...");
 
         map.put("service", "unified.trade.refund");
         map.put("version", version);
         map.put("charset", charset);
-        String reqUrl = sws.getReq_url();
+        String reqUrl = "https://pay.swiftpass.cn/pay/gateway";
         map.put("mch_id", sws.getMch_id());
         map.put("op_user_id", sws.getMch_id());
-        map.put("nonce_str", String.valueOf(new Date().getTime()));
+        map.put("nonce_str", RandomUtil.randomString(32));
 
         Map<String,String> params = SignUtils.paraFilter(map);
         StringBuilder buf = new StringBuilder((params.size() +1) * 10);
@@ -332,8 +337,4 @@ public class PayUtill {
         }
         return resultMap;
     }
-
-
-
-
 }

+ 2 - 1
src/main/resources/application-prod.yml

@@ -121,6 +121,7 @@ security:
     - /favicon.ico
     - /charge-business/v1/linkData/**
     - /applet/v1/homePage/** # 用户端分页查询站点信息
+    - /applet/v1/wft/order/notify
   # 只走第三方过滤器、不走其他安全链
   third-party-urls:
     - /charge-business/v1/linkData/notification_start_charge_result
@@ -303,7 +304,7 @@ wx:
   req_url: https://eoap.cebbank.com/uiap/bcac/pay/pay/gateway
   #支付请求成功后的回调地址--需要配置正确的域名
   #  notify_url: http://120.78.228.211:8880/orderApi/enditPay
-  notify_url: https://charge.hub.zswlgz.com/orderApi/orderNotify
+  notify_url: https://13cd4c06.r28.cpolar.top/applet/v1/wft/order/notify
   #私钥
   mchPrivateKey: MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQDeFB5G2OYT762PpUytCw7Du40i6WnzcmbvEE9IXPXi+QirPwMvW9mBqNDIUk5hQS3ZnHjj80YQRWG6yksjE6kHAYIWahCDiaPlBqYvYSJ8ePzbT61THZJbzqFaIG3svW7xq9nsUmzVBub0ATIzC1DQRu9ZTdrj/iuMUEhJyJ8IHrTP09eTwNYdoagHQlKWRVoNE3LuU4GXG3VCbkQ2ixbMo8dXBisDIi3GYOSFWzota6H+OCp9Mta1jTqdwALKAU9PNlRkQwOMLk2OmMqGUhImVVpl+eGrIYn3iARce0alNFg8hghFMJ8MKpSxJDM6YHNOJQ06S25YYhTpd+C2/VOBAgMBAAECggEBAM/W9ksJ/bJU0xOn+W3N9oB7C+jLmMwtmmZM1lZ8IefNeC6Ep59wD81ISDXiydY9YQLTbVSxPjZGKOPfJZjrcnrLD4uYsmHYtFnI8klPWC40MTmzhRxPhcWESgAGb7prw+RMGIUS0yY/8nAUmn2pLnXunVzv/1b3bpxAGpdrOmMmU28GBt9AlXiIpVmnxnkhp66c4zFj5gvvVoDrz9m/6Acyn6n7yccCHD2iYw0D78GPItEWky38tH/FV0lRcCCYAf5fc2nFnicrdgj1RYjqTWxM7A92UecviTAbiuPO6CQQl7+sMtGU2d6UeKj0Xrrl2gly+lOS97P/NcLtZf5vklECgYEA8SMlCg5AToSkIwal7dXYgM2VlNFwSRDuO3hEVoWe7bM/LkEp8dqSpV025ZtngTY/qziXsGP/7l8bcS1th19cX/+/MFFOWsoxtqvOam2Elp+Qg1johEfnWI+Bo4WiQ5CHYNQRN3cuiWTc5HHuI0KQAEPx/aYogD057X2FIsiu6S0CgYEA68RAFsupZ6R8BhJbfY2CK07XLNvXu5DAYBmU3trmQFnaxXeQFfhQ8hi9m5Awu14YCnRmHzc8+QXFD/GqTAbxtImc6AZQKWdLuUntkitPWmJK6dtJC8Is3U9Yqz5+CkSmgZSfqW0DvEt7jagoNfpKgy29Qq4r35b6JsDXtnTQICUCgYEAjmwnkEzihn2pRFbE4jiP62OBmagqHb22N8HM+x1oxRQ9mOA8GfDy9GCd7/ddpt+Xs1V1omUt4GikGLCwJGiacsjm727WTKFnw3CuNgYBbcVI4Ys9qgOeDJyWATMIp8dRbktS7+OgxN2h6fuwn3rM+psm7p2ZBkUjVbXxUJ4fUPECgYEAw1wmAv2VjRUF0/4oI5w7bWlx8XDljT1/uuHXsuZN/qq2FgRht2LAqCsKCjprtwZcA2W6LUmXU32Ncg29ICxs4j1ZcAWzLOu0GoAAxKrwoSNrkeYr2/t1M5kJDzTEOfvywNMHjduQSdl+Mr5RO5D/Zz1iYztxjV9MPwpydHTM9KUCgYAHuT98NkBilMxQNNmUB13E10MYQvZuiGVFZtT3up69Elpmtm7Z5cEW7QNG0g1LPPfkzfWPsq+6I98FmozLickqvjntdpul4czTITn8SNHqoxvdbcVnDipF1KwlHcnXhjO1KjSZg3a/iv554OR3/rbD9SWDzDAT7Zy6zX9n8OwGRA==
   #公钥(商户平台的公钥,不是自己生成的公钥)