Commit 6069a387ea17434ec9bd246ae070bae5529311cc

Authored by aquan
1 parent b18cd457

优化完善支付应用和支付渠道代码逻辑,完善单元测试,基于validator完成手动校验config

Showing 36 changed files with 1524 additions and 922 deletions
sql/pay-dict.sql
... ... @@ -22,3 +22,19 @@ INSERT INTO `sys_dict_type` (`name`, `type`, `status`, `remark`, `creator`, `cre
22 22  
23 23 INSERT INTO `sys_dict_data` (`sort`, `label`, `value`, `dict_type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1, 'v2', 'v2', 'pay_channel_wechat_version', 0, 'v2版本', '1', '2021-11-08 17:00:58', '1', '2021-11-08 17:00:58', b'0');
24 24 INSERT INTO `sys_dict_data` (`sort`, `label`, `value`, `dict_type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2, 'v3', 'v3', 'pay_channel_wechat_version', 0, 'v3版本', '1', '2021-11-08 17:01:07', '1', '2021-11-08 17:01:07', b'0');
  25 +
  26 +-- 支付渠道支付宝算法类型
  27 +INSERT INTO `sys_dict_type` (`name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES ('支付渠道支付宝算法类型', 'pay_channel_alipay_sign_type', 0, '支付渠道支付宝算法类型', '1', '2021-11-18 15:39:09', '1', '2021-11-18 15:39:09', b'0');
  28 +INSERT INTO `sys_dict_data` (`sort`, `label`, `value`, `dict_type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1, 'RSA2', 'RSA2', 'pay_channel_alipay_sign_type', 0, 'RSA2', '1', '2021-11-18 15:39:29', '1', '2021-11-18 15:39:29', b'0');
  29 +
  30 +
  31 +-- 支付渠道支付宝公钥类型
  32 +INSERT INTO `sys_dict_type` (`name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES ('支付渠道支付宝公钥类型', 'pay_channel_alipay_mode', 0, '支付渠道支付宝公钥类型', '1', '2021-11-18 15:44:28', '1', '2021-11-18 15:44:28', b'0');
  33 +INSERT INTO `sys_dict_data` (`sort`, `label`, `value`, `dict_type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1, '公钥模式', '1', 'pay_channel_alipay_mode', 0, '公钥模式:privateKey + alipayPublicKey', '1', '2021-11-18 15:45:23', '1', '2021-11-18 15:45:23', b'0');
  34 +INSERT INTO `sys_dict_data` (`sort`, `label`, `value`, `dict_type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2, '证书模式', '2', 'pay_channel_alipay_mode', 0, '证书模式:appCertContent + alipayPublicCertContent + rootCertContent', '1', '2021-11-18 15:45:40', '1', '2021-11-18 15:45:40', b'0');
  35 +
  36 +
  37 +-- 支付宝网关地址
  38 +INSERT INTO `sys_dict_type` (`name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES ('支付宝网关地址', 'pay_channel_alipay_server_type', 0, '支付宝网关地址', '1', '2021-11-18 16:58:55', '1', '2021-11-18 17:01:34', b'0');
  39 +INSERT INTO `sys_dict_data` (`sort`, `label`, `value`, `dict_type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1, '线上', 'https://openapi.alipay.com/gateway.do', 'pay_channel_alipay_server_type', 0, '网关地址 - 线上', '1', '2021-11-18 16:59:32', '1', '2021-11-21 17:37:29', b'0');
  40 +INSERT INTO `sys_dict_data` (`sort`, `label`, `value`, `dict_type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2, '沙箱', 'https://openapi.alipaydev.com/gateway.do', 'pay_channel_alipay_server_type', 0, '网关地址 - 沙箱', '1', '2021-11-18 16:59:48', '1', '2021-11-21 17:37:39', b'0');
... ...
yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/pay/controller/app/PayAppController.java
... ... @@ -123,7 +123,7 @@ public class PayAppController {
123 123  
124 124 // 得到所有的应用编号,查出所有的通道
125 125 Collection<Long> payAppIds = CollectionUtils.convertList(pageResult.getList(), PayAppDO::getId);
126   - List<PayChannelDO> channels = channelService.getSimpleChannels(payAppIds);
  126 + List<PayChannelDO> channels = channelService.getChannelListByAppIds(payAppIds);
127 127  
128 128 // 得到所有的商户信息
129 129 Collection<Long> merchantIds = CollectionUtils.convertList(pageResult.getList(), PayAppDO::getMerchantId);
... ...
yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/pay/controller/app/vo/PayAppExportReqVO.java
... ... @@ -27,8 +27,8 @@ public class PayAppExportReqVO {
27 27 @ApiModelProperty(value = "退款结果的回调地址")
28 28 private String refundNotifyUrl;
29 29  
30   - @ApiModelProperty(value = "商户编号")
31   - private Long merchantId;
  30 + @ApiModelProperty(value = "商户名称")
  31 + private String merchantName;
32 32  
33 33 @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
34 34 @ApiModelProperty(value = "开始创建时间")
... ...
yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/pay/controller/channel/PayChannelController.java
1 1 package cn.iocoder.yudao.adminserver.modules.pay.controller.channel;
2 2  
  3 +import cn.iocoder.yudao.adminserver.modules.pay.controller.channel.vo.*;
  4 +import cn.iocoder.yudao.adminserver.modules.pay.convert.channel.PayChannelConvert;
  5 +import cn.iocoder.yudao.adminserver.modules.pay.service.channel.PayChannelService;
3 6 import cn.iocoder.yudao.coreservice.modules.pay.dal.dataobject.merchant.PayChannelDO;
4   -import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
5   -import cn.iocoder.yudao.framework.pay.core.client.impl.wx.WXPayClientConfig;
6   -import cn.iocoder.yudao.framework.pay.core.client.impl.wx.WXPubPayClient;
7   -import org.springframework.util.Assert;
  7 +import cn.iocoder.yudao.framework.common.pojo.CommonResult;
  8 +import cn.iocoder.yudao.framework.common.pojo.PageResult;
  9 +import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils;
  10 +import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog;
  11 +import io.swagger.annotations.Api;
  12 +import io.swagger.annotations.ApiImplicitParam;
  13 +import io.swagger.annotations.ApiImplicitParams;
  14 +import io.swagger.annotations.ApiOperation;
  15 +import org.springframework.security.access.prepost.PreAuthorize;
  16 +import org.springframework.validation.annotation.Validated;
8 17 import org.springframework.web.bind.annotation.*;
9 18  
10 19 import javax.annotation.Resource;
11   -
12   -import org.springframework.validation.annotation.Validated;
13   -import org.springframework.security.access.prepost.PreAuthorize;
14   -
15   -import io.swagger.annotations.*;
16   -
17   -import javax.validation.*;
18   -import javax.servlet.http.*;
19   -import java.util.*;
  20 +import javax.servlet.http.HttpServletResponse;
  21 +import javax.validation.Valid;
20 22 import java.io.IOException;
21   -
22   -import cn.iocoder.yudao.framework.common.pojo.PageResult;
23   -import cn.iocoder.yudao.framework.common.pojo.CommonResult;
  23 +import java.util.Collection;
  24 +import java.util.List;
24 25  
25 26 import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
  27 +import static cn.iocoder.yudao.framework.operatelog.core.enums.OperateTypeEnum.EXPORT;
26 28  
27   -import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils;
28   -
29   -import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog;
30   -
31   -import static cn.iocoder.yudao.framework.operatelog.core.enums.OperateTypeEnum.*;
32   -
33   -import cn.iocoder.yudao.adminserver.modules.pay.controller.channel.vo.*;
34   -import cn.iocoder.yudao.adminserver.modules.pay.convert.channel.PayChannelConvert;
35   -import cn.iocoder.yudao.adminserver.modules.pay.service.channel.PayChannelService;
36   -import org.springframework.web.multipart.MultipartFile;
37   -
  29 +/**
  30 + * 支付渠道 controller 组件
  31 + * @author aquan
  32 + */
38 33 @Api(tags = "支付渠道")
39 34 @RestController
40 35 @RequestMapping("/pay/channel")
... ... @@ -44,7 +39,7 @@ public class PayChannelController {
44 39 @Resource
45 40 private PayChannelService channelService;
46 41  
47   - // todo 芋艿 这几个生成的方法是没用到的 您看要不删除了把? -----start
  42 +
48 43 @PostMapping("/create")
49 44 @ApiOperation("创建支付渠道 ")
50 45 @PreAuthorize("@ss.hasPermission('pay:channel:create')")
... ... @@ -108,28 +103,7 @@ public class PayChannelController {
108 103 ExcelUtils.write(response, "支付渠道.xls", "数据", PayChannelExcelVO.class, datas);
109 104 }
110 105  
111   - // todo 芋艿 这几个生成的方法是没用到的 您看要不删除了把? -----end
112   -
113   - @PostMapping("/parsing-pem")
114   - @ApiOperation("解析pem证书转换为字符串")
115   - @PreAuthorize("@ss.hasPermission('pay:channel:parsing')")
116   - @ApiImplicitParam(name = "file", value = "pem文件", required = true, dataTypeClass = MultipartFile.class)
117   - public CommonResult<String> parsingPemFile(@RequestParam("file") MultipartFile file) {
118   - return success(channelService.parsingPemFile(file));
119   - }
120   -
121   - @PostMapping("/create-wechat")
122   - @ApiOperation("创建支付渠道 ")
123   - @PreAuthorize("@ss.hasPermission('pay:channel:create')")
124   - public CommonResult<Long> createWechatChannel(@Valid @RequestBody PayWechatChannelCreateReqVO reqVO) {
125   - // 针对于 V2 或者 V3 版本的参数校验
126   - this.paramAdvanceCheck(reqVO.getWeChatConfig().getApiVersion(),reqVO.getWeChatConfig().getMchKey(),
127   - reqVO.getWeChatConfig().getPrivateKeyContent(),reqVO.getWeChatConfig().getPrivateCertContent());
128   -
129   - return success(channelService.createWechatChannel(reqVO));
130   - }
131   -
132   - @GetMapping("/get-wechat")
  106 + @GetMapping("/get-channel")
133 107 @ApiOperation("根据条件查询微信支付渠道")
134 108 @ApiImplicitParams({
135 109 @ApiImplicitParam(name = "merchantId", value = "商户编号",
... ... @@ -140,51 +114,16 @@ public class PayChannelController {
140 114 required = true, example = "wx_pub", dataTypeClass = String.class)
141 115 })
142 116 @PreAuthorize("@ss.hasPermission('pay:channel:query')")
143   - public CommonResult<PayWeChatChannelRespVO> getWeChatChannel(
  117 + public CommonResult<PayChannelRespVO> getChannel(
144 118 @RequestParam Long merchantId, @RequestParam Long appId, @RequestParam String code) {
145 119  
146 120 // 獲取渠道
147 121 PayChannelDO channel = channelService.getChannelByConditions(merchantId, appId, code);
148 122 if (channel == null) {
149   - return success(new PayWeChatChannelRespVO());
  123 + return success(new PayChannelRespVO());
150 124 }
151   -
152 125 // 拼凑数据
153   - PayWeChatChannelRespVO respVo = PayChannelConvert.INSTANCE.convert2(channel);
154   - WXPayClientConfig config = (WXPayClientConfig) channel.getConfig();
155   - respVo.setWeChatConfig(PayChannelConvert.INSTANCE.configConvert(config));
  126 + PayChannelRespVO respVo = PayChannelConvert.INSTANCE.convert(channel);
156 127 return success(respVo);
157 128 }
158   -
159   - @PutMapping("/update-wechat")
160   - @ApiOperation("更新微信支付渠道 ")
161   - @PreAuthorize("@ss.hasPermission('pay:channel:update')")
162   - public CommonResult<Boolean> updateWechatChannel(@Valid @RequestBody PayWechatChannelUpdateReqVO updateReqVO) {
163   -
164   - // 针对于 V2 或者 V3 版本的参数校验
165   - this.paramAdvanceCheck(updateReqVO.getWeChatConfig().getApiVersion(),updateReqVO.getWeChatConfig().getMchKey(),
166   - updateReqVO.getWeChatConfig().getPrivateKeyContent(),updateReqVO.getWeChatConfig().getPrivateCertContent());
167   -
168   - channelService.updateWechatChannel(updateReqVO);
169   - return success(true);
170   - }
171   -
172   - /**
173   - * 预检测微信秘钥参数
174   - * @param version 版本
175   - * @param mchKey v2版本秘钥
176   - * @param privateKeyContent v3版本apiclient_key
177   - * @param privateCertContent v3版本中apiclient_cert
178   - */
179   - private void paramAdvanceCheck(String version, String mchKey, String privateKeyContent, String privateCertContent) {
180   - // 针对于 V2 或者 V3 版本的参数校验
181   - if (version.equals(WXPayClientConfig.API_VERSION_V2)) {
182   - Assert.notNull(mchKey, "v2版本中商户密钥不可为空");
183   - }
184   - if (version.equals(WXPayClientConfig.API_VERSION_V3)) {
185   - Assert.notNull(privateKeyContent, "v3版本apiclient_key.pem不可为空");
186   - Assert.notNull(privateCertContent, "v3版本中apiclient_cert.pem不可为空");
187   - }
188   - }
189   -
190 129 }
... ...
yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/pay/controller/channel/vo/PayChannelCreateReqVO.java
1 1 package cn.iocoder.yudao.adminserver.modules.pay.controller.channel.vo;
2 2  
3 3 import io.swagger.annotations.ApiModel;
  4 +import io.swagger.annotations.ApiModelProperty;
4 5 import lombok.Data;
5 6 import lombok.EqualsAndHashCode;
6 7 import lombok.ToString;
7 8  
  9 +import javax.validation.constraints.NotBlank;
  10 +
8 11 @ApiModel("支付渠道 创建 Request VO")
9 12 @Data
10 13 @EqualsAndHashCode(callSuper = true)
11 14 @ToString(callSuper = true)
12 15 public class PayChannelCreateReqVO extends PayChannelBaseVO {
13 16  
14   - // TODO @aquan:我在想,要不这个创建和修改特殊一点。前端传递 string 过来,后端解析成对应的。因为有 code,所以我们都知道是哪个配置类。
15   - // 然后,在 PayChannelEnum 里,枚举每个渠道对应的配置类。另外,我们就不单独给配置类搞 vo 了。参数校验,通过手动调用 Validator 去校验。
16   - // 通过这样的方式,VO 和 api 都收成,一个 update,一个 create
  17 +
  18 + @ApiModelProperty(value = "通道配置的json字符串")
  19 + @NotBlank(message = "通道配置不能为空")
  20 + private String config;
  21 +
17 22  
18 23 }
... ...
yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/pay/controller/channel/vo/PayChannelRespVO.java
... ... @@ -16,4 +16,6 @@ public class PayChannelRespVO extends PayChannelBaseVO {
16 16 @ApiModelProperty(value = "创建时间", required = true)
17 17 private Date createTime;
18 18  
  19 + @ApiModelProperty(value = "配置", required = true)
  20 + private String config;
19 21 }
... ...
yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/pay/controller/channel/vo/PayChannelUpdateReqVO.java
... ... @@ -15,4 +15,7 @@ public class PayChannelUpdateReqVO extends PayChannelBaseVO {
15 15 @NotNull(message = "商户编号不能为空")
16 16 private Long id;
17 17  
  18 + @ApiModelProperty(value = "通道配置的json字符串")
  19 + @NotBlank(message = "通道配置不能为空")
  20 + private String config;
18 21 }
... ...
yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/pay/controller/channel/vo/PayWeChatChannelRespVO.java deleted
1   -package cn.iocoder.yudao.adminserver.modules.pay.controller.channel.vo;
2   -
3   -import io.swagger.annotations.ApiModel;
4   -import io.swagger.annotations.ApiModelProperty;
5   -import lombok.Data;
6   -import lombok.EqualsAndHashCode;
7   -import lombok.ToString;
8   -
9   -import javax.validation.Valid;
10   -import java.util.Date;
11   -
12   -@ApiModel("支付微信渠道 Response VO")
13   -@Data
14   -@EqualsAndHashCode(callSuper = true)
15   -@ToString(callSuper = true)
16   -public class PayWeChatChannelRespVO extends PayChannelBaseVO {
17   -
18   - @ApiModelProperty(value = "商户编号", required = true)
19   - private Long id;
20   -
21   - @ApiModelProperty(value = "创建时间", required = true)
22   - private Date createTime;
23   -
24   - /**
25   - * 微信配置类
26   - */
27   - @Valid
28   - private WeChatConfig weChatConfig;
29   -
30   - /**
31   - * 微信配置类
32   - */
33   - @Data
34   - @ApiModel("微信配置类")
35   - public static class WeChatConfig {
36   -
37   - @ApiModelProperty(value = "公众号或者小程序的 appid", required = true, example = "wx041349c6f39b261b")
38   - private String appId;
39   -
40   -
41   - @ApiModelProperty(value = "商户号", required = true, example = "1545083881")
42   - private String mchId;
43   -
44   - @ApiModelProperty(value = "API 版本", required = true, example = "v2")
45   - private String apiVersion;
46   -
47   - // ========== V2 版本的参数 ==========
48   -
49   - @ApiModelProperty(value = "商户密钥", required = true, example = "0alL64UDQdaCwiKZ73ib7ypaIjMns06p")
50   - private String mchKey;
51   -
52   - /// todo @aquan 暂不支持 .p12上传 后期优化
53   - /// apiclient_cert.p12 证书文件的绝对路径或者以 classpath: 开头的类路径. 对应的字符串
54   - /// private String keyContent;
55   -
56   - // ========== V3 版本的参数 ==========
57   -
58   - @ApiModelProperty(value = "apiclient_key.pem 证书对应的字符串", required = true, example = "-----BEGIN PRIVATE KEY-----")
59   - private String privateKeyContent;
60   -
61   - @ApiModelProperty(value = "apiclient_cert.pem 证书对应的字符串", required = true, example = "-----BEGIN CERTIFICATE-----")
62   - private String privateCertContent;
63   - }
64   -
65   -}
yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/pay/controller/channel/vo/PayWechatChannelCreateReqVO.java deleted
1   -package cn.iocoder.yudao.adminserver.modules.pay.controller.channel.vo;
2   -
3   -import com.fasterxml.jackson.annotation.JsonClassDescription;
4   -import io.swagger.annotations.ApiModel;
5   -import io.swagger.annotations.ApiModelProperty;
6   -import lombok.Data;
7   -import lombok.EqualsAndHashCode;
8   -import lombok.ToString;
9   -
10   -import javax.validation.Valid;
11   -import javax.validation.constraints.NotBlank;
12   -import javax.validation.constraints.NotNull;
13   -
14   -/**
15   - * 支付渠道微信创建Request VO
16   - * @author aquan
17   - */
18   -@ApiModel("支付渠道微信创建Request VO")
19   -@Data
20   -@EqualsAndHashCode(callSuper = true)
21   -@ToString(callSuper = true)
22   -public class PayWechatChannelCreateReqVO extends PayChannelBaseVO {
23   -
24   - /**
25   - * 微信配置类
26   - */
27   - @Valid
28   - private WeChatConfig weChatConfig;
29   -
30   - /**
31   - * 微信配置类
32   - */
33   - @Data
34   - @ApiModel("微信配置类")
35   - public static class WeChatConfig {
36   -
37   - @NotBlank(message = "公众号或者小程序的 appid不能为空")
38   - @ApiModelProperty(value = "公众号或者小程序的 appid", required = true, example = "wx041349c6f39b261b")
39   - private String appId;
40   -
41   -
42   - @NotBlank(message = "商户号不能为空")
43   - @ApiModelProperty(value = "商户号", required = true, example = "1545083881")
44   - private String mchId;
45   -
46   - @NotNull(message = "API 版本不能为空")
47   - @ApiModelProperty(value = "API 版本", required = true, example = "v2")
48   - private String apiVersion;
49   -
50   - // ========== V2 版本的参数 ==========
51   -
52   - @ApiModelProperty(value = "商户密钥", required = true, example = "0alL64UDQdaCwiKZ73ib7ypaIjMns06p")
53   - private String mchKey;
54   -
55   - /// todo @aquan 暂不支持 .p12上传 后期优化
56   - /// apiclient_cert.p12 证书文件的绝对路径或者以 classpath: 开头的类路径. 对应的字符串
57   - /// private String keyContent;
58   -
59   - // ========== V3 版本的参数 ==========
60   -
61   - @ApiModelProperty(value = "apiclient_key.pem 证书对应的字符串", required = true, example = "-----BEGIN PRIVATE KEY-----")
62   - private String privateKeyContent;
63   -
64   - @ApiModelProperty(value = "apiclient_cert.pem 证书对应的字符串", required = true, example = "-----BEGIN CERTIFICATE-----")
65   - private String privateCertContent;
66   - }
67   -
68   -}
yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/pay/controller/channel/vo/PayWechatChannelUpdateReqVO.java deleted
1   -package cn.iocoder.yudao.adminserver.modules.pay.controller.channel.vo;
2   -
3   -import io.swagger.annotations.ApiModel;
4   -import io.swagger.annotations.ApiModelProperty;
5   -import lombok.Data;
6   -import lombok.EqualsAndHashCode;
7   -import lombok.ToString;
8   -
9   -import javax.validation.Valid;
10   -import javax.validation.constraints.NotBlank;
11   -import javax.validation.constraints.NotNull;
12   -
13   -@ApiModel("支付渠道 更新 Request VO")
14   -@Data
15   -@EqualsAndHashCode(callSuper = true)
16   -@ToString(callSuper = true)
17   -public class PayWechatChannelUpdateReqVO extends PayChannelBaseVO {
18   -
19   - @ApiModelProperty(value = "商户编号", required = true)
20   - @NotNull(message = "商户编号不能为空")
21   - private Long id;
22   -
23   - /**
24   - * 微信配置类
25   - */
26   - @Valid
27   - private PayWechatChannelCreateReqVO.WeChatConfig weChatConfig;
28   -
29   - /**
30   - * 微信配置类
31   - */
32   - @Data
33   - @ApiModel("微信配置类")
34   - public static class WeChatConfig {
35   -
36   - @NotBlank(message = "公众号或者小程序的 appid不能为空")
37   - @ApiModelProperty(value = "公众号或者小程序的 appid", required = true, example = "wx041349c6f39b261b")
38   - private String appId;
39   -
40   -
41   - @NotBlank(message = "商户号不能为空")
42   - @ApiModelProperty(value = "商户号", required = true, example = "1545083881")
43   - private String mchId;
44   -
45   - @NotNull(message = "API 版本不能为空")
46   - @ApiModelProperty(value = "API 版本", required = true, example = "v2")
47   - private String apiVersion;
48   -
49   - // ========== V2 版本的参数 ==========
50   -
51   - @ApiModelProperty(value = "商户密钥", required = true, example = "0alL64UDQdaCwiKZ73ib7ypaIjMns06p")
52   - private String mchKey;
53   -
54   - /// todo @aquan 暂不支持 .p12上传 后期优化
55   - /// apiclient_cert.p12 证书文件的绝对路径或者以 classpath: 开头的类路径. 对应的字符串
56   - /// private String keyContent;
57   -
58   - // ========== V3 版本的参数 ==========
59   -
60   - @ApiModelProperty(value = "apiclient_key.pem 证书对应的字符串", required = true, example = "-----BEGIN PRIVATE KEY-----")
61   - private String privateKeyContent;
62   -
63   - @ApiModelProperty(value = "apiclient_cert.pem 证书对应的字符串", required = true, example = "-----BEGIN CERTIFICATE-----")
64   - private String privateCertContent;
65   -
66   - // TODO @aquan:参数校验。可以使用 @AssertTrue,v2 和 v3 的
67   - }
68   -}
yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/pay/convert/channel/PayChannelConvert.java
... ... @@ -23,15 +23,12 @@ public interface PayChannelConvert {
23 23 PayChannelConvert INSTANCE = Mappers.getMapper(PayChannelConvert.class);
24 24  
25 25 @Mapping(target = "config",ignore = true)
26   - PayChannelDO convert(PayWechatChannelCreateReqVO bean);
27   -
28   - @Mapping(target = "config",ignore = true)
29   - PayChannelDO convert(PayWechatChannelUpdateReqVO bean);
30   -
31 26 PayChannelDO convert(PayChannelCreateReqVO bean);
32 27  
  28 + @Mapping(target = "config",ignore = true)
33 29 PayChannelDO convert(PayChannelUpdateReqVO bean);
34 30  
  31 + @Mapping(target = "config",expression = "java(cn.iocoder.yudao.framework.common.util.json.JsonUtils.toJsonString(bean.getConfig()))")
35 32 PayChannelRespVO convert(PayChannelDO bean);
36 33  
37 34 List<PayChannelRespVO> convertList(List<PayChannelDO> list);
... ... @@ -39,13 +36,7 @@ public interface PayChannelConvert {
39 36 PageResult<PayChannelRespVO> convertPage(PageResult<PayChannelDO> page);
40 37  
41 38 List<PayChannelExcelVO> convertList02(List<PayChannelDO> list);
42   -
43   - WXPayClientConfig configConvert(PayWechatChannelCreateReqVO.WeChatConfig bean);
44 39  
45   - WXPayClientConfig configConvert(PayWechatChannelUpdateReqVO.WeChatConfig bean);
46 40  
47   - @Mapping(target = "weChatConfig",ignore = true)
48   - PayWeChatChannelRespVO convert2(PayChannelDO bean);
49 41  
50   - PayWeChatChannelRespVO.WeChatConfig configConvert(WXPayClientConfig bean);
51 42 }
... ...
yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/pay/dal/mysql/app/PayAppMapper.java
... ... @@ -31,14 +31,14 @@ public interface PayAppMapper extends BaseMapperX&lt;PayAppDO&gt; {
31 31 .orderByDesc("id"));
32 32 }
33 33  
34   - default List<PayAppDO> selectList(PayAppExportReqVO reqVO) {
  34 + default List<PayAppDO> selectList(PayAppExportReqVO reqVO, Collection<Long> merchantIds) {
35 35 return selectList(new QueryWrapperX<PayAppDO>()
36 36 .likeIfPresent("name", reqVO.getName())
37 37 .eqIfPresent("status", reqVO.getStatus())
38 38 .eqIfPresent("remark", reqVO.getRemark())
39 39 .eqIfPresent("pay_notify_url", reqVO.getPayNotifyUrl())
40 40 .eqIfPresent("refund_notify_url", reqVO.getRefundNotifyUrl())
41   - .eqIfPresent("merchant_id", reqVO.getMerchantId())
  41 + .inIfPresent("merchant_id", merchantIds)
42 42 .betweenIfPresent("create_time", reqVO.getBeginCreateTime(), reqVO.getEndCreateTime())
43 43 .orderByDesc("id"));
44 44 }
... ...
yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/pay/dal/mysql/channel/PayChannelMapper.java
... ... @@ -6,6 +6,7 @@ import cn.iocoder.yudao.coreservice.modules.pay.dal.dataobject.merchant.PayChann
6 6 import cn.iocoder.yudao.framework.common.pojo.PageResult;
7 7 import cn.iocoder.yudao.framework.mybatis.core.query.QueryWrapperX;
8 8 import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
  9 +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
9 10 import org.apache.ibatis.annotations.Mapper;
10 11 import cn.iocoder.yudao.adminserver.modules.pay.controller.channel.vo.*;
11 12  
... ... @@ -26,7 +27,7 @@ public interface PayChannelMapper extends BaseMapperX&lt;PayChannelDO&gt; {
26 27 .eqIfPresent("fee_rate", reqVO.getFeeRate())
27 28 .eqIfPresent("merchant_id", reqVO.getMerchantId())
28 29 .eqIfPresent("app_id", reqVO.getAppId())
29   - .eqIfPresent("config", reqVO.getConfig())
  30 + // .eqIfPresent("config", reqVO.getConfig())
30 31 .betweenIfPresent("create_time", reqVO.getBeginCreateTime(), reqVO.getEndCreateTime())
31 32 .orderByDesc("id") );
32 33 }
... ... @@ -39,9 +40,52 @@ public interface PayChannelMapper extends BaseMapperX&lt;PayChannelDO&gt; {
39 40 .eqIfPresent("fee_rate", reqVO.getFeeRate())
40 41 .eqIfPresent("merchant_id", reqVO.getMerchantId())
41 42 .eqIfPresent("app_id", reqVO.getAppId())
42   - .eqIfPresent("config", reqVO.getConfig())
  43 + // .eqIfPresent("config", reqVO.getConfig())
43 44 .betweenIfPresent("create_time", reqVO.getBeginCreateTime(), reqVO.getEndCreateTime())
44 45 .orderByDesc("id") );
45 46 }
46 47  
  48 + /**
  49 + * 根据条件获取通道数量
  50 + *
  51 + * @param merchantId 商户编号
  52 + * @param appid 应用编号
  53 + * @param code 通道编码
  54 + * @return 数量
  55 + */
  56 + default Integer getChannelCountByConditions(Long merchantId, Long appid, String code) {
  57 +
  58 + return this.selectCount(new QueryWrapper<PayChannelDO>().lambda()
  59 + .eq(PayChannelDO::getMerchantId, merchantId)
  60 + .eq(PayChannelDO::getAppId, appid)
  61 + .eq(PayChannelDO::getCode, code)
  62 + );
  63 + }
  64 +
  65 + /**
  66 + * 根据条件获取通道
  67 + *
  68 + * @param merchantId 商户编号
  69 + * @param appid 应用编号
  70 + * @param code 通道编码
  71 + * @return 数量
  72 + */
  73 + default PayChannelDO getChannelByConditions(Long merchantId, Long appid, String code) {
  74 + return this.selectOne((new QueryWrapper<PayChannelDO>().lambda()
  75 + .eq(PayChannelDO::getMerchantId, merchantId)
  76 + .eq(PayChannelDO::getAppId, appid)
  77 + .eq(PayChannelDO::getCode, code)
  78 + ));
  79 + }
  80 +
  81 + /**
  82 + * 根据支付应用ID集合获得支付渠道列表
  83 + *
  84 + * @param appIds 应用编号集合
  85 + * @return 支付渠道列表
  86 + */
  87 + default List<PayChannelDO> getChannelListByAppIds(Collection<Long> appIds){
  88 + return this.selectList(new QueryWrapper<PayChannelDO>().lambda()
  89 + .in(PayChannelDO::getAppId, appIds));
  90 + }
47 91 }
... ...
yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/pay/dal/mysql/merchant/PayMerchantMapper.java
... ... @@ -6,6 +6,7 @@ import cn.iocoder.yudao.coreservice.modules.pay.dal.dataobject.merchant.PayMerch
6 6 import cn.iocoder.yudao.framework.common.pojo.PageResult;
7 7 import cn.iocoder.yudao.framework.mybatis.core.query.QueryWrapperX;
8 8 import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
  9 +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
9 10 import org.apache.ibatis.annotations.Mapper;
10 11 import cn.iocoder.yudao.adminserver.modules.pay.controller.merchant.vo.*;
11 12  
... ... @@ -39,4 +40,14 @@ public interface PayMerchantMapper extends BaseMapperX&lt;PayMerchantDO&gt; {
39 40 .orderByDesc("id"));
40 41 }
41 42  
  43 + /**
  44 + * 根据商户名称模糊查询商户集合
  45 + *
  46 + * @param merchantName 商户名称
  47 + * @return 商户集合
  48 + */
  49 + default List<PayMerchantDO> getMerchantListByName(String merchantName) {
  50 + return this.selectList(new QueryWrapper<PayMerchantDO>()
  51 + .lambda().likeRight(PayMerchantDO::getName, merchantName));
  52 + }
42 53 }
... ...
yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/pay/service/app/impl/PayAppServiceImpl.java
1 1 package cn.iocoder.yudao.adminserver.modules.pay.service.app.impl;
2 2  
  3 +import cn.hutool.core.collection.CollectionUtil;
3 4 import cn.hutool.core.util.StrUtil;
4 5 import cn.iocoder.yudao.adminserver.modules.pay.controller.app.vo.PayAppCreateReqVO;
5 6 import cn.iocoder.yudao.adminserver.modules.pay.controller.app.vo.PayAppExportReqVO;
... ... @@ -7,6 +8,7 @@ import cn.iocoder.yudao.adminserver.modules.pay.controller.app.vo.PayAppPageReqV
7 8 import cn.iocoder.yudao.adminserver.modules.pay.controller.app.vo.PayAppUpdateReqVO;
8 9 import cn.iocoder.yudao.adminserver.modules.pay.convert.app.PayAppConvert;
9 10 import cn.iocoder.yudao.adminserver.modules.pay.dal.mysql.app.PayAppMapper;
  11 +import cn.iocoder.yudao.adminserver.modules.pay.dal.mysql.merchant.PayMerchantMapper;
10 12 import cn.iocoder.yudao.adminserver.modules.pay.service.app.PayAppService;
11 13 import cn.iocoder.yudao.adminserver.modules.pay.service.merchant.PayMerchantService;
12 14 import cn.iocoder.yudao.coreservice.modules.pay.dal.dataobject.merchant.PayAppDO;
... ... @@ -18,10 +20,7 @@ import org.springframework.stereotype.Service;
18 20 import org.springframework.validation.annotation.Validated;
19 21  
20 22 import javax.annotation.Resource;
21   -import java.util.Collection;
22   -import java.util.Collections;
23   -import java.util.List;
24   -import java.util.Set;
  23 +import java.util.*;
25 24  
26 25 import static cn.iocoder.yudao.coreservice.modules.pay.enums.PayErrorCodeCoreConstants.APP_NOT_EXISTS;
27 26 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
... ... @@ -43,7 +42,7 @@ public class PayAppServiceImpl implements PayAppService {
43 42 * 商户 service 组件
44 43 */
45 44 @Resource
46   - private PayMerchantService merchantService;
  45 + private PayMerchantMapper merchantMapper;
47 46  
48 47 @Override
49 48 public Long createApp(PayAppCreateReqVO createReqVO) {
... ... @@ -89,13 +88,20 @@ public class PayAppServiceImpl implements PayAppService {
89 88  
90 89 @Override
91 90 public PageResult<PayAppDO> getAppPage(PayAppPageReqVO pageReqVO) {
92   - // TODO @aquan:会有一个场景,merchantName 匹配不到商户编号的时候,应该返回没数据的
93   - return appMapper.selectPage(pageReqVO, this.getMerchantCondition(pageReqVO.getMerchantName()));
  91 + Set<Long> merchantIdList = this.getMerchantCondition(pageReqVO.getMerchantName());
  92 + if (StrUtil.isNotBlank(pageReqVO.getMerchantName()) && CollectionUtil.isEmpty(merchantIdList)) {
  93 + return new PageResult<>();
  94 + }
  95 + return appMapper.selectPage(pageReqVO, merchantIdList);
94 96 }
95 97  
96 98 @Override
97 99 public List<PayAppDO> getAppList(PayAppExportReqVO exportReqVO) {
98   - return appMapper.selectList(exportReqVO);
  100 + Set<Long> merchantIdList = this.getMerchantCondition(exportReqVO.getMerchantName());
  101 + if (StrUtil.isNotBlank(exportReqVO.getMerchantName()) && CollectionUtil.isEmpty(merchantIdList)) {
  102 + return new ArrayList<>();
  103 + }
  104 + return appMapper.selectList(exportReqVO, merchantIdList);
99 105 }
100 106  
101 107 /**
... ... @@ -108,7 +114,7 @@ public class PayAppServiceImpl implements PayAppService {
108 114 if (StrUtil.isBlank(merchantName)) {
109 115 return Collections.emptySet();
110 116 }
111   - return convertSet(merchantService.getMerchantListByName(merchantName), PayMerchantDO::getId);
  117 + return convertSet(merchantMapper.getMerchantListByName(merchantName), PayMerchantDO::getId);
112 118 }
113 119  
114 120 /**
... ... @@ -131,6 +137,7 @@ public class PayAppServiceImpl implements PayAppService {
131 137  
132 138 /**
133 139 * 检查商户是否存在
  140 + *
134 141 * @param id 商户编号
135 142 */
136 143 @VisibleForTesting
... ...
yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/pay/service/channel/PayChannelService.java
... ... @@ -12,7 +12,7 @@ import java.util.List;
12 12 /**
13 13 * 支付渠道 Service 接口
14 14 *
15   - * @author 芋艿 // TODO @aquan:作者不要我
  15 + * @author aquan
16 16 */
17 17 public interface PayChannelService {
18 18  
... ... @@ -76,31 +76,12 @@ public interface PayChannelService {
76 76 List<PayChannelDO> getChannelList(PayChannelExportReqVO exportReqVO);
77 77  
78 78 /**
79   - * 根据支付应用ID集合获取所有的支付渠道
  79 + * 根据支付应用ID集合获得支付渠道列表
80 80 *
81   - * @param payIds 支付应用编号集合
82   - * @return 支付渠道
83   - */
84   - // TODO @aquan:暂时不用提供这种哈。之前提供的原因,是数据字典比较特殊。
85   - List<PayChannelDO> getSimpleChannels(Collection<Long> payIds);
86   -
87   - /**
88   - * 解析pem文件获取公钥私钥字符串
89   - *
90   - * @param file pem公私钥文件
91   - * @return 解析后的字符串
92   - */
93   - // TODO @aquan:可以前端读取么?
94   - String parsingPemFile(MultipartFile file);
95   -
96   - /**
97   - * 创建微信的渠道配置
98   - *
99   - * @param reqVO 创建信息
100   - * @return 创建结果
  81 + * @param appIds 应用编号集合
  82 + * @return 支付渠道列表
101 83 */
102   - // TODO @aquan:pojo 如果要做参数校验,需要添加 @Valid
103   - Long createWechatChannel(PayWechatChannelCreateReqVO reqVO);
  84 + List<PayChannelDO> getChannelListByAppIds(Collection<Long> appIds);
104 85  
105 86 /**
106 87 * 根据条件获取通道数量
... ... @@ -122,10 +103,5 @@ public interface PayChannelService {
122 103 */
123 104 PayChannelDO getChannelByConditions(Long merchantId, Long appid, String code);
124 105  
125   - /**
126   - * 更新微信支付渠道
127   - *
128   - * @param updateReqVO 更新信息
129   - */
130   - void updateWechatChannel(PayWechatChannelUpdateReqVO updateReqVO);
  106 +
131 107 }
... ...
yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/pay/service/channel/impl/PayChannelServiceImpl.java
1 1 package cn.iocoder.yudao.adminserver.modules.pay.service.channel.impl;
2 2  
3 3 import cn.hutool.core.io.IoUtil;
  4 +import cn.hutool.core.util.StrUtil;
4 5 import cn.iocoder.yudao.adminserver.modules.pay.controller.channel.vo.*;
5 6 import cn.iocoder.yudao.adminserver.modules.pay.convert.channel.PayChannelConvert;
6 7 import cn.iocoder.yudao.adminserver.modules.pay.dal.mysql.channel.PayChannelMapper;
7 8 import cn.iocoder.yudao.adminserver.modules.pay.service.channel.PayChannelService;
8 9 import cn.iocoder.yudao.coreservice.modules.pay.dal.dataobject.merchant.PayChannelDO;
9 10 import cn.iocoder.yudao.framework.common.pojo.PageResult;
  11 +import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
  12 +import cn.iocoder.yudao.framework.pay.core.client.impl.alipay.AlipayPayClientConfig;
10 13 import cn.iocoder.yudao.framework.pay.core.client.impl.wx.WXPayClientConfig;
11   -import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
  14 +import cn.iocoder.yudao.framework.pay.core.enums.PayChannelEnum;
  15 +import com.alibaba.fastjson.JSON;
  16 +import com.alibaba.fastjson.JSONObject;
12 17 import lombok.extern.slf4j.Slf4j;
13 18 import org.springframework.stereotype.Service;
  19 +import org.springframework.util.Assert;
14 20 import org.springframework.validation.annotation.Validated;
15 21 import org.springframework.web.multipart.MultipartFile;
16 22  
17 23 import javax.annotation.Resource;
  24 +import javax.validation.ConstraintViolation;
  25 +import javax.validation.Validation;
  26 +import javax.validation.Validator;
  27 +import javax.validation.ValidatorFactory;
18 28 import java.io.IOException;
19 29 import java.util.Collection;
20 30 import java.util.List;
  31 +import java.util.Set;
  32 +import java.util.stream.Collectors;
21 33  
22 34 import static cn.iocoder.yudao.coreservice.modules.pay.enums.PayErrorCodeCoreConstants.*;
23 35 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
... ... @@ -25,7 +37,7 @@ import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionU
25 37 /**
26 38 * 支付渠道 Service 实现类
27 39 *
28   - * @author 芋艿 // TODO aquan:作者写自己哈
  40 + * @author aquan
29 41 */
30 42 @Service
31 43 @Slf4j
... ... @@ -36,11 +48,18 @@ public class PayChannelServiceImpl implements PayChannelService {
36 48 private PayChannelMapper channelMapper;
37 49  
38 50 @Override
39   - public Long createChannel(PayChannelCreateReqVO createReqVO) {
40   - // 插入
41   - PayChannelDO channel = PayChannelConvert.INSTANCE.convert(createReqVO);
  51 + public Long createChannel(PayChannelCreateReqVO reqVO) {
  52 +
  53 + // 判断是否有重复的有责无法新增
  54 + Integer channelCount = this.getChannelCountByConditions(reqVO.getMerchantId(), reqVO.getAppId(), reqVO.getCode());
  55 + if (channelCount > 0) {
  56 + throw exception(CHANNEL_EXIST_SAME_CHANNEL_ERROR);
  57 + }
  58 +
  59 + PayChannelDO channel = PayChannelConvert.INSTANCE.convert(reqVO);
  60 + settingConfigAndCheckParam(channel, reqVO.getConfig());
  61 +
42 62 channelMapper.insert(channel);
43   - // 返回
44 63 return channel.getId();
45 64 }
46 65  
... ... @@ -49,8 +68,9 @@ public class PayChannelServiceImpl implements PayChannelService {
49 68 // 校验存在
50 69 this.validateChannelExists(updateReqVO.getId());
51 70 // 更新
52   - PayChannelDO updateObj = PayChannelConvert.INSTANCE.convert(updateReqVO);
53   - channelMapper.updateById(updateObj);
  71 + PayChannelDO channel = PayChannelConvert.INSTANCE.convert(updateReqVO);
  72 + settingConfigAndCheckParam(channel, updateReqVO.getConfig());
  73 + channelMapper.updateById(channel);
54 74 }
55 75  
56 76 @Override
... ... @@ -88,53 +108,16 @@ public class PayChannelServiceImpl implements PayChannelService {
88 108 }
89 109  
90 110 /**
91   - * 根据支付应用ID集合获取所有的支付渠道
92   - *
93   - * @param payIds 支付应用编号集合
94   - * @return 支付渠道
95   - */
96   - @Override
97   - public List<PayChannelDO> getSimpleChannels(Collection<Long> payIds) {
98   - return channelMapper.selectList(new QueryWrapper<PayChannelDO>().lambda().in(PayChannelDO::getAppId, payIds));
99   - }
100   -
101   - /**
102   - * 解析pem文件获取公钥私钥字符串
  111 + * 根据支付应用ID集合获得支付渠道列表
103 112 *
104   - * @param file pem公私钥文件
105   - * @return 解析后的字符串
  113 + * @param appIds 应用编号集合
  114 + * @return 支付渠道列表
106 115 */
107 116 @Override
108   - public String parsingPemFile(MultipartFile file) {
109   - try {
110   - return IoUtil.readUtf8(file.getInputStream());
111   - } catch (IOException e) {
112   - log.error("[parsingPemToString]读取pem[{}]文件错误", file.getOriginalFilename());
113   - throw exception(CHANNEL_KEY_READ_ERROR);
114   - }
  117 + public List<PayChannelDO> getChannelListByAppIds(Collection<Long> appIds) {
  118 + return channelMapper.getChannelListByAppIds(appIds);
115 119 }
116 120  
117   - /**
118   - * 创建微信的渠道配置
119   - *
120   - * @param reqVO 创建信息
121   - * @return 创建结果
122   - */
123   - @Override
124   - public Long createWechatChannel(PayWechatChannelCreateReqVO reqVO) {
125   -
126   - // 判断是否有重复的有责无法新增
127   - Integer channelCount = this.getChannelCountByConditions(reqVO.getMerchantId(), reqVO.getAppId(), reqVO.getCode());
128   - if (channelCount > 0) {
129   - throw exception(EXIST_SAME_CHANNEL_ERROR);
130   - }
131   -
132   - PayChannelDO channel = PayChannelConvert.INSTANCE.convert(reqVO);
133   - WXPayClientConfig config = PayChannelConvert.INSTANCE.configConvert(reqVO.getWeChatConfig());
134   - channel.setConfig(config);
135   - channelMapper.insert(channel);
136   - return channel.getId();
137   - }
138 121  
139 122 /**
140 123 * 根据条件获取通道数量
... ... @@ -146,14 +129,9 @@ public class PayChannelServiceImpl implements PayChannelService {
146 129 */
147 130 @Override
148 131 public Integer getChannelCountByConditions(Long merchantId, Long appid, String code) {
149   - return this.channelMapper.selectCount(new QueryWrapper<PayChannelDO>().lambda()
150   - .eq(PayChannelDO::getMerchantId, merchantId)
151   - .eq(PayChannelDO::getAppId, appid)
152   - .eq(PayChannelDO::getCode, code)
153   - );
  132 + return this.channelMapper.getChannelCountByConditions(merchantId, appid, code);
154 133 }
155 134  
156   - // TODO @aquan:service 不出现 mybatis plus 哈
157 135 /**
158 136 * 根据条件获取通道
159 137 *
... ... @@ -164,25 +142,76 @@ public class PayChannelServiceImpl implements PayChannelService {
164 142 */
165 143 @Override
166 144 public PayChannelDO getChannelByConditions(Long merchantId, Long appid, String code) {
167   - return this.channelMapper.selectOne((new QueryWrapper<PayChannelDO>().lambda()
168   - .eq(PayChannelDO::getMerchantId, merchantId)
169   - .eq(PayChannelDO::getAppId, appid)
170   - .eq(PayChannelDO::getCode, code)
171   - ));
  145 + return this.channelMapper.getChannelByConditions(merchantId, appid, code);
172 146 }
173 147  
174 148 /**
175   - * 更新微信支付渠道
  149 + * 检测微信秘钥参数
176 150 *
177   - * @param updateReqVO 更新信息
  151 + * @param config 信秘钥参数
178 152 */
179   - @Override
180   - public void updateWechatChannel(PayWechatChannelUpdateReqVO updateReqVO) {
181   - // 校验存在
182   - this.validateChannelExists(updateReqVO.getId());
183   - PayChannelDO channel = PayChannelConvert.INSTANCE.convert(updateReqVO);
184   - WXPayClientConfig config = PayChannelConvert.INSTANCE.configConvert(updateReqVO.getWeChatConfig());
185   - channel.setConfig(config);
186   - this.channelMapper.updateById(channel);
  153 + private void wechatParamCheck(WXPayClientConfig config) {
  154 + // 针对于 V2 或者 V3 版本的参数校验
  155 + if (WXPayClientConfig.API_VERSION_V2.equals(config.getApiVersion())) {
  156 + Assert.notNull(config.getMchKey(), CHANNEL_WECHAT_VERSION_2_MCH_KEY_IS_NULL.getMsg());
  157 + }
  158 + if (WXPayClientConfig.API_VERSION_V3.equals(config.getApiVersion())) {
  159 + Assert.notNull(config.getPrivateKeyContent(), CHANNEL_WECHAT_VERSION_3_PRIVATE_KEY_IS_NULL.getMsg());
  160 + Assert.notNull(config.getPrivateCertContent(), CHANNEL_WECHAT_VERSION_3_CERT_KEY_IS_NULL.getMsg());
  161 + }
  162 + }
  163 +
  164 +
  165 +
  166 + /**
  167 + * 设置渠道配置以及参数校验
  168 + *
  169 + * @param channel 渠道
  170 + * @param configStr 配置
  171 + */
  172 + private void settingConfigAndCheckParam(PayChannelDO channel, String configStr) {
  173 +
  174 + // 得到这个渠道是微信的还是支付宝的
  175 + String channelType = PayChannelEnum.verifyWechatOrAliPay(channel.getCode());
  176 + Assert.notNull(channelType, CHANNEL_NOT_EXISTS.getMsg());
  177 +
  178 + // 进行验证
  179 + ValidatorFactory validatorFactory = Validation.buildDefaultValidatorFactory();
  180 + Validator validator = validatorFactory.getValidator();
  181 +
  182 + // 微信的验证
  183 + if (PayChannelEnum.WECHAT.equals(channelType)) {
  184 +
  185 + WXPayClientConfig config = JSON.parseObject(configStr, WXPayClientConfig.class);
  186 + // 判断是V2 版本还是 V3 版本
  187 + Class clazz = config.getApiVersion().equals(WXPayClientConfig.API_VERSION_V2)
  188 + ? WXPayClientConfig.V2.class : WXPayClientConfig.V3.class;
  189 + // 手动调用validate进行验证
  190 + Set<ConstraintViolation<WXPayClientConfig>> validate = validator.validate(config,clazz);
  191 +
  192 + // 断言没有异常
  193 + Assert.isTrue(validate.isEmpty(), validate.stream().map(ConstraintViolation::getMessage)
  194 + .collect(Collectors.joining(",")));
  195 +
  196 + channel.setConfig(config);
  197 + }
  198 +
  199 + // 支付宝验证
  200 + if (PayChannelEnum.ALIPAY.equals(channelType)) {
  201 +
  202 + AlipayPayClientConfig config = JSON.parseObject(configStr, AlipayPayClientConfig.class);
  203 +
  204 + // 判断是V2 版本还是 V3 版本
  205 + Class clazz = config.getMode().equals(AlipayPayClientConfig.MODE_PUBLIC_KEY)
  206 + ? AlipayPayClientConfig.ModePublicKey.class : AlipayPayClientConfig.ModeCertificate.class;
  207 + // 手动调用validate进行验证
  208 + Set<ConstraintViolation<AlipayPayClientConfig>> validate = validator.validate(config,clazz);
  209 +
  210 + // 断言没有异常
  211 + Assert.isTrue(validate.isEmpty(), validate.stream().map(ConstraintViolation::getMessage)
  212 + .collect(Collectors.joining(",")));
  213 + channel.setConfig(config);
  214 + }
  215 +
187 216 }
188 217 }
... ...
yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/pay/service/merchant/PayMerchantService.java
... ... @@ -101,26 +101,13 @@ public interface PayMerchantService {
101 101 List<PayMerchantDO> getMerchantListByNameLimit(String merchantName);
102 102  
103 103 /**
104   - * 获得指定编号的商户列表
105   - *
106   - * @param merchantIds 商户编号数组
107   - * @return 商户列表
108   - */
109   - // TODO @aquan:和 getMerchantList 重复了
110   - List<PayMerchantDO> getSimpleMerchants(Collection<Long> merchantIds);
111   -
112   - /**
113 104 * 获得指定编号的商户 Map
114 105 *
115 106 * @param merchantIds 商户编号数组
116 107 * @return 商户 Map
117 108 */
118 109 default Map<Long, PayMerchantDO> getMerchantMap(Collection<Long> merchantIds) {
119   - // TODO @aquan:可以不用判空,交给 getMerchantList 解决
120   - if (CollUtil.isEmpty(merchantIds)) {
121   - return Collections.emptyMap();
122   - }
123   - List<PayMerchantDO> list = getSimpleMerchants(merchantIds);
  110 + List<PayMerchantDO> list = this.getMerchantList(merchantIds);
124 111 return CollectionUtils.convertMap(list, PayMerchantDO::getId);
125 112 }
126 113  
... ...
yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/pay/service/merchant/impl/PayMerchantServiceImpl.java
... ... @@ -113,9 +113,7 @@ public class PayMerchantServiceImpl implements PayMerchantService {
113 113 */
114 114 @Override
115 115 public List<PayMerchantDO> getMerchantListByName(String merchantName) {
116   - // TODO @aquan:Service 层,不要出现 mybatis plus 的代码,要放到 mapper 里提供。技术与业务分离,原则上
117   - return this.merchantMapper.selectList(new QueryWrapper<PayMerchantDO>()
118   - .lambda().likeRight(PayMerchantDO::getName, merchantName));
  116 + return this.merchantMapper.getMerchantListByName(merchantName);
119 117 }
120 118  
121 119 /**
... ... @@ -150,16 +148,6 @@ public class PayMerchantServiceImpl implements PayMerchantService {
150 148 }
151 149 }
152 150  
153   - /**
154   - * 获得指定编号的商户列表
155   - *
156   - * @param merchantIds 商户编号数组
157   - * @return 商户列表
158   - */
159   - @Override
160   - public List<PayMerchantDO> getSimpleMerchants(Collection<Long> merchantIds) {
161   - return merchantMapper.selectBatchIds(merchantIds);
162   - }
163 151  
164 152 // TODO @芋艿:后续增加下合适的算法
165 153 /**
... ...
yudao-admin-server/src/test/java/cn/iocoder/yudao/adminserver/modules/pay/channel/PayChannelServiceTest.java deleted
1   -package cn.iocoder.yudao.adminserver.modules.pay.channel;
2   -
3   -import cn.iocoder.yudao.adminserver.BaseDbUnitTest;
4   -import cn.iocoder.yudao.adminserver.modules.pay.controller.channel.vo.PayChannelCreateReqVO;
5   -import cn.iocoder.yudao.adminserver.modules.pay.controller.channel.vo.PayChannelExportReqVO;
6   -import cn.iocoder.yudao.adminserver.modules.pay.controller.channel.vo.PayChannelPageReqVO;
7   -import cn.iocoder.yudao.adminserver.modules.pay.controller.channel.vo.PayChannelUpdateReqVO;
8   -import cn.iocoder.yudao.adminserver.modules.pay.dal.mysql.channel.PayChannelMapper;
9   -import cn.iocoder.yudao.adminserver.modules.pay.service.channel.impl.PayChannelServiceImpl;
10   -import cn.iocoder.yudao.coreservice.modules.pay.dal.dataobject.merchant.PayChannelDO;
11   -import cn.iocoder.yudao.framework.common.pojo.PageResult;
12   -import cn.iocoder.yudao.framework.common.util.object.ObjectUtils;
13   -import org.junit.jupiter.api.Test;
14   -import org.springframework.context.annotation.Import;
15   -
16   -import javax.annotation.Resource;
17   -import java.util.List;
18   -
19   -import static cn.iocoder.yudao.coreservice.modules.pay.enums.PayErrorCodeCoreConstants.CHANNEL_NOT_EXISTS;
20   -import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals;
21   -import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertServiceException;
22   -import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomLongId;
23   -import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomPojo;
24   -import static org.junit.jupiter.api.Assertions.*;
25   -
26   -/**
27   -* {@link PayChannelServiceImpl} 的单元测试类
28   -*
29   -* @author 芋艿
30   -*/
31   -@Import(PayChannelServiceImpl.class)
32   -public class PayChannelServiceTest extends BaseDbUnitTest {
33   -
34   - @Resource
35   - private PayChannelServiceImpl channelService;
36   -
37   - @Resource
38   - private PayChannelMapper channelMapper;
39   -
40   - @Test
41   - public void testCreateChannel_success() {
42   - // 准备参数
43   - PayChannelCreateReqVO reqVO = randomPojo(PayChannelCreateReqVO.class);
44   -
45   - // 调用
46   - Long channelId = channelService.createChannel(reqVO);
47   - // 断言
48   - assertNotNull(channelId);
49   - // 校验记录的属性是否正确
50   - PayChannelDO channel = channelMapper.selectById(channelId);
51   - assertPojoEquals(reqVO, channel);
52   - }
53   -
54   - @Test
55   - public void testUpdateChannel_success() {
56   - // mock 数据
57   - PayChannelDO dbChannel = randomPojo(PayChannelDO.class);
58   - channelMapper.insert(dbChannel);// @Sql: 先插入出一条存在的数据
59   - // 准备参数
60   - PayChannelUpdateReqVO reqVO = randomPojo(PayChannelUpdateReqVO.class, o -> {
61   - o.setId(dbChannel.getId()); // 设置更新的 ID
62   - });
63   -
64   - // 调用
65   - channelService.updateChannel(reqVO);
66   - // 校验是否更新正确
67   - PayChannelDO channel = channelMapper.selectById(reqVO.getId()); // 获取最新的
68   - assertPojoEquals(reqVO, channel);
69   - }
70   -
71   - @Test
72   - public void testUpdateChannel_notExists() {
73   - // 准备参数
74   - PayChannelUpdateReqVO reqVO = randomPojo(PayChannelUpdateReqVO.class);
75   -
76   - // 调用, 并断言异常
77   - assertServiceException(() -> channelService.updateChannel(reqVO), CHANNEL_NOT_EXISTS);
78   - }
79   -
80   - @Test
81   - public void testDeleteChannel_success() {
82   - // mock 数据
83   - PayChannelDO dbChannel = randomPojo(PayChannelDO.class);
84   - channelMapper.insert(dbChannel);// @Sql: 先插入出一条存在的数据
85   - // 准备参数
86   - Long id = dbChannel.getId();
87   -
88   - // 调用
89   - channelService.deleteChannel(id);
90   - // 校验数据不存在了
91   - assertNull(channelMapper.selectById(id));
92   - }
93   -
94   - @Test
95   - public void testDeleteChannel_notExists() {
96   - // 准备参数
97   - Long id = randomLongId();
98   -
99   - // 调用, 并断言异常
100   - assertServiceException(() -> channelService.deleteChannel(id), CHANNEL_NOT_EXISTS);
101   - }
102   -
103   - @Test // TODO 请修改 null 为需要的值
104   - public void testGetChannelPage() {
105   - // mock 数据
106   - PayChannelDO dbChannel = randomPojo(PayChannelDO.class, o -> { // 等会查询到
107   - o.setCode(null);
108   - o.setStatus(null);
109   - o.setRemark(null);
110   - o.setFeeRate(null);
111   - o.setMerchantId(null);
112   - o.setAppId(null);
113   - o.setConfig(null);
114   - o.setCreateTime(null);
115   - });
116   - channelMapper.insert(dbChannel);
117   - // 测试 code 不匹配
118   - channelMapper.insert(ObjectUtils.clone(dbChannel, o -> o.setCode(null)));
119   - // 测试 status 不匹配
120   - channelMapper.insert(ObjectUtils.clone(dbChannel, o -> o.setStatus(null)));
121   - // 测试 remark 不匹配
122   - channelMapper.insert(ObjectUtils.clone(dbChannel, o -> o.setRemark(null)));
123   - // 测试 feeRate 不匹配
124   - channelMapper.insert(ObjectUtils.clone(dbChannel, o -> o.setFeeRate(null)));
125   - // 测试 merchantId 不匹配
126   - channelMapper.insert(ObjectUtils.clone(dbChannel, o -> o.setMerchantId(null)));
127   - // 测试 appId 不匹配
128   - channelMapper.insert(ObjectUtils.clone(dbChannel, o -> o.setAppId(null)));
129   - // 测试 config 不匹配
130   - channelMapper.insert(ObjectUtils.clone(dbChannel, o -> o.setConfig(null)));
131   - // 测试 createTime 不匹配
132   - channelMapper.insert(ObjectUtils.clone(dbChannel, o -> o.setCreateTime(null)));
133   - // 准备参数
134   - PayChannelPageReqVO reqVO = new PayChannelPageReqVO();
135   - reqVO.setCode(null);
136   - reqVO.setStatus(null);
137   - reqVO.setRemark(null);
138   - reqVO.setFeeRate(null);
139   - reqVO.setMerchantId(null);
140   - reqVO.setAppId(null);
141   - reqVO.setConfig(null);
142   - reqVO.setBeginCreateTime(null);
143   - reqVO.setEndCreateTime(null);
144   -
145   - // 调用
146   - PageResult<PayChannelDO> pageResult = channelService.getChannelPage(reqVO);
147   - // 断言
148   - assertEquals(1, pageResult.getTotal());
149   - assertEquals(1, pageResult.getList().size());
150   - assertPojoEquals(dbChannel, pageResult.getList().get(0));
151   - }
152   -
153   - @Test // TODO aquan:请修改 null 为需要的值
154   - public void testGetChannelList() {
155   - // mock 数据
156   - PayChannelDO dbChannel = randomPojo(PayChannelDO.class, o -> { // 等会查询到
157   - o.setCode(null);
158   - o.setStatus(null);
159   - o.setRemark(null);
160   - o.setFeeRate(null);
161   - o.setMerchantId(null);
162   - o.setAppId(null);
163   - o.setConfig(null);
164   - o.setCreateTime(null);
165   - });
166   - channelMapper.insert(dbChannel);
167   - // 测试 code 不匹配
168   - channelMapper.insert(ObjectUtils.clone(dbChannel, o -> o.setCode(null)));
169   - // 测试 status 不匹配
170   - channelMapper.insert(ObjectUtils.clone(dbChannel, o -> o.setStatus(null)));
171   - // 测试 remark 不匹配
172   - channelMapper.insert(ObjectUtils.clone(dbChannel, o -> o.setRemark(null)));
173   - // 测试 feeRate 不匹配
174   - channelMapper.insert(ObjectUtils.clone(dbChannel, o -> o.setFeeRate(null)));
175   - // 测试 merchantId 不匹配
176   - channelMapper.insert(ObjectUtils.clone(dbChannel, o -> o.setMerchantId(null)));
177   - // 测试 appId 不匹配
178   - channelMapper.insert(ObjectUtils.clone(dbChannel, o -> o.setAppId(null)));
179   - // 测试 config 不匹配
180   - channelMapper.insert(ObjectUtils.clone(dbChannel, o -> o.setConfig(null)));
181   - // 测试 createTime 不匹配
182   - channelMapper.insert(ObjectUtils.clone(dbChannel, o -> o.setCreateTime(null)));
183   - // 准备参数
184   - PayChannelExportReqVO reqVO = new PayChannelExportReqVO();
185   - reqVO.setCode(null);
186   - reqVO.setStatus(null);
187   - reqVO.setRemark(null);
188   - reqVO.setFeeRate(null);
189   - reqVO.setMerchantId(null);
190   - reqVO.setAppId(null);
191   - reqVO.setConfig(null);
192   - reqVO.setBeginCreateTime(null);
193   - reqVO.setEndCreateTime(null);
194   -
195   - // 调用
196   - List<PayChannelDO> list = channelService.getChannelList(reqVO);
197   - // 断言
198   - assertEquals(1, list.size());
199   - assertPojoEquals(dbChannel, list.get(0));
200   - }
201   -
202   -}
yudao-admin-server/src/test/java/cn/iocoder/yudao/adminserver/modules/pay/app/service/PayAppServiceTest.java renamed to yudao-admin-server/src/test/java/cn/iocoder/yudao/adminserver/modules/pay/service/app/PayAppServiceTest.java
1   -package cn.iocoder.yudao.adminserver.modules.pay.app.service;
  1 +package cn.iocoder.yudao.adminserver.modules.pay.service.app;
2 2  
  3 +import cn.hutool.core.util.RandomUtil;
3 4 import cn.iocoder.yudao.adminserver.BaseDbUnitTest;
4 5 import cn.iocoder.yudao.adminserver.modules.pay.controller.app.vo.PayAppCreateReqVO;
5 6 import cn.iocoder.yudao.adminserver.modules.pay.controller.app.vo.PayAppExportReqVO;
6 7 import cn.iocoder.yudao.adminserver.modules.pay.controller.app.vo.PayAppPageReqVO;
7 8 import cn.iocoder.yudao.adminserver.modules.pay.controller.app.vo.PayAppUpdateReqVO;
  9 +
  10 +
8 11 import cn.iocoder.yudao.adminserver.modules.pay.dal.mysql.app.PayAppMapper;
  12 +import cn.iocoder.yudao.adminserver.modules.pay.dal.mysql.merchant.PayMerchantMapper;
9 13 import cn.iocoder.yudao.adminserver.modules.pay.service.app.impl.PayAppServiceImpl;
  14 +import cn.iocoder.yudao.adminserver.modules.pay.service.merchant.PayMerchantService;
10 15 import cn.iocoder.yudao.coreservice.modules.pay.dal.dataobject.merchant.PayAppDO;
  16 +import cn.iocoder.yudao.coreservice.modules.pay.dal.dataobject.merchant.PayMerchantDO;
  17 +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
11 18 import cn.iocoder.yudao.framework.common.pojo.PageResult;
12 19 import cn.iocoder.yudao.framework.common.util.object.ObjectUtils;
13 20 import org.junit.jupiter.api.Test;
  21 +import org.mockito.InjectMocks;
  22 +import org.mockito.Mockito;
  23 +import org.springframework.boot.test.mock.mockito.MockBean;
14 24 import org.springframework.context.annotation.Import;
15 25  
16 26 import javax.annotation.Resource;
  27 +import java.util.Collections;
17 28 import java.util.List;
18 29  
19   -import static cn.iocoder.yudao.coreservice.modules.pay.enums.PayErrorCodeCoreConstants.APP_NOT_EXISTS;
  30 +import static cn.iocoder.yudao.coreservice.modules.pay.enums.PayErrorCodeCoreConstants.*;
  31 +import static cn.iocoder.yudao.framework.common.util.date.DateUtils.buildTime;
20 32 import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals;
21 33 import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertServiceException;
22 34 import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomLongId;
... ... @@ -24,10 +36,10 @@ import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomPojo;
24 36 import static org.junit.jupiter.api.Assertions.*;
25 37  
26 38 /**
27   -* {@link PayAppServiceImpl} 的单元测试类
28   -*
29   -* @author 芋艿
30   -*/
  39 + * {@link PayAppServiceImpl} 的单元测试类
  40 + *
  41 + * @author 芋艿
  42 + */
31 43 @Import(PayAppServiceImpl.class)
32 44 public class PayAppServiceTest extends BaseDbUnitTest {
33 45  
... ... @@ -37,10 +49,14 @@ public class PayAppServiceTest extends BaseDbUnitTest {
37 49 @Resource
38 50 private PayAppMapper appMapper;
39 51  
  52 + @MockBean(name = "payMerchantMapper")
  53 + private PayMerchantMapper payMerchantMapper;
  54 +
40 55 @Test
41 56 public void testCreateApp_success() {
42 57 // 准备参数
43   - PayAppCreateReqVO reqVO = randomPojo(PayAppCreateReqVO.class);
  58 + PayAppCreateReqVO reqVO = randomPojo(PayAppCreateReqVO.class, o ->
  59 + o.setStatus((RandomUtil.randomEle(CommonStatusEnum.values()).getStatus())));
44 60  
45 61 // 调用
46 62 Long appId = appService.createApp(reqVO);
... ... @@ -54,10 +70,12 @@ public class PayAppServiceTest extends BaseDbUnitTest {
54 70 @Test
55 71 public void testUpdateApp_success() {
56 72 // mock 数据
57   - PayAppDO dbApp = randomPojo(PayAppDO.class);
  73 + PayAppDO dbApp = randomPojo(PayAppDO.class, o ->
  74 + o.setStatus(CommonStatusEnum.DISABLE.getStatus()));
58 75 appMapper.insert(dbApp);// @Sql: 先插入出一条存在的数据
59 76 // 准备参数
60 77 PayAppUpdateReqVO reqVO = randomPojo(PayAppUpdateReqVO.class, o -> {
  78 + o.setStatus(CommonStatusEnum.ENABLE.getStatus());
61 79 o.setId(dbApp.getId()); // 设置更新的 ID
62 80 });
63 81  
... ... @@ -71,8 +89,8 @@ public class PayAppServiceTest extends BaseDbUnitTest {
71 89 @Test
72 90 public void testUpdateApp_notExists() {
73 91 // 准备参数
74   - PayAppUpdateReqVO reqVO = randomPojo(PayAppUpdateReqVO.class);
75   -
  92 + PayAppUpdateReqVO reqVO = randomPojo(PayAppUpdateReqVO.class, o ->
  93 + o.setStatus((RandomUtil.randomEle(CommonStatusEnum.values()).getStatus())));
76 94 // 调用, 并断言异常
77 95 assertServiceException(() -> appService.updateApp(reqVO), APP_NOT_EXISTS);
78 96 }
... ... @@ -80,15 +98,16 @@ public class PayAppServiceTest extends BaseDbUnitTest {
80 98 @Test
81 99 public void testDeleteApp_success() {
82 100 // mock 数据
83   - PayAppDO dbApp = randomPojo(PayAppDO.class);
  101 + PayAppDO dbApp = randomPojo(PayAppDO.class, o ->
  102 + o.setStatus((RandomUtil.randomEle(CommonStatusEnum.values()).getStatus())));
84 103 appMapper.insert(dbApp);// @Sql: 先插入出一条存在的数据
85 104 // 准备参数
86 105 Long id = dbApp.getId();
87 106  
88 107 // 调用
89 108 appService.deleteApp(id);
90   - // 校验数据不存在了
91   - assertNull(appMapper.selectById(id));
  109 + // 校验数据不存在了
  110 + assertNull(appMapper.selectById(id));
92 111 }
93 112  
94 113 @Test
... ... @@ -100,94 +119,131 @@ public class PayAppServiceTest extends BaseDbUnitTest {
100 119 assertServiceException(() -> appService.deleteApp(id), APP_NOT_EXISTS);
101 120 }
102 121  
103   - @Test // TODO 请修改 null 为需要的值
  122 + @Test
104 123 public void testGetAppPage() {
105   - // mock 数据
106   - PayAppDO dbApp = randomPojo(PayAppDO.class, o -> { // 等会查询到
107   - o.setName(null);
108   - o.setStatus(null);
109   - o.setRemark(null);
110   - o.setPayNotifyUrl(null);
111   - o.setRefundNotifyUrl(null);
112   - o.setMerchantId(null);
113   - o.setCreateTime(null);
114   - });
115   - appMapper.insert(dbApp);
116   - // 测试 name 不匹配
117   - appMapper.insert(ObjectUtils.clone(dbApp, o -> o.setName(null)));
118   - // 测试 status 不匹配
119   - appMapper.insert(ObjectUtils.clone(dbApp, o -> o.setStatus(null)));
120   - // 测试 remark 不匹配
121   - appMapper.insert(ObjectUtils.clone(dbApp, o -> o.setRemark(null)));
122   - // 测试 payNotifyUrl 不匹配
123   - appMapper.insert(ObjectUtils.clone(dbApp, o -> o.setPayNotifyUrl(null)));
124   - // 测试 refundNotifyUrl 不匹配
125   - appMapper.insert(ObjectUtils.clone(dbApp, o -> o.setRefundNotifyUrl(null)));
126   - // 测试 merchantId 不匹配
127   - appMapper.insert(ObjectUtils.clone(dbApp, o -> o.setMerchantId(null)));
128   - // 测试 createTime 不匹配
129   - appMapper.insert(ObjectUtils.clone(dbApp, o -> o.setCreateTime(null)));
130   - // 准备参数
131   - PayAppPageReqVO reqVO = new PayAppPageReqVO();
132   - reqVO.setName(null);
133   - reqVO.setStatus(null);
134   - reqVO.setRemark(null);
135   - reqVO.setPayNotifyUrl(null);
136   - reqVO.setRefundNotifyUrl(null);
137   - reqVO.setBeginCreateTime(null);
138   - reqVO.setEndCreateTime(null);
139   -
140   - // 调用
141   - PageResult<PayAppDO> pageResult = appService.getAppPage(reqVO);
142   - // 断言
143   - assertEquals(1, pageResult.getTotal());
144   - assertEquals(1, pageResult.getList().size());
145   - assertPojoEquals(dbApp, pageResult.getList().get(0));
  124 + Long merchantId = 1L;
  125 + Long mismatchMerchantId = 2L;
  126 +
  127 + // mock 数据
  128 + PayAppDO dbApp = randomPojo(PayAppDO.class, o -> { // 等会查询到
  129 + o.setName("灿灿姐的杂货铺");
  130 + o.setStatus(CommonStatusEnum.ENABLE.getStatus());
  131 + o.setRemark("敏敏姐的小卖铺");
  132 + o.setPayNotifyUrl("https://www.hc.com");
  133 + o.setRefundNotifyUrl("https://www.xm.com");
  134 + o.setMerchantId(merchantId);
  135 + o.setCreateTime(buildTime(2021,11,20));
  136 + });
  137 +
  138 + // mock 数据
  139 + PayMerchantDO dbMerchant = randomPojo(PayMerchantDO.class, o -> { // 等会查询到
  140 + o.setId(merchantId);
  141 + o.setNo("M1008611");
  142 + o.setName("灿哥的杂货铺");
  143 + o.setShortName("灿灿子");
  144 + o.setStatus(CommonStatusEnum.ENABLE.getStatus());
  145 + o.setRemark("灿哥的杂货铺");
  146 + o.setCreateTime(buildTime(2021,11,3));
  147 + });
  148 +
  149 + Mockito.when(payMerchantMapper.getMerchantListByName(dbMerchant.getName()))
  150 + .thenReturn(Collections.singletonList(dbMerchant));
  151 +
  152 + appMapper.insert(dbApp);
  153 + // 测试 name 不匹配
  154 + appMapper.insert(ObjectUtils.clone(dbApp, o -> o.setName("敏敏姐的杂货铺")));
  155 + // 测试 status 不匹配
  156 + appMapper.insert(ObjectUtils.clone(dbApp, o -> o.setStatus(CommonStatusEnum.DISABLE.getStatus())));
  157 + // 测试 remark 不匹配
  158 + appMapper.insert(ObjectUtils.clone(dbApp, o -> o.setRemark("灿灿姐的小卖部")));
  159 + // 测试 payNotifyUrl 不匹配
  160 + appMapper.insert(ObjectUtils.clone(dbApp, o -> o.setPayNotifyUrl("xm.com")));
  161 + // 测试 refundNotifyUrl 不匹配
  162 + appMapper.insert(ObjectUtils.clone(dbApp, o -> o.setRefundNotifyUrl("hc.com")));
  163 + // 测试 merchantId 不匹配
  164 + appMapper.insert(ObjectUtils.clone(dbApp, o -> o.setMerchantId(mismatchMerchantId)));
  165 + // 测试 createTime 不匹配
  166 + appMapper.insert(ObjectUtils.clone(dbApp, o -> o.setCreateTime(buildTime(2021,12,21))));
  167 + // 准备参数
  168 + PayAppPageReqVO reqVO = new PayAppPageReqVO();
  169 + reqVO.setName("灿灿姐的杂货铺");
  170 + reqVO.setStatus(CommonStatusEnum.ENABLE.getStatus());
  171 + reqVO.setRemark("敏敏姐的小卖铺");
  172 + reqVO.setPayNotifyUrl("https://www.hc.com");
  173 + reqVO.setRefundNotifyUrl("https://www.xm.com");
  174 + reqVO.setMerchantName(dbMerchant.getName());
  175 + reqVO.setBeginCreateTime(buildTime(2021,11,19));
  176 + reqVO.setEndCreateTime(buildTime(2021,11,21));
  177 +
  178 + // 调用
  179 + PageResult<PayAppDO> pageResult = appService.getAppPage(reqVO);
  180 + // 断言
  181 + assertEquals(1, pageResult.getTotal());
  182 + assertEquals(1, pageResult.getList().size());
  183 + assertPojoEquals(dbApp, pageResult.getList().get(0));
146 184 }
147 185  
148   - @Test // TODO aquan:请修改 null 为需要的值
  186 + @Test // TODO 请修改 null 为需要的值
149 187 public void testGetAppList() {
150   - // mock 数据
151   - PayAppDO dbApp = randomPojo(PayAppDO.class, o -> { // 等会查询到
152   - o.setName(null);
153   - o.setStatus(null);
154   - o.setRemark(null);
155   - o.setPayNotifyUrl(null);
156   - o.setRefundNotifyUrl(null);
157   - o.setMerchantId(null);
158   - o.setCreateTime(null);
159   - });
160   - appMapper.insert(dbApp);
161   - // 测试 name 不匹配
162   - appMapper.insert(ObjectUtils.clone(dbApp, o -> o.setName(null)));
163   - // 测试 status 不匹配
164   - appMapper.insert(ObjectUtils.clone(dbApp, o -> o.setStatus(null)));
165   - // 测试 remark 不匹配
166   - appMapper.insert(ObjectUtils.clone(dbApp, o -> o.setRemark(null)));
167   - // 测试 payNotifyUrl 不匹配
168   - appMapper.insert(ObjectUtils.clone(dbApp, o -> o.setPayNotifyUrl(null)));
169   - // 测试 refundNotifyUrl 不匹配
170   - appMapper.insert(ObjectUtils.clone(dbApp, o -> o.setRefundNotifyUrl(null)));
171   - // 测试 merchantId 不匹配
172   - appMapper.insert(ObjectUtils.clone(dbApp, o -> o.setMerchantId(null)));
173   - // 测试 createTime 不匹配
174   - appMapper.insert(ObjectUtils.clone(dbApp, o -> o.setCreateTime(null)));
175   - // 准备参数
176   - PayAppExportReqVO reqVO = new PayAppExportReqVO();
177   - reqVO.setName(null);
178   - reqVO.setStatus(null);
179   - reqVO.setRemark(null);
180   - reqVO.setPayNotifyUrl(null);
181   - reqVO.setRefundNotifyUrl(null);
182   - reqVO.setMerchantId(null);
183   - reqVO.setBeginCreateTime(null);
184   - reqVO.setEndCreateTime(null);
185   -
186   - // 调用
187   - List<PayAppDO> list = appService.getAppList(reqVO);
188   - // 断言
189   - assertEquals(1, list.size());
190   - assertPojoEquals(dbApp, list.get(0));
  188 + Long merchantId = 1L;
  189 + Long mismatchMerchantId = 2L;
  190 +
  191 + // mock 数据
  192 + PayAppDO dbApp = randomPojo(PayAppDO.class, o -> { // 等会查询到
  193 + o.setName("灿灿姐的杂货铺");
  194 + o.setStatus(CommonStatusEnum.ENABLE.getStatus());
  195 + o.setRemark("敏敏姐的小卖铺");
  196 + o.setPayNotifyUrl("https://www.hc.com");
  197 + o.setRefundNotifyUrl("https://www.xm.com");
  198 + o.setMerchantId(merchantId);
  199 + o.setCreateTime(buildTime(2021,11,20));
  200 + });
  201 +
  202 + // mock 数据
  203 + PayMerchantDO dbMerchant = randomPojo(PayMerchantDO.class, o -> { // 等会查询到
  204 + o.setId(merchantId);
  205 + o.setNo("M1008611");
  206 + o.setName("灿哥的杂货铺");
  207 + o.setShortName("灿灿子");
  208 + o.setStatus(CommonStatusEnum.ENABLE.getStatus());
  209 + o.setRemark("灿哥的杂货铺");
  210 + o.setCreateTime(buildTime(2021,11,3));
  211 + });
  212 +
  213 + Mockito.when(payMerchantMapper.getMerchantListByName(dbMerchant.getName()))
  214 + .thenReturn(Collections.singletonList(dbMerchant));
  215 +
  216 + appMapper.insert(dbApp);
  217 + // 测试 name 不匹配
  218 + appMapper.insert(ObjectUtils.clone(dbApp, o -> o.setName("敏敏姐的杂货铺")));
  219 + // 测试 status 不匹配
  220 + appMapper.insert(ObjectUtils.clone(dbApp, o -> o.setStatus(CommonStatusEnum.DISABLE.getStatus())));
  221 + // 测试 remark 不匹配
  222 + appMapper.insert(ObjectUtils.clone(dbApp, o -> o.setRemark("灿灿姐的小卖部")));
  223 + // 测试 payNotifyUrl 不匹配
  224 + appMapper.insert(ObjectUtils.clone(dbApp, o -> o.setPayNotifyUrl("xm.com")));
  225 + // 测试 refundNotifyUrl 不匹配
  226 + appMapper.insert(ObjectUtils.clone(dbApp, o -> o.setRefundNotifyUrl("hc.com")));
  227 + // 测试 merchantId 不匹配
  228 + appMapper.insert(ObjectUtils.clone(dbApp, o -> o.setMerchantId(mismatchMerchantId)));
  229 + // 测试 createTime 不匹配
  230 + appMapper.insert(ObjectUtils.clone(dbApp, o -> o.setCreateTime(buildTime(2021,12,21))));
  231 + // 准备参数
  232 + PayAppExportReqVO reqVO = new PayAppExportReqVO();
  233 + reqVO.setName("灿灿姐的杂货铺");
  234 + reqVO.setStatus(CommonStatusEnum.ENABLE.getStatus());
  235 + reqVO.setRemark("敏敏姐的小卖铺");
  236 + reqVO.setPayNotifyUrl("https://www.hc.com");
  237 + reqVO.setRefundNotifyUrl("https://www.xm.com");
  238 + reqVO.setMerchantName(dbMerchant.getName());
  239 + reqVO.setBeginCreateTime(buildTime(2021,11,19));
  240 + reqVO.setEndCreateTime(buildTime(2021,11,21));
  241 +
  242 + // 调用
  243 + List<PayAppDO> list = appService.getAppList(reqVO);
  244 + // 断言
  245 + assertEquals(1, list.size());
  246 + assertPojoEquals(dbApp, list.get(0));
191 247 }
192 248  
193 249 -}
  250 +}
194 251 \ No newline at end of file
... ...
yudao-admin-server/src/test/java/cn/iocoder/yudao/adminserver/modules/pay/service/channel/PayChannelServiceTest.java 0 → 100644
  1 +package cn.iocoder.yudao.adminserver.modules.pay.service.channel;
  2 +
  3 +import cn.iocoder.yudao.adminserver.BaseDbUnitTest;
  4 +import cn.iocoder.yudao.adminserver.modules.pay.controller.channel.vo.PayChannelCreateReqVO;
  5 +import cn.iocoder.yudao.adminserver.modules.pay.controller.channel.vo.PayChannelExportReqVO;
  6 +import cn.iocoder.yudao.adminserver.modules.pay.controller.channel.vo.PayChannelPageReqVO;
  7 +import cn.iocoder.yudao.adminserver.modules.pay.controller.channel.vo.PayChannelUpdateReqVO;
  8 +import cn.iocoder.yudao.adminserver.modules.pay.dal.mysql.channel.PayChannelMapper;
  9 +import cn.iocoder.yudao.adminserver.modules.pay.service.channel.impl.PayChannelServiceImpl;
  10 +import cn.iocoder.yudao.coreservice.modules.pay.dal.dataobject.merchant.PayChannelDO;
  11 +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
  12 +import cn.iocoder.yudao.framework.common.pojo.PageResult;
  13 +import cn.iocoder.yudao.framework.common.util.object.ObjectUtils;
  14 +import cn.iocoder.yudao.framework.pay.core.client.impl.alipay.AlipayPayClientConfig;
  15 +import cn.iocoder.yudao.framework.pay.core.client.impl.wx.WXPayClientConfig;
  16 +import cn.iocoder.yudao.framework.pay.core.enums.PayChannelEnum;
  17 +import com.alibaba.fastjson.JSON;
  18 +import org.junit.jupiter.api.Test;
  19 +import org.springframework.context.annotation.Import;
  20 +
  21 +import javax.annotation.Resource;
  22 +import javax.validation.Validator;
  23 +import java.util.List;
  24 +
  25 +import static cn.iocoder.yudao.coreservice.modules.pay.enums.PayErrorCodeCoreConstants.CHANNEL_NOT_EXISTS;
  26 +import static cn.iocoder.yudao.framework.common.util.date.DateUtils.buildTime;
  27 +import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals;
  28 +import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertServiceException;
  29 +import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomLongId;
  30 +import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomPojo;
  31 +import static org.junit.jupiter.api.Assertions.*;
  32 +
  33 +/**
  34 + * {@link PayChannelServiceImpl} 的单元测试类
  35 + *
  36 + * @author 芋艿
  37 + */
  38 +@Import(PayChannelServiceImpl.class)
  39 +public class PayChannelServiceTest extends BaseDbUnitTest {
  40 +
  41 + @Resource
  42 + private PayChannelServiceImpl channelService;
  43 +
  44 + @Resource
  45 + private PayChannelMapper channelMapper;
  46 +
  47 +
  48 + @Test
  49 + public void testCreateWechatVersion2Channel_success() {
  50 + // 准备参数
  51 +
  52 + WXPayClientConfig v2Config = getV2Config();
  53 + PayChannelCreateReqVO reqVO = randomPojo(PayChannelCreateReqVO.class, o -> {
  54 + o.setCode(PayChannelEnum.WX_PUB.getCode());
  55 + o.setStatus(CommonStatusEnum.ENABLE.getStatus());
  56 + o.setConfig(JSON.toJSONString(v2Config));
  57 + });
  58 +
  59 + // 调用
  60 + Long channelId = channelService.createChannel(reqVO);
  61 + // 断言
  62 + assertNotNull(channelId);
  63 + // 校验记录的属性是否正确
  64 + PayChannelDO channel = channelMapper.selectById(channelId);
  65 + assertPojoEquals(reqVO, channel, "config");
  66 + // 关于config 对象应该拿出来重新对比
  67 + assertPojoEquals(v2Config, channel.getConfig());
  68 +
  69 + }
  70 +
  71 + @Test
  72 + public void testCreateWechatVersion3Channel_success() {
  73 + // 准备参数
  74 +
  75 + WXPayClientConfig v3Config = getV3Config();
  76 + PayChannelCreateReqVO reqVO = randomPojo(PayChannelCreateReqVO.class, o -> {
  77 + o.setCode(PayChannelEnum.WX_PUB.getCode());
  78 + o.setStatus(CommonStatusEnum.ENABLE.getStatus());
  79 + o.setConfig(JSON.toJSONString(v3Config));
  80 + });
  81 +
  82 + // 调用
  83 + Long channelId = channelService.createChannel(reqVO);
  84 + // 断言
  85 + assertNotNull(channelId);
  86 + // 校验记录的属性是否正确
  87 + PayChannelDO channel = channelMapper.selectById(channelId);
  88 + assertPojoEquals(reqVO, channel, "config");
  89 + // 关于config 对象应该拿出来重新对比
  90 + assertPojoEquals(v3Config, channel.getConfig());
  91 + }
  92 +
  93 + @Test
  94 + public void testCreateAliPayPublicKeyChannel_success() {
  95 + // 准备参数
  96 +
  97 + AlipayPayClientConfig payClientConfig = getPublicKeyConfig();
  98 + PayChannelCreateReqVO reqVO = randomPojo(PayChannelCreateReqVO.class, o -> {
  99 + o.setCode(PayChannelEnum.ALIPAY_APP.getCode());
  100 + o.setStatus(CommonStatusEnum.ENABLE.getStatus());
  101 + o.setConfig(JSON.toJSONString(payClientConfig));
  102 + });
  103 +
  104 + // 调用
  105 + Long channelId = channelService.createChannel(reqVO);
  106 + // 断言
  107 + assertNotNull(channelId);
  108 + // 校验记录的属性是否正确
  109 + PayChannelDO channel = channelMapper.selectById(channelId);
  110 + assertPojoEquals(reqVO, channel, "config");
  111 + // 关于config 对象应该拿出来重新对比
  112 + assertPojoEquals(payClientConfig, channel.getConfig());
  113 +
  114 + }
  115 +
  116 + @Test
  117 + public void testCreateAliPayCertificateChannel_success() {
  118 + // 准备参数
  119 +
  120 + AlipayPayClientConfig payClientConfig = getCertificateConfig();
  121 + PayChannelCreateReqVO reqVO = randomPojo(PayChannelCreateReqVO.class, o -> {
  122 + o.setCode(PayChannelEnum.ALIPAY_APP.getCode());
  123 + o.setStatus(CommonStatusEnum.ENABLE.getStatus());
  124 + o.setConfig(JSON.toJSONString(payClientConfig));
  125 + });
  126 +
  127 + // 调用
  128 + Long channelId = channelService.createChannel(reqVO);
  129 + // 断言
  130 + assertNotNull(channelId);
  131 + // 校验记录的属性是否正确
  132 + PayChannelDO channel = channelMapper.selectById(channelId);
  133 + assertPojoEquals(reqVO, channel, "config");
  134 + // 关于config 对象应该拿出来重新对比
  135 + assertPojoEquals(payClientConfig, channel.getConfig());
  136 + }
  137 +
  138 + @Test
  139 + public void testUpdateChannel_success() {
  140 + // mock 数据
  141 + AlipayPayClientConfig payClientConfig = getCertificateConfig();
  142 + PayChannelDO dbChannel = randomPojo(PayChannelDO.class, o -> {
  143 + o.setCode(PayChannelEnum.ALIPAY_APP.getCode());
  144 + o.setStatus(CommonStatusEnum.ENABLE.getStatus());
  145 + o.setConfig(payClientConfig);
  146 + });
  147 + channelMapper.insert(dbChannel);// @Sql: 先插入出一条存在的数据
  148 + // 准备参数
  149 + AlipayPayClientConfig payClientPublicKeyConfig = getPublicKeyConfig();
  150 + PayChannelUpdateReqVO reqVO = randomPojo(PayChannelUpdateReqVO.class, o -> {
  151 + o.setCode(dbChannel.getCode());
  152 + o.setStatus(dbChannel.getStatus());
  153 + o.setConfig(JSON.toJSONString(payClientPublicKeyConfig));
  154 + o.setId(dbChannel.getId()); // 设置更新的 ID
  155 + });
  156 +
  157 + // 调用
  158 + channelService.updateChannel(reqVO);
  159 + // 校验是否更新正确
  160 + PayChannelDO channel = channelMapper.selectById(reqVO.getId()); // 获取最新的
  161 + assertPojoEquals(reqVO, channel, "config");
  162 + assertPojoEquals(payClientPublicKeyConfig, channel.getConfig());
  163 + }
  164 +
  165 + @Test
  166 + public void testUpdateChannel_notExists() {
  167 + // 准备参数
  168 + AlipayPayClientConfig payClientPublicKeyConfig = getPublicKeyConfig();
  169 + PayChannelUpdateReqVO reqVO = randomPojo(PayChannelUpdateReqVO.class, o -> {
  170 + o.setCode(PayChannelEnum.ALIPAY_APP.getCode());
  171 + o.setStatus(CommonStatusEnum.ENABLE.getStatus());
  172 + o.setConfig(JSON.toJSONString(payClientPublicKeyConfig));
  173 + });
  174 +
  175 + // 调用, 并断言异常
  176 + assertServiceException(() -> channelService.updateChannel(reqVO), CHANNEL_NOT_EXISTS);
  177 + }
  178 +
  179 + @Test
  180 + public void testDeleteChannel_success() {
  181 + // mock 数据
  182 + AlipayPayClientConfig payClientConfig = getCertificateConfig();
  183 + PayChannelDO dbChannel = randomPojo(PayChannelDO.class, o -> {
  184 + o.setCode(PayChannelEnum.ALIPAY_APP.getCode());
  185 + o.setStatus(CommonStatusEnum.ENABLE.getStatus());
  186 + o.setConfig(payClientConfig);
  187 + });
  188 + channelMapper.insert(dbChannel);// @Sql: 先插入出一条存在的数据
  189 + // 准备参数
  190 + Long id = dbChannel.getId();
  191 +
  192 + // 调用
  193 + channelService.deleteChannel(id);
  194 + // 校验数据不存在了
  195 + assertNull(channelMapper.selectById(id));
  196 + }
  197 +
  198 + @Test
  199 + public void testDeleteChannel_notExists() {
  200 + // 准备参数
  201 + Long id = randomLongId();
  202 +
  203 + // 调用, 并断言异常
  204 + assertServiceException(() -> channelService.deleteChannel(id), CHANNEL_NOT_EXISTS);
  205 + }
  206 +
  207 + @Test // TODO 请修改 null 为需要的值
  208 + public void testGetChannelPage() {
  209 + // mock 数据
  210 + AlipayPayClientConfig payClientConfig = getCertificateConfig();
  211 + PayChannelDO dbChannel = randomPojo(PayChannelDO.class, o -> { // 等会查询到
  212 + o.setCode(PayChannelEnum.ALIPAY_APP.getCode());
  213 + o.setStatus(CommonStatusEnum.ENABLE.getStatus());
  214 + o.setRemark("灿灿子的支付渠道");
  215 + o.setFeeRate(0.03);
  216 + o.setMerchantId(1L);
  217 + o.setAppId(1L);
  218 + o.setConfig(payClientConfig);
  219 + o.setCreateTime(buildTime(2021,11,20));
  220 + });
  221 + channelMapper.insert(dbChannel);
  222 + // 执行拷贝的时候会出现异常,所以在插入后要重置为null 后续在写入新的
  223 + dbChannel.setConfig(null);
  224 + // 测试 code 不匹配
  225 + channelMapper.insert(ObjectUtils.clone(dbChannel, o -> {
  226 + o.setConfig(payClientConfig);
  227 + o.setCode(PayChannelEnum.WX_PUB.getCode());
  228 + }));
  229 + // 测试 status 不匹配
  230 + channelMapper.insert(ObjectUtils.clone(dbChannel, o -> {
  231 + o.setConfig(payClientConfig);
  232 + o.setStatus(CommonStatusEnum.DISABLE.getStatus());
  233 + }));
  234 + // 测试 remark 不匹配
  235 + channelMapper.insert(ObjectUtils.clone(dbChannel, o ->{
  236 + o.setConfig(payClientConfig);
  237 + o.setRemark("敏敏子的渠道");
  238 + }));
  239 + // 测试 feeRate 不匹配
  240 + channelMapper.insert(ObjectUtils.clone(dbChannel, o -> {
  241 + o.setConfig(payClientConfig);
  242 + o.setFeeRate(1.23);
  243 + }));
  244 + // 测试 merchantId 不匹配
  245 + channelMapper.insert(ObjectUtils.clone(dbChannel, o -> {
  246 + o.setConfig(payClientConfig);
  247 + o.setMerchantId(2L);
  248 + }));
  249 + // 测试 appId 不匹配
  250 + channelMapper.insert(ObjectUtils.clone(dbChannel, o -> {
  251 + o.setConfig(payClientConfig);
  252 + o.setAppId(2L);
  253 + }));
  254 + // 测试 createTime 不匹配
  255 + channelMapper.insert(ObjectUtils.clone(dbChannel, o -> {
  256 + o.setConfig(payClientConfig);
  257 + o.setCreateTime(buildTime(2021, 10, 20));
  258 + }));
  259 + // 准备参数
  260 + PayChannelPageReqVO reqVO = new PayChannelPageReqVO();
  261 + reqVO.setCode(PayChannelEnum.ALIPAY_APP.getCode());
  262 + reqVO.setStatus(CommonStatusEnum.ENABLE.getStatus());
  263 + reqVO.setRemark("灿灿子的支付渠道");
  264 + reqVO.setFeeRate(0.03);
  265 + reqVO.setMerchantId(1L);
  266 + reqVO.setAppId(1L);
  267 + reqVO.setConfig(JSON.toJSONString(payClientConfig));
  268 + reqVO.setBeginCreateTime(buildTime(2021,11,19));
  269 + reqVO.setEndCreateTime(buildTime(2021,11,21));
  270 +
  271 + // 调用
  272 + PageResult<PayChannelDO> pageResult = channelService.getChannelPage(reqVO);
  273 + // 断言
  274 + assertEquals(1, pageResult.getTotal());
  275 + assertEquals(1, pageResult.getList().size());
  276 + assertPojoEquals(dbChannel, pageResult.getList().get(0), "config");
  277 + assertPojoEquals(payClientConfig, pageResult.getList().get(0).getConfig());
  278 +
  279 + }
  280 +
  281 + @Test
  282 + public void testGetChannelList() {
  283 + // mock 数据
  284 + AlipayPayClientConfig payClientConfig = getCertificateConfig();
  285 + PayChannelDO dbChannel = randomPojo(PayChannelDO.class, o -> { // 等会查询到
  286 + o.setCode(PayChannelEnum.ALIPAY_APP.getCode());
  287 + o.setStatus(CommonStatusEnum.ENABLE.getStatus());
  288 + o.setRemark("灿灿子的支付渠道");
  289 + o.setFeeRate(0.03);
  290 + o.setMerchantId(1L);
  291 + o.setAppId(1L);
  292 + o.setConfig(payClientConfig);
  293 + o.setCreateTime(buildTime(2021,11,20));
  294 + });
  295 + channelMapper.insert(dbChannel);
  296 + // 执行拷贝的时候会出现异常,所以在插入后要重置为null 后续在写入新的
  297 + dbChannel.setConfig(null);
  298 + // 测试 code 不匹配
  299 + channelMapper.insert(ObjectUtils.clone(dbChannel, o -> {
  300 + o.setConfig(payClientConfig);
  301 + o.setCode(PayChannelEnum.WX_PUB.getCode());
  302 + }));
  303 + // 测试 status 不匹配
  304 + channelMapper.insert(ObjectUtils.clone(dbChannel, o -> {
  305 + o.setConfig(payClientConfig);
  306 + o.setStatus(CommonStatusEnum.DISABLE.getStatus());
  307 + }));
  308 + // 测试 remark 不匹配
  309 + channelMapper.insert(ObjectUtils.clone(dbChannel, o ->{
  310 + o.setConfig(payClientConfig);
  311 + o.setRemark("敏敏子的渠道");
  312 + }));
  313 + // 测试 feeRate 不匹配
  314 + channelMapper.insert(ObjectUtils.clone(dbChannel, o -> {
  315 + o.setConfig(payClientConfig);
  316 + o.setFeeRate(1.23);
  317 + }));
  318 + // 测试 merchantId 不匹配
  319 + channelMapper.insert(ObjectUtils.clone(dbChannel, o -> {
  320 + o.setConfig(payClientConfig);
  321 + o.setMerchantId(2L);
  322 + }));
  323 + // 测试 appId 不匹配
  324 + channelMapper.insert(ObjectUtils.clone(dbChannel, o -> {
  325 + o.setConfig(payClientConfig);
  326 + o.setAppId(2L);
  327 + }));
  328 + // 测试 createTime 不匹配
  329 + channelMapper.insert(ObjectUtils.clone(dbChannel, o -> {
  330 + o.setConfig(payClientConfig);
  331 + o.setCreateTime(buildTime(2021, 10, 20));
  332 + }));
  333 + // 准备参数
  334 + PayChannelExportReqVO reqVO = new PayChannelExportReqVO();
  335 + reqVO.setCode(PayChannelEnum.ALIPAY_APP.getCode());
  336 + reqVO.setStatus(CommonStatusEnum.ENABLE.getStatus());
  337 + reqVO.setRemark("灿灿子的支付渠道");
  338 + reqVO.setFeeRate(0.03);
  339 + reqVO.setMerchantId(1L);
  340 + reqVO.setAppId(1L);
  341 + reqVO.setConfig(JSON.toJSONString(payClientConfig));
  342 + reqVO.setBeginCreateTime(buildTime(2021,11,19));
  343 + reqVO.setEndCreateTime(buildTime(2021,11,21));
  344 +
  345 + // 调用
  346 + List<PayChannelDO> list = channelService.getChannelList(reqVO);
  347 + // 断言
  348 + assertEquals(1, list.size());
  349 + assertPojoEquals(dbChannel, list.get(0), "config");
  350 + assertPojoEquals(payClientConfig, list.get(0).getConfig());
  351 + }
  352 +
  353 +
  354 + public WXPayClientConfig getV2Config() {
  355 + return new WXPayClientConfig()
  356 + .setAppId("APP00001")
  357 + .setMchId("MCH00001")
  358 + .setApiVersion(WXPayClientConfig.API_VERSION_V2)
  359 + .setMchKey("dsa1d5s6a1d6sa16d1sa56d15a61das6")
  360 + .setApiV3Key("")
  361 + .setPrivateCertContent("")
  362 + .setPrivateKeyContent("");
  363 + }
  364 +
  365 + public WXPayClientConfig getV3Config() {
  366 + return new WXPayClientConfig()
  367 + .setAppId("APP00001")
  368 + .setMchId("MCH00001")
  369 + .setApiVersion(WXPayClientConfig.API_VERSION_V3)
  370 + .setMchKey("")
  371 + .setApiV3Key("sdadasdsadadsa")
  372 + .setPrivateKeyContent("dsa445das415d15asd16ad156as")
  373 + .setPrivateCertContent("dsadasd45asd4s5a");
  374 +
  375 + }
  376 +
  377 + public AlipayPayClientConfig getPublicKeyConfig() {
  378 + return new AlipayPayClientConfig()
  379 + .setServerUrl(AlipayPayClientConfig.SERVER_URL_PROD)
  380 + .setAppId("APP00001")
  381 + .setSignType(AlipayPayClientConfig.SIGN_TYPE_DEFAULT)
  382 + .setMode(AlipayPayClientConfig.MODE_PUBLIC_KEY)
  383 + .setPrivateKey("13131321312")
  384 + .setAlipayPublicKey("13321321321")
  385 + .setAppCertContent("")
  386 + .setAlipayPublicCertContent("")
  387 + .setRootCertContent("");
  388 + }
  389 +
  390 + public AlipayPayClientConfig getCertificateConfig() {
  391 + return new AlipayPayClientConfig()
  392 + .setServerUrl(AlipayPayClientConfig.SERVER_URL_PROD)
  393 + .setAppId("APP00001")
  394 + .setSignType(AlipayPayClientConfig.SIGN_TYPE_DEFAULT)
  395 + .setMode(AlipayPayClientConfig.MODE_CERTIFICATE)
  396 + .setPrivateKey("")
  397 + .setAlipayPublicKey("")
  398 + .setAppCertContent("13321321321sda")
  399 + .setAlipayPublicCertContent("13321321321aqeqw")
  400 + .setRootCertContent("13321321321dsad");
  401 + }
  402 +
  403 +
  404 +}
... ...
yudao-admin-server/src/test/java/cn/iocoder/yudao/adminserver/modules/pay/merchant/service/PayMerchantServiceTest.java renamed to yudao-admin-server/src/test/java/cn/iocoder/yudao/adminserver/modules/pay/service/merchant/PayMerchantServiceTest.java
1   -package cn.iocoder.yudao.adminserver.modules.pay.merchant.service;
  1 +package cn.iocoder.yudao.adminserver.modules.pay.service.merchant;
2 2  
3 3 import cn.hutool.core.util.RandomUtil;
4   -import cn.hutool.http.HttpUtil;
5   -import cn.hutool.json.JSONObject;
6   -import cn.hutool.json.JSONUtil;
7 4 import cn.iocoder.yudao.adminserver.BaseDbUnitTest;
8 5 import cn.iocoder.yudao.adminserver.modules.pay.controller.merchant.vo.PayMerchantCreateReqVO;
9 6 import cn.iocoder.yudao.adminserver.modules.pay.controller.merchant.vo.PayMerchantExportReqVO;
... ... @@ -54,8 +51,7 @@ public class PayMerchantServiceTest extends BaseDbUnitTest {
54 51 assertNotNull(merchantId);
55 52 // 校验记录的属性是否正确
56 53 PayMerchantDO merchant = merchantMapper.selectById(merchantId);
57   - // TODO @aquan:需要判断 no 非空
58   - assertPojoEquals(reqVO, merchant,"no");
  54 + assertPojoEquals(reqVO, merchant);
59 55 }
60 56  
61 57 @Test
... ...
yudao-admin-server/src/test/resources/sql/clean.sql
... ... @@ -27,3 +27,5 @@ DELETE FROM &quot;sys_social_user&quot;;
27 27  
28 28 -- pay 开头的 DB
29 29 DELETE FROM pay_merchant;
  30 +DELETE FROM pay_app;
  31 +DELETE FROM pay_channel
... ...
yudao-admin-server/src/test/resources/sql/create_tables.sql
... ... @@ -464,3 +464,37 @@ CREATE TABLE IF NOT EXISTS &quot;pay_merchant&quot; (
464 464 "deleted" bit(1) NOT NULL DEFAULT FALSE,
465 465 PRIMARY KEY ("id")
466 466 ) COMMENT '支付商户信息';
  467 +
  468 +CREATE TABLE IF NOT EXISTS "pay_app" (
  469 + "id" number NOT NULL GENERATED BY DEFAULT AS IDENTITY,
  470 + "name" varchar(64) NOT NULL,
  471 + "status" tinyint NOT NULL,
  472 + "remark" varchar(255) DEFAULT NULL,
  473 + `pay_notify_url` varchar(1024) NOT NULL,
  474 + `refund_notify_url`varchar(1024) NOT NULL,
  475 + `merchant_id`bigint(20) NOT NULL,
  476 + "creator" varchar(64) DEFAULT '',
  477 + "create_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ,
  478 + "updater" varchar(64) DEFAULT '',
  479 + "update_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  480 + "deleted" bit(1) NOT NULL DEFAULT FALSE,
  481 + PRIMARY KEY ("id")
  482 +) COMMENT = '支付应用信息';
  483 +
  484 +CREATE TABLE "pay_channel" (
  485 + "id" number NOT NULL GENERATED BY DEFAULT AS IDENTITY,
  486 + "code" varchar(32) NOT NULL ,
  487 + "status" tinyint(4) NOT NULL ,
  488 + "remark" varchar(255) DEFAULT NULL ,
  489 + "fee_rate" double NOT NULL DEFAULT 0 ,
  490 + "merchant_id" bigint(20) NOT NULL ,
  491 + "app_id" bigint(20) NOT NULL ,
  492 + "config" varchar(10240) NOT NULL ,
  493 + "creator" varchar(64) NULL DEFAULT '' ,
  494 + "create_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ,
  495 + "updater" varchar(64) NULL DEFAULT '' ,
  496 + "update_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  497 + "deleted" bit(1) NOT NULL DEFAULT FALSE,
  498 + PRIMARY KEY ("id")
  499 +)COMMENT = '支付渠道';
  500 +
... ...
yudao-admin-ui/src/api/pay/channel.js
... ... @@ -28,12 +28,12 @@ export function deleteChannel(id) {
28 28 }
29 29  
30 30 // 获得支付渠道
31   -export function getChannel(id) {
32   - return request({
33   - url: '/pay/channel/get?id=' + id,
34   - method: 'get'
35   - })
36   -}
  31 +// export function getChannel(id) {
  32 +// return request({
  33 +// url: '/pay/channel/get?id=' + id,
  34 +// method: 'get'
  35 +// })
  36 +// }
37 37  
38 38  
39 39  
... ... @@ -56,19 +56,10 @@ export function exportChannelExcel(query) {
56 56 })
57 57 }
58 58  
59   -// 创建微信支付渠道
60   -export function createWechatChannel(data) {
61   - return request({
62   - url: '/pay/channel/create-wechat',
63   - method: 'post',
64   - data: data
65   - })
66   -}
67   -
68 59 // 获得支付渠道
69   -export function getWechatChannel(merchantId,appId,code) {
  60 +export function getChannel(merchantId,appId,code) {
70 61 return request({
71   - url: '/pay/channel/get-wechat',
  62 + url: '/pay/channel/get-channel',
72 63 params:{
73 64 merchantId:merchantId,
74 65 appId:appId,
... ... @@ -78,11 +69,3 @@ export function getWechatChannel(merchantId,appId,code) {
78 69 })
79 70 }
80 71  
81   -// 更新支付渠道
82   -export function updateWechatChannel(data) {
83   - return request({
84   - url: '/pay/channel/update-wechat',
85   - method: 'put',
86   - data: data
87   - })
88   -}
... ...
yudao-admin-ui/src/utils/constants.js
... ... @@ -121,3 +121,8 @@ export const PayChannelEnum = {
121 121 "name": "支付宝扫码支付"
122 122 },
123 123 }
  124 +
  125 +export const PayType = {
  126 + WECHAT: "WECHAT",
  127 + ALIPAY: "ALIPAY"
  128 +}
... ...
yudao-admin-ui/src/utils/dict.js
... ... @@ -39,7 +39,12 @@ export const DICT_TYPE = {
39 39 PAY_CHANNEL_STATUS: 'pay_channel_status',
40 40 // 微信渠道版本
41 41 PAY_CHANNEL_WECHAT_VERSION:'pay_channel_wechat_version',
42   -
  42 + // 支付渠道支付宝算法类型
  43 + PAY_CHANNEL_ALIPAY_SIGN_TYPE:'pay_channel_alipay_sign_type',
  44 + // 支付宝公钥类型
  45 + PAY_CHANNEL_ALIPAY_MODE:'pay_channel_alipay_mode',
  46 + // 支付宝网关地址
  47 + PAY_CHANNEL_ALIPAY_SERVER_TYPE:'pay_channel_alipay_server_type',
43 48 }
44 49  
45 50 /**
... ...
yudao-admin-ui/src/views/pay/app/components/aliPayChannelForm.vue 0 → 100644
  1 +<template>
  2 + <div>
  3 + <el-dialog :visible.sync="transferParam.aliPayOpen" @closed="close" append-to-body width="800px">
  4 + <el-form ref="aliPayForm" :model="form" :rules="rules" size="medium" label-width="100px"
  5 + v-loading="transferParam.loading">
  6 + <el-form-item label-width="180px" label="渠道费率" prop="feeRate">
  7 + <el-input v-model="form.feeRate" placeholder="请输入渠道费率" clearable :style="{width: '100%'}">
  8 + <template slot="append">%</template>
  9 + </el-input>
  10 + </el-form-item>
  11 + <el-form-item label-width="180px" label="开放平台APPID" prop="aliPayConfig.appId">
  12 + <el-input v-model="form.aliPayConfig.appId" placeholder="请输入开放平台APPID" clearable :style="{width: '100%'}">
  13 + </el-input>
  14 + </el-form-item>
  15 + <el-form-item label-width="180px" label="渠道状态" prop="status">
  16 + <el-radio-group v-model="form.status" size="medium">
  17 + <el-radio v-for="dict in statusDictDatas" :key="parseInt(dict.value)" :label="parseInt(dict.value)">
  18 + {{ dict.label }}
  19 + </el-radio>
  20 + </el-radio-group>
  21 + </el-form-item>
  22 + <el-form-item label-width="180px" label="网关地址" prop="aliPayConfig.serverUrl">
  23 + <el-radio-group v-model="form.aliPayConfig.serverUrl" size="medium">
  24 + <el-radio v-for="dict in aliPayServerDatas" :key="dict.value" :label="dict.value">
  25 + {{ dict.label }}
  26 + </el-radio>
  27 + </el-radio-group>
  28 + </el-form-item>
  29 + <el-form-item label-width="180px" label="算法类型" prop="aliPayConfig.signType">
  30 + <el-radio-group v-model="form.aliPayConfig.signType" size="medium">
  31 + <el-radio v-for="dict in aliPaySignTypeDatas" :key="dict.value" :label="dict.value">
  32 + {{ dict.label }}
  33 + </el-radio>
  34 + </el-radio-group>
  35 + </el-form-item>
  36 + <el-form-item label-width="180px" label="公钥类型" prop="aliPayConfig.mode">
  37 + <el-radio-group v-model="form.aliPayConfig.mode" size="medium">
  38 + <el-radio v-for="dict in aliPayModeDatas" :key="parseInt(dict.value)" :label="parseInt(dict.value)">
  39 + {{ dict.label }}
  40 + </el-radio>
  41 + </el-radio-group>
  42 + </el-form-item>
  43 + <div v-if="form.aliPayConfig.mode === 1">
  44 + <el-form-item label-width="180px" label="商户私钥" prop="aliPayConfig.privateKey">
  45 + <el-input type="textarea" :autosize="{minRows: 8, maxRows: 8}" v-model="form.aliPayConfig.privateKey"
  46 + placeholder="请输入商户私钥" clearable :style="{width: '100%'}">
  47 + </el-input>
  48 + </el-form-item>
  49 + <el-form-item label-width="180px" label="支付宝公钥字符串" prop="aliPayConfig.alipayPublicKey">
  50 + <el-input
  51 + type="textarea"
  52 + :autosize="{minRows: 8, maxRows: 8}"
  53 + v-model="form.aliPayConfig.alipayPublicKey"
  54 + placeholder="请输入支付宝公钥字符串" clearable
  55 + :style="{width: '100%'}">
  56 + </el-input>
  57 + </el-form-item>
  58 + </div>
  59 + <div v-if="form.aliPayConfig.mode === 2">
  60 + <el-form-item label-width="180px" label="商户公钥应用证书" prop="aliPayConfig.appCertContent">
  61 + <el-input v-model="form.aliPayConfig.appCertContent" type="textarea"
  62 + placeholder="请上传商户公钥应用证书"
  63 + readonly :autosize="{minRows: 8, maxRows: 8}" :style="{width: '100%'}"></el-input>
  64 + </el-form-item>
  65 + <el-form-item label-width="180px" label="">
  66 + <el-upload
  67 + action=""
  68 + ref="privateKeyContentFile"
  69 + :limit="1"
  70 + :accept="fileAccept"
  71 + :http-request="appCertUpload"
  72 + :before-upload="fileBeforeUpload">
  73 + <el-button size="small" type="primary" icon="el-icon-upload">点击上传</el-button>
  74 + </el-upload>
  75 + </el-form-item>
  76 + <el-form-item label-width="180px" label="支付宝公钥证书" prop="aliPayConfig.alipayPublicCertContent">
  77 + <el-input v-model="form.aliPayConfig.alipayPublicCertContent" type="textarea"
  78 + placeholder="请上传支付宝公钥证书"
  79 + readonly :autosize="{minRows: 8, maxRows: 8}" :style="{width: '100%'}"></el-input>
  80 + </el-form-item>
  81 + <el-form-item label-width="180px" label="">
  82 + <el-upload
  83 + ref="privateCertContentFile"
  84 + action=""
  85 + :limit="1"
  86 + :accept="fileAccept"
  87 + :before-upload="fileBeforeUpload"
  88 + :http-request="alipayPublicCertUpload">
  89 + <el-button size="small" type="primary" icon="el-icon-upload">点击上传</el-button>
  90 + </el-upload>
  91 + </el-form-item>
  92 + <el-form-item label-width="180px" label="根证书" prop="aliPayConfig.rootCertContent">
  93 + <el-input
  94 + v-model="form.aliPayConfig.rootCertContent"
  95 + type="textarea"
  96 + placeholder="请上传根证书"
  97 + readonly :autosize="{minRows: 8, maxRows: 8}"
  98 + :style="{width: '100%'}">
  99 + </el-input>
  100 + </el-form-item>
  101 + <el-form-item label-width="180px" label="">
  102 + <el-upload
  103 + ref="privateCertContentFile"
  104 + :limit="1"
  105 + :accept="fileAccept"
  106 + action=""
  107 + :before-upload="fileBeforeUpload"
  108 + :http-request="rootCertUpload">
  109 + <el-button size="small" type="primary" icon="el-icon-upload">点击上传</el-button>
  110 + </el-upload>
  111 + </el-form-item>
  112 + </div>
  113 + <el-form-item label-width="180px" label="备注" prop="remark">
  114 + <el-input v-model="form.remark" :style="{width: '100%'}"></el-input>
  115 + </el-form-item>
  116 + </el-form>
  117 + <div slot="footer" class="dialog-footer">
  118 + <el-button @click="close">取消</el-button>
  119 + <el-button type="primary" @click="handleConfirm">确定</el-button>
  120 + </div>
  121 + </el-dialog>
  122 + </div>
  123 +</template>
  124 +<script>
  125 +import {DICT_TYPE, getDictDatas} from "@/utils/dict";
  126 +import {createChannel, getChannel, updateChannel} from "@/api/pay/channel";
  127 +
  128 +const defaultForm = {
  129 + code: '',
  130 + status: null,
  131 + remark: '',
  132 + feeRate: null,
  133 + appId: '',
  134 + merchantId: null,
  135 + aliPayConfig: {
  136 + appId: '',
  137 + serverUrl: null,
  138 + signType: '',
  139 + mode: null,
  140 + privateKey: '',
  141 + alipayPublicKey: '',
  142 + appCertContent: '',
  143 + alipayPublicCertContent: '',
  144 + rootCertContent: ''
  145 + }
  146 +};
  147 +
  148 +export default {
  149 + name: "aliPayChannelForm",
  150 + components: {},
  151 + props: {
  152 + // 传输的参数
  153 + transferParam: {
  154 + // 加载动画
  155 + "loading": false,
  156 + // 是否修改
  157 + "edit": false,
  158 + // 是否显示
  159 + "aliPayOpen": false,
  160 + // 应用ID
  161 + "appId": null,
  162 + // 渠道编码
  163 + "payCode": null,
  164 + // 商户对象
  165 + "payMerchant": {
  166 + // 编号
  167 + "id": null,
  168 + // 名称
  169 + "name": null
  170 + },
  171 + }
  172 + },
  173 + data() {
  174 + return {
  175 + form: JSON.parse(JSON.stringify(defaultForm)),
  176 + rules: {
  177 + feeRate: [{
  178 + required: true,
  179 + message: '请输入渠道费率',
  180 + trigger: 'blur'
  181 + }],
  182 + 'aliPayConfig.appId': [{
  183 + required: true,
  184 + message: '请输入开放平台上创建的应用的 ID',
  185 + trigger: 'blur'
  186 + }],
  187 + status: [{
  188 + required: true,
  189 + message: '渠道状态不能为空',
  190 + trigger: 'blur'
  191 + }],
  192 + 'aliPayConfig.serverUrl': [{
  193 + required: true,
  194 + message: '请传入网关地址',
  195 + trigger: 'blur'
  196 + }],
  197 + 'aliPayConfig.signType': [{
  198 + required: true,
  199 + message: '请传入签名算法类型',
  200 + trigger: 'blur'
  201 + }],
  202 + 'aliPayConfig.mode': [{
  203 + required: true,
  204 + message: '公钥类型不能为空',
  205 + trigger: 'blur'
  206 + }],
  207 + 'aliPayConfig.privateKey': [{
  208 + required: true,
  209 + message: '请输入商户私钥',
  210 + trigger: 'blur'
  211 + }],
  212 + 'aliPayConfig.alipayPublicKey': [{
  213 + required: true,
  214 + message: '请输入支付宝公钥字符串',
  215 + trigger: 'blur'
  216 + }],
  217 + 'aliPayConfig.appCertContent': [{
  218 + required: true,
  219 + message: '请上传商户公钥应用证书',
  220 + trigger: 'blur'
  221 + }],
  222 + 'aliPayConfig.alipayPublicCertContent': [{
  223 + required: true,
  224 + message: '请上传支付宝公钥证书',
  225 + trigger: 'blur'
  226 + }],
  227 + 'aliPayConfig.rootCertContent': [{
  228 + required: true,
  229 + message: '请上传指定根证书',
  230 + trigger: 'blur'
  231 + }],
  232 + },
  233 + fileAccept: ".crt",
  234 + // 渠道状态 数据字典
  235 + statusDictDatas: getDictDatas(DICT_TYPE.PAY_CHANNEL_STATUS),
  236 + // 支付宝加密方式
  237 + aliPaySignTypeDatas: getDictDatas(DICT_TYPE.PAY_CHANNEL_ALIPAY_SIGN_TYPE),
  238 + // 版本状态 数据字典
  239 + aliPayModeDatas: getDictDatas(DICT_TYPE.PAY_CHANNEL_ALIPAY_MODE),
  240 + // 支付宝网关地址
  241 + aliPayServerDatas: getDictDatas(DICT_TYPE.PAY_CHANNEL_ALIPAY_SERVER_TYPE),
  242 + }
  243 + },
  244 + watch: {
  245 + transferParam: {
  246 + deep: true, // 深度监听
  247 + handler(newVal) {
  248 + if (newVal.aliPayOpen) {
  249 + this.form.code = newVal.payCode;
  250 + this.form.appId = newVal.appId;
  251 + this.form.merchantId = newVal.payMerchant.id;
  252 + // 只有在初次进来为编辑 并且为加载中的时候才回去请求数据
  253 + if (newVal.edit === true && newVal.loading) {
  254 + this.init();
  255 + }
  256 + }
  257 + }
  258 + }
  259 + },
  260 +
  261 + methods: {
  262 + init() {
  263 + getChannel(this.transferParam.payMerchant.id, this.transferParam.appId, this.transferParam.payCode)
  264 + .then(response => {
  265 + this.form.id = response.data.id;
  266 + this.form.feeRate = response.data.feeRate;
  267 + this.form.status = response.data.status;
  268 + this.form.remark = response.data.remark;
  269 +
  270 + let config = JSON.parse(response.data.config);
  271 + this.form.aliPayConfig.appId = config.appId;
  272 + this.form.aliPayConfig.serverUrl = config.serverUrl;
  273 + this.form.aliPayConfig.signType = config.signType;
  274 + this.form.aliPayConfig.mode = config.mode;
  275 + this.form.aliPayConfig.privateKey = config.privateKey;
  276 + this.form.aliPayConfig.alipayPublicKey = config.alipayPublicKey;
  277 + this.form.aliPayConfig.appCertContent = config.appCertContent;
  278 + this.form.aliPayConfig.alipayPublicCertContent = config.alipayPublicCertContent;
  279 + this.form.aliPayConfig.rootCertContent = config.rootCertContent;
  280 + this.transferParam.loading = false;
  281 + })
  282 + },
  283 + close() {
  284 + this.transferParam.aliPayOpen = false;
  285 + this.form = JSON.parse(JSON.stringify(defaultForm));
  286 + },
  287 + handleConfirm() {
  288 + this.$refs['aliPayForm'].validate(valid => {
  289 + if (!valid) {
  290 + return
  291 + }
  292 + let data = this.form;
  293 + data.config = JSON.stringify(this.form.aliPayConfig);
  294 + if (this.transferParam.edit) {
  295 + updateChannel(data).then(response => {
  296 + if (response.code === 0) {
  297 + this.msgSuccess("修改成功");
  298 + this.close();
  299 + }
  300 +
  301 + })
  302 + } else {
  303 +
  304 + createChannel(data).then(response => {
  305 + if (response.code === 0) {
  306 + this.msgSuccess("新增成功");
  307 + this.$parent.refreshTable();
  308 + this.close();
  309 + }
  310 + });
  311 + }
  312 + });
  313 + },
  314 + fileBeforeUpload(file) {
  315 + let format = '.' + file.name.split(".")[1];
  316 + if (format !== this.fileAccept) {
  317 + this.$message.error('请上传指定格式"' + this.fileAccept + '"文件');
  318 + return false;
  319 + }
  320 + let isRightSize = file.size / 1024 / 1024 < 2
  321 + if (!isRightSize) {
  322 + this.$message.error('文件大小超过 2MB')
  323 + }
  324 + return isRightSize
  325 + },
  326 + appCertUpload(event) {
  327 + const readFile = new FileReader()
  328 + readFile.onload = (e) => {
  329 + this.form.aliPayConfig.appCertContent = e.target.result
  330 + }
  331 + readFile.readAsText(event.file);
  332 + },
  333 + alipayPublicCertUpload(event) {
  334 + const readFile = new FileReader()
  335 + readFile.onload = (e) => {
  336 + this.form.aliPayConfig.alipayPublicCertContent = e.target.result
  337 + }
  338 + readFile.readAsText(event.file);
  339 + },
  340 + rootCertUpload(event) {
  341 + const readFile = new FileReader()
  342 + readFile.onload = (e) => {
  343 + this.form.aliPayConfig.rootCertContent = e.target.result
  344 + }
  345 + readFile.readAsText(event.file);
  346 + },
  347 +
  348 + }
  349 +}
  350 +
  351 +</script>
  352 +<style scoped>
  353 +
  354 +</style>
... ...
yudao-admin-ui/src/views/pay/app/components/wechatJsApiForm.vue renamed to yudao-admin-ui/src/views/pay/app/components/wechatChannelForm.vue
1 1 <template>
2 2 <div>
3   - <el-dialog :visible.sync="transferParam.open" @close="close" append-to-body>
  3 + <el-dialog :visible.sync="transferParam.wechatOpen" @close="close" append-to-body width="800px">
4 4 <el-form ref="wechatJsApiForm" :model="form" :rules="rules" size="medium" label-width="100px"
5 5 v-loading="transferParam.loading">
6 6 <el-form-item label-width="180px" label="渠道费率" prop="feeRate">
... ... @@ -22,7 +22,7 @@
22 22 </el-radio>
23 23 </el-radio-group>
24 24 </el-form-item>
25   - <el-form-item label-width="180px" label="API 版本" prop="weChatConfig.apiVersion">
  25 + <el-form-item label-width="180px" label="API 版本" prop="weChatConfig.apiVersion">
26 26 <el-radio-group v-model="form.weChatConfig.apiVersion" size="medium">
27 27 <el-radio v-for="dict in versionDictDatas" :key="dict.value" :label="dict.value">
28 28 {{ dict.label }}
... ... @@ -32,9 +32,13 @@
32 32 <el-form-item label-width="180px" label="商户秘钥" prop="weChatConfig.mchKey"
33 33 v-if="form.weChatConfig.apiVersion === 'v2'">
34 34 <el-input v-model="form.weChatConfig.mchKey" placeholder="请输入商户秘钥" clearable
35   - :style="{width: '100%'}"></el-input>
  35 + :style="{width: '100%'}" type="textarea" :autosize="{minRows: 8, maxRows: 8}"></el-input>
36 36 </el-form-item>
37 37 <div v-if="form.weChatConfig.apiVersion === 'v3'">
  38 + <el-form-item label-width="180px" label="API V3秘钥" prop="weChatConfig.apiV3Key">
  39 + <el-input v-model="form.weChatConfig.apiV3Key" placeholder="请输入API V3秘钥" clearable
  40 + :style="{width: '100%'}" type="textarea" :autosize="{minRows: 8, maxRows: 8}"></el-input>
  41 + </el-form-item>
38 42 <el-form-item label-width="180px" label="apiclient_key.perm证书" prop="weChatConfig.privateKeyContent">
39 43 <el-input v-model="form.weChatConfig.privateKeyContent" type="textarea"
40 44 placeholder="请上传apiclient_key.perm证书"
... ... @@ -45,9 +49,9 @@
45 49 :limit="1"
46 50 :accept="fileAccept"
47 51 :headers="header"
48   - :action="pemUploadAction"
  52 + action=""
49 53 :before-upload="pemFileBeforeUpload"
50   - :on-success="privateKeyUploadSuccess"
  54 + :http-request="privateKeyUpload"
51 55 >
52 56 <el-button size="small" type="primary" icon="el-icon-upload">点击上传</el-button>
53 57 </el-upload>
... ... @@ -62,9 +66,9 @@
62 66 :limit="1"
63 67 :accept="fileAccept"
64 68 :headers="header"
65   - :action="pemUploadAction"
  69 + action=""
66 70 :before-upload="pemFileBeforeUpload"
67   - :on-success="privateCertUploadSuccess"
  71 + :http-request="privateCertUpload"
68 72 >
69 73 <el-button size="small" type="primary" icon="el-icon-upload">点击上传</el-button>
70 74 </el-upload>
... ... @@ -83,10 +87,28 @@
83 87 </template>
84 88 <script>
85 89 import {DICT_TYPE, getDictDatas} from "@/utils/dict";
86   -import {createWechatChannel, getWechatChannel, updateWechatChannel} from "@/api/pay/channel";
  90 +import {createChannel, getChannel, updateChannel} from "@/api/pay/channel";
  91 +
  92 +const defaultForm = {
  93 + code: '',
  94 + status: null,
  95 + remark: '',
  96 + feeRate: null,
  97 + appId: '',
  98 + merchantId: null,
  99 + weChatConfig: {
  100 + appId: '',
  101 + mchId: '',
  102 + apiVersion: '',
  103 + mchKey: '',
  104 + privateKeyContent: '',
  105 + privateCertContent: '',
  106 + apiV3Key:'',
  107 + }
  108 +}
87 109  
88 110 export default {
89   - name: "wechatJsApiForm",
  111 + name: "wechatChannelForm",
90 112 components: {},
91 113 props: {
92 114 // 传输的参数
... ... @@ -96,7 +118,7 @@ export default {
96 118 // 是否修改
97 119 "edit": false,
98 120 // 是否显示
99   - "open": false,
  121 + "wechatOpen": false,
100 122 // 应用ID
101 123 "appId": null,
102 124 // 渠道编码
... ... @@ -112,22 +134,7 @@ export default {
112 134 },
113 135 data() {
114 136 return {
115   - form: {
116   - code: undefined,
117   - status: undefined,
118   - remark: undefined,
119   - feeRate: undefined,
120   - appId: undefined,
121   - merchantId: undefined,
122   - weChatConfig: {
123   - appId: undefined,
124   - mchId: undefined,
125   - apiVersion: undefined,
126   - mchKey: undefined,
127   - privateKeyContent: undefined,
128   - privateCertContent: undefined,
129   - }
130   - },
  137 + form: JSON.parse(JSON.stringify(defaultForm)),
131 138 rules: {
132 139 feeRate: [{
133 140 required: true,
... ... @@ -147,12 +154,12 @@ export default {
147 154 status: [{
148 155 required: true,
149 156 message: '渠道状态不能为空',
150   - trigger: 'change'
  157 + trigger: 'blur'
151 158 }],
152 159 'weChatConfig.apiVersion': [{
153 160 required: true,
154 161 message: 'API版本不能为空',
155   - trigger: 'change'
  162 + trigger: 'blur'
156 163 }],
157 164 'weChatConfig.mchKey': [{
158 165 required: true,
... ... @@ -169,12 +176,16 @@ export default {
169 176 message: '请上传apiclient_cert.perm证书',
170 177 trigger: 'blur'
171 178 }],
  179 + 'weChatConfig.apiV3Key': [{
  180 + required: true,
  181 + message: '请上传apiV3秘钥值',
  182 + trigger: 'blur'
  183 + }],
172 184 },
173 185 // 文件上传的header
174 186 header: {
175 187 "Authorization": null
176 188 },
177   - pemUploadAction: 'http://127.0.0.1:48080/api/pay/channel/parsing-pem',
178 189 fileAccept: ".pem",
179 190 // 渠道状态 数据字典
180 191 statusDictDatas: getDictDatas(DICT_TYPE.PAY_CHANNEL_STATUS),
... ... @@ -185,51 +196,66 @@ export default {
185 196 transferParam: {
186 197 deep: true, // 深度监听
187 198 handler(newVal) {
188   - this.form.code = newVal.payCode;
189   - this.form.appId = newVal.appId;
190   - this.form.merchantId = newVal.payMerchant.id;
191   - // 只有在初次进来为编辑 并且为加载中的时候才回去请求数据
192   - if (newVal.edit === true && newVal.loading) {
193   - this.init();
  199 + if (newVal.wechatOpen) {
  200 + this.form.code = newVal.payCode;
  201 + this.form.appId = newVal.appId;
  202 + this.form.merchantId = newVal.payMerchant.id;
  203 + // 只有在初次进来为编辑 并且为加载中的时候才回去请求数据
  204 + if (newVal.edit && newVal.loading) {
  205 + this.init();
  206 + }
194 207 }
195 208 }
196 209 }
197 210 },
198   - created() {
199   - this.header.Authorization = "Bearer " + this.$store.getters.token;
200   - },
201 211 methods: {
202 212 init() {
203   - getWechatChannel(this.transferParam.payMerchant.id, this.transferParam.appId, this.transferParam.payCode)
  213 + getChannel(this.transferParam.payMerchant.id, this.transferParam.appId, this.transferParam.payCode)
204 214 .then(response => {
205   - this.form = response.data;
  215 + this.form.id = response.data.id;
  216 + this.form.feeRate = response.data.feeRate;
  217 + this.form.appId = response.data.appId;
  218 + this.form.status = response.data.status;
  219 + this.form.remark = response.data.remark;
  220 +
  221 + let config = JSON.parse(response.data.config);
  222 + this.form.weChatConfig.appId = config.appId;
  223 + this.form.weChatConfig.apiVersion = config.apiVersion;
  224 + this.form.weChatConfig.mchId = config.mchId;
  225 + this.form.weChatConfig.mchKey = config.mchKey;
  226 + this.form.weChatConfig.privateKeyContent = config.privateKeyContent;
  227 + this.form.weChatConfig.privateCertContent = config.privateCertContent;
  228 + this.form.weChatConfig.apiV3Key = config.apiV3Key;
206 229 this.transferParam.loading = false;
207 230 })
208 231 },
209 232 close() {
210   - this.transferParam.open = false;
211   - this.$refs['wechatJsApiForm'].resetFields();
  233 + this.transferParam.wechatOpen = false;
  234 + this.form = JSON.parse(JSON.stringify(defaultForm));
212 235 },
213 236 handleConfirm() {
214 237 this.$refs['wechatJsApiForm'].validate(valid => {
215 238 if (!valid) {
216 239 return
217 240 }
  241 + let data = this.form;
  242 + data.config = JSON.stringify(this.form.weChatConfig);
218 243 if (this.transferParam.edit) {
219   - updateWechatChannel(this.form).then(response => {
220   - if (response.code === 0 ) {
  244 + updateChannel(data).then(response => {
  245 + if (response.code === 0) {
221 246 this.msgSuccess("修改成功");
222 247 this.close();
223 248 }
224 249  
225 250 })
226 251 } else {
227   - createWechatChannel(this.form).then(response => {
228   - if (response.code === 0) {
229   - this.msgSuccess("新增成功");
230   - this.$parent.refreshTable();
231   - this.close();
232   - }
  252 +
  253 + createChannel(data).then(response => {
  254 + if (response.code === 0) {
  255 + this.msgSuccess("新增成功");
  256 + this.$parent.refreshTable();
  257 + this.close();
  258 + }
233 259 });
234 260 }
235 261 });
... ... @@ -246,11 +272,19 @@ export default {
246 272 }
247 273 return isRightSize
248 274 },
249   - privateKeyUploadSuccess(response) {
250   - this.form.weChatConfig.privateKeyContent = response.data;
  275 + privateKeyUpload(event) {
  276 + const readFile = new FileReader()
  277 + readFile.onload = (e) => {
  278 + this.form.weChatConfig.privateKeyContent = e.target.result
  279 + }
  280 + readFile.readAsText(event.file);
251 281 },
252   - privateCertUploadSuccess(response) {
253   - this.form.weChatConfig.privateCertContent = response.data;
  282 + privateCertUpload(event) {
  283 + const readFile = new FileReader()
  284 + readFile.onload = (e) => {
  285 + this.form.weChatConfig.privateCertContent = e.target.result
  286 + }
  287 + readFile.readAsText(event.file);
254 288 }
255 289 }
256 290 }
... ...
yudao-admin-ui/src/views/pay/app/index.vue
... ... @@ -57,68 +57,104 @@
57 57 <el-table-column label="支付宝配置" align="center">
58 58 <el-table-column :label="payChannelEnum.ALIPAY_APP.name" align="center">
59 59 <template slot-scope="scope">
60   - <el-button type="success" icon="el-icon-check" circle
61   - v-if="scope.row.payChannel.alipayApp === sysCommonStatusEnum.ENABLE"></el-button>
62   - <el-button type="danger" icon="el-icon-close" circle
63   - v-if="scope.row.payChannel.alipayApp === sysCommonStatusEnum.DISABLE"></el-button>
  60 + <el-button
  61 + type="success" icon="el-icon-check" circle
  62 + @click="handleUpdateChannel(scope.row,payChannelEnum.ALIPAY_APP.code,payType.ALIPAY)"
  63 + v-if="scope.row.payChannel.alipayApp === sysCommonStatusEnum.ENABLE">
  64 + </el-button>
  65 + <el-button
  66 + type="danger" icon="el-icon-close" circle
  67 + @click="handleCreateChannel(scope.row,payChannelEnum.ALIPAY_APP.code,payType.ALIPAY)"
  68 + v-if="scope.row.payChannel.alipayApp === sysCommonStatusEnum.DISABLE">
  69 + </el-button>
64 70 </template>
65 71 </el-table-column>
66 72 <el-table-column :label="payChannelEnum.ALIPAY_PC.name" align="center">
67 73 <template slot-scope="scope">
68   - <el-button type="success" icon="el-icon-check" circle
69   - v-if="scope.row.payChannel.alipayPc === sysCommonStatusEnum.ENABLE"></el-button>
70   - <el-button type="danger" icon="el-icon-close" circle
71   - v-if="scope.row.payChannel.alipayPc === sysCommonStatusEnum.DISABLE"></el-button>
  74 + <el-button
  75 + type="success" icon="el-icon-check" circle
  76 + @click="handleUpdateChannel(scope.row,payChannelEnum.ALIPAY_PC.code,payType.ALIPAY)"
  77 + v-if="scope.row.payChannel.alipayPc === sysCommonStatusEnum.ENABLE">
  78 + </el-button>
  79 + <el-button
  80 + type="danger" icon="el-icon-close" circle
  81 + @click="handleCreateChannel(scope.row,payChannelEnum.ALIPAY_PC.code,payType.ALIPAY)"
  82 + v-if="scope.row.payChannel.alipayPc === sysCommonStatusEnum.DISABLE">
  83 + </el-button>
72 84 </template>
73 85 </el-table-column>
74 86 <el-table-column :label="payChannelEnum.ALIPAY_WAP.name" align="center">
75 87 <template slot-scope="scope">
76   - <el-button type="success" icon="el-icon-check" circle
77   - v-if="scope.row.payChannel.alipayWap === sysCommonStatusEnum.ENABLE"></el-button>
78   - <el-button type="danger" icon="el-icon-close" circle
79   - v-if="scope.row.payChannel.alipayWap === sysCommonStatusEnum.DISABLE"></el-button>
  88 + <el-button
  89 + type="success" icon="el-icon-check" circle
  90 + @click="handleUpdateChannel(scope.row,payChannelEnum.ALIPAY_WAP.code,payType.ALIPAY)"
  91 + v-if="scope.row.payChannel.alipayWap === sysCommonStatusEnum.ENABLE">
  92 + </el-button>
  93 + <el-button
  94 + type="danger" icon="el-icon-close" circle
  95 + @click="handleCreateChannel(scope.row,payChannelEnum.ALIPAY_WAP.code,payType.ALIPAY)"
  96 + v-if="scope.row.payChannel.alipayWap === sysCommonStatusEnum.DISABLE">
  97 + </el-button>
80 98 </template>
81 99 </el-table-column>
82 100 <el-table-column :label="payChannelEnum.ALIPAY_QR.name" align="center">
83 101 <template slot-scope="scope">
84   - <el-button type="success" icon="el-icon-check" circle
85   - v-if="scope.row.payChannel.alipayQr === sysCommonStatusEnum.ENABLE"></el-button>
86   - <el-button type="danger" icon="el-icon-close" circle
87   - v-if="scope.row.payChannel.alipayQr === sysCommonStatusEnum.DISABLE"></el-button>
  102 + <el-button
  103 + type="success" icon="el-icon-check" circle
  104 + @click="handleUpdateChannel(scope.row,payChannelEnum.ALIPAY_QR.code,payType.ALIPAY)"
  105 + v-if="scope.row.payChannel.alipayQr === sysCommonStatusEnum.ENABLE">
  106 +
  107 + </el-button>
  108 + <el-button
  109 + type="danger" icon="el-icon-close" circle
  110 + @click="handleCreateChannel(scope.row,payChannelEnum.ALIPAY_QR.code,payType.ALIPAY)"
  111 + v-if="scope.row.payChannel.alipayQr === sysCommonStatusEnum.DISABLE">
  112 +
  113 + </el-button>
88 114 </template>
89 115 </el-table-column>
90 116 </el-table-column>
91 117 <el-table-column label="微信配置" align="center">
92 118 <el-table-column :label="payChannelEnum.WX_LITE.name" align="center">
93 119 <template slot-scope="scope">
94   - <el-button type="success" icon="el-icon-check" circle
95   - @click="handleUpdateChannel(scope.row,payChannelEnum.WX_LITE.code)"
96   - v-if="scope.row.payChannel.wxLite === sysCommonStatusEnum.ENABLE"></el-button>
97   - <el-button type="danger" icon="el-icon-close" circle
98   - @click="handleCreateChannel(scope.row,payChannelEnum.WX_LITE.code)"
99   - v-if="scope.row.payChannel.wxLite === sysCommonStatusEnum.DISABLE"></el-button>
  120 + <el-button
  121 + type="success" icon="el-icon-check" circle
  122 + @click="handleUpdateChannel(scope.row,payChannelEnum.WX_LITE.code,payType.WECHAT)"
  123 + v-if="scope.row.payChannel.wxLite === sysCommonStatusEnum.ENABLE">
  124 + </el-button>
  125 + <el-button
  126 + type="danger" icon="el-icon-close" circle
  127 + @click="handleCreateChannel(scope.row,payChannelEnum.WX_LITE.code,payType.WECHAT)"
  128 + v-if="scope.row.payChannel.wxLite === sysCommonStatusEnum.DISABLE">
  129 + </el-button>
100 130 </template>
101 131 </el-table-column>
102 132 <el-table-column :label="payChannelEnum.WX_PUB.name" align="center">
103 133 <template slot-scope="scope">
104 134 <el-button
105 135 type="success" icon="el-icon-check" circle
106   - @click="handleUpdateChannel(scope.row,payChannelEnum.WX_PUB.code)"
  136 + @click="handleUpdateChannel(scope.row,payChannelEnum.WX_PUB.code,payType.WECHAT)"
107 137 v-if="scope.row.payChannel.wxPub === sysCommonStatusEnum.ENABLE">
108 138 </el-button>
109   - <el-button type="danger" icon="el-icon-close" circle
110   - @click="handleCreateChannel(scope.row,payChannelEnum.WX_PUB.code)"
111   - v-if="scope.row.payChannel.wxPub === sysCommonStatusEnum.DISABLE"></el-button>
  139 + <el-button
  140 + type="danger" icon="el-icon-close" circle
  141 + @click="handleCreateChannel(scope.row,payChannelEnum.WX_PUB.code,payType.WECHAT)"
  142 + v-if="scope.row.payChannel.wxPub === sysCommonStatusEnum.DISABLE">
  143 + </el-button>
112 144 </template>
113 145 </el-table-column>
114 146 <el-table-column :label="payChannelEnum.WX_APP.name" align="center">
115 147 <template slot-scope="scope">
116   - <el-button type="success" icon="el-icon-check" circle
117   - @click="handleUpdateChannel(scope.row,payChannelEnum.WX_APP.code)"
118   - v-if="scope.row.payChannel.wxApp === sysCommonStatusEnum.ENABLE"></el-button>
119   - <el-button type="danger" icon="el-icon-close" circle
120   - @click="handleCreateChannel(scope.row,payChannelEnum.WX_APP.code)"
121   - v-if="scope.row.payChannel.wxApp === sysCommonStatusEnum.DISABLE"></el-button>
  148 + <el-button
  149 + type="success" icon="el-icon-check" circle
  150 + @click="handleUpdateChannel(scope.row,payChannelEnum.WX_APP.code,payType.WECHAT)"
  151 + v-if="scope.row.payChannel.wxApp === sysCommonStatusEnum.ENABLE">
  152 + </el-button>
  153 + <el-button
  154 + type="danger" icon="el-icon-close" circle
  155 + @click="handleCreateChannel(scope.row,payChannelEnum.WX_APP.code,payType.WECHAT)"
  156 + v-if="scope.row.payChannel.wxApp === sysCommonStatusEnum.DISABLE">
  157 + </el-button>
122 158 </template>
123 159 </el-table-column>
124 160 </el-table-column>
... ... @@ -186,21 +222,24 @@
186 222 </div>
187 223 </el-dialog>
188 224  
189   - <wechat-js-api-form :transferParam="wechatChannelParam"></wechat-js-api-form>
  225 + <wechat-channel-form :transferParam="channelParam"></wechat-channel-form>
  226 + <ali-pay-channel-form :transferParam="channelParam"></ali-pay-channel-form>
190 227 </div>
191 228 </template>
192 229  
193 230 <script>
194 231 import {createApp, updateApp, changeAppStatus, deleteApp, getApp, getAppPage, exportAppExcel} from "@/api/pay/app";
195 232 import {DICT_TYPE, getDictDatas} from "@/utils/dict";
196   -import {PayChannelEnum, SysCommonStatusEnum} from "@/utils/constants";
  233 +import {PayType, PayChannelEnum, SysCommonStatusEnum} from "@/utils/constants";
197 234 import {getMerchantListByName} from "@/api/pay/merchant";
198   -import wechatJsApiForm from "@/views/pay/app/components/wechatJsApiForm";
  235 +import wechatChannelForm from "@/views/pay/app/components/wechatChannelForm";
  236 +import aliPayChannelForm from "@/views/pay/app/components/aliPayChannelForm";
199 237  
200 238 export default {
201 239 name: "App",
202 240 components: {
203   - "wechatJsApiForm": wechatJsApiForm
  241 + "wechatChannelForm": wechatChannelForm,
  242 + "aliPayChannelForm": aliPayChannelForm
204 243 },
205 244 data() {
206 245 return {
... ... @@ -243,16 +282,20 @@ export default {
243 282 sysCommonStatusEnum: SysCommonStatusEnum,
244 283 // 支付渠道枚举
245 284 payChannelEnum: PayChannelEnum,
  285 + // 支付类型
  286 + payType: PayType,
246 287 // 商户列表
247 288 merchantList: [],
248 289 // 是否显示支付窗口
249 290 payOpen: false,
250 291 // 微信组件传参参数
251   - wechatChannelParam: {
  292 + channelParam: {
252 293 // 是否修改
253   - "edit":false,
254   - // 是否显示
255   - "open":false,
  294 + "edit": false,
  295 + // 微信是否显示
  296 + "wechatOpen": false,
  297 + // 支付宝是否显示
  298 + "aliPayOpen": false,
256 299 // 应用ID
257 300 "appId": null,
258 301 // 渠道编码
... ... @@ -279,7 +322,6 @@ export default {
279 322 this.addBeginAndEndTime(params, this.dateRangeCreateTime, 'createTime');
280 323 // 执行查询
281 324 getAppPage(params).then(response => {
282   - console.log(response.data);
283 325 this.list = response.data.list;
284 326 this.total = response.data.total;
285 327 this.loading = false;
... ... @@ -355,9 +397,9 @@ export default {
355 397 // 修改的提交
356 398 if (this.form.id != null) {
357 399 updateApp(this.form).then(response => {
358   - this.msgSuccess("修改成功");
359   - this.open = false;
360   - this.getList();
  400 + this.msgSuccess("修改成功");
  401 + this.open = false;
  402 + this.getList();
361 403 });
362 404 return;
363 405 }
... ... @@ -407,33 +449,47 @@ export default {
407 449 */
408 450 handleGetMerchantListByName(name) {
409 451 getMerchantListByName(name).then(response => {
410   - console.log(response)
411 452 this.merchantList = response.data;
412 453 });
413 454 },
414 455 /**
415 456 * 修改支付渠道信息
416 457 */
417   - handleUpdateChannel(row, payCode) {
418   - this.wechatChannelParam.edit = true;
419   - this.wechatChannelParam.loading = true;
420   - this.wechatChannelParam.appId = row.id;
421   - this.wechatChannelParam.payCode = payCode;
422   - this.wechatChannelParam.payMerchant = row.payMerchant;
423   - this.wechatChannelParam.open = true;
  458 + handleUpdateChannel(row, payCode, type) {
  459 + this.settingChannelParam(row, payCode, type)
  460 + this.channelParam.edit = true;
  461 + this.channelParam.loading = true;
  462 +
424 463 },
425 464 /**
426 465 * 新增支付渠道信息
427 466 */
428   - handleCreateChannel(row, payCode) {
429   - this.wechatChannelParam.edit = false;
430   - this.wechatChannelParam.loading = false;
431   - this.wechatChannelParam.appId = row.id;
432   - this.wechatChannelParam.payCode = payCode;
433   - this.wechatChannelParam.payMerchant = row.payMerchant;
434   - this.wechatChannelParam.open = true;
  467 + handleCreateChannel(row, payCode, type) {
  468 + this.settingChannelParam(row, payCode, type)
  469 + this.channelParam.edit = false;
  470 + this.channelParam.loading = false;
  471 +
  472 +
  473 + },
  474 + /**
  475 + * 设置支付渠道信息
  476 + */
  477 + settingChannelParam(row, payCode, type) {
  478 + if (type === PayType.WECHAT) {
  479 + this.channelParam.wechatOpen = true;
  480 + this.channelParam.aliPayOpen = false;
  481 + }
  482 + if (type === PayType.ALIPAY) {
  483 + this.channelParam.aliPayOpen = true;
  484 + this.channelParam.wechatOpen = false;
  485 + }
  486 + this.channelParam.edit = false;
  487 + this.channelParam.loading = false;
  488 + this.channelParam.appId = row.id;
  489 + this.channelParam.payCode = payCode;
  490 + this.channelParam.payMerchant = row.payMerchant;
435 491 },
436   - refreshTable(){
  492 + refreshTable() {
437 493 this.getList();
438 494 }
439 495 }
... ...
yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/pay/enums/PayErrorCodeCoreConstants.java
... ... @@ -9,21 +9,35 @@ import cn.iocoder.yudao.framework.common.exception.ErrorCode;
9 9 */
10 10 public interface PayErrorCodeCoreConstants {
11 11  
12   - // ========== APP 模块 1-007-000-000 ==========
  12 + /**
  13 + * ========== APP 模块 1-007-000-000 ==========
  14 + */
13 15 ErrorCode PAY_APP_NOT_FOUND = new ErrorCode(1007000000, "App 不存在");
14 16 ErrorCode PAY_APP_IS_DISABLE = new ErrorCode(1007000002, "App 已经被禁用");
15 17  
16   - // ========== CHANNEL 模块 1-007-001-000 ==========
  18 + /**
  19 + * ========== CHANNEL 模块 1-007-001-000 ==========
  20 + */
17 21 ErrorCode PAY_CHANNEL_NOT_FOUND = new ErrorCode(1007001000, "支付渠道的配置不存在");
18 22 ErrorCode PAY_CHANNEL_IS_DISABLE = new ErrorCode(1007001001, "支付渠道已经禁用");
19 23 ErrorCode PAY_CHANNEL_CLIENT_NOT_FOUND = new ErrorCode(1007001002, "支付渠道的客户端不存在");
20   -
21   - // ========== ORDER 模块 1-007-002-000 ==========
  24 + ErrorCode CHANNEL_NOT_EXISTS = new ErrorCode(1007001003, "支付渠道不存在");
  25 + ErrorCode CHANNEL_KEY_READ_ERROR = new ErrorCode(1007001004, "支付渠道秘钥文件读取失败");
  26 + ErrorCode CHANNEL_EXIST_SAME_CHANNEL_ERROR = new ErrorCode(1007001005, "已存在相同的渠道");
  27 + ErrorCode CHANNEL_WECHAT_VERSION_2_MCH_KEY_IS_NULL = new ErrorCode(1007001006,"微信渠道v2版本中商户密钥不可为空");
  28 + ErrorCode CHANNEL_WECHAT_VERSION_3_PRIVATE_KEY_IS_NULL = new ErrorCode(1007001006,"微信渠道v3版本apiclient_key.pem不可为空");
  29 + ErrorCode CHANNEL_WECHAT_VERSION_3_CERT_KEY_IS_NULL = new ErrorCode(1007001006,"微信渠道v3版本中apiclient_cert.pem不可为空");
  30 + /**
  31 + * ========== ORDER 模块 1-007-002-000 ==========
  32 + */
22 33 ErrorCode PAY_ORDER_NOT_FOUND = new ErrorCode(1007002000, "支付订单不存在");
23 34 ErrorCode PAY_ORDER_STATUS_IS_NOT_WAITING = new ErrorCode(1007002001, "支付订单不处于待支付");
24 35 ErrorCode PAY_ORDER_STATUS_IS_NOT_SUCCESS = new ErrorCode(1007002002, "支付订单不处于已支付");
25 36 ErrorCode PAY_ORDER_ERROR_USER = new ErrorCode(1007002003, "支付订单用户不正确");
26   - // ========== ORDER 模块(拓展单) 1-007-003-000 ==========
  37 +
  38 + /**
  39 + * ========== ORDER 模块(拓展单) 1-007-003-000 ==========
  40 + */
27 41 ErrorCode PAY_ORDER_EXTENSION_NOT_FOUND = new ErrorCode(1007003000, "支付交易拓展单不存在");
28 42 ErrorCode PAY_ORDER_EXTENSION_STATUS_IS_NOT_WAITING = new ErrorCode(1007003001, "支付交易拓展单不处于待支付");
29 43 ErrorCode PAY_ORDER_EXTENSION_STATUS_IS_NOT_SUCCESS = new ErrorCode(1007003002, "支付订单不处于已支付");
... ... @@ -46,12 +60,4 @@ public interface PayErrorCodeCoreConstants {
46 60 ErrorCode APP_NOT_EXISTS = new ErrorCode(1007005000, "支付应用信息不存在");
47 61  
48 62  
49   - /**
50   - * ========== 支付渠道 1-007-001-000 ==========
51   - */
52   - ErrorCode CHANNEL_NOT_EXISTS = new ErrorCode(1007001003, "支付渠道不存在");
53   - ErrorCode CHANNEL_KEY_READ_ERROR = new ErrorCode(1007001004, "支付渠道秘钥文件读取失败");
54   - // TODO @aquan:下面这个错误码,缺了 CHANNEL 前缀。另外,错误码的分段,上面有啦,合并下进去哈
55   - ErrorCode EXIST_SAME_CHANNEL_ERROR = new ErrorCode(1007001005, "已存在相同的渠道");
56   -
57 63 }
... ...
yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/PayClientFactoryImpl.java
... ... @@ -65,6 +65,8 @@ public class PayClientFactoryImpl implements PayClientFactory {
65 65 case WX_APP: return (AbstractPayClient<Config>) new WXPubPayClient(channelId, (WXPayClientConfig) config);
66 66 case ALIPAY_WAP: return (AbstractPayClient<Config>) new AlipayWapPayClient(channelId, (AlipayPayClientConfig) config);
67 67 case ALIPAY_QR: return (AbstractPayClient<Config>) new AlipayQrPayClient(channelId, (AlipayPayClientConfig) config);
  68 + case ALIPAY_APP: return (AbstractPayClient<Config>) new AlipayQrPayClient(channelId, (AlipayPayClientConfig) config);
  69 + case ALIPAY_PC: return (AbstractPayClient<Config>) new AlipayQrPayClient(channelId, (AlipayPayClientConfig) config);
68 70 }
69 71 // 创建失败,错误日志 + 抛出异常
70 72 log.error("[createSmsClient][配置({}) 找不到合适的客户端实现]", config);
... ...
yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/alipay/AlipayPayClientConfig.java
... ... @@ -3,7 +3,11 @@ package cn.iocoder.yudao.framework.pay.core.client.impl.alipay;
3 3 import cn.iocoder.yudao.framework.pay.core.client.PayClientConfig;
4 4 import lombok.Data;
5 5  
  6 +import javax.validation.constraints.NotBlank;
  7 +import javax.validation.constraints.NotNull;
  8 +
6 9 // TODO 芋艿:参数校验
  10 +
7 11 /**
8 12 * 支付宝的 PayClientConfig 实现类
9 13 * 属性主要来自 {@link com.alipay.api.AlipayConfig} 的必要属性
... ... @@ -25,11 +29,11 @@ public class AlipayPayClientConfig implements PayClientConfig {
25 29 /**
26 30 * 公钥类型 - 公钥模式
27 31 */
28   - private static final Integer MODE_PUBLIC_KEY = 1;
  32 + public static final Integer MODE_PUBLIC_KEY = 1;
29 33 /**
30 34 * 公钥类型 - 证书模式
31 35 */
32   - private static final Integer MODE_CERTIFICATE = 2;
  36 + public static final Integer MODE_CERTIFICATE = 2;
33 37  
34 38 /**
35 39 * 签名算法类型 - RSA
... ... @@ -41,18 +45,21 @@ public class AlipayPayClientConfig implements PayClientConfig {
41 45 * 1. {@link #SERVER_URL_PROD}
42 46 * 2. {@link #SERVER_URL_SANDBOX}
43 47 */
  48 + @NotBlank(message = "网关地址不能为空", groups = {ModePublicKey.class, ModeCertificate.class})
44 49 private String serverUrl;
45 50  
46 51 /**
47 52 * 开放平台上创建的应用的 ID
48 53 */
  54 + @NotBlank(message = "开放平台上创建的应用的 ID不能为空", groups = {ModePublicKey.class, ModeCertificate.class})
49 55 private String appId;
50 56  
51 57 /**
52 58 * 签名算法类型,推荐:RSA2
53   - *
  59 + * <p>
54 60 * {@link #SIGN_TYPE_DEFAULT}
55 61 */
  62 + @NotBlank(message = "签名算法类型不能为空", groups = {ModePublicKey.class, ModeCertificate.class})
56 63 private String signType;
57 64  
58 65 /**
... ... @@ -60,30 +67,43 @@ public class AlipayPayClientConfig implements PayClientConfig {
60 67 * 1. {@link #MODE_PUBLIC_KEY} 情况,privateKey + alipayPublicKey
61 68 * 2. {@link #MODE_CERTIFICATE} 情况,appCertContent + alipayPublicCertContent + rootCertContent
62 69 */
  70 + @NotNull(message = "公钥类型不能为空", groups = {ModePublicKey.class, ModeCertificate.class})
63 71 private Integer mode;
64 72  
65 73 // ========== 公钥模式 ==========
66 74 /**
67 75 * 商户私钥
68 76 */
  77 + @NotBlank(message = "商户私钥不能为空", groups = {ModePublicKey.class})
69 78 private String privateKey;
  79 +
70 80 /**
71 81 * 支付宝公钥字符串
72 82 */
  83 + @NotBlank(message = "支付宝公钥字符串不能为空", groups = {ModePublicKey.class})
73 84 private String alipayPublicKey;
74 85  
75 86 // ========== 证书模式 ==========
76 87 /**
77 88 * 指定商户公钥应用证书内容字符串
78 89 */
  90 + @NotBlank(message = "指定商户公钥应用证书内容不能为空", groups = {ModeCertificate.class})
79 91 private String appCertContent;
80 92 /**
81 93 * 指定支付宝公钥证书内容字符串
82 94 */
  95 + @NotBlank(message = "指定支付宝公钥证书内容不能为空", groups = {ModeCertificate.class})
83 96 private String alipayPublicCertContent;
84 97 /**
85 98 * 指定根证书内容字符串
86 99 */
  100 + @NotBlank(message = "指定根证书内容字符串不能为空", groups = {ModeCertificate.class})
87 101 private String rootCertContent;
88 102  
  103 + public interface ModePublicKey {
  104 + }
  105 +
  106 + public interface ModeCertificate {
  107 + }
  108 +
89 109 }
... ...
yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/wx/WXPayClientConfig.java
... ... @@ -4,10 +4,12 @@ import cn.hutool.core.io.IoUtil;
4 4 import cn.iocoder.yudao.framework.pay.core.client.PayClientConfig;
5 5 import lombok.Data;
6 6  
  7 +import javax.validation.constraints.NotBlank;
7 8 import java.io.FileInputStream;
8 9 import java.io.FileNotFoundException;
9 10  
10 11 // TODO 芋艿:参数校验
  12 +
11 13 /**
12 14 * 微信支付的 PayClientConfig 实现类
13 15 * 属性主要来自 {@link com.github.binarywang.wxpay.config.WxPayConfig} 的必要属性
... ... @@ -20,13 +22,13 @@ public class WXPayClientConfig implements PayClientConfig {
20 22 // TODO 芋艿:V2 or V3 客户端
21 23 /**
22 24 * API 版本 - V2
23   - *
  25 + * <p>
24 26 * https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=4_1
25 27 */
26 28 public static final String API_VERSION_V2 = "v2";
27 29 /**
28 30 * API 版本 - V3
29   - *
  31 + * <p>
30 32 * https://pay.weixin.qq.com/wiki/doc/apiv3/wechatpay/wechatpay-1.shtml
31 33 */
32 34 public static final String API_VERSION_V3 = "v3";
... ... @@ -34,14 +36,17 @@ public class WXPayClientConfig implements PayClientConfig {
34 36 /**
35 37 * 公众号或者小程序的 appid
36 38 */
  39 + @NotBlank(message = "APPID 不能为空", groups = {V2.class, V3.class})
37 40 private String appId;
38 41 /**
39 42 * 商户号
40 43 */
  44 + @NotBlank(message = "商户号 不能为空", groups = {V2.class, V3.class})
41 45 private String mchId;
42 46 /**
43 47 * API 版本
44 48 */
  49 + @NotBlank(message = "API 版本 不能为空", groups = {V2.class, V3.class})
45 50 private String apiVersion;
46 51  
47 52 // ========== V2 版本的参数 ==========
... ... @@ -49,33 +54,37 @@ public class WXPayClientConfig implements PayClientConfig {
49 54 /**
50 55 * 商户密钥
51 56 */
  57 + @NotBlank(message = "商户密钥 不能为空", groups = {V2.class})
52 58 private String mchKey;
53   -// /**
54   -// * apiclient_cert.p12 证书文件的绝对路径或者以 classpath: 开头的类路径.
55   -// * 对应的字符串
56   -// *
57   -// * 注意,可通过 {@link #main(String[])} 读取
58   -// */
59   -// private String keyContent;
  59 + /**
  60 + * apiclient_cert.p12 证书文件的绝对路径或者以 classpath: 开头的类路径.
  61 + * 对应的字符串
  62 + *
  63 + * 注意,可通过 {@link #main(String[])} 读取
  64 + */
  65 + /// private String keyContent;
60 66  
61 67 // ========== V3 版本的参数 ==========
62 68 /**
63 69 * apiclient_key.pem 证书文件的绝对路径或者以 classpath: 开头的类路径.
64 70 * 对应的字符串
65   - *
  71 + * <p>
66 72 * 注意,可通过 {@link #main(String[])} 读取
67 73 */
  74 + @NotBlank(message = "apiclient_key 不能为空", groups = {V3.class})
68 75 private String privateKeyContent;
69 76 /**
70 77 * apiclient_cert.pem 证书文件的绝对路径或者以 classpath: 开头的类路径.
71 78 * 对应的字符串
72   - *
  79 + * <p>
73 80 * 注意,可通过 {@link #main(String[])} 读取
74 81 */
  82 + @NotBlank(message = "apiclient_cert 不能为空", groups = {V3.class})
75 83 private String privateCertContent;
76 84 /**
77 85 * apiV3 秘钥值
78 86 */
  87 + @NotBlank(message = "apiV3 秘钥值 不能为空", groups = {V3.class})
79 88 private String apiV3Key;
80 89  
81 90 public static void main(String[] args) throws FileNotFoundException {
... ... @@ -85,4 +94,17 @@ public class WXPayClientConfig implements PayClientConfig {
85 94 System.out.println(IoUtil.readUtf8(new FileInputStream(path)));
86 95 }
87 96  
  97 +
  98 + /**
  99 + * 分组校验 v2版本
  100 + */
  101 + public interface V2 {
  102 + }
  103 +
  104 + /**
  105 + * 分组校验 v3版本
  106 + */
  107 + public interface V3 {
  108 + }
  109 +
88 110 }
... ...
yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/enums/PayChannelEnum.java
... ... @@ -15,8 +15,6 @@ import lombok.Getter;
15 15 public enum PayChannelEnum {
16 16  
17 17 WX_PUB("wx_pub", "微信 JSAPI 支付"), // 公众号的网页
18   - // TODO @芋艿 这个地方你写的是 wx_lit 是不是少写了一个e? 还是我这里多加了一个e
19   - // TODO @aquan:这里就是 lite 哈,轻量
20 18 WX_LITE("wx_lite","微信小程序支付"),
21 19 WX_APP("wx_app", "微信 App 支付"),
22 20  
... ... @@ -36,8 +34,38 @@ public enum PayChannelEnum {
36 34 */
37 35 private String name;
38 36  
  37 + /**
  38 + * 微信支付
  39 + */
  40 + public static final String WECHAT = "WECHAT";
  41 +
  42 + /**
  43 + * 支付宝支付
  44 + */
  45 + public static final String ALIPAY = "ALIPAY";
  46 +
39 47 public static PayChannelEnum getByCode(String code) {
40 48 return ArrayUtil.firstMatch(o -> o.getCode().equals(code), values());
41 49 }
42 50  
  51 + /**
  52 + * 判断当前渠道是那种支付方式
  53 + * @param code
  54 + * @return
  55 + */
  56 + public static String verifyWechatOrAliPay(String code){
  57 + switch (PayChannelEnum.getByCode(code)){
  58 + case WX_PUB:
  59 + case WX_LITE:
  60 + case WX_APP:
  61 + return WECHAT;
  62 + case ALIPAY_PC:
  63 + case ALIPAY_WAP:
  64 + case ALIPAY_APP:
  65 + case ALIPAY_QR:
  66 + return ALIPAY;
  67 + }
  68 + return null;
  69 + }
  70 +
43 71 }
... ...