Commit 6069a387ea17434ec9bd246ae070bae5529311cc
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
yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/pay/controller/channel/vo/PayChannelUpdateReqVO.java
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<PayAppDO> { |
| 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<PayChannelDO> { |
| 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<PayChannelDO> { |
| 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<PayMerchantDO> { |
| 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
yudao-admin-server/src/test/resources/sql/create_tables.sql
| ... | ... | @@ -464,3 +464,37 @@ CREATE TABLE IF NOT EXISTS "pay_merchant" ( |
| 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
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 | } | ... | ... |