Commit 1e7f22cde02aab86bcbe08a483601c7c7077d3dd

Authored by YunaiV
1 parent ffee1895

feat:【PAY 支付】完善支付转账示例,以及转账功能(支付宝)

Showing 30 changed files with 251 additions and 605 deletions
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/brokerage/BrokerageWithdrawServiceImpl.java
... ... @@ -11,7 +11,7 @@ import cn.iocoder.yudao.module.pay.api.transfer.dto.PayTransferRespDTO;
11 11 import cn.iocoder.yudao.module.pay.api.wallet.PayWalletApi;
12 12 import cn.iocoder.yudao.module.pay.api.wallet.dto.PayWalletAddBalanceReqDTO;
13 13 import cn.iocoder.yudao.module.pay.enums.transfer.PayTransferStatusEnum;
14   -import cn.iocoder.yudao.module.pay.enums.transfer.PayTransferTypeEnum;
  14 +//import cn.iocoder.yudao.module.pay.enums.transfer.PayTransferTypeEnum;
15 15 import cn.iocoder.yudao.module.pay.enums.wallet.PayWalletBizTypeEnum;
16 16 import cn.iocoder.yudao.module.system.api.notify.NotifyMessageSendApi;
17 17 import cn.iocoder.yudao.module.system.api.social.SocialUserApi;
... ... @@ -140,11 +140,11 @@ public class BrokerageWithdrawServiceImpl implements BrokerageWithdrawService {
140 140 // 1.2 构建请求
141 141 PayTransferCreateReqDTO payTransferCreateReqDTO = new PayTransferCreateReqDTO()
142 142 .setAppKey(tradeOrderProperties.getPayAppKey())
143   - .setChannelCode("wx_lite").setType(PayTransferTypeEnum.WX_BALANCE.getType())
  143 + .setChannelCode("wx_lite") // TODO @芋艿:【转账】这里要处理下;
144 144 .setMerchantTransferId(withdraw.getId().toString())
145 145 .setPrice(withdraw.getPrice())
146 146 .setSubject("佣金提现")
147   - .setOpenid(socialUser.getOpenid()).setUserIp(getClientIP());
  147 + .setUserAccount(socialUser.getOpenid()).setUserIp(getClientIP());
148 148 // 2. 发起请求
149 149 return payTransferApi.createTransfer(payTransferCreateReqDTO);
150 150 }
... ... @@ -226,7 +226,7 @@ public class BrokerageWithdrawServiceImpl implements BrokerageWithdrawService {
226 226 if (PayTransferStatusEnum.isSuccess(transfer.getStatus())) {
227 227 withdraw.setStatus(BrokerageWithdrawStatusEnum.WITHDRAW_SUCCESS.getStatus());
228 228 // TODO @luchi:发送站内信
229   - } else if (PayTransferStatusEnum.isPendingStatus(transfer.getStatus())) {
  229 + } else if (PayTransferStatusEnum.isWaitingOrProcessing(transfer.getStatus())) {
230 230 // TODO @luchi:这里,是不是不用更新哈?
231 231 withdraw.setStatus(BrokerageWithdrawStatusEnum.AUDIT_SUCCESS.getStatus());
232 232 } else {
... ...
yudao-module-pay/yudao-module-pay-api/src/main/java/cn/iocoder/yudao/module/pay/api/transfer/dto/PayTransferCreateReqDTO.java
1 1 package cn.iocoder.yudao.module.pay.api.transfer.dto;
2 2  
3   -import cn.iocoder.yudao.framework.common.validation.InEnum;
4   -import cn.iocoder.yudao.module.pay.enums.transfer.PayTransferTypeEnum;
5 3 import jakarta.validation.constraints.Min;
6   -import jakarta.validation.constraints.NotBlank;
7 4 import jakarta.validation.constraints.NotEmpty;
8 5 import jakarta.validation.constraints.NotNull;
9 6 import lombok.Data;
... ... @@ -24,6 +21,9 @@ public class PayTransferCreateReqDTO {
24 21 @NotNull(message = "应用标识不能为空")
25 22 private String appKey;
26 23  
  24 + /**
  25 + * 转账渠道
  26 + */
27 27 @NotEmpty(message = "转账渠道不能为空")
28 28 private String channelCode;
29 29  
... ... @@ -32,16 +32,11 @@ public class PayTransferCreateReqDTO {
32 32 */
33 33 private Map<String, String> channelExtras;
34 34  
35   - @NotEmpty(message = "用户 IP 不能为空")
36   - private String userIp;
37   -
38 35 /**
39   - * 类型
  36 + * 用户 IP
40 37 */
41   - @NotNull(message = "转账类型不能为空")
42   - @InEnum(PayTransferTypeEnum.class)
43   - private Integer type;
44   -
  38 + @NotEmpty(message = "用户 IP 不能为空")
  39 + private String userIp;
45 40  
46 41 /**
47 42 * 商户转账单编号
... ... @@ -63,15 +58,16 @@ public class PayTransferCreateReqDTO {
63 58 private String subject;
64 59  
65 60 /**
  61 + * 收款人账号
  62 + *
  63 + * 微信场景下:openid
  64 + * 支付宝场景下:支付宝账号
  65 + */
  66 + @NotEmpty(message = "收款人账号不能为空")
  67 + private String userAccount;
  68 + /**
66 69 * 收款人姓名
67 70 */
68   - @NotBlank(message = "收款人姓名不能为空", groups = {PayTransferTypeEnum.Alipay.class})
69 71 private String userName;
70 72  
71   - @NotBlank(message = "支付宝登录号不能为空", groups = {PayTransferTypeEnum.Alipay.class})
72   - private String alipayLogonId;
73   -
74   - // ========== 微信转账相关字段 ==========
75   - @NotBlank(message = "微信 openId 不能为空", groups = {PayTransferTypeEnum.WxPay.class})
76   - private String openid;
77 73 }
... ...
yudao-module-pay/yudao-module-pay-api/src/main/java/cn/iocoder/yudao/module/pay/enums/ErrorCodeConstants.java
... ... @@ -65,13 +65,12 @@ public interface ErrorCodeConstants {
65 65 ErrorCode WALLET_RECHARGE_PACKAGE_NAME_EXISTS = new ErrorCode(1_007_008_013, "钱包充值套餐名称已存在");
66 66  
67 67 // ========== 转账模块 1-007-009-000 ==========
68   - ErrorCode PAY_TRANSFER_SUBMIT_CHANNEL_ERROR = new ErrorCode(1_007_009_000, "发起转账报错,错误码:{},错误提示:{}");
69 68 ErrorCode PAY_TRANSFER_NOT_FOUND = new ErrorCode(1_007_009_001, "转账单不存在");
70   - ErrorCode PAY_SAME_MERCHANT_TRANSFER_TYPE_NOT_MATCH = new ErrorCode(1_007_009_002, "两次相同转账请求的类型不匹配");
71   - ErrorCode PAY_SAME_MERCHANT_TRANSFER_PRICE_NOT_MATCH = new ErrorCode(1_007_009_003, "两次相同转账请求的金额不匹配");
72   - ErrorCode PAY_MERCHANT_TRANSFER_EXISTS = new ErrorCode(1_007_009_004, "该笔业务的转账已经发起,请查询转账订单相关状态");
  69 + ErrorCode PAY_TRANSFER_CREATE_CHANNEL_NOT_MATCH = new ErrorCode(1_007_009_002, "转账发起失败,原因:两次相同转账请求的类型不匹配");
  70 + ErrorCode PAY_TRANSFER_CREATE_PRICE_NOT_MATCH = new ErrorCode(1_007_009_003, "转账发起失败,原因:两次相同转账请求的金额不匹配");
  71 + ErrorCode PAY_TRANSFER_CREATE_MERCHANT_EXISTS = new ErrorCode(1_007_009_004, "转账发起失败,原因:该笔业务的转账已经发起,请查询转账订单相关状态");
73 72 ErrorCode PAY_TRANSFER_STATUS_IS_NOT_WAITING = new ErrorCode(1_007_009_005, "转账单不处于待转账");
74   - ErrorCode PAY_TRANSFER_STATUS_IS_NOT_PENDING = new ErrorCode(1_007_009_006, "转账单不处于待转账或转账中");
  73 + ErrorCode PAY_TRANSFER_STATUS_IS_NOT_WAITING_OR_PROCESSING = new ErrorCode(1_007_009_006, "转账单不处于待转账或转账中");
75 74  
76 75 // ========== 示例订单 1-007-900-000 ==========
77 76 ErrorCode DEMO_ORDER_NOT_FOUND = new ErrorCode(1_007_900_000, "示例订单不存在");
... ...
yudao-module-pay/yudao-module-pay-api/src/main/java/cn/iocoder/yudao/module/pay/enums/transfer/PayTransferStatusEnum.java
... ... @@ -6,6 +6,8 @@ import lombok.Getter;
6 6 import java.util.Objects;
7 7  
8 8 /**
  9 + * 渠道的转账状态枚举
  10 + *
9 11 * @author jason
10 12 */
11 13 @Getter
... ... @@ -13,16 +15,9 @@ import java.util.Objects;
13 15 public enum PayTransferStatusEnum {
14 16  
15 17 WAITING(0, "等待转账"),
16   - /**
17   - * TODO 转账到银行卡. 会有T+0 T+1 到账的请情况。 还未实现
18   - */
19   - IN_PROGRESS(10, "转账进行中"),
20   -
21   - SUCCESS(20, "转账成功"),
22   - /**
23   - * 转账关闭 (失败,或者其它情况) // TODO 改成 转账失败状态
24   - */
25   - CLOSED(30, "转账关闭");
  18 + PROCESSING(5, "转账进行中"),
  19 + SUCCESS(10, "转账成功"),
  20 + CLOSED(20, "转账关闭");
26 21  
27 22 /**
28 23 * 状态
... ... @@ -44,16 +39,18 @@ public enum PayTransferStatusEnum {
44 39 public static boolean isWaiting(Integer status) {
45 40 return Objects.equals(status, WAITING.getStatus());
46 41 }
47   - public static boolean isInProgress(Integer status) {
48   - return Objects.equals(status, IN_PROGRESS.getStatus());
  42 +
  43 + public static boolean isProgressing(Integer status) {
  44 + return Objects.equals(status, PROCESSING.getStatus());
49 45 }
50 46  
51 47 /**
52 48 * 是否处于待转账或者转账中的状态
  49 + *
53 50 * @param status 状态
54 51 */
55   - public static boolean isPendingStatus(Integer status) {
56   - return Objects.equals(status, WAITING.getStatus()) || Objects.equals(status, IN_PROGRESS.getStatus());
  52 + public static boolean isWaitingOrProcessing(Integer status) {
  53 + return isWaiting(status) || isProgressing(status);
57 54 }
58 55  
59 56 }
... ...
yudao-module-pay/yudao-module-pay-api/src/main/java/cn/iocoder/yudao/module/pay/enums/transfer/PayTransferTypeEnum.java deleted
1   -package cn.iocoder.yudao.module.pay.enums.transfer;
2   -
3   -import cn.hutool.core.util.ArrayUtil;
4   -import cn.iocoder.yudao.framework.common.core.ArrayValuable;
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 ArrayValuable<Integer> {
18   -
19   - ALIPAY_BALANCE(1, "支付宝余额"),
20   - WX_BALANCE(2, "微信余额"),
21   - BANK_CARD(3, "银行卡"),
22   - WALLET_BALANCE(4, "钱包余额");
23   -
24   - public interface WxPay {
25   - }
26   -
27   - public interface Alipay {
28   - }
29   -
30   - private final Integer type;
31   - private final String name;
32   -
33   - public static final Integer[] ARRAYS = Arrays.stream(values()).map(PayTransferTypeEnum::getType).toArray(Integer[]::new);
34   -
35   - @Override
36   - public Integer[] array() {
37   - return ARRAYS;
38   - }
39   -
40   - public static PayTransferTypeEnum typeOf(Integer type) {
41   - return ArrayUtil.firstMatch(item -> item.getType().equals(type), values());
42   - }
43   -
44   -}
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/controller/admin/demo/PayDemoOrderController.java
... ... @@ -3,22 +3,21 @@ package cn.iocoder.yudao.module.pay.controller.admin.demo;
3 3 import cn.iocoder.yudao.framework.common.pojo.CommonResult;
4 4 import cn.iocoder.yudao.framework.common.pojo.PageParam;
5 5 import cn.iocoder.yudao.framework.common.pojo.PageResult;
  6 +import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
6 7 import cn.iocoder.yudao.module.pay.api.notify.dto.PayOrderNotifyReqDTO;
7 8 import cn.iocoder.yudao.module.pay.api.notify.dto.PayRefundNotifyReqDTO;
8 9 import cn.iocoder.yudao.module.pay.controller.admin.demo.vo.order.PayDemoOrderCreateReqVO;
9 10 import cn.iocoder.yudao.module.pay.controller.admin.demo.vo.order.PayDemoOrderRespVO;
10   -import cn.iocoder.yudao.module.pay.convert.demo.PayDemoOrderConvert;
11 11 import cn.iocoder.yudao.module.pay.dal.dataobject.demo.PayDemoOrderDO;
12 12 import cn.iocoder.yudao.module.pay.service.demo.PayDemoOrderService;
13 13 import io.swagger.v3.oas.annotations.Operation;
14 14 import io.swagger.v3.oas.annotations.Parameter;
15 15 import io.swagger.v3.oas.annotations.tags.Tag;
16   -import org.springframework.validation.annotation.Validated;
17   -import org.springframework.web.bind.annotation.*;
18   -
19 16 import jakarta.annotation.Resource;
20 17 import jakarta.annotation.security.PermitAll;
21 18 import jakarta.validation.Valid;
  19 +import org.springframework.validation.annotation.Validated;
  20 +import org.springframework.web.bind.annotation.*;
22 21  
23 22 import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
24 23 import static cn.iocoder.yudao.framework.common.util.servlet.ServletUtils.getClientIP;
... ... @@ -43,7 +42,7 @@ public class PayDemoOrderController {
43 42 @Operation(summary = "获得示例订单分页")
44 43 public CommonResult<PageResult<PayDemoOrderRespVO>> getDemoOrderPage(@Valid PageParam pageVO) {
45 44 PageResult<PayDemoOrderDO> pageResult = payDemoOrderService.getDemoOrderPage(pageVO);
46   - return success(PayDemoOrderConvert.INSTANCE.convertPage(pageResult));
  45 + return success(BeanUtils.toBean(pageResult, PayDemoOrderRespVO.class));
47 46 }
48 47  
49 48 @PostMapping("/update-paid")
... ...
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/controller/admin/demo/PayDemoTransferController.http 0 → 100644
  1 +### 请求 /pay/pay/demo-order 接口 => 成功
  2 +POST {{baseUrl}}/pay/demo-transfer/create
  3 +Authorization: Bearer {{token}}
  4 +Content-Type: application/json
  5 +tenant-id: {{adminTenantId}}
  6 +
  7 +{
  8 + "channelCode": "alipay_pc",
  9 + "subject": "测试转账",
  10 + "price": 10,
  11 + "userAccount": "oespxk7368@sandbox.com",
  12 + "userName": "oespxk7368"
  13 +}
0 14 \ No newline at end of file
... ...
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/controller/admin/demo/PayDemoTransferController.java
... ... @@ -3,20 +3,19 @@ package cn.iocoder.yudao.module.pay.controller.admin.demo;
3 3 import cn.iocoder.yudao.framework.common.pojo.CommonResult;
4 4 import cn.iocoder.yudao.framework.common.pojo.PageParam;
5 5 import cn.iocoder.yudao.framework.common.pojo.PageResult;
  6 +import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
6 7 import cn.iocoder.yudao.module.pay.api.notify.dto.PayTransferNotifyReqDTO;
7 8 import cn.iocoder.yudao.module.pay.controller.admin.demo.vo.transfer.PayDemoTransferCreateReqVO;
8 9 import cn.iocoder.yudao.module.pay.controller.admin.demo.vo.transfer.PayDemoTransferRespVO;
9   -import cn.iocoder.yudao.module.pay.convert.demo.PayDemoTransferConvert;
10 10 import cn.iocoder.yudao.module.pay.dal.dataobject.demo.PayDemoTransferDO;
11 11 import cn.iocoder.yudao.module.pay.service.demo.PayDemoTransferService;
12 12 import io.swagger.v3.oas.annotations.Operation;
13 13 import io.swagger.v3.oas.annotations.tags.Tag;
14   -import org.springframework.validation.annotation.Validated;
15   -import org.springframework.web.bind.annotation.*;
16   -
17 14 import jakarta.annotation.Resource;
18 15 import jakarta.annotation.security.PermitAll;
19 16 import jakarta.validation.Valid;
  17 +import org.springframework.validation.annotation.Validated;
  18 +import org.springframework.web.bind.annotation.*;
20 19  
21 20 import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
22 21  
... ... @@ -31,14 +30,15 @@ public class PayDemoTransferController {
31 30 @PostMapping("/create")
32 31 @Operation(summary = "创建示例转账订单")
33 32 public CommonResult<Long> createDemoTransfer(@Valid @RequestBody PayDemoTransferCreateReqVO createReqVO) {
34   - return success(demoTransferService.createDemoTransfer(createReqVO));
  33 + Long id = demoTransferService.createDemoTransfer(createReqVO);
  34 + return success(id);
35 35 }
36 36  
37 37 @GetMapping("/page")
38 38 @Operation(summary = "获得示例转账订单分页")
39 39 public CommonResult<PageResult<PayDemoTransferRespVO>> getDemoTransferPage(@Valid PageParam pageVO) {
40 40 PageResult<PayDemoTransferDO> pageResult = demoTransferService.getDemoTransferPage(pageVO);
41   - return success(PayDemoTransferConvert.INSTANCE.convertPage(pageResult));
  41 + return success(BeanUtils.toBean(pageResult, PayDemoTransferRespVO.class));
42 42 }
43 43  
44 44 @PostMapping("/update-status")
... ... @@ -49,4 +49,5 @@ public class PayDemoTransferController {
49 49 notifyReqDTO.getPayTransferId());
50 50 return success(true);
51 51 }
  52 +
52 53 }
... ...
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/controller/admin/demo/vo/transfer/PayDemoTransferCreateReqVO.java
1 1 package cn.iocoder.yudao.module.pay.controller.admin.demo.vo.transfer;
2 2  
3   -import cn.iocoder.yudao.framework.common.util.validation.ValidationUtils;
4   -import cn.iocoder.yudao.framework.common.validation.InEnum;
5   -import cn.iocoder.yudao.framework.pay.core.enums.transfer.PayTransferTypeEnum;
6 3 import io.swagger.v3.oas.annotations.media.Schema;
7   -import lombok.Data;
8   -
9   -import jakarta.validation.Validator;
10 4 import jakarta.validation.constraints.Min;
11 5 import jakarta.validation.constraints.NotBlank;
  6 +import jakarta.validation.constraints.NotEmpty;
12 7 import jakarta.validation.constraints.NotNull;
  8 +import lombok.Data;
13 9  
14   -import static cn.iocoder.yudao.module.pay.enums.transfer.PayTransferTypeEnum.*;
15   -
16   -/**
17   - * @author jason
18   - */
19 10 @Schema(description = "管理后台 - 示例转账单创建 Request VO")
20 11 @Data
21 12 public class PayDemoTransferCreateReqVO {
22 13  
23   - @Schema(description = "转账类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
24   - @NotNull(message = "转账类型不能为空")
25   - @InEnum(PayTransferTypeEnum.class)
26   - private Integer type;
  14 + @Schema(description = "转账渠道", requiredMode = Schema.RequiredMode.REQUIRED, example = "wx_lite")
  15 + @NotEmpty(message = "转账渠道不能为空")
  16 + private String channelCode;
  17 +
  18 + @Schema(description = "转账标题", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋艿是一种菜")
  19 + @NotEmpty(message = "转账标题不能为空")
  20 + private String subject;
27 21  
28   - @Schema(description = "转账金额", requiredMode = Schema.RequiredMode.REQUIRED, example = "100")
  22 + @Schema(description = "转账金额,单位:分", requiredMode = Schema.RequiredMode.REQUIRED, example = "100")
29 23 @NotNull(message = "转账金额不能为空")
30 24 @Min(value = 1, message = "转账金额必须大于零")
31 25 private Integer price;
32 26  
  27 + @Schema(description = "收款人账号", requiredMode= Schema.RequiredMode.REQUIRED, example = "test1")
  28 + @NotBlank(message = "收款人账号不能为空")
  29 + private String userAccount;
  30 +
33 31 @Schema(description = "收款人姓名", example = "test1")
34   - @NotBlank(message = "收款人姓名不能为空", groups = {Alipay.class})
  32 + @NotBlank(message = "收款人姓名不能为空")
35 33 private String userName;
36 34  
37   - // ========== 支付宝转账相关字段 ==========
38   - @Schema(description = "支付宝登录号,支持邮箱和手机号格式", example = "test1@@sandbox.com")
39   - @NotBlank(message = "支付宝登录号不能为空", groups = {Alipay.class})
40   - private String alipayLogonId;
41   -
42   - // ========== 微信转账相关字段 ==========
43   - @Schema(description = "微信 openId", example = "oLefc4g5Gxx")
44   - @NotBlank(message = "微信 openId 不能为空", groups = {WxPay.class})
45   - private String openid;
46   -
47   - // ========== 转账到银行卡和钱包相关字段 待补充 ==========
48   -
49   - public void validate(Validator validator) {
50   - PayTransferTypeEnum transferType = PayTransferTypeEnum.typeOf(type);
51   - switch (transferType) {
52   - case ALIPAY_BALANCE: {
53   - ValidationUtils.validate(validator, this, Alipay.class);
54   - break;
55   - }
56   - case WX_BALANCE: {
57   - ValidationUtils.validate(validator, this, WxPay.class);
58   - break;
59   - }
60   - default: {
61   - throw new UnsupportedOperationException("待实现");
62   - }
63   - }
64   - }
65   -
66 35 }
... ...
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/controller/admin/transfer/PayTransferController.java
... ... @@ -2,21 +2,24 @@ package cn.iocoder.yudao.module.pay.controller.admin.transfer;
2 2  
3 3 import cn.iocoder.yudao.framework.common.pojo.CommonResult;
4 4 import cn.iocoder.yudao.framework.common.pojo.PageResult;
5   -import cn.iocoder.yudao.module.pay.controller.admin.transfer.vo.*;
6   -import cn.iocoder.yudao.module.pay.convert.transfer.PayTransferConvert;
  5 +import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
  6 +import cn.iocoder.yudao.module.pay.controller.admin.transfer.vo.PayTransferPageItemRespVO;
  7 +import cn.iocoder.yudao.module.pay.controller.admin.transfer.vo.PayTransferPageReqVO;
  8 +import cn.iocoder.yudao.module.pay.controller.admin.transfer.vo.PayTransferRespVO;
7 9 import cn.iocoder.yudao.module.pay.dal.dataobject.transfer.PayTransferDO;
8 10 import cn.iocoder.yudao.module.pay.service.transfer.PayTransferService;
9 11 import io.swagger.v3.oas.annotations.Operation;
10 12 import io.swagger.v3.oas.annotations.tags.Tag;
11   -import org.springframework.security.access.prepost.PreAuthorize;
12   -import org.springframework.validation.annotation.Validated;
13   -import org.springframework.web.bind.annotation.*;
14   -
15 13 import jakarta.annotation.Resource;
16 14 import jakarta.validation.Valid;
  15 +import org.springframework.security.access.prepost.PreAuthorize;
  16 +import org.springframework.validation.annotation.Validated;
  17 +import org.springframework.web.bind.annotation.GetMapping;
  18 +import org.springframework.web.bind.annotation.RequestMapping;
  19 +import org.springframework.web.bind.annotation.RequestParam;
  20 +import org.springframework.web.bind.annotation.RestController;
17 21  
18 22 import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
19   -import static cn.iocoder.yudao.framework.common.util.servlet.ServletUtils.getClientIP;
20 23  
21 24 @Tag(name = "管理后台 - 转账单")
22 25 @RestController
... ... @@ -27,26 +30,21 @@ public class PayTransferController {
27 30 @Resource
28 31 private PayTransferService payTransferService;
29 32  
30   - @PostMapping("/create")
31   - @Operation(summary = "创建转账单,发起转账")
32   - @PreAuthorize("@ss.hasPermission('pay:transfer:create')")
33   - public CommonResult<PayTransferCreateRespVO> createPayTransfer(@Valid @RequestBody PayTransferCreateReqVO reqVO) {
34   - PayTransferDO payTransfer = payTransferService.createTransfer(reqVO, getClientIP());
35   - return success(new PayTransferCreateRespVO().setId(payTransfer.getId()).setStatus(payTransfer.getStatus()));
36   - }
37   -
38 33 @GetMapping("/get")
39 34 @Operation(summary = "获得转账订单")
40 35 @PreAuthorize("@ss.hasPermission('pay:transfer:query')")
41 36 public CommonResult<PayTransferRespVO> getTransfer(@RequestParam("id") Long id) {
42   - return success(PayTransferConvert.INSTANCE.convert(payTransferService.getTransfer(id)));
  37 + PayTransferDO transfer = payTransferService.getTransfer(id);
  38 + return success(BeanUtils.toBean(transfer, PayTransferRespVO.class));
43 39 }
44 40  
  41 + // TODO @芋艿:get 和 page 的返回,是不是统一融合
45 42 @GetMapping("/page")
46 43 @Operation(summary = "获得转账订单分页")
47 44 @PreAuthorize("@ss.hasPermission('pay:transfer:query')")
48 45 public CommonResult<PageResult<PayTransferPageItemRespVO>> getTransferPage(@Valid PayTransferPageReqVO pageVO) {
49 46 PageResult<PayTransferDO> pageResult = payTransferService.getTransferPage(pageVO);
50   - return success(PayTransferConvert.INSTANCE.convertPage(pageResult));
  47 + return success(BeanUtils.toBean(pageResult, PayTransferPageItemRespVO.class));
51 48 }
  49 +
52 50 }
... ...
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/controller/admin/transfer/vo/PayTransferCreateReqVO.java deleted
1   -package cn.iocoder.yudao.module.pay.controller.admin.transfer.vo;
2   -
3   -import cn.iocoder.yudao.framework.common.util.validation.ValidationUtils;
4   -import cn.iocoder.yudao.framework.common.validation.InEnum;
5   -import cn.iocoder.yudao.framework.pay.core.enums.channel.PayChannelEnum;
6   -import cn.iocoder.yudao.module.pay.enums.transfer.PayTransferTypeEnum;
7   -import io.swagger.v3.oas.annotations.media.Schema;
8   -import lombok.Data;
9   -
10   -import jakarta.validation.Validator;
11   -import jakarta.validation.constraints.*;
12   -import java.util.Map;
13   -
14   -import static cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants.NOT_IMPLEMENTED;
15   -import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
16   -import static cn.iocoder.yudao.module.pay.enums.transfer.PayTransferTypeEnum.*;
17   -
18   -@Schema(description = "管理后台 - 发起转账 Request VO")
19   -@Data
20   -public class PayTransferCreateReqVO {
21   -
22   - @Schema(description = "应用编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
23   - @NotNull(message = "应用编号不能为空")
24   - private Long appId;
25   -
26   - @Schema(description = "商户转账单编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
27   - @NotNull(message = "商户转账单编号不能为空")
28   - private String merchantTransferId;
29   -
30   - @Schema(description = "转账类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
31   - @NotNull(message = "转账类型不能为空")
32   - @InEnum(PayTransferTypeEnum.class)
33   - private Integer type;
34   -
35   - @Schema(description = "转账渠道", requiredMode = Schema.RequiredMode.REQUIRED, example = "alipay_pc")
36   - @NotEmpty(message = "转账渠道不能为空")
37   - private String channelCode;
38   -
39   - @Min(value = 1, message = "转账金额必须大于零")
40   - @NotNull(message = "转账金额不能为空")
41   - private Integer price;
42   -
43   - @Schema(description = "转账标题", requiredMode = Schema.RequiredMode.REQUIRED, example = "示例转账")
44   - @NotEmpty(message = "转账标题不能为空")
45   - private String subject;
46   -
47   - @Schema(description = "收款人姓名", example = "test1")
48   - @NotBlank(message = "收款人姓名不能为空", groups = {Alipay.class})
49   - private String userName;
50   -
51   - @Schema(description = "支付宝登录号", example = "test1@sandbox.com")
52   - @NotBlank(message = "支付宝登录号不能为空", groups = {Alipay.class})
53   - private String alipayLogonId;
54   -
55   - @Schema(description = "微信 openId", example = "oLefc4g5Gxx")
56   - @NotBlank(message = "微信 openId 不能为空", groups = {WxPay.class})
57   - private String openid;
58   -
59   - @Schema(description = "转账渠道的额外参数")
60   - private Map<String, String> channelExtras;
61   -
62   - public void validate(Validator validator) {
63   - PayTransferTypeEnum transferType = typeOf(type);
64   - switch (transferType) {
65   - case ALIPAY_BALANCE: {
66   - ValidationUtils.validate(validator, this, Alipay.class);
67   - break;
68   - }
69   - case WX_BALANCE: {
70   - ValidationUtils.validate(validator, this, WxPay.class);
71   - break;
72   - }
73   - default: {
74   - throw new UnsupportedOperationException("待实现");
75   - }
76   - }
77   - }
78   -
79   - @AssertTrue(message = "转账类型和转账渠道不匹配")
80   - public boolean isValidChannelCode() {
81   - PayTransferTypeEnum transferType = typeOf(type);
82   - switch (transferType) {
83   - case ALIPAY_BALANCE: {
84   - return PayChannelEnum.isAlipay(channelCode);
85   - }
86   - case WX_BALANCE:
87   - case BANK_CARD:
88   - case WALLET_BALANCE: {
89   - throw exception(NOT_IMPLEMENTED);
90   - }
91   - }
92   - return Boolean.FALSE;
93   - }
94   -
95   -}
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/controller/admin/transfer/vo/PayTransferPageReqVO.java
... ... @@ -26,9 +26,6 @@ public class PayTransferPageReqVO extends PageParam {
26 26 @Schema(description = "商户转账单编号", example = "17481")
27 27 private String merchantTransferId;
28 28  
29   - @Schema(description = "类型", example = "2")
30   - private Integer type;
31   -
32 29 @Schema(description = "转账状态", example = "2")
33 30 private Integer status;
34 31  
... ...
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/convert/demo/PayDemoOrderConvert.java deleted
1   -package cn.iocoder.yudao.module.pay.convert.demo;
2   -
3   -import cn.iocoder.yudao.framework.common.pojo.PageResult;
4   -import cn.iocoder.yudao.module.pay.controller.admin.demo.vo.order.PayDemoOrderCreateReqVO;
5   -import cn.iocoder.yudao.module.pay.controller.admin.demo.vo.order.PayDemoOrderRespVO;
6   -import cn.iocoder.yudao.module.pay.dal.dataobject.demo.PayDemoOrderDO;
7   -import org.mapstruct.Mapper;
8   -import org.mapstruct.factory.Mappers;
9   -
10   -/**
11   - * 示例订单 Convert
12   - *
13   - * @author 芋道源码
14   - */
15   -@Mapper
16   -public interface PayDemoOrderConvert {
17   -
18   - PayDemoOrderConvert INSTANCE = Mappers.getMapper(PayDemoOrderConvert.class);
19   -
20   - PayDemoOrderDO convert(PayDemoOrderCreateReqVO bean);
21   -
22   - PayDemoOrderRespVO convert(PayDemoOrderDO bean);
23   -
24   - PageResult<PayDemoOrderRespVO> convertPage(PageResult<PayDemoOrderDO> page);
25   -
26   -}
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/convert/demo/PayDemoTransferConvert.java deleted
1   -package cn.iocoder.yudao.module.pay.convert.demo;
2   -
3   -import cn.iocoder.yudao.framework.common.pojo.PageResult;
4   -import cn.iocoder.yudao.module.pay.controller.admin.demo.vo.transfer.PayDemoTransferCreateReqVO;
5   -import cn.iocoder.yudao.module.pay.controller.admin.demo.vo.transfer.PayDemoTransferRespVO;
6   -import cn.iocoder.yudao.module.pay.dal.dataobject.demo.PayDemoTransferDO;
7   -import org.mapstruct.Mapper;
8   -import org.mapstruct.factory.Mappers;
9   -
10   -/**
11   - * @author jason
12   - */
13   -@Mapper
14   -public interface PayDemoTransferConvert {
15   -
16   - PayDemoTransferConvert INSTANCE = Mappers.getMapper(PayDemoTransferConvert.class);
17   -
18   - PayDemoTransferDO convert(PayDemoTransferCreateReqVO bean);
19   -
20   - PageResult<PayDemoTransferRespVO> convertPage(PageResult<PayDemoTransferDO> pageResult);
21   -}
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/convert/transfer/PayTransferConvert.java deleted
1   -package cn.iocoder.yudao.module.pay.convert.transfer;
2   -
3   -import cn.iocoder.yudao.framework.common.pojo.PageResult;
4   -import cn.iocoder.yudao.framework.pay.core.client.dto.transfer.PayTransferUnifiedReqDTO;
5   -import cn.iocoder.yudao.module.pay.api.transfer.dto.PayTransferCreateReqDTO;
6   -import cn.iocoder.yudao.module.pay.controller.admin.demo.vo.transfer.PayDemoTransferCreateReqVO;
7   -import cn.iocoder.yudao.module.pay.controller.admin.transfer.vo.PayTransferCreateReqVO;
8   -import cn.iocoder.yudao.module.pay.controller.admin.transfer.vo.PayTransferPageItemRespVO;
9   -import cn.iocoder.yudao.module.pay.controller.admin.transfer.vo.PayTransferRespVO;
10   -import cn.iocoder.yudao.module.pay.dal.dataobject.transfer.PayTransferDO;
11   -import org.mapstruct.Mapper;
12   -import org.mapstruct.factory.Mappers;
13   -
14   -@Mapper
15   -public interface PayTransferConvert {
16   -
17   - PayTransferConvert INSTANCE = Mappers.getMapper(PayTransferConvert.class);
18   -
19   - PayTransferDO convert(PayTransferCreateReqDTO dto);
20   -
21   - PayTransferUnifiedReqDTO convert2(PayTransferDO dto);
22   -
23   - PayTransferCreateReqDTO convert(PayTransferCreateReqVO vo);
24   -
25   - PayTransferCreateReqDTO convert(PayDemoTransferCreateReqVO vo);
26   -
27   - PayTransferRespVO convert(PayTransferDO bean);
28   -
29   - PageResult<PayTransferPageItemRespVO> convertPage(PageResult<PayTransferDO> pageResult);
30   -
31   -}
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/dal/dataobject/demo/PayDemoTransferDO.java
1 1 package cn.iocoder.yudao.module.pay.dal.dataobject.demo;
2 2  
3 3 import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
4   -import cn.iocoder.yudao.framework.pay.core.enums.transfer.PayTransferTypeEnum;
5   -import cn.iocoder.yudao.module.pay.dal.dataobject.app.PayAppDO;
  4 +import cn.iocoder.yudao.module.pay.dal.dataobject.transfer.PayTransferDO;
  5 +import cn.iocoder.yudao.module.pay.enums.transfer.PayTransferStatusEnum;
6 6 import com.baomidou.mybatisplus.annotation.KeySequence;
7 7 import com.baomidou.mybatisplus.annotation.TableId;
8 8 import com.baomidou.mybatisplus.annotation.TableName;
... ... @@ -10,7 +10,6 @@ import lombok.Data;
10 10  
11 11 import java.time.LocalDateTime;
12 12  
13   -// TODO 芋艿:需要详细 review
14 13 /**
15 14 * 示例转账订单
16 15 *
... ... @@ -22,24 +21,22 @@ import java.time.LocalDateTime;
22 21 public class PayDemoTransferDO extends BaseDO {
23 22  
24 23 /**
25   - * 订单编号
  24 + * 编号,自增
26 25 */
27 26 @TableId
28 27 private Long id;
29 28  
30 29 /**
31   - * 应用编号
  30 + * 转账渠道
32 31 *
33   - * 关联 {@link PayAppDO#getId()}
  32 + * 枚举 {@link cn.iocoder.yudao.module.pay.enums.PayChannelEnum}
34 33 */
35   - private Long appId;
  34 + private String channelCode;
36 35  
37 36 /**
38   - * 转账类型
39   - * <p>
40   - * 枚举 {@link PayTransferTypeEnum}
  37 + * 转账标题
41 38 */
42   - private Integer type;
  39 + private String subject;
43 40  
44 41 /**
45 42 * 转账金额,单位:分
... ... @@ -47,37 +44,29 @@ public class PayDemoTransferDO extends BaseDO {
47 44 private Integer price;
48 45  
49 46 /**
50   - * 收款人姓名
  47 + * 收款人账号
51 48 */
52   - private String userName;
53   -
  49 + private String userAccount;
54 50 /**
55   - * 支付宝登录号
56   - */
57   - private String alipayLogonId;
58   -
59   - /**
60   - * 微信 openId
  51 + * 收款人姓名
61 52 */
62   - private String openid;
  53 + private String userName;
63 54  
64 55 /**
65 56 * 转账状态
  57 + *
  58 + * 枚举 {@link PayTransferStatusEnum}
66 59 */
67 60 private Integer transferStatus;
68 61  
69 62 /**
70 63 * 转账单编号
  64 + *
  65 + * 关联 {@link PayTransferDO#getId()}
71 66 */
72 67 private Long payTransferId;
73   -
74   - /**
75   - * 转账支付成功渠道
76   - */
77   - private String payChannelCode;
78   -
79 68 /**
80   - * 转账支付时间
  69 + * 转账成功时间
81 70 */
82 71 private LocalDateTime transferTime;
83 72  
... ...
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/dal/dataobject/transfer/PayTransferDO.java
... ... @@ -3,7 +3,6 @@ package cn.iocoder.yudao.module.pay.dal.dataobject.transfer;
3 3 import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
4 4 import cn.iocoder.yudao.framework.pay.core.enums.channel.PayChannelEnum;
5 5 import cn.iocoder.yudao.framework.pay.core.enums.transfer.PayTransferStatusRespEnum;
6   -import cn.iocoder.yudao.framework.pay.core.enums.transfer.PayTransferTypeEnum;
7 6 import cn.iocoder.yudao.module.pay.dal.dataobject.app.PayAppDO;
8 7 import cn.iocoder.yudao.module.pay.dal.dataobject.channel.PayChannelDO;
9 8 import com.baomidou.mybatisplus.annotation.KeySequence;
... ... @@ -16,7 +15,6 @@ import lombok.Data;
16 15 import java.time.LocalDateTime;
17 16 import java.util.Map;
18 17  
19   -// TODO 芋艿:需要详细 review
20 18 /**
21 19 * 转账单 DO
22 20 *
... ... @@ -35,7 +33,6 @@ public class PayTransferDO extends BaseDO {
35 33  
36 34 /**
37 35 * 转账单号
38   - *
39 36 */
40 37 private String no;
41 38  
... ... @@ -71,13 +68,6 @@ public class PayTransferDO extends BaseDO {
71 68 // ========== 转账相关字段 ==========
72 69  
73 70 /**
74   - * 类型
75   - *
76   - * 枚举 {@link PayTransferTypeEnum}
77   - */
78   - private Integer type;
79   -
80   - /**
81 71 * 转账标题
82 72 */
83 73 private String subject;
... ... @@ -88,6 +78,10 @@ public class PayTransferDO extends BaseDO {
88 78 private Integer price;
89 79  
90 80 /**
  81 + * 收款人账号
  82 + */
  83 + private String userAccount;
  84 + /**
91 85 * 收款人姓名
92 86 */
93 87 private String userName;
... ... @@ -104,19 +98,6 @@ public class PayTransferDO extends BaseDO {
104 98 */
105 99 private LocalDateTime successTime;
106 100  
107   - // ========== 支付宝转账相关字段 ==========
108   - /**
109   - * 支付宝登录号
110   - */
111   - private String alipayLogonId;
112   -
113   -
114   - // ========== 微信转账相关字段 ==========
115   - /**
116   - * 微信 openId
117   - */
118   - private String openid;
119   -
120 101 // ========== 其它字段 ==========
121 102  
122 103 /**
... ... @@ -151,7 +132,6 @@ public class PayTransferDO extends BaseDO {
151 132  
152 133 /**
153 134 * 渠道的同步/异步通知的内容
154   - *
155 135 */
156 136 private String channelNotifyData;
157 137  
... ...
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/dal/mysql/transfer/PayTransferMapper.java
... ... @@ -8,14 +8,22 @@ import cn.iocoder.yudao.module.pay.dal.dataobject.transfer.PayTransferDO;
8 8 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
9 9 import org.apache.ibatis.annotations.Mapper;
10 10  
  11 +import java.util.Collection;
11 12 import java.util.List;
12 13  
13 14 @Mapper
14 15 public interface PayTransferMapper extends BaseMapperX<PayTransferDO> {
15 16  
16   - default int updateByIdAndStatus(Long id, List<Integer> status, PayTransferDO updateObj) {
  17 + default int updateByIdAndStatus(Long id, List<Integer> whereStatuses, PayTransferDO updateObj) {
17 18 return update(updateObj, new LambdaQueryWrapper<PayTransferDO>()
18   - .eq(PayTransferDO::getId, id).in(PayTransferDO::getStatus, status));
  19 + .eq(PayTransferDO::getId, id)
  20 + .in(PayTransferDO::getStatus, whereStatuses));
  21 + }
  22 +
  23 + default int updateByIdAndStatus(Long id, Integer whereStatus, PayTransferDO updateObj) {
  24 + return update(updateObj, new LambdaQueryWrapper<PayTransferDO>()
  25 + .eq(PayTransferDO::getId, id)
  26 + .eq(PayTransferDO::getStatus, whereStatus));
19 27 }
20 28  
21 29 default PayTransferDO selectByAppIdAndMerchantTransferId(Long appId, String merchantTransferId){
... ... @@ -29,7 +37,6 @@ public interface PayTransferMapper extends BaseMapperX&lt;PayTransferDO&gt; {
29 37 .eqIfPresent(PayTransferDO::getAppId, reqVO.getAppId())
30 38 .eqIfPresent(PayTransferDO::getChannelCode, reqVO.getChannelCode())
31 39 .eqIfPresent(PayTransferDO::getMerchantTransferId, reqVO.getMerchantTransferId())
32   - .eqIfPresent(PayTransferDO::getType, reqVO.getType())
33 40 .eqIfPresent(PayTransferDO::getStatus, reqVO.getStatus())
34 41 .likeIfPresent(PayTransferDO::getUserName, reqVO.getUserName())
35 42 .eqIfPresent(PayTransferDO::getChannelTransferNo, reqVO.getChannelTransferNo())
... ... @@ -37,8 +44,8 @@ public interface PayTransferMapper extends BaseMapperX&lt;PayTransferDO&gt; {
37 44 .orderByDesc(PayTransferDO::getId));
38 45 }
39 46  
40   - default List<PayTransferDO> selectListByStatus(Integer status) {
41   - return selectList(PayTransferDO::getStatus, status);
  47 + default List<PayTransferDO> selectListByStatus(Collection<Integer> statuses) {
  48 + return selectList(PayTransferDO::getStatus, statuses);
42 49 }
43 50  
44 51 default PayTransferDO selectByAppIdAndNo(Long appId, String no) {
... ...
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/demo/PayDemoOrderServiceImpl.java
... ... @@ -96,7 +96,6 @@ public class PayDemoOrderServiceImpl implements PayDemoOrderService {
96 96 // 2.2 更新支付单到 demo 订单
97 97 payDemoOrderMapper.updateById(new PayDemoOrderDO().setId(demoOrder.getId())
98 98 .setPayOrderId(payOrderId));
99   - // 返回
100 99 return demoOrder.getId();
101 100 }
102 101  
... ...
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/demo/PayDemoTransferServiceImpl.java
... ... @@ -3,24 +3,25 @@ package cn.iocoder.yudao.module.pay.service.demo;
3 3 import cn.hutool.core.util.ObjectUtil;
4 4 import cn.iocoder.yudao.framework.common.pojo.PageParam;
5 5 import cn.iocoder.yudao.framework.common.pojo.PageResult;
  6 +import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
  7 +import cn.iocoder.yudao.module.pay.api.transfer.PayTransferApi;
  8 +import cn.iocoder.yudao.module.pay.api.transfer.dto.PayTransferCreateReqDTO;
6 9 import cn.iocoder.yudao.module.pay.controller.admin.demo.vo.transfer.PayDemoTransferCreateReqVO;
7   -import cn.iocoder.yudao.module.pay.convert.demo.PayDemoTransferConvert;
8 10 import cn.iocoder.yudao.module.pay.dal.dataobject.demo.PayDemoTransferDO;
9 11 import cn.iocoder.yudao.module.pay.dal.dataobject.transfer.PayTransferDO;
10 12 import cn.iocoder.yudao.module.pay.dal.mysql.demo.PayDemoTransferMapper;
11 13 import cn.iocoder.yudao.module.pay.enums.transfer.PayTransferStatusEnum;
12 14 import cn.iocoder.yudao.module.pay.service.transfer.PayTransferService;
  15 +import jakarta.annotation.Resource;
  16 +import jakarta.validation.Valid;
13 17 import org.springframework.stereotype.Service;
14 18 import org.springframework.validation.annotation.Validated;
15 19  
16   -import jakarta.annotation.Resource;
17   -import jakarta.validation.Valid;
18   -import jakarta.validation.Validator;
19 20 import java.util.Objects;
20 21  
21 22 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
  23 +import static cn.iocoder.yudao.framework.common.util.servlet.ServletUtils.getClientIP;
22 24 import static cn.iocoder.yudao.module.pay.enums.ErrorCodeConstants.*;
23   -import static cn.iocoder.yudao.module.pay.enums.transfer.PayTransferStatusEnum.WAITING;
24 25  
25 26 /**
26 27 * 示例转账业务 Service 实现类
... ... @@ -32,26 +33,38 @@ import static cn.iocoder.yudao.module.pay.enums.transfer.PayTransferStatusEnum.W
32 33 public class PayDemoTransferServiceImpl implements PayDemoTransferService {
33 34  
34 35 /**
35   - * 接入的实力应用编号
36   -
  36 + * 接入的支付应用标识
  37 + *
37 38 * 从 [支付管理 -> 应用信息] 里添加
38 39 */
39   - private static final Long TRANSFER_APP_ID = 8L;
  40 + private static final String PAY_APP_KEY = "demo";
  41 +
40 42 @Resource
41 43 private PayDemoTransferMapper demoTransferMapper;
  44 +
42 45 @Resource
43 46 private PayTransferService payTransferService;
  47 +
44 48 @Resource
45   - private Validator validator;
  49 + private PayTransferApi payTransferApi;
46 50  
47 51 @Override
48   - public Long createDemoTransfer(@Valid PayDemoTransferCreateReqVO vo) {
49   - // 1 校验参数
50   - vo.validate(validator);
51   - // 2 保存示例转账业务表
52   - PayDemoTransferDO demoTransfer = PayDemoTransferConvert.INSTANCE.convert(vo)
53   - .setAppId(TRANSFER_APP_ID).setTransferStatus(WAITING.getStatus());
  52 + public Long createDemoTransfer(@Valid PayDemoTransferCreateReqVO reqVO) {
  53 + // 1. 保存示例转账业务表
  54 + PayDemoTransferDO demoTransfer = BeanUtils.toBean(reqVO, PayDemoTransferDO.class)
  55 + .setTransferStatus(PayTransferStatusEnum.WAITING.getStatus());
54 56 demoTransferMapper.insert(demoTransfer);
  57 +
  58 + // 2.1 创建支付单
  59 + Long payTransferId = payTransferApi.createTransfer(new PayTransferCreateReqDTO()
  60 + .setChannelCode(reqVO.getChannelCode())
  61 + .setAppKey(PAY_APP_KEY).setUserIp(getClientIP()) // 支付应用
  62 + .setMerchantTransferId(String.valueOf(demoTransfer.getId())) // 业务的订单编号
  63 + .setSubject(reqVO.getSubject()).setPrice(demoTransfer.getPrice()) // 价格信息
  64 + .setUserAccount(reqVO.getUserAccount()).setUserName(reqVO.getUserName())); // 收款信息
  65 + // 2.2 更新转账单到 demo 示例转账业务表
  66 + demoTransferMapper.updateById(new PayDemoTransferDO().setId(demoTransfer.getId())
  67 + .setPayTransferId(payTransferId));
55 68 return demoTransfer.getId();
56 69 }
57 70  
... ... @@ -63,11 +76,11 @@ public class PayDemoTransferServiceImpl implements PayDemoTransferService {
63 76 @Override
64 77 public void updateDemoTransferStatus(Long id, Long payTransferId) {
65 78 PayTransferDO payTransfer = validateDemoTransferStatusCanUpdate(id, payTransferId);
  79 + // TODO @芋艿:这块,需要在优化下;
66 80 // 更新示例订单状态
67 81 if (payTransfer != null) {
68 82 demoTransferMapper.updateById(new PayDemoTransferDO().setId(id)
69 83 .setPayTransferId(payTransferId)
70   - .setPayChannelCode(payTransfer.getChannelCode())
71 84 .setTransferStatus(payTransfer.getStatus())
72 85 .setTransferTime(payTransfer.getSuccessTime()));
73 86 }
... ... @@ -78,9 +91,10 @@ public class PayDemoTransferServiceImpl implements PayDemoTransferService {
78 91 if (demoTransfer == null) {
79 92 throw exception(DEMO_TRANSFER_NOT_FOUND);
80 93 }
  94 + // TODO @芋艿:这里也要更新下;
  95 + // 无需更新返回 null
81 96 if (PayTransferStatusEnum.isSuccess(demoTransfer.getTransferStatus())
82 97 || PayTransferStatusEnum.isClosed(demoTransfer.getTransferStatus())) {
83   - // 无需更新返回 null
84 98 return null;
85 99 }
86 100 PayTransferDO transfer = payTransferService.getTransfer(payTransferId);
... ... @@ -96,4 +110,5 @@ public class PayDemoTransferServiceImpl implements PayDemoTransferService {
96 110 // TODO 校验账号
97 111 return transfer;
98 112 }
  113 +
99 114 }
... ...
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/transfer/PayTransferService.java
... ... @@ -3,10 +3,8 @@ package cn.iocoder.yudao.module.pay.service.transfer;
3 3 import cn.iocoder.yudao.framework.common.pojo.PageResult;
4 4 import cn.iocoder.yudao.framework.pay.core.client.dto.transfer.PayTransferRespDTO;
5 5 import cn.iocoder.yudao.module.pay.api.transfer.dto.PayTransferCreateReqDTO;
6   -import cn.iocoder.yudao.module.pay.controller.admin.transfer.vo.PayTransferCreateReqVO;
7 6 import cn.iocoder.yudao.module.pay.controller.admin.transfer.vo.PayTransferPageReqVO;
8 7 import cn.iocoder.yudao.module.pay.dal.dataobject.transfer.PayTransferDO;
9   -
10 8 import jakarta.validation.Valid;
11 9  
12 10 /**
... ... @@ -19,17 +17,6 @@ public interface PayTransferService {
19 17 /**
20 18 * 创建转账单,并发起转账
21 19 *
22   - * 此时,会发起转账渠道的调用
23   - *
24   - * @param reqVO 请求
25   - * @param userIp 用户 ip
26   - * @return 渠道的返回结果
27   - */
28   - PayTransferDO createTransfer(@Valid PayTransferCreateReqVO reqVO, String userIp);
29   -
30   - /**
31   - * 创建转账单,并发起转账
32   - *
33 20 * @param reqDTO 创建请求
34 21 * @return 转账单编号
35 22 */
... ...
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/transfer/PayTransferServiceImpl.java
... ... @@ -5,13 +5,13 @@ import cn.hutool.core.util.ObjectUtil;
5 5 import cn.hutool.extra.spring.SpringUtil;
6 6 import cn.iocoder.yudao.framework.common.pojo.PageResult;
7 7 import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
  8 +import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
8 9 import cn.iocoder.yudao.framework.pay.core.client.PayClient;
9 10 import cn.iocoder.yudao.framework.pay.core.client.dto.transfer.PayTransferRespDTO;
10 11 import cn.iocoder.yudao.framework.pay.core.client.dto.transfer.PayTransferUnifiedReqDTO;
11 12 import cn.iocoder.yudao.framework.pay.core.enums.transfer.PayTransferStatusRespEnum;
12 13 import cn.iocoder.yudao.framework.tenant.core.util.TenantUtils;
13 14 import cn.iocoder.yudao.module.pay.api.transfer.dto.PayTransferCreateReqDTO;
14   -import cn.iocoder.yudao.module.pay.controller.admin.transfer.vo.PayTransferCreateReqVO;
15 15 import cn.iocoder.yudao.module.pay.controller.admin.transfer.vo.PayTransferPageReqVO;
16 16 import cn.iocoder.yudao.module.pay.dal.dataobject.app.PayAppDO;
17 17 import cn.iocoder.yudao.module.pay.dal.dataobject.channel.PayChannelDO;
... ... @@ -25,7 +25,6 @@ import cn.iocoder.yudao.module.pay.service.app.PayAppService;
25 25 import cn.iocoder.yudao.module.pay.service.channel.PayChannelService;
26 26 import cn.iocoder.yudao.module.pay.service.notify.PayNotifyService;
27 27 import jakarta.annotation.Resource;
28   -import jakarta.validation.Validator;
29 28 import lombok.extern.slf4j.Slf4j;
30 29 import org.springframework.stereotype.Service;
31 30 import org.springframework.transaction.annotation.Transactional;
... ... @@ -33,9 +32,7 @@ import org.springframework.transaction.annotation.Transactional;
33 32 import java.util.List;
34 33  
35 34 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
36   -import static cn.iocoder.yudao.module.pay.convert.transfer.PayTransferConvert.INSTANCE;
37 35 import static cn.iocoder.yudao.module.pay.enums.ErrorCodeConstants.*;
38   -import static cn.iocoder.yudao.module.pay.enums.transfer.PayTransferStatusEnum.*;
39 36  
40 37 // TODO @jason:等彻底实现完,单测写写;
41 38  
... ... @@ -63,21 +60,6 @@ public class PayTransferServiceImpl implements PayTransferService {
63 60 private PayNotifyService notifyService;
64 61 @Resource
65 62 private PayNoRedisDAO noRedisDAO;
66   - @Resource
67   - private Validator validator;
68   -
69   - @Override
70   - public PayTransferDO createTransfer(PayTransferCreateReqVO reqVO, String userIp) {
71   - // 1. 校验参数
72   - reqVO.validate(validator);
73   -
74   - // 2. 创建转账单,发起转账
75   - PayTransferCreateReqDTO req = INSTANCE.convert(reqVO).setUserIp(userIp);
76   - Long transferId = createTransfer(req);
77   -
78   - // 3. 返回转账单
79   - return getTransfer(transferId);
80   - }
81 63  
82 64 @Override
83 65 public Long createTransfer(PayTransferCreateReqDTO reqDTO) {
... ... @@ -90,34 +72,32 @@ public class PayTransferServiceImpl implements PayTransferService {
90 72 log.error("[createTransfer][渠道编号({}) 找不到对应的支付客户端]", channel.getId());
91 73 throw exception(CHANNEL_NOT_FOUND);
92 74 }
93   - // 1.3 校验转账单已经发起过转账。
  75 + // 1.3 校验转账单已经发起过转账
94 76 PayTransferDO transfer = validateTransferCanCreate(reqDTO, payApp.getId());
95 77  
  78 + // 2. 不存在创建转账单,否则允许使用相同的 no 再次发起转账
96 79 if (transfer == null) {
97   - // 2.不存在创建转账单. 否则允许使用相同的 no 再次发起转账
98 80 String no = noRedisDAO.generate(TRANSFER_NO_PREFIX);
99   - transfer = INSTANCE.convert(reqDTO)
100   - .setChannelId(channel.getId())
101   - .setNo(no).setStatus(WAITING.getStatus())
102   - .setNotifyUrl(payApp.getTransferNotifyUrl())
103   - .setAppId(channel.getAppId());
  81 + transfer = BeanUtils.toBean(reqDTO, PayTransferDO.class)
  82 + .setAppId(channel.getAppId()).setChannelId(channel.getId())
  83 + .setNo(no).setStatus(PayTransferStatusEnum.WAITING.getStatus())
  84 + .setNotifyUrl(payApp.getTransferNotifyUrl());
104 85 transferMapper.insert(transfer);
105 86 }
106 87 try {
107 88 // 3. 调用三方渠道发起转账
108   - PayTransferUnifiedReqDTO transferUnifiedReq = INSTANCE.convert2(transfer)
109   - .setOutTransferNo(transfer.getNo());
110   - transferUnifiedReq.setNotifyUrl(genChannelTransferNotifyUrl(channel));
  89 + PayTransferUnifiedReqDTO transferUnifiedReq = BeanUtils.toBean(reqDTO, PayTransferUnifiedReqDTO.class)
  90 + .setOutTransferNo(transfer.getNo())
  91 + .setNotifyUrl(genChannelTransferNotifyUrl(channel));
111 92 PayTransferRespDTO unifiedTransferResp = client.unifiedTransfer(transferUnifiedReq);
112 93 // 4. 通知转账结果
113 94 getSelf().notifyTransfer(channel, unifiedTransferResp);
114 95 } catch (Throwable e) {
115 96 // 注意这里仅打印异常,不进行抛出。
116 97 // 原因是:虽然调用支付渠道进行转账发生异常(网络请求超时),实际转账成功。这个结果,后续转账轮询可以拿到。
117   - // 或者使用相同 no 再次发起转账请求
118   - log.error("[createTransfer][转账 id({}) requestDTO({}) 发生异常]", transfer.getId(), reqDTO, e);
  98 + // 或者,使用相同 no 再次发起转账请求
  99 + log.error("[createTransfer][转账编号({}) requestDTO({}) 发生异常]", transfer.getId(), reqDTO, e);
119 100 }
120   -
121 101 return transfer.getId();
122 102 }
123 103  
... ... @@ -131,21 +111,23 @@ public class PayTransferServiceImpl implements PayTransferService {
131 111 return payProperties.getTransferNotifyUrl() + "/" + channel.getId();
132 112 }
133 113  
134   - private PayTransferDO validateTransferCanCreate(PayTransferCreateReqDTO dto, Long appId) {
135   - PayTransferDO transfer = transferMapper.selectByAppIdAndMerchantTransferId(appId, dto.getMerchantTransferId());
  114 + private PayTransferDO validateTransferCanCreate(PayTransferCreateReqDTO reqDTO, Long appId) {
  115 + PayTransferDO transfer = transferMapper.selectByAppIdAndMerchantTransferId(appId, reqDTO.getMerchantTransferId());
136 116 if (transfer != null) {
137   - // 已经存在,并且状态不为等待状态。说明已经调用渠道转账并返回结果.
  117 + // 已经存在,并且状态不为等待状态:说明已经调用渠道转账并返回结果
138 118 if (!PayTransferStatusEnum.isWaiting(transfer.getStatus())) {
139   - throw exception(PAY_MERCHANT_TRANSFER_EXISTS);
  119 + throw exception(PAY_TRANSFER_CREATE_MERCHANT_EXISTS);
140 120 }
141   - if (ObjectUtil.notEqual(dto.getPrice(), transfer.getPrice())) {
142   - throw exception(PAY_SAME_MERCHANT_TRANSFER_PRICE_NOT_MATCH);
  121 + // 校验参数是否一致
  122 + if (ObjectUtil.notEqual(reqDTO.getPrice(), transfer.getPrice())) {
  123 + throw exception(PAY_TRANSFER_CREATE_PRICE_NOT_MATCH);
143 124 }
144   - if (ObjectUtil.notEqual(dto.getType(), transfer.getType())) {
145   - throw exception(PAY_SAME_MERCHANT_TRANSFER_TYPE_NOT_MATCH);
  125 + if (ObjectUtil.notEqual(reqDTO.getChannelCode(), transfer.getChannelCode())) {
  126 + throw exception(PAY_TRANSFER_CREATE_CHANNEL_NOT_MATCH);
146 127 }
147 128 }
148   - // 如果状态为等待状态。不知道渠道转账是否发起成功。 允许使用相同的 no 再次发起转账,渠道会保证幂等
  129 + // 如果状态为等待状态:不知道渠道转账是否发起成功
  130 + // 特殊:允许使用相同的 no 再次发起转账,渠道会保证幂等
149 131 return transfer;
150 132 }
151 133  
... ... @@ -161,94 +143,94 @@ public class PayTransferServiceImpl implements PayTransferService {
161 143 notifyTransferClosed(channel, notify);
162 144 }
163 145 // 转账处理中的回调
164   - if (PayTransferStatusRespEnum.isInProgress(notify.getStatus())) {
165   - notifyTransferInProgress(channel, notify);
  146 + if (PayTransferStatusRespEnum.isProcessing(notify.getStatus())) {
  147 + notifyTransferProgressing(channel, notify);
166 148 }
167 149 // WAITING 状态无需处理
168 150 }
169 151  
170   - private void notifyTransferInProgress(PayChannelDO channel, PayTransferRespDTO notify) {
  152 + private void notifyTransferProgressing(PayChannelDO channel, PayTransferRespDTO notify) {
171 153 // 1.校验
172 154 PayTransferDO transfer = transferMapper.selectByAppIdAndNo(channel.getAppId(), notify.getOutTransferNo());
173 155 if (transfer == null) {
174 156 throw exception(PAY_TRANSFER_NOT_FOUND);
175 157 }
176   - if (isInProgress(transfer.getStatus())) { // 如果已经是转账中,直接返回,不用重复更新
  158 + if (PayTransferStatusEnum.isProgressing(transfer.getStatus())) { // 如果已经是转账中,直接返回,不用重复更新
  159 + log.info("[notifyTransferProgressing][transfer({}) 已经是转账中状态,无需更新]", transfer.getId());
177 160 return;
178 161 }
179   - if (!isWaiting(transfer.getStatus())) {
  162 + if (!PayTransferStatusEnum.isWaiting(transfer.getStatus())) {
180 163 throw exception(PAY_TRANSFER_STATUS_IS_NOT_WAITING);
181 164 }
182   - // 2.更新
  165 +
  166 + // 2. 更新状态
183 167 int updateCounts = transferMapper.updateByIdAndStatus(transfer.getId(),
184   - CollUtil.newArrayList(WAITING.getStatus()),
185   - new PayTransferDO().setStatus(IN_PROGRESS.getStatus()));
  168 + PayTransferStatusEnum.WAITING.getStatus(),
  169 + new PayTransferDO().setStatus(PayTransferStatusEnum.PROCESSING.getStatus()));
186 170 if (updateCounts == 0) {
187 171 throw exception(PAY_TRANSFER_STATUS_IS_NOT_WAITING);
188 172 }
189   - log.info("[notifyTransferInProgress][transfer({}) 更新为转账进行中状态]", transfer.getId());
  173 + log.info("[notifyTransferProgressing][transfer({}) 更新为转账进行中状态]", transfer.getId());
190 174 }
191 175  
192   -
193 176 private void notifyTransferSuccess(PayChannelDO channel, PayTransferRespDTO notify) {
194   - // 1.校验
  177 + // 1. 校验状态
195 178 PayTransferDO transfer = transferMapper.selectByAppIdAndNo(channel.getAppId(), notify.getOutTransferNo());
196 179 if (transfer == null) {
197 180 throw exception(PAY_TRANSFER_NOT_FOUND);
198 181 }
199   - if (isSuccess(transfer.getStatus())) { // 如果已成功,直接返回,不用重复更新
  182 + if (PayTransferStatusEnum.isSuccess(transfer.getStatus())) { // 如果已成功,直接返回,不用重复更新
  183 + log.info("[notifyTransferSuccess][transfer({}) 已经是成功状态,无需更新]", transfer.getId());
200 184 return;
201 185 }
202   - if (!isPendingStatus(transfer.getStatus())) {
203   - throw exception(PAY_TRANSFER_STATUS_IS_NOT_PENDING);
  186 + if (!PayTransferStatusEnum.isWaitingOrProcessing(transfer.getStatus())) {
  187 + throw exception(PAY_TRANSFER_STATUS_IS_NOT_WAITING_OR_PROCESSING);
204 188 }
205   - // 2.更新
  189 +
  190 + // 2. 更新状态
206 191 int updateCounts = transferMapper.updateByIdAndStatus(transfer.getId(),
207   - CollUtil.newArrayList(WAITING.getStatus(), IN_PROGRESS.getStatus()),
208   - new PayTransferDO().setStatus(SUCCESS.getStatus()).setSuccessTime(notify.getSuccessTime())
  192 + CollUtil.newArrayList(PayTransferStatusEnum.WAITING.getStatus(), PayTransferStatusEnum.PROCESSING.getStatus()),
  193 + new PayTransferDO().setStatus(PayTransferStatusEnum.SUCCESS.getStatus())
  194 + .setSuccessTime(notify.getSuccessTime())
209 195 .setChannelTransferNo(notify.getChannelTransferNo())
210   - .setChannelId(channel.getId()).setChannelCode(channel.getCode())
211 196 .setChannelNotifyData(JsonUtils.toJsonString(notify)));
212 197 if (updateCounts == 0) {
213   - throw exception(PAY_TRANSFER_STATUS_IS_NOT_PENDING);
  198 + throw exception(PAY_TRANSFER_STATUS_IS_NOT_WAITING_OR_PROCESSING);
214 199 }
215   - log.info("[updateTransferSuccess][transfer({}) 更新为已转账]", transfer.getId());
  200 + log.info("[notifyTransferSuccess][transfer({}) 更新为已转账]", transfer.getId());
216 201  
217 202 // 3. 插入转账通知记录
218   - notifyService.createPayNotifyTask(PayNotifyTypeEnum.TRANSFER.getType(),
219   - transfer.getId());
  203 + notifyService.createPayNotifyTask(PayNotifyTypeEnum.TRANSFER.getType(), transfer.getId());
220 204 }
221 205  
222 206 private void notifyTransferClosed(PayChannelDO channel, PayTransferRespDTO notify) {
223   - // 1.校验
  207 + // 1. 校验状态
224 208 PayTransferDO transfer = transferMapper.selectByAppIdAndNo(channel.getAppId(), notify.getOutTransferNo());
225 209 if (transfer == null) {
226 210 throw exception(PAY_TRANSFER_NOT_FOUND);
227 211 }
228   - if (isClosed(transfer.getStatus())) { // 如果已是关闭状态,直接返回,不用重复更新
229   - log.info("[updateTransferClosed][transfer({}) 已经是关闭状态,无需更新]", transfer.getId());
  212 + if (PayTransferStatusEnum.isClosed(transfer.getStatus())) { // 如果已是关闭状态,直接返回,不用重复更新
  213 + log.info("[notifyTransferClosed][transfer({}) 已经是关闭状态,无需更新]", transfer.getId());
230 214 return;
231 215 }
232   - if (!isPendingStatus(transfer.getStatus())) {
233   - throw exception(PAY_TRANSFER_STATUS_IS_NOT_PENDING);
  216 + if (!PayTransferStatusEnum.isWaitingOrProcessing(transfer.getStatus())) {
  217 + throw exception(PAY_TRANSFER_STATUS_IS_NOT_WAITING_OR_PROCESSING);
234 218 }
235 219  
236   - // 2.更新
  220 + // 2. 更新状态
237 221 int updateCount = transferMapper.updateByIdAndStatus(transfer.getId(),
238   - CollUtil.newArrayList(WAITING.getStatus(), IN_PROGRESS.getStatus()),
239   - new PayTransferDO().setStatus(CLOSED.getStatus()).setChannelId(channel.getId())
240   - .setChannelCode(channel.getCode()).setChannelTransferNo(notify.getChannelTransferNo())
241   - .setChannelErrorCode(notify.getChannelErrorCode()).setChannelErrorMsg(notify.getChannelErrorMsg())
242   - .setChannelNotifyData(JsonUtils.toJsonString(notify)));
  222 + CollUtil.newArrayList(PayTransferStatusEnum.WAITING.getStatus(), PayTransferStatusEnum.PROCESSING.getStatus()),
  223 + new PayTransferDO().setStatus(PayTransferStatusEnum.CLOSED.getStatus())
  224 + .setChannelTransferNo(notify.getChannelTransferNo())
  225 + .setChannelNotifyData(JsonUtils.toJsonString(notify))
  226 + .setChannelErrorCode(notify.getChannelErrorCode()).setChannelErrorMsg(notify.getChannelErrorMsg()));
243 227 if (updateCount == 0) {
244   - throw exception(PAY_TRANSFER_STATUS_IS_NOT_PENDING);
  228 + throw exception(PAY_TRANSFER_STATUS_IS_NOT_WAITING_OR_PROCESSING);
245 229 }
246   - log.info("[updateTransferClosed][transfer({}) 更新为关闭状态]", transfer.getId());
  230 + log.info("[notifyTransferClosed][transfer({}) 更新为关闭状态]", transfer.getId());
247 231  
248 232 // 3. 插入转账通知记录
249   - notifyService.createPayNotifyTask(PayNotifyTypeEnum.TRANSFER.getType(),
250   - transfer.getId());
251   -
  233 + notifyService.createPayNotifyTask(PayNotifyTypeEnum.TRANSFER.getType(), transfer.getId());
252 234 }
253 235  
254 236 @Override
... ... @@ -263,7 +245,8 @@ public class PayTransferServiceImpl implements PayTransferService {
263 245  
264 246 @Override
265 247 public int syncTransfer() {
266   - List<PayTransferDO> list = transferMapper.selectListByStatus(WAITING.getStatus());
  248 + List<PayTransferDO> list = transferMapper.selectListByStatus(CollUtil.newArrayList(
  249 + PayTransferStatusEnum.WAITING.getStatus(), PayTransferStatusEnum.PROCESSING.getStatus()));
267 250 if (CollUtil.isEmpty(list)) {
268 251 return 0;
269 252 }
... ... @@ -308,4 +291,5 @@ public class PayTransferServiceImpl implements PayTransferService {
308 291 private PayTransferServiceImpl getSelf() {
309 292 return SpringUtil.getBean(getClass());
310 293 }
  294 +
311 295 }
... ...
yudao-module-pay/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/dto/transfer/PayTransferRespDTO.java
... ... @@ -66,10 +66,10 @@ public class PayTransferRespDTO {
66 66 /**
67 67 * 创建【IN_PROGRESS】状态的转账返回
68 68 */
69   - public static PayTransferRespDTO dealingOf(String channelTransferNo,
70   - String outTransferNo, Object rawData) {
  69 + public static PayTransferRespDTO processingOf(String channelTransferNo,
  70 + String outTransferNo, Object rawData) {
71 71 PayTransferRespDTO respDTO = new PayTransferRespDTO();
72   - respDTO.status = PayTransferStatusRespEnum.IN_PROGRESS.getStatus();
  72 + respDTO.status = PayTransferStatusRespEnum.PROCESSING.getStatus();
73 73 respDTO.channelTransferNo = channelTransferNo;
74 74 respDTO.outTransferNo = outTransferNo;
75 75 respDTO.rawData = rawData;
... ...
yudao-module-pay/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/dto/transfer/PayTransferUnifiedReqDTO.java
1 1 package cn.iocoder.yudao.framework.pay.core.client.dto.transfer;
2 2  
3 3 import jakarta.validation.constraints.Min;
4   -import jakarta.validation.constraints.NotBlank;
5 4 import jakarta.validation.constraints.NotEmpty;
6 5 import jakarta.validation.constraints.NotNull;
7 6 import lombok.Data;
... ... @@ -10,9 +9,6 @@ import org.hibernate.validator.constraints.URL;
10 9  
11 10 import java.util.Map;
12 11  
13   -import static cn.iocoder.yudao.framework.pay.core.enums.transfer.PayTransferTypeEnum.Alipay;
14   -import static cn.iocoder.yudao.framework.pay.core.enums.transfer.PayTransferTypeEnum.WxPay;
15   -
16 12 /**
17 13 * 统一转账 Request DTO
18 14 *
... ... @@ -27,6 +23,9 @@ public class PayTransferUnifiedReqDTO {
27 23 @NotEmpty(message = "用户 IP 不能为空")
28 24 private String userIp;
29 25  
  26 + /**
  27 + * 外部转账单编号
  28 + */
30 29 @NotEmpty(message = "外部转账单编号不能为空")
31 30 private String outTransferNo;
32 31  
... ... @@ -44,24 +43,18 @@ public class PayTransferUnifiedReqDTO {
44 43 @Length(max = 128, message = "转账标题不能超过 128")
45 44 private String subject;
46 45  
47   - // TODO @芋艿:userName、alipayLogonId、openid =》channelExtras;另外:validatePayTransferReqDTO 去掉;
48 46 /**
49   - * 收款人姓名
  47 + * 收款人账号
  48 + *
  49 + * 微信场景下:openid
  50 + * 支付宝场景下:支付宝账号
50 51 */
51   - @NotBlank(message = "收款人姓名不能为空", groups = {Alipay.class})
52   - private String userName;
53   -
  52 + @NotEmpty(message = "收款人账号不能为空")
  53 + private String userAccount;
54 54 /**
55   - * 支付宝登录号
56   - */
57   - @NotBlank(message = "支付宝登录号不能为空", groups = {Alipay.class})
58   - private String alipayLogonId;
59   -
60   - /**
61   - * 微信 openId
  55 + * 收款人姓名
62 56 */
63   - @NotBlank(message = "微信 openId 不能为空", groups = {WxPay.class})
64   - private String openid;
  57 + private String userName;
65 58  
66 59 /**
67 60 * 支付渠道的额外参数
... ...
yudao-module-pay/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/AbstractPayClient.java
... ... @@ -11,13 +11,10 @@ import cn.iocoder.yudao.framework.pay.core.client.dto.refund.PayRefundUnifiedReq
11 11 import cn.iocoder.yudao.framework.pay.core.client.dto.transfer.PayTransferRespDTO;
12 12 import cn.iocoder.yudao.framework.pay.core.client.dto.transfer.PayTransferUnifiedReqDTO;
13 13 import cn.iocoder.yudao.framework.pay.core.client.exception.PayException;
14   -import cn.iocoder.yudao.framework.pay.core.enums.transfer.PayTransferTypeEnum;
15 14 import lombok.extern.slf4j.Slf4j;
16 15  
17 16 import java.util.Map;
18 17  
19   -import static cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants.NOT_IMPLEMENTED;
20   -import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
21 18 import static cn.iocoder.yudao.framework.common.util.json.JsonUtils.toJsonString;
22 19  
23 20 /**
... ...
yudao-module-pay/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/alipay/AbstractAlipayPayClient.java
... ... @@ -4,7 +4,6 @@ import cn.hutool.core.bean.BeanUtil;
4 4 import cn.hutool.core.date.LocalDateTimeUtil;
5 5 import cn.hutool.core.lang.Assert;
6 6 import cn.hutool.core.map.MapUtil;
7   -import cn.hutool.core.util.ObjectUtil;
8 7 import cn.hutool.core.util.StrUtil;
9 8 import cn.hutool.http.HttpUtil;
10 9 import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
... ... @@ -40,8 +39,6 @@ import java.util.Objects;
40 39 import java.util.function.Supplier;
41 40  
42 41 import static cn.hutool.core.date.DatePattern.NORM_DATETIME_FORMATTER;
43   -import static cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants.ERROR_CONFIGURATION;
44   -import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception0;
45 42 import static cn.iocoder.yudao.framework.pay.core.client.impl.alipay.AlipayPayClientConfig.MODE_CERTIFICATE;
46 43 import static cn.iocoder.yudao.framework.pay.core.client.impl.alipay.AlipayPayClientConfig.MODE_PUBLIC_KEY;
47 44  
... ... @@ -235,10 +232,9 @@ public abstract class AbstractAlipayPayClient extends AbstractPayClient&lt;AlipayPa
235 232  
236 233 @Override
237 234 protected PayTransferRespDTO doUnifiedTransfer(PayTransferUnifiedReqDTO reqDTO) throws AlipayApiException {
238   - // 0. 校验公钥类型:必须使用公钥证书模式
239   - if (ObjectUtil.notEqual(config.getMode(), MODE_CERTIFICATE)) {
240   - throw exception0(ERROR_CONFIGURATION.getCode(), "支付宝单笔转账必须使用公钥证书模式");
241   - }
  235 + // 补充说明:https://opendocs.alipay.com/open/03dcrm?pathHash=4ba3b20b
  236 + // 沙箱环境:可通过 公钥模式 或 公钥证书模式 加签进行调试
  237 + // 生产环境:必须使用 公钥证书模式 加签请求强校验请求
242 238  
243 239 // 1.1 构建 AlipayFundTransUniTransferModel
244 240 AlipayFundTransUniTransferModel model = new AlipayFundTransUniTransferModel();
... ... @@ -254,7 +250,7 @@ public abstract class AbstractAlipayPayClient extends AbstractPayClient&lt;AlipayPa
254 250 // ② 个性化的参数
255 251 Participant payeeInfo = new Participant();
256 252 payeeInfo.setIdentityType("ALIPAY_LOGON_ID"); // 暂时只考虑转账到支付宝,银行没有权限 https://opendocs.alipay.com/open/02byvc?scene=66dd06f5a923403393b85de68d3c0055
257   - payeeInfo.setIdentity(reqDTO.getAlipayLogonId()); // 支付宝登录号
  253 + payeeInfo.setIdentity(reqDTO.getUserAccount()); // 支付宝登录号
258 254 payeeInfo.setName(reqDTO.getUserName()); // 支付宝账号姓名
259 255 model.setPayeeInfo(payeeInfo);
260 256 // 1.2 构建 AlipayFundTransUniTransferRequest
... ... @@ -262,7 +258,12 @@ public abstract class AbstractAlipayPayClient extends AbstractPayClient&lt;AlipayPa
262 258 request.setBizModel(model);
263 259  
264 260 // 2.1 执行请求
265   - AlipayFundTransUniTransferResponse response = client.certificateExecute(request);
  261 + AlipayFundTransUniTransferResponse response;
  262 + if (Objects.equals(config.getMode(), MODE_CERTIFICATE)) { // 证书模式
  263 + response = client.certificateExecute(request);
  264 + } else {
  265 + response = client.execute(request);
  266 + }
266 267 if (!response.isSuccess()) {
267 268 // 当出现 SYSTEM_ERROR, 转账可能成功也可能失败。 返回 WAIT 状态. 后续 job 会轮询,或相同 outBizNo 重新发起转账
268 269 // 发现 outBizNo 相同 两次请求参数相同. 会返回 "PAYMENT_INFO_INCONSISTENCY", 不知道哪里的问题. 暂时返回 WAIT. 后续job 会轮询
... ... @@ -278,7 +279,7 @@ public abstract class AbstractAlipayPayClient extends AbstractPayClient&lt;AlipayPa
278 279 reqDTO.getOutTransferNo(), response);
279 280 }
280 281 if (Objects.equals(response.getStatus(), "DEALING")) { // 转账到银行卡会出现 "DEALING" 处理中
281   - return PayTransferRespDTO.dealingOf(response.getOrderId(), reqDTO.getOutTransferNo(), response);
  282 + return PayTransferRespDTO.processingOf(response.getOrderId(), reqDTO.getOutTransferNo(), response);
282 283 }
283 284 return PayTransferRespDTO.successOf(response.getOrderId(), parseTime(response.getTransDate()),
284 285 response.getOutBizNo(), response);
... ... @@ -317,7 +318,7 @@ public abstract class AbstractAlipayPayClient extends AbstractPayClient&lt;AlipayPa
317 318 outTradeNo, response);
318 319 }
319 320 if (Objects.equals(response.getStatus(), "DEALING")) { // 转账到银行卡会出现 "DEALING" 处理中
320   - return PayTransferRespDTO.dealingOf(response.getOrderId(), outTradeNo, response);
  321 + return PayTransferRespDTO.processingOf(response.getOrderId(), outTradeNo, response);
321 322 }
322 323 return PayTransferRespDTO.successOf(response.getOrderId(), parseTime(response.getPayDate()),
323 324 response.getOutBizNo(), response);
... ...
yudao-module-pay/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/weixin/AbstractWxPayClient.java
... ... @@ -469,7 +469,7 @@ public abstract class AbstractWxPayClient extends AbstractPayClient&lt;WxPayClientC
469 469 .outDetailNo(reqDTO.getOutTransferNo())
470 470 .transferAmount(reqDTO.getPrice())
471 471 .transferRemark(reqDTO.getSubject())
472   - .openid(reqDTO.getOpenid())
  472 + .openid(reqDTO.getUserAccount())
473 473 .build());
474 474 // TODO @luchi:能不能我们搞个 TransferBatchesRequestX extends TransferBatchesRequest,这样更简洁一点。
475 475 TransferBatchesRequest transferBatches = TransferBatchesRequest.newBuilder()
... ... @@ -484,7 +484,7 @@ public abstract class AbstractWxPayClient extends AbstractPayClient&lt;WxPayClientC
484 484 // 2.1 执行请求
485 485 TransferBatchesResult transferBatchesResult = client.getTransferService().transferBatches(transferBatches);
486 486 // 2.2 创建返回结果
487   - return PayTransferRespDTO.dealingOf(transferBatchesResult.getBatchId(), reqDTO.getOutTransferNo(), transferBatchesResult);
  487 + return PayTransferRespDTO.processingOf(transferBatchesResult.getBatchId(), reqDTO.getOutTransferNo(), transferBatchesResult);
488 488 }
489 489  
490 490 @Override
... ... @@ -509,7 +509,7 @@ public abstract class AbstractWxPayClient extends AbstractPayClient&lt;WxPayClientC
509 509 return PayTransferRespDTO.closedOf(transferBatch.getBatchStatus(), transferBatch.getCloseReason(),
510 510 transferBatch.getOutBatchNo(), response);
511 511 }
512   - return PayTransferRespDTO.dealingOf(transferBatch.getBatchId(), transferBatch.getOutBatchNo(), response);
  512 + return PayTransferRespDTO.processingOf(transferBatch.getBatchId(), transferBatch.getOutBatchNo(), response);
513 513 }
514 514  
515 515 // ========== 各种工具方法 ==========
... ...
yudao-module-pay/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/weixin/WxPubPayClient.java
... ... @@ -4,7 +4,6 @@ import cn.hutool.core.map.MapUtil;
4 4 import cn.hutool.core.util.StrUtil;
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.enums.channel.PayChannelEnum;
8 7 import cn.iocoder.yudao.framework.pay.core.enums.order.PayOrderDisplayModeEnum;
9 8 import com.github.binarywang.wxpay.bean.order.WxPayMpOrderResult;
10 9 import com.github.binarywang.wxpay.bean.request.WxPayUnifiedOrderRequest;
... ... @@ -28,10 +27,6 @@ import static cn.iocoder.yudao.framework.common.util.json.JsonUtils.toJsonString
28 27 @Slf4j
29 28 public class WxPubPayClient extends AbstractWxPayClient {
30 29  
31   - public WxPubPayClient(Long channelId, WxPayClientConfig config) {
32   - super(channelId, PayChannelEnum.WX_PUB.getCode(), config);
33   - }
34   -
35 30 protected WxPubPayClient(Long channelId, String channelCode, WxPayClientConfig config) {
36 31 super(channelId, channelCode, config);
37 32 }
... ...
yudao-module-pay/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/enums/transfer/PayTransferStatusRespEnum.java
... ... @@ -15,18 +15,9 @@ import java.util.Objects;
15 15 public enum PayTransferStatusRespEnum {
16 16  
17 17 WAITING(0, "等待转账"),
18   -
19   - /**
20   - * TODO 转账到银行卡. 会有T+0 T+1 到账的请情况。 还未实现
21   - * TODO @jason:可以看看其它开源项目,针对这个场景,处理策略是怎么样的?例如说,每天主动轮询?这个状态的单子?
22   - */
23   - IN_PROGRESS(10, "转账进行中"),
24   -
25   - SUCCESS(20, "转账成功"),
26   - /**
27   - * 转账关闭 (失败,或者其它情况)
28   - */
29   - CLOSED(30, "转账关闭");
  18 + PROCESSING(5, "转账进行中"),
  19 + SUCCESS(10, "转账成功"),
  20 + CLOSED(20, "转账关闭");
30 21  
31 22 private final Integer status;
32 23 private final String name;
... ... @@ -39,7 +30,8 @@ public enum PayTransferStatusRespEnum {
39 30 return Objects.equals(status, CLOSED.getStatus());
40 31 }
41 32  
42   - public static boolean isInProgress(Integer status) {
43   - return Objects.equals(status, IN_PROGRESS.getStatus());
  33 + public static boolean isProcessing(Integer status) {
  34 + return Objects.equals(status, PROCESSING.getStatus());
44 35 }
  36 +
45 37 }
... ...
yudao-module-pay/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/enums/transfer/PayTransferTypeEnum.java deleted
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.ArrayValuable;
5   -import lombok.AllArgsConstructor;
6   -import lombok.Getter;
7   -
8   -import java.util.Arrays;
9   -
10   -// TODO @芋艿:【转账】这里要不要改成支付平台?
11   -/**
12   - * 转账类型枚举
13   - *
14   - * @author jason
15   - */
16   -@AllArgsConstructor
17   -@Getter
18   -public enum PayTransferTypeEnum implements ArrayValuable<Integer> {
19   -
20   - ALIPAY_BALANCE(1, "支付宝余额"),
21   - WX_BALANCE(2, "微信余额"),
22   - BANK_CARD(3, "银行卡"),
23   - WALLET_BALANCE(4, "钱包余额");
24   -
25   - public interface WxPay {
26   - }
27   -
28   - public interface Alipay {
29   - }
30   -
31   - private final Integer type;
32   - private final String name;
33   -
34   - public static final Integer[] ARRAYS = Arrays.stream(values()).map(PayTransferTypeEnum::getType).toArray(Integer[]::new);
35   -
36   - @Override
37   - public Integer[] array() {
38   - return ARRAYS;
39   - }
40   -
41   - public static PayTransferTypeEnum typeOf(Integer type) {
42   - return ArrayUtil.firstMatch(item -> item.getType().equals(type), values());
43   - }
44   -
45   -}