【功能优化】支付:支付应用,增加 appKey 标识,用于不同接入方的标识
【更多】同步 boot 最新代码到 cloud
This commit is contained in:
@@ -18,10 +18,10 @@ public class PayOrderCreateReqDTO implements Serializable {
|
||||
public static final int SUBJECT_MAX_LENGTH = 32;
|
||||
|
||||
/**
|
||||
* 应用编号
|
||||
* 应用标识
|
||||
*/
|
||||
@NotNull(message = "应用编号不能为空")
|
||||
private Long appId;
|
||||
@NotNull(message = "应用标识不能为空")
|
||||
private String appKey;
|
||||
/**
|
||||
* 用户 IP
|
||||
*/
|
||||
|
||||
@@ -15,10 +15,10 @@ import org.hibernate.validator.constraints.Length;
|
||||
public class PayRefundCreateReqDTO {
|
||||
|
||||
/**
|
||||
* 应用编号
|
||||
* 应用标识
|
||||
*/
|
||||
@NotNull(message = "应用编号不能为空")
|
||||
private Long appId;
|
||||
@NotNull(message = "应用标识不能为空")
|
||||
private String appKey;
|
||||
/**
|
||||
* 用户 IP
|
||||
*/
|
||||
|
||||
@@ -19,10 +19,10 @@ import java.util.Map;
|
||||
public class PayTransferCreateReqDTO {
|
||||
|
||||
/**
|
||||
* 应用编号
|
||||
* 应用标识
|
||||
*/
|
||||
@NotNull(message = "应用编号不能为空")
|
||||
private Long appId;
|
||||
@NotNull(message = "应用标识不能为空")
|
||||
private String appKey;
|
||||
|
||||
@NotEmpty(message = "转账渠道不能为空")
|
||||
private String channelCode;
|
||||
|
||||
@@ -14,6 +14,7 @@ public interface ErrorCodeConstants {
|
||||
ErrorCode APP_IS_DISABLE = new ErrorCode(1_007_000_002, "App 已经被禁用");
|
||||
ErrorCode APP_EXIST_ORDER_CANT_DELETE = new ErrorCode(1_007_000_003, "支付应用存在支付订单,无法删除");
|
||||
ErrorCode APP_EXIST_REFUND_CANT_DELETE = new ErrorCode(1_007_000_004, "支付应用存在退款订单,无法删除");
|
||||
ErrorCode APP_KEY_EXISTS = new ErrorCode(1_007_000_005, "支付应用标识已经存在");
|
||||
|
||||
// ========== CHANNEL 模块 1-007-001-000 ==========
|
||||
ErrorCode CHANNEL_NOT_FOUND = new ErrorCode(1_007_001_000, "支付渠道的配置不存在");
|
||||
|
||||
@@ -3,6 +3,7 @@ package cn.iocoder.yudao.module.pay.controller.admin.app.vo;
|
||||
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
|
||||
import cn.iocoder.yudao.framework.common.validation.InEnum;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import jakarta.validation.constraints.NotEmpty;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import lombok.Data;
|
||||
import org.hibernate.validator.constraints.URL;
|
||||
@@ -14,6 +15,10 @@ import org.hibernate.validator.constraints.URL;
|
||||
@Data
|
||||
public class PayAppBaseVO {
|
||||
|
||||
@Schema(description = "应用标识", requiredMode = Schema.RequiredMode.REQUIRED, example = "yudao")
|
||||
@NotEmpty(message = "应用标识不能为空")
|
||||
private String appKey;
|
||||
|
||||
@Schema(description = "应用名", requiredMode = Schema.RequiredMode.REQUIRED, example = "小豆")
|
||||
@NotNull(message = "应用名不能为空")
|
||||
private String name;
|
||||
|
||||
@@ -20,6 +20,9 @@ public class PayAppPageReqVO extends PageParam {
|
||||
@Schema(description = "应用名", example = "小豆")
|
||||
private String name;
|
||||
|
||||
@Schema(description = "应用标识", example = "yudao")
|
||||
private String appKey;
|
||||
|
||||
@Schema(description = "开启状态", example = "0")
|
||||
private Integer status;
|
||||
|
||||
|
||||
@@ -16,6 +16,9 @@ public class PayAppRespVO extends PayAppBaseVO {
|
||||
@Schema(description = "应用编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
|
||||
private Long id;
|
||||
|
||||
@Schema(description = "应用标识", requiredMode = Schema.RequiredMode.REQUIRED, example = "yudao")
|
||||
private String appKey;
|
||||
|
||||
@Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
private LocalDateTime createTime;
|
||||
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
package cn.iocoder.yudao.module.pay.controller.admin.wallet.vo.transaction;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.enums.UserTypeEnum;
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageParam;
|
||||
import cn.iocoder.yudao.framework.common.validation.InEnum;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
@@ -8,7 +10,14 @@ import lombok.Data;
|
||||
@Data
|
||||
public class PayWalletTransactionPageReqVO extends PageParam {
|
||||
|
||||
@Schema(description = "钱包编号", example = "1")
|
||||
@Schema(description = "钱包编号", example = "888")
|
||||
private Long walletId;
|
||||
|
||||
@Schema(description = "用户编号", example = "1024")
|
||||
private Long userId;
|
||||
|
||||
@Schema(description = "用户类型", example = "1")
|
||||
@InEnum(UserTypeEnum.class)
|
||||
private Integer userType;
|
||||
|
||||
}
|
||||
|
||||
@@ -31,6 +31,10 @@ public class PayAppDO extends BaseDO {
|
||||
*/
|
||||
@TableId
|
||||
private Long id;
|
||||
/**
|
||||
* 应用标识
|
||||
*/
|
||||
private String appKey;
|
||||
/**
|
||||
* 应用名
|
||||
*/
|
||||
|
||||
@@ -13,9 +13,14 @@ public interface PayAppMapper extends BaseMapperX<PayAppDO> {
|
||||
default PageResult<PayAppDO> selectPage(PayAppPageReqVO reqVO) {
|
||||
return selectPage(reqVO, new LambdaQueryWrapperX<PayAppDO>()
|
||||
.likeIfPresent(PayAppDO::getName, reqVO.getName())
|
||||
.likeIfPresent(PayAppDO::getAppKey, reqVO.getAppKey())
|
||||
.eqIfPresent(PayAppDO::getStatus, reqVO.getStatus())
|
||||
.betweenIfPresent(PayAppDO::getCreateTime, reqVO.getCreateTime())
|
||||
.orderByDesc(PayAppDO::getId));
|
||||
}
|
||||
|
||||
default PayAppDO selectByAppKey(String appKey) {
|
||||
return selectOne(PayAppDO::getAppKey, appKey);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -15,6 +15,8 @@ public class PayProperties {
|
||||
private static final String ORDER_NO_PREFIX = "P";
|
||||
private static final String REFUND_NO_PREFIX = "R";
|
||||
|
||||
private static final String WALLET_PAY_APP_KEY_DEFAULT = "wallet";
|
||||
|
||||
/**
|
||||
* 支付回调地址
|
||||
*
|
||||
@@ -49,4 +51,10 @@ public class PayProperties {
|
||||
@NotEmpty(message = "退款订单 no 的前缀不能为空")
|
||||
private String refundNoPrefix = REFUND_NO_PREFIX;
|
||||
|
||||
/**
|
||||
* 钱包支付应用 AppKey
|
||||
*/
|
||||
@NotEmpty(message = "钱包支付应用 AppKey 不能为空")
|
||||
private String walletPayAppKey = WALLET_PAY_APP_KEY_DEFAULT;
|
||||
|
||||
}
|
||||
|
||||
@@ -88,13 +88,13 @@ public interface PayAppService {
|
||||
* @return 商户 Map
|
||||
*/
|
||||
default Map<Long, PayAppDO> getAppMap(Collection<Long> ids) {
|
||||
List<PayAppDO> list = getAppList(ids);
|
||||
List<PayAppDO> list = getAppList(ids);
|
||||
return CollectionUtils.convertMap(list, PayAppDO::getId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 支付应用的合法性
|
||||
*
|
||||
* <p>
|
||||
* 如果不合法,抛出 {@link ServiceException} 业务异常
|
||||
*
|
||||
* @param id 应用编号
|
||||
@@ -102,4 +102,14 @@ public interface PayAppService {
|
||||
*/
|
||||
PayAppDO validPayApp(Long id);
|
||||
|
||||
/**
|
||||
* 支付应用的合法性
|
||||
* <p>
|
||||
* 如果不合法,抛出 {@link ServiceException} 业务异常
|
||||
*
|
||||
* @param appKey 应用标识
|
||||
* @return 应用
|
||||
*/
|
||||
PayAppDO validPayApp(String appKey);
|
||||
|
||||
}
|
||||
|
||||
@@ -43,6 +43,9 @@ public class PayAppServiceImpl implements PayAppService {
|
||||
|
||||
@Override
|
||||
public Long createApp(PayAppCreateReqVO createReqVO) {
|
||||
// 验证 appKey 是否重复
|
||||
validateAppKeyUnique(null, createReqVO.getAppKey());
|
||||
|
||||
// 插入
|
||||
PayAppDO app = PayAppConvert.INSTANCE.convert(createReqVO);
|
||||
appMapper.insert(app);
|
||||
@@ -54,11 +57,28 @@ public class PayAppServiceImpl implements PayAppService {
|
||||
public void updateApp(PayAppUpdateReqVO updateReqVO) {
|
||||
// 校验存在
|
||||
validateAppExists(updateReqVO.getId());
|
||||
// 验证 appKey 是否重复
|
||||
validateAppKeyUnique(updateReqVO.getId(), updateReqVO.getAppKey());
|
||||
|
||||
// 更新
|
||||
PayAppDO updateObj = PayAppConvert.INSTANCE.convert(updateReqVO);
|
||||
appMapper.updateById(updateObj);
|
||||
}
|
||||
|
||||
void validateAppKeyUnique(Long id, String appKey) {
|
||||
PayAppDO app = appMapper.selectByAppKey(appKey);
|
||||
if (app == null) {
|
||||
return;
|
||||
}
|
||||
// 如果 id 为空,说明不用比较是否为相同 appKey 的应用
|
||||
if (id == null) {
|
||||
throw exception(APP_KEY_EXISTS);
|
||||
}
|
||||
if (!app.getId().equals(id)) {
|
||||
throw exception(APP_KEY_EXISTS);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateAppStatus(Long id, Integer status) {
|
||||
// 校验商户存在
|
||||
@@ -101,7 +121,7 @@ public class PayAppServiceImpl implements PayAppService {
|
||||
|
||||
@Override
|
||||
public List<PayAppDO> getAppList() {
|
||||
return appMapper.selectList();
|
||||
return appMapper.selectList();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -110,14 +130,30 @@ public class PayAppServiceImpl implements PayAppService {
|
||||
}
|
||||
|
||||
@Override
|
||||
public PayAppDO validPayApp(Long id) {
|
||||
PayAppDO app = appMapper.selectById(id);
|
||||
public PayAppDO validPayApp(Long appId) {
|
||||
PayAppDO app = appMapper.selectById(appId);
|
||||
return validatePayApp(app);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PayAppDO validPayApp(String appKey) {
|
||||
PayAppDO app = appMapper.selectByAppKey(appKey);
|
||||
return validatePayApp(app);
|
||||
}
|
||||
|
||||
/**
|
||||
* 校验支付应用实体的有效性:存在 + 开启
|
||||
*
|
||||
* @param app 待校验的支付应用实体
|
||||
* @return 校验通过的支付应用实体
|
||||
*/
|
||||
private PayAppDO validatePayApp(PayAppDO app) {
|
||||
// 校验是否存在
|
||||
if (app == null) {
|
||||
throw exception(ErrorCodeConstants.APP_NOT_FOUND);
|
||||
}
|
||||
// 校验是否禁用
|
||||
if (CommonStatusEnum.DISABLE.getStatus().equals(app.getStatus())) {
|
||||
if (CommonStatusEnum.isDisable(app.getStatus())) {
|
||||
throw exception(ErrorCodeConstants.APP_IS_DISABLE);
|
||||
}
|
||||
return app;
|
||||
|
||||
@@ -14,11 +14,11 @@ import cn.iocoder.yudao.module.pay.dal.dataobject.demo.PayDemoOrderDO;
|
||||
import cn.iocoder.yudao.module.pay.dal.mysql.demo.PayDemoOrderMapper;
|
||||
import cn.iocoder.yudao.module.pay.enums.order.PayOrderStatusEnum;
|
||||
import cn.iocoder.yudao.module.pay.enums.refund.PayRefundStatusEnum;
|
||||
import jakarta.annotation.Resource;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
|
||||
import jakarta.annotation.Resource;
|
||||
import java.time.Duration;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.HashMap;
|
||||
@@ -43,11 +43,11 @@ import static cn.iocoder.yudao.module.pay.enums.ErrorCodeConstants.*;
|
||||
public class PayDemoOrderServiceImpl implements PayDemoOrderService {
|
||||
|
||||
/**
|
||||
* 接入的实力应用编号
|
||||
* 接入的支付应用标识
|
||||
*
|
||||
* 从 [支付管理 -> 应用信息] 里添加
|
||||
*/
|
||||
private static final Long PAY_APP_ID = 7L;
|
||||
private static final String PAY_APP_KEY = "demo";
|
||||
|
||||
/**
|
||||
* 商品信息 Map
|
||||
@@ -88,7 +88,7 @@ public class PayDemoOrderServiceImpl implements PayDemoOrderService {
|
||||
|
||||
// 2.1 创建支付单
|
||||
Long payOrderId = payOrderApi.createOrder(new PayOrderCreateReqDTO()
|
||||
.setAppId(PAY_APP_ID).setUserIp(getClientIP()) // 支付应用
|
||||
.setAppKey(PAY_APP_KEY).setUserIp(getClientIP()) // 支付应用
|
||||
.setMerchantOrderId(demoOrder.getId().toString()) // 业务的订单编号
|
||||
.setSubject(spuName).setBody("").setPrice(price) // 价格信息
|
||||
.setExpireTime(addTime(Duration.ofHours(2L)))).getCheckedData(); // 支付的过期时间
|
||||
@@ -190,7 +190,7 @@ public class PayDemoOrderServiceImpl implements PayDemoOrderService {
|
||||
String refundId = order.getId() + "-refund";
|
||||
// 2.2 创建退款单
|
||||
Long payRefundId = payRefundApi.createRefund(new PayRefundCreateReqDTO()
|
||||
.setAppId(PAY_APP_ID).setUserIp(getClientIP()) // 支付应用
|
||||
.setAppKey(PAY_APP_KEY).setUserIp(getClientIP()) // 支付应用
|
||||
.setMerchantOrderId(String.valueOf(order.getId())) // 支付单号
|
||||
.setMerchantRefundId(refundId)
|
||||
.setReason("想退钱").setPrice(order.getPrice())).getCheckedData();// 价格信息
|
||||
|
||||
@@ -32,12 +32,12 @@ import cn.iocoder.yudao.module.pay.service.app.PayAppService;
|
||||
import cn.iocoder.yudao.module.pay.service.channel.PayChannelService;
|
||||
import cn.iocoder.yudao.module.pay.service.notify.PayNotifyService;
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
import jakarta.annotation.Resource;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
|
||||
import jakarta.annotation.Resource;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
@@ -111,11 +111,11 @@ public class PayOrderServiceImpl implements PayOrderService {
|
||||
@Override
|
||||
public Long createOrder(PayOrderCreateReqDTO reqDTO) {
|
||||
// 校验 App
|
||||
PayAppDO app = appService.validPayApp(reqDTO.getAppId());
|
||||
PayAppDO app = appService.validPayApp(reqDTO.getAppKey());
|
||||
|
||||
// 查询对应的支付交易单是否已经存在。如果是,则直接返回
|
||||
PayOrderDO order = orderMapper.selectByAppIdAndMerchantOrderId(
|
||||
reqDTO.getAppId(), reqDTO.getMerchantOrderId());
|
||||
app.getId(), reqDTO.getMerchantOrderId());
|
||||
if (order != null) {
|
||||
log.warn("[createOrder][appId({}) merchantOrderId({}) 已经存在对应的支付单({})]", order.getAppId(),
|
||||
order.getMerchantOrderId(), toJsonString(order)); // 理论来说,不会出现这个情况
|
||||
|
||||
@@ -93,9 +93,9 @@ public class PayRefundServiceImpl implements PayRefundService {
|
||||
@Override
|
||||
public Long createPayRefund(PayRefundCreateReqDTO reqDTO) {
|
||||
// 1.1 校验 App
|
||||
PayAppDO app = appService.validPayApp(reqDTO.getAppId());
|
||||
PayAppDO app = appService.validPayApp(reqDTO.getAppKey());
|
||||
// 1.2 校验支付订单
|
||||
PayOrderDO order = validatePayOrderCanRefund(reqDTO);
|
||||
PayOrderDO order = validatePayOrderCanRefund(reqDTO, app.getId());
|
||||
// 1.3 校验支付渠道是否有效
|
||||
PayChannelDO channel = channelService.validPayChannel(order.getChannelId());
|
||||
PayClient client = channelService.getPayClient(channel.getId());
|
||||
@@ -113,7 +113,7 @@ public class PayRefundServiceImpl implements PayRefundService {
|
||||
// 2.1 插入退款单
|
||||
String no = noRedisDAO.generate(payProperties.getRefundNoPrefix());
|
||||
refund = PayRefundConvert.INSTANCE.convert(reqDTO)
|
||||
.setNo(no).setOrderId(order.getId()).setOrderNo(order.getNo())
|
||||
.setNo(no).setAppId(app.getId()).setOrderId(order.getId()).setOrderNo(order.getNo())
|
||||
.setChannelId(order.getChannelId()).setChannelCode(order.getChannelCode())
|
||||
// 商户相关的字段
|
||||
.setNotifyUrl(app.getRefundNotifyUrl())
|
||||
@@ -153,8 +153,8 @@ public class PayRefundServiceImpl implements PayRefundService {
|
||||
* @param reqDTO 退款申请信息
|
||||
* @return 支付订单
|
||||
*/
|
||||
private PayOrderDO validatePayOrderCanRefund(PayRefundCreateReqDTO reqDTO) {
|
||||
PayOrderDO order = orderService.getOrder(reqDTO.getAppId(), reqDTO.getMerchantOrderId());
|
||||
private PayOrderDO validatePayOrderCanRefund(PayRefundCreateReqDTO reqDTO, Long appId) {
|
||||
PayOrderDO order = orderService.getOrder(appId, reqDTO.getMerchantOrderId());
|
||||
if (order == null) {
|
||||
throw exception(PAY_ORDER_NOT_FOUND);
|
||||
}
|
||||
@@ -164,11 +164,11 @@ public class PayRefundServiceImpl implements PayRefundService {
|
||||
}
|
||||
|
||||
// 校验金额,退款金额不能大于原定的金额
|
||||
if (reqDTO.getPrice() + order.getRefundPrice() > order.getPrice()){
|
||||
if (reqDTO.getPrice() + order.getRefundPrice() > order.getPrice()) {
|
||||
throw exception(REFUND_PRICE_EXCEED);
|
||||
}
|
||||
// 是否有退款中的订单
|
||||
if (refundMapper.selectCountByAppIdAndOrderId(reqDTO.getAppId(), order.getId(),
|
||||
if (refundMapper.selectCountByAppIdAndOrderId(appId, order.getId(),
|
||||
PayRefundStatusEnum.WAITING.getStatus()) > 0) {
|
||||
throw exception(REFUND_HAS_REFUNDING);
|
||||
}
|
||||
@@ -197,9 +197,10 @@ public class PayRefundServiceImpl implements PayRefundService {
|
||||
* 通知并更新订单的退款结果
|
||||
*
|
||||
* @param channel 支付渠道
|
||||
* @param notify 通知
|
||||
* @param notify 通知
|
||||
*/
|
||||
@Transactional(rollbackFor = Exception.class) // 注意,如果是方法内调用该方法,需要通过 getSelf().notifyRefund(channel, notify) 调用,否则事务不生效
|
||||
// 注意,如果是方法内调用该方法,需要通过 getSelf().notifyRefund(channel, notify) 调用,否则事务不生效
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void notifyRefund(PayChannelDO channel, PayRefundRespDTO notify) {
|
||||
// 情况一:退款成功
|
||||
if (PayRefundStatusRespEnum.isSuccess(notify.getStatus())) {
|
||||
|
||||
@@ -79,16 +79,16 @@ public class PayTransferServiceImpl implements PayTransferService {
|
||||
@Override
|
||||
public Long createTransfer(PayTransferCreateReqDTO reqDTO) {
|
||||
// 1.1 校验 App
|
||||
PayAppDO payApp = appService.validPayApp(reqDTO.getAppId());
|
||||
PayAppDO payApp = appService.validPayApp(reqDTO.getAppKey());
|
||||
// 1.2 校验支付渠道是否有效
|
||||
PayChannelDO channel = channelService.validPayChannel(reqDTO.getAppId(), reqDTO.getChannelCode());
|
||||
PayChannelDO channel = channelService.validPayChannel(payApp.getId(), reqDTO.getChannelCode());
|
||||
PayClient client = channelService.getPayClient(channel.getId());
|
||||
if (client == null) {
|
||||
log.error("[createTransfer][渠道编号({}) 找不到对应的支付客户端]", channel.getId());
|
||||
throw exception(CHANNEL_NOT_FOUND);
|
||||
}
|
||||
// 1.3 校验转账单已经发起过转账。
|
||||
PayTransferDO transfer = validateTransferCanCreate(reqDTO);
|
||||
PayTransferDO transfer = validateTransferCanCreate(reqDTO, payApp.getId());
|
||||
|
||||
if (transfer == null) {
|
||||
// 2.不存在创建转账单. 否则允许使用相同的 no 再次发起转账
|
||||
@@ -116,8 +116,8 @@ public class PayTransferServiceImpl implements PayTransferService {
|
||||
return transfer.getId();
|
||||
}
|
||||
|
||||
private PayTransferDO validateTransferCanCreate(PayTransferCreateReqDTO dto) {
|
||||
PayTransferDO transfer = transferMapper.selectByAppIdAndMerchantTransferId(dto.getAppId(), dto.getMerchantTransferId());
|
||||
private PayTransferDO validateTransferCanCreate(PayTransferCreateReqDTO dto, Long appId) {
|
||||
PayTransferDO transfer = transferMapper.selectByAppIdAndMerchantTransferId(appId, dto.getMerchantTransferId());
|
||||
if (transfer != null) {
|
||||
// 已经存在,并且状态不为等待状态。说明已经调用渠道转账并返回结果.
|
||||
if (!PayTransferStatusEnum.isWaiting(transfer.getStatus())) {
|
||||
|
||||
@@ -18,6 +18,7 @@ import cn.iocoder.yudao.module.pay.dal.mysql.wallet.PayWalletRechargeMapper;
|
||||
import cn.iocoder.yudao.module.pay.enums.order.PayOrderStatusEnum;
|
||||
import cn.iocoder.yudao.module.pay.enums.refund.PayRefundStatusEnum;
|
||||
import cn.iocoder.yudao.module.pay.enums.wallet.PayWalletBizTypeEnum;
|
||||
import cn.iocoder.yudao.module.pay.framework.pay.config.PayProperties;
|
||||
import cn.iocoder.yudao.module.pay.service.order.PayOrderService;
|
||||
import cn.iocoder.yudao.module.pay.service.refund.PayRefundService;
|
||||
import cn.iocoder.yudao.module.system.api.social.SocialClientApi;
|
||||
@@ -51,11 +52,6 @@ import static cn.iocoder.yudao.module.pay.enums.refund.PayRefundStatusEnum.*;
|
||||
@Slf4j
|
||||
public class PayWalletRechargeServiceImpl implements PayWalletRechargeService {
|
||||
|
||||
/**
|
||||
* TODO 芋艿:放到 payconfig
|
||||
*/
|
||||
private static final Long WALLET_PAY_APP_ID = 8L;
|
||||
|
||||
private static final String WALLET_RECHARGE_ORDER_SUBJECT = "钱包余额充值";
|
||||
|
||||
@Resource
|
||||
@@ -68,9 +64,13 @@ public class PayWalletRechargeServiceImpl implements PayWalletRechargeService {
|
||||
private PayRefundService payRefundService;
|
||||
@Resource
|
||||
private PayWalletRechargePackageService payWalletRechargePackageService;
|
||||
|
||||
@Resource
|
||||
public SocialClientApi socialClientApi;
|
||||
|
||||
@Resource
|
||||
private PayProperties payProperties;
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public PayWalletRechargeDO createWalletRecharge(Long userId, Integer userType, String userIp,
|
||||
@@ -92,7 +92,7 @@ public class PayWalletRechargeServiceImpl implements PayWalletRechargeService {
|
||||
|
||||
// 2.1 创建支付单
|
||||
Long payOrderId = payOrderService.createOrder(new PayOrderCreateReqDTO()
|
||||
.setAppId(WALLET_PAY_APP_ID).setUserIp(userIp)
|
||||
.setAppKey(payProperties.getWalletPayAppKey()).setUserIp(userIp)
|
||||
.setMerchantOrderId(recharge.getId().toString()) // 业务的订单编号
|
||||
.setSubject(WALLET_RECHARGE_ORDER_SUBJECT).setBody("")
|
||||
.setPrice(recharge.getPayPrice())
|
||||
@@ -174,7 +174,7 @@ public class PayWalletRechargeServiceImpl implements PayWalletRechargeService {
|
||||
String walletRechargeId = String.valueOf(id);
|
||||
String refundId = walletRechargeId + "-refund";
|
||||
Long payRefundId = payRefundService.createPayRefund(new PayRefundCreateReqDTO()
|
||||
.setAppId(WALLET_PAY_APP_ID).setUserIp(userIp)
|
||||
.setAppKey(payProperties.getWalletPayAppKey()).setUserIp(userIp)
|
||||
.setMerchantOrderId(walletRechargeId)
|
||||
.setMerchantRefundId(refundId)
|
||||
.setReason("想退钱").setPrice(walletRecharge.getPayPrice()));
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package cn.iocoder.yudao.module.pay.service.wallet;
|
||||
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||
import cn.iocoder.yudao.module.pay.controller.admin.wallet.vo.transaction.PayWalletTransactionPageReqVO;
|
||||
import cn.iocoder.yudao.module.pay.controller.app.wallet.vo.transaction.AppPayWalletTransactionPageReqVO;
|
||||
@@ -52,6 +53,16 @@ public class PayWalletTransactionServiceImpl implements PayWalletTransactionServ
|
||||
|
||||
@Override
|
||||
public PageResult<PayWalletTransactionDO> getWalletTransactionPage(PayWalletTransactionPageReqVO pageVO) {
|
||||
// 基于 userId + userType 查询钱包
|
||||
if (pageVO.getWalletId() == null
|
||||
&& ObjectUtil.isAllNotEmpty(pageVO.getUserId(), pageVO.getUserType())) {
|
||||
PayWalletDO wallet = payWalletService.getOrCreateWallet(pageVO.getUserId(), pageVO.getUserType());
|
||||
if (wallet != null) {
|
||||
pageVO.setWalletId(wallet.getId());
|
||||
}
|
||||
}
|
||||
|
||||
// 查询分页
|
||||
return payWalletTransactionMapper.selectPage(pageVO.getWalletId(), null, pageVO, null);
|
||||
}
|
||||
|
||||
|
||||
@@ -218,11 +218,11 @@ public class PayOrderServiceTest extends BaseDbAndRedisUnitTest {
|
||||
public void testCreateOrder_success() {
|
||||
// mock 参数
|
||||
PayOrderCreateReqDTO reqDTO = randomPojo(PayOrderCreateReqDTO.class,
|
||||
o -> o.setAppId(1L).setMerchantOrderId("10")
|
||||
o -> o.setAppKey("demo").setMerchantOrderId("10")
|
||||
.setSubject(randomString()).setBody(randomString()));
|
||||
// mock 方法
|
||||
PayAppDO app = randomPojo(PayAppDO.class, o -> o.setId(1L).setOrderNotifyUrl("http://127.0.0.1"));
|
||||
when(appService.validPayApp(eq(reqDTO.getAppId()))).thenReturn(app);
|
||||
when(appService.validPayApp(eq(reqDTO.getAppKey()))).thenReturn(app);
|
||||
|
||||
// 调用
|
||||
Long orderId = orderService.createOrder(reqDTO);
|
||||
@@ -239,10 +239,13 @@ public class PayOrderServiceTest extends BaseDbAndRedisUnitTest {
|
||||
public void testCreateOrder_exists() {
|
||||
// mock 参数
|
||||
PayOrderCreateReqDTO reqDTO = randomPojo(PayOrderCreateReqDTO.class,
|
||||
o -> o.setAppId(1L).setMerchantOrderId("10"));
|
||||
o -> o.setAppKey("demo").setMerchantOrderId("10"));
|
||||
// mock 数据
|
||||
PayOrderDO dbOrder = randomPojo(PayOrderDO.class, o -> o.setAppId(1L).setMerchantOrderId("10"));
|
||||
orderMapper.insert(dbOrder);
|
||||
// mock 方法
|
||||
PayAppDO app = randomPojo(PayAppDO.class, o -> o.setId(1L).setOrderNotifyUrl("http://127.0.0.1"));
|
||||
when(appService.validPayApp(eq(reqDTO.getAppKey()))).thenReturn(app);
|
||||
|
||||
// 调用
|
||||
Long orderId = orderService.createOrder(reqDTO);
|
||||
|
||||
@@ -209,10 +209,10 @@ public class PayRefundServiceTest extends BaseDbAndRedisUnitTest {
|
||||
@Test
|
||||
public void testCreateRefund_orderNotFound() {
|
||||
PayRefundCreateReqDTO reqDTO = randomPojo(PayRefundCreateReqDTO.class,
|
||||
o -> o.setAppId(1L));
|
||||
o -> o.setAppKey("demo"));
|
||||
// mock 方法(app)
|
||||
PayAppDO app = randomPojo(PayAppDO.class, o -> o.setId(1L));
|
||||
when(appService.validPayApp(eq(1L))).thenReturn(app);
|
||||
when(appService.validPayApp(eq("demo"))).thenReturn(app);
|
||||
|
||||
// 调用,并断言异常
|
||||
assertServiceException(() -> refundService.createPayRefund(reqDTO),
|
||||
@@ -232,10 +232,10 @@ public class PayRefundServiceTest extends BaseDbAndRedisUnitTest {
|
||||
private void testCreateRefund_orderWaitingOrClosed(Integer status) {
|
||||
// 准备参数
|
||||
PayRefundCreateReqDTO reqDTO = randomPojo(PayRefundCreateReqDTO.class,
|
||||
o -> o.setAppId(1L).setMerchantOrderId("100"));
|
||||
o -> o.setAppKey("demo").setMerchantOrderId("100"));
|
||||
// mock 方法(app)
|
||||
PayAppDO app = randomPojo(PayAppDO.class, o -> o.setId(1L));
|
||||
when(appService.validPayApp(eq(1L))).thenReturn(app);
|
||||
when(appService.validPayApp(eq("demo"))).thenReturn(app);
|
||||
// mock 数据(order)
|
||||
PayOrderDO order = randomPojo(PayOrderDO.class, o -> o.setStatus(status));
|
||||
when(orderService.getOrder(eq(1L), eq("100"))).thenReturn(order);
|
||||
@@ -249,10 +249,10 @@ public class PayRefundServiceTest extends BaseDbAndRedisUnitTest {
|
||||
public void testCreateRefund_refundPriceExceed() {
|
||||
// 准备参数
|
||||
PayRefundCreateReqDTO reqDTO = randomPojo(PayRefundCreateReqDTO.class,
|
||||
o -> o.setAppId(1L).setMerchantOrderId("100").setPrice(10));
|
||||
o -> o.setAppKey("demo").setMerchantOrderId("100").setPrice(10));
|
||||
// mock 方法(app)
|
||||
PayAppDO app = randomPojo(PayAppDO.class, o -> o.setId(1L));
|
||||
when(appService.validPayApp(eq(1L))).thenReturn(app);
|
||||
when(appService.validPayApp(eq("demo"))).thenReturn(app);
|
||||
// mock 数据(order)
|
||||
PayOrderDO order = randomPojo(PayOrderDO.class, o ->
|
||||
o.setStatus(PayOrderStatusEnum.REFUND.getStatus())
|
||||
@@ -268,10 +268,10 @@ public class PayRefundServiceTest extends BaseDbAndRedisUnitTest {
|
||||
public void testCreateRefund_orderHasRefunding() {
|
||||
// 准备参数
|
||||
PayRefundCreateReqDTO reqDTO = randomPojo(PayRefundCreateReqDTO.class,
|
||||
o -> o.setAppId(1L).setMerchantOrderId("100").setPrice(10));
|
||||
o -> o.setAppKey("demo").setMerchantOrderId("100").setPrice(10));
|
||||
// mock 方法(app)
|
||||
PayAppDO app = randomPojo(PayAppDO.class, o -> o.setId(1L));
|
||||
when(appService.validPayApp(eq(1L))).thenReturn(app);
|
||||
when(appService.validPayApp(eq("demo"))).thenReturn(app);
|
||||
// mock 数据(order)
|
||||
PayOrderDO order = randomPojo(PayOrderDO.class, o ->
|
||||
o.setStatus(PayOrderStatusEnum.REFUND.getStatus())
|
||||
@@ -291,10 +291,10 @@ public class PayRefundServiceTest extends BaseDbAndRedisUnitTest {
|
||||
public void testCreateRefund_channelNotFound() {
|
||||
// 准备参数
|
||||
PayRefundCreateReqDTO reqDTO = randomPojo(PayRefundCreateReqDTO.class,
|
||||
o -> o.setAppId(1L).setMerchantOrderId("100").setPrice(9));
|
||||
o -> o.setAppKey("demo").setMerchantOrderId("100").setPrice(9));
|
||||
// mock 方法(app)
|
||||
PayAppDO app = randomPojo(PayAppDO.class, o -> o.setId(1L));
|
||||
when(appService.validPayApp(eq(1L))).thenReturn(app);
|
||||
when(appService.validPayApp(eq("demo"))).thenReturn(app);
|
||||
// mock 数据(order)
|
||||
PayOrderDO order = randomPojo(PayOrderDO.class, o ->
|
||||
o.setStatus(PayOrderStatusEnum.REFUND.getStatus())
|
||||
@@ -315,11 +315,11 @@ public class PayRefundServiceTest extends BaseDbAndRedisUnitTest {
|
||||
public void testCreateRefund_refundExists() {
|
||||
// 准备参数
|
||||
PayRefundCreateReqDTO reqDTO = randomPojo(PayRefundCreateReqDTO.class,
|
||||
o -> o.setAppId(1L).setMerchantOrderId("100").setPrice(9)
|
||||
o -> o.setAppKey("demo").setMerchantOrderId("100").setPrice(9)
|
||||
.setMerchantRefundId("200").setReason("测试退款"));
|
||||
// mock 方法(app)
|
||||
PayAppDO app = randomPojo(PayAppDO.class, o -> o.setId(1L));
|
||||
when(appService.validPayApp(eq(1L))).thenReturn(app);
|
||||
when(appService.validPayApp(eq("demo"))).thenReturn(app);
|
||||
// mock 数据(order)
|
||||
PayOrderDO order = randomPojo(PayOrderDO.class, o ->
|
||||
o.setStatus(PayOrderStatusEnum.REFUND.getStatus())
|
||||
@@ -347,11 +347,11 @@ public class PayRefundServiceTest extends BaseDbAndRedisUnitTest {
|
||||
public void testCreateRefund_invokeException() {
|
||||
// 准备参数
|
||||
PayRefundCreateReqDTO reqDTO = randomPojo(PayRefundCreateReqDTO.class,
|
||||
o -> o.setAppId(1L).setMerchantOrderId("100").setPrice(9)
|
||||
o -> o.setAppKey("demo").setMerchantOrderId("100").setPrice(9)
|
||||
.setMerchantRefundId("200").setReason("测试退款"));
|
||||
// mock 方法(app)
|
||||
PayAppDO app = randomPojo(PayAppDO.class, o -> o.setId(1L));
|
||||
when(appService.validPayApp(eq(1L))).thenReturn(app);
|
||||
when(appService.validPayApp(eq("demo"))).thenReturn(app);
|
||||
// mock 数据(order)
|
||||
PayOrderDO order = randomPojo(PayOrderDO.class, o ->
|
||||
o.setStatus(PayOrderStatusEnum.REFUND.getStatus())
|
||||
@@ -391,11 +391,11 @@ public class PayRefundServiceTest extends BaseDbAndRedisUnitTest {
|
||||
|
||||
// 准备参数
|
||||
PayRefundCreateReqDTO reqDTO = randomPojo(PayRefundCreateReqDTO.class,
|
||||
o -> o.setAppId(1L).setMerchantOrderId("100").setPrice(9)
|
||||
o -> o.setAppKey("demo").setMerchantOrderId("100").setPrice(9)
|
||||
.setMerchantRefundId("200").setReason("测试退款"));
|
||||
// mock 方法(app)
|
||||
PayAppDO app = randomPojo(PayAppDO.class, o -> o.setId(1L));
|
||||
when(appService.validPayApp(eq(1L))).thenReturn(app);
|
||||
when(appService.validPayApp(eq("demo"))).thenReturn(app);
|
||||
// mock 数据(order)
|
||||
PayOrderDO order = randomPojo(PayOrderDO.class, o ->
|
||||
o.setStatus(PayOrderStatusEnum.REFUND.getStatus())
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
CREATE TABLE IF NOT EXISTS "pay_app" (
|
||||
"id" number NOT NULL GENERATED BY DEFAULT AS IDENTITY,
|
||||
"app_key" varchar(64) NOT NULL,
|
||||
"name" varchar(64) NOT NULL,
|
||||
"status" tinyint NOT NULL,
|
||||
"remark" varchar(255) DEFAULT NULL,
|
||||
|
||||
@@ -2,10 +2,11 @@ package cn.iocoder.yudao.framework.pay.core.client.impl.alipay;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.util.validation.ValidationUtils;
|
||||
import cn.iocoder.yudao.framework.pay.core.client.PayClientConfig;
|
||||
import lombok.Data;
|
||||
|
||||
import jakarta.validation.Validator;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 支付宝的 PayClientConfig 实现类
|
||||
@@ -25,6 +26,11 @@ public class AlipayPayClientConfig implements PayClientConfig {
|
||||
*/
|
||||
public static final Integer MODE_CERTIFICATE = 2;
|
||||
|
||||
/**
|
||||
* 接口内容加密方式 - AES 加密
|
||||
*/
|
||||
public static final String ENC_TYPE_AES = "AES";
|
||||
|
||||
/**
|
||||
* 签名算法类型 - RSA
|
||||
*/
|
||||
@@ -91,6 +97,22 @@ public class AlipayPayClientConfig implements PayClientConfig {
|
||||
@NotBlank(message = "指定根证书内容字符串不能为空", groups = {ModeCertificate.class})
|
||||
private String rootCertContent;
|
||||
|
||||
/**
|
||||
* 接口内容加密方式
|
||||
*
|
||||
* 1. 如果为空,将使用无加密方式
|
||||
* 2. 如果要加密,目前支付宝只有 AES 一种加密方式
|
||||
*
|
||||
* @see <a href="https://opendocs.alipay.com/common/02mse3">支付宝开放平台</a>
|
||||
* @see AlipayPayClientConfig#ENC_TYPE_AES
|
||||
*/
|
||||
private String encryptType;
|
||||
|
||||
/**
|
||||
* 接口内容加密的私钥
|
||||
*/
|
||||
private String encryptKey;
|
||||
|
||||
public interface ModePublicKey {
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user