退款提现

This commit is contained in:
zengtao01
2024-07-26 14:33:43 +08:00
parent 82eb8b56ce
commit 1de2f8a6b7
41 changed files with 1038 additions and 119 deletions

View File

@ -3,33 +3,49 @@ package cn.iocoder.yudao.module.pay.controller.admin.notify;
import cn.hutool.core.collection.CollUtil;
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog;
import cn.iocoder.yudao.framework.pay.core.client.PayClient;
import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderRespDTO;
import cn.iocoder.yudao.framework.pay.core.client.dto.refund.PayRefundRespDTO;
import cn.iocoder.yudao.framework.pay.core.enums.divide.PayDivideRefundStatusRespEnum;
import cn.iocoder.yudao.framework.pay.core.enums.refund.PayRefundStatusRespEnum;
import cn.iocoder.yudao.module.pay.controller.admin.notify.vo.PayNotifyTaskDetailRespVO;
import cn.iocoder.yudao.module.pay.controller.admin.notify.vo.PayNotifyTaskPageReqVO;
import cn.iocoder.yudao.module.pay.controller.admin.notify.vo.PayNotifyTaskRespVO;
import cn.iocoder.yudao.module.pay.convert.notify.PayNotifyTaskConvert;
import cn.iocoder.yudao.module.pay.dal.dataobject.app.PayAppDO;
import cn.iocoder.yudao.module.pay.dal.dataobject.divide.DivideDO;
import cn.iocoder.yudao.module.pay.dal.dataobject.notify.PayNotifyLogDO;
import cn.iocoder.yudao.module.pay.dal.dataobject.notify.PayNotifyTaskDO;
import cn.iocoder.yudao.module.pay.dal.dataobject.refundrecord.RefundRecordDO;
import cn.iocoder.yudao.module.pay.dal.mysql.refundrecord.RefundRecordMapper;
import cn.iocoder.yudao.module.pay.service.app.PayAppService;
import cn.iocoder.yudao.module.pay.service.channel.PayChannelService;
import cn.iocoder.yudao.module.pay.service.divide.DivideService;
import cn.iocoder.yudao.module.pay.service.notify.PayNotifyService;
import cn.iocoder.yudao.module.pay.service.order.PayOrderService;
import cn.iocoder.yudao.module.pay.service.refund.PayRefundService;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
import javax.annotation.security.PermitAll;
import javax.validation.Valid;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
@ -50,11 +66,17 @@ public class PayNotifyController {
@Resource
private PayRefundService refundService;
@Resource
private RefundRecordMapper refundRecordMapper;
@Resource
private PayNotifyService notifyService;
@Resource
private PayAppService appService;
@Resource
private PayChannelService channelService;
@Resource
private DivideService divideService;
@Resource
private StringRedisTemplate notifyRedisTemplate;
@PostMapping(value = "/order/{channelId}")
@Operation(summary = "支付渠道的统一【支付】回调")
@ -91,10 +113,37 @@ public class PayNotifyController {
log.error("[notifyCallback][渠道编号({}) 找不到对应的支付客户端]", channelId);
throw exception(CHANNEL_NOT_FOUND);
}
// 2. 解析通知数据
PayRefundRespDTO notify = payClient.parseRefundNotify(params, body);
refundService.notifyRefund(channelId, notify);
RefundRecordDO refundRecordDO = refundRecordMapper.selectOne(Wrappers.<RefundRecordDO>lambdaUpdate()
.eq(RefundRecordDO::getOutRefundNo, notify.getOutRefundNo())
.last("limit 1"));
String status = "";
if(PayRefundStatusRespEnum.SUCCESS.getStatus().equals(notify.getStatus())){
status = PayDivideRefundStatusRespEnum.SUCCESS.getStatus();
}else{
status = PayDivideRefundStatusRespEnum.ABNORMAL.getStatus();
}
refundRecordDO.setStatus(status);
if("0".equals(refundRecordDO.getNotifyStatus()) && "20".equals(refundRecordDO.getStatus())){
//扣除积分
divideService.subtractWx(refundRecordDO.getTotalPrice(),Long.valueOf(refundRecordDO.getUserId()));
//更改订单
String jsonString = notifyRedisTemplate.opsForValue().get("R" + notify.getOutRefundNo());
DivideDO divideDO1 = JsonUtils.parseObject(jsonString, DivideDO.class);
divideService.updateDivideById(divideDO1);
//分账回退
if (divideDO1.getBackPrice()!=0){
ArrayList<DivideDO> divideDOS = new ArrayList<>();
divideDOS.add(divideDO1);
divideService.returnMoney(divideDOS);
}
}
refundRecordDO.setNotifyStatus("1");
refundRecordMapper.updateById(refundRecordDO);
//refundService.notifyRefund(channelId, notify);
return "success";
}

View File

@ -0,0 +1,76 @@
package cn.iocoder.yudao.module.pay.dal.dataobject.card;
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
import com.baomidou.mybatisplus.annotation.KeySequence;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
import lombok.ToString;
import java.math.BigDecimal;
/**
* 余额变动明细 DO
*
* @author 开发账号
*/
@TableName("member_card")
@KeySequence("member_card_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class DivideCardDO extends BaseDO {
public final static String ADD = "1";
public final static String MINUS = "0";
/**
* 编号
*/
@TableId
private Long id;
/**
* 用户Id
*/
private Long userId;
/**
* 余额
*/
private BigDecimal money;
/**
* 加减标志01
*/
private String flag;
/**
* 变动金额
*/
private BigDecimal changeMoney;
/**
* 消费类型
*/
private String type;
/**
* 赠送金额
*/
private BigDecimal giftAmount;
/**
* 现金金额
*/
private BigDecimal cashAmount;
/**
* 微信充值金额
*/
private BigDecimal wxAmount;
}

View File

@ -58,6 +58,11 @@ public class DivideDO extends BaseDO {
* 待分金额,单位:分
*/
private Integer unPrice;
/**
* 待退款金额,单位:分
*/
private Integer unRePrice;
/**
* 分账状态
*/
@ -68,6 +73,11 @@ public class DivideDO extends BaseDO {
*/
private String refundStatus;
/**
* 是否解冻
*/
private String unfreezeStatus;
/**
* 商户分账单号
*/
@ -82,7 +92,17 @@ public class DivideDO extends BaseDO {
private String orderId;
/**
* 退款金额
* 已用于退款金额
*/
private Integer refundPrice;
/**
* 分账回退金额
*/
private Integer backPrice;
/**
* 用于分账的金额
*/
private Integer psPrice;
}

View File

@ -39,6 +39,11 @@ public class DivideInfoDO extends BaseDO {
* 商户分账单号
*/
private String outOrderNo;
/**
* 微信分账单号
*/
private String orderId;
/**
* 分账个人接收方姓名
*/

View File

@ -1,11 +1,15 @@
package cn.iocoder.yudao.module.pay.dal.dataobject.refundrecord;
import lombok.*;
import java.util.*;
import java.time.LocalDateTime;
import java.time.LocalDateTime;
import com.baomidou.mybatisplus.annotation.*;
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
import com.baomidou.mybatisplus.annotation.KeySequence;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
import lombok.ToString;
/**
* 退款记录 DO
@ -68,4 +72,9 @@ public class RefundRecordDO extends BaseDO {
*/
private String refundId;
/**
* 是否回调
*/
private String notifyStatus;
}

View File

@ -0,0 +1,16 @@
package cn.iocoder.yudao.module.pay.dal.mysql.card;
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
import cn.iocoder.yudao.module.pay.dal.dataobject.card.DivideCardDO;
import org.apache.ibatis.annotations.Mapper;
/**
* 余额变动明细 Mapper
*
* @author 开发账号
*/
@Mapper
public interface DivideCardMapper extends BaseMapperX<DivideCardDO> {
}

View File

@ -41,7 +41,4 @@ public interface DivideMapper extends BaseMapperX<DivideDO> {
int orderCount(Long userId);
}

View File

@ -1,13 +1,11 @@
package cn.iocoder.yudao.module.pay.dal.mysql.refundrecord;
import java.util.*;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
import cn.iocoder.yudao.module.pay.controller.admin.refundrecord.vo.RefundRecordPageReqVO;
import cn.iocoder.yudao.module.pay.dal.dataobject.refundrecord.RefundRecordDO;
import org.apache.ibatis.annotations.Mapper;
import cn.iocoder.yudao.module.pay.controller.admin.refundrecord.vo.*;
/**
* 退款记录 Mapper

View File

@ -4,6 +4,8 @@ import cn.hutool.core.lang.Assert;
import cn.hutool.core.map.MapUtil;
import cn.hutool.extra.spring.SpringUtil;
import cn.iocoder.yudao.framework.common.exception.ServiceException;
import cn.iocoder.yudao.framework.pay.core.client.dto.divide.PayDivideBackRespDto;
import cn.iocoder.yudao.framework.pay.core.client.dto.divide.PayDivideBackUnifiedDto;
import cn.iocoder.yudao.framework.pay.core.client.dto.divide.PayDivideRespDto;
import cn.iocoder.yudao.framework.pay.core.client.dto.divide.PayDivideUnifiedDto;
import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderRespDTO;
@ -204,4 +206,9 @@ public class WalletPayClient extends AbstractPayClient<NonePayClientConfig> {
return null;
}
@Override
protected PayDivideBackRespDto doUnifiedDivideback(PayDivideBackUnifiedDto reqDTO) throws Throwable {
return null;
}
}

View File

@ -7,6 +7,7 @@ import cn.iocoder.yudao.module.pay.controller.app.divide.vo.DrawMoneyVO;
import cn.iocoder.yudao.module.pay.dal.dataobject.divide.DivideDO;
import javax.validation.Valid;
import java.util.List;
/**
* 分账单 Service 接口
@ -70,7 +71,20 @@ public interface DivideService {
/**
* 解冻剩余资金
* 提现(退款)
*/
Boolean drawMoney( DrawMoneyVO drawMoneyVO);
/**
* 扣除wx充值
*/
void subtractWx(Integer amount,Long userId);
/**
* 分账回退
*/
void returnMoney(List<DivideDO> backList);
void updateDivideById(DivideDO divideDO);
}

View File

@ -4,20 +4,31 @@ import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.iocoder.yudao.framework.common.exception.ServiceException;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.framework.pay.core.client.PayClient;
import cn.iocoder.yudao.framework.pay.core.client.dto.divide.PayDivideBackRespDto;
import cn.iocoder.yudao.framework.pay.core.client.dto.divide.PayDivideBackUnifiedDto;
import cn.iocoder.yudao.framework.pay.core.client.dto.divide.PayDivideRespDto;
import cn.iocoder.yudao.framework.pay.core.client.dto.divide.PayDivideUnifiedDto;
import cn.iocoder.yudao.framework.pay.core.client.dto.refund.PayRefundRespDTO;
import cn.iocoder.yudao.framework.pay.core.client.dto.refund.PayRefundUnifiedReqDTO;
import cn.iocoder.yudao.framework.pay.core.enums.divide.PayDivideRefundStatusRespEnum;
import cn.iocoder.yudao.framework.pay.core.enums.divide.PayDivideStatusRespEnum;
import cn.iocoder.yudao.module.pay.controller.admin.divide.vo.DividePageReqVO;
import cn.iocoder.yudao.module.pay.controller.admin.divide.vo.DivideSaveReqVO;
import cn.iocoder.yudao.module.pay.controller.app.divide.vo.DrawMoneyVO;
import cn.iocoder.yudao.module.pay.dal.dataobject.card.DivideCardDO;
import cn.iocoder.yudao.module.pay.dal.dataobject.channel.PayChannelDO;
import cn.iocoder.yudao.module.pay.dal.dataobject.divide.DivideDO;
import cn.iocoder.yudao.module.pay.dal.dataobject.dividecompany.DivideCompanyDO;
import cn.iocoder.yudao.module.pay.dal.dataobject.divideinfo.DivideInfoDO;
import cn.iocoder.yudao.module.pay.dal.dataobject.dividerecord.DivideRecordDO;
import cn.iocoder.yudao.module.pay.dal.dataobject.refundrecord.RefundRecordDO;
import cn.iocoder.yudao.module.pay.dal.mysql.card.DivideCardMapper;
import cn.iocoder.yudao.module.pay.dal.mysql.divide.DivideMapper;
import cn.iocoder.yudao.module.pay.dal.mysql.dividerecord.DivideRecordMapper;
import cn.iocoder.yudao.module.pay.dal.mysql.refundrecord.RefundRecordMapper;
import cn.iocoder.yudao.module.pay.dal.redis.no.PayNoRedisDAO;
import cn.iocoder.yudao.module.pay.framework.pay.config.PayProperties;
import cn.iocoder.yudao.module.pay.service.channel.PayChannelService;
@ -26,6 +37,8 @@ import cn.iocoder.yudao.module.pay.service.divideinfo.DivideInfoService;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.github.binarywang.wxpay.bean.profitsharing.request.ProfitSharingV3Request;
import com.github.binarywang.wxpay.bean.profitsharing.result.ProfitSharingV3Result;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.validation.annotation.Validated;
@ -34,9 +47,9 @@ import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.module.pay.enums.ErrorCodeConstants.*;
@ -48,6 +61,7 @@ import static cn.iocoder.yudao.module.pay.enums.ErrorCodeConstants.*;
*/
@Service
@Validated
@Slf4j
public class DivideServiceImpl implements DivideService {
@Resource
@ -70,6 +84,14 @@ public class DivideServiceImpl implements DivideService {
@Resource
private DivideRecordMapper divideRecordMapper;
@Resource
private RefundRecordMapper refundRecordMapper;
@Resource
private DivideCardMapper divideCardMapper;
@Resource
private StringRedisTemplate divideRedisTemplate;
@Override
@ -150,6 +172,11 @@ public class DivideServiceImpl implements DivideService {
public int startDivide() {
BigDecimal today = divideMapper.getToday();
if (today == null) {
return 0;
}
int money = today.multiply(new BigDecimal("100")).intValue();
LocalDateTime now = LocalDateTime.now();
LocalDateTime thirtyDaysAgo = now.minusDays(30);
@ -157,32 +184,41 @@ public class DivideServiceImpl implements DivideService {
List<DivideDO> divideDOS = divideMapper.selectList(Wrappers.<DivideDO>lambdaQuery()
.eq(DivideDO::getStatus, PayDivideStatusRespEnum.WAITING.getStatus())
.ge(DivideDO::getCreateTime,thirtyDaysAgo)
.eq(DivideDO::getUnfreezeStatus,"0")
.ge(DivideDO::getCreateTime, thirtyDaysAgo)
.orderByAsc(DivideDO::getCreateTime));
if(CollectionUtil.isEmpty(divideDOS)){
if (CollectionUtil.isEmpty(divideDOS)) {
return 0;
}
List<DivideDO> divideList = new ArrayList<>();
int sum = 0;
for(DivideDO divideDO: divideDOS){
Integer totalPrice = divideDO.getTotalPrice()-divideDO.getCpPrice();
for (DivideDO divideDO : divideDOS) {
Integer totalPrice = divideDO.getTotalPrice() - divideDO.getCpPrice();
sum = sum+totalPrice;
if(sum<money){
if(totalPrice == 0){
continue;
}
sum = sum + totalPrice;
if (sum < money) {
divideDO.setUnPrice(totalPrice);
divideDO.setCpPrice(divideDO.getCpPrice()+totalPrice);
divideDO.setCpPrice(divideDO.getCpPrice() + totalPrice);
divideDO.setPsPrice(divideDO.getPsPrice()+totalPrice);
divideList.add(divideDO);
} else if (sum ==money) {
} else if (sum == money) {
divideDO.setUnPrice(totalPrice);
divideDO.setCpPrice(divideDO.getCpPrice()+totalPrice);
divideDO.setCpPrice(divideDO.getCpPrice() + totalPrice);
divideDO.setPsPrice(divideDO.getPsPrice()+totalPrice);
divideList.add(divideDO);
break;
}else {
int leftMoney = money+totalPrice- sum;
} else {
int leftMoney = money + totalPrice - sum;
divideDO.setUnPrice(leftMoney);
divideDO.setCpPrice(divideDO.getCpPrice()+leftMoney);
divideDO.setCpPrice(divideDO.getCpPrice() + leftMoney);
divideDO.setPsPrice(divideDO.getPsPrice()+leftMoney);
divideList.add(divideDO);
break;
}
@ -198,7 +234,7 @@ public class DivideServiceImpl implements DivideService {
.setUnfreezeUnsplit(false)
.setReceivers(createDivideInfo(divideDO));
if(divideDO.getTotalPrice().equals(divideDO.getCpPrice())) {
if (divideDO.getTotalPrice().equals(divideDO.getCpPrice())) {
payDivideUnifiedDto.setUnfreezeUnsplit(true);
divideDO.setStatus(PayDivideStatusRespEnum.PROGRESS.getStatus());
}
@ -222,9 +258,11 @@ public class DivideServiceImpl implements DivideService {
}
}
}
divideMapper.updateBatch(divideDOS);
divideRecordMapper.insertBatch(divideRecordDOList);
return divideDOS.size();
if(CollectionUtil.isNotEmpty(divideRecordDOList)){
divideMapper.updateBatch(divideDOS);
divideRecordMapper.insertBatch(divideRecordDOList);
}
return divideRecordDOList.size();
}
@ -232,18 +270,18 @@ public class DivideServiceImpl implements DivideService {
public int getDivideResult() {
List<DivideRecordDO> divideRecordDOList = divideRecordMapper.getList();
if(CollectionUtil.isEmpty(divideRecordDOList)){
if (CollectionUtil.isEmpty(divideRecordDOList)) {
return 0;
}
Stream<String> transactionIdList = divideRecordDOList.stream().map(DivideRecordDO::getTransactionId);
Stream<String> OutOrderNoList = divideRecordDOList.stream().map(DivideRecordDO::getOutOrderNo);
List<String> transactionIdList = divideRecordDOList.stream().map(DivideRecordDO::getTransactionId).collect(Collectors.toList());
List<String> OutOrderNoList = divideRecordDOList.stream().map(DivideRecordDO::getOutOrderNo).collect(Collectors.toList());
List<DivideDO> divideDOS = divideMapper.selectList(Wrappers.<DivideDO>lambdaQuery()
.in(DivideDO::getChannelOrderNo, transactionIdList)
.in(DivideDO::getNo,OutOrderNoList));
.in(DivideDO::getNo, OutOrderNoList));
if(CollectionUtil.isEmpty(divideDOS)){
if (CollectionUtil.isEmpty(divideDOS)) {
return 0;
}
@ -254,11 +292,11 @@ public class DivideServiceImpl implements DivideService {
payDivideUnifiedDto.setTransactionId(divideDO.getChannelOrderNo())
.setOutOrderNo(divideDO.getNo());
PayDivideRespDto payDivideRespDto = client.unifiedDivideResult(payDivideUnifiedDto);
updateDivideInfo(payDivideRespDto.getReceivers(),divideDO.getNo());
updateDivideInfo(payDivideRespDto.getReceivers(), divideDO.getNo(),payDivideRespDto.getOrderId());
if(divideDO.getTotalPrice().equals(divideDO.getCpPrice())) {
if (divideDO.getTotalPrice().equals(divideDO.getCpPrice())) {
divideDO.setStatus(PayDivideStatusRespEnum.COMPLETE.getStatus());
}else{
} else {
divideDO.setNo(noRedisDAO.generate(payProperties.getOrderNoPrefix()));
}
}
@ -266,15 +304,24 @@ public class DivideServiceImpl implements DivideService {
return divideDOS.size();
}
void updateDivideInfo(List<ProfitSharingV3Result.Receiver> receivers,String outOrderNo){
List<DivideInfoDO> list = new ArrayList<>();
for (ProfitSharingV3Result.Receiver receiver:receivers){
void updateDivideInfo(List<ProfitSharingV3Result.Receiver> receivers, String outOrderNo,String orderId) {
List<DivideCompanyDO> list1 = divideCompanyService.getList();
List<String> accountList = list1.stream().map(DivideCompanyDO::getAccount).collect(Collectors.toList());
List<DivideInfoDO> list = new ArrayList<>();
for (ProfitSharingV3Result.Receiver receiver : receivers) {
if(!accountList.contains(receiver.getAccount())){
continue;
}
DivideInfoDO divideInfo = divideInfoService.getDivideInfo(outOrderNo, receiver.getAccount());
divideInfo.setResult(receiver.getResult())
.setFailReason(receiver.getFailReason())
.setDetailId(receiver.getDetailId())
.setDCreateTime(receiver.getCreateTime())
.setDFinishTime(receiver.getFinishTime());
.setDFinishTime(receiver.getFinishTime())
.setOrderId(orderId);
list.add(divideInfo);
}
divideInfoService.updateList(list);
@ -284,14 +331,14 @@ public class DivideServiceImpl implements DivideService {
public int getDivideFreeze() {
List<DivideDO> divideDOS = divideMapper.selectList(Wrappers.<DivideDO>lambdaQuery()
.eq(DivideDO::getStatus, PayDivideStatusRespEnum.COMPLETE.getStatus()));
if(CollectionUtil.isEmpty(divideDOS)){
if (CollectionUtil.isEmpty(divideDOS)) {
return 0;
}
for (DivideDO divideDO : divideDOS) {
PayClient client = channelService.getPayClient(divideDO.getChannelId());
PayDivideUnifiedDto payDivideUnifiedDto = new PayDivideUnifiedDto();
if(checkSuccess(divideDO.getNo())){
if (checkSuccess(divideDO.getNo())) {
payDivideUnifiedDto.setTransactionId(divideDO.getChannelOrderNo())
.setOutOrderNo(divideDO.getNo());
client.unifiedDivideFreeze(payDivideUnifiedDto);
@ -302,10 +349,10 @@ public class DivideServiceImpl implements DivideService {
return divideDOS.size();
}
boolean checkSuccess(String outOrderNo){
boolean checkSuccess(String outOrderNo) {
List<DivideInfoDO> divideList = divideInfoService.getDivideList(outOrderNo);
for (DivideInfoDO divideInfoDO:divideList){
if(!"SUCCESS".equals(divideInfoDO.getResult())){
for (DivideInfoDO divideInfoDO : divideList) {
if (!"SUCCESS".equals(divideInfoDO.getResult())) {
return false;
}
}
@ -317,32 +364,41 @@ public class DivideServiceImpl implements DivideService {
//判断订单是否完成
int i = divideMapper.orderCount(drawMoneyVO.getUserId());
if(i>0){
if (i > 0) {
throw new ServiceException(ORDER_NOT_COMPLETE);
}
//判断是否有金额可退款
BigDecimal wxAmount = divideMapper.getWxAmount(drawMoneyVO.getUserId());
int money = wxAmount.multiply(new BigDecimal("100")).intValue();
if(money<=0){
if(wxAmount == null){
throw new ServiceException(WX_ACCOUNT_NO);
}
int money = wxAmount.multiply(new BigDecimal("100")).intValue();
if (money <= 0) {
throw new ServiceException(WX_ACCOUNT_NO);
}
LocalDateTime now = LocalDateTime.now(); // 获取当前时间
LocalDateTime threeYearsAgo = now.minusDays(365);
//查询最近的充值订单,时间倒序退款
List<DivideDO> divideDOS = divideMapper.selectList(Wrappers.<DivideDO>lambdaQuery()
.eq(DivideDO::getChannelUserId, drawMoneyVO.getChannelUserId())
.eq(DivideDO::getRefundStatus,"0")
.eq(DivideDO::getRefundStatus, "0")
.ge(DivideDO::getCreateTime,threeYearsAgo)
.orderByDesc(DivideDO::getCreateTime));
//筛选出未分账的订单
List<DivideDO> unDivideList = divideDOS.stream().filter(vo -> vo.getCpPrice() == 0).collect(Collectors.toList());
List<DivideDO> unDivideList = divideDOS.stream().filter(vo -> vo.getPsPrice() == 0).collect(Collectors.toList());
//筛选出已分账的订单
List<DivideDO> divideList = divideDOS.stream().filter(vo -> vo.getCpPrice().equals(vo.getTotalPrice())).collect(Collectors.toList());
List<DivideDO> divideCpList = divideDOS.stream().filter(vo -> vo.getPsPrice() != 0).collect(Collectors.toList());
//筛选出分账完的订单
List<DivideDO> divideList = divideCpList.stream().filter(vo -> vo.getTotalPrice().equals(vo.getCpPrice())).collect(Collectors.toList());
//剩下的是未分账完 的订单
divideDOS.removeAll(unDivideList);
divideDOS.removeAll(divideList);
divideCpList.removeAll(divideList);
//需要回退的订单
List<DivideDO> backList = new ArrayList<>();
@ -353,54 +409,329 @@ public class DivideServiceImpl implements DivideService {
//先从未分账的订单计算
for (DivideDO divideDO: unDivideList){
Integer totalPrice = divideDO.getTotalPrice()-divideDO.getCpPrice();
for (DivideDO divideDO : unDivideList) {
Integer totalPrice = divideDO.getTotalPrice() - divideDO.getCpPrice();
sum = sum + totalPrice;
sum = sum+totalPrice;
if(sum<=money){
divideDO.setRefundPrice(totalPrice);
divideDO.setCpPrice(divideDO.getCpPrice()+totalPrice);
if (sum < money) {
divideDO.setUnRePrice(totalPrice);
divideDO.setRefundPrice(totalPrice+divideDO.getCpPrice());
divideDO.setCpPrice(divideDO.getCpPrice() + totalPrice);
noBackList.add(divideDO);
}else {
int leftMoney = money+totalPrice- sum;
divideDO.setRefundPrice(leftMoney);
divideDO.setCpPrice(divideDO.getCpPrice()+leftMoney);
} else if (sum == money) {
divideDO.setUnRePrice(totalPrice);
divideDO.setRefundPrice(totalPrice+divideDO.getCpPrice());
divideDO.setCpPrice(divideDO.getCpPrice() + totalPrice);
noBackList.add(divideDO);
break;
} else {
int leftMoney = money + totalPrice - sum;
divideDO.setUnRePrice(leftMoney);
divideDO.setRefundPrice(divideDO.getCpPrice() + leftMoney);
divideDO.setCpPrice(divideDO.getCpPrice() + leftMoney);
noBackList.add(divideDO);
break;
}
}
//从分账完的订单进行退款
if(sum<money){
if (sum < money) {
for (DivideDO divideDO : divideList) {
Integer totalPrice = divideDO.getTotalPrice()-divideDO.getRefundPrice();
for (DivideDO divideDO:divideList){
Integer totalPrice = divideDO.getTotalPrice()-divideDO.getCpPrice();
sum = sum + totalPrice;
sum = sum+totalPrice;
if(sum<=money){
divideDO.setRefundPrice(totalPrice);
if (sum < money) {
divideDO.setUnRePrice(totalPrice);
divideDO.setRefundPrice(divideDO.getTotalPrice());
divideDO.setBackPrice(totalPrice);
divideDO.setPsPrice(0);
backList.add(divideDO);
}else {
int leftMoney = money+totalPrice- sum;
divideDO.setRefundPrice(leftMoney);
} else if (sum == money) {
divideDO.setUnRePrice(totalPrice);
divideDO.setRefundPrice(divideDO.getTotalPrice());
divideDO.setBackPrice(totalPrice);
divideDO.setPsPrice(0);
backList.add(divideDO);
break;
} else {
int leftMoney = money + totalPrice - sum;
divideDO.setUnRePrice(leftMoney);
divideDO.setRefundPrice(leftMoney+divideDO.getRefundPrice());
divideDO.setBackPrice(leftMoney);
divideDO.setPsPrice(divideDO.getPsPrice()-leftMoney);
backList.add(divideDO);
break;
}
}
}
//从部分分账完的订单进行退款,由于分账的特性只会存在一条未分账完全的资金
if (sum < money) {
for (DivideDO divideDO : divideCpList) {
//剩余未解冻的资金
Integer totalPrice = divideDO.getTotalPrice() - divideDO.getCpPrice();
sum = sum + totalPrice;
if (sum < money) {
if("0".equals(divideDO.getUnfreezeStatus())){
//先解冻剩余资金
String unFreeNo = noRedisDAO.generate(payProperties.getOrderNoPrefix());
PayClient client = channelService.getPayClient(divideDO.getChannelId());
PayDivideUnifiedDto payDivideUnifiedDto = new PayDivideUnifiedDto();
payDivideUnifiedDto.setTransactionId(divideDO.getChannelOrderNo())
.setOutOrderNo(unFreeNo);
client.unifiedDivideFreeze(payDivideUnifiedDto);
DivideDO unfree = new DivideDO();
unfree.setUnfreezeStatus("1").setId(divideDO.getId());
divideMapper.updateById(unfree);
String state = "";
do {
try {
//等待解冻成功
TimeUnit.SECONDS.sleep(15);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
PayDivideUnifiedDto resultDto = new PayDivideUnifiedDto();
resultDto.setTransactionId(divideDO.getChannelOrderNo())
.setOutOrderNo(unFreeNo);
PayDivideRespDto payDivideRespDto = client.unifiedDivideResult(resultDto);
state = payDivideRespDto.getState();
}while ("PROCESSING".equals(state));
return null;
}
//计算退款资金
//可用于退款的资金
Integer psPrice = divideDO.getTotalPrice()- divideDO.getRefundPrice();
sum = sum - totalPrice+psPrice;
if (sum <= money){
divideDO.setUnRePrice(psPrice);
divideDO.setRefundPrice(divideDO.getTotalPrice());
divideDO.setBackPrice(divideDO.getPsPrice());
divideDO.setPsPrice(0);
}else{
int left = money-sum+psPrice;
divideDO.setUnRePrice(left);
divideDO.setRefundPrice(divideDO.getRefundPrice()+left);
//分账回退金额
int back = left-totalPrice;
divideDO.setBackPrice(back);
divideDO.setPsPrice(divideDO.getPsPrice()-back);
}
backList.add(divideDO);
divideDO.setCpPrice(divideDO.getTotalPrice());
} else {
//计算使用资金
Integer useMoney = sum - money;
divideDO.setUnRePrice(useMoney);
divideDO.setRefundPrice(useMoney+divideDO.getRefundPrice());
divideDO.setCpPrice(divideDO.getCpPrice() + useMoney);
noBackList.add(divideDO);
}
break;
}
}
//退款记录
List<RefundRecordDO> list = new ArrayList();
int failNum = 0;
//调用退款接口
if(CollectionUtil.isNotEmpty(noBackList)){
for (DivideDO divideDO : noBackList) {
String outRefundNo = noRedisDAO.generate(payProperties.getRefundNoPrefix());
RefundRecordDO refundRecordDO = new RefundRecordDO();
refundRecordDO.setOutTradeNo(divideDO.getNo());
refundRecordDO.setOutRefundNo(outRefundNo);
refundRecordDO.setTransactionId(divideDO.getChannelOrderNo());
refundRecordDO.setChannelId(divideDO.getChannelId());
refundRecordDO.setChannelUserId(divideDO.getChannelUserId());
refundRecordDO.setChannelCode(divideDO.getChannelCode());
refundRecordDO.setUserId(String.valueOf(drawMoneyVO.getUserId()));
refundRecordDO.setTotalPrice(divideDO.getUnRePrice());
PayChannelDO channel = channelService.validPayChannel(divideDO.getChannelId());
PayClient client = channelService.getPayClient(divideDO.getChannelId());
PayRefundUnifiedReqDTO payRefundUnifiedReqDTO = new PayRefundUnifiedReqDTO();
payRefundUnifiedReqDTO.setTransactionId(divideDO.getChannelOrderNo())
.setReason("提现").setRefundPrice(divideDO.getUnRePrice())
.setOutRefundNo(outRefundNo).setPayPrice(divideDO.getTotalPrice())
.setNotifyUrl(genChannelRefundNotifyUrl(channel));
PayRefundRespDTO payRefundRespDTO = client.unifiedRefund(payRefundUnifiedReqDTO);
String channelRefundNo = payRefundRespDTO.getChannelRefundNo();
refundRecordDO.setRefundId(channelRefundNo);
if(cn.iocoder.yudao.framework.pay.core.enums.refund.PayRefundStatusRespEnum.WAITING.getStatus().equals(payRefundRespDTO.getStatus())){
refundRecordDO.setStatus(PayDivideRefundStatusRespEnum.PROCESSING.getStatus());
} else if (cn.iocoder.yudao.framework.pay.core.enums.refund.PayRefundStatusRespEnum.SUCCESS.getStatus().equals(payRefundRespDTO.getStatus())) {
refundRecordDO.setStatus(PayDivideRefundStatusRespEnum.SUCCESS.getStatus());
}else {
failNum++;
refundRecordDO.setStatus(PayDivideRefundStatusRespEnum.ABNORMAL.getStatus());
}
refundRecordMapper.insert(refundRecordDO);
if(divideDO.getRefundPrice().equals(divideDO.getTotalPrice())){
divideDO.setRefundStatus("1");
}
if(divideDO.getTotalPrice().equals(divideDO.getCpPrice())){
divideDO.setStatus(PayDivideStatusRespEnum.CASH.getStatus());
}
String jsonString = JsonUtils.toJsonString(divideDO);
//DivideDO divideDO1 = JsonUtils.parseObject(jsonString, DivideDO.class);
divideRedisTemplate.opsForValue().set("R"+outRefundNo,jsonString);
//divideMapper.updateById(divideDO);
//list.add(refundRecordDO);
}
}
//调用退款接口
if(CollectionUtil.isNotEmpty(backList)){
for (DivideDO divideDO : backList) {
String outRefundNo = noRedisDAO.generate(payProperties.getRefundNoPrefix());
RefundRecordDO refundRecordDO = new RefundRecordDO();
refundRecordDO.setOutTradeNo(divideDO.getNo());
refundRecordDO.setOutRefundNo(outRefundNo);
refundRecordDO.setTransactionId(divideDO.getChannelOrderNo());
refundRecordDO.setChannelId(divideDO.getChannelId());
refundRecordDO.setChannelUserId(divideDO.getChannelUserId());
refundRecordDO.setChannelCode(divideDO.getChannelCode());
refundRecordDO.setUserId(String.valueOf(drawMoneyVO.getUserId()));
refundRecordDO.setTotalPrice(divideDO.getUnRePrice());
PayChannelDO channel = channelService.validPayChannel(divideDO.getChannelId());
PayClient client = channelService.getPayClient(divideDO.getChannelId());
PayRefundUnifiedReqDTO payRefundUnifiedReqDTO = new PayRefundUnifiedReqDTO();
payRefundUnifiedReqDTO.setTransactionId(divideDO.getChannelOrderNo())
.setReason("提现").setRefundPrice(divideDO.getUnRePrice())
.setOutRefundNo(outRefundNo).setPayPrice(divideDO.getTotalPrice())
.setNotifyUrl(genChannelRefundNotifyUrl(channel));
PayRefundRespDTO payRefundRespDTO = client.unifiedRefund(payRefundUnifiedReqDTO);
String channelRefundNo = payRefundRespDTO.getChannelRefundNo();
refundRecordDO.setRefundId(channelRefundNo);
if(cn.iocoder.yudao.framework.pay.core.enums.refund.PayRefundStatusRespEnum.WAITING.getStatus().equals(payRefundRespDTO.getStatus())){
refundRecordDO.setStatus(PayDivideRefundStatusRespEnum.PROCESSING.getStatus());
} else if (cn.iocoder.yudao.framework.pay.core.enums.refund.PayRefundStatusRespEnum.SUCCESS.getStatus().equals(payRefundRespDTO.getStatus())) {
refundRecordDO.setStatus(PayDivideRefundStatusRespEnum.SUCCESS.getStatus());
}else {
failNum++;
refundRecordDO.setStatus(PayDivideRefundStatusRespEnum.ABNORMAL.getStatus());
}
//list.add(refundRecordDO);
refundRecordMapper.insert(refundRecordDO);
if(divideDO.getRefundPrice().equals(divideDO.getTotalPrice())){
divideDO.setRefundStatus("1");
}
if(divideDO.getTotalPrice().equals(divideDO.getCpPrice())){
divideDO.setStatus(PayDivideStatusRespEnum.CASH.getStatus());
}
String jsonString = JsonUtils.toJsonString(divideDO);
//DivideDO divideDO1 = JsonUtils.parseObject(jsonString, DivideDO.class);
divideRedisTemplate.opsForValue().set("R"+outRefundNo,jsonString);
//divideMapper.updateById(divideDO);
}
}
//分账回退
//if(CollectionUtil.isNotEmpty(backList)){
// returnMoney(backList);
//}
//refundRecordMapper.insertBatch(list);
// todo清空
return failNum>0?false:true;
}
private String genChannelRefundNotifyUrl(PayChannelDO channel) {
return payProperties.getRefundNotifyUrl() + "/" + channel.getId();
}
@Override
public void returnMoney(List<DivideDO> backList){
List<DivideCompanyDO> companyList = divideCompanyService.getList();
Map<String, DivideCompanyDO> companyMap = companyList.stream().collect(Collectors.toMap(DivideCompanyDO::getName, vo -> vo));
for(DivideDO divideDO:backList){
PayClient client = channelService.getPayClient(divideDO.getChannelId());
//查询出分账的具体条数
List<DivideInfoDO> divideListByTi = divideInfoService.getDivideListByTi(divideDO.getChannelOrderNo());
//全部回退
if(divideDO.getRefundPrice().equals(divideDO.getTotalPrice())){
for(DivideInfoDO divideInfoDO : divideListByTi){
doReturn(client,divideInfoDO,companyMap.get(divideInfoDO.getName()).getAccount());
}
}else{
Integer refundPrice = divideDO.getRefundPrice();
//算出每个公司应回退的金额
for(DivideCompanyDO companyDO:companyList){
int backmoney = (int) Math.ceil(refundPrice* Double.valueOf(companyDO.getProportion()));
List<DivideInfoDO> baList = divideListByTi.stream().filter(vo -> vo.getName().equals(companyDO.getName())).collect(Collectors.toList());
int sum = 0;
for (DivideInfoDO infoDO : baList){
sum = sum+infoDO.getAmount();
if(sum<backmoney){
doReturn(client,infoDO,companyDO.getAccount());
}else if (sum==backmoney){
doReturn(client,infoDO,companyDO.getAccount());
break;
}else{
int left = backmoney+infoDO.getAmount()-sum;
infoDO.setAmount(left);
doReturn(client,infoDO,companyDO.getAccount());
break;
}
}
}
}
}
}
void doReturn(PayClient client ,DivideInfoDO divideInfoDO ,String account){
PayDivideBackUnifiedDto payDivideBackUnifiedDto = new PayDivideBackUnifiedDto();
payDivideBackUnifiedDto.setAmount(divideInfoDO.getAmount().longValue());
payDivideBackUnifiedDto.setDescription("分账回退");
payDivideBackUnifiedDto.setOrderId(divideInfoDO.getOrderId());
payDivideBackUnifiedDto.setReturnMchid(account);
payDivideBackUnifiedDto.setOutOrderNo(divideInfoDO.getOutOrderNo());
payDivideBackUnifiedDto.setOutReturnNo(noRedisDAO.generate(payProperties.getOrderNoPrefix()));
PayDivideBackRespDto payDivideBackRespDto = client.unifiedDivideback(payDivideBackUnifiedDto);
log.info("订单:"+payDivideBackRespDto.getOrderId()+"回退结果:"+payDivideBackRespDto.getResult());
}
@Override
public void subtractWx(Integer amount, Long userId) {
DivideCardDO lastCardDO = divideCardMapper.selectOne(Wrappers.<DivideCardDO>lambdaQuery()
.eq(DivideCardDO::getUserId, userId)
.orderByDesc(DivideCardDO::getCreateTime)
.last("limit 1"));
BigDecimal changeMoney = new BigDecimal(amount).divide(new BigDecimal("100"));
//获取最新余额
DivideCardDO cardDO = new DivideCardDO();
cardDO.setUserId(userId);
cardDO.setFlag(DivideCardDO.MINUS);
cardDO.setChangeMoney(changeMoney);
cardDO.setType("7");
BigDecimal oldMoney = lastCardDO.getMoney();
BigDecimal wxOldMoney= lastCardDO.getWxAmount();
cardDO.setMoney(oldMoney.subtract(changeMoney).setScale(2, BigDecimal.ROUND_HALF_UP));
cardDO.setWxAmount(wxOldMoney.subtract(changeMoney).setScale(2, BigDecimal.ROUND_HALF_UP));
cardDO.setGiftAmount(lastCardDO.getGiftAmount());
divideCardMapper.insert(cardDO);
}
@Override
public void updateDivideById(DivideDO divideDO) {
divideMapper.updateById(divideDO);
}
}

View File

@ -60,4 +60,6 @@ public interface DivideInfoService {
List<DivideInfoDO> getDivideList(String outOrderNo);
void updateList(List<DivideInfoDO> list);
List<DivideInfoDO> getDivideListByTi(String transactionId);
}

View File

@ -105,4 +105,10 @@ public class DivideInfoServiceImpl implements DivideInfoService {
return divideInfoMapper.selectList(Wrappers.<DivideInfoDO>lambdaQuery()
.eq(DivideInfoDO::getOutOrderNo, outOrderNo));
}
@Override
public List<DivideInfoDO> getDivideListByTi(String transactionId) {
return divideInfoMapper.selectList(Wrappers.<DivideInfoDO>lambdaQuery()
.eq(DivideInfoDO::getTransactionId, transactionId));
}
}

View File

@ -380,6 +380,7 @@ public class PayOrderServiceImpl implements PayOrderService {
//3.添加分账
order.setChannelOrderNo(notify.getChannelOrderNo());
order.setChannelUserId(notify.getChannelUserId());
addDivide(channel,order);
return false;
}

View File

@ -52,4 +52,5 @@ public interface RefundRecordService {
*/
PageResult<RefundRecordDO> getRefundRecordPage(RefundRecordPageReqVO pageReqVO);
}

View File

@ -1,5 +1,7 @@
package cn.iocoder.yudao.framework.pay.core.client;
import cn.iocoder.yudao.framework.pay.core.client.dto.divide.PayDivideBackRespDto;
import cn.iocoder.yudao.framework.pay.core.client.dto.divide.PayDivideBackUnifiedDto;
import cn.iocoder.yudao.framework.pay.core.client.dto.divide.PayDivideRespDto;
import cn.iocoder.yudao.framework.pay.core.client.dto.divide.PayDivideUnifiedDto;
import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderRespDTO;
@ -125,4 +127,13 @@ public interface PayClient {
PayDivideRespDto unifiedDivideFreeze(PayDivideUnifiedDto reqDTO);
/**
* 分账回退
*
* @param reqDTO 分账参数
* @return 分账
*/
PayDivideBackRespDto unifiedDivideback(PayDivideBackUnifiedDto reqDTO);
}

View File

@ -0,0 +1,51 @@
package cn.iocoder.yudao.framework.pay.core.client.dto.divide;
import lombok.Data;
/**
* @author zt
* @description <description class purpose>
* @since 2024/7/17
*/
@Data
public class PayDivideBackRespDto {
private String subMchId;
private String orderId;
private String outOrderNo;
private String outReturnNo;
private String returnId;
private String returnMchid;
private Long amount;
private String description;
private String result;
private String failReason;
private String createTime;
private String finishTime;
private String channelErrorCode;
/**
* 调用渠道报错时,错误信息
*/
private String channelErrorMsg;
/**
* 原始的异步通知结果
*/
private Object rawData;
/**
* 创建【FAILURE】状态的退款返回
*/
public static PayDivideBackRespDto failureOf(String channelErrorCode, String channelErrorMsg,
String transactionId, Object rawData) {
PayDivideBackRespDto respDTO = new PayDivideBackRespDto();
//respDTO.status = PayRefundStatusRespEnum.FAILURE.getStatus();
respDTO.channelErrorCode = channelErrorCode;
respDTO.channelErrorMsg = channelErrorMsg;
// 相对通用的字段
respDTO.orderId = transactionId;
respDTO.rawData = rawData;
return respDTO;
}
}

View File

@ -0,0 +1,47 @@
package cn.iocoder.yudao.framework.pay.core.client.dto.divide;
import lombok.Data;
/**
* @author zt
* @description <description class purpose>
* @since 2024/7/17
*/
@Data
public class PayDivideBackUnifiedDto {
/**
* 应用ID
*/
private String appid;
/**
* 微信分账单号
*/
private String orderId;
/**
* 商户分账单号
*/
private String outOrderNo;
/**
* 商户回退单号
*/
private String outReturnNo;
/**
* 回退商户号
*/
private String returnMchid;
/**
* 回退金额
*/
private Long amount;
/**
* 回退描述
*/
private String description;
}

View File

@ -28,9 +28,12 @@ public class PayRefundUnifiedReqDTO {
*
* 对应 PayOrderExtensionDO 的 no 字段
*/
@NotEmpty(message = "外部订单编号不能为空")
//@NotEmpty(message = "外部订单编号不能为空")
private String outTradeNo;
@NotEmpty(message = "微信订单编号不能为空")
private String transactionId;
/**
* 外部退款号
*

View File

@ -4,6 +4,8 @@ import cn.iocoder.yudao.framework.common.exception.ServiceException;
import cn.iocoder.yudao.framework.common.util.validation.ValidationUtils;
import cn.iocoder.yudao.framework.pay.core.client.PayClient;
import cn.iocoder.yudao.framework.pay.core.client.PayClientConfig;
import cn.iocoder.yudao.framework.pay.core.client.dto.divide.PayDivideBackRespDto;
import cn.iocoder.yudao.framework.pay.core.client.dto.divide.PayDivideBackUnifiedDto;
import cn.iocoder.yudao.framework.pay.core.client.dto.divide.PayDivideRespDto;
import cn.iocoder.yudao.framework.pay.core.client.dto.divide.PayDivideUnifiedDto;
import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderRespDTO;
@ -313,4 +315,26 @@ public abstract class AbstractPayClient<Config extends PayClientConfig> implemen
}
protected abstract PayDivideRespDto dounifiedDivideFreeze(PayDivideUnifiedDto reqDTO)
throws Throwable;
@Override
public PayDivideBackRespDto unifiedDivideback(PayDivideBackUnifiedDto reqDTO) {
ValidationUtils.validate(reqDTO);
// 执行统一下单
PayDivideBackRespDto resp;
try {
resp = doUnifiedDivideback(reqDTO);
} catch (ServiceException ex) { // 业务异常,都是实现类已经翻译,所以直接抛出即可
throw ex;
} catch (Throwable ex) {
// 系统异常,则包装成 PayException 异常抛出
log.error("[unifiedOrder][客户端({}) request({}) 发起支付异常]",
getId(), toJsonString(reqDTO), ex);
throw buildPayException(ex);
}
return resp;
}
protected abstract PayDivideBackRespDto doUnifiedDivideback(PayDivideBackUnifiedDto reqDTO)
throws Throwable;
}

View File

@ -1,5 +1,7 @@
package cn.iocoder.yudao.framework.pay.core.client.impl.alipay;
import cn.iocoder.yudao.framework.pay.core.client.dto.divide.PayDivideBackRespDto;
import cn.iocoder.yudao.framework.pay.core.client.dto.divide.PayDivideBackUnifiedDto;
import cn.iocoder.yudao.framework.pay.core.client.dto.divide.PayDivideRespDto;
import cn.iocoder.yudao.framework.pay.core.client.dto.divide.PayDivideUnifiedDto;
import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderRespDTO;
@ -73,4 +75,9 @@ public class AlipayAppPayClient extends AbstractAlipayPayClient {
protected PayDivideRespDto dounifiedDivideFreeze(PayDivideUnifiedDto reqDTO) throws Throwable {
return null;
}
@Override
protected PayDivideBackRespDto doUnifiedDivideback(PayDivideBackUnifiedDto reqDTO) throws Throwable {
return null;
}
}

View File

@ -3,6 +3,8 @@ package cn.iocoder.yudao.framework.pay.core.client.impl.alipay;
import cn.hutool.core.date.LocalDateTimeUtil;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.StrUtil;
import cn.iocoder.yudao.framework.pay.core.client.dto.divide.PayDivideBackRespDto;
import cn.iocoder.yudao.framework.pay.core.client.dto.divide.PayDivideBackUnifiedDto;
import cn.iocoder.yudao.framework.pay.core.client.dto.divide.PayDivideRespDto;
import cn.iocoder.yudao.framework.pay.core.client.dto.divide.PayDivideUnifiedDto;
import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderRespDTO;
@ -99,4 +101,9 @@ public class AlipayBarPayClient extends AbstractAlipayPayClient {
protected PayDivideRespDto dounifiedDivideFreeze(PayDivideUnifiedDto reqDTO) throws Throwable {
return null;
}
@Override
protected PayDivideBackRespDto doUnifiedDivideback(PayDivideBackUnifiedDto reqDTO) throws Throwable {
return null;
}
}

View File

@ -2,6 +2,8 @@ package cn.iocoder.yudao.framework.pay.core.client.impl.alipay;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.http.Method;
import cn.iocoder.yudao.framework.pay.core.client.dto.divide.PayDivideBackRespDto;
import cn.iocoder.yudao.framework.pay.core.client.dto.divide.PayDivideBackUnifiedDto;
import cn.iocoder.yudao.framework.pay.core.client.dto.divide.PayDivideRespDto;
import cn.iocoder.yudao.framework.pay.core.client.dto.divide.PayDivideUnifiedDto;
import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderRespDTO;
@ -83,4 +85,9 @@ public class AlipayPcPayClient extends AbstractAlipayPayClient {
protected PayDivideRespDto dounifiedDivideFreeze(PayDivideUnifiedDto reqDTO) throws Throwable {
return null;
}
@Override
protected PayDivideBackRespDto doUnifiedDivideback(PayDivideBackUnifiedDto reqDTO) throws Throwable {
return null;
}
}

View File

@ -1,5 +1,7 @@
package cn.iocoder.yudao.framework.pay.core.client.impl.alipay;
import cn.iocoder.yudao.framework.pay.core.client.dto.divide.PayDivideBackRespDto;
import cn.iocoder.yudao.framework.pay.core.client.dto.divide.PayDivideBackUnifiedDto;
import cn.iocoder.yudao.framework.pay.core.client.dto.divide.PayDivideRespDto;
import cn.iocoder.yudao.framework.pay.core.client.dto.divide.PayDivideUnifiedDto;
import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderRespDTO;
@ -80,4 +82,9 @@ public class AlipayQrPayClient extends AbstractAlipayPayClient {
protected PayDivideRespDto dounifiedDivideFreeze(PayDivideUnifiedDto reqDTO) throws Throwable {
return null;
}
@Override
protected PayDivideBackRespDto doUnifiedDivideback(PayDivideBackUnifiedDto reqDTO) throws Throwable {
return null;
}
}

View File

@ -1,6 +1,8 @@
package cn.iocoder.yudao.framework.pay.core.client.impl.alipay;
import cn.hutool.http.Method;
import cn.iocoder.yudao.framework.pay.core.client.dto.divide.PayDivideBackRespDto;
import cn.iocoder.yudao.framework.pay.core.client.dto.divide.PayDivideBackUnifiedDto;
import cn.iocoder.yudao.framework.pay.core.client.dto.divide.PayDivideRespDto;
import cn.iocoder.yudao.framework.pay.core.client.dto.divide.PayDivideUnifiedDto;
import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderRespDTO;
@ -73,4 +75,9 @@ public class AlipayWapPayClient extends AbstractAlipayPayClient {
protected PayDivideRespDto dounifiedDivideFreeze(PayDivideUnifiedDto reqDTO) throws Throwable {
return null;
}
@Override
protected PayDivideBackRespDto doUnifiedDivideback(PayDivideBackUnifiedDto reqDTO) throws Throwable {
return null;
}
}

View File

@ -1,5 +1,7 @@
package cn.iocoder.yudao.framework.pay.core.client.impl.mock;
import cn.iocoder.yudao.framework.pay.core.client.dto.divide.PayDivideBackRespDto;
import cn.iocoder.yudao.framework.pay.core.client.dto.divide.PayDivideBackUnifiedDto;
import cn.iocoder.yudao.framework.pay.core.client.dto.divide.PayDivideRespDto;
import cn.iocoder.yudao.framework.pay.core.client.dto.divide.PayDivideUnifiedDto;
import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderRespDTO;
@ -94,4 +96,9 @@ public class MockPayClient extends AbstractPayClient<NonePayClientConfig> {
return null;
}
@Override
protected PayDivideBackRespDto doUnifiedDivideback(PayDivideBackUnifiedDto reqDTO) throws Throwable {
return null;
}
}

View File

@ -8,6 +8,8 @@ import cn.hutool.core.date.TemporalAccessorUtil;
import cn.hutool.core.util.StrUtil;
import cn.iocoder.yudao.framework.common.util.io.FileUtils;
import cn.iocoder.yudao.framework.common.util.object.ObjectUtils;
import cn.iocoder.yudao.framework.pay.core.client.dto.divide.PayDivideBackRespDto;
import cn.iocoder.yudao.framework.pay.core.client.dto.divide.PayDivideBackUnifiedDto;
import cn.iocoder.yudao.framework.pay.core.client.dto.divide.PayDivideRespDto;
import cn.iocoder.yudao.framework.pay.core.client.dto.divide.PayDivideUnifiedDto;
import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderRespDTO;
@ -23,8 +25,10 @@ import com.github.binarywang.wxpay.bean.notify.WxPayNotifyV3Result;
import com.github.binarywang.wxpay.bean.notify.WxPayOrderNotifyResult;
import com.github.binarywang.wxpay.bean.notify.WxPayRefundNotifyResult;
import com.github.binarywang.wxpay.bean.notify.WxPayRefundNotifyV3Result;
import com.github.binarywang.wxpay.bean.profitsharing.request.ProfitSharingReturnV3Request;
import com.github.binarywang.wxpay.bean.profitsharing.request.ProfitSharingUnfreezeV3Request;
import com.github.binarywang.wxpay.bean.profitsharing.request.ProfitSharingV3Request;
import com.github.binarywang.wxpay.bean.profitsharing.result.ProfitSharingReturnV3Result;
import com.github.binarywang.wxpay.bean.profitsharing.result.ProfitSharingUnfreezeV3Result;
import com.github.binarywang.wxpay.bean.profitsharing.result.ProfitSharingV3Result;
import com.github.binarywang.wxpay.bean.request.WxPayOrderQueryRequest;
@ -314,7 +318,7 @@ public abstract class AbstractWxPayClient extends AbstractPayClient<WxPayClientC
private PayRefundRespDTO doUnifiedRefundV3(PayRefundUnifiedReqDTO reqDTO) throws Throwable {
// 1. 构建 WxPayRefundRequest 请求
WxPayRefundV3Request request = new WxPayRefundV3Request()
.setOutTradeNo(reqDTO.getOutTradeNo())
.setTransactionId(reqDTO.getTransactionId())
.setOutRefundNo(reqDTO.getOutRefundNo())
.setAmount(new WxPayRefundV3Request.Amount().setRefund(reqDTO.getRefundPrice())
.setTotal(reqDTO.getPayPrice()).setCurrency("CNY"))
@ -610,4 +614,44 @@ public abstract class AbstractWxPayClient extends AbstractPayClient<WxPayClientC
BeanUtil.copyProperties(response,payDivideRespDto);
return payDivideRespDto;
}
@Override
protected PayDivideBackRespDto doUnifiedDivideback(PayDivideBackUnifiedDto reqDTO) throws Throwable {
try {
switch (config.getApiVersion()) {
case API_VERSION_V2:
return doUnifiedDividebackV2(reqDTO);
case WxPayClientConfig.API_VERSION_V3:
return doUnifiedDividebackV3(reqDTO);
default:
throw new IllegalArgumentException(String.format("未知的 API 版本(%s)", config.getApiVersion()));
}
} catch (WxPayException e) {
String errorCode = getErrorCode(e);
String errorMessage = getErrorMessage(e);
return PayDivideBackRespDto.failureOf(errorCode, errorMessage,
reqDTO.getOrderId(), e.getXmlString());
}
}
private PayDivideBackRespDto doUnifiedDividebackV2(PayDivideBackUnifiedDto reqDTO) throws Throwable {
throw new UnsupportedOperationException("待实现");
}
private PayDivideBackRespDto doUnifiedDividebackV3(PayDivideBackUnifiedDto reqDTO) throws Throwable {
// 1. 构建 WxPayRefundRequest 请求
ProfitSharingReturnV3Request request = new ProfitSharingReturnV3Request();
request.setOrderId(reqDTO.getOrderId());
request.setOutOrderNo(reqDTO.getOutOrderNo());
request.setOutReturnNo(reqDTO.getOutReturnNo());
request.setReturnMchid(reqDTO.getReturnMchid());
request.setAmount(reqDTO.getAmount());
request.setDescription(reqDTO.getDescription());
// 2.1 执行请求
ProfitSharingReturnV3Result response = client.getProfitSharingService().profitSharingReturnV3(request);
PayDivideBackRespDto payDivideRespDto = new PayDivideBackRespDto();
BeanUtil.copyProperties(response,payDivideRespDto);
return payDivideRespDto;
}
}

View File

@ -0,0 +1,24 @@
package cn.iocoder.yudao.framework.pay.core.enums.divide;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* 渠道的退款状态枚举
*
* @author jason
*/
@Getter
@AllArgsConstructor
public enum PayDivideRefundStatusRespEnum {
PROCESSING("10", "退款处理中"),
SUCCESS("20", "退款成功"),
CLOSED("30", "退款关闭"),
ABNORMAL("40", "退款异常"),
;
private final String status;
private final String name;
}

View File

@ -16,6 +16,8 @@ public enum PayDivideStatusRespEnum {
PROGRESS("10", "分账进行中"),
COMPLETE("20", "分账完成"),
FINISH("30", "分账结束"),
CASH("40", "提现"),
;
private final String status;