mirror of
				https://gitee.com/hhyykk/ipms-sjy.git
				synced 2025-10-31 18:28:43 +08:00 
			
		
		
		
	优化渠道 config 校验和逻辑转换问题
This commit is contained in:
		| @@ -80,7 +80,7 @@ public class PayMerchantController { | |||||||
|     @ApiImplicitParam(name = "name", value = "商户名称", required = true, example = "芋道", dataTypeClass = Long.class) |     @ApiImplicitParam(name = "name", value = "商户名称", required = true, example = "芋道", dataTypeClass = Long.class) | ||||||
|     @PreAuthorize("@ss.hasPermission('pay:merchant:query')") |     @PreAuthorize("@ss.hasPermission('pay:merchant:query')") | ||||||
|     public CommonResult<List<PayMerchantRespVO>> getMerchantListByName(@RequestParam("name") String name) { |     public CommonResult<List<PayMerchantRespVO>> getMerchantListByName(@RequestParam("name") String name) { | ||||||
|         List<PayMerchantDO> merchantListDO = merchantService.getMerchantListByNameLimit(name); |         List<PayMerchantDO> merchantListDO = merchantService.getMerchantListByName(name); | ||||||
|         return success(PayMerchantConvert.INSTANCE.convertList(merchantListDO)); |         return success(PayMerchantConvert.INSTANCE.convertList(merchantListDO)); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -6,6 +6,7 @@ import cn.iocoder.yudao.coreservice.modules.pay.dal.dataobject.merchant.PayMerch | |||||||
| import cn.iocoder.yudao.framework.common.pojo.PageResult; | import cn.iocoder.yudao.framework.common.pojo.PageResult; | ||||||
| import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; | import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; | ||||||
| import cn.iocoder.yudao.framework.mybatis.core.query.QueryWrapperX; | import cn.iocoder.yudao.framework.mybatis.core.query.QueryWrapperX; | ||||||
|  | import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; | ||||||
| import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; | import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; | ||||||
| import org.apache.ibatis.annotations.Mapper; | import org.apache.ibatis.annotations.Mapper; | ||||||
|  |  | ||||||
| @@ -48,8 +49,6 @@ public interface PayMerchantMapper extends BaseMapperX<PayMerchantDO> { | |||||||
|      * @return 商户集合 |      * @return 商户集合 | ||||||
|      */ |      */ | ||||||
|     default List<PayMerchantDO> getMerchantListByName(String merchantName) { |     default List<PayMerchantDO> getMerchantListByName(String merchantName) { | ||||||
|         // TODO @aquan:全模糊匹配,暂时不考虑索引的事;另外,可以直接 new Lambada 的 QueryWrapper 实现类呀 |         return this.selectList(new LambdaQueryWrapper<PayMerchantDO>().like(PayMerchantDO::getName, merchantName)); | ||||||
|         return this.selectList(new QueryWrapper<PayMerchantDO>() |  | ||||||
|                 .lambda().likeRight(PayMerchantDO::getName, merchantName)); |  | ||||||
|     } |     } | ||||||
| } | } | ||||||
|   | |||||||
| @@ -22,7 +22,7 @@ import org.springframework.validation.annotation.Validated; | |||||||
| import javax.annotation.Resource; | import javax.annotation.Resource; | ||||||
| import java.util.*; | import java.util.*; | ||||||
|  |  | ||||||
| import static cn.iocoder.yudao.coreservice.modules.pay.enums.PayErrorCodeCoreConstants.APP_NOT_EXISTS; | import static cn.iocoder.yudao.coreservice.modules.pay.enums.PayErrorCodeCoreConstants.*; | ||||||
| import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; | import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; | ||||||
| import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet; | import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet; | ||||||
|  |  | ||||||
| @@ -72,7 +72,7 @@ public class PayAppServiceImpl implements PayAppService { | |||||||
|  |  | ||||||
|     private void validateAppExists(Long id) { |     private void validateAppExists(Long id) { | ||||||
|         if (appMapper.selectById(id) == null) { |         if (appMapper.selectById(id) == null) { | ||||||
|             throw exception(APP_NOT_EXISTS); |             throw exception(PAY_APP_NOT_FOUND); | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -148,7 +148,7 @@ public class PayAppServiceImpl implements PayAppService { | |||||||
|         } |         } | ||||||
|         PayAppDO payApp = appMapper.selectById(id); |         PayAppDO payApp = appMapper.selectById(id); | ||||||
|         if (payApp == null) { |         if (payApp == null) { | ||||||
|             throw exception(APP_NOT_EXISTS); |             throw exception(PAY_APP_NOT_FOUND); | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| } | } | ||||||
|   | |||||||
| @@ -1,5 +1,6 @@ | |||||||
| package cn.iocoder.yudao.adminserver.modules.pay.service.channel.impl; | package cn.iocoder.yudao.adminserver.modules.pay.service.channel.impl; | ||||||
|  |  | ||||||
|  | import cn.hutool.json.JSONUtil; | ||||||
| import cn.iocoder.yudao.adminserver.modules.pay.controller.channel.vo.PayChannelCreateReqVO; | import cn.iocoder.yudao.adminserver.modules.pay.controller.channel.vo.PayChannelCreateReqVO; | ||||||
| import cn.iocoder.yudao.adminserver.modules.pay.controller.channel.vo.PayChannelExportReqVO; | import cn.iocoder.yudao.adminserver.modules.pay.controller.channel.vo.PayChannelExportReqVO; | ||||||
| import cn.iocoder.yudao.adminserver.modules.pay.controller.channel.vo.PayChannelPageReqVO; | import cn.iocoder.yudao.adminserver.modules.pay.controller.channel.vo.PayChannelPageReqVO; | ||||||
| @@ -9,24 +10,20 @@ import cn.iocoder.yudao.adminserver.modules.pay.dal.mysql.channel.PayChannelMapp | |||||||
| import cn.iocoder.yudao.adminserver.modules.pay.service.channel.PayChannelService; | import cn.iocoder.yudao.adminserver.modules.pay.service.channel.PayChannelService; | ||||||
| import cn.iocoder.yudao.coreservice.modules.pay.dal.dataobject.merchant.PayChannelDO; | import cn.iocoder.yudao.coreservice.modules.pay.dal.dataobject.merchant.PayChannelDO; | ||||||
| import cn.iocoder.yudao.framework.common.pojo.PageResult; | import cn.iocoder.yudao.framework.common.pojo.PageResult; | ||||||
| import cn.iocoder.yudao.framework.pay.core.client.impl.alipay.AlipayPayClientConfig; | import cn.iocoder.yudao.framework.pay.core.client.PayClientConfig; | ||||||
| import cn.iocoder.yudao.framework.pay.core.client.impl.wx.WXPayClientConfig; | import cn.iocoder.yudao.framework.pay.core.client.impl.wx.WXPayClientConfig; | ||||||
| import cn.iocoder.yudao.framework.pay.core.enums.PayChannelEnum; | import cn.iocoder.yudao.framework.pay.core.enums.PayChannelEnum; | ||||||
| import com.alibaba.fastjson.JSON; |  | ||||||
| import lombok.extern.slf4j.Slf4j; | import lombok.extern.slf4j.Slf4j; | ||||||
|  | import org.springframework.beans.factory.annotation.Autowired; | ||||||
|  | import org.springframework.beans.factory.annotation.Qualifier; | ||||||
| import org.springframework.stereotype.Service; | import org.springframework.stereotype.Service; | ||||||
| import org.springframework.util.Assert; | import org.springframework.util.Assert; | ||||||
| import org.springframework.validation.annotation.Validated; | import org.springframework.validation.annotation.Validated; | ||||||
|  |  | ||||||
| import javax.annotation.Resource; | import javax.annotation.Resource; | ||||||
| import javax.validation.ConstraintViolation; |  | ||||||
| import javax.validation.Validation; |  | ||||||
| import javax.validation.Validator; | import javax.validation.Validator; | ||||||
| import javax.validation.ValidatorFactory; |  | ||||||
| import java.util.Collection; | import java.util.Collection; | ||||||
| import java.util.List; | import java.util.List; | ||||||
| import java.util.Set; |  | ||||||
| import java.util.stream.Collectors; |  | ||||||
|  |  | ||||||
| import static cn.iocoder.yudao.coreservice.modules.pay.enums.PayErrorCodeCoreConstants.*; | import static cn.iocoder.yudao.coreservice.modules.pay.enums.PayErrorCodeCoreConstants.*; | ||||||
| import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; | import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; | ||||||
| @@ -44,14 +41,15 @@ public class PayChannelServiceImpl implements PayChannelService { | |||||||
|     @Resource |     @Resource | ||||||
|     private PayChannelMapper channelMapper; |     private PayChannelMapper channelMapper; | ||||||
|  |  | ||||||
|  |     @Resource | ||||||
|  |     private Validator validator; | ||||||
|  |  | ||||||
|     @Override |     @Override | ||||||
|     public Long createChannel(PayChannelCreateReqVO reqVO) { |     public Long createChannel(PayChannelCreateReqVO reqVO) { | ||||||
|         // TODO @aquan:感觉获得那一条比较合适。因为是有唯一性的。注释有错别字哈。 |  | ||||||
|         // 判断是否有重复的有责无法新增 |         // 断言是否有重复的 | ||||||
|         Integer channelCount = this.getChannelCountByConditions(reqVO.getMerchantId(), reqVO.getAppId(), reqVO.getCode()); |         PayChannelDO channelDO = this.getChannelByConditions(reqVO.getMerchantId(), reqVO.getAppId(), reqVO.getCode()); | ||||||
|         if (channelCount > 0) { |         Assert.isNull(channelDO, CHANNEL_EXIST_SAME_CHANNEL_ERROR.getMsg()); | ||||||
|             throw exception(CHANNEL_EXIST_SAME_CHANNEL_ERROR); |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         // 新增渠道 |         // 新增渠道 | ||||||
|         PayChannelDO channel = PayChannelConvert.INSTANCE.convert(reqVO); |         PayChannelDO channel = PayChannelConvert.INSTANCE.convert(reqVO); | ||||||
| @@ -142,24 +140,6 @@ public class PayChannelServiceImpl implements PayChannelService { | |||||||
|         return this.channelMapper.getChannelByConditions(merchantId, appid, code); |         return this.channelMapper.getChannelByConditions(merchantId, appid, code); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     /** |  | ||||||
|      * 检测微信秘钥参数 |  | ||||||
|      * |  | ||||||
|      * @param config 信秘钥参数 |  | ||||||
|      */ |  | ||||||
|     private void wechatParamCheck(WXPayClientConfig config) { |  | ||||||
|         // 针对于 V2 或者 V3 版本的参数校验 |  | ||||||
|         if (WXPayClientConfig.API_VERSION_V2.equals(config.getApiVersion())) { |  | ||||||
|             Assert.notNull(config.getMchKey(), CHANNEL_WECHAT_VERSION_2_MCH_KEY_IS_NULL.getMsg()); |  | ||||||
|         } |  | ||||||
|         if (WXPayClientConfig.API_VERSION_V3.equals(config.getApiVersion())) { |  | ||||||
|             Assert.notNull(config.getPrivateKeyContent(), CHANNEL_WECHAT_VERSION_3_PRIVATE_KEY_IS_NULL.getMsg()); |  | ||||||
|             Assert.notNull(config.getPrivateCertContent(), CHANNEL_WECHAT_VERSION_3_CERT_KEY_IS_NULL.getMsg()); |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
|     /** |     /** | ||||||
|      * 设置渠道配置以及参数校验 |      * 设置渠道配置以及参数校验 | ||||||
|      * |      * | ||||||
| @@ -168,48 +148,11 @@ public class PayChannelServiceImpl implements PayChannelService { | |||||||
|      */ |      */ | ||||||
|     private void settingConfigAndCheckParam(PayChannelDO channel, String configStr) { |     private void settingConfigAndCheckParam(PayChannelDO channel, String configStr) { | ||||||
|         // 得到这个渠道是微信的还是支付宝的 |         // 得到这个渠道是微信的还是支付宝的 | ||||||
|         String channelType = PayChannelEnum.verifyWechatOrAliPay(channel.getCode()); |         Class<? extends PayClientConfig> payClass = PayChannelEnum.findByCodeGetClass(channel.getCode()); | ||||||
|         Assert.notNull(channelType, CHANNEL_NOT_EXISTS.getMsg()); |         Assert.notNull(payClass, CHANNEL_NOT_EXISTS.getMsg()); | ||||||
|  |         PayClientConfig config = JSONUtil.toBean(configStr, payClass); | ||||||
|         // 进行验证 |         // 验证参数 | ||||||
|         // TODO @阿全:Spring 可以注入 Validator 哈 |         config.verifyParam(validator); | ||||||
|         ValidatorFactory validatorFactory = Validation.buildDefaultValidatorFactory(); |         channel.setConfig(config); | ||||||
|         Validator validator = validatorFactory.getValidator(); |  | ||||||
|  |  | ||||||
|         // 微信的验证 |  | ||||||
|         // TODO @aquan:这么实现,可扩性不好。@AssertTrue 注解。 |  | ||||||
|         if (PayChannelEnum.WECHAT.equals(channelType)) { |  | ||||||
|  |  | ||||||
|             WXPayClientConfig config = JSON.parseObject(configStr, WXPayClientConfig.class); |  | ||||||
|             // 判断是V2 版本还是 V3 版本 |  | ||||||
|             Class clazz = config.getApiVersion().equals(WXPayClientConfig.API_VERSION_V2) |  | ||||||
|                     ? WXPayClientConfig.V2.class : WXPayClientConfig.V3.class; |  | ||||||
|             // 手动调用validate进行验证 |  | ||||||
|             Set<ConstraintViolation<WXPayClientConfig>> validate = validator.validate(config,clazz); |  | ||||||
|  |  | ||||||
|             // 断言没有异常 |  | ||||||
|             Assert.isTrue(validate.isEmpty(), validate.stream().map(ConstraintViolation::getMessage) |  | ||||||
|                     .collect(Collectors.joining(","))); |  | ||||||
|  |  | ||||||
|             channel.setConfig(config); |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         // 支付宝验证 |  | ||||||
|         if (PayChannelEnum.ALIPAY.equals(channelType)) { |  | ||||||
|  |  | ||||||
|             AlipayPayClientConfig config = JSON.parseObject(configStr, AlipayPayClientConfig.class); |  | ||||||
|  |  | ||||||
|             // 判断是V2 版本还是 V3 版本 |  | ||||||
|             Class clazz = config.getMode().equals(AlipayPayClientConfig.MODE_PUBLIC_KEY) |  | ||||||
|                     ? AlipayPayClientConfig.ModePublicKey.class : AlipayPayClientConfig.ModeCertificate.class; |  | ||||||
|             // 手动调用validate进行验证 |  | ||||||
|             Set<ConstraintViolation<AlipayPayClientConfig>> validate = validator.validate(config,clazz); |  | ||||||
|  |  | ||||||
|             // 断言没有异常 |  | ||||||
|             Assert.isTrue(validate.isEmpty(), validate.stream().map(ConstraintViolation::getMessage) |  | ||||||
|                     .collect(Collectors.joining(","))); |  | ||||||
|             channel.setConfig(config); |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|     } |     } | ||||||
| } | } | ||||||
|   | |||||||
| @@ -92,14 +92,6 @@ public interface PayMerchantService { | |||||||
|      */ |      */ | ||||||
|     List<PayMerchantDO> getMerchantListByName(String merchantName); |     List<PayMerchantDO> getMerchantListByName(String merchantName); | ||||||
|  |  | ||||||
|     // TODO aquan:暂时不用提供这样的检索。商户不多的。 |  | ||||||
|     /** |  | ||||||
|      * 根据商户名称模糊查询一定数量的商户集合 |  | ||||||
|      * @param merchantName 商户名称 |  | ||||||
|      * @return 商户集合 |  | ||||||
|      */ |  | ||||||
|     List<PayMerchantDO> getMerchantListByNameLimit(String merchantName); |  | ||||||
|  |  | ||||||
|     /** |     /** | ||||||
|      * 获得指定编号的商户 Map |      * 获得指定编号的商户 Map | ||||||
|      * |      * | ||||||
|   | |||||||
| @@ -10,8 +10,6 @@ import cn.iocoder.yudao.adminserver.modules.pay.dal.mysql.merchant.PayMerchantMa | |||||||
| import cn.iocoder.yudao.adminserver.modules.pay.service.merchant.PayMerchantService; | import cn.iocoder.yudao.adminserver.modules.pay.service.merchant.PayMerchantService; | ||||||
| import cn.iocoder.yudao.coreservice.modules.pay.dal.dataobject.merchant.PayMerchantDO; | import cn.iocoder.yudao.coreservice.modules.pay.dal.dataobject.merchant.PayMerchantDO; | ||||||
| import cn.iocoder.yudao.framework.common.pojo.PageResult; | import cn.iocoder.yudao.framework.common.pojo.PageResult; | ||||||
| import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; |  | ||||||
| import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; |  | ||||||
| import com.google.common.annotations.VisibleForTesting; | import com.google.common.annotations.VisibleForTesting; | ||||||
| import org.springframework.stereotype.Service; | import org.springframework.stereotype.Service; | ||||||
| import org.springframework.validation.annotation.Validated; | import org.springframework.validation.annotation.Validated; | ||||||
| @@ -21,7 +19,7 @@ import java.time.LocalDateTime; | |||||||
| import java.util.Collection; | import java.util.Collection; | ||||||
| import java.util.List; | import java.util.List; | ||||||
|  |  | ||||||
| import static cn.iocoder.yudao.coreservice.modules.pay.enums.PayErrorCodeCoreConstants.MERCHANT_NOT_EXISTS; | import static cn.iocoder.yudao.coreservice.modules.pay.enums.PayErrorCodeCoreConstants.PAY_MERCHANT_NOT_EXISTS; | ||||||
| import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; | import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; | ||||||
| /** | /** | ||||||
|  * 支付商户信息 Service 实现类 |  * 支付商户信息 Service 实现类 | ||||||
| @@ -64,7 +62,7 @@ public class PayMerchantServiceImpl implements PayMerchantService { | |||||||
|  |  | ||||||
|     private void validateMerchantExists(Long id) { |     private void validateMerchantExists(Long id) { | ||||||
|         if (merchantMapper.selectById(id) == null) { |         if (merchantMapper.selectById(id) == null) { | ||||||
|             throw exception(MERCHANT_NOT_EXISTS); |             throw exception(PAY_MERCHANT_NOT_EXISTS); | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -116,22 +114,6 @@ public class PayMerchantServiceImpl implements PayMerchantService { | |||||||
|         return this.merchantMapper.getMerchantListByName(merchantName); |         return this.merchantMapper.getMerchantListByName(merchantName); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     /** |  | ||||||
|      * 根据商户名称模糊查询一定数量的商户集合 |  | ||||||
|      * |  | ||||||
|      * @param merchantName 商户名称 |  | ||||||
|      * @return 商户集合 |  | ||||||
|      */ |  | ||||||
|     @Override |  | ||||||
|     public List<PayMerchantDO> getMerchantListByNameLimit(String merchantName) { |  | ||||||
|         // TODO @aquan:mybatis plus 哈 |  | ||||||
|         LambdaQueryWrapper<PayMerchantDO> queryWrapper = new QueryWrapper<PayMerchantDO>().lambda() |  | ||||||
|                 .select(PayMerchantDO::getId, PayMerchantDO::getName) |  | ||||||
|                 .likeRight(PayMerchantDO::getName, merchantName) |  | ||||||
|                 .last("limit 200"); |  | ||||||
|  |  | ||||||
|         return this.merchantMapper.selectList(queryWrapper); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     /** |     /** | ||||||
|      * 检查商户是否存在 |      * 检查商户是否存在 | ||||||
| @@ -144,7 +126,7 @@ public class PayMerchantServiceImpl implements PayMerchantService { | |||||||
|         } |         } | ||||||
|         PayMerchantDO merchant = merchantMapper.selectById(id); |         PayMerchantDO merchant = merchantMapper.selectById(id); | ||||||
|         if (merchant == null) { |         if (merchant == null) { | ||||||
|             throw exception(MERCHANT_NOT_EXISTS); |             throw exception(PAY_MERCHANT_NOT_EXISTS); | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -92,7 +92,7 @@ public class PayAppServiceTest extends BaseDbUnitTest { | |||||||
|         PayAppUpdateReqVO reqVO = randomPojo(PayAppUpdateReqVO.class, o -> |         PayAppUpdateReqVO reqVO = randomPojo(PayAppUpdateReqVO.class, o -> | ||||||
|                 o.setStatus((RandomUtil.randomEle(CommonStatusEnum.values()).getStatus()))); |                 o.setStatus((RandomUtil.randomEle(CommonStatusEnum.values()).getStatus()))); | ||||||
|         // 调用, 并断言异常 |         // 调用, 并断言异常 | ||||||
|         assertServiceException(() -> appService.updateApp(reqVO), APP_NOT_EXISTS); |         assertServiceException(() -> appService.updateApp(reqVO), PAY_APP_NOT_FOUND); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     @Test |     @Test | ||||||
| @@ -116,7 +116,7 @@ public class PayAppServiceTest extends BaseDbUnitTest { | |||||||
|         Long id = randomLongId(); |         Long id = randomLongId(); | ||||||
|  |  | ||||||
|         // 调用, 并断言异常 |         // 调用, 并断言异常 | ||||||
|         assertServiceException(() -> appService.deleteApp(id), APP_NOT_EXISTS); |         assertServiceException(() -> appService.deleteApp(id), PAY_APP_NOT_FOUND); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     @Test |     @Test | ||||||
|   | |||||||
| @@ -0,0 +1,20 @@ | |||||||
|  | package cn.iocoder.yudao.adminserver.modules.pay.service.channel; | ||||||
|  |  | ||||||
|  | import org.springframework.context.annotation.Bean; | ||||||
|  | import org.springframework.context.annotation.Configuration; | ||||||
|  |  | ||||||
|  | import javax.validation.Validation; | ||||||
|  | import javax.validation.Validator; | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * 用于初始化 validator Bean 对象 | ||||||
|  |  * @author aquan | ||||||
|  |  */ | ||||||
|  | @Configuration | ||||||
|  | public class PayChannelConfig { | ||||||
|  |  | ||||||
|  |     @Bean | ||||||
|  |     public Validator validator(){ | ||||||
|  |         return Validation.buildDefaultValidatorFactory().getValidator(); | ||||||
|  |     } | ||||||
|  | } | ||||||
| @@ -34,7 +34,10 @@ import static org.junit.jupiter.api.Assertions.*; | |||||||
|  * |  * | ||||||
|  * @author 芋艿 |  * @author 芋艿 | ||||||
|  */ |  */ | ||||||
| @Import(PayChannelServiceImpl.class) | @Import({ | ||||||
|  |         PayChannelServiceImpl.class, | ||||||
|  |         PayChannelConfig.class | ||||||
|  | }) | ||||||
| public class PayChannelServiceTest extends BaseDbUnitTest { | public class PayChannelServiceTest extends BaseDbUnitTest { | ||||||
|  |  | ||||||
|     @Resource |     @Resource | ||||||
| @@ -43,7 +46,6 @@ public class PayChannelServiceTest extends BaseDbUnitTest { | |||||||
|     @Resource |     @Resource | ||||||
|     private PayChannelMapper channelMapper; |     private PayChannelMapper channelMapper; | ||||||
|  |  | ||||||
|  |  | ||||||
|     @Test |     @Test | ||||||
|     public void testCreateWechatVersion2Channel_success() { |     public void testCreateWechatVersion2Channel_success() { | ||||||
|         // 准备参数 |         // 准备参数 | ||||||
| @@ -69,8 +71,7 @@ public class PayChannelServiceTest extends BaseDbUnitTest { | |||||||
|  |  | ||||||
|     @Test |     @Test | ||||||
|     public void testCreateWechatVersion3Channel_success() { |     public void testCreateWechatVersion3Channel_success() { | ||||||
|         // 准备参数 TODO @aquan:多余的空行去掉哈。例如说 74 行。 |         // 准备参数 | ||||||
|  |  | ||||||
|         WXPayClientConfig v3Config = getV3Config(); |         WXPayClientConfig v3Config = getV3Config(); | ||||||
|         PayChannelCreateReqVO reqVO = randomPojo(PayChannelCreateReqVO.class, o -> { |         PayChannelCreateReqVO reqVO = randomPojo(PayChannelCreateReqVO.class, o -> { | ||||||
|             o.setCode(PayChannelEnum.WX_PUB.getCode()); |             o.setCode(PayChannelEnum.WX_PUB.getCode()); | ||||||
|   | |||||||
| @@ -18,7 +18,7 @@ import org.springframework.context.annotation.Import; | |||||||
| import javax.annotation.Resource; | import javax.annotation.Resource; | ||||||
| import java.util.List; | import java.util.List; | ||||||
|  |  | ||||||
| import static cn.iocoder.yudao.coreservice.modules.pay.enums.PayErrorCodeCoreConstants.MERCHANT_NOT_EXISTS; | import static cn.iocoder.yudao.coreservice.modules.pay.enums.PayErrorCodeCoreConstants.PAY_MERCHANT_NOT_EXISTS; | ||||||
| import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals; | import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals; | ||||||
| import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertServiceException; | import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertServiceException; | ||||||
| import static cn.iocoder.yudao.framework.common.util.date.DateUtils.buildTime; | import static cn.iocoder.yudao.framework.common.util.date.DateUtils.buildTime; | ||||||
| @@ -79,7 +79,7 @@ public class PayMerchantServiceTest extends BaseDbUnitTest { | |||||||
|         PayMerchantUpdateReqVO reqVO = randomPojo(PayMerchantUpdateReqVO.class); |         PayMerchantUpdateReqVO reqVO = randomPojo(PayMerchantUpdateReqVO.class); | ||||||
|  |  | ||||||
|         // 调用, 并断言异常 |         // 调用, 并断言异常 | ||||||
|         assertServiceException(() -> merchantService.updateMerchant(reqVO), MERCHANT_NOT_EXISTS); |         assertServiceException(() -> merchantService.updateMerchant(reqVO), PAY_MERCHANT_NOT_EXISTS); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     @Test |     @Test | ||||||
| @@ -103,7 +103,7 @@ public class PayMerchantServiceTest extends BaseDbUnitTest { | |||||||
|         Long id = randomLongId(); |         Long id = randomLongId(); | ||||||
|  |  | ||||||
|         // 调用, 并断言异常 |         // 调用, 并断言异常 | ||||||
|         assertServiceException(() -> merchantService.deleteMerchant(id), MERCHANT_NOT_EXISTS); |         assertServiceException(() -> merchantService.deleteMerchant(id), PAY_MERCHANT_NOT_EXISTS); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     @Test |     @Test | ||||||
|   | |||||||
| @@ -22,7 +22,6 @@ public interface PayErrorCodeCoreConstants { | |||||||
|     ErrorCode PAY_CHANNEL_IS_DISABLE = new ErrorCode(1007001001, "支付渠道已经禁用"); |     ErrorCode PAY_CHANNEL_IS_DISABLE = new ErrorCode(1007001001, "支付渠道已经禁用"); | ||||||
|     ErrorCode PAY_CHANNEL_CLIENT_NOT_FOUND = new ErrorCode(1007001002, "支付渠道的客户端不存在"); |     ErrorCode PAY_CHANNEL_CLIENT_NOT_FOUND = new ErrorCode(1007001002, "支付渠道的客户端不存在"); | ||||||
|     ErrorCode CHANNEL_NOT_EXISTS = new ErrorCode(1007001003, "支付渠道不存在"); |     ErrorCode CHANNEL_NOT_EXISTS = new ErrorCode(1007001003, "支付渠道不存在"); | ||||||
|     ErrorCode CHANNEL_KEY_READ_ERROR = new ErrorCode(1007001004, "支付渠道秘钥文件读取失败"); |  | ||||||
|     ErrorCode CHANNEL_EXIST_SAME_CHANNEL_ERROR = new ErrorCode(1007001005, "已存在相同的渠道"); |     ErrorCode CHANNEL_EXIST_SAME_CHANNEL_ERROR = new ErrorCode(1007001005, "已存在相同的渠道"); | ||||||
|     ErrorCode CHANNEL_WECHAT_VERSION_2_MCH_KEY_IS_NULL = new ErrorCode(1007001006,"微信渠道v2版本中商户密钥不可为空"); |     ErrorCode CHANNEL_WECHAT_VERSION_2_MCH_KEY_IS_NULL = new ErrorCode(1007001006,"微信渠道v2版本中商户密钥不可为空"); | ||||||
|     ErrorCode CHANNEL_WECHAT_VERSION_3_PRIVATE_KEY_IS_NULL = new ErrorCode(1007001006,"微信渠道v3版本apiclient_key.pem不可为空"); |     ErrorCode CHANNEL_WECHAT_VERSION_3_PRIVATE_KEY_IS_NULL = new ErrorCode(1007001006,"微信渠道v3版本apiclient_key.pem不可为空"); | ||||||
| @@ -49,18 +48,14 @@ public interface PayErrorCodeCoreConstants { | |||||||
|     ErrorCode PAY_REFUND_CHN_ORDER_NO_IS_NULL = new ErrorCode(1007006002, "该订单的渠道订单为空"); |     ErrorCode PAY_REFUND_CHN_ORDER_NO_IS_NULL = new ErrorCode(1007006002, "该订单的渠道订单为空"); | ||||||
|     ErrorCode PAY_REFUND_POST_HANDLER_NOT_FOUND = new ErrorCode(1007006003, "未找到对应的退款后置处理类"); |     ErrorCode PAY_REFUND_POST_HANDLER_NOT_FOUND = new ErrorCode(1007006003, "未找到对应的退款后置处理类"); | ||||||
|     ErrorCode PAY_REFUND_NOT_FOUND = new ErrorCode(1007006004, "支付退款单不存在"); |     ErrorCode PAY_REFUND_NOT_FOUND = new ErrorCode(1007006004, "支付退款单不存在"); | ||||||
|     // TODO @aquan:下面还两个要合并上去哈。另外一般中英文之间要有空格。例如说, 新建一个 order 数据;这样可读性更好。 |  | ||||||
|  |  | ||||||
|     /** |     /** | ||||||
|      * ========== 支付商户信息 1-007-004-000 ========== |      * ========== 支付商户信息 1-007-004-000 ========== | ||||||
|      */ |      */ | ||||||
|     ErrorCode MERCHANT_NOT_EXISTS = new ErrorCode(1007004000, "支付商户信息不存在"); |     ErrorCode PAY_MERCHANT_NOT_EXISTS = new ErrorCode(1007004000, "支付商户信息不存在"); | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
|     /** |  | ||||||
|      * ========== 支付应用信息 1-007-005-000 ========== |  | ||||||
|      */ |  | ||||||
|     ErrorCode APP_NOT_EXISTS = new ErrorCode(1007005000, "支付应用信息不存在"); |  | ||||||
|  |  | ||||||
|  |  | ||||||
| } | } | ||||||
|   | |||||||
| @@ -1,7 +1,11 @@ | |||||||
| package cn.iocoder.yudao.framework.pay.core.client; | package cn.iocoder.yudao.framework.pay.core.client; | ||||||
|  |  | ||||||
|  | import cn.hutool.json.JSONUtil; | ||||||
|  | import cn.iocoder.yudao.framework.pay.core.enums.PayChannelEnum; | ||||||
| import com.fasterxml.jackson.annotation.JsonTypeInfo; | import com.fasterxml.jackson.annotation.JsonTypeInfo; | ||||||
|  |  | ||||||
|  | import javax.validation.Validator; | ||||||
|  |  | ||||||
| /** | /** | ||||||
|  * 支付客户端的配置,本质是支付渠道的配置 |  * 支付客户端的配置,本质是支付渠道的配置 | ||||||
|  * 每个不同的渠道,需要不同的配置,通过子类来定义 |  * 每个不同的渠道,需要不同的配置,通过子类来定义 | ||||||
| @@ -13,4 +17,10 @@ import com.fasterxml.jackson.annotation.JsonTypeInfo; | |||||||
| // 1. 序列化到时数据库时,增加 @class 属性。 | // 1. 序列化到时数据库时,增加 @class 属性。 | ||||||
| // 2. 反序列化到内存对象时,通过 @class 属性,可以创建出正确的类型 | // 2. 反序列化到内存对象时,通过 @class 属性,可以创建出正确的类型 | ||||||
| public interface PayClientConfig { | public interface PayClientConfig { | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * 验证配置参数是否正确 | ||||||
|  |      * @param validator 校验对象 | ||||||
|  |      */ | ||||||
|  |     void verifyParam(Validator validator); | ||||||
| } | } | ||||||
|   | |||||||
| @@ -2,9 +2,17 @@ package cn.iocoder.yudao.framework.pay.core.client.impl.alipay; | |||||||
|  |  | ||||||
| import cn.iocoder.yudao.framework.pay.core.client.PayClientConfig; | import cn.iocoder.yudao.framework.pay.core.client.PayClientConfig; | ||||||
| import lombok.Data; | import lombok.Data; | ||||||
|  | import org.springframework.beans.factory.annotation.Autowired; | ||||||
|  | import org.springframework.util.Assert; | ||||||
|  |  | ||||||
|  | import javax.annotation.Resource; | ||||||
|  | import javax.validation.ConstraintViolation; | ||||||
|  | import javax.validation.Validator; | ||||||
|  | import javax.validation.constraints.AssertTrue; | ||||||
| import javax.validation.constraints.NotBlank; | import javax.validation.constraints.NotBlank; | ||||||
| import javax.validation.constraints.NotNull; | import javax.validation.constraints.NotNull; | ||||||
|  | import java.util.Set; | ||||||
|  | import java.util.stream.Collectors; | ||||||
|  |  | ||||||
| // TODO 芋艿:参数校验 | // TODO 芋艿:参数校验 | ||||||
|  |  | ||||||
| @@ -106,4 +114,18 @@ public class AlipayPayClientConfig implements PayClientConfig { | |||||||
|     public interface ModeCertificate { |     public interface ModeCertificate { | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * 验证配置参数是否正确 | ||||||
|  |      * @param validator 校验对象 | ||||||
|  |      */ | ||||||
|  |     @Override | ||||||
|  |     public void verifyParam(Validator validator) { | ||||||
|  |         // 手动调用validate进行验证 | ||||||
|  |         Set<ConstraintViolation<AlipayPayClientConfig>> validate = validator.validate(this, | ||||||
|  |                 MODE_PUBLIC_KEY.equals(this.getMode()) ? ModePublicKey.class : ModeCertificate.class); | ||||||
|  |  | ||||||
|  |         // 断言没有异常 | ||||||
|  |         Assert.isTrue(validate.isEmpty(), validate.stream().map(ConstraintViolation::getMessage) | ||||||
|  |                 .collect(Collectors.joining(","))); | ||||||
|  |     } | ||||||
| } | } | ||||||
|   | |||||||
| @@ -1,17 +1,26 @@ | |||||||
| package cn.iocoder.yudao.framework.pay.core.client.impl.wx; | package cn.iocoder.yudao.framework.pay.core.client.impl.wx; | ||||||
|  |  | ||||||
| import cn.hutool.core.io.IoUtil; | import cn.hutool.core.io.IoUtil; | ||||||
|  | import cn.hutool.json.JSONObject; | ||||||
|  | import cn.hutool.json.JSONUtil; | ||||||
| import cn.iocoder.yudao.framework.pay.core.client.PayClientConfig; | import cn.iocoder.yudao.framework.pay.core.client.PayClientConfig; | ||||||
|  | import com.alibaba.fastjson.JSON; | ||||||
| import lombok.Data; | import lombok.Data; | ||||||
|  | import org.springframework.beans.factory.annotation.Autowired; | ||||||
|  | import org.springframework.util.Assert; | ||||||
|  |  | ||||||
|  | import javax.annotation.Resource; | ||||||
|  | import javax.validation.ConstraintViolation; | ||||||
|  | import javax.validation.Validator; | ||||||
|  | import javax.validation.constraints.AssertTrue; | ||||||
| import javax.validation.constraints.NotBlank; | import javax.validation.constraints.NotBlank; | ||||||
| import java.io.FileInputStream; | import java.io.FileInputStream; | ||||||
| import java.io.FileNotFoundException; | import java.io.FileNotFoundException; | ||||||
|  | import java.util.Set; | ||||||
|  | import java.util.stream.Collectors; | ||||||
|  |  | ||||||
| // TODO 芋艿:参数校验 | // TODO 芋艿:参数校验 | ||||||
|  |  | ||||||
| // TODO @aquan: 不要全文件格式化哈,去掉下 <p> 看着不太友好哈 |  | ||||||
|  |  | ||||||
| /** | /** | ||||||
|  * 微信支付的 PayClientConfig 实现类 |  * 微信支付的 PayClientConfig 实现类 | ||||||
|  * 属性主要来自 {@link com.github.binarywang.wxpay.config.WxPayConfig} 的必要属性 |  * 属性主要来自 {@link com.github.binarywang.wxpay.config.WxPayConfig} 的必要属性 | ||||||
| @@ -24,13 +33,11 @@ public class WXPayClientConfig implements PayClientConfig { | |||||||
|     // TODO 芋艿:V2 or V3 客户端 |     // TODO 芋艿:V2 or V3 客户端 | ||||||
|     /** |     /** | ||||||
|      * API 版本 - V2 |      * API 版本 - V2 | ||||||
|      * <p> |  | ||||||
|      * https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=4_1 |      * https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=4_1 | ||||||
|      */ |      */ | ||||||
|     public static final String API_VERSION_V2 = "v2"; |     public static final String API_VERSION_V2 = "v2"; | ||||||
|     /** |     /** | ||||||
|      * API 版本 - V3 |      * API 版本 - V3 | ||||||
|      * <p> |  | ||||||
|      * https://pay.weixin.qq.com/wiki/doc/apiv3/wechatpay/wechatpay-1.shtml |      * https://pay.weixin.qq.com/wiki/doc/apiv3/wechatpay/wechatpay-1.shtml | ||||||
|      */ |      */ | ||||||
|     public static final String API_VERSION_V3 = "v3"; |     public static final String API_VERSION_V3 = "v3"; | ||||||
| @@ -56,7 +63,7 @@ public class WXPayClientConfig implements PayClientConfig { | |||||||
|     /** |     /** | ||||||
|      * 商户密钥 |      * 商户密钥 | ||||||
|      */ |      */ | ||||||
|     @NotBlank(message = "商户密钥 不能为空", groups = {V2.class}) |     @NotBlank(message = "商户密钥 不能为空", groups = V2.class) | ||||||
|     private String mchKey; |     private String mchKey; | ||||||
|     /** |     /** | ||||||
|      * apiclient_cert.p12 证书文件的绝对路径或者以 classpath: 开头的类路径. |      * apiclient_cert.p12 证书文件的绝对路径或者以 classpath: 开头的类路径. | ||||||
| @@ -70,11 +77,9 @@ public class WXPayClientConfig implements PayClientConfig { | |||||||
|     /** |     /** | ||||||
|      * apiclient_key.pem 证书文件的绝对路径或者以 classpath: 开头的类路径. |      * apiclient_key.pem 证书文件的绝对路径或者以 classpath: 开头的类路径. | ||||||
|      * 对应的字符串 |      * 对应的字符串 | ||||||
|      * <p> |  | ||||||
|      * 注意,可通过 {@link #main(String[])} 读取 |      * 注意,可通过 {@link #main(String[])} 读取 | ||||||
|      */ |      */ | ||||||
|     // TODO @aquan:对于只有一个值的时候,直接 groups = V3.class 即可,简洁。例如说,我们在 Spring MVC 注解,url 可以多个,也只写单个,一个道理哈 |     @NotBlank(message = "apiclient_key 不能为空", groups = V3.class) | ||||||
|     @NotBlank(message = "apiclient_key 不能为空", groups = {V3.class}) |  | ||||||
|     private String privateKeyContent; |     private String privateKeyContent; | ||||||
|     /** |     /** | ||||||
|      * apiclient_cert.pem 证书文件的绝对路径或者以 classpath: 开头的类路径. |      * apiclient_cert.pem 证书文件的绝对路径或者以 classpath: 开头的类路径. | ||||||
| @@ -82,21 +87,14 @@ public class WXPayClientConfig implements PayClientConfig { | |||||||
|      * <p> |      * <p> | ||||||
|      * 注意,可通过 {@link #main(String[])} 读取 |      * 注意,可通过 {@link #main(String[])} 读取 | ||||||
|      */ |      */ | ||||||
|     @NotBlank(message = "apiclient_cert 不能为空", groups = {V3.class}) |     @NotBlank(message = "apiclient_cert 不能为空", groups = V3.class) | ||||||
|     private String privateCertContent; |     private String privateCertContent; | ||||||
|     /** |     /** | ||||||
|      * apiV3 秘钥值 |      * apiV3 秘钥值 | ||||||
|      */ |      */ | ||||||
|     @NotBlank(message = "apiV3 秘钥值 不能为空", groups = {V3.class}) |     @NotBlank(message = "apiV3 秘钥值 不能为空", groups = V3.class) | ||||||
|     private String apiV3Key; |     private String apiV3Key; | ||||||
|  |  | ||||||
|     public static void main(String[] args) throws FileNotFoundException { |  | ||||||
|         String path = "/Users/yunai/Downloads/wx_pay/apiclient_cert.p12"; |  | ||||||
| //        String path = "/Users/yunai/Downloads/wx_pay/apiclient_key.pem"; |  | ||||||
| //        String path = "/Users/yunai/Downloads/wx_pay/apiclient_cert.pem"; |  | ||||||
|         System.out.println(IoUtil.readUtf8(new FileInputStream(path))); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|  |  | ||||||
|     /** |     /** | ||||||
|      * 分组校验 v2版本 |      * 分组校验 v2版本 | ||||||
| @@ -110,4 +108,26 @@ public class WXPayClientConfig implements PayClientConfig { | |||||||
|     public interface V3 { |     public interface V3 { | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * 验证配置参数是否正确 | ||||||
|  |      * @param validator 校验对象 | ||||||
|  |      */ | ||||||
|  |     @Override | ||||||
|  |     public void verifyParam(Validator validator) { | ||||||
|  |         // 手动调用validate进行验证 | ||||||
|  |         Set<ConstraintViolation<PayClientConfig>> validate = validator.validate(this, | ||||||
|  |                 this.getApiVersion().equals(API_VERSION_V2) ? V2.class : V3.class); | ||||||
|  |         // 断言没有异常 | ||||||
|  |         Assert.isTrue(validate.isEmpty(), validate.stream().map(ConstraintViolation::getMessage) | ||||||
|  |                 .collect(Collectors.joining(","))); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public static void main(String[] args) throws FileNotFoundException { | ||||||
|  |         String path = "/Users/yunai/Downloads/wx_pay/apiclient_cert.p12"; | ||||||
|  |         /// String path = "/Users/yunai/Downloads/wx_pay/apiclient_key.pem"; | ||||||
|  |         /// String path = "/Users/yunai/Downloads/wx_pay/apiclient_cert.pem"; | ||||||
|  |         System.out.println(IoUtil.readUtf8(new FileInputStream(path))); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |  | ||||||
| } | } | ||||||
|   | |||||||
| @@ -1,6 +1,9 @@ | |||||||
| package cn.iocoder.yudao.framework.pay.core.enums; | package cn.iocoder.yudao.framework.pay.core.enums; | ||||||
|  |  | ||||||
| import cn.hutool.core.util.ArrayUtil; | import cn.hutool.core.util.ArrayUtil; | ||||||
|  | import cn.iocoder.yudao.framework.pay.core.client.PayClientConfig; | ||||||
|  | import cn.iocoder.yudao.framework.pay.core.client.impl.alipay.AlipayPayClientConfig; | ||||||
|  | import cn.iocoder.yudao.framework.pay.core.client.impl.wx.WXPayClientConfig; | ||||||
| import lombok.AllArgsConstructor; | import lombok.AllArgsConstructor; | ||||||
| import lombok.Getter; | import lombok.Getter; | ||||||
|  |  | ||||||
| @@ -49,21 +52,21 @@ public enum PayChannelEnum { | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     /** |     /** | ||||||
|      * 判断当前渠道是那种支付方式 |      * 根据编码得到支付类 | ||||||
|      * @param code |      * @param code 编码 | ||||||
|      * @return |      * @return 支付配置类 | ||||||
|      */ |      */ | ||||||
|     public static String verifyWechatOrAliPay(String code){ |     public static Class<? extends PayClientConfig> findByCodeGetClass(String code){ | ||||||
|         switch (PayChannelEnum.getByCode(code)){ |         switch (PayChannelEnum.getByCode(code)){ | ||||||
|             case WX_PUB: |             case WX_PUB: | ||||||
|             case WX_LITE: |             case WX_LITE: | ||||||
|             case WX_APP: |             case WX_APP: | ||||||
|                 return WECHAT; |                 return WXPayClientConfig.class; | ||||||
|             case ALIPAY_PC: |             case ALIPAY_PC: | ||||||
|             case ALIPAY_WAP: |             case ALIPAY_WAP: | ||||||
|             case ALIPAY_APP: |             case ALIPAY_APP: | ||||||
|             case ALIPAY_QR: |             case ALIPAY_QR: | ||||||
|                 return ALIPAY; |                 return AlipayPayClientConfig.class; | ||||||
|         } |         } | ||||||
|         return null; |         return null; | ||||||
|     } |     } | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user
	 aquan
					aquan