diff --git a/sql/mysql/pay_wallet.sql b/sql/mysql/pay_wallet.sql index ea27cb9ac..e1c2fa8cd 100644 --- a/sql/mysql/pay_wallet.sql +++ b/sql/mysql/pay_wallet.sql @@ -9,17 +9,18 @@ CREATE TABLE `pay_transfer` `app_id` bigint NOT NULL COMMENT '应用编号', `channel_id` bigint NOT NULL COMMENT '转账渠道编号', `channel_code` varchar(32) NOT NULL COMMENT '转账渠道编码', - `merchant_order_id` varchar(64) NOT NULL COMMENT '商户订单编号', + `merchant_transfer_id` varchar(64) NOT NULL COMMENT '商户转账单编号', `type` int NOT NULL COMMENT '类型', `status` tinyint NOT NULL COMMENT '转账状态', `success_time` datetime NULL COMMENT '转账成功时间', `price` int NOT NULL COMMENT '转账金额,单位:分', `subject` varchar(512) NOT NULL COMMENT '转账标题', `alipay_logon_id` varchar(64) NULL COMMENT '支付宝登录号', - `alipay_account_name` varchar(64) NULL COMMENT '支付宝账号名称', - `openid` varchar(64) NULL COMMENT '微信 openId', - `wx_account_name` varchar(64) NULL COMMENT '微信账号名称', - `notify_url` varchar(64) NULL COMMENT '异步通知商户地址', + `alipay_account_name` varchar(64) NULL COMMENT '支付宝账号名称', + `openid` varchar(64) NULL COMMENT '微信 openId', + `wx_account_name` varchar(64) NULL COMMENT '微信账号名称', + `notify_url` varchar(1024) NOT NULL COMMENT '异步通知商户地址', + `user_ip` varchar(50) NOT NULL COMMENT '用户 IP', `channel_extras` varchar(512) NULL DEFAULT NULL COMMENT '渠道的额外参数', `channel_transfer_no` varchar(64) NULL DEFAULT NULL COMMENT '渠道转账单号', `channel_error_code` varchar(128) NULL DEFAULT NULL COMMENT '调用渠道的错误码', @@ -64,6 +65,7 @@ DROP TABLE IF EXISTS `pay_demo_transfer`; CREATE TABLE `pay_demo_transfer` ( `id` bigint NOT NULL AUTO_INCREMENT COMMENT '订单编号', `user_id` bigint UNSIGNED NOT NULL COMMENT '用户编号', + `app_id` bigint NOT NULL COMMENT '应用编号', `price` int NOT NULL COMMENT '转账金额,单位:分', `type` int NOT NULL COMMENT '转账类型', `alipay_logon_id` varchar(64) NULL COMMENT '支付宝登录号', @@ -81,7 +83,7 @@ CREATE TABLE `pay_demo_transfer` ( `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除', `tenant_id` bigint NOT NULL DEFAULT 0 COMMENT '租户编号', PRIMARY KEY (`id`) USING BTREE -) ENGINE = InnoDB COMMENT = '示例业务转账订单\n'; +) ENGINE = InnoDB COMMENT = '示例业务转账订单'; ALTER TABLE `pay_channel` diff --git a/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/dto/transfer/PayTransferRespDTO.java b/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/dto/transfer/PayTransferRespDTO.java index 400abb510..da6f22774 100644 --- a/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/dto/transfer/PayTransferRespDTO.java +++ b/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/dto/transfer/PayTransferRespDTO.java @@ -29,7 +29,7 @@ public class PayTransferRespDTO { /** * 支付渠道编号 */ - private String channelOrderNo; + private String channelTransferNo; /** * 支付成功时间 @@ -57,7 +57,7 @@ public class PayTransferRespDTO { String outTransferNo, Object rawData) { PayTransferRespDTO respDTO = new PayTransferRespDTO(); respDTO.status = PayTransferStatusRespEnum.WAITING.getStatus(); - respDTO.channelOrderNo = channelOrderNo; + respDTO.channelTransferNo = channelOrderNo; respDTO.outTransferNo = outTransferNo; respDTO.rawData = rawData; return respDTO; @@ -85,7 +85,7 @@ public class PayTransferRespDTO { String outTransferNo, Object rawData) { PayTransferRespDTO respDTO = new PayTransferRespDTO(); respDTO.status = PayTransferStatusRespEnum.SUCCESS.getStatus(); - respDTO.channelOrderNo = channelTransferNo; + respDTO.channelTransferNo = channelTransferNo; respDTO.successTime = successTime; // 相对通用的字段 respDTO.outTransferNo = outTransferNo; diff --git a/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/dto/transfer/PayTransferUnifiedReqDTO.java b/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/dto/transfer/PayTransferUnifiedReqDTO.java index eb61d7ae1..39427b4c4 100644 --- a/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/dto/transfer/PayTransferUnifiedReqDTO.java +++ b/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/dto/transfer/PayTransferUnifiedReqDTO.java @@ -6,6 +6,7 @@ import lombok.Data; import org.hibernate.validator.constraints.Length; import javax.validation.constraints.Min; +import javax.validation.constraints.NotBlank; import javax.validation.constraints.NotEmpty; import javax.validation.constraints.NotNull; import java.util.Map; @@ -50,27 +51,23 @@ public class PayTransferUnifiedReqDTO { @Length(max = 128, message = "转账标题不能超过 128") private String title; - /** - * 收款方信息。 - * - * 转账类型 {@link #type} 不同,收款方信息不同 - */ - @NotEmpty(message = "收款方信息 不能为空") - private Map payeeInfo; /** * 支付宝登录号 */ + @NotBlank(message = "支付宝登录号不能为空", groups = {PayTransferTypeEnum.Alipay.class}) private String alipayLogonId; /** * 支付宝账号名称 */ + @NotBlank(message = "支付宝账号名称不能为空", groups = {PayTransferTypeEnum.Alipay.class}) private String alipayAccountName; /** * 微信 openId */ + @NotBlank(message = "微信 openId 不能为空", groups = {PayTransferTypeEnum.WxPay.class}) private String openid; /** @@ -82,5 +79,4 @@ public class PayTransferUnifiedReqDTO { * 支付渠道的额外参数 */ private Map channelExtras; - } diff --git a/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/AbstractPayClient.java b/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/AbstractPayClient.java index 49114fb5b..472c417e7 100644 --- a/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/AbstractPayClient.java +++ b/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/AbstractPayClient.java @@ -11,6 +11,7 @@ import cn.iocoder.yudao.framework.pay.core.client.dto.refund.PayRefundUnifiedReq import cn.iocoder.yudao.framework.pay.core.client.dto.transfer.PayTransferRespDTO; import cn.iocoder.yudao.framework.pay.core.client.dto.transfer.PayTransferUnifiedReqDTO; import cn.iocoder.yudao.framework.pay.core.client.exception.PayException; +import cn.iocoder.yudao.framework.pay.core.enums.transfer.PayTransferTypeEnum; import lombok.extern.slf4j.Slf4j; import java.util.Map; @@ -185,7 +186,7 @@ public abstract class AbstractPayClient implemen @Override public final PayTransferRespDTO unifiedTransfer(PayTransferUnifiedReqDTO reqDTO) { - ValidationUtils.validate(reqDTO); + validatePayTransferReqDTO(reqDTO); PayTransferRespDTO resp; try{ resp = doUnifiedTransfer(reqDTO); @@ -199,6 +200,22 @@ public abstract class AbstractPayClient implemen } return resp; } + private void validatePayTransferReqDTO(PayTransferUnifiedReqDTO reqDTO) { + PayTransferTypeEnum transferType = PayTransferTypeEnum.typeOf(reqDTO.getType()); + switch (transferType) { + case ALIPAY_BALANCE: { + ValidationUtils.validate(reqDTO, PayTransferTypeEnum.Alipay.class); + break; + } + case WX_BALANCE: { + ValidationUtils.validate(reqDTO, PayTransferTypeEnum.WxPay.class); + break; + } + default: { + throw new UnsupportedOperationException("待实现"); + } + } + } protected abstract PayTransferRespDTO doUnifiedTransfer(PayTransferUnifiedReqDTO reqDTO) throws Throwable; diff --git a/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/alipay/AbstractAlipayPayClient.java b/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/alipay/AbstractAlipayPayClient.java index cb8a0df6e..ceea64739 100644 --- a/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/alipay/AbstractAlipayPayClient.java +++ b/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/alipay/AbstractAlipayPayClient.java @@ -44,6 +44,8 @@ import java.util.function.Supplier; import static cn.hutool.core.date.DatePattern.NORM_DATETIME_FORMATTER; import static cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants.BAD_REQUEST; +import static cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants.NOT_IMPLEMENTED; +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception0; import static cn.iocoder.yudao.framework.pay.core.client.impl.alipay.AlipayPayClientConfig.MODE_CERTIFICATE; @@ -227,9 +229,8 @@ public abstract class AbstractAlipayPayClient extends AbstractPayClient o.getCode().equals(code), values()); } + public static boolean isAlipay(String channelCode) { + return channelCode != null && channelCode.startsWith("alipay"); + } } diff --git a/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/enums/transfer/PayTransferTypeEnum.java b/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/enums/transfer/PayTransferTypeEnum.java index 2de6fd21f..a7580f013 100644 --- a/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/enums/transfer/PayTransferTypeEnum.java +++ b/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/enums/transfer/PayTransferTypeEnum.java @@ -21,8 +21,11 @@ public enum PayTransferTypeEnum implements IntArrayValuable { BANK_CARD(3, "银行卡"), WALLET_BALANCE(4, "钱包余额"); - public static final String ALIPAY_LOGON_ID = "ALIPAY_LOGON_ID"; - public static final String ALIPAY_ACCOUNT_NAME = "ALIPAY_ACCOUNT_NAME"; + public interface WxPay { + } + + public interface Alipay { + } private final Integer type; private final String name; diff --git a/yudao-module-pay/yudao-module-pay-api/src/main/java/cn/iocoder/yudao/module/pay/api/transfer/dto/PayTransferCreateReqDTO.java b/yudao-module-pay/yudao-module-pay-api/src/main/java/cn/iocoder/yudao/module/pay/api/transfer/dto/PayTransferCreateReqDTO.java index 2ea7b25a8..8dea48b70 100644 --- a/yudao-module-pay/yudao-module-pay-api/src/main/java/cn/iocoder/yudao/module/pay/api/transfer/dto/PayTransferCreateReqDTO.java +++ b/yudao-module-pay/yudao-module-pay-api/src/main/java/cn/iocoder/yudao/module/pay/api/transfer/dto/PayTransferCreateReqDTO.java @@ -5,6 +5,7 @@ import cn.iocoder.yudao.module.pay.enums.transfer.PayTransferTypeEnum; import lombok.Data; import javax.validation.constraints.Min; +import javax.validation.constraints.NotBlank; import javax.validation.constraints.NotEmpty; import javax.validation.constraints.NotNull; import java.util.Map; @@ -21,6 +22,17 @@ public class PayTransferCreateReqDTO { @NotNull(message = "应用编号不能为空") private Long appId; + @NotEmpty(message = "转账渠道不能为空") + private String channelCode; + + /** + * 转账渠道的额外参数 + */ + private Map channelExtras; + + @NotEmpty(message = "用户 IP 不能为空") + private String userIp; + /** * 类型 */ @@ -28,11 +40,12 @@ public class PayTransferCreateReqDTO { @InEnum(PayTransferTypeEnum.class) private Integer type; + /** - * 商户订单编号 + * 商户转账单编号 */ - @NotEmpty(message = "商户订单编号不能为空") - private String merchantOrderId; + @NotEmpty(message = "商户转账单编号能为空") + private String merchantTransferId; /** * 转账金额,单位:分 @@ -45,9 +58,21 @@ public class PayTransferCreateReqDTO { * 转账标题 */ @NotEmpty(message = "转账标题不能为空") - private String title; + private String subject; @NotEmpty(message = "收款方信息不能为空") private Map payeeInfo; + @NotBlank(message = "支付宝登录号不能为空", groups = {PayTransferTypeEnum.Alipay.class}) + private String alipayLogonId; + + @NotBlank(message = "支付宝登录号不能为空", groups = {PayTransferTypeEnum.Alipay.class}) + private String alipayAccountName; + + // ========== 微信转账相关字段 ========== + @NotBlank(message = "微信 openId 不能为空", groups = {PayTransferTypeEnum.WxPay.class}) + private String openid; + + private String wxAccountName; + } diff --git a/yudao-module-pay/yudao-module-pay-api/src/main/java/cn/iocoder/yudao/module/pay/enums/ErrorCodeConstants.java b/yudao-module-pay/yudao-module-pay-api/src/main/java/cn/iocoder/yudao/module/pay/enums/ErrorCodeConstants.java index 53c4c1213..9112b111e 100644 --- a/yudao-module-pay/yudao-module-pay-api/src/main/java/cn/iocoder/yudao/module/pay/enums/ErrorCodeConstants.java +++ b/yudao-module-pay/yudao-module-pay-api/src/main/java/cn/iocoder/yudao/module/pay/enums/ErrorCodeConstants.java @@ -67,11 +67,11 @@ public interface ErrorCodeConstants { ErrorCode PAY_TRANSFER_SUBMIT_CHANNEL_ERROR = new ErrorCode(1_007_009_000, "发起转账报错,错误码:{},错误提示:{}"); ErrorCode PAY_TRANSFER_NOT_FOUND = new ErrorCode(1_007_009_001, "转账交易单不存在"); ErrorCode PAY_TRANSFER_STATUS_IS_SUCCESS = new ErrorCode(1_007_009_002, "转账单已成功转账"); - ErrorCode PAY_TRANSFER_STATUS_IS_NOT_WAITING = new ErrorCode(1_007_009_003, "转账单不处于待转账"); - ErrorCode PAY_TRANSFER_STATUS_IS_NOT_PENDING = new ErrorCode(1_007_009_004, "转账单不处于待转账或转账中"); - ErrorCode PAY_TRANSFER_EXTENSION_NOT_FOUND = new ErrorCode(1_007_009_005, "转账交易拓展单不存在"); - ErrorCode PAY_TRANSFER_TYPE_AND_CHANNEL_NOT_MATCH = new ErrorCode(1_007_009_006, "转账类型和转账渠道不匹配"); - ErrorCode PAY_TRANSFER_EXTENSION_STATUS_IS_NOT_PENDING = new ErrorCode(1_007_009_007, "转账拓展单不处于待转账或转账中"); + ErrorCode PAY_TRANSFER_EXISTS = new ErrorCode(1_007_009_003, "已经存在转账单"); + ErrorCode PAY_MERCHANT_TRANSFER_EXISTS = new ErrorCode(1_007_009_004, "该笔业务的转账已经存在,请查询相关状态"); + ErrorCode PAY_TRANSFER_STATUS_IS_NOT_WAITING = new ErrorCode(1_007_009_005, "转账单不处于待转账"); + ErrorCode PAY_TRANSFER_STATUS_IS_NOT_PENDING = new ErrorCode(1_007_009_006, "转账单不处于待转账或转账中"); + ErrorCode PAY_TRANSFER_TYPE_AND_CHANNEL_NOT_MATCH = new ErrorCode(1_007_009_008, "转账类型和转账渠道不匹配"); // ========== 示例订单 1-007-900-000 ========== ErrorCode DEMO_ORDER_NOT_FOUND = new ErrorCode(1_007_900_000, "示例订单不存在"); diff --git a/yudao-module-pay/yudao-module-pay-api/src/main/java/cn/iocoder/yudao/module/pay/enums/transfer/PayTransferStatusEnum.java b/yudao-module-pay/yudao-module-pay-api/src/main/java/cn/iocoder/yudao/module/pay/enums/transfer/PayTransferStatusEnum.java index f3eff1495..d601fad15 100644 --- a/yudao-module-pay/yudao-module-pay-api/src/main/java/cn/iocoder/yudao/module/pay/enums/transfer/PayTransferStatusEnum.java +++ b/yudao-module-pay/yudao-module-pay-api/src/main/java/cn/iocoder/yudao/module/pay/enums/transfer/PayTransferStatusEnum.java @@ -49,7 +49,7 @@ public enum PayTransferStatusEnum { * @param status 状态 */ public static boolean isPendingStatus(Integer status) { - return Objects.equals(status, WAITING.getStatus()) || Objects.equals(status, IN_PROGRESS.status); + return Objects.equals(status, WAITING.getStatus()) || Objects.equals(status, IN_PROGRESS.getStatus()); } } diff --git a/yudao-module-pay/yudao-module-pay-api/src/main/java/cn/iocoder/yudao/module/pay/enums/transfer/PayTransferTypeEnum.java b/yudao-module-pay/yudao-module-pay-api/src/main/java/cn/iocoder/yudao/module/pay/enums/transfer/PayTransferTypeEnum.java index b86b37384..c88151589 100644 --- a/yudao-module-pay/yudao-module-pay-api/src/main/java/cn/iocoder/yudao/module/pay/enums/transfer/PayTransferTypeEnum.java +++ b/yudao-module-pay/yudao-module-pay-api/src/main/java/cn/iocoder/yudao/module/pay/enums/transfer/PayTransferTypeEnum.java @@ -21,8 +21,11 @@ public enum PayTransferTypeEnum implements IntArrayValuable { BANK_CARD(3, "银行卡"), WALLET_BALANCE(4, "钱包余额"); - public static final String ALIPAY_LOGON_ID = "ALIPAY_LOGON_ID"; - public static final String ALIPAY_ACCOUNT_NAME = "ALIPAY_ACCOUNT_NAME"; + public interface WxPay { + } + + public interface Alipay { + } private final Integer type; private final String name; diff --git a/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/controller/admin/demo/vo/transfer/PayDemoTransferCreateReqVO.java b/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/controller/admin/demo/vo/transfer/PayDemoTransferCreateReqVO.java index 35f71ea87..85aae02c5 100644 --- a/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/controller/admin/demo/vo/transfer/PayDemoTransferCreateReqVO.java +++ b/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/controller/admin/demo/vo/transfer/PayDemoTransferCreateReqVO.java @@ -28,13 +28,13 @@ public class PayDemoTransferCreateReqVO { @Min(value = 1, message = "转账金额必须大于零") private Integer price; - // ========== 支付宝,微信转账相关字段 ========== + // ========== 支付宝转账相关字段 ========== @Schema(description = "支付宝登录号,支持邮箱和手机号格式", example = "test1@@sandbox.com") @NotBlank(message = "支付宝登录号不能为空", groups = {Alipay.class}) private String alipayLogonId; @Schema(description = "支付宝账号名称", example = "test1") - @NotBlank(message = "支付宝登录号不能为空", groups = {Alipay.class}) + @NotBlank(message = "支付宝账号名称不能为空", groups = {Alipay.class}) private String alipayAccountName; // ========== 微信转账相关字段 ========== @@ -42,7 +42,7 @@ public class PayDemoTransferCreateReqVO { @NotBlank(message = "微信 openId 不能为空", groups = {WxPay.class}) private String openid; - @Schema(description = "微信账号名称", example = "oLefc4g5Gjxxxxxx") + @Schema(description = "微信账号名称", example = "test2") private String wxAccountName; // ========== 转账到银行卡和钱包相关字段 待补充 ========== diff --git a/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/controller/admin/transfer/PayTransferController.java b/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/controller/admin/transfer/PayTransferController.java index 6d8592058..10bf8f891 100644 --- a/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/controller/admin/transfer/PayTransferController.java +++ b/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/controller/admin/transfer/PayTransferController.java @@ -1,8 +1,9 @@ package cn.iocoder.yudao.module.pay.controller.admin.transfer; import cn.iocoder.yudao.framework.common.pojo.CommonResult; -import cn.iocoder.yudao.module.pay.controller.admin.transfer.vo.PayTransferSubmitReqVO; -import cn.iocoder.yudao.module.pay.controller.admin.transfer.vo.PayTransferSubmitRespVO; +import cn.iocoder.yudao.module.pay.controller.admin.transfer.vo.PayTransferCreateReqVO; +import cn.iocoder.yudao.module.pay.controller.admin.transfer.vo.PayTransferCreateRespVO; +import cn.iocoder.yudao.module.pay.dal.dataobject.transfer.PayTransferDO; import cn.iocoder.yudao.module.pay.service.transfer.PayTransferService; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.tags.Tag; @@ -27,12 +28,12 @@ public class PayTransferController { @Resource private PayTransferService payTransferService; - @PostMapping("/submit") - @Operation(summary = "提交转账订单") + @PostMapping("/initiate") + @Operation(summary = "发起转账") // TODO @jason:权限的设置, 管理后台页面加的时候加一下 - public CommonResult submitPayTransfer(@Valid @RequestBody PayTransferSubmitReqVO reqVO) { - PayTransferSubmitRespVO respVO = payTransferService.submitTransfer(reqVO, getClientIP()); - return success(respVO); + public CommonResult initiatePayTransfer(@Valid @RequestBody PayTransferCreateReqVO reqVO) { + PayTransferDO payTransfer = payTransferService.initiateTransfer(reqVO, getClientIP()); + return success(new PayTransferCreateRespVO().setId(payTransfer.getId()).setStatus(payTransfer.getStatus())); } } diff --git a/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/controller/admin/transfer/vo/PayTransferCreateReqVO.java b/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/controller/admin/transfer/vo/PayTransferCreateReqVO.java new file mode 100644 index 000000000..87e49da98 --- /dev/null +++ b/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/controller/admin/transfer/vo/PayTransferCreateReqVO.java @@ -0,0 +1,81 @@ +package cn.iocoder.yudao.module.pay.controller.admin.transfer.vo; + +import cn.iocoder.yudao.framework.common.util.validation.ValidationUtils; +import cn.iocoder.yudao.framework.common.validation.InEnum; +import cn.iocoder.yudao.module.pay.controller.admin.demo.vo.transfer.PayDemoTransferCreateReqVO; +import cn.iocoder.yudao.module.pay.enums.transfer.PayTransferTypeEnum; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import javax.validation.Validator; +import javax.validation.constraints.Min; +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.NotEmpty; +import javax.validation.constraints.NotNull; +import java.util.Map; + +@Schema(description = "管理后台 - 发起转账 Request VO") +@Data +public class PayTransferCreateReqVO { + + @Schema(description = "应用编号不能为空", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + @NotNull(message = "应用编号不能为空") + private Long appId; + + @Schema(description = "商户转账单编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + @NotNull(message = "商户转账单编号不能为空") + private String merchantTransferId; + + @Schema(description = "转账类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + @NotNull(message = "转账类型不能为空") + @InEnum(PayTransferTypeEnum.class) + private Integer type; + + @Schema(description = "转账渠道", requiredMode = Schema.RequiredMode.REQUIRED, example = "alipay_pc") + @NotEmpty(message = "转账渠道不能为空") + private String channelCode; + + @Min(value = 1, message = "转账金额必须大于零") + @NotNull(message = "转账金额不能为空") + private Integer price; + + @Schema(description = "转账标题", requiredMode = Schema.RequiredMode.REQUIRED, example = "示例转账") + @NotEmpty(message = "转账标题不能为空") + private String subject; + + @Schema(description = "支付宝登录号", example = "test1@sandbox.com") + @NotBlank(message = "支付宝登录号不能为空", groups = {PayTransferTypeEnum.Alipay.class}) + private String alipayLogonId; + + @Schema(description = "支付宝账号名称", example = "test1") + @NotBlank(message = "支付宝账号名称不能为空", groups = {PayTransferTypeEnum.Alipay.class}) + private String alipayAccountName; + + @Schema(description = "微信 openId", example = "oLefc4g5Gxx") + @NotBlank(message = "微信 openId 不能为空", groups = {PayTransferTypeEnum.WxPay.class}) + private String openid; + + @Schema(description = "微信账号名称", example = "test2") + private String wxAccountName; + + @Schema(description = "转账渠道的额外参数") + private Map channelExtras; + + public void validate(Validator validator) { + PayTransferTypeEnum transferType = PayTransferTypeEnum.typeOf(type); + switch (transferType) { + case ALIPAY_BALANCE: { + ValidationUtils.validate(validator, this, PayDemoTransferCreateReqVO.Alipay.class); + break; + } + case WX_BALANCE: { + ValidationUtils.validate(validator, this, PayDemoTransferCreateReqVO.WxPay.class); + break; + } + default: { + throw new UnsupportedOperationException("待实现"); + } + } + } + +} diff --git a/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/controller/admin/transfer/vo/PayTransferSubmitRespVO.java b/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/controller/admin/transfer/vo/PayTransferCreateRespVO.java similarity index 57% rename from yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/controller/admin/transfer/vo/PayTransferSubmitRespVO.java rename to yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/controller/admin/transfer/vo/PayTransferCreateRespVO.java index fef296f09..9cb44bf60 100644 --- a/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/controller/admin/transfer/vo/PayTransferSubmitRespVO.java +++ b/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/controller/admin/transfer/vo/PayTransferCreateRespVO.java @@ -3,9 +3,12 @@ package cn.iocoder.yudao.module.pay.controller.admin.transfer.vo; import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; -@Schema(description = "管理后台 - 转账单提交 Response VO") +@Schema(description = "管理后台 - 发起转账 Response VO") @Data -public class PayTransferSubmitRespVO { +public class PayTransferCreateRespVO { + + @Schema(description = "转账单编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + private Long id; @Schema(description = "转账状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") // 参见 PayTransferStatusEnum 枚举 private Integer status; diff --git a/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/controller/admin/transfer/vo/PayTransferSubmitReqVO.java b/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/controller/admin/transfer/vo/PayTransferSubmitReqVO.java deleted file mode 100644 index 36e67ce01..000000000 --- a/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/controller/admin/transfer/vo/PayTransferSubmitReqVO.java +++ /dev/null @@ -1,25 +0,0 @@ -package cn.iocoder.yudao.module.pay.controller.admin.transfer.vo; - -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; - -import javax.validation.constraints.NotEmpty; -import javax.validation.constraints.NotNull; -import java.util.Map; - -@Schema(description = "管理后台 - 转账单提交 Request VO") -@Data -public class PayTransferSubmitReqVO { - - @Schema(description = "转账单编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") - @NotNull(message = "转账单编号不能为空") - private Long id; - - @Schema(description = "转账渠道", requiredMode = Schema.RequiredMode.REQUIRED, example = "alipay_transfer") - @NotEmpty(message = "转账渠道不能为空") - private String channelCode; - - @Schema(description = "转账渠道的额外参数") - private Map channelExtras; - -} diff --git a/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/convert/transfer/PayTransferConvert.java b/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/convert/transfer/PayTransferConvert.java index 440f0103b..a570c254f 100644 --- a/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/convert/transfer/PayTransferConvert.java +++ b/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/convert/transfer/PayTransferConvert.java @@ -1,7 +1,9 @@ package cn.iocoder.yudao.module.pay.convert.transfer; +import cn.iocoder.yudao.framework.pay.core.client.dto.transfer.PayTransferUnifiedReqDTO; import cn.iocoder.yudao.module.pay.api.transfer.dto.PayTransferCreateReqDTO; import cn.iocoder.yudao.module.pay.controller.admin.demo.vo.transfer.PayDemoTransferCreateReqVO; +import cn.iocoder.yudao.module.pay.controller.admin.transfer.vo.PayTransferCreateReqVO; import cn.iocoder.yudao.module.pay.dal.dataobject.transfer.PayTransferDO; import org.mapstruct.Mapper; import org.mapstruct.Mapping; @@ -12,9 +14,13 @@ public interface PayTransferConvert { PayTransferConvert INSTANCE = Mappers.getMapper(PayTransferConvert.class); - @Mapping(source = "title", target = "subject") // TODO @jason:是不是都改成 subject 完事呀? PayTransferDO convert(PayTransferCreateReqDTO dto); + @Mapping(source = "subject", target = "title") + PayTransferUnifiedReqDTO convert2(PayTransferCreateReqDTO dto); + + PayTransferCreateReqDTO convert(PayTransferCreateReqVO vo); + PayTransferCreateReqDTO convert(PayDemoTransferCreateReqVO vo); } diff --git a/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/convert/wallet/PayWalletConvert.java b/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/convert/wallet/PayWalletConvert.java index e162b88bc..5a003a22e 100644 --- a/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/convert/wallet/PayWalletConvert.java +++ b/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/convert/wallet/PayWalletConvert.java @@ -22,14 +22,10 @@ public interface PayWalletConvert { PageResult convertPage(PageResult page); - default PageResult convertPage(PageResult page, Map userMap){ + default PageResult convertPage(PageResult page, Map userMap) { PageResult pageResult = convertPage(page); - pageResult.getList().forEach( wallet -> MapUtils.findAndThen(userMap, wallet.getUserId(), - user -> { - // TODO @jason:可以链式调用哈; - wallet.setNickname(user.getNickname()); - wallet.setAvatar(user.getAvatar()); - })); + pageResult.getList().forEach(wallet -> MapUtils.findAndThen(userMap, wallet.getUserId(), + user -> wallet.setNickname(user.getNickname()).setAvatar(user.getAvatar()))); return pageResult; } diff --git a/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/dal/dataobject/demo/PayDemoTransferDO.java b/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/dal/dataobject/demo/PayDemoTransferDO.java index 1d25d233e..f3b2a6c9c 100644 --- a/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/dal/dataobject/demo/PayDemoTransferDO.java +++ b/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/dal/dataobject/demo/PayDemoTransferDO.java @@ -2,6 +2,7 @@ package cn.iocoder.yudao.module.pay.dal.dataobject.demo; import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; import cn.iocoder.yudao.framework.pay.core.enums.transfer.PayTransferTypeEnum; +import cn.iocoder.yudao.module.pay.dal.dataobject.app.PayAppDO; import com.baomidou.mybatisplus.annotation.KeySequence; import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableName; @@ -30,6 +31,13 @@ public class PayDemoTransferDO extends BaseDO { */ private Long userId; + /** + * 应用编号 + * + * 关联 {@link PayAppDO#getId()} + */ + private Long appId; + /** * 转账金额,单位:分 */ diff --git a/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/dal/dataobject/transfer/PayTransferDO.java b/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/dal/dataobject/transfer/PayTransferDO.java index 37758e38d..6f730c74a 100644 --- a/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/dal/dataobject/transfer/PayTransferDO.java +++ b/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/dal/dataobject/transfer/PayTransferDO.java @@ -61,11 +61,11 @@ public class PayTransferDO extends BaseDO { // ========== 商户相关字段 ========== /** - * 商户订单编号 + * 商户转账单编号 * * 例如说,内部系统 A 的订单号,需要保证每个 PayAppDO 唯一 */ - private String merchantOrderId; + private String merchantTransferId; // ========== 转账相关字段 ========== diff --git a/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/dal/mysql/transfer/PayTransferMapper.java b/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/dal/mysql/transfer/PayTransferMapper.java index b0bb9ad43..019054d10 100644 --- a/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/dal/mysql/transfer/PayTransferMapper.java +++ b/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/dal/mysql/transfer/PayTransferMapper.java @@ -15,6 +15,14 @@ public interface PayTransferMapper extends BaseMapperX { .eq(PayTransferDO::getId, id).in(PayTransferDO::getStatus, status)); } + default PayTransferDO selectByAppIdAndMerchantTransferId(Long appId, String merchantTransferId){ + return selectOne(PayTransferDO::getAppId, appId, + PayTransferDO::getMerchantTransferId, merchantTransferId); + } + + default PayTransferDO selectByNo(String no){ + return selectOne(PayTransferDO::getNo, no); + } } diff --git a/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/demo/PayDemoTransferServiceImpl.java b/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/demo/PayDemoTransferServiceImpl.java index 25f96ff82..343c7762a 100644 --- a/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/demo/PayDemoTransferServiceImpl.java +++ b/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/demo/PayDemoTransferServiceImpl.java @@ -4,7 +4,6 @@ import cn.iocoder.yudao.module.pay.controller.admin.demo.vo.transfer.PayDemoTran import cn.iocoder.yudao.module.pay.convert.demo.PayDemoTransferConvert; import cn.iocoder.yudao.module.pay.dal.dataobject.demo.PayDemoTransferDO; import cn.iocoder.yudao.module.pay.dal.mysql.demo.PayDemoTransferMapper; -import cn.iocoder.yudao.module.pay.service.transfer.PayTransferService; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.springframework.validation.annotation.Validated; @@ -33,8 +32,6 @@ public class PayDemoTransferServiceImpl implements PayDemoTransferService { @Resource private PayDemoTransferMapper demoTransferMapper; @Resource - private PayTransferService transferService; - @Resource private Validator validator; @Override @@ -44,7 +41,7 @@ public class PayDemoTransferServiceImpl implements PayDemoTransferService { vo.validate(validator); // 2 保存示例转账业务表 PayDemoTransferDO demoTransfer = PayDemoTransferConvert.INSTANCE.convert(vo) - .setUserId(userId).setTransferStatus(WAITING.getStatus()); + .setUserId(userId).setAppId(TRANSFER_APP_ID).setTransferStatus(WAITING.getStatus()); demoTransferMapper.insert(demoTransfer); return demoTransfer.getId(); } diff --git a/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/transfer/PayTransferService.java b/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/transfer/PayTransferService.java index 0da38c640..cf4aeae2f 100644 --- a/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/transfer/PayTransferService.java +++ b/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/transfer/PayTransferService.java @@ -1,8 +1,8 @@ package cn.iocoder.yudao.module.pay.service.transfer; import cn.iocoder.yudao.module.pay.api.transfer.dto.PayTransferCreateReqDTO; -import cn.iocoder.yudao.module.pay.controller.admin.transfer.vo.PayTransferSubmitReqVO; -import cn.iocoder.yudao.module.pay.controller.admin.transfer.vo.PayTransferSubmitRespVO; +import cn.iocoder.yudao.module.pay.controller.admin.transfer.vo.PayTransferCreateReqVO; +import cn.iocoder.yudao.module.pay.dal.dataobject.transfer.PayTransferDO; import javax.validation.Valid; @@ -14,7 +14,7 @@ import javax.validation.Valid; public interface PayTransferService { /** - * 提交转账单 + * 发起转账 * * 此时,会发起支付渠道的调用 * @@ -22,7 +22,7 @@ public interface PayTransferService { * @param userIp 用户 ip * @return 渠道的返回结果 */ - PayTransferSubmitRespVO submitTransfer(@Valid PayTransferSubmitReqVO reqVO, String userIp); + PayTransferDO initiateTransfer(@Valid PayTransferCreateReqVO reqVO, String userIp); /** * 创建转账单 @@ -32,4 +32,10 @@ public interface PayTransferService { */ Long createTransfer(@Valid PayTransferCreateReqDTO reqDTO); + /** + * 获取转账单 + * @param id 转账单编号 + */ + PayTransferDO getTransfer(Long id); + } diff --git a/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/transfer/PayTransferServiceImpl.java b/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/transfer/PayTransferServiceImpl.java index e08992df2..95738d1b7 100644 --- a/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/transfer/PayTransferServiceImpl.java +++ b/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/transfer/PayTransferServiceImpl.java @@ -1,23 +1,20 @@ package cn.iocoder.yudao.module.pay.service.transfer; import cn.hutool.core.collection.CollUtil; -import cn.hutool.core.util.StrUtil; import cn.hutool.extra.spring.SpringUtil; import cn.iocoder.yudao.framework.common.util.json.JsonUtils; import cn.iocoder.yudao.framework.pay.core.client.PayClient; import cn.iocoder.yudao.framework.pay.core.client.dto.transfer.PayTransferRespDTO; import cn.iocoder.yudao.framework.pay.core.client.dto.transfer.PayTransferUnifiedReqDTO; +import cn.iocoder.yudao.framework.pay.core.client.exception.PayException; import cn.iocoder.yudao.framework.pay.core.enums.channel.PayChannelEnum; import cn.iocoder.yudao.framework.pay.core.enums.transfer.PayTransferStatusRespEnum; import cn.iocoder.yudao.framework.pay.core.enums.transfer.PayTransferTypeEnum; import cn.iocoder.yudao.module.pay.api.transfer.dto.PayTransferCreateReqDTO; -import cn.iocoder.yudao.module.pay.controller.admin.transfer.vo.PayTransferSubmitReqVO; -import cn.iocoder.yudao.module.pay.controller.admin.transfer.vo.PayTransferSubmitRespVO; -import cn.iocoder.yudao.module.pay.convert.transfer.PayTransferConvert; +import cn.iocoder.yudao.module.pay.controller.admin.transfer.vo.PayTransferCreateReqVO; +import cn.iocoder.yudao.module.pay.dal.dataobject.app.PayAppDO; import cn.iocoder.yudao.module.pay.dal.dataobject.channel.PayChannelDO; import cn.iocoder.yudao.module.pay.dal.dataobject.transfer.PayTransferDO; -import cn.iocoder.yudao.module.pay.dal.dataobject.transfer.PayTransferExtensionDO; -import cn.iocoder.yudao.module.pay.dal.mysql.transfer.PayTransferExtensionMapper; import cn.iocoder.yudao.module.pay.dal.mysql.transfer.PayTransferMapper; import cn.iocoder.yudao.module.pay.dal.redis.no.PayNoRedisDAO; import cn.iocoder.yudao.module.pay.enums.transfer.PayTransferStatusEnum; @@ -28,8 +25,10 @@ import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import javax.annotation.Resource; +import javax.validation.Validator; import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; +import static cn.iocoder.yudao.module.pay.convert.transfer.PayTransferConvert.INSTANCE; import static cn.iocoder.yudao.module.pay.enums.ErrorCodeConstants.*; import static cn.iocoder.yudao.module.pay.enums.transfer.PayTransferStatusEnum.*; @@ -47,63 +46,72 @@ public class PayTransferServiceImpl implements PayTransferService { @Resource private PayTransferMapper transferMapper; - @Resource - private PayTransferExtensionMapper transferExtensionMapper; - @Resource private PayAppService appService; @Resource private PayChannelService channelService; - @Resource private PayNoRedisDAO noRedisDAO; + @Resource + private Validator validator; @Override - public PayTransferSubmitRespVO submitTransfer(PayTransferSubmitReqVO reqVO, String userIp) { - // 1.1 校验转账单是否可以提交 - PayTransferDO transfer = validateTransferCanSubmit(reqVO.getId()); - // 1.2 校验转账类型和渠道是否匹配 - validateChannelCodeAndTypeMatch(reqVO.getChannelCode(), transfer.getType()); - // 1.3 校验支付渠道是否有效 - PayChannelDO channel = validateChannelCanSubmit(transfer.getAppId(), reqVO.getChannelCode()); - PayClient client = channelService.getPayClient(channel.getId()); - - // 2. 新增转账拓展单 - String no = noRedisDAO.generate(TRANSFER_NO_PREFIX); - PayTransferExtensionDO transferExtension = new PayTransferExtensionDO().setNo(no) - .setTransferId(transfer.getId()).setChannelId(channel.getId()) - .setChannelCode(channel.getCode()).setStatus(WAITING.getStatus()); - transferExtensionMapper.insert(transferExtension); - - // 3. 调用三方渠道发起转账 - PayTransferUnifiedReqDTO transferUnifiedReq = new PayTransferUnifiedReqDTO() - .setOutTransferNo(transferExtension.getNo()).setPrice(transfer.getPrice()) - .setType(transfer.getType()).setTitle(transfer.getSubject()) - .setUserIp(userIp) - .setChannelExtras(reqVO.getChannelExtras()); - PayTransferRespDTO unifiedTransferResp = client.unifiedTransfer(transferUnifiedReq); - - // 4. 通知转账结果 - getSelf().notifyTransfer(channel, unifiedTransferResp); - // 如有渠道错误码,则抛出业务异常,提示用户 - if (StrUtil.isNotEmpty(unifiedTransferResp.getChannelErrorCode())) { - throw exception(PAY_TRANSFER_SUBMIT_CHANNEL_ERROR, unifiedTransferResp.getChannelErrorCode(), - unifiedTransferResp.getChannelErrorMsg()); - } - return new PayTransferSubmitRespVO().setStatus(unifiedTransferResp.getStatus()); + @Transactional(rollbackFor = Exception.class) + public PayTransferDO initiateTransfer(PayTransferCreateReqVO reqVO, String userIp) { + // 1.1 校验参数 + reqVO.validate(validator); + // todo 使用 AssertTrue + validateChannelCodeAndTypeMatch(reqVO.getChannelCode(), reqVO.getType()); + // 1.2 校验转账单是否可以创建 + validateTransferCanCreate(reqVO.getAppId(), reqVO.getMerchantTransferId()); + // 2. 创建转账单,发起转账 + PayTransferCreateReqDTO req = INSTANCE.convert(reqVO).setUserIp(userIp); + Long transferId = createTransfer(req); + // 3. 返回转账单 + return getTransfer(transferId); } @Override + @Transactional(rollbackFor = Exception.class) public Long createTransfer(PayTransferCreateReqDTO reqDTO) { - // 校验 App - appService.validPayApp(reqDTO.getAppId()); - // 创建转账单 - PayTransferDO transfer = PayTransferConvert.INSTANCE.convert(reqDTO) - .setStatus(WAITING.getStatus()); + // 1.1 校验转账单是否可以提交 + validateTransferExist(reqDTO.getAppId(), reqDTO.getMerchantTransferId()); + // 1.2 校验 App + PayAppDO appDO = appService.validPayApp(reqDTO.getAppId()); + // 1.3 校验支付渠道是否有效 + PayChannelDO channel = channelService.validPayChannel(reqDTO.getAppId(), reqDTO.getChannelCode()); + PayClient client = channelService.getPayClient(channel.getId()); + if (client == null) { + log.error("[createTransfer][渠道编号({}) 找不到对应的支付客户端]", channel.getId()); + throw exception(CHANNEL_NOT_FOUND); + } + // 2.创建转账单 + String no = noRedisDAO.generate(TRANSFER_NO_PREFIX); + PayTransferDO transfer = INSTANCE.convert(reqDTO) + .setChannelId(channel.getId()) + .setNo(no).setStatus(WAITING.getStatus()) + .setNotifyUrl(appDO.getRefundNotifyUrl()); // TODO 需要加个transfer Notify url transferMapper.insert(transfer); + try { + // 3. 调用三方渠道发起转账 + PayTransferUnifiedReqDTO transferUnifiedReq = INSTANCE.convert2(reqDTO) + .setOutTransferNo(no); + PayTransferRespDTO unifiedTransferResp = client.unifiedTransfer(transferUnifiedReq); + // 4. 通知转账结果 + getSelf().notifyTransfer(channel, unifiedTransferResp); + } catch (PayException e) { + // 这里仅打印异常,不进行抛出。 + // TODO 说明 + log.error("[createTransfer][转账 id({}) requestDTO({}) 发生异常]", transfer.getId(), reqDTO, e); + } return transfer.getId(); } + @Override + public PayTransferDO getTransfer(Long id) { + return transferMapper.selectById(id); + } + @Transactional(rollbackFor = Exception.class) // 注意,如果是方法内调用该方法,需要通过 getSelf().notifyTransfer(channel, notify) 调用,否则事务不生效 public void notifyTransfer(PayChannelDO channel, PayTransferRespDTO notify) { @@ -119,26 +127,38 @@ public class PayTransferServiceImpl implements PayTransferService { // TODO IN_PROGRESS 待处理 } - private void notifyTransferSuccess(PayChannelDO channel, PayTransferRespDTO notify) { - // 1. 更新 PayTransferExtensionDO 转账成功 - PayTransferExtensionDO transferExtension = updateTransferExtensionSuccess(notify); + private void validateTransferExist(Long appId, String merchantTransferId ) { + PayTransferDO transfer = transferMapper.selectByAppIdAndMerchantTransferId(appId, merchantTransferId); + if (transfer != null) { // 是否存在 + throw exception(PAY_TRANSFER_EXISTS); + } + } - // 2. 更新 PayTransferDO 转账成功 - Boolean transferred = updateTransferSuccess(channel,transferExtension, notify); + private void validateTransferCanCreate(Long appId, String merchantTransferId ) { + PayTransferDO transfer = transferMapper.selectByAppIdAndMerchantTransferId(appId, merchantTransferId); + if (transfer != null) { // 是否存在 + throw exception(PAY_MERCHANT_TRANSFER_EXISTS); + } + } + + private void notifyTransferSuccess(PayChannelDO channel, PayTransferRespDTO notify) { + // 1. 更新 PayTransferDO 转账成功 + Boolean transferred = updateTransferSuccess(channel, notify); if (transferred) { return; } - // 3. TODO 插入转账通知记录 + // 2. TODO 插入转账通知记录 } - private Boolean updateTransferSuccess(PayChannelDO channel, PayTransferExtensionDO transferExtension, - PayTransferRespDTO notify) { + private Boolean updateTransferSuccess(PayChannelDO channel, PayTransferRespDTO notify) { // 1.校验 - PayTransferDO transfer = transferMapper.selectById(transferExtension.getTransferId()); + PayTransferDO transfer = transferMapper.selectByNo(notify.getOutTransferNo()); if (transfer == null) { throw exception(PAY_TRANSFER_NOT_FOUND); } - + if (isSuccess(transfer.getStatus())) { // 如果已成功,直接返回,不用重复更新 + return Boolean.TRUE; + } if (!isPendingStatus(transfer.getStatus())) { throw exception(PAY_TRANSFER_STATUS_IS_NOT_PENDING); } @@ -146,76 +166,52 @@ public class PayTransferServiceImpl implements PayTransferService { int updateCounts = transferMapper.updateByIdAndStatus(transfer.getId(), CollUtil.newArrayList(WAITING.getStatus(), IN_PROGRESS.getStatus()), new PayTransferDO().setStatus(SUCCESS.getStatus()).setSuccessTime(notify.getSuccessTime()) + .setChannelTransferNo(notify.getChannelTransferNo()) .setChannelId(channel.getId()).setChannelCode(channel.getCode()) - .setNo(transferExtension.getNo())); + .setChannelNotifyData(JsonUtils.toJsonString(notify))); if (updateCounts == 0) { throw exception(PAY_TRANSFER_STATUS_IS_NOT_PENDING); } log.info("[updateTransferSuccess][transfer({}) 更新为已转账]", transfer.getId()); - return false; + return Boolean.FALSE; } - private PayTransferExtensionDO updateTransferExtensionSuccess(PayTransferRespDTO notify) { - // 1 校验 - PayTransferExtensionDO transferExtension = transferExtensionMapper.selectByNo(notify.getOutTransferNo()); - if (transferExtension == null) { - throw exception(PAY_TRANSFER_EXTENSION_NOT_FOUND); + private void updateTransferClosed(PayChannelDO channel, PayTransferRespDTO notify) { + // 1.校验 + PayTransferDO transfer = transferMapper.selectByNo(notify.getOutTransferNo()); + if (transfer == null) { + throw exception(PAY_TRANSFER_NOT_FOUND); } - if (isSuccess(transferExtension.getStatus())) { // 如果已成功,直接返回,不用重复更新 - log.info("[updateTransferExtensionSuccess][transferExtension({}) 已经是成功状态,无需更新]", transferExtension.getId()); - return transferExtension; + if (isClosed(transfer.getStatus())) { // 如果已是关闭状态,直接返回,不用重复更新 + log.info("[updateTransferClosed][transfer({}) 已经是关闭状态,无需更新]", transfer.getId()); + return; } - if (!isPendingStatus(transferExtension.getStatus())) { - throw exception(PAY_TRANSFER_EXTENSION_STATUS_IS_NOT_PENDING); + if (!isPendingStatus(transfer.getStatus())) { + throw exception(PAY_TRANSFER_STATUS_IS_NOT_PENDING); } - // 2. 更新 PayTransferExtensionDO - int updateCount = transferExtensionMapper.updateByIdAndStatus(transferExtension.getId(), + // 2.更新 + int updateCount = transferMapper.updateByIdAndStatus(transfer.getId(), CollUtil.newArrayList(WAITING.getStatus(), IN_PROGRESS.getStatus()), - new PayTransferExtensionDO().setStatus(SUCCESS.getStatus()) + new PayTransferDO().setStatus(CLOSED.getStatus()) .setChannelId(channel.getId()) + .setChannelCode(channel.getCode()).setChannelTransferNo(notify.getChannelTransferNo()) + .setChannelErrorCode(notify.getChannelErrorCode()).setChannelErrorMsg(notify.getChannelErrorMsg()) .setChannelNotifyData(JsonUtils.toJsonString(notify))); if (updateCount == 0) { - throw exception(PAY_TRANSFER_EXTENSION_STATUS_IS_NOT_PENDING); + throw exception(PAY_TRANSFER_STATUS_IS_NOT_PENDING); } - log.info("[updateTransferExtensionSuccess][transferExtension({}) 更新为已转账]", transferExtension.getId()); - return transferExtension; + log.info("[updateTransferClosed][transfer({}) 更新为关闭状态]", transfer.getId()); } private void notifyTransferClosed(PayChannelDO channel, PayTransferRespDTO notify) { - // 更新 PayTransferExtensionDO 转账关闭 - updateTransferExtensionClosed(notify); - } - - private void updateTransferExtensionClosed(PayTransferRespDTO notify) { - // 1 校验 - PayTransferExtensionDO transferExtension = transferExtensionMapper.selectByNo(notify.getOutTransferNo()); - if (transferExtension == null) { - throw exception(PAY_TRANSFER_EXTENSION_NOT_FOUND); - } - if (isClosed(transferExtension.getStatus())) { // 如果已是关闭状态,直接返回,不用重复更新 - log.info("[updateTransferExtensionSuccess][transferExtension({}) 已经是关闭状态,无需更新]", transferExtension.getId()); - return; - } - if (!isPendingStatus(transferExtension.getStatus())) { - throw exception(PAY_TRANSFER_EXTENSION_STATUS_IS_NOT_PENDING); - } - // 2. 更新 PayTransferExtensionDO - int updateCount = transferExtensionMapper.updateByIdAndStatus(transferExtension.getId(), - CollUtil.newArrayList(WAITING.getStatus(), IN_PROGRESS.getStatus()), - new PayTransferExtensionDO().setStatus(CLOSED.getStatus()) - .setChannelNotifyData(JsonUtils.toJsonString(notify))); - if (updateCount == 0) { - throw exception(PAY_TRANSFER_EXTENSION_STATUS_IS_NOT_PENDING); - } - log.info("[updateTransferExtensionSuccess][transferExtension({}) 更新为关闭状态]", transferExtension.getId()); + // 更新 PayTransferDO 转账关闭 + updateTransferClosed(channel,notify); } private void validateChannelCodeAndTypeMatch(String channelCode, Integer type) { PayTransferTypeEnum transferType = PayTransferTypeEnum.typeOf(type); - PayChannelEnum payChannel = PayChannelEnum.getByCode(channelCode); switch (transferType) { case ALIPAY_BALANCE: { - // TODO @jason:可以抽到 PayChannelEnum 里,isAlipay? 类似这种哈 - if (!payChannel.getCode().startsWith("alipay")) { + if (!PayChannelEnum.isAlipay(channelCode)) { throw exception(PAY_TRANSFER_TYPE_AND_CHANNEL_NOT_MATCH); } break; @@ -241,7 +237,7 @@ public class PayTransferServiceImpl implements PayTransferService { return channel; } - private PayTransferDO validateTransferCanSubmit(Long id) { + private PayTransferDO validateTransferExist(Long id) { PayTransferDO transfer = transferMapper.selectById(id); if (transfer == null) { // 是否存在 throw exception(PAY_TRANSFER_NOT_FOUND);