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

This commit is contained in:
aquan 2021-11-21 19:31:20 +08:00
parent b18cd457c8
commit 6069a387ea
37 changed files with 1623 additions and 1021 deletions

View File

@ -22,3 +22,19 @@ INSERT INTO `sys_dict_type` (`name`, `type`, `status`, `remark`, `creator`, `cre
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');
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');
-- 支付渠道支付宝算法类型
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');
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');
-- 支付渠道支付宝公钥类型
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');
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');
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');
-- 支付宝网关地址
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');
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');
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');

View File

@ -123,7 +123,7 @@ public class PayAppController {
// 得到所有的应用编号查出所有的通道
Collection<Long> payAppIds = CollectionUtils.convertList(pageResult.getList(), PayAppDO::getId);
List<PayChannelDO> channels = channelService.getSimpleChannels(payAppIds);
List<PayChannelDO> channels = channelService.getChannelListByAppIds(payAppIds);
// 得到所有的商户信息
Collection<Long> merchantIds = CollectionUtils.convertList(pageResult.getList(), PayAppDO::getMerchantId);

View File

@ -27,8 +27,8 @@ public class PayAppExportReqVO {
@ApiModelProperty(value = "退款结果的回调地址")
private String refundNotifyUrl;
@ApiModelProperty(value = "商户编号")
private Long merchantId;
@ApiModelProperty(value = "商户名称")
private String merchantName;
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
@ApiModelProperty(value = "开始创建时间")

View File

@ -1,40 +1,35 @@
package cn.iocoder.yudao.adminserver.modules.pay.controller.channel;
import cn.iocoder.yudao.coreservice.modules.pay.dal.dataobject.merchant.PayChannelDO;
import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
import cn.iocoder.yudao.framework.pay.core.client.impl.wx.WXPayClientConfig;
import cn.iocoder.yudao.framework.pay.core.client.impl.wx.WXPubPayClient;
import org.springframework.util.Assert;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import org.springframework.validation.annotation.Validated;
import org.springframework.security.access.prepost.PreAuthorize;
import io.swagger.annotations.*;
import javax.validation.*;
import javax.servlet.http.*;
import java.util.*;
import java.io.IOException;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils;
import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog;
import static cn.iocoder.yudao.framework.operatelog.core.enums.OperateTypeEnum.*;
import cn.iocoder.yudao.adminserver.modules.pay.controller.channel.vo.*;
import cn.iocoder.yudao.adminserver.modules.pay.convert.channel.PayChannelConvert;
import cn.iocoder.yudao.adminserver.modules.pay.service.channel.PayChannelService;
import org.springframework.web.multipart.MultipartFile;
import cn.iocoder.yudao.coreservice.modules.pay.dal.dataobject.merchant.PayChannelDO;
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils;
import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiOperation;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import javax.validation.Valid;
import java.io.IOException;
import java.util.Collection;
import java.util.List;
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
import static cn.iocoder.yudao.framework.operatelog.core.enums.OperateTypeEnum.EXPORT;
/**
* 支付渠道 controller 组件
* @author aquan
*/
@Api(tags = "支付渠道")
@RestController
@RequestMapping("/pay/channel")
@ -44,7 +39,7 @@ public class PayChannelController {
@Resource
private PayChannelService channelService;
// todo 芋艿 这几个生成的方法是没用到的 您看要不删除了把 -----start
@PostMapping("/create")
@ApiOperation("创建支付渠道 ")
@PreAuthorize("@ss.hasPermission('pay:channel:create')")
@ -108,28 +103,7 @@ public class PayChannelController {
ExcelUtils.write(response, "支付渠道.xls", "数据", PayChannelExcelVO.class, datas);
}
// todo 芋艿 这几个生成的方法是没用到的 您看要不删除了把 -----end
@PostMapping("/parsing-pem")
@ApiOperation("解析pem证书转换为字符串")
@PreAuthorize("@ss.hasPermission('pay:channel:parsing')")
@ApiImplicitParam(name = "file", value = "pem文件", required = true, dataTypeClass = MultipartFile.class)
public CommonResult<String> parsingPemFile(@RequestParam("file") MultipartFile file) {
return success(channelService.parsingPemFile(file));
}
@PostMapping("/create-wechat")
@ApiOperation("创建支付渠道 ")
@PreAuthorize("@ss.hasPermission('pay:channel:create')")
public CommonResult<Long> createWechatChannel(@Valid @RequestBody PayWechatChannelCreateReqVO reqVO) {
// 针对于 V2 或者 V3 版本的参数校验
this.paramAdvanceCheck(reqVO.getWeChatConfig().getApiVersion(),reqVO.getWeChatConfig().getMchKey(),
reqVO.getWeChatConfig().getPrivateKeyContent(),reqVO.getWeChatConfig().getPrivateCertContent());
return success(channelService.createWechatChannel(reqVO));
}
@GetMapping("/get-wechat")
@GetMapping("/get-channel")
@ApiOperation("根据条件查询微信支付渠道")
@ApiImplicitParams({
@ApiImplicitParam(name = "merchantId", value = "商户编号",
@ -140,51 +114,16 @@ public class PayChannelController {
required = true, example = "wx_pub", dataTypeClass = String.class)
})
@PreAuthorize("@ss.hasPermission('pay:channel:query')")
public CommonResult<PayWeChatChannelRespVO> getWeChatChannel(
public CommonResult<PayChannelRespVO> getChannel(
@RequestParam Long merchantId, @RequestParam Long appId, @RequestParam String code) {
// 獲取渠道
PayChannelDO channel = channelService.getChannelByConditions(merchantId, appId, code);
if (channel == null) {
return success(new PayWeChatChannelRespVO());
return success(new PayChannelRespVO());
}
// 拼凑数据
PayWeChatChannelRespVO respVo = PayChannelConvert.INSTANCE.convert2(channel);
WXPayClientConfig config = (WXPayClientConfig) channel.getConfig();
respVo.setWeChatConfig(PayChannelConvert.INSTANCE.configConvert(config));
PayChannelRespVO respVo = PayChannelConvert.INSTANCE.convert(channel);
return success(respVo);
}
@PutMapping("/update-wechat")
@ApiOperation("更新微信支付渠道 ")
@PreAuthorize("@ss.hasPermission('pay:channel:update')")
public CommonResult<Boolean> updateWechatChannel(@Valid @RequestBody PayWechatChannelUpdateReqVO updateReqVO) {
// 针对于 V2 或者 V3 版本的参数校验
this.paramAdvanceCheck(updateReqVO.getWeChatConfig().getApiVersion(),updateReqVO.getWeChatConfig().getMchKey(),
updateReqVO.getWeChatConfig().getPrivateKeyContent(),updateReqVO.getWeChatConfig().getPrivateCertContent());
channelService.updateWechatChannel(updateReqVO);
return success(true);
}
/**
* 预检测微信秘钥参数
* @param version 版本
* @param mchKey v2版本秘钥
* @param privateKeyContent v3版本apiclient_key
* @param privateCertContent v3版本中apiclient_cert
*/
private void paramAdvanceCheck(String version, String mchKey, String privateKeyContent, String privateCertContent) {
// 针对于 V2 或者 V3 版本的参数校验
if (version.equals(WXPayClientConfig.API_VERSION_V2)) {
Assert.notNull(mchKey, "v2版本中商户密钥不可为空");
}
if (version.equals(WXPayClientConfig.API_VERSION_V3)) {
Assert.notNull(privateKeyContent, "v3版本apiclient_key.pem不可为空");
Assert.notNull(privateCertContent, "v3版本中apiclient_cert.pem不可为空");
}
}
}

View File

@ -1,18 +1,23 @@
package cn.iocoder.yudao.adminserver.modules.pay.controller.channel.vo;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
import javax.validation.constraints.NotBlank;
@ApiModel("支付渠道 创建 Request VO")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class PayChannelCreateReqVO extends PayChannelBaseVO {
// TODO @aquan我在想要不这个创建和修改特殊一点前端传递 string 过来后端解析成对应的因为有 code所以我们都知道是哪个配置类
// 然后 PayChannelEnum 枚举每个渠道对应的配置类另外我们就不单独给配置类搞 vo 参数校验通过手动调用 Validator 去校验
// 通过这样的方式VO api 都收成一个 update一个 create
@ApiModelProperty(value = "通道配置的json字符串")
@NotBlank(message = "通道配置不能为空")
private String config;
}

View File

@ -16,4 +16,6 @@ public class PayChannelRespVO extends PayChannelBaseVO {
@ApiModelProperty(value = "创建时间", required = true)
private Date createTime;
@ApiModelProperty(value = "配置", required = true)
private String config;
}

View File

@ -15,4 +15,7 @@ public class PayChannelUpdateReqVO extends PayChannelBaseVO {
@NotNull(message = "商户编号不能为空")
private Long id;
@ApiModelProperty(value = "通道配置的json字符串")
@NotBlank(message = "通道配置不能为空")
private String config;
}

View File

@ -1,65 +0,0 @@
package cn.iocoder.yudao.adminserver.modules.pay.controller.channel.vo;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
import javax.validation.Valid;
import java.util.Date;
@ApiModel("支付微信渠道 Response VO")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class PayWeChatChannelRespVO extends PayChannelBaseVO {
@ApiModelProperty(value = "商户编号", required = true)
private Long id;
@ApiModelProperty(value = "创建时间", required = true)
private Date createTime;
/**
* 微信配置类
*/
@Valid
private WeChatConfig weChatConfig;
/**
* 微信配置类
*/
@Data
@ApiModel("微信配置类")
public static class WeChatConfig {
@ApiModelProperty(value = "公众号或者小程序的 appid", required = true, example = "wx041349c6f39b261b")
private String appId;
@ApiModelProperty(value = "商户号", required = true, example = "1545083881")
private String mchId;
@ApiModelProperty(value = "API 版本", required = true, example = "v2")
private String apiVersion;
// ========== V2 版本的参数 ==========
@ApiModelProperty(value = "商户密钥", required = true, example = "0alL64UDQdaCwiKZ73ib7ypaIjMns06p")
private String mchKey;
/// todo @aquan 暂不支持 .p12上传 后期优化
/// apiclient_cert.p12 证书文件的绝对路径或者以 classpath: 开头的类路径. 对应的字符串
/// private String keyContent;
// ========== V3 版本的参数 ==========
@ApiModelProperty(value = "apiclient_key.pem 证书对应的字符串", required = true, example = "-----BEGIN PRIVATE KEY-----")
private String privateKeyContent;
@ApiModelProperty(value = "apiclient_cert.pem 证书对应的字符串", required = true, example = "-----BEGIN CERTIFICATE-----")
private String privateCertContent;
}
}

View File

@ -1,68 +0,0 @@
package cn.iocoder.yudao.adminserver.modules.pay.controller.channel.vo;
import com.fasterxml.jackson.annotation.JsonClassDescription;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
import javax.validation.Valid;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
/**
* 支付渠道微信创建Request VO
* @author aquan
*/
@ApiModel("支付渠道微信创建Request VO")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class PayWechatChannelCreateReqVO extends PayChannelBaseVO {
/**
* 微信配置类
*/
@Valid
private WeChatConfig weChatConfig;
/**
* 微信配置类
*/
@Data
@ApiModel("微信配置类")
public static class WeChatConfig {
@NotBlank(message = "公众号或者小程序的 appid不能为空")
@ApiModelProperty(value = "公众号或者小程序的 appid", required = true, example = "wx041349c6f39b261b")
private String appId;
@NotBlank(message = "商户号不能为空")
@ApiModelProperty(value = "商户号", required = true, example = "1545083881")
private String mchId;
@NotNull(message = "API 版本不能为空")
@ApiModelProperty(value = "API 版本", required = true, example = "v2")
private String apiVersion;
// ========== V2 版本的参数 ==========
@ApiModelProperty(value = "商户密钥", required = true, example = "0alL64UDQdaCwiKZ73ib7ypaIjMns06p")
private String mchKey;
/// todo @aquan 暂不支持 .p12上传 后期优化
/// apiclient_cert.p12 证书文件的绝对路径或者以 classpath: 开头的类路径. 对应的字符串
/// private String keyContent;
// ========== V3 版本的参数 ==========
@ApiModelProperty(value = "apiclient_key.pem 证书对应的字符串", required = true, example = "-----BEGIN PRIVATE KEY-----")
private String privateKeyContent;
@ApiModelProperty(value = "apiclient_cert.pem 证书对应的字符串", required = true, example = "-----BEGIN CERTIFICATE-----")
private String privateCertContent;
}
}

View File

@ -1,68 +0,0 @@
package cn.iocoder.yudao.adminserver.modules.pay.controller.channel.vo;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
import javax.validation.Valid;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
@ApiModel("支付渠道 更新 Request VO")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class PayWechatChannelUpdateReqVO extends PayChannelBaseVO {
@ApiModelProperty(value = "商户编号", required = true)
@NotNull(message = "商户编号不能为空")
private Long id;
/**
* 微信配置类
*/
@Valid
private PayWechatChannelCreateReqVO.WeChatConfig weChatConfig;
/**
* 微信配置类
*/
@Data
@ApiModel("微信配置类")
public static class WeChatConfig {
@NotBlank(message = "公众号或者小程序的 appid不能为空")
@ApiModelProperty(value = "公众号或者小程序的 appid", required = true, example = "wx041349c6f39b261b")
private String appId;
@NotBlank(message = "商户号不能为空")
@ApiModelProperty(value = "商户号", required = true, example = "1545083881")
private String mchId;
@NotNull(message = "API 版本不能为空")
@ApiModelProperty(value = "API 版本", required = true, example = "v2")
private String apiVersion;
// ========== V2 版本的参数 ==========
@ApiModelProperty(value = "商户密钥", required = true, example = "0alL64UDQdaCwiKZ73ib7ypaIjMns06p")
private String mchKey;
/// todo @aquan 暂不支持 .p12上传 后期优化
/// apiclient_cert.p12 证书文件的绝对路径或者以 classpath: 开头的类路径. 对应的字符串
/// private String keyContent;
// ========== V3 版本的参数 ==========
@ApiModelProperty(value = "apiclient_key.pem 证书对应的字符串", required = true, example = "-----BEGIN PRIVATE KEY-----")
private String privateKeyContent;
@ApiModelProperty(value = "apiclient_cert.pem 证书对应的字符串", required = true, example = "-----BEGIN CERTIFICATE-----")
private String privateCertContent;
// TODO @aquan参数校验可以使用 @AssertTruev2 v3
}
}

View File

@ -23,15 +23,12 @@ public interface PayChannelConvert {
PayChannelConvert INSTANCE = Mappers.getMapper(PayChannelConvert.class);
@Mapping(target = "config",ignore = true)
PayChannelDO convert(PayWechatChannelCreateReqVO bean);
@Mapping(target = "config",ignore = true)
PayChannelDO convert(PayWechatChannelUpdateReqVO bean);
PayChannelDO convert(PayChannelCreateReqVO bean);
@Mapping(target = "config",ignore = true)
PayChannelDO convert(PayChannelUpdateReqVO bean);
@Mapping(target = "config",expression = "java(cn.iocoder.yudao.framework.common.util.json.JsonUtils.toJsonString(bean.getConfig()))")
PayChannelRespVO convert(PayChannelDO bean);
List<PayChannelRespVO> convertList(List<PayChannelDO> list);
@ -39,13 +36,7 @@ public interface PayChannelConvert {
PageResult<PayChannelRespVO> convertPage(PageResult<PayChannelDO> page);
List<PayChannelExcelVO> convertList02(List<PayChannelDO> list);
WXPayClientConfig configConvert(PayWechatChannelCreateReqVO.WeChatConfig bean);
WXPayClientConfig configConvert(PayWechatChannelUpdateReqVO.WeChatConfig bean);
@Mapping(target = "weChatConfig",ignore = true)
PayWeChatChannelRespVO convert2(PayChannelDO bean);
PayWeChatChannelRespVO.WeChatConfig configConvert(WXPayClientConfig bean);
}

View File

@ -31,14 +31,14 @@ public interface PayAppMapper extends BaseMapperX<PayAppDO> {
.orderByDesc("id"));
}
default List<PayAppDO> selectList(PayAppExportReqVO reqVO) {
default List<PayAppDO> selectList(PayAppExportReqVO reqVO, Collection<Long> merchantIds) {
return selectList(new QueryWrapperX<PayAppDO>()
.likeIfPresent("name", reqVO.getName())
.eqIfPresent("status", reqVO.getStatus())
.eqIfPresent("remark", reqVO.getRemark())
.eqIfPresent("pay_notify_url", reqVO.getPayNotifyUrl())
.eqIfPresent("refund_notify_url", reqVO.getRefundNotifyUrl())
.eqIfPresent("merchant_id", reqVO.getMerchantId())
.inIfPresent("merchant_id", merchantIds)
.betweenIfPresent("create_time", reqVO.getBeginCreateTime(), reqVO.getEndCreateTime())
.orderByDesc("id"));
}

View File

@ -6,6 +6,7 @@ import cn.iocoder.yudao.coreservice.modules.pay.dal.dataobject.merchant.PayChann
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.mybatis.core.query.QueryWrapperX;
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import org.apache.ibatis.annotations.Mapper;
import cn.iocoder.yudao.adminserver.modules.pay.controller.channel.vo.*;
@ -26,7 +27,7 @@ public interface PayChannelMapper extends BaseMapperX<PayChannelDO> {
.eqIfPresent("fee_rate", reqVO.getFeeRate())
.eqIfPresent("merchant_id", reqVO.getMerchantId())
.eqIfPresent("app_id", reqVO.getAppId())
.eqIfPresent("config", reqVO.getConfig())
// .eqIfPresent("config", reqVO.getConfig())
.betweenIfPresent("create_time", reqVO.getBeginCreateTime(), reqVO.getEndCreateTime())
.orderByDesc("id") );
}
@ -39,9 +40,52 @@ public interface PayChannelMapper extends BaseMapperX<PayChannelDO> {
.eqIfPresent("fee_rate", reqVO.getFeeRate())
.eqIfPresent("merchant_id", reqVO.getMerchantId())
.eqIfPresent("app_id", reqVO.getAppId())
.eqIfPresent("config", reqVO.getConfig())
// .eqIfPresent("config", reqVO.getConfig())
.betweenIfPresent("create_time", reqVO.getBeginCreateTime(), reqVO.getEndCreateTime())
.orderByDesc("id") );
}
/**
* 根据条件获取通道数量
*
* @param merchantId 商户编号
* @param appid 应用编号
* @param code 通道编码
* @return 数量
*/
default Integer getChannelCountByConditions(Long merchantId, Long appid, String code) {
return this.selectCount(new QueryWrapper<PayChannelDO>().lambda()
.eq(PayChannelDO::getMerchantId, merchantId)
.eq(PayChannelDO::getAppId, appid)
.eq(PayChannelDO::getCode, code)
);
}
/**
* 根据条件获取通道
*
* @param merchantId 商户编号
* @param appid 应用编号
* @param code 通道编码
* @return 数量
*/
default PayChannelDO getChannelByConditions(Long merchantId, Long appid, String code) {
return this.selectOne((new QueryWrapper<PayChannelDO>().lambda()
.eq(PayChannelDO::getMerchantId, merchantId)
.eq(PayChannelDO::getAppId, appid)
.eq(PayChannelDO::getCode, code)
));
}
/**
* 根据支付应用ID集合获得支付渠道列表
*
* @param appIds 应用编号集合
* @return 支付渠道列表
*/
default List<PayChannelDO> getChannelListByAppIds(Collection<Long> appIds){
return this.selectList(new QueryWrapper<PayChannelDO>().lambda()
.in(PayChannelDO::getAppId, appIds));
}
}

View File

@ -6,6 +6,7 @@ import cn.iocoder.yudao.coreservice.modules.pay.dal.dataobject.merchant.PayMerch
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.mybatis.core.query.QueryWrapperX;
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import org.apache.ibatis.annotations.Mapper;
import cn.iocoder.yudao.adminserver.modules.pay.controller.merchant.vo.*;
@ -39,4 +40,14 @@ public interface PayMerchantMapper extends BaseMapperX<PayMerchantDO> {
.orderByDesc("id"));
}
/**
* 根据商户名称模糊查询商户集合
*
* @param merchantName 商户名称
* @return 商户集合
*/
default List<PayMerchantDO> getMerchantListByName(String merchantName) {
return this.selectList(new QueryWrapper<PayMerchantDO>()
.lambda().likeRight(PayMerchantDO::getName, merchantName));
}
}

View File

@ -1,5 +1,6 @@
package cn.iocoder.yudao.adminserver.modules.pay.service.app.impl;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.StrUtil;
import cn.iocoder.yudao.adminserver.modules.pay.controller.app.vo.PayAppCreateReqVO;
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
import cn.iocoder.yudao.adminserver.modules.pay.controller.app.vo.PayAppUpdateReqVO;
import cn.iocoder.yudao.adminserver.modules.pay.convert.app.PayAppConvert;
import cn.iocoder.yudao.adminserver.modules.pay.dal.mysql.app.PayAppMapper;
import cn.iocoder.yudao.adminserver.modules.pay.dal.mysql.merchant.PayMerchantMapper;
import cn.iocoder.yudao.adminserver.modules.pay.service.app.PayAppService;
import cn.iocoder.yudao.adminserver.modules.pay.service.merchant.PayMerchantService;
import cn.iocoder.yudao.coreservice.modules.pay.dal.dataobject.merchant.PayAppDO;
@ -18,10 +20,7 @@ import org.springframework.stereotype.Service;
import org.springframework.validation.annotation.Validated;
import javax.annotation.Resource;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import java.util.*;
import static cn.iocoder.yudao.coreservice.modules.pay.enums.PayErrorCodeCoreConstants.APP_NOT_EXISTS;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
@ -43,7 +42,7 @@ public class PayAppServiceImpl implements PayAppService {
* 商户 service 组件
*/
@Resource
private PayMerchantService merchantService;
private PayMerchantMapper merchantMapper;
@Override
public Long createApp(PayAppCreateReqVO createReqVO) {
@ -89,13 +88,20 @@ public class PayAppServiceImpl implements PayAppService {
@Override
public PageResult<PayAppDO> getAppPage(PayAppPageReqVO pageReqVO) {
// TODO @aquan会有一个场景merchantName 匹配不到商户编号的时候应该返回没数据的
return appMapper.selectPage(pageReqVO, this.getMerchantCondition(pageReqVO.getMerchantName()));
Set<Long> merchantIdList = this.getMerchantCondition(pageReqVO.getMerchantName());
if (StrUtil.isNotBlank(pageReqVO.getMerchantName()) && CollectionUtil.isEmpty(merchantIdList)) {
return new PageResult<>();
}
return appMapper.selectPage(pageReqVO, merchantIdList);
}
@Override
public List<PayAppDO> getAppList(PayAppExportReqVO exportReqVO) {
return appMapper.selectList(exportReqVO);
Set<Long> merchantIdList = this.getMerchantCondition(exportReqVO.getMerchantName());
if (StrUtil.isNotBlank(exportReqVO.getMerchantName()) && CollectionUtil.isEmpty(merchantIdList)) {
return new ArrayList<>();
}
return appMapper.selectList(exportReqVO, merchantIdList);
}
/**
@ -108,7 +114,7 @@ public class PayAppServiceImpl implements PayAppService {
if (StrUtil.isBlank(merchantName)) {
return Collections.emptySet();
}
return convertSet(merchantService.getMerchantListByName(merchantName), PayMerchantDO::getId);
return convertSet(merchantMapper.getMerchantListByName(merchantName), PayMerchantDO::getId);
}
/**
@ -131,6 +137,7 @@ public class PayAppServiceImpl implements PayAppService {
/**
* 检查商户是否存在
*
* @param id 商户编号
*/
@VisibleForTesting

View File

@ -12,7 +12,7 @@ import java.util.List;
/**
* 支付渠道 Service 接口
*
* @author 芋艿 // TODO @aquan作者不要我
* @author aquan
*/
public interface PayChannelService {
@ -76,31 +76,12 @@ public interface PayChannelService {
List<PayChannelDO> getChannelList(PayChannelExportReqVO exportReqVO);
/**
* 根据支付应用ID集合获取所有的支付渠道
* 根据支付应用ID集合获得支付渠道列表
*
* @param payIds 支付应用编号集合
* @return 支付渠道
* @param appIds 应用编号集合
* @return 支付渠道列表
*/
// TODO @aquan暂时不用提供这种哈之前提供的原因是数据字典比较特殊
List<PayChannelDO> getSimpleChannels(Collection<Long> payIds);
/**
* 解析pem文件获取公钥私钥字符串
*
* @param file pem公私钥文件
* @return 解析后的字符串
*/
// TODO @aquan可以前端读取么
String parsingPemFile(MultipartFile file);
/**
* 创建微信的渠道配置
*
* @param reqVO 创建信息
* @return 创建结果
*/
// TODO @aquanpojo 如果要做参数校验需要添加 @Valid
Long createWechatChannel(PayWechatChannelCreateReqVO reqVO);
List<PayChannelDO> getChannelListByAppIds(Collection<Long> appIds);
/**
* 根据条件获取通道数量
@ -122,10 +103,5 @@ public interface PayChannelService {
*/
PayChannelDO getChannelByConditions(Long merchantId, Long appid, String code);
/**
* 更新微信支付渠道
*
* @param updateReqVO 更新信息
*/
void updateWechatChannel(PayWechatChannelUpdateReqVO updateReqVO);
}

View File

@ -1,23 +1,35 @@
package cn.iocoder.yudao.adminserver.modules.pay.service.channel.impl;
import cn.hutool.core.io.IoUtil;
import cn.hutool.core.util.StrUtil;
import cn.iocoder.yudao.adminserver.modules.pay.controller.channel.vo.*;
import cn.iocoder.yudao.adminserver.modules.pay.convert.channel.PayChannelConvert;
import cn.iocoder.yudao.adminserver.modules.pay.dal.mysql.channel.PayChannelMapper;
import cn.iocoder.yudao.adminserver.modules.pay.service.channel.PayChannelService;
import cn.iocoder.yudao.coreservice.modules.pay.dal.dataobject.merchant.PayChannelDO;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
import cn.iocoder.yudao.framework.pay.core.client.impl.alipay.AlipayPayClientConfig;
import cn.iocoder.yudao.framework.pay.core.client.impl.wx.WXPayClientConfig;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import cn.iocoder.yudao.framework.pay.core.enums.PayChannelEnum;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.util.Assert;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.multipart.MultipartFile;
import javax.annotation.Resource;
import javax.validation.ConstraintViolation;
import javax.validation.Validation;
import javax.validation.Validator;
import javax.validation.ValidatorFactory;
import java.io.IOException;
import java.util.Collection;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
import static cn.iocoder.yudao.coreservice.modules.pay.enums.PayErrorCodeCoreConstants.*;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
@ -25,7 +37,7 @@ import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionU
/**
* 支付渠道 Service 实现类
*
* @author 芋艿 // TODO aquan作者写自己哈
* @author aquan
*/
@Service
@Slf4j
@ -36,11 +48,18 @@ public class PayChannelServiceImpl implements PayChannelService {
private PayChannelMapper channelMapper;
@Override
public Long createChannel(PayChannelCreateReqVO createReqVO) {
// 插入
PayChannelDO channel = PayChannelConvert.INSTANCE.convert(createReqVO);
public Long createChannel(PayChannelCreateReqVO reqVO) {
// 判断是否有重复的有责无法新增
Integer channelCount = this.getChannelCountByConditions(reqVO.getMerchantId(), reqVO.getAppId(), reqVO.getCode());
if (channelCount > 0) {
throw exception(CHANNEL_EXIST_SAME_CHANNEL_ERROR);
}
PayChannelDO channel = PayChannelConvert.INSTANCE.convert(reqVO);
settingConfigAndCheckParam(channel, reqVO.getConfig());
channelMapper.insert(channel);
// 返回
return channel.getId();
}
@ -49,8 +68,9 @@ public class PayChannelServiceImpl implements PayChannelService {
// 校验存在
this.validateChannelExists(updateReqVO.getId());
// 更新
PayChannelDO updateObj = PayChannelConvert.INSTANCE.convert(updateReqVO);
channelMapper.updateById(updateObj);
PayChannelDO channel = PayChannelConvert.INSTANCE.convert(updateReqVO);
settingConfigAndCheckParam(channel, updateReqVO.getConfig());
channelMapper.updateById(channel);
}
@Override
@ -88,53 +108,16 @@ public class PayChannelServiceImpl implements PayChannelService {
}
/**
* 根据支付应用ID集合获取所有的支付渠道
* 根据支付应用ID集合获得支付渠道列表
*
* @param payIds 支付应用编号集合
* @return 支付渠道
* @param appIds 应用编号集合
* @return 支付渠道列表
*/
@Override
public List<PayChannelDO> getSimpleChannels(Collection<Long> payIds) {
return channelMapper.selectList(new QueryWrapper<PayChannelDO>().lambda().in(PayChannelDO::getAppId, payIds));
public List<PayChannelDO> getChannelListByAppIds(Collection<Long> appIds) {
return channelMapper.getChannelListByAppIds(appIds);
}
/**
* 解析pem文件获取公钥私钥字符串
*
* @param file pem公私钥文件
* @return 解析后的字符串
*/
@Override
public String parsingPemFile(MultipartFile file) {
try {
return IoUtil.readUtf8(file.getInputStream());
} catch (IOException e) {
log.error("[parsingPemToString]读取pem[{}]文件错误", file.getOriginalFilename());
throw exception(CHANNEL_KEY_READ_ERROR);
}
}
/**
* 创建微信的渠道配置
*
* @param reqVO 创建信息
* @return 创建结果
*/
@Override
public Long createWechatChannel(PayWechatChannelCreateReqVO reqVO) {
// 判断是否有重复的有责无法新增
Integer channelCount = this.getChannelCountByConditions(reqVO.getMerchantId(), reqVO.getAppId(), reqVO.getCode());
if (channelCount > 0) {
throw exception(EXIST_SAME_CHANNEL_ERROR);
}
PayChannelDO channel = PayChannelConvert.INSTANCE.convert(reqVO);
WXPayClientConfig config = PayChannelConvert.INSTANCE.configConvert(reqVO.getWeChatConfig());
channel.setConfig(config);
channelMapper.insert(channel);
return channel.getId();
}
/**
* 根据条件获取通道数量
@ -146,14 +129,9 @@ public class PayChannelServiceImpl implements PayChannelService {
*/
@Override
public Integer getChannelCountByConditions(Long merchantId, Long appid, String code) {
return this.channelMapper.selectCount(new QueryWrapper<PayChannelDO>().lambda()
.eq(PayChannelDO::getMerchantId, merchantId)
.eq(PayChannelDO::getAppId, appid)
.eq(PayChannelDO::getCode, code)
);
return this.channelMapper.getChannelCountByConditions(merchantId, appid, code);
}
// TODO @aquanservice 不出现 mybatis plus
/**
* 根据条件获取通道
*
@ -164,25 +142,76 @@ public class PayChannelServiceImpl implements PayChannelService {
*/
@Override
public PayChannelDO getChannelByConditions(Long merchantId, Long appid, String code) {
return this.channelMapper.selectOne((new QueryWrapper<PayChannelDO>().lambda()
.eq(PayChannelDO::getMerchantId, merchantId)
.eq(PayChannelDO::getAppId, appid)
.eq(PayChannelDO::getCode, code)
));
return this.channelMapper.getChannelByConditions(merchantId, appid, code);
}
/**
* 更新微信支付渠道
* 检测微信秘钥参数
*
* @param updateReqVO 更新信息
* @param config 信秘钥参数
*/
@Override
public void updateWechatChannel(PayWechatChannelUpdateReqVO updateReqVO) {
// 校验存在
this.validateChannelExists(updateReqVO.getId());
PayChannelDO channel = PayChannelConvert.INSTANCE.convert(updateReqVO);
WXPayClientConfig config = PayChannelConvert.INSTANCE.configConvert(updateReqVO.getWeChatConfig());
channel.setConfig(config);
this.channelMapper.updateById(channel);
private void wechatParamCheck(WXPayClientConfig config) {
// 针对于 V2 或者 V3 版本的参数校验
if (WXPayClientConfig.API_VERSION_V2.equals(config.getApiVersion())) {
Assert.notNull(config.getMchKey(), CHANNEL_WECHAT_VERSION_2_MCH_KEY_IS_NULL.getMsg());
}
if (WXPayClientConfig.API_VERSION_V3.equals(config.getApiVersion())) {
Assert.notNull(config.getPrivateKeyContent(), CHANNEL_WECHAT_VERSION_3_PRIVATE_KEY_IS_NULL.getMsg());
Assert.notNull(config.getPrivateCertContent(), CHANNEL_WECHAT_VERSION_3_CERT_KEY_IS_NULL.getMsg());
}
}
/**
* 设置渠道配置以及参数校验
*
* @param channel 渠道
* @param configStr 配置
*/
private void settingConfigAndCheckParam(PayChannelDO channel, String configStr) {
// 得到这个渠道是微信的还是支付宝的
String channelType = PayChannelEnum.verifyWechatOrAliPay(channel.getCode());
Assert.notNull(channelType, CHANNEL_NOT_EXISTS.getMsg());
// 进行验证
ValidatorFactory validatorFactory = Validation.buildDefaultValidatorFactory();
Validator validator = validatorFactory.getValidator();
// 微信的验证
if (PayChannelEnum.WECHAT.equals(channelType)) {
WXPayClientConfig config = JSON.parseObject(configStr, WXPayClientConfig.class);
// 判断是V2 版本还是 V3 版本
Class clazz = config.getApiVersion().equals(WXPayClientConfig.API_VERSION_V2)
? WXPayClientConfig.V2.class : WXPayClientConfig.V3.class;
// 手动调用validate进行验证
Set<ConstraintViolation<WXPayClientConfig>> validate = validator.validate(config,clazz);
// 断言没有异常
Assert.isTrue(validate.isEmpty(), validate.stream().map(ConstraintViolation::getMessage)
.collect(Collectors.joining(",")));
channel.setConfig(config);
}
// 支付宝验证
if (PayChannelEnum.ALIPAY.equals(channelType)) {
AlipayPayClientConfig config = JSON.parseObject(configStr, AlipayPayClientConfig.class);
// 判断是V2 版本还是 V3 版本
Class clazz = config.getMode().equals(AlipayPayClientConfig.MODE_PUBLIC_KEY)
? AlipayPayClientConfig.ModePublicKey.class : AlipayPayClientConfig.ModeCertificate.class;
// 手动调用validate进行验证
Set<ConstraintViolation<AlipayPayClientConfig>> validate = validator.validate(config,clazz);
// 断言没有异常
Assert.isTrue(validate.isEmpty(), validate.stream().map(ConstraintViolation::getMessage)
.collect(Collectors.joining(",")));
channel.setConfig(config);
}
}
}

View File

@ -100,15 +100,6 @@ public interface PayMerchantService {
*/
List<PayMerchantDO> getMerchantListByNameLimit(String merchantName);
/**
* 获得指定编号的商户列表
*
* @param merchantIds 商户编号数组
* @return 商户列表
*/
// TODO @aquan getMerchantList 重复了
List<PayMerchantDO> getSimpleMerchants(Collection<Long> merchantIds);
/**
* 获得指定编号的商户 Map
*
@ -116,11 +107,7 @@ public interface PayMerchantService {
* @return 商户 Map
*/
default Map<Long, PayMerchantDO> getMerchantMap(Collection<Long> merchantIds) {
// TODO @aquan可以不用判空交给 getMerchantList 解决
if (CollUtil.isEmpty(merchantIds)) {
return Collections.emptyMap();
}
List<PayMerchantDO> list = getSimpleMerchants(merchantIds);
List<PayMerchantDO> list = this.getMerchantList(merchantIds);
return CollectionUtils.convertMap(list, PayMerchantDO::getId);
}

View File

@ -113,9 +113,7 @@ public class PayMerchantServiceImpl implements PayMerchantService {
*/
@Override
public List<PayMerchantDO> getMerchantListByName(String merchantName) {
// TODO @aquanService 不要出现 mybatis plus 的代码要放到 mapper 里提供技术与业务分离原则上
return this.merchantMapper.selectList(new QueryWrapper<PayMerchantDO>()
.lambda().likeRight(PayMerchantDO::getName, merchantName));
return this.merchantMapper.getMerchantListByName(merchantName);
}
/**
@ -150,16 +148,6 @@ public class PayMerchantServiceImpl implements PayMerchantService {
}
}
/**
* 获得指定编号的商户列表
*
* @param merchantIds 商户编号数组
* @return 商户列表
*/
@Override
public List<PayMerchantDO> getSimpleMerchants(Collection<Long> merchantIds) {
return merchantMapper.selectBatchIds(merchantIds);
}
// TODO @芋艿后续增加下合适的算法
/**

View File

@ -1,193 +0,0 @@
package cn.iocoder.yudao.adminserver.modules.pay.app.service;
import cn.iocoder.yudao.adminserver.BaseDbUnitTest;
import cn.iocoder.yudao.adminserver.modules.pay.controller.app.vo.PayAppCreateReqVO;
import cn.iocoder.yudao.adminserver.modules.pay.controller.app.vo.PayAppExportReqVO;
import cn.iocoder.yudao.adminserver.modules.pay.controller.app.vo.PayAppPageReqVO;
import cn.iocoder.yudao.adminserver.modules.pay.controller.app.vo.PayAppUpdateReqVO;
import cn.iocoder.yudao.adminserver.modules.pay.dal.mysql.app.PayAppMapper;
import cn.iocoder.yudao.adminserver.modules.pay.service.app.impl.PayAppServiceImpl;
import cn.iocoder.yudao.coreservice.modules.pay.dal.dataobject.merchant.PayAppDO;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.object.ObjectUtils;
import org.junit.jupiter.api.Test;
import org.springframework.context.annotation.Import;
import javax.annotation.Resource;
import java.util.List;
import static cn.iocoder.yudao.coreservice.modules.pay.enums.PayErrorCodeCoreConstants.APP_NOT_EXISTS;
import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals;
import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertServiceException;
import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomLongId;
import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomPojo;
import static org.junit.jupiter.api.Assertions.*;
/**
* {@link PayAppServiceImpl} 的单元测试类
*
* @author 芋艿
*/
@Import(PayAppServiceImpl.class)
public class PayAppServiceTest extends BaseDbUnitTest {
@Resource
private PayAppServiceImpl appService;
@Resource
private PayAppMapper appMapper;
@Test
public void testCreateApp_success() {
// 准备参数
PayAppCreateReqVO reqVO = randomPojo(PayAppCreateReqVO.class);
// 调用
Long appId = appService.createApp(reqVO);
// 断言
assertNotNull(appId);
// 校验记录的属性是否正确
PayAppDO app = appMapper.selectById(appId);
assertPojoEquals(reqVO, app);
}
@Test
public void testUpdateApp_success() {
// mock 数据
PayAppDO dbApp = randomPojo(PayAppDO.class);
appMapper.insert(dbApp);// @Sql: 先插入出一条存在的数据
// 准备参数
PayAppUpdateReqVO reqVO = randomPojo(PayAppUpdateReqVO.class, o -> {
o.setId(dbApp.getId()); // 设置更新的 ID
});
// 调用
appService.updateApp(reqVO);
// 校验是否更新正确
PayAppDO app = appMapper.selectById(reqVO.getId()); // 获取最新的
assertPojoEquals(reqVO, app);
}
@Test
public void testUpdateApp_notExists() {
// 准备参数
PayAppUpdateReqVO reqVO = randomPojo(PayAppUpdateReqVO.class);
// 调用, 并断言异常
assertServiceException(() -> appService.updateApp(reqVO), APP_NOT_EXISTS);
}
@Test
public void testDeleteApp_success() {
// mock 数据
PayAppDO dbApp = randomPojo(PayAppDO.class);
appMapper.insert(dbApp);// @Sql: 先插入出一条存在的数据
// 准备参数
Long id = dbApp.getId();
// 调用
appService.deleteApp(id);
// 校验数据不存在了
assertNull(appMapper.selectById(id));
}
@Test
public void testDeleteApp_notExists() {
// 准备参数
Long id = randomLongId();
// 调用, 并断言异常
assertServiceException(() -> appService.deleteApp(id), APP_NOT_EXISTS);
}
@Test // TODO 请修改 null 为需要的值
public void testGetAppPage() {
// mock 数据
PayAppDO dbApp = randomPojo(PayAppDO.class, o -> { // 等会查询到
o.setName(null);
o.setStatus(null);
o.setRemark(null);
o.setPayNotifyUrl(null);
o.setRefundNotifyUrl(null);
o.setMerchantId(null);
o.setCreateTime(null);
});
appMapper.insert(dbApp);
// 测试 name 不匹配
appMapper.insert(ObjectUtils.clone(dbApp, o -> o.setName(null)));
// 测试 status 不匹配
appMapper.insert(ObjectUtils.clone(dbApp, o -> o.setStatus(null)));
// 测试 remark 不匹配
appMapper.insert(ObjectUtils.clone(dbApp, o -> o.setRemark(null)));
// 测试 payNotifyUrl 不匹配
appMapper.insert(ObjectUtils.clone(dbApp, o -> o.setPayNotifyUrl(null)));
// 测试 refundNotifyUrl 不匹配
appMapper.insert(ObjectUtils.clone(dbApp, o -> o.setRefundNotifyUrl(null)));
// 测试 merchantId 不匹配
appMapper.insert(ObjectUtils.clone(dbApp, o -> o.setMerchantId(null)));
// 测试 createTime 不匹配
appMapper.insert(ObjectUtils.clone(dbApp, o -> o.setCreateTime(null)));
// 准备参数
PayAppPageReqVO reqVO = new PayAppPageReqVO();
reqVO.setName(null);
reqVO.setStatus(null);
reqVO.setRemark(null);
reqVO.setPayNotifyUrl(null);
reqVO.setRefundNotifyUrl(null);
reqVO.setBeginCreateTime(null);
reqVO.setEndCreateTime(null);
// 调用
PageResult<PayAppDO> pageResult = appService.getAppPage(reqVO);
// 断言
assertEquals(1, pageResult.getTotal());
assertEquals(1, pageResult.getList().size());
assertPojoEquals(dbApp, pageResult.getList().get(0));
}
@Test // TODO aquan请修改 null 为需要的值
public void testGetAppList() {
// mock 数据
PayAppDO dbApp = randomPojo(PayAppDO.class, o -> { // 等会查询到
o.setName(null);
o.setStatus(null);
o.setRemark(null);
o.setPayNotifyUrl(null);
o.setRefundNotifyUrl(null);
o.setMerchantId(null);
o.setCreateTime(null);
});
appMapper.insert(dbApp);
// 测试 name 不匹配
appMapper.insert(ObjectUtils.clone(dbApp, o -> o.setName(null)));
// 测试 status 不匹配
appMapper.insert(ObjectUtils.clone(dbApp, o -> o.setStatus(null)));
// 测试 remark 不匹配
appMapper.insert(ObjectUtils.clone(dbApp, o -> o.setRemark(null)));
// 测试 payNotifyUrl 不匹配
appMapper.insert(ObjectUtils.clone(dbApp, o -> o.setPayNotifyUrl(null)));
// 测试 refundNotifyUrl 不匹配
appMapper.insert(ObjectUtils.clone(dbApp, o -> o.setRefundNotifyUrl(null)));
// 测试 merchantId 不匹配
appMapper.insert(ObjectUtils.clone(dbApp, o -> o.setMerchantId(null)));
// 测试 createTime 不匹配
appMapper.insert(ObjectUtils.clone(dbApp, o -> o.setCreateTime(null)));
// 准备参数
PayAppExportReqVO reqVO = new PayAppExportReqVO();
reqVO.setName(null);
reqVO.setStatus(null);
reqVO.setRemark(null);
reqVO.setPayNotifyUrl(null);
reqVO.setRefundNotifyUrl(null);
reqVO.setMerchantId(null);
reqVO.setBeginCreateTime(null);
reqVO.setEndCreateTime(null);
// 调用
List<PayAppDO> list = appService.getAppList(reqVO);
// 断言
assertEquals(1, list.size());
assertPojoEquals(dbApp, list.get(0));
}
}

View File

@ -1,202 +0,0 @@
package cn.iocoder.yudao.adminserver.modules.pay.channel;
import cn.iocoder.yudao.adminserver.BaseDbUnitTest;
import cn.iocoder.yudao.adminserver.modules.pay.controller.channel.vo.PayChannelCreateReqVO;
import cn.iocoder.yudao.adminserver.modules.pay.controller.channel.vo.PayChannelExportReqVO;
import cn.iocoder.yudao.adminserver.modules.pay.controller.channel.vo.PayChannelPageReqVO;
import cn.iocoder.yudao.adminserver.modules.pay.controller.channel.vo.PayChannelUpdateReqVO;
import cn.iocoder.yudao.adminserver.modules.pay.dal.mysql.channel.PayChannelMapper;
import cn.iocoder.yudao.adminserver.modules.pay.service.channel.impl.PayChannelServiceImpl;
import cn.iocoder.yudao.coreservice.modules.pay.dal.dataobject.merchant.PayChannelDO;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.object.ObjectUtils;
import org.junit.jupiter.api.Test;
import org.springframework.context.annotation.Import;
import javax.annotation.Resource;
import java.util.List;
import static cn.iocoder.yudao.coreservice.modules.pay.enums.PayErrorCodeCoreConstants.CHANNEL_NOT_EXISTS;
import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals;
import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertServiceException;
import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomLongId;
import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomPojo;
import static org.junit.jupiter.api.Assertions.*;
/**
* {@link PayChannelServiceImpl} 的单元测试类
*
* @author 芋艿
*/
@Import(PayChannelServiceImpl.class)
public class PayChannelServiceTest extends BaseDbUnitTest {
@Resource
private PayChannelServiceImpl channelService;
@Resource
private PayChannelMapper channelMapper;
@Test
public void testCreateChannel_success() {
// 准备参数
PayChannelCreateReqVO reqVO = randomPojo(PayChannelCreateReqVO.class);
// 调用
Long channelId = channelService.createChannel(reqVO);
// 断言
assertNotNull(channelId);
// 校验记录的属性是否正确
PayChannelDO channel = channelMapper.selectById(channelId);
assertPojoEquals(reqVO, channel);
}
@Test
public void testUpdateChannel_success() {
// mock 数据
PayChannelDO dbChannel = randomPojo(PayChannelDO.class);
channelMapper.insert(dbChannel);// @Sql: 先插入出一条存在的数据
// 准备参数
PayChannelUpdateReqVO reqVO = randomPojo(PayChannelUpdateReqVO.class, o -> {
o.setId(dbChannel.getId()); // 设置更新的 ID
});
// 调用
channelService.updateChannel(reqVO);
// 校验是否更新正确
PayChannelDO channel = channelMapper.selectById(reqVO.getId()); // 获取最新的
assertPojoEquals(reqVO, channel);
}
@Test
public void testUpdateChannel_notExists() {
// 准备参数
PayChannelUpdateReqVO reqVO = randomPojo(PayChannelUpdateReqVO.class);
// 调用, 并断言异常
assertServiceException(() -> channelService.updateChannel(reqVO), CHANNEL_NOT_EXISTS);
}
@Test
public void testDeleteChannel_success() {
// mock 数据
PayChannelDO dbChannel = randomPojo(PayChannelDO.class);
channelMapper.insert(dbChannel);// @Sql: 先插入出一条存在的数据
// 准备参数
Long id = dbChannel.getId();
// 调用
channelService.deleteChannel(id);
// 校验数据不存在了
assertNull(channelMapper.selectById(id));
}
@Test
public void testDeleteChannel_notExists() {
// 准备参数
Long id = randomLongId();
// 调用, 并断言异常
assertServiceException(() -> channelService.deleteChannel(id), CHANNEL_NOT_EXISTS);
}
@Test // TODO 请修改 null 为需要的值
public void testGetChannelPage() {
// mock 数据
PayChannelDO dbChannel = randomPojo(PayChannelDO.class, o -> { // 等会查询到
o.setCode(null);
o.setStatus(null);
o.setRemark(null);
o.setFeeRate(null);
o.setMerchantId(null);
o.setAppId(null);
o.setConfig(null);
o.setCreateTime(null);
});
channelMapper.insert(dbChannel);
// 测试 code 不匹配
channelMapper.insert(ObjectUtils.clone(dbChannel, o -> o.setCode(null)));
// 测试 status 不匹配
channelMapper.insert(ObjectUtils.clone(dbChannel, o -> o.setStatus(null)));
// 测试 remark 不匹配
channelMapper.insert(ObjectUtils.clone(dbChannel, o -> o.setRemark(null)));
// 测试 feeRate 不匹配
channelMapper.insert(ObjectUtils.clone(dbChannel, o -> o.setFeeRate(null)));
// 测试 merchantId 不匹配
channelMapper.insert(ObjectUtils.clone(dbChannel, o -> o.setMerchantId(null)));
// 测试 appId 不匹配
channelMapper.insert(ObjectUtils.clone(dbChannel, o -> o.setAppId(null)));
// 测试 config 不匹配
channelMapper.insert(ObjectUtils.clone(dbChannel, o -> o.setConfig(null)));
// 测试 createTime 不匹配
channelMapper.insert(ObjectUtils.clone(dbChannel, o -> o.setCreateTime(null)));
// 准备参数
PayChannelPageReqVO reqVO = new PayChannelPageReqVO();
reqVO.setCode(null);
reqVO.setStatus(null);
reqVO.setRemark(null);
reqVO.setFeeRate(null);
reqVO.setMerchantId(null);
reqVO.setAppId(null);
reqVO.setConfig(null);
reqVO.setBeginCreateTime(null);
reqVO.setEndCreateTime(null);
// 调用
PageResult<PayChannelDO> pageResult = channelService.getChannelPage(reqVO);
// 断言
assertEquals(1, pageResult.getTotal());
assertEquals(1, pageResult.getList().size());
assertPojoEquals(dbChannel, pageResult.getList().get(0));
}
@Test // TODO aquan请修改 null 为需要的值
public void testGetChannelList() {
// mock 数据
PayChannelDO dbChannel = randomPojo(PayChannelDO.class, o -> { // 等会查询到
o.setCode(null);
o.setStatus(null);
o.setRemark(null);
o.setFeeRate(null);
o.setMerchantId(null);
o.setAppId(null);
o.setConfig(null);
o.setCreateTime(null);
});
channelMapper.insert(dbChannel);
// 测试 code 不匹配
channelMapper.insert(ObjectUtils.clone(dbChannel, o -> o.setCode(null)));
// 测试 status 不匹配
channelMapper.insert(ObjectUtils.clone(dbChannel, o -> o.setStatus(null)));
// 测试 remark 不匹配
channelMapper.insert(ObjectUtils.clone(dbChannel, o -> o.setRemark(null)));
// 测试 feeRate 不匹配
channelMapper.insert(ObjectUtils.clone(dbChannel, o -> o.setFeeRate(null)));
// 测试 merchantId 不匹配
channelMapper.insert(ObjectUtils.clone(dbChannel, o -> o.setMerchantId(null)));
// 测试 appId 不匹配
channelMapper.insert(ObjectUtils.clone(dbChannel, o -> o.setAppId(null)));
// 测试 config 不匹配
channelMapper.insert(ObjectUtils.clone(dbChannel, o -> o.setConfig(null)));
// 测试 createTime 不匹配
channelMapper.insert(ObjectUtils.clone(dbChannel, o -> o.setCreateTime(null)));
// 准备参数
PayChannelExportReqVO reqVO = new PayChannelExportReqVO();
reqVO.setCode(null);
reqVO.setStatus(null);
reqVO.setRemark(null);
reqVO.setFeeRate(null);
reqVO.setMerchantId(null);
reqVO.setAppId(null);
reqVO.setConfig(null);
reqVO.setBeginCreateTime(null);
reqVO.setEndCreateTime(null);
// 调用
List<PayChannelDO> list = channelService.getChannelList(reqVO);
// 断言
assertEquals(1, list.size());
assertPojoEquals(dbChannel, list.get(0));
}
}

View File

@ -0,0 +1,249 @@
package cn.iocoder.yudao.adminserver.modules.pay.service.app;
import cn.hutool.core.util.RandomUtil;
import cn.iocoder.yudao.adminserver.BaseDbUnitTest;
import cn.iocoder.yudao.adminserver.modules.pay.controller.app.vo.PayAppCreateReqVO;
import cn.iocoder.yudao.adminserver.modules.pay.controller.app.vo.PayAppExportReqVO;
import cn.iocoder.yudao.adminserver.modules.pay.controller.app.vo.PayAppPageReqVO;
import cn.iocoder.yudao.adminserver.modules.pay.controller.app.vo.PayAppUpdateReqVO;
import cn.iocoder.yudao.adminserver.modules.pay.dal.mysql.app.PayAppMapper;
import cn.iocoder.yudao.adminserver.modules.pay.dal.mysql.merchant.PayMerchantMapper;
import cn.iocoder.yudao.adminserver.modules.pay.service.app.impl.PayAppServiceImpl;
import cn.iocoder.yudao.adminserver.modules.pay.service.merchant.PayMerchantService;
import cn.iocoder.yudao.coreservice.modules.pay.dal.dataobject.merchant.PayAppDO;
import cn.iocoder.yudao.coreservice.modules.pay.dal.dataobject.merchant.PayMerchantDO;
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.object.ObjectUtils;
import org.junit.jupiter.api.Test;
import org.mockito.InjectMocks;
import org.mockito.Mockito;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.context.annotation.Import;
import javax.annotation.Resource;
import java.util.Collections;
import java.util.List;
import static cn.iocoder.yudao.coreservice.modules.pay.enums.PayErrorCodeCoreConstants.*;
import static cn.iocoder.yudao.framework.common.util.date.DateUtils.buildTime;
import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals;
import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertServiceException;
import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomLongId;
import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomPojo;
import static org.junit.jupiter.api.Assertions.*;
/**
* {@link PayAppServiceImpl} 的单元测试类
*
* @author 芋艿
*/
@Import(PayAppServiceImpl.class)
public class PayAppServiceTest extends BaseDbUnitTest {
@Resource
private PayAppServiceImpl appService;
@Resource
private PayAppMapper appMapper;
@MockBean(name = "payMerchantMapper")
private PayMerchantMapper payMerchantMapper;
@Test
public void testCreateApp_success() {
// 准备参数
PayAppCreateReqVO reqVO = randomPojo(PayAppCreateReqVO.class, o ->
o.setStatus((RandomUtil.randomEle(CommonStatusEnum.values()).getStatus())));
// 调用
Long appId = appService.createApp(reqVO);
// 断言
assertNotNull(appId);
// 校验记录的属性是否正确
PayAppDO app = appMapper.selectById(appId);
assertPojoEquals(reqVO, app);
}
@Test
public void testUpdateApp_success() {
// mock 数据
PayAppDO dbApp = randomPojo(PayAppDO.class, o ->
o.setStatus(CommonStatusEnum.DISABLE.getStatus()));
appMapper.insert(dbApp);// @Sql: 先插入出一条存在的数据
// 准备参数
PayAppUpdateReqVO reqVO = randomPojo(PayAppUpdateReqVO.class, o -> {
o.setStatus(CommonStatusEnum.ENABLE.getStatus());
o.setId(dbApp.getId()); // 设置更新的 ID
});
// 调用
appService.updateApp(reqVO);
// 校验是否更新正确
PayAppDO app = appMapper.selectById(reqVO.getId()); // 获取最新的
assertPojoEquals(reqVO, app);
}
@Test
public void testUpdateApp_notExists() {
// 准备参数
PayAppUpdateReqVO reqVO = randomPojo(PayAppUpdateReqVO.class, o ->
o.setStatus((RandomUtil.randomEle(CommonStatusEnum.values()).getStatus())));
// 调用, 并断言异常
assertServiceException(() -> appService.updateApp(reqVO), APP_NOT_EXISTS);
}
@Test
public void testDeleteApp_success() {
// mock 数据
PayAppDO dbApp = randomPojo(PayAppDO.class, o ->
o.setStatus((RandomUtil.randomEle(CommonStatusEnum.values()).getStatus())));
appMapper.insert(dbApp);// @Sql: 先插入出一条存在的数据
// 准备参数
Long id = dbApp.getId();
// 调用
appService.deleteApp(id);
// 校验数据不存在了
assertNull(appMapper.selectById(id));
}
@Test
public void testDeleteApp_notExists() {
// 准备参数
Long id = randomLongId();
// 调用, 并断言异常
assertServiceException(() -> appService.deleteApp(id), APP_NOT_EXISTS);
}
@Test
public void testGetAppPage() {
Long merchantId = 1L;
Long mismatchMerchantId = 2L;
// mock 数据
PayAppDO dbApp = randomPojo(PayAppDO.class, o -> { // 等会查询到
o.setName("灿灿姐的杂货铺");
o.setStatus(CommonStatusEnum.ENABLE.getStatus());
o.setRemark("敏敏姐的小卖铺");
o.setPayNotifyUrl("https://www.hc.com");
o.setRefundNotifyUrl("https://www.xm.com");
o.setMerchantId(merchantId);
o.setCreateTime(buildTime(2021,11,20));
});
// mock 数据
PayMerchantDO dbMerchant = randomPojo(PayMerchantDO.class, o -> { // 等会查询到
o.setId(merchantId);
o.setNo("M1008611");
o.setName("灿哥的杂货铺");
o.setShortName("灿灿子");
o.setStatus(CommonStatusEnum.ENABLE.getStatus());
o.setRemark("灿哥的杂货铺");
o.setCreateTime(buildTime(2021,11,3));
});
Mockito.when(payMerchantMapper.getMerchantListByName(dbMerchant.getName()))
.thenReturn(Collections.singletonList(dbMerchant));
appMapper.insert(dbApp);
// 测试 name 不匹配
appMapper.insert(ObjectUtils.clone(dbApp, o -> o.setName("敏敏姐的杂货铺")));
// 测试 status 不匹配
appMapper.insert(ObjectUtils.clone(dbApp, o -> o.setStatus(CommonStatusEnum.DISABLE.getStatus())));
// 测试 remark 不匹配
appMapper.insert(ObjectUtils.clone(dbApp, o -> o.setRemark("灿灿姐的小卖部")));
// 测试 payNotifyUrl 不匹配
appMapper.insert(ObjectUtils.clone(dbApp, o -> o.setPayNotifyUrl("xm.com")));
// 测试 refundNotifyUrl 不匹配
appMapper.insert(ObjectUtils.clone(dbApp, o -> o.setRefundNotifyUrl("hc.com")));
// 测试 merchantId 不匹配
appMapper.insert(ObjectUtils.clone(dbApp, o -> o.setMerchantId(mismatchMerchantId)));
// 测试 createTime 不匹配
appMapper.insert(ObjectUtils.clone(dbApp, o -> o.setCreateTime(buildTime(2021,12,21))));
// 准备参数
PayAppPageReqVO reqVO = new PayAppPageReqVO();
reqVO.setName("灿灿姐的杂货铺");
reqVO.setStatus(CommonStatusEnum.ENABLE.getStatus());
reqVO.setRemark("敏敏姐的小卖铺");
reqVO.setPayNotifyUrl("https://www.hc.com");
reqVO.setRefundNotifyUrl("https://www.xm.com");
reqVO.setMerchantName(dbMerchant.getName());
reqVO.setBeginCreateTime(buildTime(2021,11,19));
reqVO.setEndCreateTime(buildTime(2021,11,21));
// 调用
PageResult<PayAppDO> pageResult = appService.getAppPage(reqVO);
// 断言
assertEquals(1, pageResult.getTotal());
assertEquals(1, pageResult.getList().size());
assertPojoEquals(dbApp, pageResult.getList().get(0));
}
@Test // TODO 请修改 null 为需要的值
public void testGetAppList() {
Long merchantId = 1L;
Long mismatchMerchantId = 2L;
// mock 数据
PayAppDO dbApp = randomPojo(PayAppDO.class, o -> { // 等会查询到
o.setName("灿灿姐的杂货铺");
o.setStatus(CommonStatusEnum.ENABLE.getStatus());
o.setRemark("敏敏姐的小卖铺");
o.setPayNotifyUrl("https://www.hc.com");
o.setRefundNotifyUrl("https://www.xm.com");
o.setMerchantId(merchantId);
o.setCreateTime(buildTime(2021,11,20));
});
// mock 数据
PayMerchantDO dbMerchant = randomPojo(PayMerchantDO.class, o -> { // 等会查询到
o.setId(merchantId);
o.setNo("M1008611");
o.setName("灿哥的杂货铺");
o.setShortName("灿灿子");
o.setStatus(CommonStatusEnum.ENABLE.getStatus());
o.setRemark("灿哥的杂货铺");
o.setCreateTime(buildTime(2021,11,3));
});
Mockito.when(payMerchantMapper.getMerchantListByName(dbMerchant.getName()))
.thenReturn(Collections.singletonList(dbMerchant));
appMapper.insert(dbApp);
// 测试 name 不匹配
appMapper.insert(ObjectUtils.clone(dbApp, o -> o.setName("敏敏姐的杂货铺")));
// 测试 status 不匹配
appMapper.insert(ObjectUtils.clone(dbApp, o -> o.setStatus(CommonStatusEnum.DISABLE.getStatus())));
// 测试 remark 不匹配
appMapper.insert(ObjectUtils.clone(dbApp, o -> o.setRemark("灿灿姐的小卖部")));
// 测试 payNotifyUrl 不匹配
appMapper.insert(ObjectUtils.clone(dbApp, o -> o.setPayNotifyUrl("xm.com")));
// 测试 refundNotifyUrl 不匹配
appMapper.insert(ObjectUtils.clone(dbApp, o -> o.setRefundNotifyUrl("hc.com")));
// 测试 merchantId 不匹配
appMapper.insert(ObjectUtils.clone(dbApp, o -> o.setMerchantId(mismatchMerchantId)));
// 测试 createTime 不匹配
appMapper.insert(ObjectUtils.clone(dbApp, o -> o.setCreateTime(buildTime(2021,12,21))));
// 准备参数
PayAppExportReqVO reqVO = new PayAppExportReqVO();
reqVO.setName("灿灿姐的杂货铺");
reqVO.setStatus(CommonStatusEnum.ENABLE.getStatus());
reqVO.setRemark("敏敏姐的小卖铺");
reqVO.setPayNotifyUrl("https://www.hc.com");
reqVO.setRefundNotifyUrl("https://www.xm.com");
reqVO.setMerchantName(dbMerchant.getName());
reqVO.setBeginCreateTime(buildTime(2021,11,19));
reqVO.setEndCreateTime(buildTime(2021,11,21));
// 调用
List<PayAppDO> list = appService.getAppList(reqVO);
// 断言
assertEquals(1, list.size());
assertPojoEquals(dbApp, list.get(0));
}
}

View File

@ -0,0 +1,404 @@
package cn.iocoder.yudao.adminserver.modules.pay.service.channel;
import cn.iocoder.yudao.adminserver.BaseDbUnitTest;
import cn.iocoder.yudao.adminserver.modules.pay.controller.channel.vo.PayChannelCreateReqVO;
import cn.iocoder.yudao.adminserver.modules.pay.controller.channel.vo.PayChannelExportReqVO;
import cn.iocoder.yudao.adminserver.modules.pay.controller.channel.vo.PayChannelPageReqVO;
import cn.iocoder.yudao.adminserver.modules.pay.controller.channel.vo.PayChannelUpdateReqVO;
import cn.iocoder.yudao.adminserver.modules.pay.dal.mysql.channel.PayChannelMapper;
import cn.iocoder.yudao.adminserver.modules.pay.service.channel.impl.PayChannelServiceImpl;
import cn.iocoder.yudao.coreservice.modules.pay.dal.dataobject.merchant.PayChannelDO;
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.object.ObjectUtils;
import cn.iocoder.yudao.framework.pay.core.client.impl.alipay.AlipayPayClientConfig;
import cn.iocoder.yudao.framework.pay.core.client.impl.wx.WXPayClientConfig;
import cn.iocoder.yudao.framework.pay.core.enums.PayChannelEnum;
import com.alibaba.fastjson.JSON;
import org.junit.jupiter.api.Test;
import org.springframework.context.annotation.Import;
import javax.annotation.Resource;
import javax.validation.Validator;
import java.util.List;
import static cn.iocoder.yudao.coreservice.modules.pay.enums.PayErrorCodeCoreConstants.CHANNEL_NOT_EXISTS;
import static cn.iocoder.yudao.framework.common.util.date.DateUtils.buildTime;
import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals;
import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertServiceException;
import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomLongId;
import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomPojo;
import static org.junit.jupiter.api.Assertions.*;
/**
* {@link PayChannelServiceImpl} 的单元测试类
*
* @author 芋艿
*/
@Import(PayChannelServiceImpl.class)
public class PayChannelServiceTest extends BaseDbUnitTest {
@Resource
private PayChannelServiceImpl channelService;
@Resource
private PayChannelMapper channelMapper;
@Test
public void testCreateWechatVersion2Channel_success() {
// 准备参数
WXPayClientConfig v2Config = getV2Config();
PayChannelCreateReqVO reqVO = randomPojo(PayChannelCreateReqVO.class, o -> {
o.setCode(PayChannelEnum.WX_PUB.getCode());
o.setStatus(CommonStatusEnum.ENABLE.getStatus());
o.setConfig(JSON.toJSONString(v2Config));
});
// 调用
Long channelId = channelService.createChannel(reqVO);
// 断言
assertNotNull(channelId);
// 校验记录的属性是否正确
PayChannelDO channel = channelMapper.selectById(channelId);
assertPojoEquals(reqVO, channel, "config");
// 关于config 对象应该拿出来重新对比
assertPojoEquals(v2Config, channel.getConfig());
}
@Test
public void testCreateWechatVersion3Channel_success() {
// 准备参数
WXPayClientConfig v3Config = getV3Config();
PayChannelCreateReqVO reqVO = randomPojo(PayChannelCreateReqVO.class, o -> {
o.setCode(PayChannelEnum.WX_PUB.getCode());
o.setStatus(CommonStatusEnum.ENABLE.getStatus());
o.setConfig(JSON.toJSONString(v3Config));
});
// 调用
Long channelId = channelService.createChannel(reqVO);
// 断言
assertNotNull(channelId);
// 校验记录的属性是否正确
PayChannelDO channel = channelMapper.selectById(channelId);
assertPojoEquals(reqVO, channel, "config");
// 关于config 对象应该拿出来重新对比
assertPojoEquals(v3Config, channel.getConfig());
}
@Test
public void testCreateAliPayPublicKeyChannel_success() {
// 准备参数
AlipayPayClientConfig payClientConfig = getPublicKeyConfig();
PayChannelCreateReqVO reqVO = randomPojo(PayChannelCreateReqVO.class, o -> {
o.setCode(PayChannelEnum.ALIPAY_APP.getCode());
o.setStatus(CommonStatusEnum.ENABLE.getStatus());
o.setConfig(JSON.toJSONString(payClientConfig));
});
// 调用
Long channelId = channelService.createChannel(reqVO);
// 断言
assertNotNull(channelId);
// 校验记录的属性是否正确
PayChannelDO channel = channelMapper.selectById(channelId);
assertPojoEquals(reqVO, channel, "config");
// 关于config 对象应该拿出来重新对比
assertPojoEquals(payClientConfig, channel.getConfig());
}
@Test
public void testCreateAliPayCertificateChannel_success() {
// 准备参数
AlipayPayClientConfig payClientConfig = getCertificateConfig();
PayChannelCreateReqVO reqVO = randomPojo(PayChannelCreateReqVO.class, o -> {
o.setCode(PayChannelEnum.ALIPAY_APP.getCode());
o.setStatus(CommonStatusEnum.ENABLE.getStatus());
o.setConfig(JSON.toJSONString(payClientConfig));
});
// 调用
Long channelId = channelService.createChannel(reqVO);
// 断言
assertNotNull(channelId);
// 校验记录的属性是否正确
PayChannelDO channel = channelMapper.selectById(channelId);
assertPojoEquals(reqVO, channel, "config");
// 关于config 对象应该拿出来重新对比
assertPojoEquals(payClientConfig, channel.getConfig());
}
@Test
public void testUpdateChannel_success() {
// mock 数据
AlipayPayClientConfig payClientConfig = getCertificateConfig();
PayChannelDO dbChannel = randomPojo(PayChannelDO.class, o -> {
o.setCode(PayChannelEnum.ALIPAY_APP.getCode());
o.setStatus(CommonStatusEnum.ENABLE.getStatus());
o.setConfig(payClientConfig);
});
channelMapper.insert(dbChannel);// @Sql: 先插入出一条存在的数据
// 准备参数
AlipayPayClientConfig payClientPublicKeyConfig = getPublicKeyConfig();
PayChannelUpdateReqVO reqVO = randomPojo(PayChannelUpdateReqVO.class, o -> {
o.setCode(dbChannel.getCode());
o.setStatus(dbChannel.getStatus());
o.setConfig(JSON.toJSONString(payClientPublicKeyConfig));
o.setId(dbChannel.getId()); // 设置更新的 ID
});
// 调用
channelService.updateChannel(reqVO);
// 校验是否更新正确
PayChannelDO channel = channelMapper.selectById(reqVO.getId()); // 获取最新的
assertPojoEquals(reqVO, channel, "config");
assertPojoEquals(payClientPublicKeyConfig, channel.getConfig());
}
@Test
public void testUpdateChannel_notExists() {
// 准备参数
AlipayPayClientConfig payClientPublicKeyConfig = getPublicKeyConfig();
PayChannelUpdateReqVO reqVO = randomPojo(PayChannelUpdateReqVO.class, o -> {
o.setCode(PayChannelEnum.ALIPAY_APP.getCode());
o.setStatus(CommonStatusEnum.ENABLE.getStatus());
o.setConfig(JSON.toJSONString(payClientPublicKeyConfig));
});
// 调用, 并断言异常
assertServiceException(() -> channelService.updateChannel(reqVO), CHANNEL_NOT_EXISTS);
}
@Test
public void testDeleteChannel_success() {
// mock 数据
AlipayPayClientConfig payClientConfig = getCertificateConfig();
PayChannelDO dbChannel = randomPojo(PayChannelDO.class, o -> {
o.setCode(PayChannelEnum.ALIPAY_APP.getCode());
o.setStatus(CommonStatusEnum.ENABLE.getStatus());
o.setConfig(payClientConfig);
});
channelMapper.insert(dbChannel);// @Sql: 先插入出一条存在的数据
// 准备参数
Long id = dbChannel.getId();
// 调用
channelService.deleteChannel(id);
// 校验数据不存在了
assertNull(channelMapper.selectById(id));
}
@Test
public void testDeleteChannel_notExists() {
// 准备参数
Long id = randomLongId();
// 调用, 并断言异常
assertServiceException(() -> channelService.deleteChannel(id), CHANNEL_NOT_EXISTS);
}
@Test // TODO 请修改 null 为需要的值
public void testGetChannelPage() {
// mock 数据
AlipayPayClientConfig payClientConfig = getCertificateConfig();
PayChannelDO dbChannel = randomPojo(PayChannelDO.class, o -> { // 等会查询到
o.setCode(PayChannelEnum.ALIPAY_APP.getCode());
o.setStatus(CommonStatusEnum.ENABLE.getStatus());
o.setRemark("灿灿子的支付渠道");
o.setFeeRate(0.03);
o.setMerchantId(1L);
o.setAppId(1L);
o.setConfig(payClientConfig);
o.setCreateTime(buildTime(2021,11,20));
});
channelMapper.insert(dbChannel);
// 执行拷贝的时候会出现异常所以在插入后要重置为null 后续在写入新的
dbChannel.setConfig(null);
// 测试 code 不匹配
channelMapper.insert(ObjectUtils.clone(dbChannel, o -> {
o.setConfig(payClientConfig);
o.setCode(PayChannelEnum.WX_PUB.getCode());
}));
// 测试 status 不匹配
channelMapper.insert(ObjectUtils.clone(dbChannel, o -> {
o.setConfig(payClientConfig);
o.setStatus(CommonStatusEnum.DISABLE.getStatus());
}));
// 测试 remark 不匹配
channelMapper.insert(ObjectUtils.clone(dbChannel, o ->{
o.setConfig(payClientConfig);
o.setRemark("敏敏子的渠道");
}));
// 测试 feeRate 不匹配
channelMapper.insert(ObjectUtils.clone(dbChannel, o -> {
o.setConfig(payClientConfig);
o.setFeeRate(1.23);
}));
// 测试 merchantId 不匹配
channelMapper.insert(ObjectUtils.clone(dbChannel, o -> {
o.setConfig(payClientConfig);
o.setMerchantId(2L);
}));
// 测试 appId 不匹配
channelMapper.insert(ObjectUtils.clone(dbChannel, o -> {
o.setConfig(payClientConfig);
o.setAppId(2L);
}));
// 测试 createTime 不匹配
channelMapper.insert(ObjectUtils.clone(dbChannel, o -> {
o.setConfig(payClientConfig);
o.setCreateTime(buildTime(2021, 10, 20));
}));
// 准备参数
PayChannelPageReqVO reqVO = new PayChannelPageReqVO();
reqVO.setCode(PayChannelEnum.ALIPAY_APP.getCode());
reqVO.setStatus(CommonStatusEnum.ENABLE.getStatus());
reqVO.setRemark("灿灿子的支付渠道");
reqVO.setFeeRate(0.03);
reqVO.setMerchantId(1L);
reqVO.setAppId(1L);
reqVO.setConfig(JSON.toJSONString(payClientConfig));
reqVO.setBeginCreateTime(buildTime(2021,11,19));
reqVO.setEndCreateTime(buildTime(2021,11,21));
// 调用
PageResult<PayChannelDO> pageResult = channelService.getChannelPage(reqVO);
// 断言
assertEquals(1, pageResult.getTotal());
assertEquals(1, pageResult.getList().size());
assertPojoEquals(dbChannel, pageResult.getList().get(0), "config");
assertPojoEquals(payClientConfig, pageResult.getList().get(0).getConfig());
}
@Test
public void testGetChannelList() {
// mock 数据
AlipayPayClientConfig payClientConfig = getCertificateConfig();
PayChannelDO dbChannel = randomPojo(PayChannelDO.class, o -> { // 等会查询到
o.setCode(PayChannelEnum.ALIPAY_APP.getCode());
o.setStatus(CommonStatusEnum.ENABLE.getStatus());
o.setRemark("灿灿子的支付渠道");
o.setFeeRate(0.03);
o.setMerchantId(1L);
o.setAppId(1L);
o.setConfig(payClientConfig);
o.setCreateTime(buildTime(2021,11,20));
});
channelMapper.insert(dbChannel);
// 执行拷贝的时候会出现异常所以在插入后要重置为null 后续在写入新的
dbChannel.setConfig(null);
// 测试 code 不匹配
channelMapper.insert(ObjectUtils.clone(dbChannel, o -> {
o.setConfig(payClientConfig);
o.setCode(PayChannelEnum.WX_PUB.getCode());
}));
// 测试 status 不匹配
channelMapper.insert(ObjectUtils.clone(dbChannel, o -> {
o.setConfig(payClientConfig);
o.setStatus(CommonStatusEnum.DISABLE.getStatus());
}));
// 测试 remark 不匹配
channelMapper.insert(ObjectUtils.clone(dbChannel, o ->{
o.setConfig(payClientConfig);
o.setRemark("敏敏子的渠道");
}));
// 测试 feeRate 不匹配
channelMapper.insert(ObjectUtils.clone(dbChannel, o -> {
o.setConfig(payClientConfig);
o.setFeeRate(1.23);
}));
// 测试 merchantId 不匹配
channelMapper.insert(ObjectUtils.clone(dbChannel, o -> {
o.setConfig(payClientConfig);
o.setMerchantId(2L);
}));
// 测试 appId 不匹配
channelMapper.insert(ObjectUtils.clone(dbChannel, o -> {
o.setConfig(payClientConfig);
o.setAppId(2L);
}));
// 测试 createTime 不匹配
channelMapper.insert(ObjectUtils.clone(dbChannel, o -> {
o.setConfig(payClientConfig);
o.setCreateTime(buildTime(2021, 10, 20));
}));
// 准备参数
PayChannelExportReqVO reqVO = new PayChannelExportReqVO();
reqVO.setCode(PayChannelEnum.ALIPAY_APP.getCode());
reqVO.setStatus(CommonStatusEnum.ENABLE.getStatus());
reqVO.setRemark("灿灿子的支付渠道");
reqVO.setFeeRate(0.03);
reqVO.setMerchantId(1L);
reqVO.setAppId(1L);
reqVO.setConfig(JSON.toJSONString(payClientConfig));
reqVO.setBeginCreateTime(buildTime(2021,11,19));
reqVO.setEndCreateTime(buildTime(2021,11,21));
// 调用
List<PayChannelDO> list = channelService.getChannelList(reqVO);
// 断言
assertEquals(1, list.size());
assertPojoEquals(dbChannel, list.get(0), "config");
assertPojoEquals(payClientConfig, list.get(0).getConfig());
}
public WXPayClientConfig getV2Config() {
return new WXPayClientConfig()
.setAppId("APP00001")
.setMchId("MCH00001")
.setApiVersion(WXPayClientConfig.API_VERSION_V2)
.setMchKey("dsa1d5s6a1d6sa16d1sa56d15a61das6")
.setApiV3Key("")
.setPrivateCertContent("")
.setPrivateKeyContent("");
}
public WXPayClientConfig getV3Config() {
return new WXPayClientConfig()
.setAppId("APP00001")
.setMchId("MCH00001")
.setApiVersion(WXPayClientConfig.API_VERSION_V3)
.setMchKey("")
.setApiV3Key("sdadasdsadadsa")
.setPrivateKeyContent("dsa445das415d15asd16ad156as")
.setPrivateCertContent("dsadasd45asd4s5a");
}
public AlipayPayClientConfig getPublicKeyConfig() {
return new AlipayPayClientConfig()
.setServerUrl(AlipayPayClientConfig.SERVER_URL_PROD)
.setAppId("APP00001")
.setSignType(AlipayPayClientConfig.SIGN_TYPE_DEFAULT)
.setMode(AlipayPayClientConfig.MODE_PUBLIC_KEY)
.setPrivateKey("13131321312")
.setAlipayPublicKey("13321321321")
.setAppCertContent("")
.setAlipayPublicCertContent("")
.setRootCertContent("");
}
public AlipayPayClientConfig getCertificateConfig() {
return new AlipayPayClientConfig()
.setServerUrl(AlipayPayClientConfig.SERVER_URL_PROD)
.setAppId("APP00001")
.setSignType(AlipayPayClientConfig.SIGN_TYPE_DEFAULT)
.setMode(AlipayPayClientConfig.MODE_CERTIFICATE)
.setPrivateKey("")
.setAlipayPublicKey("")
.setAppCertContent("13321321321sda")
.setAlipayPublicCertContent("13321321321aqeqw")
.setRootCertContent("13321321321dsad");
}
}

View File

@ -1,9 +1,6 @@
package cn.iocoder.yudao.adminserver.modules.pay.merchant.service;
package cn.iocoder.yudao.adminserver.modules.pay.service.merchant;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.http.HttpUtil;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import cn.iocoder.yudao.adminserver.BaseDbUnitTest;
import cn.iocoder.yudao.adminserver.modules.pay.controller.merchant.vo.PayMerchantCreateReqVO;
import cn.iocoder.yudao.adminserver.modules.pay.controller.merchant.vo.PayMerchantExportReqVO;
@ -54,8 +51,7 @@ public class PayMerchantServiceTest extends BaseDbUnitTest {
assertNotNull(merchantId);
// 校验记录的属性是否正确
PayMerchantDO merchant = merchantMapper.selectById(merchantId);
// TODO @aquan需要判断 no 非空
assertPojoEquals(reqVO, merchant,"no");
assertPojoEquals(reqVO, merchant);
}
@Test

View File

@ -27,3 +27,5 @@ DELETE FROM "sys_social_user";
-- pay 开头的 DB
DELETE FROM pay_merchant;
DELETE FROM pay_app;
DELETE FROM pay_channel

View File

@ -464,3 +464,37 @@ CREATE TABLE IF NOT EXISTS "pay_merchant" (
"deleted" bit(1) NOT NULL DEFAULT FALSE,
PRIMARY KEY ("id")
) COMMENT '支付商户信息';
CREATE TABLE IF NOT EXISTS "pay_app" (
"id" number NOT NULL GENERATED BY DEFAULT AS IDENTITY,
"name" varchar(64) NOT NULL,
"status" tinyint NOT NULL,
"remark" varchar(255) DEFAULT NULL,
`pay_notify_url` varchar(1024) NOT NULL,
`refund_notify_url`varchar(1024) NOT NULL,
`merchant_id`bigint(20) NOT NULL,
"creator" varchar(64) DEFAULT '',
"create_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ,
"updater" varchar(64) DEFAULT '',
"update_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
"deleted" bit(1) NOT NULL DEFAULT FALSE,
PRIMARY KEY ("id")
) COMMENT = '支付应用信息';
CREATE TABLE "pay_channel" (
"id" number NOT NULL GENERATED BY DEFAULT AS IDENTITY,
"code" varchar(32) NOT NULL ,
"status" tinyint(4) NOT NULL ,
"remark" varchar(255) DEFAULT NULL ,
"fee_rate" double NOT NULL DEFAULT 0 ,
"merchant_id" bigint(20) NOT NULL ,
"app_id" bigint(20) NOT NULL ,
"config" varchar(10240) NOT NULL ,
"creator" varchar(64) NULL DEFAULT '' ,
"create_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ,
"updater" varchar(64) NULL DEFAULT '' ,
"update_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
"deleted" bit(1) NOT NULL DEFAULT FALSE,
PRIMARY KEY ("id")
)COMMENT = '支付渠道';

View File

@ -28,12 +28,12 @@ export function deleteChannel(id) {
}
// 获得支付渠道
export function getChannel(id) {
return request({
url: '/pay/channel/get?id=' + id,
method: 'get'
})
}
// export function getChannel(id) {
// return request({
// url: '/pay/channel/get?id=' + id,
// method: 'get'
// })
// }
@ -56,19 +56,10 @@ export function exportChannelExcel(query) {
})
}
// 创建微信支付渠道
export function createWechatChannel(data) {
return request({
url: '/pay/channel/create-wechat',
method: 'post',
data: data
})
}
// 获得支付渠道
export function getWechatChannel(merchantId,appId,code) {
export function getChannel(merchantId,appId,code) {
return request({
url: '/pay/channel/get-wechat',
url: '/pay/channel/get-channel',
params:{
merchantId:merchantId,
appId:appId,
@ -78,11 +69,3 @@ export function getWechatChannel(merchantId,appId,code) {
})
}
// 更新支付渠道
export function updateWechatChannel(data) {
return request({
url: '/pay/channel/update-wechat',
method: 'put',
data: data
})
}

View File

@ -121,3 +121,8 @@ export const PayChannelEnum = {
"name": "支付宝扫码支付"
},
}
export const PayType = {
WECHAT: "WECHAT",
ALIPAY: "ALIPAY"
}

View File

@ -39,7 +39,12 @@ export const DICT_TYPE = {
PAY_CHANNEL_STATUS: 'pay_channel_status',
// 微信渠道版本
PAY_CHANNEL_WECHAT_VERSION:'pay_channel_wechat_version',
// 支付渠道支付宝算法类型
PAY_CHANNEL_ALIPAY_SIGN_TYPE:'pay_channel_alipay_sign_type',
// 支付宝公钥类型
PAY_CHANNEL_ALIPAY_MODE:'pay_channel_alipay_mode',
// 支付宝网关地址
PAY_CHANNEL_ALIPAY_SERVER_TYPE:'pay_channel_alipay_server_type',
}
/**

View File

@ -0,0 +1,354 @@
<template>
<div>
<el-dialog :visible.sync="transferParam.aliPayOpen" @closed="close" append-to-body width="800px">
<el-form ref="aliPayForm" :model="form" :rules="rules" size="medium" label-width="100px"
v-loading="transferParam.loading">
<el-form-item label-width="180px" label="渠道费率" prop="feeRate">
<el-input v-model="form.feeRate" placeholder="请输入渠道费率" clearable :style="{width: '100%'}">
<template slot="append">%</template>
</el-input>
</el-form-item>
<el-form-item label-width="180px" label="开放平台APPID" prop="aliPayConfig.appId">
<el-input v-model="form.aliPayConfig.appId" placeholder="请输入开放平台APPID" clearable :style="{width: '100%'}">
</el-input>
</el-form-item>
<el-form-item label-width="180px" label="渠道状态" prop="status">
<el-radio-group v-model="form.status" size="medium">
<el-radio v-for="dict in statusDictDatas" :key="parseInt(dict.value)" :label="parseInt(dict.value)">
{{ dict.label }}
</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label-width="180px" label="网关地址" prop="aliPayConfig.serverUrl">
<el-radio-group v-model="form.aliPayConfig.serverUrl" size="medium">
<el-radio v-for="dict in aliPayServerDatas" :key="dict.value" :label="dict.value">
{{ dict.label }}
</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label-width="180px" label="算法类型" prop="aliPayConfig.signType">
<el-radio-group v-model="form.aliPayConfig.signType" size="medium">
<el-radio v-for="dict in aliPaySignTypeDatas" :key="dict.value" :label="dict.value">
{{ dict.label }}
</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label-width="180px" label="公钥类型" prop="aliPayConfig.mode">
<el-radio-group v-model="form.aliPayConfig.mode" size="medium">
<el-radio v-for="dict in aliPayModeDatas" :key="parseInt(dict.value)" :label="parseInt(dict.value)">
{{ dict.label }}
</el-radio>
</el-radio-group>
</el-form-item>
<div v-if="form.aliPayConfig.mode === 1">
<el-form-item label-width="180px" label="商户私钥" prop="aliPayConfig.privateKey">
<el-input type="textarea" :autosize="{minRows: 8, maxRows: 8}" v-model="form.aliPayConfig.privateKey"
placeholder="请输入商户私钥" clearable :style="{width: '100%'}">
</el-input>
</el-form-item>
<el-form-item label-width="180px" label="支付宝公钥字符串" prop="aliPayConfig.alipayPublicKey">
<el-input
type="textarea"
:autosize="{minRows: 8, maxRows: 8}"
v-model="form.aliPayConfig.alipayPublicKey"
placeholder="请输入支付宝公钥字符串" clearable
:style="{width: '100%'}">
</el-input>
</el-form-item>
</div>
<div v-if="form.aliPayConfig.mode === 2">
<el-form-item label-width="180px" label="商户公钥应用证书" prop="aliPayConfig.appCertContent">
<el-input v-model="form.aliPayConfig.appCertContent" type="textarea"
placeholder="请上传商户公钥应用证书"
readonly :autosize="{minRows: 8, maxRows: 8}" :style="{width: '100%'}"></el-input>
</el-form-item>
<el-form-item label-width="180px" label="">
<el-upload
action=""
ref="privateKeyContentFile"
:limit="1"
:accept="fileAccept"
:http-request="appCertUpload"
:before-upload="fileBeforeUpload">
<el-button size="small" type="primary" icon="el-icon-upload">点击上传</el-button>
</el-upload>
</el-form-item>
<el-form-item label-width="180px" label="支付宝公钥证书" prop="aliPayConfig.alipayPublicCertContent">
<el-input v-model="form.aliPayConfig.alipayPublicCertContent" type="textarea"
placeholder="请上传支付宝公钥证书"
readonly :autosize="{minRows: 8, maxRows: 8}" :style="{width: '100%'}"></el-input>
</el-form-item>
<el-form-item label-width="180px" label="">
<el-upload
ref="privateCertContentFile"
action=""
:limit="1"
:accept="fileAccept"
:before-upload="fileBeforeUpload"
:http-request="alipayPublicCertUpload">
<el-button size="small" type="primary" icon="el-icon-upload">点击上传</el-button>
</el-upload>
</el-form-item>
<el-form-item label-width="180px" label="根证书" prop="aliPayConfig.rootCertContent">
<el-input
v-model="form.aliPayConfig.rootCertContent"
type="textarea"
placeholder="请上传根证书"
readonly :autosize="{minRows: 8, maxRows: 8}"
:style="{width: '100%'}">
</el-input>
</el-form-item>
<el-form-item label-width="180px" label="">
<el-upload
ref="privateCertContentFile"
:limit="1"
:accept="fileAccept"
action=""
:before-upload="fileBeforeUpload"
:http-request="rootCertUpload">
<el-button size="small" type="primary" icon="el-icon-upload">点击上传</el-button>
</el-upload>
</el-form-item>
</div>
<el-form-item label-width="180px" label="备注" prop="remark">
<el-input v-model="form.remark" :style="{width: '100%'}"></el-input>
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button @click="close">取消</el-button>
<el-button type="primary" @click="handleConfirm">确定</el-button>
</div>
</el-dialog>
</div>
</template>
<script>
import {DICT_TYPE, getDictDatas} from "@/utils/dict";
import {createChannel, getChannel, updateChannel} from "@/api/pay/channel";
const defaultForm = {
code: '',
status: null,
remark: '',
feeRate: null,
appId: '',
merchantId: null,
aliPayConfig: {
appId: '',
serverUrl: null,
signType: '',
mode: null,
privateKey: '',
alipayPublicKey: '',
appCertContent: '',
alipayPublicCertContent: '',
rootCertContent: ''
}
};
export default {
name: "aliPayChannelForm",
components: {},
props: {
//
transferParam: {
//
"loading": false,
//
"edit": false,
//
"aliPayOpen": false,
// ID
"appId": null,
//
"payCode": null,
//
"payMerchant": {
//
"id": null,
//
"name": null
},
}
},
data() {
return {
form: JSON.parse(JSON.stringify(defaultForm)),
rules: {
feeRate: [{
required: true,
message: '请输入渠道费率',
trigger: 'blur'
}],
'aliPayConfig.appId': [{
required: true,
message: '请输入开放平台上创建的应用的 ID',
trigger: 'blur'
}],
status: [{
required: true,
message: '渠道状态不能为空',
trigger: 'blur'
}],
'aliPayConfig.serverUrl': [{
required: true,
message: '请传入网关地址',
trigger: 'blur'
}],
'aliPayConfig.signType': [{
required: true,
message: '请传入签名算法类型',
trigger: 'blur'
}],
'aliPayConfig.mode': [{
required: true,
message: '公钥类型不能为空',
trigger: 'blur'
}],
'aliPayConfig.privateKey': [{
required: true,
message: '请输入商户私钥',
trigger: 'blur'
}],
'aliPayConfig.alipayPublicKey': [{
required: true,
message: '请输入支付宝公钥字符串',
trigger: 'blur'
}],
'aliPayConfig.appCertContent': [{
required: true,
message: '请上传商户公钥应用证书',
trigger: 'blur'
}],
'aliPayConfig.alipayPublicCertContent': [{
required: true,
message: '请上传支付宝公钥证书',
trigger: 'blur'
}],
'aliPayConfig.rootCertContent': [{
required: true,
message: '请上传指定根证书',
trigger: 'blur'
}],
},
fileAccept: ".crt",
//
statusDictDatas: getDictDatas(DICT_TYPE.PAY_CHANNEL_STATUS),
//
aliPaySignTypeDatas: getDictDatas(DICT_TYPE.PAY_CHANNEL_ALIPAY_SIGN_TYPE),
//
aliPayModeDatas: getDictDatas(DICT_TYPE.PAY_CHANNEL_ALIPAY_MODE),
//
aliPayServerDatas: getDictDatas(DICT_TYPE.PAY_CHANNEL_ALIPAY_SERVER_TYPE),
}
},
watch: {
transferParam: {
deep: true, //
handler(newVal) {
if (newVal.aliPayOpen) {
this.form.code = newVal.payCode;
this.form.appId = newVal.appId;
this.form.merchantId = newVal.payMerchant.id;
//
if (newVal.edit === true && newVal.loading) {
this.init();
}
}
}
}
},
methods: {
init() {
getChannel(this.transferParam.payMerchant.id, this.transferParam.appId, this.transferParam.payCode)
.then(response => {
this.form.id = response.data.id;
this.form.feeRate = response.data.feeRate;
this.form.status = response.data.status;
this.form.remark = response.data.remark;
let config = JSON.parse(response.data.config);
this.form.aliPayConfig.appId = config.appId;
this.form.aliPayConfig.serverUrl = config.serverUrl;
this.form.aliPayConfig.signType = config.signType;
this.form.aliPayConfig.mode = config.mode;
this.form.aliPayConfig.privateKey = config.privateKey;
this.form.aliPayConfig.alipayPublicKey = config.alipayPublicKey;
this.form.aliPayConfig.appCertContent = config.appCertContent;
this.form.aliPayConfig.alipayPublicCertContent = config.alipayPublicCertContent;
this.form.aliPayConfig.rootCertContent = config.rootCertContent;
this.transferParam.loading = false;
})
},
close() {
this.transferParam.aliPayOpen = false;
this.form = JSON.parse(JSON.stringify(defaultForm));
},
handleConfirm() {
this.$refs['aliPayForm'].validate(valid => {
if (!valid) {
return
}
let data = this.form;
data.config = JSON.stringify(this.form.aliPayConfig);
if (this.transferParam.edit) {
updateChannel(data).then(response => {
if (response.code === 0) {
this.msgSuccess("修改成功");
this.close();
}
})
} else {
createChannel(data).then(response => {
if (response.code === 0) {
this.msgSuccess("新增成功");
this.$parent.refreshTable();
this.close();
}
});
}
});
},
fileBeforeUpload(file) {
let format = '.' + file.name.split(".")[1];
if (format !== this.fileAccept) {
this.$message.error('请上传指定格式"' + this.fileAccept + '"文件');
return false;
}
let isRightSize = file.size / 1024 / 1024 < 2
if (!isRightSize) {
this.$message.error('文件大小超过 2MB')
}
return isRightSize
},
appCertUpload(event) {
const readFile = new FileReader()
readFile.onload = (e) => {
this.form.aliPayConfig.appCertContent = e.target.result
}
readFile.readAsText(event.file);
},
alipayPublicCertUpload(event) {
const readFile = new FileReader()
readFile.onload = (e) => {
this.form.aliPayConfig.alipayPublicCertContent = e.target.result
}
readFile.readAsText(event.file);
},
rootCertUpload(event) {
const readFile = new FileReader()
readFile.onload = (e) => {
this.form.aliPayConfig.rootCertContent = e.target.result
}
readFile.readAsText(event.file);
},
}
}
</script>
<style scoped>
</style>

View File

@ -1,6 +1,6 @@
<template>
<div>
<el-dialog :visible.sync="transferParam.open" @close="close" append-to-body>
<el-dialog :visible.sync="transferParam.wechatOpen" @close="close" append-to-body width="800px">
<el-form ref="wechatJsApiForm" :model="form" :rules="rules" size="medium" label-width="100px"
v-loading="transferParam.loading">
<el-form-item label-width="180px" label="渠道费率" prop="feeRate">
@ -22,7 +22,7 @@
</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label-width="180px" label="API 版本" prop="weChatConfig.apiVersion">
<el-form-item label-width="180px" label="API 版本" prop="weChatConfig.apiVersion">
<el-radio-group v-model="form.weChatConfig.apiVersion" size="medium">
<el-radio v-for="dict in versionDictDatas" :key="dict.value" :label="dict.value">
{{ dict.label }}
@ -32,9 +32,13 @@
<el-form-item label-width="180px" label="商户秘钥" prop="weChatConfig.mchKey"
v-if="form.weChatConfig.apiVersion === 'v2'">
<el-input v-model="form.weChatConfig.mchKey" placeholder="请输入商户秘钥" clearable
:style="{width: '100%'}"></el-input>
:style="{width: '100%'}" type="textarea" :autosize="{minRows: 8, maxRows: 8}"></el-input>
</el-form-item>
<div v-if="form.weChatConfig.apiVersion === 'v3'">
<el-form-item label-width="180px" label="API V3秘钥" prop="weChatConfig.apiV3Key">
<el-input v-model="form.weChatConfig.apiV3Key" placeholder="请输入API V3秘钥" clearable
:style="{width: '100%'}" type="textarea" :autosize="{minRows: 8, maxRows: 8}"></el-input>
</el-form-item>
<el-form-item label-width="180px" label="apiclient_key.perm证书" prop="weChatConfig.privateKeyContent">
<el-input v-model="form.weChatConfig.privateKeyContent" type="textarea"
placeholder="请上传apiclient_key.perm证书"
@ -45,9 +49,9 @@
:limit="1"
:accept="fileAccept"
:headers="header"
:action="pemUploadAction"
action=""
:before-upload="pemFileBeforeUpload"
:on-success="privateKeyUploadSuccess"
:http-request="privateKeyUpload"
>
<el-button size="small" type="primary" icon="el-icon-upload">点击上传</el-button>
</el-upload>
@ -62,9 +66,9 @@
:limit="1"
:accept="fileAccept"
:headers="header"
:action="pemUploadAction"
action=""
:before-upload="pemFileBeforeUpload"
:on-success="privateCertUploadSuccess"
:http-request="privateCertUpload"
>
<el-button size="small" type="primary" icon="el-icon-upload">点击上传</el-button>
</el-upload>
@ -83,10 +87,28 @@
</template>
<script>
import {DICT_TYPE, getDictDatas} from "@/utils/dict";
import {createWechatChannel, getWechatChannel, updateWechatChannel} from "@/api/pay/channel";
import {createChannel, getChannel, updateChannel} from "@/api/pay/channel";
const defaultForm = {
code: '',
status: null,
remark: '',
feeRate: null,
appId: '',
merchantId: null,
weChatConfig: {
appId: '',
mchId: '',
apiVersion: '',
mchKey: '',
privateKeyContent: '',
privateCertContent: '',
apiV3Key:'',
}
}
export default {
name: "wechatJsApiForm",
name: "wechatChannelForm",
components: {},
props: {
//
@ -96,7 +118,7 @@ export default {
//
"edit": false,
//
"open": false,
"wechatOpen": false,
// ID
"appId": null,
//
@ -112,22 +134,7 @@ export default {
},
data() {
return {
form: {
code: undefined,
status: undefined,
remark: undefined,
feeRate: undefined,
appId: undefined,
merchantId: undefined,
weChatConfig: {
appId: undefined,
mchId: undefined,
apiVersion: undefined,
mchKey: undefined,
privateKeyContent: undefined,
privateCertContent: undefined,
}
},
form: JSON.parse(JSON.stringify(defaultForm)),
rules: {
feeRate: [{
required: true,
@ -147,12 +154,12 @@ export default {
status: [{
required: true,
message: '渠道状态不能为空',
trigger: 'change'
trigger: 'blur'
}],
'weChatConfig.apiVersion': [{
required: true,
message: 'API版本不能为空',
trigger: 'change'
trigger: 'blur'
}],
'weChatConfig.mchKey': [{
required: true,
@ -169,12 +176,16 @@ export default {
message: '请上传apiclient_cert.perm证书',
trigger: 'blur'
}],
'weChatConfig.apiV3Key': [{
required: true,
message: '请上传apiV3秘钥值',
trigger: 'blur'
}],
},
// header
header: {
"Authorization": null
},
pemUploadAction: 'http://127.0.0.1:48080/api/pay/channel/parsing-pem',
fileAccept: ".pem",
//
statusDictDatas: getDictDatas(DICT_TYPE.PAY_CHANNEL_STATUS),
@ -185,51 +196,66 @@ export default {
transferParam: {
deep: true, //
handler(newVal) {
this.form.code = newVal.payCode;
this.form.appId = newVal.appId;
this.form.merchantId = newVal.payMerchant.id;
//
if (newVal.edit === true && newVal.loading) {
this.init();
if (newVal.wechatOpen) {
this.form.code = newVal.payCode;
this.form.appId = newVal.appId;
this.form.merchantId = newVal.payMerchant.id;
//
if (newVal.edit && newVal.loading) {
this.init();
}
}
}
}
},
created() {
this.header.Authorization = "Bearer " + this.$store.getters.token;
},
methods: {
init() {
getWechatChannel(this.transferParam.payMerchant.id, this.transferParam.appId, this.transferParam.payCode)
getChannel(this.transferParam.payMerchant.id, this.transferParam.appId, this.transferParam.payCode)
.then(response => {
this.form = response.data;
this.form.id = response.data.id;
this.form.feeRate = response.data.feeRate;
this.form.appId = response.data.appId;
this.form.status = response.data.status;
this.form.remark = response.data.remark;
let config = JSON.parse(response.data.config);
this.form.weChatConfig.appId = config.appId;
this.form.weChatConfig.apiVersion = config.apiVersion;
this.form.weChatConfig.mchId = config.mchId;
this.form.weChatConfig.mchKey = config.mchKey;
this.form.weChatConfig.privateKeyContent = config.privateKeyContent;
this.form.weChatConfig.privateCertContent = config.privateCertContent;
this.form.weChatConfig.apiV3Key = config.apiV3Key;
this.transferParam.loading = false;
})
},
close() {
this.transferParam.open = false;
this.$refs['wechatJsApiForm'].resetFields();
this.transferParam.wechatOpen = false;
this.form = JSON.parse(JSON.stringify(defaultForm));
},
handleConfirm() {
this.$refs['wechatJsApiForm'].validate(valid => {
if (!valid) {
return
}
let data = this.form;
data.config = JSON.stringify(this.form.weChatConfig);
if (this.transferParam.edit) {
updateWechatChannel(this.form).then(response => {
if (response.code === 0 ) {
updateChannel(data).then(response => {
if (response.code === 0) {
this.msgSuccess("修改成功");
this.close();
}
})
} else {
createWechatChannel(this.form).then(response => {
if (response.code === 0) {
this.msgSuccess("新增成功");
this.$parent.refreshTable();
this.close();
}
createChannel(data).then(response => {
if (response.code === 0) {
this.msgSuccess("新增成功");
this.$parent.refreshTable();
this.close();
}
});
}
});
@ -246,11 +272,19 @@ export default {
}
return isRightSize
},
privateKeyUploadSuccess(response) {
this.form.weChatConfig.privateKeyContent = response.data;
privateKeyUpload(event) {
const readFile = new FileReader()
readFile.onload = (e) => {
this.form.weChatConfig.privateKeyContent = e.target.result
}
readFile.readAsText(event.file);
},
privateCertUploadSuccess(response) {
this.form.weChatConfig.privateCertContent = response.data;
privateCertUpload(event) {
const readFile = new FileReader()
readFile.onload = (e) => {
this.form.weChatConfig.privateCertContent = e.target.result
}
readFile.readAsText(event.file);
}
}
}

View File

@ -57,68 +57,104 @@
<el-table-column label="支付宝配置" align="center">
<el-table-column :label="payChannelEnum.ALIPAY_APP.name" align="center">
<template slot-scope="scope">
<el-button type="success" icon="el-icon-check" circle
v-if="scope.row.payChannel.alipayApp === sysCommonStatusEnum.ENABLE"></el-button>
<el-button type="danger" icon="el-icon-close" circle
v-if="scope.row.payChannel.alipayApp === sysCommonStatusEnum.DISABLE"></el-button>
<el-button
type="success" icon="el-icon-check" circle
@click="handleUpdateChannel(scope.row,payChannelEnum.ALIPAY_APP.code,payType.ALIPAY)"
v-if="scope.row.payChannel.alipayApp === sysCommonStatusEnum.ENABLE">
</el-button>
<el-button
type="danger" icon="el-icon-close" circle
@click="handleCreateChannel(scope.row,payChannelEnum.ALIPAY_APP.code,payType.ALIPAY)"
v-if="scope.row.payChannel.alipayApp === sysCommonStatusEnum.DISABLE">
</el-button>
</template>
</el-table-column>
<el-table-column :label="payChannelEnum.ALIPAY_PC.name" align="center">
<template slot-scope="scope">
<el-button type="success" icon="el-icon-check" circle
v-if="scope.row.payChannel.alipayPc === sysCommonStatusEnum.ENABLE"></el-button>
<el-button type="danger" icon="el-icon-close" circle
v-if="scope.row.payChannel.alipayPc === sysCommonStatusEnum.DISABLE"></el-button>
<el-button
type="success" icon="el-icon-check" circle
@click="handleUpdateChannel(scope.row,payChannelEnum.ALIPAY_PC.code,payType.ALIPAY)"
v-if="scope.row.payChannel.alipayPc === sysCommonStatusEnum.ENABLE">
</el-button>
<el-button
type="danger" icon="el-icon-close" circle
@click="handleCreateChannel(scope.row,payChannelEnum.ALIPAY_PC.code,payType.ALIPAY)"
v-if="scope.row.payChannel.alipayPc === sysCommonStatusEnum.DISABLE">
</el-button>
</template>
</el-table-column>
<el-table-column :label="payChannelEnum.ALIPAY_WAP.name" align="center">
<template slot-scope="scope">
<el-button type="success" icon="el-icon-check" circle
v-if="scope.row.payChannel.alipayWap === sysCommonStatusEnum.ENABLE"></el-button>
<el-button type="danger" icon="el-icon-close" circle
v-if="scope.row.payChannel.alipayWap === sysCommonStatusEnum.DISABLE"></el-button>
<el-button
type="success" icon="el-icon-check" circle
@click="handleUpdateChannel(scope.row,payChannelEnum.ALIPAY_WAP.code,payType.ALIPAY)"
v-if="scope.row.payChannel.alipayWap === sysCommonStatusEnum.ENABLE">
</el-button>
<el-button
type="danger" icon="el-icon-close" circle
@click="handleCreateChannel(scope.row,payChannelEnum.ALIPAY_WAP.code,payType.ALIPAY)"
v-if="scope.row.payChannel.alipayWap === sysCommonStatusEnum.DISABLE">
</el-button>
</template>
</el-table-column>
<el-table-column :label="payChannelEnum.ALIPAY_QR.name" align="center">
<template slot-scope="scope">
<el-button type="success" icon="el-icon-check" circle
v-if="scope.row.payChannel.alipayQr === sysCommonStatusEnum.ENABLE"></el-button>
<el-button type="danger" icon="el-icon-close" circle
v-if="scope.row.payChannel.alipayQr === sysCommonStatusEnum.DISABLE"></el-button>
<el-button
type="success" icon="el-icon-check" circle
@click="handleUpdateChannel(scope.row,payChannelEnum.ALIPAY_QR.code,payType.ALIPAY)"
v-if="scope.row.payChannel.alipayQr === sysCommonStatusEnum.ENABLE">
</el-button>
<el-button
type="danger" icon="el-icon-close" circle
@click="handleCreateChannel(scope.row,payChannelEnum.ALIPAY_QR.code,payType.ALIPAY)"
v-if="scope.row.payChannel.alipayQr === sysCommonStatusEnum.DISABLE">
</el-button>
</template>
</el-table-column>
</el-table-column>
<el-table-column label="微信配置" align="center">
<el-table-column :label="payChannelEnum.WX_LITE.name" align="center">
<template slot-scope="scope">
<el-button type="success" icon="el-icon-check" circle
@click="handleUpdateChannel(scope.row,payChannelEnum.WX_LITE.code)"
v-if="scope.row.payChannel.wxLite === sysCommonStatusEnum.ENABLE"></el-button>
<el-button type="danger" icon="el-icon-close" circle
@click="handleCreateChannel(scope.row,payChannelEnum.WX_LITE.code)"
v-if="scope.row.payChannel.wxLite === sysCommonStatusEnum.DISABLE"></el-button>
<el-button
type="success" icon="el-icon-check" circle
@click="handleUpdateChannel(scope.row,payChannelEnum.WX_LITE.code,payType.WECHAT)"
v-if="scope.row.payChannel.wxLite === sysCommonStatusEnum.ENABLE">
</el-button>
<el-button
type="danger" icon="el-icon-close" circle
@click="handleCreateChannel(scope.row,payChannelEnum.WX_LITE.code,payType.WECHAT)"
v-if="scope.row.payChannel.wxLite === sysCommonStatusEnum.DISABLE">
</el-button>
</template>
</el-table-column>
<el-table-column :label="payChannelEnum.WX_PUB.name" align="center">
<template slot-scope="scope">
<el-button
type="success" icon="el-icon-check" circle
@click="handleUpdateChannel(scope.row,payChannelEnum.WX_PUB.code)"
@click="handleUpdateChannel(scope.row,payChannelEnum.WX_PUB.code,payType.WECHAT)"
v-if="scope.row.payChannel.wxPub === sysCommonStatusEnum.ENABLE">
</el-button>
<el-button type="danger" icon="el-icon-close" circle
@click="handleCreateChannel(scope.row,payChannelEnum.WX_PUB.code)"
v-if="scope.row.payChannel.wxPub === sysCommonStatusEnum.DISABLE"></el-button>
<el-button
type="danger" icon="el-icon-close" circle
@click="handleCreateChannel(scope.row,payChannelEnum.WX_PUB.code,payType.WECHAT)"
v-if="scope.row.payChannel.wxPub === sysCommonStatusEnum.DISABLE">
</el-button>
</template>
</el-table-column>
<el-table-column :label="payChannelEnum.WX_APP.name" align="center">
<template slot-scope="scope">
<el-button type="success" icon="el-icon-check" circle
@click="handleUpdateChannel(scope.row,payChannelEnum.WX_APP.code)"
v-if="scope.row.payChannel.wxApp === sysCommonStatusEnum.ENABLE"></el-button>
<el-button type="danger" icon="el-icon-close" circle
@click="handleCreateChannel(scope.row,payChannelEnum.WX_APP.code)"
v-if="scope.row.payChannel.wxApp === sysCommonStatusEnum.DISABLE"></el-button>
<el-button
type="success" icon="el-icon-check" circle
@click="handleUpdateChannel(scope.row,payChannelEnum.WX_APP.code,payType.WECHAT)"
v-if="scope.row.payChannel.wxApp === sysCommonStatusEnum.ENABLE">
</el-button>
<el-button
type="danger" icon="el-icon-close" circle
@click="handleCreateChannel(scope.row,payChannelEnum.WX_APP.code,payType.WECHAT)"
v-if="scope.row.payChannel.wxApp === sysCommonStatusEnum.DISABLE">
</el-button>
</template>
</el-table-column>
</el-table-column>
@ -186,21 +222,24 @@
</div>
</el-dialog>
<wechat-js-api-form :transferParam="wechatChannelParam"></wechat-js-api-form>
<wechat-channel-form :transferParam="channelParam"></wechat-channel-form>
<ali-pay-channel-form :transferParam="channelParam"></ali-pay-channel-form>
</div>
</template>
<script>
import {createApp, updateApp, changeAppStatus, deleteApp, getApp, getAppPage, exportAppExcel} from "@/api/pay/app";
import {DICT_TYPE, getDictDatas} from "@/utils/dict";
import {PayChannelEnum, SysCommonStatusEnum} from "@/utils/constants";
import {PayType, PayChannelEnum, SysCommonStatusEnum} from "@/utils/constants";
import {getMerchantListByName} from "@/api/pay/merchant";
import wechatJsApiForm from "@/views/pay/app/components/wechatJsApiForm";
import wechatChannelForm from "@/views/pay/app/components/wechatChannelForm";
import aliPayChannelForm from "@/views/pay/app/components/aliPayChannelForm";
export default {
name: "App",
components: {
"wechatJsApiForm": wechatJsApiForm
"wechatChannelForm": wechatChannelForm,
"aliPayChannelForm": aliPayChannelForm
},
data() {
return {
@ -243,16 +282,20 @@ export default {
sysCommonStatusEnum: SysCommonStatusEnum,
//
payChannelEnum: PayChannelEnum,
//
payType: PayType,
//
merchantList: [],
//
payOpen: false,
//
wechatChannelParam: {
channelParam: {
//
"edit":false,
//
"open":false,
"edit": false,
//
"wechatOpen": false,
//
"aliPayOpen": false,
// ID
"appId": null,
//
@ -279,7 +322,6 @@ export default {
this.addBeginAndEndTime(params, this.dateRangeCreateTime, 'createTime');
//
getAppPage(params).then(response => {
console.log(response.data);
this.list = response.data.list;
this.total = response.data.total;
this.loading = false;
@ -355,9 +397,9 @@ export default {
//
if (this.form.id != null) {
updateApp(this.form).then(response => {
this.msgSuccess("修改成功");
this.open = false;
this.getList();
this.msgSuccess("修改成功");
this.open = false;
this.getList();
});
return;
}
@ -407,33 +449,47 @@ export default {
*/
handleGetMerchantListByName(name) {
getMerchantListByName(name).then(response => {
console.log(response)
this.merchantList = response.data;
});
},
/**
* 修改支付渠道信息
*/
handleUpdateChannel(row, payCode) {
this.wechatChannelParam.edit = true;
this.wechatChannelParam.loading = true;
this.wechatChannelParam.appId = row.id;
this.wechatChannelParam.payCode = payCode;
this.wechatChannelParam.payMerchant = row.payMerchant;
this.wechatChannelParam.open = true;
handleUpdateChannel(row, payCode, type) {
this.settingChannelParam(row, payCode, type)
this.channelParam.edit = true;
this.channelParam.loading = true;
},
/**
* 新增支付渠道信息
*/
handleCreateChannel(row, payCode) {
this.wechatChannelParam.edit = false;
this.wechatChannelParam.loading = false;
this.wechatChannelParam.appId = row.id;
this.wechatChannelParam.payCode = payCode;
this.wechatChannelParam.payMerchant = row.payMerchant;
this.wechatChannelParam.open = true;
handleCreateChannel(row, payCode, type) {
this.settingChannelParam(row, payCode, type)
this.channelParam.edit = false;
this.channelParam.loading = false;
},
refreshTable(){
/**
* 设置支付渠道信息
*/
settingChannelParam(row, payCode, type) {
if (type === PayType.WECHAT) {
this.channelParam.wechatOpen = true;
this.channelParam.aliPayOpen = false;
}
if (type === PayType.ALIPAY) {
this.channelParam.aliPayOpen = true;
this.channelParam.wechatOpen = false;
}
this.channelParam.edit = false;
this.channelParam.loading = false;
this.channelParam.appId = row.id;
this.channelParam.payCode = payCode;
this.channelParam.payMerchant = row.payMerchant;
},
refreshTable() {
this.getList();
}
}

View File

@ -9,21 +9,35 @@ import cn.iocoder.yudao.framework.common.exception.ErrorCode;
*/
public interface PayErrorCodeCoreConstants {
// ========== APP 模块 1-007-000-000 ==========
/**
* ========== APP 模块 1-007-000-000 ==========
*/
ErrorCode PAY_APP_NOT_FOUND = new ErrorCode(1007000000, "App 不存在");
ErrorCode PAY_APP_IS_DISABLE = new ErrorCode(1007000002, "App 已经被禁用");
// ========== CHANNEL 模块 1-007-001-000 ==========
/**
* ========== CHANNEL 模块 1-007-001-000 ==========
*/
ErrorCode PAY_CHANNEL_NOT_FOUND = new ErrorCode(1007001000, "支付渠道的配置不存在");
ErrorCode PAY_CHANNEL_IS_DISABLE = new ErrorCode(1007001001, "支付渠道已经禁用");
ErrorCode PAY_CHANNEL_CLIENT_NOT_FOUND = new ErrorCode(1007001002, "支付渠道的客户端不存在");
// ========== ORDER 模块 1-007-002-000 ==========
ErrorCode CHANNEL_NOT_EXISTS = new ErrorCode(1007001003, "支付渠道不存在");
ErrorCode CHANNEL_KEY_READ_ERROR = new ErrorCode(1007001004, "支付渠道秘钥文件读取失败");
ErrorCode CHANNEL_EXIST_SAME_CHANNEL_ERROR = new ErrorCode(1007001005, "已存在相同的渠道");
ErrorCode CHANNEL_WECHAT_VERSION_2_MCH_KEY_IS_NULL = new ErrorCode(1007001006,"微信渠道v2版本中商户密钥不可为空");
ErrorCode CHANNEL_WECHAT_VERSION_3_PRIVATE_KEY_IS_NULL = new ErrorCode(1007001006,"微信渠道v3版本apiclient_key.pem不可为空");
ErrorCode CHANNEL_WECHAT_VERSION_3_CERT_KEY_IS_NULL = new ErrorCode(1007001006,"微信渠道v3版本中apiclient_cert.pem不可为空");
/**
* ========== ORDER 模块 1-007-002-000 ==========
*/
ErrorCode PAY_ORDER_NOT_FOUND = new ErrorCode(1007002000, "支付订单不存在");
ErrorCode PAY_ORDER_STATUS_IS_NOT_WAITING = new ErrorCode(1007002001, "支付订单不处于待支付");
ErrorCode PAY_ORDER_STATUS_IS_NOT_SUCCESS = new ErrorCode(1007002002, "支付订单不处于已支付");
ErrorCode PAY_ORDER_ERROR_USER = new ErrorCode(1007002003, "支付订单用户不正确");
// ========== ORDER 模块(拓展单) 1-007-003-000 ==========
/**
* ========== ORDER 模块(拓展单) 1-007-003-000 ==========
*/
ErrorCode PAY_ORDER_EXTENSION_NOT_FOUND = new ErrorCode(1007003000, "支付交易拓展单不存在");
ErrorCode PAY_ORDER_EXTENSION_STATUS_IS_NOT_WAITING = new ErrorCode(1007003001, "支付交易拓展单不处于待支付");
ErrorCode PAY_ORDER_EXTENSION_STATUS_IS_NOT_SUCCESS = new ErrorCode(1007003002, "支付订单不处于已支付");
@ -46,12 +60,4 @@ public interface PayErrorCodeCoreConstants {
ErrorCode APP_NOT_EXISTS = new ErrorCode(1007005000, "支付应用信息不存在");
/**
* ========== 支付渠道 1-007-001-000 ==========
*/
ErrorCode CHANNEL_NOT_EXISTS = new ErrorCode(1007001003, "支付渠道不存在");
ErrorCode CHANNEL_KEY_READ_ERROR = new ErrorCode(1007001004, "支付渠道秘钥文件读取失败");
// TODO @aquan下面这个错误码缺了 CHANNEL 前缀另外错误码的分段上面有啦合并下进去哈
ErrorCode EXIST_SAME_CHANNEL_ERROR = new ErrorCode(1007001005, "已存在相同的渠道");
}

View File

@ -65,6 +65,8 @@ public class PayClientFactoryImpl implements PayClientFactory {
case WX_APP: return (AbstractPayClient<Config>) new WXPubPayClient(channelId, (WXPayClientConfig) config);
case ALIPAY_WAP: return (AbstractPayClient<Config>) new AlipayWapPayClient(channelId, (AlipayPayClientConfig) config);
case ALIPAY_QR: return (AbstractPayClient<Config>) new AlipayQrPayClient(channelId, (AlipayPayClientConfig) config);
case ALIPAY_APP: return (AbstractPayClient<Config>) new AlipayQrPayClient(channelId, (AlipayPayClientConfig) config);
case ALIPAY_PC: return (AbstractPayClient<Config>) new AlipayQrPayClient(channelId, (AlipayPayClientConfig) config);
}
// 创建失败错误日志 + 抛出异常
log.error("[createSmsClient][配置({}) 找不到合适的客户端实现]", config);

View File

@ -3,7 +3,11 @@ package cn.iocoder.yudao.framework.pay.core.client.impl.alipay;
import cn.iocoder.yudao.framework.pay.core.client.PayClientConfig;
import lombok.Data;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
// TODO 芋艿参数校验
/**
* 支付宝的 PayClientConfig 实现类
* 属性主要来自 {@link com.alipay.api.AlipayConfig} 的必要属性
@ -25,11 +29,11 @@ public class AlipayPayClientConfig implements PayClientConfig {
/**
* 公钥类型 - 公钥模式
*/
private static final Integer MODE_PUBLIC_KEY = 1;
public static final Integer MODE_PUBLIC_KEY = 1;
/**
* 公钥类型 - 证书模式
*/
private static final Integer MODE_CERTIFICATE = 2;
public static final Integer MODE_CERTIFICATE = 2;
/**
* 签名算法类型 - RSA
@ -41,18 +45,21 @@ public class AlipayPayClientConfig implements PayClientConfig {
* 1. {@link #SERVER_URL_PROD}
* 2. {@link #SERVER_URL_SANDBOX}
*/
@NotBlank(message = "网关地址不能为空", groups = {ModePublicKey.class, ModeCertificate.class})
private String serverUrl;
/**
* 开放平台上创建的应用的 ID
*/
@NotBlank(message = "开放平台上创建的应用的 ID不能为空", groups = {ModePublicKey.class, ModeCertificate.class})
private String appId;
/**
* 签名算法类型推荐RSA2
*
* <p>
* {@link #SIGN_TYPE_DEFAULT}
*/
@NotBlank(message = "签名算法类型不能为空", groups = {ModePublicKey.class, ModeCertificate.class})
private String signType;
/**
@ -60,30 +67,43 @@ public class AlipayPayClientConfig implements PayClientConfig {
* 1. {@link #MODE_PUBLIC_KEY} 情况privateKey + alipayPublicKey
* 2. {@link #MODE_CERTIFICATE} 情况appCertContent + alipayPublicCertContent + rootCertContent
*/
@NotNull(message = "公钥类型不能为空", groups = {ModePublicKey.class, ModeCertificate.class})
private Integer mode;
// ========== 公钥模式 ==========
/**
* 商户私钥
*/
@NotBlank(message = "商户私钥不能为空", groups = {ModePublicKey.class})
private String privateKey;
/**
* 支付宝公钥字符串
*/
@NotBlank(message = "支付宝公钥字符串不能为空", groups = {ModePublicKey.class})
private String alipayPublicKey;
// ========== 证书模式 ==========
/**
* 指定商户公钥应用证书内容字符串
*/
@NotBlank(message = "指定商户公钥应用证书内容不能为空", groups = {ModeCertificate.class})
private String appCertContent;
/**
* 指定支付宝公钥证书内容字符串
*/
@NotBlank(message = "指定支付宝公钥证书内容不能为空", groups = {ModeCertificate.class})
private String alipayPublicCertContent;
/**
* 指定根证书内容字符串
*/
@NotBlank(message = "指定根证书内容字符串不能为空", groups = {ModeCertificate.class})
private String rootCertContent;
public interface ModePublicKey {
}
public interface ModeCertificate {
}
}

View File

@ -4,10 +4,12 @@ import cn.hutool.core.io.IoUtil;
import cn.iocoder.yudao.framework.pay.core.client.PayClientConfig;
import lombok.Data;
import javax.validation.constraints.NotBlank;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
// TODO 芋艿参数校验
/**
* 微信支付的 PayClientConfig 实现类
* 属性主要来自 {@link com.github.binarywang.wxpay.config.WxPayConfig} 的必要属性
@ -20,13 +22,13 @@ public class WXPayClientConfig implements PayClientConfig {
// TODO 芋艿V2 or V3 客户端
/**
* API 版本 - V2
*
* <p>
* https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=4_1
*/
public static final String API_VERSION_V2 = "v2";
/**
* API 版本 - V3
*
* <p>
* https://pay.weixin.qq.com/wiki/doc/apiv3/wechatpay/wechatpay-1.shtml
*/
public static final String API_VERSION_V3 = "v3";
@ -34,14 +36,17 @@ public class WXPayClientConfig implements PayClientConfig {
/**
* 公众号或者小程序的 appid
*/
@NotBlank(message = "APPID 不能为空", groups = {V2.class, V3.class})
private String appId;
/**
* 商户号
*/
@NotBlank(message = "商户号 不能为空", groups = {V2.class, V3.class})
private String mchId;
/**
* API 版本
*/
@NotBlank(message = "API 版本 不能为空", groups = {V2.class, V3.class})
private String apiVersion;
// ========== V2 版本的参数 ==========
@ -49,33 +54,37 @@ public class WXPayClientConfig implements PayClientConfig {
/**
* 商户密钥
*/
@NotBlank(message = "商户密钥 不能为空", groups = {V2.class})
private String mchKey;
// /**
// * apiclient_cert.p12 证书文件的绝对路径或者以 classpath: 开头的类路径.
// * 对应的字符串
// *
// * 注意可通过 {@link #main(String[])} 读取
// */
// private String keyContent;
/**
* apiclient_cert.p12 证书文件的绝对路径或者以 classpath: 开头的类路径.
* 对应的字符串
*
* 注意可通过 {@link #main(String[])} 读取
*/
/// private String keyContent;
// ========== V3 版本的参数 ==========
/**
* apiclient_key.pem 证书文件的绝对路径或者以 classpath: 开头的类路径.
* 对应的字符串
*
* <p>
* 注意可通过 {@link #main(String[])} 读取
*/
@NotBlank(message = "apiclient_key 不能为空", groups = {V3.class})
private String privateKeyContent;
/**
* apiclient_cert.pem 证书文件的绝对路径或者以 classpath: 开头的类路径.
* 对应的字符串
*
* <p>
* 注意可通过 {@link #main(String[])} 读取
*/
@NotBlank(message = "apiclient_cert 不能为空", groups = {V3.class})
private String privateCertContent;
/**
* apiV3 秘钥值
*/
@NotBlank(message = "apiV3 秘钥值 不能为空", groups = {V3.class})
private String apiV3Key;
public static void main(String[] args) throws FileNotFoundException {
@ -85,4 +94,17 @@ public class WXPayClientConfig implements PayClientConfig {
System.out.println(IoUtil.readUtf8(new FileInputStream(path)));
}
/**
* 分组校验 v2版本
*/
public interface V2 {
}
/**
* 分组校验 v3版本
*/
public interface V3 {
}
}

View File

@ -15,8 +15,6 @@ import lombok.Getter;
public enum PayChannelEnum {
WX_PUB("wx_pub", "微信 JSAPI 支付"), // 公众号的网页
// TODO @芋艿 这个地方你写的是 wx_lit 是不是少写了一个e 还是我这里多加了一个e
// TODO @aquan这里就是 lite 轻量
WX_LITE("wx_lite","微信小程序支付"),
WX_APP("wx_app", "微信 App 支付"),
@ -36,8 +34,38 @@ public enum PayChannelEnum {
*/
private String name;
/**
* 微信支付
*/
public static final String WECHAT = "WECHAT";
/**
* 支付宝支付
*/
public static final String ALIPAY = "ALIPAY";
public static PayChannelEnum getByCode(String code) {
return ArrayUtil.firstMatch(o -> o.getCode().equals(code), values());
}
/**
* 判断当前渠道是那种支付方式
* @param code
* @return
*/
public static String verifyWechatOrAliPay(String code){
switch (PayChannelEnum.getByCode(code)){
case WX_PUB:
case WX_LITE:
case WX_APP:
return WECHAT;
case ALIPAY_PC:
case ALIPAY_WAP:
case ALIPAY_APP:
case ALIPAY_QR:
return ALIPAY;
}
return null;
}
}