增加商户转账到零钱v3版本

This commit is contained in:
2265829957@qq.com 2026-07-04 21:54:32 +08:00
parent 2b0eebdc0f
commit 41432e8d79
5 changed files with 400 additions and 112 deletions

View File

@ -97,4 +97,15 @@ public class XhpcSettingConfig extends BaseEntity {
*/ */
private String tenantId; private String tenantId;
// 商户API证书序列号从证书中获取十进制转十六进制大写
private String wxApiSerialNo;
// APIv3密钥32位在商户平台设置
private String wxApiV3Key;
// 微信支付公钥ID或平台证书序列号用于解密回调可暂不配置
private String wxPayPublicKeyId;
// 微信支付公钥文件路径PEM格式用于加密敏感字段本业务可不配置
private String wxPayPublicKeyPath;
// 微信V3退款回调地址
private String wxV3CallbackUrl;
} }

View File

@ -100,12 +100,15 @@ public class XhpcPlaceholderOrderServiceImpl extends BaseService implements IXhp
if(count==0){ if(count==0){
endTime = startTime; endTime = startTime;
}else{ }else{
int number = xhpcPlaceholderOrderMapper.getXhpcHistoryOrderNextOrderTimeYu(xhpcPlaceholderOrder.getHistoryOrderId(), xhpcPlaceholderOrder.getTerminalId(), startTimeYu);
//有下一单返回1 没有下一单返回0
if(number>0){
//查看下一单的时间是否超过30分钟 //查看下一单的时间是否超过30分钟
// int result = xhpcPlaceholderOrderMapper.getXhpcHistoryOrderNextOrderTime(xhpcPlaceholderOrder.getHistoryOrderId(), xhpcPlaceholderOrder.getTerminalId(), startTimeYu); int result = xhpcPlaceholderOrderMapper.getXhpcHistoryOrderNextOrderTime(xhpcPlaceholderOrder.getHistoryOrderId(), xhpcPlaceholderOrder.getTerminalId(), startTimeYu);
// if(result==0){ if(result==0){
// endTime = startTime; endTime = startTime;
// } }
}
} }
String start = DateUtil.formatTime(startTime); String start = DateUtil.formatTime(startTime);
String end = DateUtil.formatTime(endTime); String end = DateUtil.formatTime(endTime);

View File

@ -1,5 +1,7 @@
package com.xhpc.payment.controller; package com.xhpc.payment.controller;
import com.wechat.pay.java.core.RSAAutoCertificateConfig;;
import com.wechat.pay.java.core.notification.NotificationParser;
import com.xhpc.common.core.annotation.NoRepeatSubmit; import com.xhpc.common.core.annotation.NoRepeatSubmit;
import com.xhpc.common.core.constant.HttpStatus; import com.xhpc.common.core.constant.HttpStatus;
import com.xhpc.common.core.constant.StatusConstants; import com.xhpc.common.core.constant.StatusConstants;
@ -9,9 +11,9 @@ import com.xhpc.common.core.utils.WXPayUtil;
import com.xhpc.common.core.web.controller.BaseController; import com.xhpc.common.core.web.controller.BaseController;
import com.xhpc.common.core.web.domain.AjaxResult; import com.xhpc.common.core.web.domain.AjaxResult;
import com.xhpc.common.domain.XhpcRefundOrder; import com.xhpc.common.domain.XhpcRefundOrder;
import com.xhpc.common.domain.XhpcSettingConfig;
import com.xhpc.common.log.annotation.Log; import com.xhpc.common.log.annotation.Log;
import com.xhpc.common.log.enums.BusinessType; import com.xhpc.common.log.enums.BusinessType;
import com.xhpc.common.security.service.TokenService;
import com.xhpc.common.util.UserTypeUtil; import com.xhpc.common.util.UserTypeUtil;
import com.xhpc.payment.domain.XhpcAppInternetUser; import com.xhpc.payment.domain.XhpcAppInternetUser;
import com.xhpc.payment.domain.XhpcAppUser; import com.xhpc.payment.domain.XhpcAppUser;
@ -19,16 +21,16 @@ import com.xhpc.payment.domain.XhpcRefundAudit;
import com.xhpc.payment.mapper.XhpcRefundOrderMapper; import com.xhpc.payment.mapper.XhpcRefundOrderMapper;
import com.xhpc.payment.mapper.XhpcRefundOriginalOrderMapper; import com.xhpc.payment.mapper.XhpcRefundOriginalOrderMapper;
import com.xhpc.payment.mapper.XhpcUserAccountStatementMapper; import com.xhpc.payment.mapper.XhpcUserAccountStatementMapper;
import com.xhpc.payment.service.IXhpcCommonPayment;
import com.xhpc.payment.service.IXhpcRefundAuditService; import com.xhpc.payment.service.IXhpcRefundAuditService;
import com.xhpc.payment.service.IXhpcRefundOrderService; import com.xhpc.payment.service.IXhpcRefundOrderService;
import com.xhpc.payment.service.IXhpcRefundOriginalOrderService; import com.xhpc.payment.service.IXhpcRefundOriginalOrderService;
import com.xhpc.payment.util.AESUtil; import com.xhpc.payment.util.AESUtil;
import com.xhpc.system.api.model.LoginUser; import com.xhpc.payment.util.TransferBatchNotifyData;
import io.swagger.annotations.Api; import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation; import io.swagger.annotations.ApiOperation;
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.scheduling.annotation.Scheduled; import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.interceptor.TransactionAspectSupport; import org.springframework.transaction.interceptor.TransactionAspectSupport;
@ -51,6 +53,7 @@ import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.concurrent.ExecutorService; import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors; import java.util.concurrent.Executors;
import java.util.stream.Collectors;
/** /**
* @author yuyang * @author yuyang
@ -73,6 +76,10 @@ public class XhpcRefundOriginalOrderController extends BaseController {
private IXhpcRefundOrderService iXhpcRefundOrderService; private IXhpcRefundOrderService iXhpcRefundOrderService;
@Resource @Resource
private XhpcRefundOrderMapper xhpcRefundOrderMapper; private XhpcRefundOrderMapper xhpcRefundOrderMapper;
@Resource
private IXhpcCommonPayment xhpcCommonPayment;
private final ExecutorService executorService = Executors.newFixedThreadPool(40); private final ExecutorService executorService = Executors.newFixedThreadPool(40);
@ -93,8 +100,71 @@ public class XhpcRefundOriginalOrderController extends BaseController {
return xhpcRefundOriginalOrderService.wxRefundOriginalOrder(refundOrderId); return xhpcRefundOriginalOrderService.wxRefundOriginalOrder(refundOrderId);
} }
/** /**
* 微信回调Api(退款) * 微信回调Api(v3退款)
*/
@PostMapping("/transferCallback")
public String transferCallback(HttpServletRequest request) {
try {
// 1. 获取请求头
String serialNumber = request.getHeader("Wechatpay-Serial");
String timestamp = request.getHeader("Wechatpay-Timestamp");
String nonce = request.getHeader("Wechatpay-Nonce");
String signature = request.getHeader("Wechatpay-Signature");
// 2. 获取请求体
String body = request.getReader().lines().collect(Collectors.joining());
// 3. 构建配置与发起转账使用相同配置
XhpcSettingConfig config = xhpcCommonPayment.getXhpcSettingConfigTenantId(UserTypeUtil.OPERATION_WX_TYPE, "000000");
RSAAutoCertificateConfig v3Config = new RSAAutoCertificateConfig.Builder()
.merchantId(config.getWxMchId())
.privateKeyFromPath(config.getWxApiclientKeyPem())
.merchantSerialNumber(config.getWxApiSerialNo())
.apiV3Key(config.getWxApiV3Key())
.build();
// 4. 解析并验签
com.wechat.pay.java.core.notification.RequestParam param = new com.wechat.pay.java.core.notification.RequestParam.Builder()
.serialNumber(serialNumber)
.timestamp(timestamp)
.nonce(nonce)
.signature(signature)
.body(body)
.build();
NotificationParser parser = new NotificationParser(v3Config);
// 解析通知
TransferBatchNotifyData notifyData = parser.parse(param, TransferBatchNotifyData.class);
// 7. 处理业务逻辑
String outBatchNo = notifyData.getOutBatchNo();
String batchStatus = notifyData.getBatchStatus();
logger.info("回调业务数据outBatchNo={}, batchStatus={}, totalAmount={}分, totalNum={}",
outBatchNo, batchStatus, notifyData.getTotalAmount(), notifyData.getTotalNum());
// 根据批次状态更新订单
if ("FINISHED".equals(batchStatus)) {
// 检查明细是否全部成功
boolean allSuccess = notifyData.getTransferDetailList().stream()
.allMatch(d -> "SUCCESS".equals(d.getDetailStatus()));
if (allSuccess) {
xhpcRefundOriginalOrderService.updateXhpcRefundOriginalOrderV3(notifyData);
logger.info("转账全部成功订单更新成功outBatchNo={}", outBatchNo);
}
}
// 8. 返回成功应答必须返回这个格式否则微信会重试
return "{\"code\":\"SUCCESS\"}";
}catch (Exception e){
logger.error("处理转账回调异常", e);
return "{\"code\":\"FAIL\",\"message\":\"" + e.getMessage() + "\"}";
}
}
/**
* 微信回调Api(v2退款)
*/ */
@RequestMapping("/paymentCallback") @RequestMapping("/paymentCallback")
public Object payNotify(HttpServletRequest request, HttpServletResponse response) { public Object payNotify(HttpServletRequest request, HttpServletResponse response) {

View File

@ -130,4 +130,6 @@ public class XhpcRefundOriginalOrder extends BaseEntity {
*/ */
private Integer lastOne; private Integer lastOne;
//商家明细单号
private String outDetailNo;
} }

View File

@ -11,6 +11,12 @@ import com.alipay.api.request.AlipayTradeRefundRequest;
import com.alipay.api.response.AlipayFundTransUniTransferResponse; import com.alipay.api.response.AlipayFundTransUniTransferResponse;
import com.alipay.api.response.AlipayTradeFastpayRefundQueryResponse; import com.alipay.api.response.AlipayTradeFastpayRefundQueryResponse;
import com.alipay.api.response.AlipayTradeRefundResponse; import com.alipay.api.response.AlipayTradeRefundResponse;
import com.wechat.pay.java.core.Config;
import com.wechat.pay.java.core.RSAAutoCertificateConfig;
import com.wechat.pay.java.service.transferbatch.TransferBatchService;
import com.wechat.pay.java.service.transferbatch.model.InitiateBatchTransferRequest;
import com.wechat.pay.java.service.transferbatch.model.InitiateBatchTransferResponse;
import com.wechat.pay.java.service.transferbatch.model.TransferDetailInput;
import com.xhpc.common.api.SmsService; import com.xhpc.common.api.SmsService;
import com.xhpc.common.core.constant.HttpStatus; import com.xhpc.common.core.constant.HttpStatus;
import com.xhpc.common.core.constant.StatusConstants; import com.xhpc.common.core.constant.StatusConstants;
@ -28,6 +34,7 @@ import com.xhpc.payment.domain.XhpcRefundOriginalOrder;
import com.xhpc.payment.mapper.XhpcRefundOriginalOrderMapper; import com.xhpc.payment.mapper.XhpcRefundOriginalOrderMapper;
import com.xhpc.payment.mapper.XhpcUserAccountStatementMapper; import com.xhpc.payment.mapper.XhpcUserAccountStatementMapper;
import com.xhpc.payment.service.*; import com.xhpc.payment.service.*;
import com.xhpc.payment.util.TransferBatchNotifyData;
import org.apache.http.HttpEntity; import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse; import org.apache.http.HttpResponse;
import org.apache.http.client.methods.HttpPost; import org.apache.http.client.methods.HttpPost;
@ -557,6 +564,98 @@ public class XhpcRefundOriginalOrderServiceImpl implements IXhpcRefundOriginalOr
return R.ok(); return R.ok();
} }
@Override
public void updateXhpcRefundOriginalOrderV3(TransferBatchNotifyData notifyData) {
XhpcRefundOriginalOrder xhpcRefundOriginalOrder = xhpcRefundOriginalOrderMapper.getXhpcRefundOriginalOrderRefundId(notifyData.getBatchId());
if(xhpcRefundOriginalOrder !=null){
if ("FINISHED".equals(notifyData.getBatchStatus())) {
//退款成功
xhpcRefundOriginalOrder.setStatus(1);
BigDecimal refundFeeAmount = xhpcRefundOriginalOrder.getRefundFee();
XhpcRechargeOrder xhpcRechargeOrder = xhpcRefundOriginalOrderMapper.getXhpcRechargeOrder(xhpcRefundOriginalOrder.getRechargeOrderId());
BigDecimal amount = xhpcRechargeOrder.getAmount();
if(xhpcRechargeOrder.getRefundStatus()==0){
//修改支付订单付款金额和状态
if(amount.compareTo(refundFeeAmount)==0){
//全部退完
iXhpcRechargeOrderService.updateRechargeOrderFeundFee(xhpcRechargeOrder.getRechargeOrderId(),amount,2);
}else if(amount.compareTo(refundFeeAmount)>0){
//退了一部分
iXhpcRechargeOrderService.updateRechargeOrderFeundFee(xhpcRechargeOrder.getRechargeOrderId(),refundFeeAmount,1);
}
}else{
BigDecimal subtract = xhpcRechargeOrder.getAmount().subtract(xhpcRechargeOrder.getRefundFee());
//修改支付订单付款金额和状态
if(subtract.compareTo(refundFeeAmount)==0){
//全部退完
iXhpcRechargeOrderService.updateRechargeOrderFeundFee(xhpcRechargeOrder.getRechargeOrderId(),amount,2);
}else if(subtract.compareTo(refundFeeAmount)>0){
//退了一部分
iXhpcRechargeOrderService.updateRechargeOrderFeundFee(xhpcRechargeOrder.getRechargeOrderId(),xhpcRechargeOrder.getRefundFee().add(refundFeeAmount),1);
}
}
//修改用户金额
Long userId = xhpcRefundOriginalOrder.getUserId();
Integer source = xhpcRefundOriginalOrder.getSource();
//增加用户流水记录
xhpcUserAccountStatementService.add(xhpcRefundOriginalOrder.getRefundOrderId(), "-"+refundFeeAmount.toString(), userId.toString(), StatusConstants.FLOWING_WATER_REFUND,source,xhpcRefundOriginalOrder.getTenantId());
if(UserTypeUtil.USER_TYPE.equals(source)){
Map<String, Object> appUserInfo = xhpcUserAccountStatementMapper.appUserInfo(userId);
String balance = StringUtils.valueOf(appUserInfo.get("balance"));
//减少用户余额
XhpcAppUser xhpcAppUser = new XhpcAppUser();
xhpcAppUser.setAppUserId(userId);
BigDecimal surplus =new BigDecimal(balance).subtract(refundFeeAmount);
xhpcAppUser.setBalance(surplus);
if(xhpcRefundOriginalOrder.getLastOne()==1){
xhpcAppUser.setIsRefundApplication(0);
}
xhpcUserAccountStatementMapper.updateAppUserBalance(xhpcAppUser);
if(surplus.compareTo(new BigDecimal(0))==0){
xhpcRefundOriginalOrderMapper.updateRechargeOrderRefundStatus(userId,source);
}
}else if(UserTypeUtil.COMMUNIT_TYPE.equals(source)){
Map<String, Object> communityPersonnel = iXhpcRefundAuditService.getCommunityPersonnelById(userId, xhpcRefundOriginalOrder.getTenantId());
BigDecimal surplus =new BigDecimal(communityPersonnel.get("balance").toString()).subtract(refundFeeAmount);
if(xhpcRefundOriginalOrder.getLastOne()==1){
int i = iXhpcRefundAuditService.updateCommunityPersonnelMoney(userId, surplus,0);
}else{
int i = iXhpcRefundAuditService.updateCommunityPersonnelMoney(userId, surplus,null);
}
if(surplus.compareTo(new BigDecimal(0))==0){
xhpcRefundOriginalOrderMapper.updateRechargeOrderRefundStatus(userId,source);
}
}else if(UserTypeUtil.INTERNET_TYPE.equals(source) && userId>10000){
System.out.println("=================查询微信支付信息==========修改金额=======================");
System.out.println("=================查询微信支付信息==========修改金额=======================");
Map<String, Object> appUserInfo = xhpcUserAccountStatementMapper.getAppInternetUser(userId);
String balance = StringUtils.valueOf(appUserInfo.get("balance"));
//减少用户余额
XhpcAppInternetUser xhpcAppUser = new XhpcAppInternetUser();
xhpcAppUser.setAppInternetUserId(userId);
BigDecimal surplus =new BigDecimal(balance).subtract(refundFeeAmount);
xhpcAppUser.setBalance(surplus);
if(xhpcRefundOriginalOrder.getLastOne()==1){
xhpcAppUser.setIsRefundApplication(0);
}
xhpcUserAccountStatementMapper.updateAppInternetUserBalance(xhpcAppUser);
if(surplus.compareTo(new BigDecimal(0))==0){
xhpcRefundOriginalOrderMapper.updateRechargeOrderRefundStatus(userId,source);
}
}
}else{
//退款失败
xhpcRefundOriginalOrder.setStatus(2);
}
xhpcRefundOriginalOrderMapper.updateXhpcRefundOriginalOrder(xhpcRefundOriginalOrder);
}
}
@Override @Override
public void updateXhpcRefundOriginalOrder(Map<String, String> reqInfo) { public void updateXhpcRefundOriginalOrder(Map<String, String> reqInfo) {
XhpcRefundOriginalOrder xhpcRefundOriginalOrder = xhpcRefundOriginalOrderMapper.getXhpcRefundOriginalOrderRefundId(reqInfo.get("refund_id").toString()); XhpcRefundOriginalOrder xhpcRefundOriginalOrder = xhpcRefundOriginalOrderMapper.getXhpcRefundOriginalOrderRefundId(reqInfo.get("refund_id").toString());
@ -679,6 +778,109 @@ public class XhpcRefundOriginalOrderServiceImpl implements IXhpcRefundOriginalOr
} }
//微信退款原路退回 //微信退款原路退回
public R getWxTradeRefundYu(XhpcRefundOrder xhpcRefundOrder,XhpcRechargeOrder rechargeOrder,String orderNumber,BigDecimal refundFee,Integer type){
try {
readWriteLock.writeLock().lock();//
XhpcSettingConfig config = xhpcCommonPayment.getXhpcSettingConfigTenantId(UserTypeUtil.OPERATION_WX_TYPE, rechargeOrder.getTenantId());
if (config == null) {
return R.fail(HttpStatus.ALREADY_EXISTING, "微信退款配置缺失");
}
// 构建V3配置自动管理平台证书
Config v3Config = new RSAAutoCertificateConfig.Builder()
.merchantId(config.getWxMchId())
.privateKeyFromPath(config.getWxApiclientKeyPem())
.merchantSerialNumber(config.getWxApiSerialNo())
.apiV3Key(config.getWxApiV3Key())
.build();
TransferBatchService transferService = new TransferBatchService.Builder().config(v3Config).build();
// 4. 构建请求参数
InitiateBatchTransferRequest request = new InitiateBatchTransferRequest();
request.setAppid(config.getWxAppId());
request.setOutBatchNo(orderNumber);
request.setBatchName("用户退款");
request.setBatchRemark("退款至零钱");
// 总金额
Long totalAmount = refundFee.multiply(BigDecimal.valueOf(100)).longValue();
request.setTotalAmount(totalAmount);
request.setTotalNum(1); // 单笔转账
// 构建明细列表
List<TransferDetailInput> detailList = new ArrayList<>();
TransferDetailInput detail = new TransferDetailInput();
//商家明细单号
String outDetailNo = StringUtils.numFormat(11L, 1, StatusConstants.FLOWING_WATER_REFUND_TYPE);
detail.setOutDetailNo(outDetailNo);
detail.setTransferAmount(totalAmount);
detail.setTransferRemark("退款");
detail.setOpenid(xhpcRefundOrder.getOpenId());
// 如果单笔金额 >= 2000元需要加密用户姓名
// if (totalAmount >= 200000) {
// detail.setUserName(encryptUserName(userName));
// }
detailList.add(detail);
request.setTransferDetailList(detailList);
//v3回调地址在微信商户后台配置
request.setNotifyUrl(config.getWxV3CallbackUrl());
InitiateBatchTransferResponse response = transferService.initiateBatchTransfer(request);
// 6. 直接从响应中获取返回信息
String outBatchNoResp = response.getOutBatchNo();
String batchId = response.getBatchId();
String createTime = response.getCreateTime();
String batchStatus = response.getBatchStatus();
XhpcRefundOriginalOrder xhpcRefundOriginalOrder = new XhpcRefundOriginalOrder();
if ("ACCEPTED".equals(batchStatus) || "PROCESSING".equals(batchStatus) || "FINISHED".equals(batchStatus)) {
xhpcRefundOriginalOrder.setStatus(0);
} else {
xhpcRefundOriginalOrder.setStatus(2);
}
xhpcRefundOriginalOrder.setOutTradeNo(outBatchNoResp);
xhpcRefundOriginalOrder.setRefundId(batchId);
xhpcRefundOriginalOrder.setCashRefundFee(refundFee);
//入库
xhpcRefundOriginalOrder.setUserId(rechargeOrder.getUserId());
xhpcRefundOriginalOrder.setType(1);
xhpcRefundOriginalOrder.setSource(rechargeOrder.getSource());
xhpcRefundOriginalOrder.setDelFlag(0);
xhpcRefundOriginalOrder.setCreateTime(new Date());
xhpcRefundOriginalOrder.setRemark("微信退款");
xhpcRefundOriginalOrder.setTenantId(rechargeOrder.getTenantId());
xhpcRefundOriginalOrder.setAppId(config.getWxAppId());
xhpcRefundOriginalOrder.setMchId(config.getWxMchId());
xhpcRefundOriginalOrder.setTransactionId(rechargeOrder.getPrepayId());
xhpcRefundOriginalOrder.setOutRefundNo(orderNumber);
xhpcRefundOriginalOrder.setOutDetailNo(outDetailNo);
xhpcRefundOriginalOrder.setTotalFee(rechargeOrder.getAmount());
xhpcRefundOriginalOrder.setRefundFee(refundFee);
xhpcRefundOriginalOrder.setRefundOrderId(xhpcRefundOrder.getRefundOrderId());
xhpcRefundOriginalOrder.setRechargeOrderId(rechargeOrder.getRechargeOrderId());
xhpcRefundOriginalOrder.setLastOne(type);
xhpcRefundOriginalOrderMapper.addXhpcRefundOriginalOrder(xhpcRefundOriginalOrder);
Thread.sleep(500);
return R.ok();
}catch (Exception e){
e.printStackTrace();
}finally {
readWriteLock.writeLock().unlock();//释放锁
}
return R.fail();
}
public R getWxTradeRefund(XhpcRefundOrder xhpcRefundOrder,XhpcRechargeOrder rechargeOrder,String orderNumber,BigDecimal refundFee,Integer type){ public R getWxTradeRefund(XhpcRefundOrder xhpcRefundOrder,XhpcRechargeOrder rechargeOrder,String orderNumber,BigDecimal refundFee,Integer type){
try { try {
readWriteLock.writeLock().lock();// readWriteLock.writeLock().lock();//