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.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; 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; import com.zsElectric.boot.core.pay.swiftpass.util.SignUtils; import com.zsElectric.boot.core.pay.swiftpass.util.XmlUtils; import com.zsElectric.boot.security.util.SecurityUtils; import jakarta.annotation.Resource; import jakarta.servlet.ServletException; import jakarta.servlet.http.HttpServletRequest; import jakarta.validation.Valid; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; 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.*; /** * 威富通支付服务 * 用于处理威富通JSAPI支付接口 * * @author zsElectric * @since 2025-12-29 */ @Slf4j @Service public class WFTOrderService { @Resource private UserOrderInfoMapper userOrderInfoMapper; @Resource private UserInfoMapper userInfoMapper; @Resource private RechargeLevelMapper rechargeLevelMapper; @Resource protected SwiftpassConfig swiftpassConfig; @Resource private UserAccountService userAccountService; @Resource private ChargeOrderInfoMapper chargeOrderInfoMapper; @Resource private UserRefundsOrderInfoMapper userRefundsOrderInfoMapper; /** * 创建商户订单号 * 要求 32个字符内,只能是数字、大小写字母_-|*且在同一个商户号下唯一 * 组成 两位前缀 + 17位时间戳 + 9位id补零 + 4位随机数 合计32位 * * @param head 例如 商品-SP 退款-TK 等等 * @param id 用户id * @return */ public String createOrderNo(String head, Long id) { StringBuilder uid = new StringBuilder(id.toString()); Date date = new Date(); SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmssSSS"); int length = uid.length(); for (int i = 0; i < 9 - length; i++) { uid.insert(0, "0"); } return head + sdf.format(date) + uid + (int) ((Math.random() * 9 + 1) * 1000); } /** * 金额元转分字符串 * * @param cny 元 * @return */ public String amount_fee(BigDecimal cny) { BigDecimal b2 = new BigDecimal("100"); 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); //构建支付表单返回给前端支撑JsApi支付调用 AppUserPayForm payForm = new AppUserPayForm(); payForm.setOrderId(orderInfo.getId()).setOrderNo(orderNo); PayUtill wx = new PayUtill(); /** * 小程序支付 */ String payWay = "1"; /** * 请求支付 */ try { log.debug("通知第三开始支付:"); // 通知第三方支付---------------------------------------------------------- SortedMap map = new TreeMap(); // 订单编号 map.put("out_trade_no", orderNo); // 商品描述 map.put("body", "购买充电抵扣券"); // 附加信息 map.put("attach", "支付人" + userId); // pifList.get(0).setHydOrderPayMoney(new BigDecimal("0.01")); // 总金额(分) map.put("total_fee", amount_fee(level.getMoney())); // 终端ip map.put("mch_create_ip", appLevelOrderForm.getIp()); // 签名方式 map.put("sign_type", "RSA_1_256"); // 回调地址 map.put("notify_url", swiftpassConfig.getNotify_url()); // 公众账号或小程序ID map.put("sub_appid", "wx9894a01b9e92c368"); if (payWay.equals("1")) {// 支付渠道(1 微信 2支付宝支付 4建行支付 6微信小程序支付) // 是否小程序支付--值为1,表示小程序支付;不传或值不为1,表示公众账号内支付 map.put("is_minipg", "1"); map.put("sub_appid", "wx9894a01b9e92c368"); } else if (payWay.equals("2")) { map.put("is_minipg", "2"); } // --------微信支付请求 Map wxMap = wx.pay(map, userOpenId, swiftpassConfig); log.info("威富通支付返回结果: {}", wxMap); if (wxMap.get("status").toString().equals("200")) { payForm.setParams(wxMap); return payForm; } else { String errorMsg = wxMap.get("msg") != null ? wxMap.get("msg").toString() : "未知错误"; log.error("请求支付失败,返回结果: {}", wxMap); throw new RuntimeException("请求支付失败:" + errorMsg); } } catch (Exception e) { log.error("创建订单异常,用户ID: {}, 订单号: {}", userId, orderNo, e); throw new RuntimeException("请求支付失败:" + e.getMessage(), e); } } public AppUserPayForm payOrder(String orderId, String ip) { UserOrderInfo orderInfo = userOrderInfoMapper.selectById(orderId); //构建支付表单 AppUserPayForm payForm = new AppUserPayForm(); payForm.setOrderId(orderInfo.getId()).setOrderNo(orderInfo.getOrderNo()); //查询档位 RechargeLevel level = rechargeLevelMapper.selectById(orderInfo.getLevelId()); PayUtill wx = new PayUtill(); /** * 小程序支付 */ String payWay = "1"; try { log.debug("通知第三开始支付:"); // 通知第三方支付---------------------------------------------------------- SortedMap map = new TreeMap(); // 订单编号 map.put("out_trade_no", orderInfo.getOrderNo()); // 商品描述 map.put("body", "购买充电抵扣券"); // 附加信息 map.put("attach", "支付人" + orderInfo.getUserId()); // pifList.get(0).setHydOrderPayMoney(new BigDecimal("0.01")); // 总金额(分) map.put("total_fee", amount_fee(level.getMoney())); // 终端ip map.put("mch_create_ip", ip); // 签名方式 map.put("sign_type", "RSA_1_256"); // 回调地址 map.put("notify_url", swiftpassConfig.getNotify_url()); // 公众账号或小程序ID map.put("sub_appid", "wx9894a01b9e92c368"); if (payWay.equals("1")) {// 支付渠道(1 微信 2支付宝支付 4建行支付 6微信小程序支付) // 是否小程序支付--值为1,表示小程序支付;不传或值不为1,表示公众账号内支付 map.put("is_minipg", "1"); map.put("sub_appid", "wx9894a01b9e92c368"); } else if (payWay.equals("2")) { map.put("is_minipg", "2"); } // --------微信支付请求 Map wxMap = wx.pay(map, orderInfo.getOpenid(), swiftpassConfig); log.info("威富通支付返回结果: {}", wxMap); if (wxMap.get("status").toString().equals("200")) { payForm.setParams(wxMap); return payForm; } else { String errorMsg = wxMap.get("msg") != null ? wxMap.get("msg").toString() : "未知错误"; log.error("支付订单失败,返回结果: {}", wxMap); throw new RuntimeException("支付失败:" + errorMsg); } } catch (Exception e) { log.error("支付订单异常,订单ID: {}", orderId, e); throw new RuntimeException("支付失败:" + e.getMessage(), e); } } public Boolean closeOrder(String orderNo) { UserOrderInfo userOrderInfo = userOrderInfoMapper.selectOne(Wrappers.lambdaQuery(UserOrderInfo.class).eq(UserOrderInfo::getOrderNo, orderNo).last("limit 1")); if(ObjectUtil.isNull(userOrderInfo)){ throw new BusinessException("订单不存在"); } userOrderInfo.setOrderStatus(SystemConstants.STATUS_THREE); int i = userOrderInfoMapper.updateById(userOrderInfo); if(i > 0){ return Boolean.TRUE; } return Boolean.FALSE; } /** * 处理威富通支付通知回调 * * 重要说明: * 1. 接收威富通POST的XML数据流 * 2. 验证签名(支持RSA_1_256、RSA_1_1、MD5) * 3. 校验订单号和金额 * 4. 幂等性处理,防止重复通知 * 5. 使用数据库锁避免并发问题 * 6. 更新订单状态 * 7. 必须返回纯字符串"success"或"fail" * * 通知重试机制:0/15/15/30/180/1800/1800/1800/1800/3600秒 * * @param request HTTP请求对象 * @return "success" 表示处理成功,"fail" 表示处理失败 */ @Transactional(rollbackFor = Exception.class) public String handlePayNotify(HttpServletRequest request) { log.info("========== 开始处理威富通支付通知 =========="); try { // 1. 读取POST的XML数据 String xmlData = XmlUtils.parseRequst(request); log.info("接收到的XML通知数据: {}", xmlData); if (xmlData == null || xmlData.trim().isEmpty()) { log.error("通知数据为空"); return "fail"; } // 2. 解析XML为Map Map notifyData = XmlUtils.toMap(xmlData.getBytes("UTF-8"), "UTF-8"); log.info("解析后的通知参数: {}", notifyData); // 3. 验证基本参数 String status = notifyData.get("status"); String resultCode = notifyData.get("result_code"); String signType = notifyData.get("sign_type"); String sign = notifyData.get("sign"); if (status == null || resultCode == null || signType == null || sign == null) { log.error("通知参数缺失: status={}, result_code={}, sign_type={}, sign={}", status, resultCode, signType, sign); return "fail"; } // 4. 验证签名 boolean signValid = SignUtil.verifySign(sign, signType, notifyData, swiftpassConfig); if (!signValid) { log.error("签名验证失败"); return "fail"; } log.info("签名验证成功"); // 5. 检查通信状态和业务结果 if (!"0".equals(status)) { log.error("通信状态失败: status={}, message={}", status, notifyData.get("message")); return "fail"; } if (!"0".equals(resultCode)) { log.error("业务结果失败: result_code={}, err_code={}, err_msg={}", resultCode, notifyData.get("err_code"), notifyData.get("err_msg")); return "fail"; } // 6. 提取支付结果参数 String payResult = notifyData.get("pay_result"); if (!"0".equals(payResult)) { log.error("支付结果失败: pay_result={}, pay_info={}", payResult, notifyData.get("pay_info")); return "fail"; } // 7. 提取订单相关信息 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"); // 附加信息 if (outTradeNo == null || transactionId == null || totalFeeStr == null) { log.error("订单关键参数缺失: out_trade_no={}, transaction_id={}, total_fee={}", outTradeNo, transactionId, totalFeeStr); return "fail"; } log.info("订单信息: out_trade_no={}, transaction_id={}, total_fee={}, time_end={}", outTradeNo, transactionId, totalFeeStr, timeEnd); // 8. 查询订单并加锁(防止并发) UserOrderInfo orderInfo = userOrderInfoMapper.selectOne( Wrappers.lambdaQuery(UserOrderInfo.class) .eq(UserOrderInfo::getOrderNo, orderNo) .last("FOR UPDATE") // 数据库行锁 ); if (orderInfo == null) { log.error("订单不存在: out_trade_no={}", orderNo); return "fail"; } // 9. 幂等性检查 - 如果订单已支付,直接返回成功 if (SystemConstants.STATUS_TWO.equals(orderInfo.getOrderStatus())) { log.info("订单已支付,幂等性处理: out_trade_no={}, transaction_id={}", outTradeNo, orderInfo.getTransactionId()); return "success"; } // 10. 验证订单金额 String expectedFee = amount_fee(orderInfo.getOrderMoney()); if (!expectedFee.equals(totalFeeStr)) { log.error("订单金额不匹配: expected={}, actual={}", expectedFee, totalFeeStr); return "fail"; } // 11. 更新订单状态 orderInfo.setOrderStatus(SystemConstants.STATUS_TWO); // 已支付 orderInfo.setTransactionId(transactionId); // 平台订单号 orderInfo.setPayMoney(payMoney); // 支付金额 orderInfo.setOutTradeNo(outTradeNo); // 第三方订单号 orderInfo.setPayTime(payTime); // 解析支付时间 if (timeEnd != null && timeEnd.length() == 14) { try { java.time.format.DateTimeFormatter formatter = java.time.format.DateTimeFormatter.ofPattern("yyyyMMddHHmmss"); orderInfo.setPayTime(java.time.LocalDateTime.parse(timeEnd, formatter)); } catch (Exception e) { log.warn("解析支付时间失败: time_end={}", timeEnd, e); } } int updateCount = userOrderInfoMapper.updateById(orderInfo); if (updateCount <= 0) { log.error("更新订单状态失败: out_trade_no={}", outTradeNo); return "fail"; } log.info("订单支付成功: out_trade_no={}, transaction_id={}, total_fee={}", outTradeNo, transactionId, totalFeeStr); //支付成功业务处理 successPayOrder(orderInfo, transactionId, payTime, payMoney); return "success"; } catch (Exception e) { log.error("处理支付通知异常", e); return "fail"; } finally { log.info("========== 威富通支付通知处理完成 =========="); } } /** * 支付成功业务处理 * * @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 chargeOrderInfoList = chargeOrderInfoMapper.selectList(Wrappers.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() ); } } } } /** * 订单是否支付成功查询 * * 应用场景: * 1. 用户支付后前端轮询查询订单状态 * 2. 后台定时任务查询未支付订单 * 3. 支付通知未收到时的补偿查询 * * 查询策略建议: * - 方案一:以订单创建时间为基准,间隔5秒/30秒/1分钟/3分钟/5分钟/10分钟/30分钟查询 * - 方案二:定时任务每30秒查询最近10分钟内未支付订单,最多查询10次 * * @param orderNo 商户订单号 * @return true-已支付成功,false-未支付或查询失败 */ public Boolean queryOrder(String orderNo) { log.info("========== 开始查询订单支付状态 =========="); log.info("查询订单号: {}", orderNo); try { // 1. 查询本地订单信息 UserOrderInfo orderInfo = userOrderInfoMapper.selectOne( Wrappers.lambdaQuery(UserOrderInfo.class) .eq(UserOrderInfo::getOrderNo, orderNo) ); if (orderInfo == null) { log.error("订单不存在: orderNo={}", orderNo); return false; } // 2. 如果订单已经是已支付状态,直接返回true if (SystemConstants.STATUS_TWO.equals(orderInfo.getOrderStatus())) { log.info("订单已支付: orderNo={}, transactionId={}", orderNo, orderInfo.getTransactionId()); return true; } // 3. 如果订单已取消,返回false if (SystemConstants.STATUS_THREE.equals(orderInfo.getOrderStatus())) { log.info("订单已取消: orderNo={}", orderNo); return false; } // 4. 构建查询请求参数 SortedMap queryParams = new TreeMap<>(); queryParams.put("out_trade_no", orderNo); // 商户订单号 queryParams.put("sign_type", "RSA_1_256"); // 签名方式 // 5. 调用威富通订单查询API Map queryResult = queryOrderFromWFT(queryParams); if (!"200".equals(queryResult.get("status"))) { log.error("查询订单失败: orderNo={}, result={}", orderNo, queryResult); return false; } // 6. 解析查询结果 String tradeState = queryResult.get("trade_state"); String payResult = queryResult.get("pay_result"); log.info("订单查询结果: orderNo={}, trade_state={}, pay_result={}", orderNo, tradeState, payResult); // 7. 判断支付状态 // trade_state: SUCCESS-支付成功, NOTPAY-未支付, CLOSED-已关闭, REFUND-转入退款 // pay_result: 0-成功 boolean isPaid = "SUCCESS".equals(tradeState) || "0".equals(payResult); // 8. 如果支付成功但本地订单状态未更新,同步更新订单状态 if (isPaid && !SystemConstants.STATUS_TWO.equals(orderInfo.getOrderStatus())) { log.info("同步更新订单支付状态: orderNo={}", orderNo); 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) { log.error("查询订单支付状态异常: orderNo={}", orderNo, e); return false; } finally { log.info("========== 订单支付状态查询完成 =========="); } } /** * 调用威富通订单查询API * * @param queryParams 查询参数 * @return 查询结果 */ private Map queryOrderFromWFT(SortedMap queryParams) { log.info("调用威富通订单查询API: params={}", queryParams); try { // 设置公共参数 queryParams.put("service", "unified.trade.query"); queryParams.put("version", "2.0"); queryParams.put("charset", "UTF-8"); queryParams.put("mch_id", swiftpassConfig.getMch_id()); queryParams.put("nonce_str", String.valueOf(new Date().getTime())); // 生成签名 Map params = SignUtils.paraFilter(queryParams); StringBuilder buf = new StringBuilder((params.size() + 1) * 10); SignUtils.buildPayParams(buf, params, false); String preStr = buf.toString(); String signType = queryParams.get("sign_type"); String sign = SignUtil.getSign(signType, preStr, swiftpassConfig); queryParams.put("sign", sign); log.info("查询请求XML: {}", XmlUtils.toXml(queryParams)); // 发送HTTP请求 String reqUrl = swiftpassConfig.getReq_url(); org.apache.http.client.methods.HttpPost httpPost = new org.apache.http.client.methods.HttpPost(reqUrl); org.apache.http.entity.StringEntity entityParams = new org.apache.http.entity.StringEntity(XmlUtils.parseXML(queryParams), "utf-8"); httpPost.setEntity(entityParams); httpPost.setHeader("Content-Type", "text/xml;utf-8"); org.apache.http.impl.client.CloseableHttpClient client = org.apache.http.impl.client.HttpClients.createDefault(); org.apache.http.client.methods.CloseableHttpResponse response = client.execute(httpPost); try { if (response != null && response.getEntity() != null) { // 解析响应 Map resultMap = XmlUtils.toMap( org.apache.http.util.EntityUtils.toByteArray(response.getEntity()), "utf-8"); log.info("查询响应结果: {}", resultMap); // 验证签名 String reSign = resultMap.get("sign"); String reSignType = resultMap.get("sign_type"); if (resultMap.containsKey("sign")) { boolean signValid = SignUtil.verifySign(reSign, reSignType, resultMap, swiftpassConfig); if (!signValid) { log.error("查询结果签名验证失败"); resultMap.put("status", "500"); resultMap.put("msg", "签名验证失败"); return resultMap; } } // 检查通信状态和业务结果 if ("0".equals(resultMap.get("status")) && "0".equals(resultMap.get("result_code"))) { resultMap.put("status", "200"); return resultMap; } else { log.error("查询失败: status={}, result_code={}, err_code={}, err_msg={}", resultMap.get("status"), resultMap.get("result_code"), resultMap.get("err_code"), resultMap.get("err_msg")); resultMap.put("status", "500"); return resultMap; } } else { log.error("查询响应为空"); Map errorResult = new HashMap<>(); errorResult.put("status", "500"); errorResult.put("msg", "查询响应为空"); return errorResult; } } finally { response.close(); client.close(); } } catch (Exception e) { log.error("调用威富通查询API异常", e); Map errorResult = new HashMap<>(); errorResult.put("status", "500"); errorResult.put("msg", "系统异常: " + e.getMessage()); return errorResult; } } /** * 同步订单支付状态 * 当查询到订单已支付但本地状态未更新时,同步更新本地订单状态 * * @param orderInfo 订单信息 * @param queryResult 查询结果 */ @Transactional(rollbackFor = Exception.class) public void syncOrderStatus(UserOrderInfo orderInfo, Map queryResult) { log.info("同步订单状态: orderNo={}", orderInfo.getOrderNo()); try { // 提取支付信息 String transactionId = queryResult.get("transaction_id"); String totalFeeStr = queryResult.get("total_fee"); String timeEnd = queryResult.get("time_end"); // 查询档位信息验证金额 RechargeLevel level = rechargeLevelMapper.selectById(orderInfo.getLevelId()); if (level != null) { String expectedFee = amount_fee(level.getMoney()); if (expectedFee.equals(totalFeeStr)) { // 更新订单状态 orderInfo.setOrderStatus(SystemConstants.STATUS_TWO); orderInfo.setTransactionId(transactionId); orderInfo.setPayMoney(level.getMoney()); orderInfo.setOutTradeNo(transactionId); // 解析支付时间 if (timeEnd != null && timeEnd.length() == 14) { try { java.time.format.DateTimeFormatter formatter = java.time.format.DateTimeFormatter.ofPattern("yyyyMMddHHmmss"); orderInfo.setPayTime(java.time.LocalDateTime.parse(timeEnd, formatter)); } catch (Exception e) { log.warn("解析支付时间失败: time_end={}", timeEnd, e); } } int updateCount = userOrderInfoMapper.updateById(orderInfo); if (updateCount > 0) { log.info("订单状态同步成功: orderNo={}, transactionId={}", orderInfo.getOrderNo(), transactionId); } else { log.error("订单状态同步失败: orderNo={}", orderInfo.getOrderNo()); } } else { log.error("订单金额不匹配,不同步状态: expected={}, actual={}", expectedFee, totalFeeStr); } } } catch (Exception e) { log.error("同步订单状态异常: orderNo={}", orderInfo.getOrderNo(), e); throw e; } } /** * 账户退款 * * @return */ @Transactional(rollbackFor = Exception.class) public String refundOrder() throws Exception { //查询账户余额 UserAccount userAccount = userAccountService.getOne(Wrappers.lambdaQuery().eq(UserAccount::getUserId, SecurityUtils.getUserId()).last("limit 1")); if (userAccount.getBalance().compareTo(BigDecimal.ZERO) == 0) { return "账户余额为 0"; } BigDecimal refundMoney = userAccount.getBalance(); //查询一年内已支付的所有券订单 List userOrderInfoList = userOrderInfoMapper.selectList(Wrappers.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 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 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); } } }