Commit 7237bc07bb7ab656c8ce5ac1de07053cf8517b27
1 parent
c7f6f921
钱包退款的实现
Showing
15 changed files
with
202 additions
and
29 deletions
yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/PayClientFactory.java
| ... | ... | @@ -27,6 +27,12 @@ public interface PayClientFactory { |
| 27 | 27 | <Config extends PayClientConfig> void createOrUpdatePayClient(Long channelId, String channelCode, |
| 28 | 28 | Config config); |
| 29 | 29 | |
| 30 | - <Config extends PayClientConfig> void createOrUpdateDelegatePayClient(Long channelId, DelegatePayClient<Config> delegatePayClient); | |
| 30 | + /** | |
| 31 | + * 新增或更新代理支付客户端 | |
| 32 | + * @param channelId 渠道编号 | |
| 33 | + * @param delegatePayClient 代理支付客户端 | |
| 34 | + * @param <Config> 支付配置 | |
| 35 | + */ | |
| 36 | + <Config extends PayClientConfig> void addOrUpdateDelegatePayClient(Long channelId, DelegatePayClient<Config> delegatePayClient); | |
| 31 | 37 | |
| 32 | 38 | } | ... | ... |
yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/PayClientFactoryImpl.java
| ... | ... | @@ -53,7 +53,7 @@ public class PayClientFactoryImpl implements PayClientFactory { |
| 53 | 53 | } |
| 54 | 54 | |
| 55 | 55 | @Override |
| 56 | - public <Config extends PayClientConfig> void createOrUpdateDelegatePayClient(Long channelId, DelegatePayClient<Config> delegatePayClient) { | |
| 56 | + public <Config extends PayClientConfig> void addOrUpdateDelegatePayClient(Long channelId, DelegatePayClient<Config> delegatePayClient) { | |
| 57 | 57 | clients.put(channelId, delegatePayClient); |
| 58 | 58 | } |
| 59 | 59 | ... | ... |
yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/delegate/DelegatePayClient.java
| ... | ... | @@ -10,6 +10,8 @@ import cn.iocoder.yudao.framework.pay.core.client.impl.AbstractPayClient; |
| 10 | 10 | import java.util.Map; |
| 11 | 11 | |
| 12 | 12 | /** |
| 13 | + * 代理支付 Client 的抽象类。用于支付 Client 由其它模块实现, 例如钱包支付 | |
| 14 | + * | |
| 13 | 15 | * @author jason |
| 14 | 16 | */ |
| 15 | 17 | public abstract class DelegatePayClient<Config extends PayClientConfig> extends AbstractPayClient<PayClientConfig> { | ... | ... |
yudao-module-pay/yudao-module-pay-api/src/main/java/cn/iocoder/yudao/module/pay/enums/ErrorCodeConstants.java
| ... | ... | @@ -44,6 +44,9 @@ public interface ErrorCodeConstants { |
| 44 | 44 | // ========== 钱包模块(退款) 1007007000 ========== |
| 45 | 45 | ErrorCode WALLET_NOT_FOUND = new ErrorCode(1007007000, "用户钱包不存在"); |
| 46 | 46 | ErrorCode WALLET_NOT_ENOUGH = new ErrorCode(1007007001, "钱包余额不足"); |
| 47 | + ErrorCode WALLET_TRANSACTION_NOT_FOUND = new ErrorCode(1007007002, "未找到对应的钱包交易"); | |
| 48 | + ErrorCode WALLET_REFUND_AMOUNT_ERROR = new ErrorCode(1007007003, "钱包退款金额不对"); | |
| 49 | + ErrorCode WALLET_REFUND_EXIST = new ErrorCode(1007007004, "已经存在钱包退款"); | |
| 47 | 50 | |
| 48 | 51 | // ========== 示例订单 1007900000 ========== |
| 49 | 52 | ErrorCode DEMO_ORDER_NOT_FOUND = new ErrorCode(1007900000, "示例订单不存在"); | ... | ... |
yudao-module-pay/yudao-module-pay-api/src/main/java/cn/iocoder/yudao/module/pay/enums/member/WalletBizTypeEnum.java
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/dal/mysql/refund/PayRefundMapper.java
| ... | ... | @@ -37,6 +37,11 @@ public interface PayRefundMapper extends BaseMapperX<PayRefundDO> { |
| 37 | 37 | .eq(PayRefundDO::getNo, no)); |
| 38 | 38 | } |
| 39 | 39 | |
| 40 | + default PayRefundDO selectByNo(String no){ | |
| 41 | + return selectOne(new LambdaQueryWrapperX<PayRefundDO>() | |
| 42 | + .eq(PayRefundDO::getNo, no)); | |
| 43 | + } | |
| 44 | + | |
| 40 | 45 | default int updateByIdAndStatus(Long id, Integer status, PayRefundDO update) { |
| 41 | 46 | return update(update, new LambdaQueryWrapper<PayRefundDO>() |
| 42 | 47 | .eq(PayRefundDO::getId, id).eq(PayRefundDO::getStatus, status)); |
| ... | ... | @@ -71,5 +76,4 @@ public interface PayRefundMapper extends BaseMapperX<PayRefundDO> { |
| 71 | 76 | default List<PayRefundDO> selectListByStatus(Integer status) { |
| 72 | 77 | return selectList(PayRefundDO::getStatus, status); |
| 73 | 78 | } |
| 74 | - | |
| 75 | 79 | } | ... | ... |
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/dal/mysql/wallet/PayWalletTransactionMapper.java
| ... | ... | @@ -26,6 +26,15 @@ public interface PayWalletTransactionMapper extends BaseMapperX<PayWalletTransac |
| 26 | 26 | query.orderByDesc(PayWalletTransactionDO::getTransactionTime); |
| 27 | 27 | return selectPage(pageParam, query); |
| 28 | 28 | } |
| 29 | + | |
| 30 | + default PayWalletTransactionDO selectByNo(String no) { | |
| 31 | + return selectOne(PayWalletTransactionDO::getNo, no); | |
| 32 | + } | |
| 33 | + | |
| 34 | + default PayWalletTransactionDO selectByWalletIdAndBiz(Long walletId, Long bizId, Integer bizType) { | |
| 35 | + return selectOne(PayWalletTransactionDO::getWalletId, walletId, PayWalletTransactionDO::getBizId, | |
| 36 | + bizId, PayWalletTransactionDO::getBizType, bizType); | |
| 37 | + } | |
| 29 | 38 | } |
| 30 | 39 | |
| 31 | 40 | ... | ... |
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/framework/pay/wallet/WalletPayClient.java
| 1 | 1 | package cn.iocoder.yudao.module.pay.framework.pay.wallet; |
| 2 | 2 | |
| 3 | -import cn.iocoder.yudao.framework.pay.core.client.impl.NonePayClientConfig; | |
| 3 | +import cn.iocoder.yudao.framework.common.exception.ServiceException; | |
| 4 | 4 | import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderRespDTO; |
| 5 | 5 | import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderUnifiedReqDTO; |
| 6 | 6 | import cn.iocoder.yudao.framework.pay.core.client.dto.refund.PayRefundRespDTO; |
| 7 | 7 | import cn.iocoder.yudao.framework.pay.core.client.dto.refund.PayRefundUnifiedReqDTO; |
| 8 | +import cn.iocoder.yudao.framework.pay.core.client.impl.NonePayClientConfig; | |
| 8 | 9 | import cn.iocoder.yudao.framework.pay.core.client.impl.delegate.DelegatePayClient; |
| 9 | 10 | import cn.iocoder.yudao.module.pay.dal.dataobject.wallet.PayWalletTransactionDO; |
| 10 | 11 | import cn.iocoder.yudao.module.pay.service.wallet.PayWalletService; |
| ... | ... | @@ -12,6 +13,8 @@ import lombok.extern.slf4j.Slf4j; |
| 12 | 13 | |
| 13 | 14 | import java.util.Map; |
| 14 | 15 | |
| 16 | +import static cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants.INTERNAL_SERVER_ERROR; | |
| 17 | + | |
| 15 | 18 | /** |
| 16 | 19 | * 钱包支付的 PayClient 实现类 |
| 17 | 20 | * |
| ... | ... | @@ -19,7 +22,6 @@ import java.util.Map; |
| 19 | 22 | */ |
| 20 | 23 | @Slf4j |
| 21 | 24 | public class WalletPayClient extends DelegatePayClient<NonePayClientConfig> { |
| 22 | - | |
| 23 | 25 | private PayWalletService payWalletService; |
| 24 | 26 | |
| 25 | 27 | public WalletPayClient(Long channelId, String channelCode, NonePayClientConfig config) { |
| ... | ... | @@ -38,13 +40,24 @@ public class WalletPayClient extends DelegatePayClient<NonePayClientConfig> { |
| 38 | 40 | |
| 39 | 41 | @Override |
| 40 | 42 | protected PayOrderRespDTO doUnifiedOrder(PayOrderUnifiedReqDTO reqDTO) { |
| 41 | - PayWalletTransactionDO payWalletTransaction = payWalletService.pay(reqDTO.getOutTradeNo(), reqDTO.getPrice()); | |
| 42 | - return PayOrderRespDTO.successOf(payWalletTransaction.getNo(), payWalletTransaction.getCreator(), | |
| 43 | - payWalletTransaction.getTransactionTime(), | |
| 44 | - reqDTO.getOutTradeNo(), ""); | |
| 43 | + try { | |
| 44 | + PayWalletTransactionDO payWalletTransaction = payWalletService.pay(reqDTO.getOutTradeNo(), reqDTO.getPrice()); | |
| 45 | + return PayOrderRespDTO.successOf(payWalletTransaction.getNo(), payWalletTransaction.getCreator(), | |
| 46 | + payWalletTransaction.getTransactionTime(), | |
| 47 | + reqDTO.getOutTradeNo(), "WALLET_PAY_SUCCESS"); | |
| 48 | + } catch (Throwable ex) { | |
| 49 | + log.error("[doUnifiedOrder] 失败", ex); | |
| 50 | + String errorCode = String.valueOf(INTERNAL_SERVER_ERROR); | |
| 51 | + String errorMsg = INTERNAL_SERVER_ERROR.getMsg(); | |
| 52 | + if (ex instanceof ServiceException) { | |
| 53 | + ServiceException serviceException = (ServiceException) ex; | |
| 54 | + errorCode = String.valueOf(serviceException.getCode()); | |
| 55 | + errorMsg = serviceException.getMessage(); | |
| 56 | + } | |
| 57 | + return PayOrderRespDTO.closedOf(errorCode, errorMsg, reqDTO.getOutTradeNo(), ex); | |
| 58 | + } | |
| 45 | 59 | } |
| 46 | 60 | |
| 47 | - | |
| 48 | 61 | @Override |
| 49 | 62 | protected PayOrderRespDTO doParseOrderNotify(Map<String, String> params, String body) { |
| 50 | 63 | throw new UnsupportedOperationException("钱包支付无支付回调"); |
| ... | ... | @@ -52,12 +65,27 @@ public class WalletPayClient extends DelegatePayClient<NonePayClientConfig> { |
| 52 | 65 | |
| 53 | 66 | @Override |
| 54 | 67 | protected PayOrderRespDTO doGetOrder(String outTradeNo) { |
| 55 | - return null; | |
| 68 | + throw new UnsupportedOperationException("待实现"); | |
| 56 | 69 | } |
| 57 | 70 | |
| 58 | 71 | @Override |
| 59 | 72 | protected PayRefundRespDTO doUnifiedRefund(PayRefundUnifiedReqDTO reqDTO) { |
| 60 | - return null; | |
| 73 | + try { | |
| 74 | + PayWalletTransactionDO payWalletTransaction = payWalletService.refund(reqDTO.getOutRefundNo(), | |
| 75 | + reqDTO.getRefundPrice(), reqDTO.getReason()); | |
| 76 | + return PayRefundRespDTO.successOf(payWalletTransaction.getNo(), payWalletTransaction.getTransactionTime(), | |
| 77 | + reqDTO.getOutRefundNo(), "WALLET_REFUND_SUCCESS"); | |
| 78 | + } catch (Throwable ex) { | |
| 79 | + log.error("[doUnifiedRefund] 失败", ex); | |
| 80 | + String errorCode = String.valueOf(INTERNAL_SERVER_ERROR); | |
| 81 | + String errorMsg = INTERNAL_SERVER_ERROR.getMsg(); | |
| 82 | + if (ex instanceof ServiceException) { | |
| 83 | + ServiceException serviceException = (ServiceException) ex; | |
| 84 | + errorCode = String.valueOf(serviceException.getCode()); | |
| 85 | + errorMsg = serviceException.getMessage(); | |
| 86 | + } | |
| 87 | + return PayRefundRespDTO.failureOf(errorCode, errorMsg, reqDTO.getOutRefundNo(), ex); | |
| 88 | + } | |
| 61 | 89 | } |
| 62 | 90 | |
| 63 | 91 | @Override |
| ... | ... | @@ -67,6 +95,6 @@ public class WalletPayClient extends DelegatePayClient<NonePayClientConfig> { |
| 67 | 95 | |
| 68 | 96 | @Override |
| 69 | 97 | protected PayRefundRespDTO doGetRefund(String outTradeNo, String outRefundNo) { |
| 70 | - return null; | |
| 98 | + throw new UnsupportedOperationException("待实现"); | |
| 71 | 99 | } |
| 72 | 100 | } | ... | ... |
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/channel/PayChannelServiceImpl.java
| ... | ... | @@ -81,6 +81,7 @@ public class PayChannelServiceImpl implements PayChannelService { |
| 81 | 81 | log.error("[支付模块 yudao-module-pay - 表结构未导入][参考 https://doc.iocoder.cn/pay/build/ 开启]"); |
| 82 | 82 | } |
| 83 | 83 | log.info("[initLocalCache][缓存支付渠道,数量为:{}]", channels.size()); |
| 84 | + // 钱包 client 需要和其它 client 分开了创建 | |
| 84 | 85 | List<PayChannelDO> walletChannels = new ArrayList<>(); |
| 85 | 86 | List<PayChannelDO> otherChannels = new ArrayList<>(); |
| 86 | 87 | channels.forEach(t -> { |
| ... | ... | @@ -97,7 +98,7 @@ public class PayChannelServiceImpl implements PayChannelService { |
| 97 | 98 | walletChannels.forEach(payChannel -> { |
| 98 | 99 | WalletPayClient walletPayClient = new WalletPayClient(payChannel.getId(), payChannel.getCode(), |
| 99 | 100 | (NonePayClientConfig) payChannel.getConfig(), payWalletService); |
| 100 | - payClientFactory.createOrUpdateDelegatePayClient(payChannel.getId(), walletPayClient); | |
| 101 | + payClientFactory.addOrUpdateDelegatePayClient(payChannel.getId(), walletPayClient); | |
| 101 | 102 | }); |
| 102 | 103 | this.channelCache = channels; |
| 103 | 104 | }); | ... | ... |
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/refund/PayRefundService.java
| ... | ... | @@ -25,6 +25,14 @@ public interface PayRefundService { |
| 25 | 25 | PayRefundDO getRefund(Long id); |
| 26 | 26 | |
| 27 | 27 | /** |
| 28 | + * 获得退款订单 | |
| 29 | + * | |
| 30 | + * @param no 外部退款单号 | |
| 31 | + * @return 退款订单 | |
| 32 | + */ | |
| 33 | + PayRefundDO getRefundByNo(String no); | |
| 34 | + | |
| 35 | + /** | |
| 28 | 36 | * 获得指定应用的退款数量 |
| 29 | 37 | * |
| 30 | 38 | * @param appId 应用编号 | ... | ... |
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/refund/PayRefundServiceImpl.java
| ... | ... | @@ -75,6 +75,11 @@ public class PayRefundServiceImpl implements PayRefundService { |
| 75 | 75 | } |
| 76 | 76 | |
| 77 | 77 | @Override |
| 78 | + public PayRefundDO getRefundByNo(String no) { | |
| 79 | + return refundMapper.selectByNo(no); | |
| 80 | + } | |
| 81 | + | |
| 82 | + @Override | |
| 78 | 83 | public Long getRefundCountByAppId(Long appId) { |
| 79 | 84 | return refundMapper.selectCountByAppId(appId); |
| 80 | 85 | } | ... | ... |
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/wallet/PayWalletService.java
| ... | ... | @@ -17,5 +17,18 @@ public interface PayWalletService { |
| 17 | 17 | */ |
| 18 | 18 | PayWalletDO getPayWallet(Long userId, Integer userType); |
| 19 | 19 | |
| 20 | + /** | |
| 21 | + * 钱包支付 | |
| 22 | + * @param outTradeNo 外部订单号 | |
| 23 | + * @param price 金额 | |
| 24 | + */ | |
| 20 | 25 | PayWalletTransactionDO pay(String outTradeNo, Integer price); |
| 26 | + | |
| 27 | + /** | |
| 28 | + * 钱包支付退款 | |
| 29 | + * @param outRefundNo 外部退款号 | |
| 30 | + * @param refundPrice 退款金额 | |
| 31 | + * @param reason 退款原因 | |
| 32 | + */ | |
| 33 | + PayWalletTransactionDO refund(String outRefundNo, Integer refundPrice, String reason); | |
| 21 | 34 | } | ... | ... |
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/wallet/PayWalletServiceImpl.java
| 1 | 1 | package cn.iocoder.yudao.module.pay.service.wallet; |
| 2 | 2 | |
| 3 | 3 | import cn.iocoder.yudao.module.pay.dal.dataobject.order.PayOrderExtensionDO; |
| 4 | +import cn.iocoder.yudao.module.pay.dal.dataobject.refund.PayRefundDO; | |
| 4 | 5 | import cn.iocoder.yudao.module.pay.dal.dataobject.wallet.PayWalletDO; |
| 5 | 6 | import cn.iocoder.yudao.module.pay.dal.dataobject.wallet.PayWalletTransactionDO; |
| 6 | 7 | import cn.iocoder.yudao.module.pay.dal.mysql.wallet.PayWalletMapper; |
| 7 | 8 | import cn.iocoder.yudao.module.pay.dal.redis.no.PayNoRedisDAO; |
| 8 | 9 | import cn.iocoder.yudao.module.pay.service.order.PayOrderService; |
| 10 | +import cn.iocoder.yudao.module.pay.service.refund.PayRefundService; | |
| 9 | 11 | import lombok.extern.slf4j.Slf4j; |
| 10 | 12 | import org.springframework.context.annotation.Lazy; |
| 11 | 13 | import org.springframework.stereotype.Service; |
| 12 | 14 | import org.springframework.transaction.annotation.Transactional; |
| 13 | 15 | |
| 14 | 16 | import javax.annotation.Resource; |
| 15 | - | |
| 16 | 17 | import java.time.LocalDateTime; |
| 17 | 18 | |
| 18 | 19 | import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; |
| ... | ... | @@ -20,6 +21,7 @@ import static cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils.getLogi |
| 20 | 21 | import static cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils.getLoginUserType; |
| 21 | 22 | import static cn.iocoder.yudao.module.pay.enums.ErrorCodeConstants.*; |
| 22 | 23 | import static cn.iocoder.yudao.module.pay.enums.member.WalletBizTypeEnum.PAYMENT; |
| 24 | +import static cn.iocoder.yudao.module.pay.enums.member.WalletBizTypeEnum.PAYMENT_REFUND; | |
| 23 | 25 | |
| 24 | 26 | /** |
| 25 | 27 | * 钱包 Service 实现类 |
| ... | ... | @@ -29,16 +31,20 @@ import static cn.iocoder.yudao.module.pay.enums.member.WalletBizTypeEnum.PAYMENT |
| 29 | 31 | @Service |
| 30 | 32 | @Slf4j |
| 31 | 33 | public class PayWalletServiceImpl implements PayWalletService { |
| 32 | - private static final String WALLET_CHANNEL_NO_PREFIX = "W"; | |
| 33 | - @Resource | |
| 34 | - @Lazy | |
| 35 | - private PayOrderService payOrderService; | |
| 34 | + private static final String WALLET_PAY_NO_PREFIX = "WP"; | |
| 35 | + private static final String WALLET_REFUND_NO_PREFIX = "WR"; | |
| 36 | 36 | @Resource |
| 37 | 37 | private PayWalletMapper payWalletMapper; |
| 38 | 38 | @Resource |
| 39 | 39 | private PayWalletTransactionService payWalletTransactionService; |
| 40 | 40 | @Resource |
| 41 | 41 | private PayNoRedisDAO noRedisDAO; |
| 42 | + @Resource | |
| 43 | + @Lazy | |
| 44 | + private PayOrderService payOrderService; | |
| 45 | + @Resource | |
| 46 | + @Lazy | |
| 47 | + private PayRefundService payRefundService; | |
| 42 | 48 | |
| 43 | 49 | @Override |
| 44 | 50 | public PayWalletDO getPayWallet(Long userId, Integer userType) { |
| ... | ... | @@ -53,13 +59,7 @@ public class PayWalletServiceImpl implements PayWalletService { |
| 53 | 59 | if (orderExtension == null) { |
| 54 | 60 | throw exception(ORDER_EXTENSION_NOT_FOUND); |
| 55 | 61 | } |
| 56 | - Long userId = getLoginUserId(); | |
| 57 | - Integer userType = getLoginUserType(); | |
| 58 | - PayWalletDO payWallet = getPayWallet(userId, userType); | |
| 59 | - if (payWallet == null) { | |
| 60 | - log.error("[pay] 用户 {} 钱包不存在", userId); | |
| 61 | - throw exception(WALLET_NOT_FOUND); | |
| 62 | - } | |
| 62 | + PayWalletDO payWallet = validatePayWallet(); | |
| 63 | 63 | // 判断余额是否足够 |
| 64 | 64 | int afterBalance = payWallet.getBalance() - price; |
| 65 | 65 | if(afterBalance < 0){ |
| ... | ... | @@ -70,12 +70,73 @@ public class PayWalletServiceImpl implements PayWalletService { |
| 70 | 70 | payWalletMapper.updateById(payWallet); |
| 71 | 71 | |
| 72 | 72 | // 生成钱包渠道流水号 |
| 73 | - String walletNo = noRedisDAO.generate(WALLET_CHANNEL_NO_PREFIX); | |
| 73 | + String walletNo = noRedisDAO.generate(WALLET_PAY_NO_PREFIX); | |
| 74 | 74 | PayWalletTransactionDO payWalletTransaction = new PayWalletTransactionDO().setWalletId(payWallet.getId()) |
| 75 | - .setNo(walletNo).setAmount(price).setBalance(afterBalance).setTransactionTime(LocalDateTime.now()) | |
| 75 | + .setNo(walletNo).setAmount(price * -1).setBalance(afterBalance).setTransactionTime(LocalDateTime.now()) | |
| 76 | 76 | .setBizId(orderExtension.getOrderId()).setBizType(PAYMENT.getBizType()); |
| 77 | 77 | payWalletTransactionService.addPayWalletTransaction(payWalletTransaction); |
| 78 | 78 | |
| 79 | 79 | return payWalletTransaction; |
| 80 | 80 | } |
| 81 | + | |
| 82 | + private PayWalletDO validatePayWallet() { | |
| 83 | + Long userId = getLoginUserId(); | |
| 84 | + Integer userType = getLoginUserType(); | |
| 85 | + PayWalletDO payWallet = getPayWallet(userId, userType); | |
| 86 | + if (payWallet == null) { | |
| 87 | + log.error("[validatePayWallet] 用户 {} 钱包不存在", userId); | |
| 88 | + throw exception(WALLET_NOT_FOUND); | |
| 89 | + } | |
| 90 | + return payWallet; | |
| 91 | + } | |
| 92 | + | |
| 93 | + @Override | |
| 94 | + @Transactional(rollbackFor = Exception.class) | |
| 95 | + public PayWalletTransactionDO refund(String outRefundNo, Integer refundPrice, String reason) { | |
| 96 | + // 判断退款单是否存在 | |
| 97 | + PayRefundDO payRefund = payRefundService.getRefundByNo(outRefundNo); | |
| 98 | + if (payRefund == null) { | |
| 99 | + throw exception(REFUND_NOT_FOUND); | |
| 100 | + } | |
| 101 | + PayWalletDO payWallet = validatePayWallet(); | |
| 102 | + // 校验是否可以退款 | |
| 103 | + validateWalletCanRefund(payRefund.getId(), payRefund.getChannelOrderNo(), payWallet.getId(), refundPrice); | |
| 104 | + | |
| 105 | + Integer afterBalance = payWallet.getBalance() + refundPrice; | |
| 106 | + payWallet.setBalance(afterBalance); | |
| 107 | + payWallet.setTotalExpense(payWallet.getTotalExpense() + refundPrice * -1L); | |
| 108 | + payWalletMapper.updateById(payWallet); | |
| 109 | + | |
| 110 | + String walletNo = noRedisDAO.generate(WALLET_REFUND_NO_PREFIX); | |
| 111 | + PayWalletTransactionDO newWalletTransaction = new PayWalletTransactionDO().setWalletId(payWallet.getId()) | |
| 112 | + .setNo(walletNo).setAmount(refundPrice).setBalance(afterBalance).setTransactionTime(LocalDateTime.now()) | |
| 113 | + .setBizId(payRefund.getId()).setBizType(PAYMENT_REFUND.getBizType()) | |
| 114 | + .setDescription(reason); | |
| 115 | + payWalletTransactionService.addPayWalletTransaction(newWalletTransaction); | |
| 116 | + | |
| 117 | + return newWalletTransaction; | |
| 118 | + } | |
| 119 | + | |
| 120 | + /** | |
| 121 | + * 校验是否能退款 | |
| 122 | + * @param refundId 支付退款单 id | |
| 123 | + * @param walletPayNo 钱包支付 no | |
| 124 | + * @param walletId 钱包 id | |
| 125 | + */ | |
| 126 | + private void validateWalletCanRefund(long refundId, String walletPayNo, long walletId, int refundPrice) { | |
| 127 | + // 查询钱包支付交易 | |
| 128 | + PayWalletTransactionDO payWalletTransaction = payWalletTransactionService.getPayWalletTransactionByNo(walletPayNo); | |
| 129 | + if (payWalletTransaction == null) { | |
| 130 | + throw exception(WALLET_TRANSACTION_NOT_FOUND); | |
| 131 | + } | |
| 132 | + // 原来的支付金额 | |
| 133 | + int amount = payWalletTransaction.getAmount() * -1; | |
| 134 | + if (refundPrice != amount) { | |
| 135 | + throw exception(WALLET_REFUND_AMOUNT_ERROR); | |
| 136 | + } | |
| 137 | + PayWalletTransactionDO refundTransaction = payWalletTransactionService.getPayWalletTransaction(walletId, refundId, PAYMENT_REFUND); | |
| 138 | + if (refundTransaction != null) { | |
| 139 | + throw exception(WALLET_REFUND_EXIST); | |
| 140 | + } | |
| 141 | + } | |
| 81 | 142 | } | ... | ... |
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/wallet/PayWalletTransactionService.java
| ... | ... | @@ -3,6 +3,7 @@ package cn.iocoder.yudao.module.pay.service.wallet; |
| 3 | 3 | import cn.iocoder.yudao.framework.common.pojo.PageResult; |
| 4 | 4 | import cn.iocoder.yudao.module.pay.controller.app.wallet.vo.AppPayWalletTransactionPageReqVO; |
| 5 | 5 | import cn.iocoder.yudao.module.pay.dal.dataobject.wallet.PayWalletTransactionDO; |
| 6 | +import cn.iocoder.yudao.module.pay.enums.member.WalletBizTypeEnum; | |
| 6 | 7 | |
| 7 | 8 | /** |
| 8 | 9 | * 钱包余额明细 Service 接口 |
| ... | ... | @@ -21,5 +22,25 @@ public interface PayWalletTransactionService { |
| 21 | 22 | PageResult<PayWalletTransactionDO> getWalletTransactionPage(Long userId, Integer userType, |
| 22 | 23 | AppPayWalletTransactionPageReqVO pageVO); |
| 23 | 24 | |
| 25 | + /** | |
| 26 | + * 新增钱包余额明细 | |
| 27 | + * @param payWalletTransaction 余额明细 | |
| 28 | + * @return id | |
| 29 | + */ | |
| 24 | 30 | Long addPayWalletTransaction(PayWalletTransactionDO payWalletTransaction); |
| 31 | + | |
| 32 | + /** | |
| 33 | + * 获取钱包余额明细 根据 no | |
| 34 | + * @param no 流水号 | |
| 35 | + */ | |
| 36 | + PayWalletTransactionDO getPayWalletTransactionByNo(String no); | |
| 37 | + | |
| 38 | + /** | |
| 39 | + * 获取钱包 | |
| 40 | + * @param walletId 钱包 id | |
| 41 | + * @param bizId 业务编号 | |
| 42 | + * @param typeEnum 业务类型 | |
| 43 | + * @return 钱包余额明细 | |
| 44 | + */ | |
| 45 | + PayWalletTransactionDO getPayWalletTransaction(Long walletId, Long bizId, WalletBizTypeEnum typeEnum); | |
| 25 | 46 | } | ... | ... |
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/wallet/PayWalletTransactionServiceImpl.java
| ... | ... | @@ -5,6 +5,7 @@ import cn.iocoder.yudao.module.pay.controller.app.wallet.vo.AppPayWalletTransact |
| 5 | 5 | import cn.iocoder.yudao.module.pay.dal.dataobject.wallet.PayWalletDO; |
| 6 | 6 | import cn.iocoder.yudao.module.pay.dal.dataobject.wallet.PayWalletTransactionDO; |
| 7 | 7 | import cn.iocoder.yudao.module.pay.dal.mysql.wallet.PayWalletTransactionMapper; |
| 8 | +import cn.iocoder.yudao.module.pay.enums.member.WalletBizTypeEnum; | |
| 8 | 9 | import cn.iocoder.yudao.module.pay.enums.member.WalletTransactionQueryTypeEnum; |
| 9 | 10 | import lombok.extern.slf4j.Slf4j; |
| 10 | 11 | import org.springframework.stereotype.Service; |
| ... | ... | @@ -45,5 +46,15 @@ public class PayWalletTransactionServiceImpl implements PayWalletTransactionServ |
| 45 | 46 | return payWalletTransaction.getId(); |
| 46 | 47 | } |
| 47 | 48 | |
| 49 | + @Override | |
| 50 | + public PayWalletTransactionDO getPayWalletTransactionByNo(String no) { | |
| 51 | + return payWalletTransactionMapper.selectByNo(no); | |
| 52 | + } | |
| 53 | + | |
| 54 | + @Override | |
| 55 | + public PayWalletTransactionDO getPayWalletTransaction(Long walletId, Long bizId, WalletBizTypeEnum typeEnum) { | |
| 56 | + return payWalletTransactionMapper.selectByWalletIdAndBiz(walletId, bizId, typeEnum.getBizType()); | |
| 57 | + } | |
| 58 | + | |
| 48 | 59 | |
| 49 | 60 | } | ... | ... |