Commit 9e99b043754c736f810fef7e9991e12113af773e

Authored by YunaiV
2 parents 826d792f 437185cb

Merge remote-tracking branch 'origin/feature/mall_product' into feature/mall_product

Showing 20 changed files with 426 additions and 24 deletions
yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/PayClient.java
... ... @@ -4,6 +4,8 @@ import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderRespDTO;
4 4 import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderUnifiedReqDTO;
5 5 import cn.iocoder.yudao.framework.pay.core.client.dto.refund.PayRefundRespDTO;
6 6 import cn.iocoder.yudao.framework.pay.core.client.dto.refund.PayRefundUnifiedReqDTO;
  7 +import cn.iocoder.yudao.framework.pay.core.client.dto.transfer.PayTransferRespDTO;
  8 +import cn.iocoder.yudao.framework.pay.core.client.dto.transfer.PayTransferUnifiedReqDTO;
7 9  
8 10 import java.util.Map;
9 11  
... ... @@ -76,4 +78,12 @@ public interface PayClient {
76 78 */
77 79 PayRefundRespDTO getRefund(String outTradeNo, String outRefundNo);
78 80  
  81 + /**
  82 + * 调用渠道,进行转账
  83 + *
  84 + * @param reqDTO 统一转账请求信息
  85 + * @return 转账信息
  86 + */
  87 + PayTransferRespDTO unifiedTransfer(PayTransferUnifiedReqDTO reqDTO);
  88 +
79 89 }
... ...
yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/dto/transfer/PayTransferRespDTO.java 0 → 100644
  1 +package cn.iocoder.yudao.framework.pay.core.client.dto.transfer;
  2 +
  3 +import cn.iocoder.yudao.framework.pay.core.enums.transfer.PayTransferStatusRespEnum;
  4 +import lombok.Data;
  5 +
  6 +import java.time.LocalDateTime;
  7 +
  8 +/**
  9 + * 统一转账 Response DTO
  10 + *
  11 + * @author jason
  12 + */
  13 +@Data
  14 +public class PayTransferRespDTO {
  15 +
  16 + /**
  17 + * 转账状态
  18 + *
  19 + * 关联 {@link PayTransferStatusRespEnum#getStatus()}
  20 + */
  21 + private Integer status;
  22 +
  23 + /**
  24 + * 外部转账单号
  25 + *
  26 + */
  27 + private String outTransferNo;
  28 +
  29 + /**
  30 + * 支付渠道编号
  31 + */
  32 + private String channelOrderNo;
  33 +
  34 + /**
  35 + * 支付成功时间
  36 + */
  37 + private LocalDateTime successTime;
  38 +
  39 + /**
  40 + * 原始的返回结果
  41 + */
  42 + private Object rawData;
  43 +
  44 + /**
  45 + * 调用渠道的错误码
  46 + */
  47 + private String channelErrorCode;
  48 + /**
  49 + * 调用渠道报错时,错误信息
  50 + */
  51 + private String channelErrorMsg;
  52 +
  53 + /**
  54 + * 创建【WAITING】状态的转账返回
  55 + */
  56 + public static PayTransferRespDTO waitingOf(String channelOrderNo,
  57 + String outTransferNo, Object rawData) {
  58 + PayTransferRespDTO respDTO = new PayTransferRespDTO();
  59 + respDTO.status = PayTransferStatusRespEnum.WAITING.getStatus();
  60 + respDTO.channelOrderNo = channelOrderNo;
  61 + respDTO.outTransferNo = outTransferNo;
  62 + respDTO.rawData = rawData;
  63 + return respDTO;
  64 + }
  65 +
  66 + /**
  67 + * 创建【FAILURE】状态的转账返回
  68 + */
  69 + public static PayTransferRespDTO failureOf(String channelErrorCode, String channelErrorMsg,
  70 + String outTransferNo, Object rawData) {
  71 + PayTransferRespDTO respDTO = new PayTransferRespDTO();
  72 + respDTO.status = PayTransferStatusRespEnum.FAILURE.getStatus();
  73 + respDTO.channelErrorCode = channelErrorCode;
  74 + respDTO.channelErrorMsg = channelErrorMsg;
  75 + // 相对通用的字段
  76 + respDTO.outTransferNo = outTransferNo;
  77 + respDTO.rawData = rawData;
  78 + return respDTO;
  79 + }
  80 +
  81 + /**
  82 + * 创建【SUCCESS】状态的转账返回
  83 + */
  84 + public static PayTransferRespDTO successOf(String channelTransferNo, LocalDateTime successTime,
  85 + String outTransferNo, Object rawData) {
  86 + PayTransferRespDTO respDTO = new PayTransferRespDTO();
  87 + respDTO.status = PayTransferStatusRespEnum.SUCCESS.getStatus();
  88 + respDTO.channelOrderNo = channelTransferNo;
  89 + respDTO.successTime = successTime;
  90 + // 相对通用的字段
  91 + respDTO.outTransferNo = outTransferNo;
  92 + respDTO.rawData = rawData;
  93 + return respDTO;
  94 + }
  95 +
  96 +}
... ...
yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/dto/transfer/PayTransferUnifiedReqDTO.java 0 → 100644
  1 +package cn.iocoder.yudao.framework.pay.core.client.dto.transfer;
  2 +
  3 +import cn.iocoder.yudao.framework.common.validation.InEnum;
  4 +import cn.iocoder.yudao.framework.pay.core.enums.transfer.PayTransferTypeEnum;
  5 +import lombok.Data;
  6 +import org.hibernate.validator.constraints.Length;
  7 +
  8 +import javax.validation.constraints.Min;
  9 +import javax.validation.constraints.NotEmpty;
  10 +import javax.validation.constraints.NotNull;
  11 +import java.util.Map;
  12 +
  13 +/**
  14 + * 统一转账 Request DTO
  15 + *
  16 + * @author jason
  17 + */
  18 +@Data
  19 +public class PayTransferUnifiedReqDTO {
  20 +
  21 + /**
  22 + * 转账类型
  23 + *
  24 + * 关联 {@link PayTransferTypeEnum#getType()}
  25 + */
  26 + @NotNull(message = "转账类型不能为空")
  27 + @InEnum(PayTransferTypeEnum.class)
  28 + private Integer type;
  29 +
  30 + /**
  31 + * 用户 IP
  32 + */
  33 + @NotEmpty(message = "用户 IP 不能为空")
  34 + private String userIp;
  35 +
  36 + @NotEmpty(message = "外部转账单编号不能为空")
  37 + private String outTransferNo;
  38 +
  39 + /**
  40 + * 转账金额,单位:分
  41 + */
  42 + @NotNull(message = "转账金额不能为空")
  43 + @Min(value = 1, message = "转账金额必须大于零")
  44 + private Integer price;
  45 +
  46 + /**
  47 + * 转账标题
  48 + */
  49 + @NotEmpty(message = "转账标题不能为空")
  50 + @Length(max = 128, message = "转账标题不能超过 128")
  51 + private String title;
  52 +
  53 + /**
  54 + * 收款方信息,转账类型不同,收款方信息不同
  55 + */
  56 + @NotEmpty(message = "收款方信息 不能为空")
  57 + private Map<String, String> payeeInfo;
  58 +
  59 + /**
  60 + * 支付渠道的额外参数
  61 + *
  62 + */
  63 + private Map<String, String> channelExtras;
  64 +
  65 +}
... ...
yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/AbstractPayClient.java
... ... @@ -8,6 +8,8 @@ import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderRespDTO;
8 8 import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderUnifiedReqDTO;
9 9 import cn.iocoder.yudao.framework.pay.core.client.dto.refund.PayRefundRespDTO;
10 10 import cn.iocoder.yudao.framework.pay.core.client.dto.refund.PayRefundUnifiedReqDTO;
  11 +import cn.iocoder.yudao.framework.pay.core.client.dto.transfer.PayTransferRespDTO;
  12 +import cn.iocoder.yudao.framework.pay.core.client.dto.transfer.PayTransferUnifiedReqDTO;
11 13 import cn.iocoder.yudao.framework.pay.core.client.exception.PayException;
12 14 import lombok.extern.slf4j.Slf4j;
13 15  
... ... @@ -181,6 +183,26 @@ public abstract class AbstractPayClient&lt;Config extends PayClientConfig&gt; implemen
181 183 protected abstract PayRefundRespDTO doGetRefund(String outTradeNo, String outRefundNo)
182 184 throws Throwable;
183 185  
  186 + @Override
  187 + public final PayTransferRespDTO unifiedTransfer(PayTransferUnifiedReqDTO reqDTO) {
  188 + ValidationUtils.validate(reqDTO);
  189 + PayTransferRespDTO resp;
  190 + try{
  191 + resp = doUnifiedTransfer(reqDTO);
  192 + }catch (ServiceException ex) { // 业务异常,都是实现类已经翻译,所以直接抛出即可
  193 + throw ex;
  194 + } catch (Throwable ex) {
  195 + // 系统异常,则包装成 PayException 异常抛出
  196 + log.error("[unifiedTransfer][客户端({}) request({}) 发起转账异常]",
  197 + getId(), toJsonString(reqDTO), ex);
  198 + throw buildPayException(ex);
  199 + }
  200 + return resp;
  201 + }
  202 +
  203 + protected abstract PayTransferRespDTO doUnifiedTransfer(PayTransferUnifiedReqDTO reqDTO)
  204 + throws Throwable;
  205 +
184 206 // ========== 各种工具方法 ==========
185 207  
186 208 private PayException buildPayException(Throwable ex) {
... ...
yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/PayClientFactoryImpl.java
... ... @@ -50,6 +50,8 @@ public class PayClientFactoryImpl implements PayClientFactory {
50 50 clientClass.put(ALIPAY_APP, AlipayAppPayClient.class);
51 51 clientClass.put(ALIPAY_PC, AlipayPcPayClient.class);
52 52 clientClass.put(ALIPAY_BAR, AlipayBarPayClient.class);
  53 + // 支付包转账客户端
  54 + clientClass.put(ALIPAY_TRANSFER, AlipayTransferClient.class);
53 55 // Mock 支付客户端
54 56 clientClass.put(MOCK, MockPayClient.class);
55 57 }
... ...
yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/alipay/AbstractAlipayPayClient.java
... ... @@ -150,6 +150,10 @@ public abstract class AbstractAlipayPayClient extends AbstractPayClient&lt;AlipayPa
150 150 // 2.1 执行请求
151 151 AlipayTradeRefundResponse response = client.execute(request);
152 152 if (!response.isSuccess()) {
  153 + // 当出现 ACQ.SYSTEM_ERROR, 退款可能成功也可能失败。 返回 WAIT 状态. 后续 job 会轮询
  154 + if (ObjectUtils.equalsAny(response.getSubCode(), "ACQ.SYSTEM_ERROR", "SYSTEM_ERROR")) {
  155 + return PayRefundRespDTO.waitingOf(null, reqDTO.getOutRefundNo(), response);
  156 + }
153 157 return PayRefundRespDTO.failureOf(response.getSubCode(), response.getSubMsg(), reqDTO.getOutRefundNo(), response);
154 158 }
155 159 // 2.2 创建返回结果
... ... @@ -198,6 +202,8 @@ public abstract class AbstractAlipayPayClient extends AbstractPayClient&lt;AlipayPa
198 202 return PayRefundRespDTO.waitingOf(null, outRefundNo, response);
199 203 }
200 204  
  205 +
  206 +
201 207 // ========== 各种工具方法 ==========
202 208  
203 209 protected String formatAmount(Integer amount) {
... ...
yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/alipay/AlipayAppPayClient.java
... ... @@ -2,6 +2,8 @@ package cn.iocoder.yudao.framework.pay.core.client.impl.alipay;
2 2  
3 3 import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderRespDTO;
4 4 import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderUnifiedReqDTO;
  5 +import cn.iocoder.yudao.framework.pay.core.client.dto.transfer.PayTransferRespDTO;
  6 +import cn.iocoder.yudao.framework.pay.core.client.dto.transfer.PayTransferUnifiedReqDTO;
5 7 import cn.iocoder.yudao.framework.pay.core.enums.channel.PayChannelEnum;
6 8 import cn.iocoder.yudao.framework.pay.core.enums.order.PayOrderDisplayModeEnum;
7 9 import com.alipay.api.AlipayApiException;
... ... @@ -57,4 +59,8 @@ public class AlipayAppPayClient extends AbstractAlipayPayClient {
57 59 reqDTO.getOutTradeNo(), response);
58 60 }
59 61  
  62 + @Override
  63 + protected PayTransferRespDTO doUnifiedTransfer(PayTransferUnifiedReqDTO reqDTO) {
  64 + throw new UnsupportedOperationException("支付宝【App 支付】不支持转账操作");
  65 + }
60 66 }
... ...
yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/alipay/AlipayBarPayClient.java
... ... @@ -5,6 +5,8 @@ import cn.hutool.core.map.MapUtil;
5 5 import cn.hutool.core.util.StrUtil;
6 6 import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderRespDTO;
7 7 import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderUnifiedReqDTO;
  8 +import cn.iocoder.yudao.framework.pay.core.client.dto.transfer.PayTransferRespDTO;
  9 +import cn.iocoder.yudao.framework.pay.core.client.dto.transfer.PayTransferUnifiedReqDTO;
8 10 import cn.iocoder.yudao.framework.pay.core.enums.channel.PayChannelEnum;
9 11 import cn.iocoder.yudao.framework.pay.core.enums.order.PayOrderDisplayModeEnum;
10 12 import com.alipay.api.AlipayApiException;
... ... @@ -75,4 +77,8 @@ public class AlipayBarPayClient extends AbstractAlipayPayClient {
75 77 reqDTO.getOutTradeNo(), response);
76 78 }
77 79  
  80 + @Override
  81 + protected PayTransferRespDTO doUnifiedTransfer(PayTransferUnifiedReqDTO reqDTO) {
  82 + throw new UnsupportedOperationException("支付宝【条码支付】不支持转账操作");
  83 + }
78 84 }
... ...
yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/alipay/AlipayPcPayClient.java
... ... @@ -4,6 +4,8 @@ import cn.hutool.core.util.ObjectUtil;
4 4 import cn.hutool.http.Method;
5 5 import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderRespDTO;
6 6 import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderUnifiedReqDTO;
  7 +import cn.iocoder.yudao.framework.pay.core.client.dto.transfer.PayTransferRespDTO;
  8 +import cn.iocoder.yudao.framework.pay.core.client.dto.transfer.PayTransferUnifiedReqDTO;
7 9 import cn.iocoder.yudao.framework.pay.core.enums.channel.PayChannelEnum;
8 10 import cn.iocoder.yudao.framework.pay.core.enums.order.PayOrderDisplayModeEnum;
9 11 import com.alipay.api.AlipayApiException;
... ... @@ -67,4 +69,8 @@ public class AlipayPcPayClient extends AbstractAlipayPayClient {
67 69 reqDTO.getOutTradeNo(), response);
68 70 }
69 71  
  72 + @Override
  73 + protected PayTransferRespDTO doUnifiedTransfer(PayTransferUnifiedReqDTO reqDTO) {
  74 + throw new UnsupportedOperationException("支付宝【PC 网站】不支持转账操作");
  75 + }
70 76 }
... ...
yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/alipay/AlipayQrPayClient.java
... ... @@ -2,6 +2,8 @@ package cn.iocoder.yudao.framework.pay.core.client.impl.alipay;
2 2  
3 3 import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderRespDTO;
4 4 import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderUnifiedReqDTO;
  5 +import cn.iocoder.yudao.framework.pay.core.client.dto.transfer.PayTransferRespDTO;
  6 +import cn.iocoder.yudao.framework.pay.core.client.dto.transfer.PayTransferUnifiedReqDTO;
5 7 import cn.iocoder.yudao.framework.pay.core.enums.channel.PayChannelEnum;
6 8 import cn.iocoder.yudao.framework.pay.core.enums.order.PayOrderDisplayModeEnum;
7 9 import com.alipay.api.AlipayApiException;
... ... @@ -54,4 +56,8 @@ public class AlipayQrPayClient extends AbstractAlipayPayClient {
54 56 reqDTO.getOutTradeNo(), response);
55 57 }
56 58  
  59 + @Override
  60 + protected PayTransferRespDTO doUnifiedTransfer(PayTransferUnifiedReqDTO reqDTO) {
  61 + throw new UnsupportedOperationException("支付宝【扫码支付】不支持转账操作");
  62 + }
57 63 }
... ...
yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/alipay/AlipayTransferClient.java 0 → 100644
  1 +package cn.iocoder.yudao.framework.pay.core.client.impl.alipay;
  2 +
  3 +import cn.hutool.core.map.MapUtil;
  4 +import cn.hutool.core.util.StrUtil;
  5 +import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
  6 +import cn.iocoder.yudao.framework.common.util.object.ObjectUtils;
  7 +import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderRespDTO;
  8 +import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderUnifiedReqDTO;
  9 +import cn.iocoder.yudao.framework.pay.core.client.dto.refund.PayRefundRespDTO;
  10 +import cn.iocoder.yudao.framework.pay.core.client.dto.refund.PayRefundUnifiedReqDTO;
  11 +import cn.iocoder.yudao.framework.pay.core.client.dto.transfer.PayTransferRespDTO;
  12 +import cn.iocoder.yudao.framework.pay.core.client.dto.transfer.PayTransferUnifiedReqDTO;
  13 +import cn.iocoder.yudao.framework.pay.core.enums.channel.PayChannelEnum;
  14 +import cn.iocoder.yudao.framework.pay.core.enums.transfer.PayTransferTypeEnum;
  15 +import com.alipay.api.AlipayApiException;
  16 +import com.alipay.api.domain.AlipayFundTransUniTransferModel;
  17 +import com.alipay.api.domain.Participant;
  18 +import com.alipay.api.request.AlipayFundTransUniTransferRequest;
  19 +import com.alipay.api.response.AlipayFundTransUniTransferResponse;
  20 +import lombok.extern.slf4j.Slf4j;
  21 +
  22 +import static cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants.BAD_REQUEST;
  23 +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception0;
  24 +
  25 +/**
  26 + * 支付宝转账的 PayClient 实现类
  27 + *
  28 + * @author jason
  29 + */
  30 +@Slf4j
  31 +public class AlipayTransferClient extends AbstractAlipayPayClient {
  32 + public AlipayTransferClient(Long channelId, AlipayPayClientConfig config) {
  33 + super(channelId, PayChannelEnum.ALIPAY_TRANSFER.getCode(), config);
  34 + }
  35 + @Override
  36 + protected PayOrderRespDTO doUnifiedOrder(PayOrderUnifiedReqDTO reqDTO) {
  37 + throw new UnsupportedOperationException("支付宝转账不支持统一下单请求");
  38 + }
  39 + @Override
  40 + protected PayRefundRespDTO doUnifiedRefund(PayRefundUnifiedReqDTO reqDTO) {
  41 + throw new UnsupportedOperationException("支付宝转账不支持统一退款请求");
  42 + }
  43 + @Override
  44 + protected PayTransferRespDTO doUnifiedTransfer(PayTransferUnifiedReqDTO reqDTO) throws AlipayApiException {
  45 + // 1.1 构建 AlipayFundTransUniTransferModel
  46 + AlipayFundTransUniTransferModel model = new AlipayFundTransUniTransferModel();
  47 + // ① 通用的参数
  48 + model.setTransAmount(formatAmount(reqDTO.getPrice())); // 转账金额
  49 + model.setOrderTitle(reqDTO.getTitle()); // 转账业务的标题,用于在支付宝用户的账单里显示。
  50 + model.setOutBizNo(reqDTO.getOutTransferNo());
  51 + model.setProductCode("TRANS_ACCOUNT_NO_PWD"); // 销售产品码。单笔无密转账固定为 TRANS_ACCOUNT_NO_PWD
  52 + model.setBizScene("DIRECT_TRANSFER"); // 业务场景 单笔无密转账固定为 DIRECT_TRANSFER。
  53 + model.setBusinessParams(JsonUtils.toJsonString(reqDTO.getChannelExtras()));
  54 + PayTransferTypeEnum transferType = PayTransferTypeEnum.valueOf(reqDTO.getType());
  55 + switch(transferType){
  56 + case WX_BALANCE :
  57 + case WALLET_BALANCE : {
  58 + log.error("[doUnifiedTransfer],支付宝转账不支持的转账类型{}", transferType);
  59 + throw new UnsupportedOperationException(String.format("支付宝转账不支持转账类型: %s",transferType.getName()));
  60 + }
  61 + case ALIPAY_BALANCE : {
  62 + // ② 个性化的参数
  63 + Participant payeeInfo = new Participant();
  64 + payeeInfo.setIdentityType("ALIPAY_LOGON_ID");
  65 + String logonId = MapUtil.getStr(reqDTO.getPayeeInfo(), "ALIPAY_LOGON_ID");
  66 + if (StrUtil.isEmpty(logonId)) {
  67 + throw exception0(BAD_REQUEST.getCode(), "支付包登录 ID 不能为空");
  68 + }
  69 + String accountName = MapUtil.getStr(reqDTO.getPayeeInfo(), "ALIPAY_ACCOUNT_NAME");
  70 + if (StrUtil.isEmpty(accountName)) {
  71 + throw exception0(BAD_REQUEST.getCode(), "支付包账户名称不能为空");
  72 + }
  73 + payeeInfo.setIdentity(logonId); // 支付宝登录号
  74 + payeeInfo.setName(accountName); // 支付宝账号姓名
  75 + model.setPayeeInfo(payeeInfo);
  76 + // 1.2 构建 AlipayFundTransUniTransferRequest
  77 + AlipayFundTransUniTransferRequest request = new AlipayFundTransUniTransferRequest();
  78 + request.setBizModel(model);
  79 + // 执行请求
  80 + AlipayFundTransUniTransferResponse response = client.certificateExecute(request);
  81 + // 处理结果
  82 + if (!response.isSuccess()) {
  83 + // 当出现 SYSTEM_ERROR, 转账可能成功也可能失败。 返回 WAIT 状态. 后续 job 会轮询
  84 + if (ObjectUtils.equalsAny(response.getSubCode(), "SYSTEM_ERROR", "ACQ.SYSTEM_ERROR")) {
  85 + return PayTransferRespDTO.waitingOf(null, reqDTO.getOutTransferNo(), response);
  86 + }
  87 + return PayTransferRespDTO.failureOf(response.getSubCode(), response.getSubMsg(),
  88 + reqDTO.getOutTransferNo(), response);
  89 + }
  90 + return PayTransferRespDTO.successOf(response.getOrderId(), parseTime(response.getTransDate()),
  91 + response.getOutBizNo(), response);
  92 + }
  93 + case BANK_CARD : {
  94 + Participant payeeInfo = new Participant();
  95 + payeeInfo.setIdentityType("BANKCARD_ACCOUNT");
  96 + throw new UnsupportedOperationException("待实现");
  97 + }
  98 + default: {
  99 + throw new IllegalStateException("不正确的转账类型: " + transferType);
  100 + }
  101 + }
  102 + }
  103 +}
... ...
yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/alipay/AlipayWapPayClient.java
... ... @@ -3,6 +3,8 @@ package cn.iocoder.yudao.framework.pay.core.client.impl.alipay;
3 3 import cn.hutool.http.Method;
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 +import cn.iocoder.yudao.framework.pay.core.client.dto.transfer.PayTransferRespDTO;
  7 +import cn.iocoder.yudao.framework.pay.core.client.dto.transfer.PayTransferUnifiedReqDTO;
6 8 import cn.iocoder.yudao.framework.pay.core.enums.channel.PayChannelEnum;
7 9 import cn.iocoder.yudao.framework.pay.core.enums.order.PayOrderDisplayModeEnum;
8 10 import com.alipay.api.AlipayApiException;
... ... @@ -56,4 +58,8 @@ public class AlipayWapPayClient extends AbstractAlipayPayClient {
56 58 reqDTO.getOutTradeNo(), response);
57 59 }
58 60  
  61 + @Override
  62 + public PayTransferRespDTO doUnifiedTransfer(PayTransferUnifiedReqDTO reqDTO) {
  63 + throw new UnsupportedOperationException("支付宝【Wap 网站】不支持转账操作");
  64 + }
59 65 }
... ...
yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/mock/MockPayClient.java
... ... @@ -4,6 +4,8 @@ import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderRespDTO;
4 4 import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderUnifiedReqDTO;
5 5 import cn.iocoder.yudao.framework.pay.core.client.dto.refund.PayRefundRespDTO;
6 6 import cn.iocoder.yudao.framework.pay.core.client.dto.refund.PayRefundUnifiedReqDTO;
  7 +import cn.iocoder.yudao.framework.pay.core.client.dto.transfer.PayTransferRespDTO;
  8 +import cn.iocoder.yudao.framework.pay.core.client.dto.transfer.PayTransferUnifiedReqDTO;
7 9 import cn.iocoder.yudao.framework.pay.core.client.impl.AbstractPayClient;
8 10 import cn.iocoder.yudao.framework.pay.core.client.impl.NonePayClientConfig;
9 11 import cn.iocoder.yudao.framework.pay.core.enums.channel.PayChannelEnum;
... ... @@ -64,4 +66,8 @@ public class MockPayClient extends AbstractPayClient&lt;NonePayClientConfig&gt; {
64 66 throw new UnsupportedOperationException("模拟支付无支付回调");
65 67 }
66 68  
  69 + @Override
  70 + protected PayTransferRespDTO doUnifiedTransfer(PayTransferUnifiedReqDTO reqDTO) {
  71 + throw new UnsupportedOperationException("待实现");
  72 + }
67 73 }
... ...
yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/weixin/AbstractWxPayClient.java
... ... @@ -12,6 +12,8 @@ import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderRespDTO;
12 12 import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderUnifiedReqDTO;
13 13 import cn.iocoder.yudao.framework.pay.core.client.dto.refund.PayRefundRespDTO;
14 14 import cn.iocoder.yudao.framework.pay.core.client.dto.refund.PayRefundUnifiedReqDTO;
  15 +import cn.iocoder.yudao.framework.pay.core.client.dto.transfer.PayTransferRespDTO;
  16 +import cn.iocoder.yudao.framework.pay.core.client.dto.transfer.PayTransferUnifiedReqDTO;
15 17 import cn.iocoder.yudao.framework.pay.core.client.impl.AbstractPayClient;
16 18 import cn.iocoder.yudao.framework.pay.core.enums.order.PayOrderStatusRespEnum;
17 19 import com.github.binarywang.wxpay.bean.notify.WxPayOrderNotifyResult;
... ... @@ -425,6 +427,10 @@ public abstract class AbstractWxPayClient extends AbstractPayClient&lt;WxPayClientC
425 427 }
426 428 }
427 429  
  430 + @Override
  431 + protected PayTransferRespDTO doUnifiedTransfer(PayTransferUnifiedReqDTO reqDTO) {
  432 + throw new UnsupportedOperationException("待实现");
  433 + }
428 434 // ========== 各种工具方法 ==========
429 435  
430 436 static String formatDateV2(LocalDateTime time) {
... ...
yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/enums/channel/PayChannelEnum.java
... ... @@ -28,6 +28,7 @@ public enum PayChannelEnum {
28 28 ALIPAY_APP("alipay_app", "支付宝App 支付", AlipayPayClientConfig.class),
29 29 ALIPAY_QR("alipay_qr", "支付宝扫码支付", AlipayPayClientConfig.class),
30 30 ALIPAY_BAR("alipay_bar", "支付宝条码支付", AlipayPayClientConfig.class),
  31 + ALIPAY_TRANSFER("alipay_transfer", "支付宝转账", AlipayPayClientConfig.class),
31 32  
32 33 MOCK("mock", "模拟支付", NonePayClientConfig.class),
33 34  
... ...
yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/enums/transfer/PayTransferStatusRespEnum.java 0 → 100644
  1 +package cn.iocoder.yudao.framework.pay.core.enums.transfer;
  2 +
  3 +import lombok.AllArgsConstructor;
  4 +import lombok.Getter;
  5 +
  6 +/**
  7 + * 渠道的转账状态枚举
  8 + *
  9 + * @author jason
  10 + */
  11 +@Getter
  12 +@AllArgsConstructor
  13 +public enum PayTransferStatusRespEnum {
  14 +
  15 + WAITING(0, "等待转账"),
  16 + SUCCESS(10, "转账成功"),
  17 + FAILURE(20, "转账失败");
  18 +
  19 + private final Integer status;
  20 + private final String name;
  21 +}
... ...
yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/enums/transfer/PayTransferTypeEnum.java 0 → 100644
  1 +package cn.iocoder.yudao.framework.pay.core.enums.transfer;
  2 +
  3 +import cn.hutool.core.util.ArrayUtil;
  4 +import cn.iocoder.yudao.framework.common.core.IntArrayValuable;
  5 +import lombok.AllArgsConstructor;
  6 +import lombok.Getter;
  7 +
  8 +import java.util.Arrays;
  9 +
  10 +/**
  11 + * 转账类型枚举
  12 + *
  13 + * @author jason
  14 + */
  15 +@AllArgsConstructor
  16 +@Getter
  17 +public enum PayTransferTypeEnum implements IntArrayValuable {
  18 + ALIPAY_BALANCE(1, "支付宝余额"),
  19 + WX_BALANCE(2, "微信余额"),
  20 + BANK_CARD(3, "银行卡"),
  21 + WALLET_BALANCE(4, "钱包余额");
  22 +
  23 + private final Integer type;
  24 + private final String name;
  25 +
  26 + public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(PayTransferTypeEnum::getType).toArray();
  27 +
  28 + @Override
  29 + public int[] array() {
  30 + return ARRAYS;
  31 + }
  32 +
  33 + public static PayTransferTypeEnum valueOf(Integer type) {
  34 + return ArrayUtil.firstMatch(item -> item.getType().equals(type), values());
  35 + }
  36 +}
... ...
yudao-module-pay/yudao-module-pay-api/src/main/java/cn/iocoder/yudao/module/pay/enums/ErrorCodeConstants.java
... ... @@ -44,23 +44,21 @@ public interface ErrorCodeConstants {
44 44 ErrorCode WALLET_NOT_FOUND = new ErrorCode(1007007000, "用户钱包不存在");
45 45 ErrorCode WALLET_BALANCE_NOT_ENOUGH = new ErrorCode(1007007001, "钱包余额不足");
46 46 ErrorCode WALLET_TRANSACTION_NOT_FOUND = new ErrorCode(1007007002, "未找到对应的钱包交易");
47   - ErrorCode WALLET_REFUND_AMOUNT_ERROR = new ErrorCode(1007007003, "钱包退款金额不对");
48   - ErrorCode WALLET_REFUND_EXIST = new ErrorCode(1007007004, "已经存在钱包退款");
49   - ErrorCode WALLET_FREEZE_PRICE_NOT_ENOUGH = new ErrorCode(1007007005, "钱包冻结余额不足");
  47 + ErrorCode WALLET_REFUND_EXIST = new ErrorCode(1007007003, "已经存在钱包退款");
  48 + ErrorCode WALLET_FREEZE_PRICE_NOT_ENOUGH = new ErrorCode(1007007004, "钱包冻结余额不足");
50 49  
51   - // TODO @jason:把钱包充值,单独搞个错误码段哈;
52   -
53   - ErrorCode WALLET_RECHARGE_NOT_FOUND = new ErrorCode(1007007005, "钱包充值记录不存在");
54   - ErrorCode WALLET_RECHARGE_UPDATE_PAID_STATUS_NOT_UNPAID = new ErrorCode(1007007006, "钱包充值更新支付状态失败,钱包充值记录不是【未支付】状态");
55   - ErrorCode WALLET_RECHARGE_UPDATE_PAID_PAY_ORDER_ID_ERROR = new ErrorCode(1007007007, "钱包充值更新支付状态失败,支付单编号不匹配");
56   - ErrorCode WALLET_RECHARGE_UPDATE_PAID_PAY_ORDER_STATUS_NOT_SUCCESS = new ErrorCode(1007007008, "钱包充值更新支付状态失败,支付单状态不是【支付成功】状态");
57   - ErrorCode WALLET_RECHARGE_UPDATE_PAID_PAY_PRICE_NOT_MATCH = new ErrorCode(1007007009, "钱包充值更新支付状态失败,支付单金额不匹配");
58   - ErrorCode WALLET_RECHARGE_REFUND_FAIL_NOT_PAID = new ErrorCode(1007900010, "钱包发起退款失败,钱包充值订单未支付");
59   - ErrorCode WALLET_RECHARGE_REFUND_FAIL_REFUNDED = new ErrorCode(1007900011, "钱包发起退款失败,钱包充值订单已退款");
60   - ErrorCode WALLET_RECHARGE_REFUND_BALANCE_NOT_ENOUGH = new ErrorCode(1007900012, "钱包发起退款失败,钱包余额不足");
61   - ErrorCode WALLET_RECHARGE_REFUND_FAIL_REFUND_ORDER_ID_ERROR = new ErrorCode(1007900013, "钱包退款更新失败,钱包退款单编号不匹配");
62   - ErrorCode WALLET_RECHARGE_REFUND_FAIL_REFUND_NOT_FOUND = new ErrorCode(1007900014, "钱包退款更新失败,退款订单不存在");
63   - ErrorCode WALLET_RECHARGE_REFUND_FAIL_REFUND_PRICE_NOT_MATCH = new ErrorCode(1007900015, "钱包退款更新失败,退款单金额不匹配");
  50 + // ========== 钱包充值模块 1007008000 ==========
  51 + ErrorCode WALLET_RECHARGE_NOT_FOUND = new ErrorCode(1007008000, "钱包充值记录不存在");
  52 + ErrorCode WALLET_RECHARGE_UPDATE_PAID_STATUS_NOT_UNPAID = new ErrorCode(1007008001, "钱包充值更新支付状态失败,钱包充值记录不是【未支付】状态");
  53 + ErrorCode WALLET_RECHARGE_UPDATE_PAID_PAY_ORDER_ID_ERROR = new ErrorCode(1007008002, "钱包充值更新支付状态失败,支付单编号不匹配");
  54 + ErrorCode WALLET_RECHARGE_UPDATE_PAID_PAY_ORDER_STATUS_NOT_SUCCESS = new ErrorCode(1007008003, "钱包充值更新支付状态失败,支付单状态不是【支付成功】状态");
  55 + ErrorCode WALLET_RECHARGE_UPDATE_PAID_PAY_PRICE_NOT_MATCH = new ErrorCode(1007008004, "钱包充值更新支付状态失败,支付单金额不匹配");
  56 + ErrorCode WALLET_RECHARGE_REFUND_FAIL_NOT_PAID = new ErrorCode(1007008005, "钱包发起退款失败,钱包充值订单未支付");
  57 + ErrorCode WALLET_RECHARGE_REFUND_FAIL_REFUNDED = new ErrorCode(1007008006, "钱包发起退款失败,钱包充值订单已退款");
  58 + ErrorCode WALLET_RECHARGE_REFUND_BALANCE_NOT_ENOUGH = new ErrorCode(1007008007, "钱包发起退款失败,钱包余额不足");
  59 + ErrorCode WALLET_RECHARGE_REFUND_FAIL_REFUND_ORDER_ID_ERROR = new ErrorCode(1007008008, "钱包退款更新失败,钱包退款单编号不匹配");
  60 + ErrorCode WALLET_RECHARGE_REFUND_FAIL_REFUND_NOT_FOUND = new ErrorCode(1007008009, "钱包退款更新失败,退款订单不存在");
  61 + ErrorCode WALLET_RECHARGE_REFUND_FAIL_REFUND_PRICE_NOT_MATCH = new ErrorCode(1007008010, "钱包退款更新失败,退款单金额不匹配");
64 62 // ========== 示例订单 1007900000 ==========
65 63 ErrorCode DEMO_ORDER_NOT_FOUND = new ErrorCode(1007900000, "示例订单不存在");
66 64 ErrorCode DEMO_ORDER_UPDATE_PAID_STATUS_NOT_UNPAID = new ErrorCode(1007900001, "示例订单更新支付状态失败,订单不是【未支付】状态");
... ...
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/framework/pay/wallet/WalletPayClient.java
... ... @@ -8,6 +8,8 @@ import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderRespDTO;
8 8 import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderUnifiedReqDTO;
9 9 import cn.iocoder.yudao.framework.pay.core.client.dto.refund.PayRefundRespDTO;
10 10 import cn.iocoder.yudao.framework.pay.core.client.dto.refund.PayRefundUnifiedReqDTO;
  11 +import cn.iocoder.yudao.framework.pay.core.client.dto.transfer.PayTransferRespDTO;
  12 +import cn.iocoder.yudao.framework.pay.core.client.dto.transfer.PayTransferUnifiedReqDTO;
11 13 import cn.iocoder.yudao.framework.pay.core.client.impl.AbstractPayClient;
12 14 import cn.iocoder.yudao.framework.pay.core.client.impl.NonePayClientConfig;
13 15 import cn.iocoder.yudao.framework.pay.core.enums.channel.PayChannelEnum;
... ... @@ -174,4 +176,8 @@ public class WalletPayClient extends AbstractPayClient&lt;NonePayClientConfig&gt; {
174 176 throw new IllegalStateException(String.format("支付退款单[%s] 状态不正确", outRefundNo));
175 177 }
176 178  
  179 + @Override
  180 + public PayTransferRespDTO doUnifiedTransfer(PayTransferUnifiedReqDTO reqDTO) {
  181 + throw new UnsupportedOperationException("待实现");
  182 + }
177 183 }
... ...
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/wallet/PayWalletServiceImpl.java
... ... @@ -82,7 +82,7 @@ public class PayWalletServiceImpl implements PayWalletService {
82 82 throw exception(REFUND_NOT_FOUND);
83 83 }
84 84 // 1.2 校验是否可以退款
85   - Long walletId = validateWalletCanRefund(payRefund.getId(), payRefund.getChannelOrderNo(), refundPrice);
  85 + Long walletId = validateWalletCanRefund(payRefund.getId(), payRefund.getChannelOrderNo());
86 86 PayWalletDO wallet = walletMapper.selectById(walletId);
87 87 Assert.notNull(wallet, "钱包 {} 不存在", walletId);
88 88  
... ... @@ -96,18 +96,12 @@ public class PayWalletServiceImpl implements PayWalletService {
96 96 * @param refundId 支付退款单 id
97 97 * @param walletPayNo 钱包支付 no
98 98 */
99   - private Long validateWalletCanRefund(Long refundId, String walletPayNo, Integer refundPrice) {
  99 + private Long validateWalletCanRefund(Long refundId, String walletPayNo) {
100 100 // 1. 校验钱包支付交易存在
101 101 PayWalletTransactionDO walletTransaction = walletTransactionService.getWalletTransactionByNo(walletPayNo);
102 102 if (walletTransaction == null) {
103 103 throw exception(WALLET_TRANSACTION_NOT_FOUND);
104 104 }
105   - // 原来的支付金额
106   - // TODO @jason:应该允许多次退款哈;
107   - int amount = - walletTransaction.getPrice();
108   - if (refundPrice != amount) {
109   - throw exception(WALLET_REFUND_AMOUNT_ERROR);
110   - }
111 105 PayWalletTransactionDO refundTransaction = walletTransactionService.getWalletTransaction(
112 106 String.valueOf(refundId), PAYMENT_REFUND);
113 107 if (refundTransaction != null) {
... ...