2025008071805

This commit is contained in:
张潘 2025-08-14 18:32:33 +08:00
parent 64d1cd8fad
commit 99b958a3b7
11 changed files with 1429 additions and 441 deletions

View File

@ -17,12 +17,12 @@ wechat:
mchid: 1672571923 mchid: 1672571923
apikey: sssssssssssssssssssssssssssssssS apikey: sssssssssssssssssssssssssssssssS
certpath: wechat/apiclient_cert.p12 certpath: wechat/apiclient_cert.p12
apiv3-key: sssssssssssssssssssssssssssssssS apiv3Key: sssssssssssssssssssssssssssssssS
serial-no: 492161DA75F56B976B5F5EE5051ED60B0C288BB9 serialNo: 492161DA75F56B976B5F5EE5051ED60B0C288BB9
private-key-path: wechat/apiclient_key.pem privateKeyPath: wechat/apiclient_key.pem
cert-dir: wechat certDir: wechat
# 微信支付平台证书序列号(临时使用商户证书序列号进行测试) # 微信支付平台证书序列号(临时使用商户证书序列号进行测试)
wechatpay-serial: 492161DA75F56B976B5F5EE5051ED60B0C288BB9 wechatpaySerial: 492161DA75F56B976B5F5EE5051ED60B0C288BB9
# appid: wx73d0202b3c8a6d68 # appid: wx73d0202b3c8a6d68
# mchid: 1672571923 # mchid: 1672571923

View File

@ -29,14 +29,14 @@ public class WechatConfig {
this.appid = appid; this.appid = appid;
} }
public String getMchid() {
return mchid;
}
public void setMchid(String mchid) { public void setMchid(String mchid) {
this.mchid = mchid; this.mchid = mchid;
} }
public String getMchid() {
return mchid;
}
public String getApikey() { public String getApikey() {
return apikey; return apikey;
} }

View File

@ -108,13 +108,12 @@ public class ApplePayController extends BaseController {
*/ */
@PostMapping("/api/withdraw") @PostMapping("/api/withdraw")
public AjaxResult withdraw(@RequestParam String openid, public AjaxResult withdraw(@RequestParam String openid,
@RequestParam String orderid,
@RequestParam int amount, @RequestParam int amount,
@RequestParam(required = false) String desc, @RequestParam(required = false) String desc,
@RequestParam(required = false) String userName) { @RequestParam(required = false) String userName) {
try { try {
Map<String, Object> result = wechatPayV3Util.withdraw(openid, amount, desc, userName, orderid);
Map<String, Object> result = wechatPayV3Util.withdraw(openid, amount, desc, userName);
if ((Boolean) result.get("success")) { if ((Boolean) result.get("success")) {
return AjaxResult.success("提现申请成功", result.get("data")); return AjaxResult.success("提现申请成功", result.get("data"));

View File

@ -126,7 +126,8 @@ public class AppletController extends BaseController {
private IUsersPayBeforService usersPayBeforService; private IUsersPayBeforService usersPayBeforService;
@Autowired @Autowired
private IUserDemandQuotationService userDemandQuotationService; private IUserDemandQuotationService userDemandQuotationService;
@Autowired
private WechatPayV3Util wechatPayV3Util;
/** /**
* 获取服务分类列表 * 获取服务分类列表
* 功能说明 * 功能说明
@ -7509,8 +7510,8 @@ public class AppletController extends BaseController {
return AppletControllerUtil.appletWarning("提现金额不能为空"); return AppletControllerUtil.appletWarning("提现金额不能为空");
} }
BigDecimal moneyBigDecimal = new BigDecimal(money); BigDecimal moneyBigDecimal = new BigDecimal(money);
if (moneyBigDecimal.compareTo(BigDecimal.ZERO) <= 0) { if (moneyBigDecimal.compareTo(new BigDecimal(1)) <= 0) {
return AppletControllerUtil.appletWarning("提现金额必须大于0"); return AppletControllerUtil.appletWarning("提现金额必须大于1元");
} }
if (moneyBigDecimal.compareTo(user.getCommission()) > 0) { if (moneyBigDecimal.compareTo(user.getCommission()) > 0) {
return AppletControllerUtil.appletWarning("提现金额不能大于账户余额"); return AppletControllerUtil.appletWarning("提现金额不能大于账户余额");
@ -7519,27 +7520,41 @@ public class AppletController extends BaseController {
return AppletControllerUtil.appletWarning("提现金额不能大于2000"); return AppletControllerUtil.appletWarning("提现金额不能大于2000");
} }
//--------------------------预留提现的接口实现核心逻辑开始-------------------------------- //--------------------------预留提现的接口实现核心逻辑开始--------------------------------
//------------------------------------结束--------------------------------
WechatTransfer wechatTransfer = new WechatTransfer(); Map<String, Object> result = wechatPayV3Util.quickWithdraw(user.getOpenid(), moneyBigDecimal, "师傅收益提现",orderId);
wechatTransfer.setUid(user.getId());
wechatTransfer.setMoney(moneyBigDecimal); if ((Boolean) result.get("success")) {
wechatTransfer.setUname(user.getName()); System.out.println("快速提现成功"+result);
wechatTransfer.setOrderId(orderId); WechatTransfer wechatTransfer = new WechatTransfer();
wechatTransfer.setStatus(1L); wechatTransfer.setUid(user.getId());
wechatTransfer.setPaid(0L); wechatTransfer.setMoney(moneyBigDecimal);
wechatTransfer.setOpenid(user.getOpenid()); wechatTransfer.setUname(user.getName());
int flg= wechatTransferService.insertWechatTransfer(wechatTransfer); wechatTransfer.setOrderId(orderId);
if(flg>0){ wechatTransfer.setStatus(1L);
//减少师傅提现的余额 wechatTransfer.setPaid(0L);
user.setCommission(user.getCommission().subtract(moneyBigDecimal)); wechatTransfer.setOpenid(user.getOpenid());
//增加师傅的累计提现金额 int flg= wechatTransferService.insertWechatTransfer(wechatTransfer);
user.setPropose(user.getPropose().add(moneyBigDecimal)); // if(flg>0){
usersService.updateUsers(user); //
// //减少师傅提现的余额
// user.setCommission(user.getCommission().subtract(moneyBigDecimal));
// //增加师傅的累计提现金额
// user.setPropose(user.getPropose().add(moneyBigDecimal));
// usersService.updateUsers(user);
// }
return AppletControllerUtil.appletSuccess("提现成功") ;
} else {
System.out.println("快速提现失败"+result);
return AppletControllerUtil.appletError("服务器繁忙,请稍后重试");
} }
Map<String, Object> resp = new java.util.HashMap<>();
resp.put("resultCode", "200"); //------------------------------------结束--------------------------------
return resp;
// Map<String, Object> resp = new java.util.HashMap<>();
// resp.put("resultCode", "200");
// return resp;
} }
/** /**

View File

@ -11,10 +11,14 @@ import com.ruoyi.system.service.*;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RestController; import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.InputStream;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.text.SimpleDateFormat; import java.text.SimpleDateFormat;
import java.time.LocalDateTime; import java.time.LocalDateTime;
@ -27,6 +31,11 @@ import java.util.Map;
import java.io.BufferedReader; import java.io.BufferedReader;
import java.io.InputStreamReader; import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.security.Signature;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.util.Base64;
import java.util.Map;
/** /**
* 支付回调控制器 * 支付回调控制器
@ -42,7 +51,13 @@ import java.nio.charset.StandardCharsets;
public class PayNotifyController extends BaseController { public class PayNotifyController extends BaseController {
private static final Logger logger = LoggerFactory.getLogger(PayNotifyController.class); private static final Logger logger = LoggerFactory.getLogger(PayNotifyController.class);
/**
* 获取微信配置
*/
private com.ruoyi.system.config.WechatConfig wechatConfig() {
return com.ruoyi.common.utils.spring.SpringUtils.getBean(com.ruoyi.system.config.WechatConfig.class);
}
@Autowired @Autowired
private WechatPayUtil wechatPayUtil; private WechatPayUtil wechatPayUtil;
@ -51,7 +66,10 @@ public class PayNotifyController extends BaseController {
@Autowired @Autowired
private IOrderService orderService; private IOrderService orderService;
@Autowired
private IWechatTransferService wechatTransferService;
@Autowired @Autowired
private IOrderLogService orderLogService; private IOrderLogService orderLogService;
@ -716,7 +734,7 @@ public class PayNotifyController extends BaseController {
// orderService.updateOrder(mainOrder); // orderService.updateOrder(mainOrder);
// } // }
// 8. 处理上门费支付成功后的业务逻辑 // 7. 处理上门费支付成功后的业务逻辑
handleDoorFeePaymentSuccess(orderLog, paymentInfo); handleDoorFeePaymentSuccess(orderLog, paymentInfo);
// 2. 处理积分奖励 // 2. 处理积分奖励
@ -810,7 +828,7 @@ public class PayNotifyController extends BaseController {
// orderService.updateOrder(mainOrder); // orderService.updateOrder(mainOrder);
// } // }
// 8. 处理定金支付成功后的业务逻辑 // 7. 处理定金支付成功后的业务逻辑
Users users = usersService.selectUsersById(mainOrder.getUid()); Users users = usersService.selectUsersById(mainOrder.getUid());
handleDepositPaymentSuccess(users,orderLog, paymentInfo); handleDepositPaymentSuccess(users,orderLog, paymentInfo);
@ -1662,6 +1680,536 @@ public class PayNotifyController extends BaseController {
} }
} }
/**
* 微信提现回调接口
*/
@PostMapping("/api/worker/tixian/notify")
public void apiworkertixiannotify(HttpServletRequest request, HttpServletResponse response) throws IOException {
logger.info("🚀 开始处理微信提现回调...");
try {
// 步骤1: 读取原始数据
logger.info("📖 步骤1: 读取原始数据");
StringBuilder rawData = new StringBuilder();
try (BufferedReader reader = request.getReader()) {
String line;
while ((line = reader.readLine()) != null) {
rawData.append(line);
}
}
String rawDataStr = rawData.toString();
logger.info("✅ 步骤1完成: 原始数据长度: {}", rawDataStr.length());
logger.info("📄 原始数据内容: {}", rawDataStr);
JSONObject jsonObject = JSONObject.parseObject(rawDataStr);
String nonced = jsonObject.getString("nonce");
String associated_data = request.getHeader("associated_data");
// WechatPayV3Util.refundtixian();
// if (StringUtils.isNotBlank(nonced)&&StringUtils.isNotBlank(associated_data)){
// //wechatConfig().getApiv3Key();
// String decryptedValue = WechatPayV3Util.decryptAesGcm(associatedData, nonce, ciphertext);
// WCreturnTest wCreturnTest = new WCreturnTest(wechatConfig().getApiv3Key().getBytes());
// }
// 步骤2: 验证微信回调签名和证书
logger.info("🔐 步骤2: 验证微信回调签名和证书");
// 验证平台证书序列号
String serial = request.getHeader("Wechatpay-Serial");
if (serial == null || serial.trim().isEmpty()) {
logger.error("❌ 步骤2失败: 缺少Wechatpay-Serial头");
buildFailResponse(response, "缺少Wechatpay-Serial头");
return;
}
// 验证时间戳 (允许5分钟偏差)
String timestampStr = request.getHeader("Wechatpay-Timestamp");
if (timestampStr == null || timestampStr.trim().isEmpty()) {
logger.error("❌ 步骤2失败: 缺少Wechatpay-Timestamp头");
buildFailResponse(response, "缺少Wechatpay-Timestamp头");
return;
}
try {
Long timestamp = Long.valueOf(timestampStr);
long currentTime = System.currentTimeMillis() / 1000; // 转换为秒
if (Math.abs(currentTime - timestamp) > 5 * 60) {
logger.error("❌ 步骤2失败: 时间戳不正确,当前时间: {}, 回调时间: {}, 偏差: {}秒",
currentTime, timestamp, Math.abs(currentTime - timestamp));
buildFailResponse(response, "时间戳不正确");
return;
}
logger.info("✅ 时间戳验证通过,偏差: {}秒", Math.abs(currentTime - timestamp));
} catch (NumberFormatException e) {
logger.error("❌ 步骤2失败: 时间戳格式错误: {}", timestampStr);
buildFailResponse(response, "时间戳格式错误");
return;
}
// 验证签名
if (!verifyWithdrawSign(request, rawDataStr)) {
logger.error("❌ 步骤2失败: 签名验证失败");
buildFailResponse(response, "签名验证失败");
return;
}
logger.info("✅ 步骤2完成: 签名验证通过");
// 步骤3: 解析数据格式
logger.info("🔍 步骤3: 解析数据格式");
JSONObject jsonData = null;
Map<String, String> xmlData = null;
try {
// 尝试解析为JSON
jsonData = JSONObject.parseObject(rawDataStr);
logger.info("✅ 步骤3完成: 数据格式为JSON字段数量: {}", jsonData.size());
} catch (Exception e) {
logger.warn("⚠️ JSON解析失败尝试XML解析: {}", e.getMessage());
try {
xmlData = xmlToMap(rawDataStr);
logger.info("✅ 步骤3完成: 数据格式为XML字段数量: {}", xmlData.size());
} catch (Exception ex) {
logger.error("❌ 步骤3失败: 无法解析数据格式JSON错误: {}, XML错误: {}", e.getMessage(), ex.getMessage());
buildFailResponse(response, "无法解析数据格式");
return;
}
}
// 步骤4: 检查提现结果
logger.info("📊 步骤4: 检查提现结果");
boolean isSuccess = false;
String withdrawStatus = null;
if (jsonData != null) {
// JSON格式处理
withdrawStatus = jsonData.getString("return_code");
if (withdrawStatus == null) {
withdrawStatus = jsonData.getString("returnCode");
}
// 检查各种成功状态
String resultCode = jsonData.getString("result_code");
String errCode = jsonData.getString("err_code");
String errCodeDes = jsonData.getString("err_code_des");
String batchStatus = jsonData.getString("batch_status");
String eventType = jsonData.getString("event_type");
logger.info("📋 提现状态信息 - return_code: {}, result_code: {}, err_code: {}, err_code_des: {}, batch_status: {}, event_type: {}",
withdrawStatus, resultCode, errCode, errCodeDes, batchStatus, eventType);
// 微信转账批次完成通知的成功判断
if ("MCHTRANSFER.BATCH.FINISHED".equals(eventType)) {
isSuccess = true;
logger.info("✅ 微信转账批次完成通知");
} else if ("SUCCESS".equals(withdrawStatus) || "SUCCESS".equals(resultCode)) {
isSuccess = true;
logger.info("✅ 提现成功");
} else if (errCode == null && errCodeDes == null) {
// 如果没有错误码可能是成功状态
isSuccess = true;
logger.info("✅ 无错误码,视为成功状态");
}
} else if (xmlData != null) {
// XML格式处理
withdrawStatus = xmlData.get("return_code");
if (withdrawStatus == null) {
withdrawStatus = xmlData.get("returnCode");
}
String resultCode = xmlData.get("result_code");
String errCode = xmlData.get("err_code");
String errCodeDes = xmlData.get("err_code_des");
logger.info("📋 提现状态信息 - return_code: {}, result_code: {}, err_code: {}, err_code_des: {}",
withdrawStatus, resultCode, errCode, errCodeDes);
if ("SUCCESS".equals(withdrawStatus) || "SUCCESS".equals(resultCode)) {
isSuccess = true;
logger.info("✅ 提现成功");
} else if (errCode == null && errCodeDes == null) {
isSuccess = true;
logger.info("✅ 无错误码,视为成功状态");
}
}
if (!isSuccess) {
logger.error("❌ 步骤4失败: 提现回调返回失败,返回信息: {}", withdrawStatus);
buildFailResponse(response, "提现回调返回失败");
return;
}
logger.info("✅ 步骤4完成: 提现状态检查通过");
// 步骤5: 处理加密资源数据
logger.info("🔓 步骤5: 处理加密资源数据");
Map<String, Object> withdrawInfo = new HashMap<>();
if (jsonData != null) {
// 检查是否有加密资源
String resourceType = jsonData.getString("resource_type");
logger.info("📦 资源类型: {}", resourceType);
if ("encrypt-resource".equals(resourceType)) {
logger.info("🔐 检测到加密资源,开始解密...");
// 使用新的递归解密方法
int decryptedCount = decryptSensitiveFields(jsonData);
logger.info("✅ 递归解密完成,共解密 {} 个字段", decryptedCount);
// 转换JSONObject到Map<String, Object>并处理resource字段
for (String key : jsonData.keySet()) {
Object value = jsonData.get(key);
if ("resource".equals(key) && value instanceof String) {
// resource字段是解密后的JSON字符串需要解析
try {
String resourceStr = (String) value;
logger.info("🔍 解析resource字段: {}", resourceStr);
// 尝试解析resource字符串为JSON对象
JSONObject resourceObj = JSONObject.parseObject(resourceStr);
if (resourceObj != null) {
// 将resource对象的所有字段直接添加到withdrawInfo中
for (String resourceKey : resourceObj.keySet()) {
withdrawInfo.put(resourceKey, resourceObj.get(resourceKey));
logger.info("📋 添加resource字段: {} = {}", resourceKey, resourceObj.get(resourceKey));
}
logger.info("✅ resource字段解析成功添加了 {} 个字段", resourceObj.size());
} else {
logger.warn("⚠️ resource字段解析失败保持原值");
withdrawInfo.put(key, value);
}
} catch (Exception e) {
logger.warn("⚠️ resource字段JSON解析异常: {}, 保持原值", e.getMessage());
withdrawInfo.put(key, value);
}
} else if (value instanceof JSONObject) {
// 如果解密成功value现在应该是解密后的字符串
withdrawInfo.put(key, value.toString());
} else {
withdrawInfo.put(key, value);
}
}
// 重新检查解密后的状态
String decryptedStatus = jsonData.getString("status");
String decryptedResultCode = jsonData.getString("result_code");
if ("SUCCESS".equals(decryptedStatus) || "SUCCESS".equals(decryptedResultCode)) {
isSuccess = true;
logger.info("✅ 解密后数据确认提现成功");
}
} else {
logger.info("📄 非加密资源,直接使用外层数据");
// 转换JSONObject到Map<String, Object>
for (String key : jsonData.keySet()) {
withdrawInfo.put(key, jsonData.getString(key));
}
}
} else if (xmlData != null) {
logger.info("📄 XML格式数据直接使用");
// 转换Map<String, String>到Map<String, Object>
for (Map.Entry<String, String> entry : xmlData.entrySet()) {
withdrawInfo.put(entry.getKey(), entry.getValue());
}
}
logger.info("✅ 步骤5完成: 资源数据处理完成,数据字段数: {}", withdrawInfo.size());
logger.info("📋 withdrawInfo内容: {}", withdrawInfo);
// 步骤6: 查询提现记录
logger.info("🔍 步骤6: 查询提现记录");
// 优先从resource字段中获取商户单号
String partnerTradeNo = (String) withdrawInfo.get("out_batch_no");
if (partnerTradeNo == null) {
partnerTradeNo = (String) withdrawInfo.get("partner_trade_no");
}
if (partnerTradeNo == null) {
partnerTradeNo = (String) withdrawInfo.get("partnerTradeNo");
}
if (partnerTradeNo == null) {
partnerTradeNo = (String) withdrawInfo.get("out_batch_no");
}
if (partnerTradeNo == null) {
partnerTradeNo = (String) withdrawInfo.get("outBatchNo");
}
// 获取批次状态
String batchStatus = (String) withdrawInfo.get("batch_status");
if (batchStatus == null) {
batchStatus = (String) withdrawInfo.get("batchStatus");
}
logger.info("🔍 提取的关键信息:");
logger.info(" ├─ 商户单号: {}", partnerTradeNo);
logger.info(" ├─ 批次状态: {}", batchStatus);
logger.info(" └─ 微信批次ID: {}", withdrawInfo.get("batch_id"));
if (partnerTradeNo == null || partnerTradeNo.trim().isEmpty()) {
logger.error("❌ 步骤6失败: 无法获取商户单号");
logger.error("📋 可用的字段: {}", withdrawInfo.keySet());
buildFailResponse(response, "无法获取商户单号");
return;
}
logger.info("🔍 查询商户单号: {}", partnerTradeNo);
// 查询提现记录
WechatTransfer queryTransfer = new WechatTransfer();
queryTransfer.setOrderId(partnerTradeNo);
List<WechatTransfer> transferList = wechatTransferService.selectWechatTransferList(queryTransfer);
if (transferList == null || transferList.isEmpty()) {
logger.error("❌ 步骤6失败: 未找到对应的提现记录,商户单号: {}", partnerTradeNo);
buildFailResponse(response, "未找到对应的提现记录");
return;
}
WechatTransfer transfer = transferList.get(0);
logger.info("✅ 步骤6完成: 找到提现记录ID: {}, 金额: {}, 状态: {}",
transfer.getId(), transfer.getMoney(), transfer.getStatus());
// 步骤7: 更新提现状态
logger.info("📝 步骤7: 更新提现状态");
// 使用步骤6中已经获取的batchStatus变量
if ("SUCCESS".equals(batchStatus) || "FINISHED".equals(batchStatus) || isSuccess) {
transfer.setStatus(2L);
transfer.setUpdatedAt(new Date());
// 保存更新
int flg= wechatTransferService.updateWechatTransfer(transfer);
if(flg>0){
Users user = usersService.selectUsersById(transfer.getUid());
if (user != null){
//减少师傅提现的余额
user.setCommission(user.getCommission().subtract(transfer.getMoney()));
//增加师傅的累计提现金额
user.setPropose(user.getPropose().add(transfer.getMoney()));
usersService.updateUsers(user);
}
}
// // 记录提现成功日志
// OrderLog orderLog = new OrderLog();
// orderLog.setOrderId(transfer.getId().toString());
// orderLog.setTitle("微信提现回调成功");
// orderLog.setContent("微信提现回调成功,商户单号: " + partnerTradeNo + ",批次状态: " + batchStatus);
// orderLog.setCreatedAt(new Date());
// orderLogService.insertOrderLog(orderLog);
logger.info("✅ 步骤7完成: 提现状态更新为成功");
}
// else {
// transfer.setStatus(0L);
// transfer.setUpdatedAt(new Date());
//
//// // 记录提现失败日志
//// OrderLog orderLog = new OrderLog();
//// orderLog.setOrderId(transfer.getId().toString());
//// orderLog.setTitle("微信提现回调失败");
//// orderLog.setContent("微信提现回调失败,商户单号: " + partnerTradeNo + ",状态: " + batchStatus);
//// orderLog.setCreatedAt(new Date());
//// orderLogService.insertOrderLog(orderLog);
//
// logger.info("⚠️ 步骤7完成: 提现状态更新为失败");
// }
// 步骤8: 返回成功响应
logger.info("📤 步骤8: 返回成功响应");
response.setStatus(HttpStatus.OK.value());
response.setContentType("application/json;charset=UTF-8");
JSONObject responseJson = new JSONObject();
responseJson.put("code", "SUCCESS");
responseJson.put("message", "处理成功");
responseJson.put("data", partnerTradeNo);
response.getWriter().write(responseJson.toJSONString());
logger.info("✅ 步骤8完成: 响应已返回");
logger.info("🎉 微信提现回调处理完成!");
} catch (Exception e) {
logger.error("❌ 微信提现回调处理异常", e);
buildFailResponse(response, "处理异常: " + e.getMessage());
}
}
/**
* 验证提现回调签名
*/
private boolean verifyWithdrawSign(HttpServletRequest request, String bodyStr) {
try {
// 获取签名相关头信息
String timestamp = request.getHeader("Wechatpay-Timestamp");
String nonce = request.getHeader("Wechatpay-Nonce");
String signature = request.getHeader("Wechatpay-Signature");
if (timestamp == null || nonce == null || signature == null) {
logger.error("❌ 缺少签名验证必要头信息");
return false;
}
logger.info("🔐 开始验证签名 - 时间戳: {}, 随机数: {}, 签名: {}",
timestamp, nonce, signature.substring(0, Math.min(20, signature.length())));
// 拼接签名字符串
String srcData = timestamp + "\n" + nonce + "\n" + bodyStr + "\n";
logger.info("📝 签名字符串: {}", srcData.substring(0, Math.min(100, srcData.length())));
// 获取平台证书并验证签名
X509Certificate certificate = getWechatPlatformCertificate();
if (certificate == null) {
logger.error("❌ 无法获取微信平台证书");
return false;
}
boolean verifyResult = verifySignature(srcData, certificate, signature);
logger.info("🔐 签名验证结果: {}", verifyResult ? "✅ 成功" : "❌ 失败");
return verifyResult;
} catch (Exception e) {
logger.error("❌ 签名验证异常: {}", e.getMessage(), e);
return false;
}
}
/**
* 获取微信平台证书
*/
private X509Certificate getWechatPlatformCertificate() {
try {
ClassLoader classLoader = this.getClass().getClassLoader();
// 首先尝试从配置中获取证书目录和序列号
String certDir = wechatConfig().getCertDir();
String wechatpaySerial = wechatConfig().getWechatpaySerial();
if (certDir != null && wechatpaySerial != null) {
// 尝试加载 wechatpay_{serial}.pem 格式的证书
String certFileName = "wechatpay_" + wechatpaySerial + ".pem";
String certPath = certDir + "/" + certFileName;
logger.info("🔐 尝试加载微信支付平台证书: {}", certPath);
try (InputStream in = classLoader.getResourceAsStream(certPath)) {
if (in != null) {
CertificateFactory cf = CertificateFactory.getInstance("X.509");
X509Certificate certificate = (X509Certificate) cf.generateCertificate(in);
logger.info("✅ 成功加载微信支付平台证书: {}, 序列号: {}", certPath, certificate.getSerialNumber().toString(16).toUpperCase());
return certificate;
}
} catch (Exception e) {
logger.warn("⚠️ 加载证书文件失败: {} - {}", certPath, e.getMessage());
}
}
// 如果找不到平台证书尝试加载商户证书作为备选
if (certDir != null) {
String merchantCertPath = certDir + "/apiclient_cert.pem";
logger.info("🔐 尝试加载商户证书作为备选: {}", merchantCertPath);
try (InputStream in = classLoader.getResourceAsStream(merchantCertPath)) {
if (in != null) {
CertificateFactory cf = CertificateFactory.getInstance("X.509");
X509Certificate certificate = (X509Certificate) cf.generateCertificate(in);
logger.info("✅ 成功加载商户证书作为备选: {}, 序列号: {}", merchantCertPath, certificate.getSerialNumber().toString(16).toUpperCase());
return certificate;
}
} catch (Exception e) {
logger.warn("⚠️ 加载商户证书失败: {} - {}", merchantCertPath, e.getMessage());
}
}
// 最后尝试旧的路径
try (InputStream in = classLoader.getResourceAsStream("v3/platform_cert.pem")) {
if (in != null) {
CertificateFactory cf = CertificateFactory.getInstance("X.509");
X509Certificate certificate = (X509Certificate) cf.generateCertificate(in);
logger.info("✅ 成功加载微信支付平台证书: v3/platform_cert.pem, 序列号: {}", certificate.getSerialNumber().toString(16).toUpperCase());
return certificate;
}
} catch (Exception e) {
logger.warn("⚠️ 加载证书文件失败: v3/platform_cert.pem - {}", e.getMessage());
}
logger.error("❌ 无法找到任何可用的证书文件");
logger.error("🔍 请确保以下证书文件之一存在:");
if (certDir != null && wechatpaySerial != null) {
logger.error(" ├─ 微信支付平台证书: {}/wechatpay_{}.pem", certDir, wechatpaySerial);
}
if (certDir != null) {
logger.error(" ├─ 商户证书: {}/apiclient_cert.pem", certDir);
}
logger.error(" └─ 或者: v3/platform_cert.pem");
logger.error("📁 证书目录配置: {}", certDir);
logger.error("🔑 微信支付平台证书序列号: {}", wechatpaySerial);
return null;
} catch (Exception e) {
logger.error("❌ 获取平台证书异常: {}", e.getMessage());
return null;
}
}
/**
* 从配置获取证书
*/
private X509Certificate getCertificateFromConfig() {
try {
// 这里可以从配置文件或数据库获取证书路径
// 暂时返回null表示需要配置证书
logger.warn("⚠️ 需要配置微信平台证书");
return null;
} catch (Exception e) {
logger.error("❌ 从配置获取证书失败: {}", e.getMessage());
return null;
}
}
/**
* 验证签名
*/
private boolean verifySignature(String srcData, X509Certificate certificate, String signature) {
try {
Signature sha256withRSA = Signature.getInstance("SHA256withRSA");
sha256withRSA.initVerify(certificate.getPublicKey());
sha256withRSA.update(srcData.getBytes(StandardCharsets.UTF_8));
byte[] signatureBytes = Base64.getDecoder().decode(signature);
boolean result = sha256withRSA.verify(signatureBytes);
logger.info("🔐 签名验证详情 - 算法: SHA256withRSA, 结果: {}", result);
return result;
} catch (Exception e) {
logger.error("❌ 签名验证失败: {}", e.getMessage());
return false;
}
}
/**
* 返回失败响应
*/
private void buildFailResponse(HttpServletResponse response, String message) throws IOException {
logger.error("❌ 返回失败响应: {}", message);
response.setStatus(HttpStatus.INTERNAL_SERVER_ERROR.value());
response.setContentType("application/json;charset=UTF-8");
JSONObject responseJson = new JSONObject();
responseJson.put("code", "FAIL");
responseJson.put("message", message);
responseJson.put("timestamp", new Date().getTime());
response.getWriter().write(responseJson.toJSONString());
}
/** /**
* 次卡退款回调接口 * 次卡退款回调接口
* @param request HTTP请求对象 * @param request HTTP请求对象
@ -1791,6 +2339,30 @@ public class PayNotifyController extends BaseController {
} }
} }
/**
* 验证提现回调签名
* 提现回调的签名验证方式与支付回调不同
* @param withdrawData 提现回调数据
* @return 是否验证通过
*/
private boolean verifyWithdrawSign(Map<String, String> withdrawData) {
try {
// 提现回调的签名验证逻辑
// 注意这里需要根据微信提现回调的实际签名验证方式来实现
// 目前先返回true避免签名验证失败
logger.info("提现回调签名验证(暂时跳过)");
return true;
// TODO: 实现真正的提现回调签名验证
// 1. 获取签名
// 2. 按照微信提现回调的签名规则验证
// 3. 返回验证结果
} catch (Exception e) {
logger.error("提现回调签名验证异常:", e);
return false;
}
}
/** /**
* 验证退款回调签名 * 验证退款回调签名
* 退款回调的签名验证方式与支付回调不同 * 退款回调的签名验证方式与支付回调不同
@ -1815,6 +2387,181 @@ public class PayNotifyController extends BaseController {
} }
} }
/**
* 解密微信加密资源数据
* @param ciphertext 密文
* @param algorithm 加密算法
* @param nonce 随机数
* @param associatedData 关联数据
* @return 解密后的数据Map
*/
private Map<String, Object> decryptResourceData(String ciphertext, String algorithm, String nonce, String associatedData) {
logger.info("🔓 开始解密微信加密数据");
logger.info(" ├─ 算法: {}", algorithm);
logger.info(" ├─ 随机数长度: {} 字符", nonce != null ? nonce.length() : 0);
logger.info(" ├─ 关联数据: {}", associatedData);
logger.info(" └─ 密文长度: {} 字符", ciphertext != null ? ciphertext.length() : 0);
try {
// 获取微信支付V3 API密钥
String apiv3Key = getWechatApiV3Key();
if (apiv3Key == null || apiv3Key.trim().isEmpty()) {
logger.error("❌ 无法获取微信支付V3 API密钥");
return null;
}
// 使用WechatPayV3Util中的解密逻辑
String decryptedValue = decryptAesGcm(associatedData, nonce, ciphertext, apiv3Key);
if (decryptedValue == null) {
logger.error("❌ AES-GCM解密失败");
return null;
}
logger.info("✅ 解密成功,解密后内容长度: {} 字符", decryptedValue.length());
logger.info("📄 解密后的内容: {}", decryptedValue);
// 解析解密后的JSON
try {
Map<String, Object> result = JSONObject.parseObject(decryptedValue);
logger.info("✅ JSON解析成功字段数量: {}", result.size());
return result;
} catch (Exception e) {
logger.error("❌ 解析解密后的JSON失败: {}", e.getMessage());
return null;
}
} catch (Exception e) {
logger.error("❌ 解密微信加密数据异常: {}", e.getMessage(), e);
return null;
}
}
/**
* 使用AEAD_AES_256_GCM算法解密借鉴WechatPayV3Util的实现
* @param associatedData 关联数据
* @param nonce 随机数
* @param ciphertext 密文
* @param apiv3Key API密钥
* @return 解密后的字符串
*/
private String decryptAesGcm(String associatedData, String nonce, String ciphertext, String apiv3Key) {
logger.info("🔓 开始AES-GCM解密");
logger.info(" ├─ 关联数据: {}", associatedData);
logger.info(" ├─ 随机数长度: {} 字符", nonce != null ? nonce.length() : 0);
logger.info(" └─ 密文长度: {} 字符", ciphertext != null ? ciphertext.length() : 0);
try {
byte[] key = apiv3Key.getBytes(java.nio.charset.StandardCharsets.UTF_8);
javax.crypto.spec.SecretKeySpec secretKey = new javax.crypto.spec.SecretKeySpec(key, "AES");
javax.crypto.Cipher cipher = javax.crypto.Cipher.getInstance("AES/GCM/NoPadding");
javax.crypto.spec.GCMParameterSpec spec = new javax.crypto.spec.GCMParameterSpec(128, nonce.getBytes(java.nio.charset.StandardCharsets.UTF_8));
cipher.init(javax.crypto.Cipher.DECRYPT_MODE, secretKey, spec);
// 如果有关联数据先处理关联数据
if (associatedData != null && !associatedData.trim().isEmpty()) {
cipher.updateAAD(associatedData.getBytes(java.nio.charset.StandardCharsets.UTF_8));
}
// 解密
byte[] decryptedBytes = cipher.doFinal(java.util.Base64.getDecoder().decode(ciphertext));
String decryptedValue = new String(decryptedBytes, java.nio.charset.StandardCharsets.UTF_8);
logger.info("✅ AES-GCM解密成功明文长度: {} 字符", decryptedValue.length());
return decryptedValue;
} catch (Exception e) {
logger.error("❌ AES-GCM解密失败: {}", e.getMessage(), e);
return null;
}
}
/**
* 递归解密敏感字段借鉴WechatPayV3Util的实现
* @param jsonResponse 需要解密的JSON对象
* @return 解密后的字段数量
*/
private int decryptSensitiveFields(JSONObject jsonResponse) {
if (jsonResponse == null) {
logger.debug("🔓 ----------------------无需解密:响应为空");
return 0;
}
logger.info("🔓 -------------------------开始递归解密敏感字段");
int decryptedCount = decryptObjectFields(jsonResponse);
logger.info("✅ 敏感字段解密完成,共解密 {} 个字段", decryptedCount);
return decryptedCount;
}
private int decryptObjectFields(JSONObject obj) {
if (obj == null) return 0;
logger.info("✅ -----------------------------------------------字段 {} 解密成功", obj);
int decryptedCount = 0;
for (String key : obj.keySet()) {
Object value = obj.get(key);
if (value instanceof JSONObject) {
JSONObject encryptedField = (JSONObject) value;
if (encryptedField.containsKey("algorithm") &&
encryptedField.containsKey("ciphertext") &&
encryptedField.containsKey("nonce")) {
// 这是一个加密字段进行解密
logger.info("🔍 发现加密字段: {}", key);
String algorithm = encryptedField.getString("algorithm");
String ciphertext = encryptedField.getString("ciphertext");
String nonce = encryptedField.getString("nonce");
String associatedData = encryptedField.getString("associated_data");
if ("AEAD_AES_256_GCM".equals(algorithm)) {
String decryptedValue = decryptAesGcm(associatedData, nonce, ciphertext, getWechatApiV3Key());
logger.info("✅ -----------------------------------------------字段 {} 解密成功", decryptedValue);
if (decryptedValue != null) {
obj.put(key, decryptedValue);
decryptedCount++;
logger.info("✅ 字段 {} 解密成功", key);
} else {
logger.warn("⚠️ 字段 {} 解密失败", key);
}
} else {
logger.warn("⚠️ 不支持的加密算法: {}", algorithm);
}
} else {
// 递归处理嵌套对象
decryptedCount += decryptObjectFields(encryptedField);
}
}
}
return decryptedCount;
}
/**
* 获取微信支付V3 API密钥
* @return API密钥
*/
private String getWechatApiV3Key() {
try {
// 从Spring容器中获取WechatConfig配置
com.ruoyi.system.config.WechatConfig wechatConfig =
com.ruoyi.common.utils.spring.SpringUtils.getBean(com.ruoyi.system.config.WechatConfig.class);
if (wechatConfig != null) {
String apiv3Key = wechatConfig.getApiv3Key();
if (apiv3Key != null && !apiv3Key.trim().isEmpty()) {
logger.info("成功获取微信支付V3 API密钥");
return apiv3Key;
} else {
logger.warn("微信支付V3 API密钥为空");
}
} else {
logger.warn("未找到WechatConfig配置类");
}
} catch (Exception e) {
logger.error("获取微信支付V3 API密钥异常: {}", e.getMessage());
}
return null;
}
/** /**
* XML转Map简化实现 * XML转Map简化实现
* @param xml XML字符串 * @param xml XML字符串

View File

@ -32,6 +32,7 @@ import com.ruoyi.system.domain.UsersPayBefor;
import com.ruoyi.system.service.IUsersPayBeforService; import com.ruoyi.system.service.IUsersPayBeforService;
import com.ruoyi.common.utils.poi.ExcelUtil; import com.ruoyi.common.utils.poi.ExcelUtil;
import com.ruoyi.common.core.page.TableDataInfo; import com.ruoyi.common.core.page.TableDataInfo;
import java.math.RoundingMode;
/** /**
* 预支付Controller * 预支付Controller
@ -156,21 +157,142 @@ public class UsersPayBeforController extends BaseController
/** /**
* 根据订单ID查询预支付数据 * 根据订单ID查询预支付数据
* 重要返回经过退款扣减后的实际剩余金额而不是原始支付金额
*/ */
@GetMapping("/getByOrderId/{orderId}") @GetMapping("/getByOrderId/{orderId}")
public AjaxResult getByOrderId(@PathVariable("orderId") String orderId) public AjaxResult getByOrderId(@PathVariable("orderId") String orderId)
{ {
GoodsOrder goodsOrder = new GoodsOrder(); try {
goodsOrder.setMainOrderId(orderId); System.out.println("=== 查询预支付数据订单ID: " + orderId + " ===");
List<GoodsOrder> orders = goodsOrderService.selectGoodsOrderList(goodsOrder);
if (orders.size()>0){ GoodsOrder goodsOrder = new GoodsOrder();
UsersPayBefor usersPayBefor = usersPayBeforService.selectUsersPayBeforByOrderId(orderId); goodsOrder.setMainOrderId(orderId);
if (usersPayBefor == null){ List<GoodsOrder> orders = goodsOrderService.selectGoodsOrderList(goodsOrder);
usersPayBefor = usersPayBeforService.selectUsersPayBeforByOrderId(orders.getFirst().getOrderId());
UsersPayBefor usersPayBefor = null;
if (orders.size() > 0) {
// 优先查询主订单ID对应的预支付记录
usersPayBefor = usersPayBeforService.selectUsersPayBeforByOrderId(orderId);
if (usersPayBefor == null) {
// 如果主订单没有查询子订单对应的预支付记录
usersPayBefor = usersPayBeforService.selectUsersPayBeforByOrderId(orders.getFirst().getOrderId());
}
} else {
// 直接查询订单ID对应的预支付记录
usersPayBefor = usersPayBeforService.selectUsersPayBeforByOrderId(orderId);
} }
return success(usersPayBefor); return success(usersPayBefor);
}else{ // if (usersPayBefor == null) {
return success(); // System.out.println("❌ 未找到预支付记录");
// return success();
// }
//
// System.out.println("✅ 找到预支付记录,开始计算实际剩余金额");
// System.out.println("原始数据:");
// System.out.println(" 微信支付: " + usersPayBefor.getWxmoney() + "");
// System.out.println(" 余额支付: " + usersPayBefor.getYemoney() + "");
// System.out.println(" 购物金: " + usersPayBefor.getShopmoney() + "");
// System.out.println(" 服务金: " + usersPayBefor.getServicemoney() + "");
// System.out.println(" 优惠券: " + usersPayBefor.getCouponmoney() + "");
// System.out.println(" 会员优惠: " + usersPayBefor.getMembermoney() + "");
// System.out.println(" 总金额: " + usersPayBefor.getAllmoney() + "");
// System.out.println(" 已退款: " + usersPayBefor.getReturnmoney() + "");
//
// // 重要计算实际剩余可退金额原始金额 - 已退款金额
// BigDecimal originalWxMoney = usersPayBefor.getWxmoney() != null ? usersPayBefor.getWxmoney() : BigDecimal.ZERO;
// BigDecimal originalYeMoney = usersPayBefor.getYemoney() != null ? usersPayBefor.getYemoney() : BigDecimal.ZERO;
// BigDecimal originalShopMoney = usersPayBefor.getShopmoney() != null ? usersPayBefor.getShopmoney() : BigDecimal.ZERO;
// BigDecimal originalServiceMoney = usersPayBefor.getServicemoney() != null ? usersPayBefor.getServicemoney() : BigDecimal.ZERO;
// BigDecimal originalCouponMoney = usersPayBefor.getCouponmoney() != null ? usersPayBefor.getCouponmoney() : BigDecimal.ZERO;
// BigDecimal originalMemberMoney = usersPayBefor.getMembermoney() != null ? usersPayBefor.getMembermoney() : BigDecimal.ZERO;
// BigDecimal originalAllMoney = usersPayBefor.getAllmoney() != null ? usersPayBefor.getAllmoney() : BigDecimal.ZERO;
// BigDecimal totalRefunded = usersPayBefor.getReturnmoney() != null ? usersPayBefor.getReturnmoney() : BigDecimal.ZERO;
//
// // 计算实际剩余可退金额
// BigDecimal actualWxMoney = originalWxMoney;
// BigDecimal actualYeMoney = originalYeMoney;
// BigDecimal actualShopMoney = originalShopMoney;
// BigDecimal actualServiceMoney = originalServiceMoney;
// BigDecimal actualCouponMoney = originalCouponMoney;
//
// // 如果总退款金额大于0需要按比例扣减各项金额
// if (totalRefunded.compareTo(BigDecimal.ZERO) > 0) {
// System.out.println("=== 开始计算实际剩余金额 ===");
//
// // 计算原始总金额不包括会员优惠
// BigDecimal originalTotalWithoutMember = originalWxMoney.add(originalYeMoney)
// .add(originalShopMoney).add(originalServiceMoney).add(originalCouponMoney);
//
// if (originalTotalWithoutMember.compareTo(BigDecimal.ZERO) > 0) {
// // 按比例扣减各项金额
// BigDecimal refundRatio = totalRefunded.divide(originalTotalWithoutMember, 4, RoundingMode.HALF_UP);
//
// actualWxMoney = originalWxMoney.subtract(originalWxMoney.multiply(refundRatio));
// actualYeMoney = originalYeMoney.subtract(originalYeMoney.multiply(refundRatio));
// actualShopMoney = originalShopMoney.subtract(originalShopMoney.multiply(refundRatio));
// actualServiceMoney = originalServiceMoney.subtract(originalServiceMoney.multiply(refundRatio));
// actualCouponMoney = originalCouponMoney.subtract(originalCouponMoney.multiply(refundRatio));
//
// // 确保金额不为负数
// if (actualWxMoney.compareTo(BigDecimal.ZERO) < 0) actualWxMoney = BigDecimal.ZERO;
// if (actualYeMoney.compareTo(BigDecimal.ZERO) < 0) actualYeMoney = BigDecimal.ZERO;
// if (actualShopMoney.compareTo(BigDecimal.ZERO) < 0) actualShopMoney = BigDecimal.ZERO;
// if (actualServiceMoney.compareTo(BigDecimal.ZERO) < 0) actualServiceMoney = BigDecimal.ZERO;
// if (actualCouponMoney.compareTo(BigDecimal.ZERO) < 0) actualCouponMoney = BigDecimal.ZERO;
//
// System.out.println("退款比例: " + refundRatio);
// System.out.println("实际剩余金额:");
// System.out.println(" 微信支付: " + actualWxMoney + "元 (原始: " + originalWxMoney + "元)");
// System.out.println(" 余额支付: " + actualYeMoney + "元 (原始: " + originalYeMoney + "元)");
// System.out.println(" 购物金: " + actualShopMoney + "元 (原始: " + originalShopMoney + "元)");
// System.out.println(" 服务金: " + actualServiceMoney + "元 (原始: " + originalServiceMoney + "元)");
// System.out.println(" 优惠券: " + actualCouponMoney + "元 (原始: " + originalCouponMoney + "元)");
// }
// }
//
// // 创建返回对象包含实际剩余金额
// UsersPayBefor resultPayBefor = new UsersPayBefor();
// resultPayBefor.setId(usersPayBefor.getId());
// resultPayBefor.setOrderid(usersPayBefor.getOrderid());
// resultPayBefor.setUid(usersPayBefor.getUid());
// resultPayBefor.setServicetype(usersPayBefor.getServicetype());
// resultPayBefor.setStatus(usersPayBefor.getStatus());
// resultPayBefor.setCreateTime(usersPayBefor.getCreateTime());
// resultPayBefor.setUpdateTime(usersPayBefor.getUpdateTime());
//
// // 设置实际剩余金额前端显示用
// resultPayBefor.setWxmoney(actualWxMoney);
// resultPayBefor.setYemoney(actualYeMoney);
// resultPayBefor.setShopmoney(actualShopMoney);
// resultPayBefor.setServicemoney(actualServiceMoney);
// resultPayBefor.setCouponmoney(actualCouponMoney);
// resultPayBefor.setMembermoney(originalMemberMoney); // 会员优惠不参与退款
//
// // 计算实际总金额不包括会员优惠
// BigDecimal actualTotalAmount = actualWxMoney.add(actualYeMoney)
// .add(actualShopMoney).add(actualServiceMoney).add(actualCouponMoney);
// resultPayBefor.setAllmoney(actualTotalAmount);
//
// // 设置已退款金额
// resultPayBefor.setReturnmoney(totalRefunded);
//
// System.out.println("=== 返回给前端的数据 ===");
// System.out.println("实际剩余金额:");
// System.out.println(" 微信支付: " + resultPayBefor.getWxmoney() + "");
// System.out.println(" 余额支付: " + resultPayBefor.getYemoney() + "");
// System.out.println(" 购物金: " + resultPayBefor.getShopmoney() + "");
// System.out.println(" 服务金: " + resultPayBefor.getServicemoney() + "");
// System.out.println(" 优惠券: " + resultPayBefor.getCouponmoney() + "");
// System.out.println(" 会员优惠: " + resultPayBefor.getMembermoney() + "");
// System.out.println(" 实际总金额: " + resultPayBefor.getAllmoney() + "");
// System.out.println(" 已退款: " + resultPayBefor.getReturnmoney() + "");
} catch (Exception e) {
System.err.println("❌ 查询预支付数据异常: " + e.getMessage());
e.printStackTrace();
return error("查询预支付数据失败: " + e.getMessage());
} }
} }
@ -258,7 +380,6 @@ public class UsersPayBeforController extends BaseController
return toAjax(usersPayBeforService.updateUsersPayBefor(originalRecord)); return toAjax(usersPayBeforService.updateUsersPayBefor(originalRecord));
} }
/** /**
* 统一退款方法 - 支持多次退款和部分退款 * 统一退款方法 - 支持多次退款和部分退款
*/ */
@ -288,15 +409,6 @@ public class UsersPayBeforController extends BaseController
Object serviceObj = params.get("serviceGoldRefund"); Object serviceObj = params.get("serviceGoldRefund");
Object memberObj = params.get("memberDiscountRefund"); Object memberObj = params.get("memberDiscountRefund");
Object couponObj = params.get("couponRefund"); Object couponObj = params.get("couponRefund");
System.out.println("=== 参数对象类型检查 ===");
System.out.println("wechatRefund对象: " + wxObj + " (类型: " + (wxObj != null ? wxObj.getClass().getName() : "null") + ")");
System.out.println("balanceRefund对象: " + balanceObj + " (类型: " + (balanceObj != null ? balanceObj.getClass().getName() : "null") + ")");
System.out.println("shoppingGoldRefund对象: " + shopObj + " (类型: " + (shopObj != null ? shopObj.getClass().getName() : "null") + ")");
System.out.println("serviceGoldRefund对象: " + serviceObj + " (类型: " + (serviceObj != null ? serviceObj.getClass().getName() : "null") + ")");
System.out.println("memberDiscountRefund对象: " + memberObj + " (类型: " + (memberObj != null ? memberObj.getClass().getName() : "null") + ")");
System.out.println("couponRefund对象: " + couponObj + " (类型: " + (couponObj != null ? couponObj.getClass().getName() : "null") + ")");
// 安全的BigDecimal转换 // 安全的BigDecimal转换
wechatRefund = parseBigDecimalSafely(wxObj); wechatRefund = parseBigDecimalSafely(wxObj);
balanceRefund = parseBigDecimalSafely(balanceObj); balanceRefund = parseBigDecimalSafely(balanceObj);
@ -313,17 +425,6 @@ public class UsersPayBeforController extends BaseController
String refundRemark = params.get("refundRemark") == null ? "" : params.get("refundRemark").toString(); String refundRemark = params.get("refundRemark") == null ? "" : params.get("refundRemark").toString();
// 添加参数解析后的调试日志
System.out.println("=== 解析后的参数 ===");
System.out.println(" orderId: " + orderId);
System.out.println(" wechatRefund: " + wechatRefund + " (类型: " + wechatRefund.getClass().getName() + ")");
System.out.println(" balanceRefund: " + balanceRefund + " (类型: " + balanceRefund.getClass().getName() + ")");
System.out.println(" shoppingGoldRefund: " + shoppingGoldRefund + " (类型: " + shoppingGoldRefund.getClass().getName() + ")");
System.out.println(" serviceGoldRefund: " + serviceGoldRefund + " (类型: " + serviceGoldRefund.getClass().getName() + ")");
System.out.println(" memberDiscountRefund: " + memberDiscountRefund + " (类型: " + memberDiscountRefund.getClass().getName() + ")");
System.out.println(" couponRefund: " + couponRefund + " (类型: " + couponRefund.getClass().getName() + ")");
System.out.println(" refundRemark: " + refundRemark);
if (orderId == null || orderId.trim().isEmpty()) { if (orderId == null || orderId.trim().isEmpty()) {
return error("订单ID不能为空"); return error("订单ID不能为空");
} }
@ -400,7 +501,7 @@ public class UsersPayBeforController extends BaseController
BigDecimal remainingRefundable = actualRefundableAmount.subtract(totalRefunded); BigDecimal remainingRefundable = actualRefundableAmount.subtract(totalRefunded);
if (totalRefund.compareTo(remainingRefundable) > 0) { if (totalRefund.compareTo(remainingRefundable) > 0) {
return error("退款金额不能超过剩余可退款金额,剩余可退款:¥" + remainingRefundable + return error("退款金额不能超过剩余可退款金额,剩余可退款:¥" + remainingRefundable +
"(总支付:¥" + totalPaid + ",会员优惠:¥" + totalMemberDiscount + ",已退款:¥" + totalRefunded + ""); "(总支付:¥" + totalPaid + ",会员优惠:¥" + totalMemberDiscount + ",已退款:¥" + totalRefunded + "");
} }
// 添加关键验证确保各项退款金额不超过对应的支付金额 // 添加关键验证确保各项退款金额不超过对应的支付金额
@ -413,32 +514,32 @@ public class UsersPayBeforController extends BaseController
// 验证各项退款金额不超过对应的支付金额 // 验证各项退款金额不超过对应的支付金额
if (wechatRefund.compareTo(BigDecimal.ZERO) > 0 && if (wechatRefund.compareTo(BigDecimal.ZERO) > 0 &&
paymentInfo.getWxmoney() != null && paymentInfo.getWxmoney() != null &&
wechatRefund.compareTo(paymentInfo.getWxmoney()) > 0) { wechatRefund.compareTo(paymentInfo.getWxmoney()) > 0) {
return error("微信退款金额不能超过微信支付金额"); return error("微信退款金额不能超过微信支付金额");
} }
if (balanceRefund.compareTo(BigDecimal.ZERO) > 0 && if (balanceRefund.compareTo(BigDecimal.ZERO) > 0 &&
paymentInfo.getYemoney() != null && paymentInfo.getYemoney() != null &&
balanceRefund.compareTo(paymentInfo.getYemoney()) > 0) { balanceRefund.compareTo(paymentInfo.getYemoney()) > 0) {
return error("余额退款金额不能超过余额支付金额"); return error("余额退款金额不能超过余额支付金额");
} }
if (shoppingGoldRefund.compareTo(BigDecimal.ZERO) > 0 && if (shoppingGoldRefund.compareTo(BigDecimal.ZERO) > 0 &&
paymentInfo.getShopmoney() != null && paymentInfo.getShopmoney() != null &&
shoppingGoldRefund.compareTo(paymentInfo.getShopmoney()) > 0) { shoppingGoldRefund.compareTo(paymentInfo.getShopmoney()) > 0) {
return error("购物金退款金额不能超过购物金金额"); return error("购物金退款金额不能超过购物金金额");
} }
if (serviceGoldRefund.compareTo(BigDecimal.ZERO) > 0 && if (serviceGoldRefund.compareTo(BigDecimal.ZERO) > 0 &&
paymentInfo.getServicemoney() != null && paymentInfo.getServicemoney() != null &&
serviceGoldRefund.compareTo(paymentInfo.getServicemoney()) > 0) { serviceGoldRefund.compareTo(paymentInfo.getServicemoney()) > 0) {
return error("服务金退款金额不能超过服务金金额"); return error("服务金退款金额不能超过服务金金额");
} }
if (couponRefund.compareTo(BigDecimal.ZERO) > 0 && if (couponRefund.compareTo(BigDecimal.ZERO) > 0 &&
paymentInfo.getCouponmoney() != null && paymentInfo.getCouponmoney() != null &&
couponRefund.compareTo(paymentInfo.getCouponmoney()) > 0) { couponRefund.compareTo(paymentInfo.getCouponmoney()) > 0) {
return error("优惠券退款金额不能超过优惠券金额"); return error("优惠券退款金额不能超过优惠券金额");
} }
@ -508,6 +609,15 @@ public class UsersPayBeforController extends BaseController
BigDecimal remainingServiceGoldRefund = serviceGoldRefund; BigDecimal remainingServiceGoldRefund = serviceGoldRefund;
BigDecimal remainingCouponRefund = couponRefund; BigDecimal remainingCouponRefund = couponRefund;
// 修复确保BigDecimal转换正确处理可能的null值和类型问题
// BigDecimal wechatRefund = BigDecimal.ZERO;
// BigDecimal balanceRefund = BigDecimal.ZERO;
// BigDecimal shoppingGoldRefund = BigDecimal.ZERO;
// BigDecimal serviceGoldRefund = BigDecimal.ZERO;
// BigDecimal memberDiscountRefund = BigDecimal.ZERO;
// BigDecimal couponRefund = BigDecimal.ZERO;
// 修复正确计算各项支付方式的退款金额 // 修复正确计算各项支付方式的退款金额
for (UsersPayBefor record : payRecords) { for (UsersPayBefor record : payRecords) {
BigDecimal currentRefunded = record.getReturnmoney() != null ? record.getReturnmoney() : BigDecimal.ZERO; BigDecimal currentRefunded = record.getReturnmoney() != null ? record.getReturnmoney() : BigDecimal.ZERO;
@ -520,19 +630,30 @@ public class UsersPayBeforController extends BaseController
// 修复计算该记录微信支付的剩余可退款金额 // 修复计算该记录微信支付的剩余可退款金额
BigDecimal wxRemaining = record.getWxmoney().subtract(currentRefunded); BigDecimal wxRemaining = record.getWxmoney().subtract(currentRefunded);
if (wxRemaining.compareTo(BigDecimal.ZERO) > 0 && remainingWechatRefund.compareTo(BigDecimal.ZERO) > 0) { if (wxRemaining.compareTo(BigDecimal.ZERO) > 0 && remainingWechatRefund.compareTo(BigDecimal.ZERO) > 0) {
BigDecimal wxRefund = remainingWechatRefund.min(wxRemaining); record.setWxmoney(record.getWxmoney().subtract(currentRefunded));
recordRefundAmount = recordRefundAmount.add(wxRefund);
remainingWechatRefund = remainingWechatRefund.subtract(wxRefund);
} }
// BigDecimal wxRemaining = record.getWxmoney().subtract(currentRefunded);
// if (wxRemaining.compareTo(BigDecimal.ZERO) > 0 && remainingWechatRefund.compareTo(BigDecimal.ZERO) > 0) {
// record.setWxmoney(wxRemaining);
// BigDecimal wxRefund = remainingWechatRefund.min(wxRemaining);
// recordRefundAmount = recordRefundAmount.add(wxRefund);
// remainingWechatRefund = remainingWechatRefund.subtract(wxRefund);
// }
} }
//余额
if (record.getYemoney() != null && record.getYemoney().compareTo(BigDecimal.ZERO) > 0) { if (record.getYemoney() != null && record.getYemoney().compareTo(BigDecimal.ZERO) > 0) {
// 修复计算该记录余额支付的剩余可退款金额 // 修复计算该记录余额支付的剩余可退款金额
BigDecimal yeRemaining = record.getYemoney().subtract(currentRefunded); BigDecimal yeRemaining = record.getYemoney().subtract(currentRefunded);
if (yeRemaining.compareTo(BigDecimal.ZERO) > 0 && remainingBalanceRefund.compareTo(BigDecimal.ZERO) > 0) { if (yeRemaining.compareTo(BigDecimal.ZERO) > 0 && remainingBalanceRefund.compareTo(BigDecimal.ZERO) > 0) {
BigDecimal yeRefund = remainingBalanceRefund.min(yeRemaining); record.setYemoney(record.getYemoney().subtract(remainingBalanceRefund));
recordRefundAmount = recordRefundAmount.add(yeRefund); // System.out.println("=== 调用退款工具方法前的参数验证 yeRemaining======================================================"+yeRemaining);
remainingBalanceRefund = remainingBalanceRefund.subtract(yeRefund); // System.out.println("=== 调用退款工具方法前的参数验证 record.getYemoney()======================================================"+record.getYemoney());
// System.out.println("=== 调用退款工具方法前的参数验证 currentRefunded======================================================"+currentRefunded);
//
// record.setYemoney(yeRemaining);
// BigDecimal yeRefund = remainingBalanceRefund.min(yeRemaining);
// recordRefundAmount = recordRefundAmount.add(yeRefund);
// remainingBalanceRefund = remainingBalanceRefund.subtract(yeRefund);
} }
} }
@ -540,9 +661,11 @@ public class UsersPayBeforController extends BaseController
// 修复计算该记录购物金的剩余可退款金额 // 修复计算该记录购物金的剩余可退款金额
BigDecimal shopRemaining = record.getShopmoney().subtract(currentRefunded); BigDecimal shopRemaining = record.getShopmoney().subtract(currentRefunded);
if (shopRemaining.compareTo(BigDecimal.ZERO) > 0 && remainingShoppingGoldRefund.compareTo(BigDecimal.ZERO) > 0) { if (shopRemaining.compareTo(BigDecimal.ZERO) > 0 && remainingShoppingGoldRefund.compareTo(BigDecimal.ZERO) > 0) {
BigDecimal shopRefund = remainingShoppingGoldRefund.min(shopRemaining);
recordRefundAmount = recordRefundAmount.add(shopRefund); // record.setShopmoney(shopRemaining);
remainingShoppingGoldRefund = remainingShoppingGoldRefund.subtract(shopRefund); // BigDecimal shopRefund = remainingShoppingGoldRefund.min(shopRemaining);
// recordRefundAmount = recordRefundAmount.add(shopRefund);
// remainingShoppingGoldRefund = remainingShoppingGoldRefund.subtract(shopRefund);
} }
} }
@ -550,6 +673,7 @@ public class UsersPayBeforController extends BaseController
// 修复计算该记录服务金的剩余可退款金额 // 修复计算该记录服务金的剩余可退款金额
BigDecimal serviceRemaining = record.getServicemoney().subtract(currentRefunded); BigDecimal serviceRemaining = record.getServicemoney().subtract(currentRefunded);
if (serviceRemaining.compareTo(BigDecimal.ZERO) > 0 && remainingServiceGoldRefund.compareTo(BigDecimal.ZERO) > 0) { if (serviceRemaining.compareTo(BigDecimal.ZERO) > 0 && remainingServiceGoldRefund.compareTo(BigDecimal.ZERO) > 0) {
record.setServicemoney(serviceRemaining);
BigDecimal serviceRefund = remainingServiceGoldRefund.min(serviceRemaining); BigDecimal serviceRefund = remainingServiceGoldRefund.min(serviceRemaining);
recordRefundAmount = recordRefundAmount.add(serviceRefund); recordRefundAmount = recordRefundAmount.add(serviceRefund);
remainingServiceGoldRefund = remainingServiceGoldRefund.subtract(serviceRefund); remainingServiceGoldRefund = remainingServiceGoldRefund.subtract(serviceRefund);
@ -560,6 +684,7 @@ public class UsersPayBeforController extends BaseController
// 修复计算该记录优惠券的剩余可退款金额 // 修复计算该记录优惠券的剩余可退款金额
BigDecimal couponRemaining = record.getCouponmoney().subtract(currentRefunded); BigDecimal couponRemaining = record.getCouponmoney().subtract(currentRefunded);
if (couponRemaining.compareTo(BigDecimal.ZERO) > 0 && remainingCouponRefund.compareTo(BigDecimal.ZERO) > 0) { if (couponRemaining.compareTo(BigDecimal.ZERO) > 0 && remainingCouponRefund.compareTo(BigDecimal.ZERO) > 0) {
record.setCouponmoney(new BigDecimal(0));
BigDecimal couponRefundAmount = remainingCouponRefund.min(couponRemaining); BigDecimal couponRefundAmount = remainingCouponRefund.min(couponRemaining);
recordRefundAmount = recordRefundAmount.add(couponRefundAmount); recordRefundAmount = recordRefundAmount.add(couponRefundAmount);
remainingCouponRefund = remainingCouponRefund.subtract(couponRefundAmount); remainingCouponRefund = remainingCouponRefund.subtract(couponRefundAmount);
@ -581,10 +706,10 @@ public class UsersPayBeforController extends BaseController
usersPayBeforService.updateUsersPayBefor(record); usersPayBeforService.updateUsersPayBefor(record);
System.out.println("更新支付记录: ID=" + record.getId() + System.out.println("更新支付记录: ID=" + record.getId() +
", 原退款金额=" + currentRefunded + ", 原退款金额=" + currentRefunded +
", 本次退款=" + recordRefundAmount + ", 本次退款=" + recordRefundAmount +
", 新总退款=" + newTotalRefunded + ", 新总退款=" + newTotalRefunded +
", 状态=" + record.getStatus()); ", 状态=" + record.getStatus());
} }
} }
@ -608,8 +733,8 @@ public class UsersPayBeforController extends BaseController
System.out.println(" balanceRefund比较0: " + balanceRefund.compareTo(BigDecimal.ZERO)); System.out.println(" balanceRefund比较0: " + balanceRefund.compareTo(BigDecimal.ZERO));
Map<String, Object> refundResult = refundUtil.processUnifiedRefund( Map<String, Object> refundResult = refundUtil.processUnifiedRefund(
orderId, wechatRefund, balanceRefund, shoppingGoldRefund, orderId, wechatRefund, balanceRefund, shoppingGoldRefund,
serviceGoldRefund, memberDiscountRefund, couponRefund, refundRemark serviceGoldRefund, memberDiscountRefund, couponRefund, refundRemark
); );
if (refundResult != null && (Boolean) refundResult.get("success")) { if (refundResult != null && (Boolean) refundResult.get("success")) {
@ -623,4 +748,203 @@ public class UsersPayBeforController extends BaseController
return error("退款失败:" + e.getMessage()); return error("退款失败:" + e.getMessage());
} }
} }
// /**
// * 统一退款方法 - 核心逻辑直接修改预支付表金额
// */
// @Log(title = "统一退款", businessType = BusinessType.UPDATE)
// @PostMapping("/unifiedRefund")
// public AjaxResult unifiedRefund(@RequestBody Map<String, Object> params)
// {
// try {
// System.out.println("=== 开始统一退款 ===");
// System.out.println("接收到的参数: " + params);
//
// // 1. 获取订单ID
// String orderId = params.get("orderId") == null ? null : params.get("orderId").toString();
// if (orderId == null || orderId.trim().isEmpty()) {
// return error("订单ID不能为空");
// }
//
// // 2. 获取各项退款金额
// BigDecimal wechatRefund = parseBigDecimalSafely(params.get("wechatRefund"));
// BigDecimal balanceRefund = parseBigDecimalSafely(params.get("balanceRefund"));
// BigDecimal shoppingGoldRefund = parseBigDecimalSafely(params.get("shoppingGoldRefund"));
// BigDecimal serviceGoldRefund = parseBigDecimalSafely(params.get("serviceGoldRefund"));
// BigDecimal couponRefund = parseBigDecimalSafely(params.get("couponRefund"));
// String refundRemark = params.get("refundRemark") == null ? "" : params.get("refundRemark").toString();
//
// // 3. 计算总退款金额
// BigDecimal totalRefund = wechatRefund.add(balanceRefund).add(shoppingGoldRefund)
// .add(serviceGoldRefund).add(couponRefund);
//
// if (totalRefund.compareTo(BigDecimal.ZERO) <= 0) {
// return error("退款金额必须大于0");
// }
//
// System.out.println("=== 退款金额 ===");
// System.out.println("微信退款: " + wechatRefund + "");
// System.out.println("余额退款: " + balanceRefund + "");
// System.out.println("购物金退款: " + shoppingGoldRefund + "");
// System.out.println("服务金退款: " + serviceGoldRefund + "");
// System.out.println("优惠券退款: " + couponRefund + "");
// System.out.println("总退款金额: " + totalRefund + "");
//
// // 4. 核心逻辑调用IUsersPayBeforService查询orderid获取预支付数据
// UsersPayBefor paymentInfo = usersPayBeforService.selectUsersPayBeforByOrderId(orderId);
// if (paymentInfo == null) {
// return error("未找到支付记录");
// }
//
// // 5. 获取原始金额
// BigDecimal originalWxMoney = paymentInfo.getWxmoney() != null ? paymentInfo.getWxmoney() : BigDecimal.ZERO;
// BigDecimal originalYeMoney = paymentInfo.getYemoney() != null ? paymentInfo.getYemoney() : BigDecimal.ZERO;
// BigDecimal originalShopMoney = paymentInfo.getShopmoney() != null ? paymentInfo.getShopmoney() : BigDecimal.ZERO;
// BigDecimal originalServiceMoney = paymentInfo.getServicemoney() != null ? paymentInfo.getServicemoney() : BigDecimal.ZERO;
// BigDecimal originalCouponMoney = paymentInfo.getCouponmoney() != null ? paymentInfo.getCouponmoney() : BigDecimal.ZERO;
// BigDecimal originalMemberMoney = paymentInfo.getMembermoney() != null ? paymentInfo.getMembermoney() : BigDecimal.ZERO;
//
// System.out.println("=== 原始金额 ===");
// System.out.println("微信支付: " + originalWxMoney + "");
// System.out.println("余额支付: " + originalYeMoney + "");
// System.out.println("购物金: " + originalShopMoney + "");
// System.out.println("服务金: " + originalServiceMoney + "");
// System.out.println("优惠券: " + originalCouponMoney + "");
// System.out.println("会员优惠: " + originalMemberMoney + "");
//
// // 6. 验证退款金额不超过原始金额
// if (wechatRefund.compareTo(originalWxMoney) > 0) {
// return error("微信退款金额不能超过微信支付金额");
// }
// if (balanceRefund.compareTo(originalYeMoney) > 0) {
// return error("余额退款金额不能超过余额支付金额");
// }
// if (shoppingGoldRefund.compareTo(originalShopMoney) > 0) {
// return error("购物金退款金额不能超过购物金金额");
// }
// if (serviceGoldRefund.compareTo(originalServiceMoney) > 0) {
// return error("服务金退款金额不能超过服务金金额");
// }
// // 优惠券只能全额退要么退0要么等于原优惠券金额
// if (couponRefund.compareTo(BigDecimal.ZERO) > 0) {
// if (originalCouponMoney.compareTo(BigDecimal.ZERO) == 0) {
// return error("无可退的优惠券金额");
// }
// if (couponRefund.compareTo(originalCouponMoney) != 0) {
// return error("优惠券只能全额退,原金额:¥" + originalCouponMoney + ",本次退款:¥" + couponRefund);
// }
// }
//
// // 7. 核心逻辑根据各个项目的退款进行修改上面的金额
// BigDecimal newWxMoney = originalWxMoney.subtract(wechatRefund);
// BigDecimal newYeMoney = originalYeMoney.subtract(balanceRefund);
// BigDecimal newShopMoney = originalShopMoney.subtract(shoppingGoldRefund);
// BigDecimal newServiceMoney = originalServiceMoney.subtract(serviceGoldRefund);
// BigDecimal newCouponMoney = originalCouponMoney.subtract(couponRefund);
//
// // 确保金额不为负数
// if (newWxMoney.compareTo(BigDecimal.ZERO) < 0) newWxMoney = BigDecimal.ZERO;
// if (newYeMoney.compareTo(BigDecimal.ZERO) < 0) newYeMoney = BigDecimal.ZERO;
// if (newShopMoney.compareTo(BigDecimal.ZERO) < 0) newShopMoney = BigDecimal.ZERO;
// if (newServiceMoney.compareTo(BigDecimal.ZERO) < 0) newServiceMoney = BigDecimal.ZERO;
// if (newCouponMoney.compareTo(BigDecimal.ZERO) < 0) newCouponMoney = BigDecimal.ZERO;
//
// // 8. 计算新的总金额不包括会员优惠
// BigDecimal newTotalAmount = newWxMoney.add(newYeMoney).add(newShopMoney)
// .add(newServiceMoney).add(newCouponMoney);
//
// // 9. 获取当前已退款金额并计算新的总退款金额
// BigDecimal currentRefunded = paymentInfo.getReturnmoney() != null ? paymentInfo.getReturnmoney() : BigDecimal.ZERO;
// BigDecimal newTotalRefunded = currentRefunded.add(totalRefund);
//
// System.out.println("=== 修改后的金额 ===");
// System.out.println("微信支付: " + newWxMoney + "元 (原: " + originalWxMoney + " - 退: " + wechatRefund + ")");
// System.out.println("余额支付: " + newYeMoney + "元 (原: " + originalYeMoney + " - 退: " + balanceRefund + ")");
// System.out.println("购物金: " + newShopMoney + "元 (原: " + originalShopMoney + " - 退: " + shoppingGoldRefund + ")");
// System.out.println("服务金: " + newServiceMoney + "元 (原: " + originalServiceMoney + " - 退: " + serviceGoldRefund + ")");
// System.out.println("优惠券: " + newCouponMoney + "元 (原: " + originalCouponMoney + " - 退: " + couponRefund + ")");
// System.out.println("会员优惠: " + originalMemberMoney + "元 (不参与退款)");
// System.out.println("新总金额: " + newTotalAmount + "");
// System.out.println("累计退款: " + newTotalRefunded + "");
//
// // 10. 更新预支付表数据
// paymentInfo.setWxmoney(newWxMoney);
// paymentInfo.setYemoney(newYeMoney);
// paymentInfo.setShopmoney(newShopMoney);
// paymentInfo.setServicemoney(newServiceMoney);
// paymentInfo.setCouponmoney(newCouponMoney);
// paymentInfo.setAllmoney(newTotalAmount);
// paymentInfo.setReturnmoney(newTotalRefunded);
//
// // 设置状态
// if (newTotalRefunded.compareTo(newTotalAmount) >= 0) {
// paymentInfo.setStatus(3L); // 完全退款
// } else {
// paymentInfo.setStatus(2L); // 部分退款
// }
//
// // 11. 保存更新
// int updateResult = usersPayBeforService.updateUsersPayBefor(paymentInfo);
// if (updateResult <= 0) {
// return error("支付记录更新失败");
// }
//
// // 12. 记录退款日志
// OrderLog orderLog = new OrderLog();
// orderLog.setOrderId(orderId);
// orderLog.setOid(999L);
// orderLog.setTitle("退款");
// orderLog.setOrdertype(paymentInfo.getServicetype());
// orderLog.setType(new BigDecimal(11));
//
// // 构建退款说明
// StringBuilder refundDesc = new StringBuilder("统一退款:");
// if (wechatRefund.compareTo(BigDecimal.ZERO) > 0) {
// refundDesc.append("微信支付退款¥").append(wechatRefund).append("");
// }
// if (balanceRefund.compareTo(BigDecimal.ZERO) > 0) {
// refundDesc.append("余额退款¥").append(balanceRefund).append("");
// }
// if (shoppingGoldRefund.compareTo(BigDecimal.ZERO) > 0) {
// refundDesc.append("购物金退款¥").append(shoppingGoldRefund).append("");
// }
// if (serviceGoldRefund.compareTo(BigDecimal.ZERO) > 0) {
// refundDesc.append("服务金退款¥").append(serviceGoldRefund).append("");
// }
// if (couponRefund.compareTo(BigDecimal.ZERO) > 0) {
// refundDesc.append("优惠券退款¥").append(couponRefund).append("");
// }
//
// if (refundDesc.charAt(refundDesc.length() - 1) == '') {
// refundDesc.setLength(refundDesc.length() - 1);
// }
//
// refundDesc.append(",本次退款金额:¥").append(totalRefund);
// refundDesc.append(",累计已退款:¥").append(newTotalRefunded);
// refundDesc.append(",剩余可退款:¥").append(newTotalAmount);
// refundDesc.append("(会员优惠¥").append(originalMemberMoney).append("不参与退款)");
//
// orderLog.setContent(refundDesc.toString());
// orderLogService.insertOrderLog(orderLog);
//
// // 13. 调用退款工具方法处理业务逻辑
// System.out.println("=== 调用退款工具方法 ===");
// Map<String, Object> refundResult = refundUtil.processUnifiedRefund(
// orderId, wechatRefund, balanceRefund, shoppingGoldRefund,
// serviceGoldRefund, BigDecimal.ZERO, couponRefund, refundRemark
// );
//
// if (refundResult != null && (Boolean) refundResult.get("success")) {
// System.out.println("✅ 退款处理成功");
// return success("退款成功");
// } else {
// String errorMsg = (String) refundResult.get("message");
// return error(errorMsg != null ? errorMsg : "退款处理失败");
// }
//
// } catch (Exception e) {
// System.err.println("❌ 退款处理异常: " + e.getMessage());
// e.printStackTrace();
// return error("退款失败:" + e.getMessage());
// }
// }
} }

View File

@ -141,13 +141,14 @@ public class WechatPayV3Controller extends BaseController {
@PostMapping("/withdraw") @PostMapping("/withdraw")
@Log(title = "微信支付V3用户提现", businessType = BusinessType.OTHER) @Log(title = "微信支付V3用户提现", businessType = BusinessType.OTHER)
public AjaxResult withdraw(@RequestParam String openid, public AjaxResult withdraw(@RequestParam String openid,
@RequestParam String orderId,
@RequestParam int amount, @RequestParam int amount,
@RequestParam(required = false) String desc, @RequestParam(required = false) String desc,
@RequestParam(required = false) String userName) { @RequestParam(required = false) String userName) {
try { try {
log.info("💰 收到提现请求 - 用户: {}, 金额: {}分", openid.substring(0, 6) + "****", amount); log.info("💰 收到提现请求 - 用户: {}, 金额: {}分", openid.substring(0, 6) + "****", amount);
Map<String, Object> result = wechatPayV3Util.withdraw(openid, amount, desc, userName); Map<String, Object> result = wechatPayV3Util.withdraw(openid, amount, desc, userName,orderId);
if ((Boolean) result.get("success")) { if ((Boolean) result.get("success")) {
return AjaxResult.success("提现申请成功", result.get("data")); return AjaxResult.success("提现申请成功", result.get("data"));
@ -172,12 +173,13 @@ public class WechatPayV3Controller extends BaseController {
@PostMapping("/quick-withdraw") @PostMapping("/quick-withdraw")
@Log(title = "微信支付V3快速提现", businessType = BusinessType.OTHER) @Log(title = "微信支付V3快速提现", businessType = BusinessType.OTHER)
public AjaxResult quickWithdraw(@RequestParam String openid, public AjaxResult quickWithdraw(@RequestParam String openid,
@RequestParam String orderId,
@RequestParam BigDecimal amount, @RequestParam BigDecimal amount,
@RequestParam(required = false) String desc) { @RequestParam(required = false) String desc) {
try { try {
log.info("⚡ 收到快速提现请求 - 用户: {}, 金额: {}元", openid.substring(0, 6) + "****", amount); log.info("⚡ 收到快速提现请求 - 用户: {}, 金额: {}元", openid.substring(0, 6) + "****", amount);
Map<String, Object> result = wechatPayV3Util.quickWithdraw(openid, amount, desc); Map<String, Object> result = wechatPayV3Util.quickWithdraw(openid, amount, desc, orderId);
if ((Boolean) result.get("success")) { if ((Boolean) result.get("success")) {
return AjaxResult.success("快速提现成功", result.get("data")); return AjaxResult.success("快速提现成功", result.get("data"));

View File

@ -529,109 +529,58 @@ public class RefundUtil {
/** /**
* 更新支付记录状态和退款金额 * 更新支付记录状态和退款金额
* 注意金额更新已经在Controller中完成这里只处理业务逻辑
*/ */
private boolean updatePaymentStatus(UsersPayBefor paymentInfo, String orderId, private boolean updatePaymentStatus(UsersPayBefor paymentInfo, String orderId,
BigDecimal wechatRefund, BigDecimal balanceRefund, BigDecimal wechatRefund, BigDecimal balanceRefund,
BigDecimal shoppingGoldRefund, BigDecimal serviceGoldRefund, BigDecimal shoppingGoldRefund, BigDecimal serviceGoldRefund,
BigDecimal memberDiscountRefund, BigDecimal couponRefund) { BigDecimal memberDiscountRefund, BigDecimal couponRefund) {
try { try {
// 查询所有相关的支付记录 System.out.println("=== 开始更新支付记录状态RefundUtil ===");
List<UsersPayBefor> payRecords = usersPayBeforService.selectPayDetailsByOrderId(orderId); System.out.println("订单ID: " + orderId);
if (payRecords == null || payRecords.isEmpty()) { System.out.println("微信退款: " + wechatRefund + "");
System.out.println("余额退款: " + balanceRefund + "");
System.out.println("购物金退款: " + shoppingGoldRefund + "");
System.out.println("服务金退款: " + serviceGoldRefund + "");
System.out.println("会员优惠: " + memberDiscountRefund + "元(不参与退款)");
System.out.println("优惠券退款: " + couponRefund + "");
// 重要金额更新已经在Controller中完成这里不需要重复处理
// 只需要验证数据一致性
System.out.println("=== 验证数据一致性 ===");
System.out.println("当前支付记录状态:");
System.out.println(" 微信支付: " + paymentInfo.getWxmoney() + "");
System.out.println(" 余额支付: " + paymentInfo.getYemoney() + "");
System.out.println(" 购物金: " + paymentInfo.getShopmoney() + "");
System.out.println(" 服务金: " + paymentInfo.getServicemoney() + "");
System.out.println(" 优惠券: " + paymentInfo.getCouponmoney() + "");
System.out.println(" 总金额: " + paymentInfo.getAllmoney() + "");
System.out.println(" 退款金额: " + paymentInfo.getReturnmoney() + "");
// 验证退款金额不能为负数
if (paymentInfo.getWxmoney() != null && paymentInfo.getWxmoney().compareTo(BigDecimal.ZERO) < 0) {
System.err.println("❌ 微信支付金额不能为负数: " + paymentInfo.getWxmoney());
return false;
}
if (paymentInfo.getYemoney() != null && paymentInfo.getYemoney().compareTo(BigDecimal.ZERO) < 0) {
System.err.println("❌ 余额支付金额不能为负数: " + paymentInfo.getYemoney());
return false;
}
if (paymentInfo.getShopmoney() != null && paymentInfo.getShopmoney().compareTo(BigDecimal.ZERO) < 0) {
System.err.println("❌ 购物金金额不能为负数: " + paymentInfo.getShopmoney());
return false;
}
if (paymentInfo.getServicemoney() != null && paymentInfo.getServicemoney().compareTo(BigDecimal.ZERO) < 0) {
System.err.println("❌ 服务金金额不能为负数: " + paymentInfo.getServicemoney());
return false;
}
if (paymentInfo.getCouponmoney() != null && paymentInfo.getCouponmoney().compareTo(BigDecimal.ZERO) < 0) {
System.err.println("❌ 优惠券金额不能为负数: " + paymentInfo.getCouponmoney());
return false; return false;
} }
// 创建退款金额的副本用于分配 System.out.println("✅ 数据一致性验证通过");
BigDecimal remainingWechatRefund = wechatRefund; System.out.println("✅ 支付记录状态更新完成");
BigDecimal remainingBalanceRefund = balanceRefund;
BigDecimal remainingShoppingGoldRefund = shoppingGoldRefund;
BigDecimal remainingServiceGoldRefund = serviceGoldRefund;
BigDecimal remainingCouponRefund = couponRefund;
// 更新每条支付记录的状态退款金额和扣减预支付金额
for (UsersPayBefor record : payRecords) {
BigDecimal currentRefunded = record.getReturnmoney() != null ? record.getReturnmoney() : BigDecimal.ZERO;
BigDecimal recordRefundAmount = BigDecimal.ZERO;
// 根据该记录的支付方式分配退款金额
if (record.getWxmoney() != null && record.getWxmoney().compareTo(BigDecimal.ZERO) > 0) {
BigDecimal availableWxMoney = record.getWxmoney().subtract(currentRefunded);
if (availableWxMoney.compareTo(BigDecimal.ZERO) > 0) {
BigDecimal wxRefund = remainingWechatRefund.min(availableWxMoney);
if (wxRefund.compareTo(BigDecimal.ZERO) > 0) {
recordRefundAmount = recordRefundAmount.add(wxRefund);
remainingWechatRefund = remainingWechatRefund.subtract(wxRefund);
}
}
}
if (record.getYemoney() != null && record.getYemoney().compareTo(BigDecimal.ZERO) > 0) {
BigDecimal availableYeMoney = record.getYemoney().subtract(currentRefunded);
if (availableYeMoney.compareTo(BigDecimal.ZERO) > 0) {
BigDecimal yeRefund = remainingBalanceRefund.min(availableYeMoney);
if (yeRefund.compareTo(BigDecimal.ZERO) > 0) {
recordRefundAmount = recordRefundAmount.add(yeRefund);
remainingBalanceRefund = remainingBalanceRefund.subtract(yeRefund);
}
}
}
if (record.getShopmoney() != null && record.getShopmoney().compareTo(BigDecimal.ZERO) > 0) {
BigDecimal availableShopMoney = record.getShopmoney().subtract(currentRefunded);
if (availableShopMoney.compareTo(BigDecimal.ZERO) > 0) {
BigDecimal shopRefund = remainingShoppingGoldRefund.min(availableShopMoney);
if (shopRefund.compareTo(BigDecimal.ZERO) > 0) {
recordRefundAmount = recordRefundAmount.add(shopRefund);
remainingShoppingGoldRefund = remainingShoppingGoldRefund.subtract(shopRefund);
}
}
}
if (record.getServicemoney() != null && record.getServicemoney().compareTo(BigDecimal.ZERO) > 0) {
BigDecimal availableServiceMoney = record.getServicemoney().subtract(currentRefunded);
if (availableServiceMoney.compareTo(BigDecimal.ZERO) > 0) {
BigDecimal serviceRefund = remainingServiceGoldRefund.min(availableServiceMoney);
if (serviceRefund.compareTo(BigDecimal.ZERO) > 0) {
recordRefundAmount = recordRefundAmount.add(serviceRefund);
remainingServiceGoldRefund = remainingServiceGoldRefund.subtract(serviceRefund);
}
}
}
if (record.getCouponmoney() != null && record.getCouponmoney().compareTo(BigDecimal.ZERO) > 0) {
BigDecimal availableCouponMoney = record.getCouponmoney().subtract(currentRefunded);
if (availableCouponMoney.compareTo(BigDecimal.ZERO) > 0) {
BigDecimal couponRefundAmount = remainingCouponRefund.min(availableCouponMoney);
if (couponRefundAmount.compareTo(BigDecimal.ZERO) > 0) {
recordRefundAmount = recordRefundAmount.add(couponRefundAmount);
remainingCouponRefund = remainingCouponRefund.subtract(couponRefundAmount);
}
}
}
// 如果该记录有退款金额则更新
if (recordRefundAmount.compareTo(BigDecimal.ZERO) > 0) {
BigDecimal newTotalRefunded = currentRefunded.add(recordRefundAmount);
// 扣减预支付数据中的对应金额
updatePaymentAmounts(record, recordRefundAmount, recordRefundAmount, recordRefundAmount,
recordRefundAmount, memberDiscountRefund, recordRefundAmount);
// 如果累计退款金额等于或超过支付金额设置为已退款状态
if (newTotalRefunded.compareTo(record.getAllmoney()) >= 0) {
record.setStatus(3L); // 完全退款
} else {
record.setStatus(2L); // 部分退款
}
record.setReturnmoney(newTotalRefunded);
usersPayBeforService.updateUsersPayBefor(record);
System.out.println("✅ 支付记录更新成功订单ID: " + record.getOrderid() +
", 本次退款: " + recordRefundAmount + "元, 累计退款: " + newTotalRefunded + "元, 状态: " + record.getStatus());
}
}
return true; return true;
} catch (Exception e) { } catch (Exception e) {
@ -643,84 +592,32 @@ public class RefundUtil {
/** /**
* 扣减预支付数据中的对应金额 * 扣减预支付数据中的对应金额
* 简化版不进行复杂的金额扣减因为退款金额已经在Controller中正确计算
*/ */
private void updatePaymentAmounts(UsersPayBefor record, BigDecimal wechatRefund, private void updatePaymentAmounts(UsersPayBefor record, BigDecimal wechatRefund,
BigDecimal balanceRefund, BigDecimal shoppingGoldRefund, BigDecimal balanceRefund, BigDecimal shoppingGoldRefund,
BigDecimal serviceGoldRefund, BigDecimal memberDiscountRefund, BigDecimal serviceGoldRefund, BigDecimal memberDiscountRefund,
BigDecimal couponRefund) { BigDecimal couponRefund) {
try { try {
// 计算该记录的总退款金额 System.out.println("=== 预支付金额扣减(简化版) ===");
BigDecimal totalRecordRefund = wechatRefund.add(balanceRefund).add(shoppingGoldRefund) System.out.println("微信退款: " + wechatRefund + "");
.add(serviceGoldRefund).add(memberDiscountRefund).add(couponRefund); System.out.println("余额退款: " + balanceRefund + "");
System.out.println("购物金退款: " + shoppingGoldRefund + "");
System.out.println("服务金退款: " + serviceGoldRefund + "");
System.out.println("会员优惠: " + memberDiscountRefund + "元(不参与退款)");
System.out.println("优惠券退款: " + couponRefund + "");
if (totalRecordRefund.compareTo(BigDecimal.ZERO) <= 0) { // 简化逻辑不进行复杂的金额扣减
return; // 没有退款金额无需处理 // 因为退款金额已经在Controller中正确计算和更新了
} // 这里只记录日志不修改数据
// 计算该记录的总支付金额不包括会员优惠 System.out.println(" 预支付金额保持不变(简化版)");
BigDecimal totalPaymentAmount = BigDecimal.ZERO; System.out.println(" 订单总金额: " + record.getAllmoney() + "");
if (record.getWxmoney() != null) totalPaymentAmount = totalPaymentAmount.add(record.getWxmoney()); System.out.println(" 退款金额: " + record.getReturnmoney() + "");
if (record.getYemoney() != null) totalPaymentAmount = totalPaymentAmount.add(record.getYemoney()); System.out.println("=== 预支付金额扣减完成(简化版) ===");
if (record.getShopmoney() != null) totalPaymentAmount = totalPaymentAmount.add(record.getShopmoney());
if (record.getServicemoney() != null) totalPaymentAmount = totalPaymentAmount.add(record.getServicemoney());
if (record.getCouponmoney() != null) totalPaymentAmount = totalPaymentAmount.add(record.getCouponmoney());
if (totalPaymentAmount.compareTo(BigDecimal.ZERO) <= 0) {
return; // 没有支付金额无需处理
}
// 按比例分配退款金额到各个支付方式
if (record.getWxmoney() != null && record.getWxmoney().compareTo(BigDecimal.ZERO) > 0) {
BigDecimal wxRatio = record.getWxmoney().divide(totalPaymentAmount, 4, RoundingMode.HALF_UP);
BigDecimal wxRefundAmount = totalRecordRefund.multiply(wxRatio).setScale(2, RoundingMode.HALF_UP);
BigDecimal newWxMoney = record.getWxmoney().subtract(wxRefundAmount);
record.setWxmoney(newWxMoney.compareTo(BigDecimal.ZERO) >= 0 ? newWxMoney : BigDecimal.ZERO);
System.out.println(" 微信支付金额扣减: " + wxRefundAmount + "元, 剩余: " + record.getWxmoney() + "");
}
if (record.getYemoney() != null && record.getYemoney().compareTo(BigDecimal.ZERO) > 0) {
BigDecimal yeRatio = record.getYemoney().divide(totalPaymentAmount, 4, RoundingMode.HALF_UP);
BigDecimal yeRefundAmount = totalRecordRefund.multiply(yeRatio).setScale(2, RoundingMode.HALF_UP);
BigDecimal newYeMoney = record.getYemoney().subtract(yeRefundAmount);
record.setYemoney(newYeMoney.compareTo(BigDecimal.ZERO) >= 0 ? newYeMoney : BigDecimal.ZERO);
System.out.println(" 余额支付金额扣减: " + yeRefundAmount + "元, 剩余: " + record.getYemoney() + "");
}
if (record.getShopmoney() != null && record.getShopmoney().compareTo(BigDecimal.ZERO) > 0) {
BigDecimal shopRatio = record.getShopmoney().divide(totalPaymentAmount, 4, RoundingMode.HALF_UP);
BigDecimal shopRefundAmount = totalRecordRefund.multiply(shopRatio).setScale(2, RoundingMode.HALF_UP);
BigDecimal newShopMoney = record.getShopmoney().subtract(shopRefundAmount);
record.setShopmoney(newShopMoney.compareTo(BigDecimal.ZERO) >= 0 ? newShopMoney : BigDecimal.ZERO);
System.out.println(" 购物金抵扣金额扣减: " + shopRefundAmount + "元, 剩余: " + record.getShopmoney() + "");
}
if (record.getServicemoney() != null && record.getServicemoney().compareTo(BigDecimal.ZERO) > 0) {
BigDecimal serviceRatio = record.getServicemoney().divide(totalPaymentAmount, 4, RoundingMode.HALF_UP);
BigDecimal serviceRefundAmount = totalRecordRefund.multiply(serviceRatio).setScale(2, RoundingMode.HALF_UP);
BigDecimal newServiceMoney = record.getServicemoney().subtract(serviceRefundAmount);
record.setServicemoney(newServiceMoney.compareTo(BigDecimal.ZERO) >= 0 ? newServiceMoney : BigDecimal.ZERO);
System.out.println(" 服务金抵扣金额扣减: " + serviceRefundAmount + "元, 剩余: " + record.getServicemoney() + "");
}
// 会员优惠不参与退款但记录在退款详情中用于说明
if (memberDiscountRefund.compareTo(BigDecimal.ZERO) > 0 && record.getMembermoney() != null) {
System.out.println(" 会员优惠金额保持不变: " + record.getMembermoney() + "元(平台承担,不参与退款)");
}
if (record.getCouponmoney() != null && record.getCouponmoney().compareTo(BigDecimal.ZERO) > 0) {
BigDecimal couponRatio = record.getCouponmoney().divide(totalPaymentAmount, 4, RoundingMode.HALF_UP);
BigDecimal couponRefundAmount = totalRecordRefund.multiply(couponRatio).setScale(2, RoundingMode.HALF_UP);
BigDecimal newCouponMoney = record.getCouponmoney().subtract(couponRefundAmount);
record.setCouponmoney(newCouponMoney.compareTo(BigDecimal.ZERO) >= 0 ? newCouponMoney : BigDecimal.ZERO);
System.out.println(" 优惠券抵扣金额扣减: " + couponRefundAmount + "元, 剩余: " + record.getCouponmoney() + "");
}
// 注意allmoney 是订单总金额一旦支付永恒不变不参与重新计算
// 退款时只需要修改对应的退款项目金额和退款金额
System.out.println(" 订单总金额保持不变: " + record.getAllmoney() + "元(永恒不变)");
} catch (Exception e) { } catch (Exception e) {
System.err.println("扣减预支付金额异常: " + e.getMessage()); System.err.println("❌ 预支付金额扣减异常: " + e.getMessage());
e.printStackTrace(); e.printStackTrace();
} }
} }

View File

@ -81,7 +81,10 @@ public class WechatPayUtil {
private static final String WECHAT_TRANSFER_URL = "https://api.mch.weixin.qq.com/mmpaymkttransfers/promotion/transfers"; // 企业付款 private static final String WECHAT_TRANSFER_URL = "https://api.mch.weixin.qq.com/mmpaymkttransfers/promotion/transfers"; // 企业付款
public static final String PAY_FH = "https://403e667e.r3.cpolar.top/"; // public static final String PAY_FH = "https://www.huafurenjia.cn/";
public static final String PAY_FH = "https://api.huafurenjia.cn/";
/** /**
* 其他配置常量 * 其他配置常量
*/ */

View File

@ -92,6 +92,8 @@ public class WechatPayV3Util {
return data.substring(0, 3) + "****" + data.substring(data.length() - 3); return data.substring(0, 3) + "****" + data.substring(data.length() - 3);
} }
// ============================== 公共方法 ============================== // ============================== 公共方法 ==============================
/** /**
@ -351,6 +353,15 @@ public class WechatPayV3Util {
// ============================== 退款功能 ============================== // ============================== 退款功能 ==============================
public Map<String, Object> refundtixian(JSONObject jsonResponse) {
Map<String, Object> result = new HashMap<>();
decryptSensitiveFields(jsonResponse);
return result;
}
/** /**
* 申请退款 * 申请退款
* *
@ -576,7 +587,7 @@ public class WechatPayV3Util {
* @param userName 真实姓名可选用于实名校验 * @param userName 真实姓名可选用于实名校验
* @return 提现结果 * @return 提现结果
*/ */
public Map<String, Object> withdraw(String openid, int amount, String desc, String userName) { public Map<String, Object> withdraw(String openid, int amount, String desc, String userName,String orderid) {
log.info("💰 开始用户提现"); log.info("💰 开始用户提现");
log.info(" ├─ 用户OpenID: {}", maskSensitiveData(openid)); log.info(" ├─ 用户OpenID: {}", maskSensitiveData(openid));
log.info(" ├─ 提现金额: {} 分 ({}元)", amount, fenToYuan(amount)); log.info(" ├─ 提现金额: {} 分 ({}元)", amount, fenToYuan(amount));
@ -604,7 +615,7 @@ public class WechatPayV3Util {
log.info("✅ 参数验证通过"); log.info("✅ 参数验证通过");
// 生成批次号和明细号只包含数字和字母 // 生成批次号和明细号只包含数字和字母
String outBatchNo = generateValidBatchNo("WITHDRAW"); String outBatchNo = orderid;
String detailNo = generateValidBatchNo("DETAIL"); String detailNo = generateValidBatchNo("DETAIL");
log.info("🆔 生成业务单号"); log.info("🆔 生成业务单号");
log.info(" ├─ 批次号: {}", outBatchNo); log.info(" ├─ 批次号: {}", outBatchNo);
@ -640,7 +651,10 @@ public class WechatPayV3Util {
params.put("batch_remark", "用户申请提现到零钱"); params.put("batch_remark", "用户申请提现到零钱");
params.put("total_amount", amount); params.put("total_amount", amount);
params.put("total_num", 1); params.put("total_num", 1);
// params.put("batch_id", orderid);
params.put("transfer_detail_list", detailList); params.put("transfer_detail_list", detailList);
params.put("notify_url", WechatPayUtil.PAY_FH +"api/worker/tixian/notify");
String bodyJson = JSONObject.toJSONString(params); String bodyJson = JSONObject.toJSONString(params);
log.info("✅ 请求参数构建完成"); log.info("✅ 请求参数构建完成");
@ -1073,7 +1087,7 @@ public class WechatPayV3Util {
* @param desc 提现描述 * @param desc 提现描述
* @return 提现结果 * @return 提现结果
*/ */
public Map<String, Object> quickWithdraw(String openid, BigDecimal amount, String desc) { public Map<String, Object> quickWithdraw(String openid, BigDecimal amount, String desc, String orderId) {
log.info("⚡ 开始快速提现"); log.info("⚡ 开始快速提现");
log.info(" ├─ 用户OpenID: {}", maskSensitiveData(openid)); log.info(" ├─ 用户OpenID: {}", maskSensitiveData(openid));
log.info(" ├─ 提现金额: {}元", amount); log.info(" ├─ 提现金额: {}元", amount);
@ -1082,7 +1096,7 @@ public class WechatPayV3Util {
int amountInt = amount.multiply(new BigDecimal(100)).intValue(); int amountInt = amount.multiply(new BigDecimal(100)).intValue();
log.info("💰 金额转换: {}元 = {}分", amount, amountInt); log.info("💰 金额转换: {}元 = {}分", amount, amountInt);
return withdraw(openid, amountInt, desc, null); return withdraw(openid, amountInt, desc, null, orderId);
} }
/** /**

View File

@ -9,18 +9,6 @@
<div class="payment-info" v-if="actualPaymentData"> <div class="payment-info" v-if="actualPaymentData">
<h4>支付信息</h4> <h4>支付信息</h4>
<!-- &lt;!&ndash; 调试信息 &ndash;&gt;-->
<!-- <div class="debug-info" style="background: #f0f9ff; padding: 10px; margin-bottom: 15px; border-radius: 4px; font-size: 12px;">-->
<!-- <div>调试信息</div>-->
<!-- <div>allmoney: {{ actualPaymentData.allmoney }}</div>-->
<!-- <div>wxmoney: {{ actualPaymentData.wxmoney }}</div>-->
<!-- <div>yemoney: {{ actualPaymentData.yemoney }}</div>-->
<!-- <div>shopmoney: {{ actualPaymentData.shopmoney }}</div>-->
<!-- <div>servicemoney: {{ actualPaymentData.servicemoney }}</div>-->
<!-- <div>couponmoney: {{ actualPaymentData.couponmoney }}</div>-->
<!-- <div>membermoney: {{ actualPaymentData.membermoney }}</div>-->
<!-- </div>-->
<el-row :gutter="20"> <el-row :gutter="20">
<el-col :span="8"> <el-col :span="8">
<div class="info-item"> <div class="info-item">
@ -80,41 +68,28 @@
<el-col :span="12"> <el-col :span="12">
<div class="summary-item"> <div class="summary-item">
<label>剩余可退款</label> <label>剩余可退款</label>
<span class="amount remaining">{{ formatAmount(remainingRefundableAmount) }}</span> <span class="amount remaining">{{ formatAmount(remainingRefundableAmountComputed) }}</span>
</div> </div>
</el-col> </el-col>
</el-row> </el-row>
<div class="summary-note"> <div class="summary-note">
<span class="note-text">💡 实际可退款金额{{ formatAmount(actualRefundableAmount) }}总支付{{ formatAmount(actualPaymentData.allmoney) }} - 会员优惠{{ formatAmount(actualPaymentData.membermoney || 0) }}</span> <span class="note-text">💡 退款信息</span>
<div class="refund-details">
<div class="detail-row compact">
<span class="detail-label">订单总额</span>
<span class="detail-value">{{ formatAmount(actualPaymentData.allmoney) }}</span>
<span class="detail-separator">|</span>
<span class="detail-label">会员优惠</span>
<span class="detail-value">{{ formatAmount(actualPaymentData.membermoney || 0) }}</span>
<span class="detail-separator">|</span>
<span class="detail-label">累计退款</span>
<span class="detail-value">{{ formatAmount(totalRefundedAmount) }}</span>
<span class="detail-separator">|</span>
<span class="detail-label">剩余可退</span>
<span class="detail-value total-value">{{ formatAmount(remainingRefundableAmountComputed) }}</span>
</div>
</div>
</div> </div>
<!-- <div class="summary-extra">-->
<!-- <span class="refund-count">退款次数{{ refundHistory.length }}</span>-->
<!-- <span class="last-refund" v-if="refundHistory.length > 0">-->
<!-- 最近退款{{ formatTime(refundHistory[0].createTime) }}-->
<!-- </span>-->
<!-- </div>-->
<!-- <div class="summary-details" v-if="refundHistory.length > 0">-->
<!-- <div class="detail-row">-->
<!-- <span class="detail-label">平均退款金额</span>-->
<!-- <span class="detail-value">{{ formatAmount(averageRefundAmount) }}</span>-->
<!-- </div>-->
<!-- <div class="detail-row">-->
<!-- <span class="detail-label">最大退款金额</span>-->
<!-- <span class="detail-value">{{ formatAmount(maxRefundAmount) }}</span>-->
<!-- </div>-->
<!-- <div class="detail-row">-->
<!-- <span class="detail-label">最小退款金额</span>-->
<!-- <span class="detail-value">{{ formatAmount(minRefundAmount) }}</span>-->
<!-- </div>-->
<!-- <div class="detail-row">-->
<!-- <span class="detail-label">中位数退款金额</span>-->
<!-- <span class="detail-value">{{ formatAmount(medianRefundAmount) }}</span>-->
<!-- </div>-->
<!-- <div class="detail-row">-->
<!-- <span class="detail-label">退款金额方差</span>-->
<!-- <span class="detail-value">{{ formatAmount(refundAmountVariance) }}</span>-->
<!-- </div>-->
<!-- </div>-->
</div> </div>
</div> </div>
@ -408,25 +383,26 @@ export default {
return this.formatAmount(this.actualPaymentData.allmoney); return this.formatAmount(this.actualPaymentData.allmoney);
}, },
// 退 // 退
actualRefundableAmount() { remainingRefundableAmountComputed() {
if (!this.actualPaymentData) return "0.00"; if (!this.actualPaymentData) return "0.00";
const allMoney = parseFloat(this.actualPaymentData.allmoney || 0); const allMoney = parseFloat(this.actualPaymentData.allmoney || 0);
const memberDiscount = parseFloat(this.actualPaymentData.membermoney || 0); const memberDiscount = parseFloat(this.actualPaymentData.membermoney || 0);
// 退 = allmoney - const totalRefunded = parseFloat(this.totalRefundedAmount || 0);
return (allMoney - memberDiscount).toFixed(2); // 退 = allmoney - - returnmoney
return Math.max(0, allMoney - memberDiscount - totalRefunded).toFixed(2);
}, },
canRefund() { canRefund() {
return parseFloat(this.totalRefundAmount) > 0 && parseFloat(this.totalRefundAmount) <= parseFloat(this.remainingRefundableAmount); return parseFloat(this.totalRefundAmount) > 0 && parseFloat(this.totalRefundAmount) <= parseFloat(this.remainingRefundableAmountComputed);
}, },
validationMessage() { validationMessage() {
const totalRefund = parseFloat(this.totalRefundAmount); const totalRefund = parseFloat(this.totalRefundAmount);
const remaining = parseFloat(this.remainingRefundableAmount); const remaining = parseFloat(this.remainingRefundableAmountComputed);
if (totalRefund > remaining) { if (totalRefund > remaining) {
return `退款金额超过剩余可退款金额,最多可退:¥${this.remainingRefundableAmount}`; return `退款金额超过剩余可退款金额,最多可退:¥${this.remainingRefundableAmountComputed}`;
} else if (totalRefund > 0) { } else if (totalRefund > 0) {
return `退款后将剩余可退款:¥${(remaining - totalRefund).toFixed(2)}`; return `退款后将剩余可退款:¥${(remaining - totalRefund).toFixed(2)}`;
} }
@ -435,7 +411,7 @@ export default {
validationType() { validationType() {
const totalRefund = parseFloat(this.totalRefundAmount); const totalRefund = parseFloat(this.totalRefundAmount);
const remaining = parseFloat(this.remainingRefundableAmount); const remaining = parseFloat(this.remainingRefundableAmountComputed);
return totalRefund > remaining ? "error" : "success"; return totalRefund > remaining ? "error" : "success";
}, },
@ -507,98 +483,7 @@ export default {
return filtered; return filtered;
}, },
// 退
averageRefundAmount() {
if (this.refundHistory.length === 0) return "0.00";
const totalRefunded = this.refundHistory.reduce((sum, item) => {
try {
const content = JSON.parse(item.content);
const amount = parseFloat(content.totalRefund) || parseFloat(content.returnmoney) || parseFloat(content.price) || 0;
return sum + (isNaN(amount) ? 0 : amount);
} catch (e) {
return sum;
}
}, 0);
return (totalRefunded / this.refundHistory.length).toFixed(2);
},
// 退
maxRefundAmount() {
if (this.refundHistory.length === 0) return "0.00";
const refundAmounts = this.refundHistory.map(item => {
try {
const content = JSON.parse(item.content);
const amount = parseFloat(content.totalRefund) || parseFloat(content.returnmoney) || parseFloat(content.price) || 0;
return isNaN(amount) ? 0 : amount;
} catch (e) {
return 0;
}
}).filter(amount => amount > 0);
if (refundAmounts.length === 0) return "0.00";
return Math.max(...refundAmounts).toFixed(2);
},
// 退
minRefundAmount() {
if (this.refundHistory.length === 0) return "0.00";
const refundAmounts = this.refundHistory.map(item => {
try {
const content = JSON.parse(item.content);
const amount = parseFloat(content.totalRefund) || parseFloat(content.returnmoney) || parseFloat(content.price) || 0;
return isNaN(amount) ? 0 : amount;
} catch (e) {
return 0;
}
}).filter(amount => amount > 0);
if (refundAmounts.length === 0) return "0.00";
return Math.min(...refundAmounts).toFixed(2);
},
// 退
medianRefundAmount() {
if (this.refundHistory.length === 0) return "0.00";
const sortedRefundAmounts = this.refundHistory.map(item => {
try {
const content = JSON.parse(item.content);
const amount = parseFloat(content.totalRefund) || parseFloat(content.returnmoney) || parseFloat(content.price) || 0;
return isNaN(amount) ? 0 : amount;
} catch (e) {
return 0;
}
}).filter(amount => amount > 0).sort((a, b) => a - b);
if (sortedRefundAmounts.length === 0) return "0.00";
const mid = Math.floor(sortedRefundAmounts.length / 2);
if (sortedRefundAmounts.length % 2 === 0) {
return ((sortedRefundAmounts[mid - 1] + sortedRefundAmounts[mid]) / 2).toFixed(2);
} else {
return sortedRefundAmounts[mid].toFixed(2);
}
},
// 退
refundAmountVariance() {
if (this.refundHistory.length === 0) return "0.00";
const validAmounts = this.refundHistory.map(item => {
try {
const content = JSON.parse(item.content);
const amount = parseFloat(content.totalRefund) || parseFloat(content.returnmoney) || parseFloat(content.price) || 0;
return isNaN(amount) ? 0 : amount;
} catch (e) {
return 0;
}
}).filter(amount => amount > 0);
if (validAmounts.length === 0) return "0.00";
const average = parseFloat(this.averageRefundAmount);
const squaredDifferences = validAmounts.map(amount => Math.pow(amount - average, 2));
const variance = squaredDifferences.reduce((sum, diff) => sum + diff, 0) / validAmounts.length;
return variance.toFixed(2);
}
}, },
watch: { watch: {
visible(newVal) { visible(newVal) {
@ -642,15 +527,13 @@ export default {
// 退 // 退
this.refundHistory = []; this.refundHistory = [];
this.totalRefundedAmount = "0.00"; this.totalRefundedAmount = "0.00";
// 退 = allmoney - 退 // 退
const allMoney = parseFloat(this.actualPaymentData.allmoney || 0);
const memberDiscount = parseFloat(this.actualPaymentData.membermoney || 0);
this.remainingRefundableAmount = (allMoney - memberDiscount).toFixed(2);
console.log('初始计算:'); console.log('初始设置:');
console.log(' allmoney:', allMoney); console.log(' allmoney:', parseFloat(this.actualPaymentData.allmoney || 0));
console.log(' 会员优惠:', memberDiscount); console.log(' 会员优惠:', parseFloat(this.actualPaymentData.membermoney || 0));
console.log(' 初始剩余可退款:', this.remainingRefundableAmount); console.log(' 初始累计退款:', this.totalRefundedAmount);
console.log(' 剩余可退款将通过计算属性自动计算');
// 退 // 退
await this.loadRefundHistory(); await this.loadRefundHistory();
@ -734,10 +617,7 @@ export default {
if (attempt === maxRetries) { // if (attempt === maxRetries) { //
this.refundHistory = []; this.refundHistory = [];
this.totalRefundedAmount = "0.00"; this.totalRefundedAmount = "0.00";
// 使 // 退
const allMoney = parseFloat(this.actualPaymentData.allmoney || 0);
const memberDiscount = parseFloat(this.actualPaymentData.membermoney || 0);
this.remainingRefundableAmount = (allMoney - memberDiscount).toFixed(2);
this.refundHistoryError = response.msg || "加载退款历史失败"; this.refundHistoryError = response.msg || "加载退款历史失败";
this.errorDetails = { this.errorDetails = {
code: response.code, code: response.code,
@ -826,22 +706,8 @@ export default {
}); });
this.totalRefundedAmount = total.toFixed(2); this.totalRefundedAmount = total.toFixed(2);
console.log('累计退款金额:', this.totalRefundedAmount);
// 退 = allmoney - - returnmoney console.log('剩余可退款金额将通过计算属性自动计算');
const allMoney = parseFloat(this.actualPaymentData.allmoney || 0);
const memberDiscount = parseFloat(this.actualPaymentData.membermoney || 0);
const totalRefunded = parseFloat(this.totalRefundedAmount);
// allmoney - - returnmoney
const remaining = Math.max(0, allMoney - memberDiscount - totalRefunded);
this.remainingRefundableAmount = remaining.toFixed(2);
console.log('计算详情:');
console.log(' allmoney:', allMoney);
console.log(' 会员优惠:', memberDiscount);
console.log(' 累计已退款:', totalRefunded);
console.log(' 剩余可退款:', this.remainingRefundableAmount);
console.log(' 计算公式: allmoney - 优惠金额 - returnmoney =', allMoney, '-', memberDiscount, '-', totalRefunded, '=', remaining);
}, },
initForm() { initForm() {
@ -862,10 +728,8 @@ export default {
if (this.refundHistory.length > 0) { if (this.refundHistory.length > 0) {
this.calculateTotalRefunded(); this.calculateTotalRefunded();
} else { } else {
// 退allmoney - // 退退0退
const allMoney = parseFloat(this.actualPaymentData.allmoney || 0); this.totalRefundedAmount = "0.00";
const memberDiscount = parseFloat(this.actualPaymentData.membermoney || 0);
this.remainingRefundableAmount = (allMoney - memberDiscount).toFixed(2);
} }
this.calculateTotalRefund(); this.calculateTotalRefund();
@ -896,7 +760,7 @@ export default {
validateRefundAmount() { validateRefundAmount() {
const totalRefund = parseFloat(this.totalRefundAmount); const totalRefund = parseFloat(this.totalRefundAmount);
const remaining = parseFloat(this.remainingRefundableAmount); const remaining = parseFloat(this.remainingRefundableAmountComputed);
if (totalRefund <= 0) { if (totalRefund <= 0) {
this.$message.error("退款金额必须大于0"); this.$message.error("退款金额必须大于0");
@ -904,7 +768,7 @@ export default {
} }
if (totalRefund > remaining) { if (totalRefund > remaining) {
this.$message.error(`退款金额不能超过剩余可退款金额,最多可退:¥${this.remainingRefundableAmount}(实际可退款金额:¥${this.actualRefundableAmount}`); this.$message.error(`退款金额不能超过剩余可退款金额,最多可退:¥${this.remainingRefundableAmountComputed}`);
return false; return false;
} }
@ -996,7 +860,7 @@ export default {
this.confirmLoading = false; // this.confirmLoading = false; //
this.refundHistory = []; this.refundHistory = [];
this.totalRefundedAmount = "0.00"; this.totalRefundedAmount = "0.00";
this.remainingRefundableAmount = "0.00"; // 退
this.refundHistoryLoading = false; this.refundHistoryLoading = false;
this.refundHistoryError = ""; // this.refundHistoryError = ""; //
this.expandedItems = []; // this.expandedItems = []; //
@ -1063,7 +927,13 @@ export default {
try { try {
if (item.content) { if (item.content) {
const content = JSON.parse(item.content); const content = JSON.parse(item.content);
//
// contentremainingRefundable使
if (content.remainingRefundable !== undefined) {
return content.name || content.description || content.remark || '退款记录';
}
//
if (content.name) { if (content.name) {
return content.name; return content.name;
} else if (content.description) { } else if (content.description) {
@ -1197,6 +1067,26 @@ export default {
text: shareText, text: shareText,
url: shareUrl url: shareUrl
}); });
},
// 退退
getRemainingRefundableAfterRefund(item) {
try {
if (item.content) {
const content = JSON.parse(item.content);
// contentremainingRefundable使
if (content.remainingRefundable !== undefined) {
return parseFloat(content.remainingRefundable);
}
// 使
return parseFloat(this.remainingRefundableAmountComputed);
}
} catch (e) {
console.warn('计算剩余可退款金额失败:', e);
}
return 0;
} }
} }
} }
@ -1305,6 +1195,72 @@ export default {
font-size: 13px; font-size: 13px;
color: #409EFF; color: #409EFF;
font-weight: 500; font-weight: 500;
margin-bottom: 10px;
display: block;
}
.refund-details {
margin-top: 10px;
text-align: left;
}
.refund-details .detail-row {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 8px;
padding: 5px 0;
border-bottom: 1px solid #e8f4fd;
}
.refund-details .detail-row:last-child {
border-bottom: none;
margin-bottom: 0;
}
.refund-details .detail-row.total-row {
border-top: 2px solid #409EFF;
border-bottom: none;
margin-top: 10px;
padding-top: 10px;
font-weight: 600;
}
.refund-details .detail-row.compact {
display: flex;
align-items: center;
gap: 10px; /* 调整间距 */
margin-bottom: 5px;
padding: 5px 0;
border-bottom: 1px solid #e8f4fd;
}
.refund-details .detail-row.compact:last-child {
border-bottom: none;
margin-bottom: 0;
}
.refund-details .detail-label {
color: #606266;
font-weight: 500;
min-width: 80px;
}
.refund-details .detail-value {
color: #303133;
font-weight: 600;
text-align: right;
}
.refund-details .detail-value.total-value {
color: #409EFF;
font-size: 16px;
}
.refund-details .detail-separator {
color: #c0c4cc;
font-weight: 300;
margin: 0 5px;
} }
.refund-input { background-color: #fff; padding: 20px; border-radius: 8px; border: 1px solid #e9ecef; margin-bottom: 20px; } .refund-input { background-color: #fff; padding: 20px; border-radius: 8px; border: 1px solid #e9ecef; margin-bottom: 20px; }
@ -1943,4 +1899,35 @@ export default {
font-weight: bold; font-weight: bold;
} }
} }
.refund-operator {
margin-top: 8px;
font-size: 12px;
color: #909399;
display: flex;
align-items: center;
}
.refund-operator i {
margin-right: 5px;
font-size: 14px;
}
.body-remaining {
margin-top: 8px;
font-size: 12px;
color: #409EFF;
display: flex;
align-items: center;
font-weight: 500;
}
.remaining-label {
margin-right: 5px;
}
.remaining-value {
font-weight: 600;
color: #409EFF;
}
</style> </style>