Merge branch 'master' of https://gitee.com/zhijiantianya/ruoyi-vue-pro into feature/user-social
Conflicts: yudao-admin-server/pom.xml yudao-dependencies/pom.xml yudao-user-server/src/main/resources/application-dev.yaml yudao-user-server/src/main/resources/application-local.yamlplp
commit
c3aa2acddf
File diff suppressed because one or more lines are too long
@ -0,0 +1,6 @@
|
|||||||
|
/**
|
||||||
|
* 提供 POJO 类的实体转换
|
||||||
|
*
|
||||||
|
* 目前使用 MapStruct 框架
|
||||||
|
*/
|
||||||
|
package cn.iocoder.yudao.adminserver.modules.pay.convert;
|
||||||
@ -0,0 +1 @@
|
|||||||
|
<http://www.iocoder.cn/Spring-Boot/MapStruct/?yudao>
|
||||||
@ -0,0 +1 @@
|
|||||||
|
package cn.iocoder.yudao.adminserver.modules.pay.job;
|
||||||
@ -0,0 +1,22 @@
|
|||||||
|
package cn.iocoder.yudao.coreservice.modules.pay.convert.order;
|
||||||
|
|
||||||
|
import cn.iocoder.yudao.coreservice.modules.pay.dal.dataobject.order.PayOrderDO;
|
||||||
|
import cn.iocoder.yudao.coreservice.modules.pay.dal.dataobject.order.PayOrderExtensionDO;
|
||||||
|
import cn.iocoder.yudao.coreservice.modules.pay.service.order.dto.PayOrderCreateReqDTO;
|
||||||
|
import cn.iocoder.yudao.coreservice.modules.pay.service.order.dto.PayOrderSubmitReqDTO;
|
||||||
|
import cn.iocoder.yudao.framework.pay.core.client.dto.PayOrderUnifiedReqDTO;
|
||||||
|
import org.mapstruct.Mapper;
|
||||||
|
import org.mapstruct.factory.Mappers;
|
||||||
|
|
||||||
|
@Mapper
|
||||||
|
public interface PayOrderCoreConvert {
|
||||||
|
|
||||||
|
PayOrderCoreConvert INSTANCE = Mappers.getMapper(PayOrderCoreConvert.class);
|
||||||
|
|
||||||
|
PayOrderDO convert(PayOrderCreateReqDTO bean);
|
||||||
|
|
||||||
|
PayOrderExtensionDO convert(PayOrderSubmitReqDTO bean);
|
||||||
|
|
||||||
|
PayOrderUnifiedReqDTO convert2(PayOrderSubmitReqDTO bean);
|
||||||
|
|
||||||
|
}
|
||||||
@ -0,0 +1,6 @@
|
|||||||
|
/**
|
||||||
|
* 提供 POJO 类的实体转换
|
||||||
|
*
|
||||||
|
* 目前使用 MapStruct 框架
|
||||||
|
*/
|
||||||
|
package cn.iocoder.yudao.coreservice.modules.pay.convert;
|
||||||
@ -0,0 +1 @@
|
|||||||
|
<http://www.iocoder.cn/Spring-Boot/MapStruct/?yudao>
|
||||||
@ -0,0 +1,9 @@
|
|||||||
|
package cn.iocoder.yudao.coreservice.modules.pay.dal.mysql.merchant;
|
||||||
|
|
||||||
|
import cn.iocoder.yudao.coreservice.modules.pay.dal.dataobject.merchant.PayAppDO;
|
||||||
|
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
|
||||||
|
import org.apache.ibatis.annotations.Mapper;
|
||||||
|
|
||||||
|
@Mapper
|
||||||
|
public interface PayAppCoreMapper extends BaseMapperX<PayAppDO> {
|
||||||
|
}
|
||||||
@ -0,0 +1,20 @@
|
|||||||
|
package cn.iocoder.yudao.coreservice.modules.pay.dal.mysql.merchant;
|
||||||
|
|
||||||
|
import cn.iocoder.yudao.coreservice.modules.pay.dal.dataobject.merchant.PayChannelDO;
|
||||||
|
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
|
||||||
|
import org.apache.ibatis.annotations.Mapper;
|
||||||
|
import org.apache.ibatis.annotations.Select;
|
||||||
|
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
|
@Mapper
|
||||||
|
public interface PayChannelCoreMapper extends BaseMapperX<PayChannelDO> {
|
||||||
|
|
||||||
|
default PayChannelDO selectByAppIdAndCode(Long appId, String code) {
|
||||||
|
return selectOne("app_id", appId, "code", code);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Select("SELECT id FROM pay_channel WHERE update_time > #{maxUpdateTime} LIMIT 1")
|
||||||
|
Long selectExistsByUpdateTimeAfter(Date maxUpdateTime);
|
||||||
|
|
||||||
|
}
|
||||||
@ -0,0 +1,9 @@
|
|||||||
|
package cn.iocoder.yudao.coreservice.modules.pay.dal.mysql.notify;
|
||||||
|
|
||||||
|
import cn.iocoder.yudao.coreservice.modules.pay.dal.dataobject.notify.PayNotifyLogDO;
|
||||||
|
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
|
||||||
|
import org.apache.ibatis.annotations.Mapper;
|
||||||
|
|
||||||
|
@Mapper
|
||||||
|
public interface PayNotifyLogCoreMapper extends BaseMapperX<PayNotifyLogDO> {
|
||||||
|
}
|
||||||
@ -0,0 +1,22 @@
|
|||||||
|
package cn.iocoder.yudao.coreservice.modules.pay.dal.mysql.order;
|
||||||
|
|
||||||
|
import cn.iocoder.yudao.coreservice.modules.pay.dal.dataobject.order.PayOrderDO;
|
||||||
|
import cn.iocoder.yudao.coreservice.modules.pay.dal.dataobject.order.PayOrderExtensionDO;
|
||||||
|
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
|
||||||
|
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||||
|
import org.apache.ibatis.annotations.Mapper;
|
||||||
|
|
||||||
|
@Mapper
|
||||||
|
public interface PayOrderCoreMapper extends BaseMapperX<PayOrderDO> {
|
||||||
|
|
||||||
|
default PayOrderDO selectByAppIdAndMerchantOrderId(Long appId, String merchantOrderId) {
|
||||||
|
return selectOne(new QueryWrapper<PayOrderDO>().eq("app_id", appId)
|
||||||
|
.eq("merchant_order_id", merchantOrderId));
|
||||||
|
}
|
||||||
|
|
||||||
|
default int updateByIdAndStatus(Long id, Integer status, PayOrderDO update) {
|
||||||
|
return update(update, new QueryWrapper<PayOrderDO>()
|
||||||
|
.eq("id", id).eq("status", status));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -0,0 +1,20 @@
|
|||||||
|
package cn.iocoder.yudao.coreservice.modules.pay.dal.mysql.order;
|
||||||
|
|
||||||
|
import cn.iocoder.yudao.coreservice.modules.pay.dal.dataobject.order.PayOrderExtensionDO;
|
||||||
|
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
|
||||||
|
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||||
|
import org.apache.ibatis.annotations.Mapper;
|
||||||
|
|
||||||
|
@Mapper
|
||||||
|
public interface PayOrderExtensionCoreMapper extends BaseMapperX<PayOrderExtensionDO> {
|
||||||
|
|
||||||
|
default PayOrderExtensionDO selectByNo(String no) {
|
||||||
|
return selectOne("no", no);
|
||||||
|
}
|
||||||
|
|
||||||
|
default int updateByIdAndStatus(Long id, Integer status, PayOrderExtensionDO update) {
|
||||||
|
return update(update, new QueryWrapper<PayOrderExtensionDO>()
|
||||||
|
.eq("id", id).eq("status", status));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -0,0 +1,19 @@
|
|||||||
|
package cn.iocoder.yudao.coreservice.modules.pay.dal.redis;
|
||||||
|
|
||||||
|
import cn.iocoder.yudao.framework.redis.core.RedisKeyDefine;
|
||||||
|
import org.redisson.api.RLock;
|
||||||
|
|
||||||
|
import static cn.iocoder.yudao.framework.redis.core.RedisKeyDefine.KeyTypeEnum.HASH;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Lock4j Redis Key 枚举类
|
||||||
|
*
|
||||||
|
* @author 芋道源码
|
||||||
|
*/
|
||||||
|
public interface PayRedisKeyCoreConstants {
|
||||||
|
|
||||||
|
RedisKeyDefine PAY_NOTIFY_LOCK = new RedisKeyDefine("通知任务的分布式锁",
|
||||||
|
"pay_notify:lock:", // 参数来自 DefaultLockKeyBuilder 类
|
||||||
|
HASH, RLock.class, RedisKeyDefine.TimeoutTypeEnum.DYNAMIC); // Redisson 的 Lock 锁,使用 Hash 数据结构
|
||||||
|
|
||||||
|
}
|
||||||
@ -0,0 +1,39 @@
|
|||||||
|
package cn.iocoder.yudao.coreservice.modules.pay.dal.redis.notify;
|
||||||
|
|
||||||
|
import org.redisson.api.RLock;
|
||||||
|
import org.redisson.api.RedissonClient;
|
||||||
|
import org.springframework.stereotype.Repository;
|
||||||
|
|
||||||
|
import javax.annotation.Resource;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
|
import static cn.iocoder.yudao.coreservice.modules.pay.dal.redis.PayRedisKeyCoreConstants.PAY_NOTIFY_LOCK;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 支付通知的锁 Redis DAO
|
||||||
|
*
|
||||||
|
* @author 芋道源码
|
||||||
|
*/
|
||||||
|
@Repository
|
||||||
|
public class PayNotifyLockCoreRedisDAO {
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private RedissonClient redissonClient;
|
||||||
|
|
||||||
|
public void lock(Long id, Long timeoutMillis, Runnable runnable) {
|
||||||
|
String lockKey = formatKey(id);
|
||||||
|
RLock lock = redissonClient.getLock(lockKey);
|
||||||
|
try {
|
||||||
|
lock.lock(timeoutMillis, TimeUnit.MILLISECONDS);
|
||||||
|
// 执行逻辑
|
||||||
|
runnable.run();
|
||||||
|
} finally {
|
||||||
|
lock.unlock();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String formatKey(Long id) {
|
||||||
|
return String.format(PAY_NOTIFY_LOCK.getKeyTemplate(), id);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -0,0 +1,32 @@
|
|||||||
|
package cn.iocoder.yudao.coreservice.modules.pay.enums.notify;
|
||||||
|
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Getter;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 支付通知状态枚举
|
||||||
|
*
|
||||||
|
* @author 芋道源码
|
||||||
|
*/
|
||||||
|
@Getter
|
||||||
|
@AllArgsConstructor
|
||||||
|
public enum PayNotifyStatusEnum {
|
||||||
|
|
||||||
|
WAITING(1, "等待通知"),
|
||||||
|
SUCCESS(2, "通知成功"),
|
||||||
|
FAILURE(3, "通知失败"), // 多次尝试,彻底失败
|
||||||
|
REQUEST_SUCCESS(4, "请求成功,但是结果失败"),
|
||||||
|
REQUEST_FAILURE(5, "请求失败"),
|
||||||
|
|
||||||
|
;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 状态
|
||||||
|
*/
|
||||||
|
private final Integer status;
|
||||||
|
/**
|
||||||
|
* 名字
|
||||||
|
*/
|
||||||
|
private final String name;
|
||||||
|
|
||||||
|
}
|
||||||
@ -0,0 +1,28 @@
|
|||||||
|
package cn.iocoder.yudao.coreservice.modules.pay.enums.notify;
|
||||||
|
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Getter;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 支付通知类型
|
||||||
|
*
|
||||||
|
* @author 芋道源码
|
||||||
|
*/
|
||||||
|
@Getter
|
||||||
|
@AllArgsConstructor
|
||||||
|
public enum PayNotifyTypeEnum {
|
||||||
|
|
||||||
|
ORDER(1, "支付单"),
|
||||||
|
REFUND(2, "退款单"),
|
||||||
|
;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 类型
|
||||||
|
*/
|
||||||
|
private final Integer type;
|
||||||
|
/**
|
||||||
|
* 名字
|
||||||
|
*/
|
||||||
|
private final String name;
|
||||||
|
|
||||||
|
}
|
||||||
@ -0,0 +1,29 @@
|
|||||||
|
package cn.iocoder.yudao.coreservice.modules.pay.enums.order;
|
||||||
|
|
||||||
|
import cn.iocoder.yudao.framework.common.core.IntArrayValuable;
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Getter;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 支付订单的通知状态枚举
|
||||||
|
*
|
||||||
|
* @author 芋道源码
|
||||||
|
*/
|
||||||
|
@Getter
|
||||||
|
@AllArgsConstructor
|
||||||
|
public enum PayOrderNotifyStatusEnum implements IntArrayValuable {
|
||||||
|
|
||||||
|
NO(0, "未通知"),
|
||||||
|
SUCCESS(10, "通知成功"),
|
||||||
|
FAILURE(20, "通知失败")
|
||||||
|
;
|
||||||
|
|
||||||
|
private final Integer status;
|
||||||
|
private final String name;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int[] array() {
|
||||||
|
return new int[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -0,0 +1,29 @@
|
|||||||
|
package cn.iocoder.yudao.coreservice.modules.pay.enums.order;
|
||||||
|
|
||||||
|
import cn.iocoder.yudao.framework.common.core.IntArrayValuable;
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Getter;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 支付订单的退款状态枚举
|
||||||
|
*
|
||||||
|
* @author 芋道源码
|
||||||
|
*/
|
||||||
|
@Getter
|
||||||
|
@AllArgsConstructor
|
||||||
|
public enum PayOrderRefundStatusEnum implements IntArrayValuable {
|
||||||
|
|
||||||
|
NO(0, "未退款"),
|
||||||
|
SOME(10, "部分退款"),
|
||||||
|
ALL(20, "全部退款")
|
||||||
|
;
|
||||||
|
|
||||||
|
private final Integer status;
|
||||||
|
private final String name;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int[] array() {
|
||||||
|
return new int[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -0,0 +1,29 @@
|
|||||||
|
package cn.iocoder.yudao.coreservice.modules.pay.enums.order;
|
||||||
|
|
||||||
|
import cn.iocoder.yudao.framework.common.core.IntArrayValuable;
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Getter;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 支付订单的状态枚举
|
||||||
|
*
|
||||||
|
* @author 芋道源码
|
||||||
|
*/
|
||||||
|
@Getter
|
||||||
|
@AllArgsConstructor
|
||||||
|
public enum PayOrderStatusEnum implements IntArrayValuable {
|
||||||
|
|
||||||
|
WAITING(0, "未支付"),
|
||||||
|
SUCCESS(10, "支付成功"),
|
||||||
|
CLOSED(20, "支付关闭"), // 未付款交易超时关闭,或支付完成后全额退款 TODO 芋艿:需要优化下
|
||||||
|
;
|
||||||
|
|
||||||
|
private final Integer status;
|
||||||
|
private final String name;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int[] array() {
|
||||||
|
return new int[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -0,0 +1,43 @@
|
|||||||
|
package cn.iocoder.yudao.coreservice.modules.pay.service.merchant.impl;
|
||||||
|
|
||||||
|
import cn.iocoder.yudao.coreservice.modules.pay.dal.dataobject.merchant.PayAppDO;
|
||||||
|
import cn.iocoder.yudao.coreservice.modules.pay.dal.mysql.merchant.PayAppCoreMapper;
|
||||||
|
import cn.iocoder.yudao.coreservice.modules.pay.service.merchant.PayAppCoreService;
|
||||||
|
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
import javax.annotation.Resource;
|
||||||
|
import javax.validation.Valid;
|
||||||
|
|
||||||
|
import static cn.iocoder.yudao.coreservice.modules.pay.enums.PayErrorCodeCoreConstants.*;
|
||||||
|
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 支付应用 Core Service 实现类
|
||||||
|
*
|
||||||
|
* @author 芋道源码
|
||||||
|
*/
|
||||||
|
@Service
|
||||||
|
@Valid
|
||||||
|
@Slf4j
|
||||||
|
public class PayAppCoreServiceImpl implements PayAppCoreService {
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private PayAppCoreMapper payAppCoreMapper;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public PayAppDO validPayApp(Long id) {
|
||||||
|
PayAppDO app = payAppCoreMapper.selectById(id);
|
||||||
|
// 校验是否存在
|
||||||
|
if (app == null) {
|
||||||
|
throw exception(PAY_APP_NOT_FOUND);
|
||||||
|
}
|
||||||
|
// 校验是否禁用
|
||||||
|
if (CommonStatusEnum.DISABLE.getStatus().equals(app.getStatus())) {
|
||||||
|
throw exception(PAY_APP_IS_DISABLE);
|
||||||
|
}
|
||||||
|
return app;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -0,0 +1,32 @@
|
|||||||
|
package cn.iocoder.yudao.coreservice.modules.pay.service.notify.dto;
|
||||||
|
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Builder;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
|
import javax.validation.constraints.NotNull;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 支付通知创建 DTO
|
||||||
|
*
|
||||||
|
* @author 芋道源码
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@Builder
|
||||||
|
@NoArgsConstructor
|
||||||
|
@AllArgsConstructor
|
||||||
|
public class PayNotifyTaskCreateReqDTO {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 类型
|
||||||
|
*/
|
||||||
|
@NotNull(message = "类型不能为空")
|
||||||
|
private Integer type;
|
||||||
|
/**
|
||||||
|
* 数据编号
|
||||||
|
*/
|
||||||
|
@NotNull(message = "数据编号不能为空")
|
||||||
|
private Long dataId;
|
||||||
|
|
||||||
|
}
|
||||||
@ -0,0 +1,28 @@
|
|||||||
|
package cn.iocoder.yudao.coreservice.modules.pay.service.notify.vo;
|
||||||
|
|
||||||
|
import io.swagger.annotations.ApiModel;
|
||||||
|
import io.swagger.annotations.ApiModelProperty;
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Builder;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
|
import javax.validation.constraints.NotEmpty;
|
||||||
|
import javax.validation.constraints.NotNull;
|
||||||
|
|
||||||
|
@ApiModel(value = "支付单的通知 Request VO", description = "业务方接入支付回调时,使用该 VO 对象")
|
||||||
|
@Data
|
||||||
|
@Builder
|
||||||
|
@NoArgsConstructor
|
||||||
|
@AllArgsConstructor
|
||||||
|
public class PayNotifyOrderReqVO {
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "商户订单编号", required = true, example = "10")
|
||||||
|
@NotEmpty(message = "商户订单号不能为空")
|
||||||
|
private String merchantOrderId;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "支付订单编号", required = true, example = "20")
|
||||||
|
@NotNull(message = "支付订单编号不能为空")
|
||||||
|
private Long payOrderId;
|
||||||
|
|
||||||
|
}
|
||||||
@ -0,0 +1,28 @@
|
|||||||
|
package cn.iocoder.yudao.coreservice.modules.pay.service.notify.vo;
|
||||||
|
|
||||||
|
import io.swagger.annotations.ApiModel;
|
||||||
|
import io.swagger.annotations.ApiModelProperty;
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Builder;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
|
import javax.validation.constraints.NotEmpty;
|
||||||
|
import javax.validation.constraints.NotNull;
|
||||||
|
|
||||||
|
@ApiModel(value = "退款单的通知 Request VO", description = "业务方接入退款回调时,使用该 VO 对象")
|
||||||
|
@Data
|
||||||
|
@Builder
|
||||||
|
@NoArgsConstructor
|
||||||
|
@AllArgsConstructor
|
||||||
|
public class PayRefundOrderReqVO {
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "商户订单编号", required = true, example = "10")
|
||||||
|
@NotEmpty(message = "商户订单号不能为空")
|
||||||
|
private String merchantOrderId;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "支付退款编号", required = true, example = "20")
|
||||||
|
@NotNull(message = "支付退款编号不能为空")
|
||||||
|
private Long payRefundId;
|
||||||
|
|
||||||
|
}
|
||||||
@ -0,0 +1,47 @@
|
|||||||
|
package cn.iocoder.yudao.coreservice.modules.pay.service.order.dto;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.experimental.Accessors;
|
||||||
|
|
||||||
|
import javax.validation.constraints.NotEmpty;
|
||||||
|
import javax.validation.constraints.NotNull;
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 支付单提交 Request DTO
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@Accessors(chain = true)
|
||||||
|
public class PayOrderSubmitReqDTO implements Serializable {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 应用编号
|
||||||
|
*/
|
||||||
|
@NotNull(message = "应用编号不能为空")
|
||||||
|
private Long appId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 支付单编号
|
||||||
|
*/
|
||||||
|
@NotNull(message = "支付单编号不能为空")
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 支付渠道
|
||||||
|
*/
|
||||||
|
@NotEmpty(message = "支付渠道不能为空")
|
||||||
|
private String channelCode;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 用户 IP
|
||||||
|
*/
|
||||||
|
@NotEmpty(message = "用户 IP 不能为空")
|
||||||
|
private String userIp;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 支付渠道的额外参数
|
||||||
|
*/
|
||||||
|
private Map<String, String> channelExtras;
|
||||||
|
|
||||||
|
}
|
||||||
@ -0,0 +1,23 @@
|
|||||||
|
package cn.iocoder.yudao.coreservice.modules.pay.service.order.dto;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 支付单提交 Response DTO
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
public class PayOrderSubmitRespDTO implements Serializable {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 支付拓展单的编号
|
||||||
|
*/
|
||||||
|
private Long extensionId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 调用支付渠道的响应结果
|
||||||
|
*/
|
||||||
|
private Object invokeResponse;
|
||||||
|
|
||||||
|
}
|
||||||
@ -0,0 +1 @@
|
|||||||
|
package cn.iocoder.yudao.coreservice.modules.pay.service;
|
||||||
@ -0,0 +1,38 @@
|
|||||||
|
package cn.iocoder.yudao.coreservice;
|
||||||
|
|
||||||
|
import cn.iocoder.yudao.framework.datasource.config.YudaoDataSourceAutoConfiguration;
|
||||||
|
import cn.iocoder.yudao.framework.mybatis.config.YudaoMybatisAutoConfiguration;
|
||||||
|
import cn.iocoder.yudao.framework.redis.config.YudaoRedisAutoConfiguration;
|
||||||
|
import com.baomidou.dynamic.datasource.spring.boot.autoconfigure.DynamicDataSourceAutoConfiguration;
|
||||||
|
import com.baomidou.mybatisplus.autoconfigure.MybatisPlusAutoConfiguration;
|
||||||
|
import org.redisson.spring.starter.RedissonAutoConfiguration;
|
||||||
|
import org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration;
|
||||||
|
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
|
||||||
|
import org.springframework.boot.autoconfigure.jdbc.DataSourceTransactionManagerAutoConfiguration;
|
||||||
|
import org.springframework.boot.test.context.SpringBootTest;
|
||||||
|
import org.springframework.context.annotation.Import;
|
||||||
|
import org.springframework.test.context.ActiveProfiles;
|
||||||
|
|
||||||
|
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.NONE, classes = BaseDbAndRedisIntegrationTest.Application.class)
|
||||||
|
@ActiveProfiles("integration-test") // 设置使用 application-integration-test 配置文件
|
||||||
|
public class BaseDbAndRedisIntegrationTest {
|
||||||
|
|
||||||
|
@Import({
|
||||||
|
// DB 配置类
|
||||||
|
DynamicDataSourceAutoConfiguration.class, // Dynamic Datasource 配置类
|
||||||
|
YudaoDataSourceAutoConfiguration.class, // 自己的 DB 配置类
|
||||||
|
DataSourceAutoConfiguration.class, // Spring DB 自动配置类
|
||||||
|
DataSourceTransactionManagerAutoConfiguration.class, // Spring 事务自动配置类
|
||||||
|
// MyBatis 配置类
|
||||||
|
YudaoMybatisAutoConfiguration.class, // 自己的 MyBatis 配置类
|
||||||
|
MybatisPlusAutoConfiguration.class, // MyBatis 的自动配置类
|
||||||
|
|
||||||
|
// Redis 配置类
|
||||||
|
RedisAutoConfiguration.class, // Spring Redis 自动配置类
|
||||||
|
YudaoRedisAutoConfiguration.class, // 自己的 Redis 配置类
|
||||||
|
RedissonAutoConfiguration.class, // Redisson 自动高配置类
|
||||||
|
})
|
||||||
|
public static class Application {
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -0,0 +1,30 @@
|
|||||||
|
package cn.iocoder.yudao.coreservice;
|
||||||
|
|
||||||
|
import cn.iocoder.yudao.framework.datasource.config.YudaoDataSourceAutoConfiguration;
|
||||||
|
import cn.iocoder.yudao.framework.mybatis.config.YudaoMybatisAutoConfiguration;
|
||||||
|
import com.baomidou.dynamic.datasource.spring.boot.autoconfigure.DynamicDataSourceAutoConfiguration;
|
||||||
|
import com.baomidou.mybatisplus.autoconfigure.MybatisPlusAutoConfiguration;
|
||||||
|
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
|
||||||
|
import org.springframework.boot.autoconfigure.jdbc.DataSourceTransactionManagerAutoConfiguration;
|
||||||
|
import org.springframework.boot.test.context.SpringBootTest;
|
||||||
|
import org.springframework.context.annotation.Import;
|
||||||
|
import org.springframework.test.context.ActiveProfiles;
|
||||||
|
|
||||||
|
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.NONE, classes = BaseDbIntegrationTest.Application.class)
|
||||||
|
@ActiveProfiles("integration-test") // 设置使用 application-integration-test 配置文件
|
||||||
|
public class BaseDbIntegrationTest {
|
||||||
|
|
||||||
|
@Import({
|
||||||
|
// DB 配置类
|
||||||
|
DynamicDataSourceAutoConfiguration.class, // Dynamic Datasource 配置类
|
||||||
|
YudaoDataSourceAutoConfiguration.class, // 自己的 DB 配置类
|
||||||
|
DataSourceAutoConfiguration.class, // Spring DB 自动配置类
|
||||||
|
DataSourceTransactionManagerAutoConfiguration.class, // Spring 事务自动配置类
|
||||||
|
// MyBatis 配置类
|
||||||
|
YudaoMybatisAutoConfiguration.class, // 自己的 MyBatis 配置类
|
||||||
|
MybatisPlusAutoConfiguration.class, // MyBatis 的自动配置类
|
||||||
|
})
|
||||||
|
public static class Application {
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -0,0 +1,23 @@
|
|||||||
|
package cn.iocoder.yudao.coreservice;
|
||||||
|
|
||||||
|
import cn.iocoder.yudao.framework.redis.config.YudaoRedisAutoConfiguration;
|
||||||
|
import org.redisson.spring.starter.RedissonAutoConfiguration;
|
||||||
|
import org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration;
|
||||||
|
import org.springframework.boot.test.context.SpringBootTest;
|
||||||
|
import org.springframework.context.annotation.Import;
|
||||||
|
import org.springframework.test.context.ActiveProfiles;
|
||||||
|
|
||||||
|
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.NONE, classes = BaseRedisIntegrationTest.Application.class)
|
||||||
|
@ActiveProfiles("integration-test") // 设置使用 application-integration-test 配置文件
|
||||||
|
public class BaseRedisIntegrationTest {
|
||||||
|
|
||||||
|
@Import({
|
||||||
|
// Redis 配置类
|
||||||
|
RedisAutoConfiguration.class, // Spring Redis 自动配置类
|
||||||
|
YudaoRedisAutoConfiguration.class, // 自己的 Redis 配置类
|
||||||
|
RedissonAutoConfiguration.class, // Redisson 自动高配置类
|
||||||
|
})
|
||||||
|
public static class Application {
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -0,0 +1,29 @@
|
|||||||
|
package cn.iocoder.yudao.coreservice.modules.pay.dal.dataobject.merchant;
|
||||||
|
|
||||||
|
import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
|
||||||
|
import cn.iocoder.yudao.framework.pay.core.client.impl.wx.WXPayClientConfig;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
public class PayChannelDOTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSerialization() {
|
||||||
|
PayChannelDO payChannelDO = new PayChannelDO();
|
||||||
|
// 创建配置
|
||||||
|
WXPayClientConfig config = new WXPayClientConfig();
|
||||||
|
config.setAppId("wx041349c6f39b268b");
|
||||||
|
config.setMchId("1545083881");
|
||||||
|
config.setApiVersion(WXPayClientConfig.API_VERSION_V2);
|
||||||
|
config.setMchKey("0alL64UDQdlCwiKZ73ib7ypaIjMns06p");
|
||||||
|
payChannelDO.setConfig(config);
|
||||||
|
|
||||||
|
// 序列化
|
||||||
|
String text = JsonUtils.toJsonString(payChannelDO);
|
||||||
|
System.out.println(text);
|
||||||
|
|
||||||
|
// 反序列化
|
||||||
|
payChannelDO = JsonUtils.parseObject(text, PayChannelDO.class);
|
||||||
|
System.out.println(payChannelDO.getConfig().getClass());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -0,0 +1,52 @@
|
|||||||
|
package cn.iocoder.yudao.coreservice.modules.pay.service.order;
|
||||||
|
|
||||||
|
import cn.iocoder.yudao.coreservice.BaseDbIntegrationTest;
|
||||||
|
import cn.iocoder.yudao.coreservice.modules.pay.service.merchant.impl.PayAppCoreServiceImpl;
|
||||||
|
import cn.iocoder.yudao.coreservice.modules.pay.service.merchant.impl.PayChannelCoreServiceImpl;
|
||||||
|
import cn.iocoder.yudao.coreservice.modules.pay.service.order.dto.PayOrderCreateReqDTO;
|
||||||
|
import cn.iocoder.yudao.coreservice.modules.pay.service.order.dto.PayOrderSubmitReqDTO;
|
||||||
|
import cn.iocoder.yudao.coreservice.modules.pay.service.order.impl.PayOrderCoreServiceImpl;
|
||||||
|
import cn.iocoder.yudao.framework.common.util.date.DateUtils;
|
||||||
|
import cn.iocoder.yudao.framework.pay.config.YudaoPayAutoConfiguration;
|
||||||
|
import cn.iocoder.yudao.framework.pay.core.enums.PayChannelEnum;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.springframework.context.annotation.Import;
|
||||||
|
|
||||||
|
import javax.annotation.Resource;
|
||||||
|
import java.time.Duration;
|
||||||
|
|
||||||
|
@Import({PayOrderCoreServiceImpl.class, PayAppCoreServiceImpl.class,
|
||||||
|
PayChannelCoreServiceImpl.class, YudaoPayAutoConfiguration.class})
|
||||||
|
public class PayOrderCoreServiceTest extends BaseDbIntegrationTest {
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private PayOrderCoreService payOrderCoreService;
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testCreatePayOrder() {
|
||||||
|
// 构造请求
|
||||||
|
PayOrderCreateReqDTO reqDTO = new PayOrderCreateReqDTO();
|
||||||
|
reqDTO.setAppId(6L);
|
||||||
|
reqDTO.setUserIp("127.0.0.1");
|
||||||
|
reqDTO.setMerchantOrderId(String.valueOf(System.currentTimeMillis()));
|
||||||
|
reqDTO.setSubject("标题");
|
||||||
|
reqDTO.setBody("内容");
|
||||||
|
reqDTO.setAmount(100);
|
||||||
|
reqDTO.setExpireTime(DateUtils.addTime(Duration.ofDays(1)));
|
||||||
|
// 发起请求
|
||||||
|
payOrderCoreService.createPayOrder(reqDTO);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSubmitPayOrder() {
|
||||||
|
// 构造请求
|
||||||
|
PayOrderSubmitReqDTO reqDTO = new PayOrderSubmitReqDTO();
|
||||||
|
reqDTO.setId(10L);
|
||||||
|
reqDTO.setAppId(6L);
|
||||||
|
reqDTO.setChannelCode(PayChannelEnum.WX_PUB.getCode());
|
||||||
|
reqDTO.setUserIp("127.0.0.1");
|
||||||
|
// 发起请求
|
||||||
|
payOrderCoreService.submitPayOrder(reqDTO);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -0,0 +1 @@
|
|||||||
|
package cn.iocoder.yudao.coreservice.modules.pay.service;
|
||||||
@ -0,0 +1,76 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
|
<parent>
|
||||||
|
<artifactId>yudao-framework</artifactId>
|
||||||
|
<groupId>cn.iocoder.boot</groupId>
|
||||||
|
<version>${revision}</version>
|
||||||
|
</parent>
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
||||||
|
<artifactId>yudao-spring-boot-starter-biz-pay</artifactId>
|
||||||
|
<name>${artifactId}</name>
|
||||||
|
<description>支付拓展,接入国内多个支付渠道
|
||||||
|
1. 支付宝,基于官方 SDK 接入
|
||||||
|
2. 微信支付,基于 weixin-java-pay 接入
|
||||||
|
</description>
|
||||||
|
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>cn.iocoder.boot</groupId>
|
||||||
|
<artifactId>yudao-common</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!-- Spring 核心 -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!-- 工具类相关 -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>jakarta.validation</groupId>
|
||||||
|
<artifactId>jakarta.validation-api</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.hibernate.validator</groupId>
|
||||||
|
<artifactId>hibernate-validator</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.slf4j</groupId>
|
||||||
|
<artifactId>slf4j-api</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.fasterxml.jackson.core</groupId>
|
||||||
|
<artifactId>jackson-databind</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.fasterxml.jackson.core</groupId>
|
||||||
|
<artifactId>jackson-core</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!-- 三方云服务相关 -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.alipay.sdk</groupId>
|
||||||
|
<artifactId>alipay-sdk-java</artifactId>
|
||||||
|
<version>4.17.9.ALL</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.github.binarywang</groupId>
|
||||||
|
<artifactId>weixin-java-pay</artifactId>
|
||||||
|
<version>4.1.9.B</version>
|
||||||
|
</dependency>
|
||||||
|
<!-- TODO 芋艿:清理 -->
|
||||||
|
|
||||||
|
<!-- Test 测试相关 -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>cn.iocoder.boot</groupId>
|
||||||
|
<artifactId>yudao-spring-boot-starter-test</artifactId>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
|
||||||
|
</project>
|
||||||
@ -0,0 +1,22 @@
|
|||||||
|
package cn.iocoder.yudao.framework.pay.config;
|
||||||
|
|
||||||
|
import cn.iocoder.yudao.framework.pay.core.client.PayClientFactory;
|
||||||
|
import cn.iocoder.yudao.framework.pay.core.client.impl.PayClientFactoryImpl;
|
||||||
|
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 支付配置类
|
||||||
|
*
|
||||||
|
* @author 芋道源码
|
||||||
|
*/
|
||||||
|
@EnableConfigurationProperties(PayProperties.class)
|
||||||
|
public class YudaoPayAutoConfiguration {
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public PayClientFactory payClientFactory() {
|
||||||
|
return new PayClientFactoryImpl();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -0,0 +1,28 @@
|
|||||||
|
package cn.iocoder.yudao.framework.pay.core.client;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 支付客户端的工厂接口
|
||||||
|
*
|
||||||
|
* @author 芋道源码
|
||||||
|
*/
|
||||||
|
public interface PayClientFactory {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获得支付客户端
|
||||||
|
*
|
||||||
|
* @param channelId 渠道编号
|
||||||
|
* @return 支付客户端
|
||||||
|
*/
|
||||||
|
PayClient getPayClient(Long channelId);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建支付客户端
|
||||||
|
*
|
||||||
|
* @param channelId 渠道编号
|
||||||
|
* @param channelCode 渠道编码
|
||||||
|
* @param config 支付配置
|
||||||
|
*/
|
||||||
|
<Config extends PayClientConfig> void createOrUpdatePayClient(Long channelId, String channelCode,
|
||||||
|
Config config);
|
||||||
|
|
||||||
|
}
|
||||||
@ -0,0 +1,24 @@
|
|||||||
|
package cn.iocoder.yudao.framework.pay.core.client.impl.alipay;
|
||||||
|
|
||||||
|
import cn.iocoder.yudao.framework.common.exception.ErrorCode;
|
||||||
|
import cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants;
|
||||||
|
import cn.iocoder.yudao.framework.pay.core.client.AbstractPayCodeMapping;
|
||||||
|
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 支付宝的 PayCodeMapping 实现类
|
||||||
|
*
|
||||||
|
* @author 芋道源码
|
||||||
|
*/
|
||||||
|
public class AlipayPayCodeMapping extends AbstractPayCodeMapping {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected ErrorCode apply0(String apiCode, String apiMsg) {
|
||||||
|
if (Objects.equals(apiCode, "10000")) {
|
||||||
|
return GlobalErrorCodeConstants.SUCCESS;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -0,0 +1,41 @@
|
|||||||
|
package cn.iocoder.yudao.framework.pay.core.enums;
|
||||||
|
|
||||||
|
import cn.hutool.core.util.ArrayUtil;
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Getter;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 支付渠道的编码的枚举
|
||||||
|
* 枚举值
|
||||||
|
*
|
||||||
|
* @author 芋道源码
|
||||||
|
*/
|
||||||
|
@Getter
|
||||||
|
@AllArgsConstructor
|
||||||
|
public enum PayChannelEnum {
|
||||||
|
|
||||||
|
WX_PUB("wx_pub", "微信 JSAPI 支付"), // 公众号的网页
|
||||||
|
WX_LITE("wx_lit","微信小程序支付"),
|
||||||
|
WX_APP("wx_app", "微信 App 支付"),
|
||||||
|
|
||||||
|
ALIPAY_PC("alipay_pc", "支付宝 PC 网站支付"),
|
||||||
|
ALIPAY_WAP("alipay_wap", "支付宝 Wap 网站支付"),
|
||||||
|
ALIPAY_APP("alipay_app", "支付宝App 支付"),
|
||||||
|
ALIPAY_QR("alipay_qr", "支付宝扫码支付");
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 编码
|
||||||
|
*
|
||||||
|
* 参考 https://www.pingxx.com/api/支付渠道属性值.html
|
||||||
|
*/
|
||||||
|
private String code;
|
||||||
|
/**
|
||||||
|
* 名字
|
||||||
|
*/
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
public static PayChannelEnum getByCode(String code) {
|
||||||
|
return ArrayUtil.firstMatch(o -> o.getCode().equals(code), values());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -0,0 +1,2 @@
|
|||||||
|
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
|
||||||
|
cn.iocoder.yudao.framework.pay.config.YudaoPayAutoConfiguration
|
||||||
@ -0,0 +1,129 @@
|
|||||||
|
package cn.iocoder.yudao.framework.core.client.impl;
|
||||||
|
|
||||||
|
import cn.hutool.core.io.IoUtil;
|
||||||
|
import cn.hutool.core.util.RandomUtil;
|
||||||
|
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
|
||||||
|
import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
|
||||||
|
import cn.iocoder.yudao.framework.pay.core.client.PayClient;
|
||||||
|
import cn.iocoder.yudao.framework.pay.core.client.dto.PayOrderUnifiedReqDTO;
|
||||||
|
import cn.iocoder.yudao.framework.pay.core.client.impl.PayClientFactoryImpl;
|
||||||
|
import cn.iocoder.yudao.framework.pay.core.client.impl.alipay.AlipayPayClientConfig;
|
||||||
|
import cn.iocoder.yudao.framework.pay.core.client.impl.alipay.AlipayQrPayClient;
|
||||||
|
import cn.iocoder.yudao.framework.pay.core.client.impl.alipay.AlipayWapPayClient;
|
||||||
|
import cn.iocoder.yudao.framework.pay.core.client.impl.wx.WXPayClientConfig;
|
||||||
|
import cn.iocoder.yudao.framework.pay.core.client.impl.wx.WXPubPayClient;
|
||||||
|
import cn.iocoder.yudao.framework.pay.core.enums.PayChannelEnum;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import java.io.FileInputStream;
|
||||||
|
import java.io.FileNotFoundException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@link PayClientFactoryImpl} 的集成测试
|
||||||
|
*
|
||||||
|
* @author 芋道源码
|
||||||
|
*/
|
||||||
|
public class PayClientFactoryImplTest {
|
||||||
|
|
||||||
|
private final PayClientFactoryImpl payClientFactory = new PayClientFactoryImpl();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@link WXPubPayClient} 的 V2 版本
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void testCreatePayClient_WX_PUB_V2() {
|
||||||
|
// 创建配置
|
||||||
|
WXPayClientConfig config = new WXPayClientConfig();
|
||||||
|
config.setAppId("wx041349c6f39b268b");
|
||||||
|
config.setMchId("1545083881");
|
||||||
|
config.setApiVersion(WXPayClientConfig.API_VERSION_V2);
|
||||||
|
config.setMchKey("0alL64UDQdlCwiKZ73ib7ypaIjMns06p");
|
||||||
|
// 创建客户端
|
||||||
|
Long channelId = RandomUtil.randomLong();
|
||||||
|
payClientFactory.createOrUpdatePayClient(channelId, PayChannelEnum.WX_PUB.getCode(), config);
|
||||||
|
PayClient client = payClientFactory.getPayClient(channelId);
|
||||||
|
// 发起支付
|
||||||
|
PayOrderUnifiedReqDTO reqDTO = buildPayOrderUnifiedReqDTO();
|
||||||
|
CommonResult<?> result = client.unifiedOrder(reqDTO);
|
||||||
|
System.out.println(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@link WXPubPayClient} 的 V3 版本
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void testCreatePayClient_WX_PUB_V3() throws FileNotFoundException {
|
||||||
|
// 创建配置
|
||||||
|
WXPayClientConfig config = new WXPayClientConfig();
|
||||||
|
config.setAppId("wx041349c6f39b268b");
|
||||||
|
config.setMchId("1545083881");
|
||||||
|
config.setApiVersion(WXPayClientConfig.API_VERSION_V3);
|
||||||
|
config.setPrivateKeyContent(IoUtil.readUtf8(new FileInputStream("/Users/yunai/Downloads/wx_pay/apiclient_key.pem")));
|
||||||
|
config.setPrivateCertContent(IoUtil.readUtf8(new FileInputStream("/Users/yunai/Downloads/wx_pay/apiclient_cert.pem")));
|
||||||
|
config.setApiV3Key("joerVi8y5DJ3o4ttA0o1uH47Xz1u2Ase");
|
||||||
|
// 创建客户端
|
||||||
|
Long channelId = RandomUtil.randomLong();
|
||||||
|
payClientFactory.createOrUpdatePayClient(channelId, PayChannelEnum.WX_PUB.getCode(), config);
|
||||||
|
PayClient client = payClientFactory.getPayClient(channelId);
|
||||||
|
// 发起支付
|
||||||
|
PayOrderUnifiedReqDTO reqDTO = buildPayOrderUnifiedReqDTO();
|
||||||
|
CommonResult<?> result = client.unifiedOrder(reqDTO);
|
||||||
|
System.out.println(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@link AlipayQrPayClient}
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void testCreatePayClient_ALIPAY_QR() {
|
||||||
|
// 创建配置
|
||||||
|
AlipayPayClientConfig config = new AlipayPayClientConfig();
|
||||||
|
config.setAppId("2021000118634035");
|
||||||
|
config.setServerUrl(AlipayPayClientConfig.SERVER_URL_SANDBOX);
|
||||||
|
config.setSignType(AlipayPayClientConfig.SIGN_TYPE_DEFAULT);
|
||||||
|
config.setPrivateKey("MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCHsEV1cDupwJv890x84qbppUtRIfhaKSwSVN0thCcsDCaAsGR5MZslDkO8NCT9V4r2SVXjyY7eJUZlZd1M0C8T01Tg4UOx5LUbic0O3A1uJMy6V1n9IyYwbAW3AEZhBd5bSbPgrqvmv3NeWSTQT6Anxnllf+2iDH6zyA2fPl7cYyQtbZoDJQFGqr4F+cGh2R6akzRKNoBkAeMYwoY6es2lX8sJxCVPWUmxNUoL3tScwlSpd7Bxw0q9c/X01jMwuQ0+Va358zgFiGERTE6yD01eu40OBDXOYO3z++y+TAYHlQQ2toMO63trepo88X3xV3R44/1DH+k2pAm2IF5ixiLrAgMBAAECggEAPx3SoXcseaD7rmcGcE0p4SMfbsUDdkUSmBBbtfF0GzwnqNLkWa+mgE0rWt9SmXngTQH97vByAYmLPl1s3G82ht1V7Sk7yQMe74lhFllr8eEyTjeVx3dTK1EEM4TwN+936DTXdFsr4TELJEcJJdD0KaxcCcfBLRDs2wnitEFZ9N+GoZybVmY8w0e0MI7PLObUZ2l0X4RurQnfG9ZxjXjC7PkeMVv7cGGylpNFi3BbvkRhdhLPDC2E6wqnr9e7zk+hiENivAezXrtxtwKovzCtnWJ1r0IO14Rh47H509Ic0wFnj+o5YyUL4LdmpL7yaaH6fM7zcSLFjNZPHvZCKPwYcQKBgQDQFho98QvnL8ex4v6cry4VitGpjSXm1qP3vmMQk4rTsn8iPWtcxPjqGEqOQJjdi4Mi0VZKQOLFwlH0kl95wNrD/isJ4O1yeYfX7YAXApzHqYNINzM79HemO3Yx1qLMW3okRFJ9pPRzbQ9qkTpsaegsmyX316zOBhzGRYjKbutTYwKBgQCm7phr9XdFW5Vh+XR90mVs483nrLmMiDKg7YKxSLJ8amiDjzPejCn7i95Hah08P+2MIZLIPbh2VLacczR6ltRRzN5bg5etFuqSgfkuHyxpoDmpjbe08+Q2h8JBYqcC5Nhv1AKU4iOUhVLHo/FBAQliMcGc/J3eiYTFC7EsNx382QKBgClb20doe7cttgFTXswBvaUmfFm45kmla924B7SpvrQpDD/f+VDtDZRp05fGmxuduSjYdtA3aVtpLiTwWu22OUUvZZqHDGruYOO4Hvdz23mL5b4ayqImCwoNU4bAZIc9v18p/UNf3/55NNE3oGcf/bev9rH2OjCQ4nM+Ktwhg8CFAoGACSgvbkShzUkv0ZcIf9ppu+ZnJh1AdGgINvGwaJ8vQ0nm/8h8NOoFZ4oNoGc+wU5Ubops7dUM6FjPR5e+OjdJ4E7Xp7d5O4J1TaIZlCEbo5OpdhaTDDcQvrkFu+Z4eN0qzj+YAKjDAOOrXc4tbr5q0FsgXscwtcNfaBuzFVTUrUkCgYEAwzPnMNhWG3zOWLUs2QFA2GP4Y+J8cpUYfj6pbKKzeLwyG9qBwF1NJpN8m+q9q7V9P2LY+9Lp9e1mGsGeqt5HMEA3P6vIpcqLJLqE/4PBLLRzfccTcmqb1m71+erxTRhHBRkGS+I7dZEb3olQfnS1Y1tpMBxiwYwR3LW4oXuJwj8=");
|
||||||
|
config.setAlipayPublicKey("MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAnq90KnF4dTnlzzmxpujbI05OYqi5WxAS6cL0gnZFv2gK51HExF8v/BaP7P979PhFMgWTqmOOI+Dtno5s+yD09XTY1WkshbLk6i4g2Xlr8fyW9ODnkU88RI2w9UdPhQU4cPPwBNlrsYhKkVK2OxwM3kFqjoBBY0CZoZCsSQ3LDH5WeZqPArlsS6xa2zqJBuuoKjMrdpELl3eXSjP8K54eDJCbeetCZNKWLL3DPahTPB7LZikfYmslb0QUvCgGapD0xkS7eVq70NaL1G57MWABs4tbfWgxike4Daj3EfUrzIVspQxj7w8HEj9WozJPgL88kSJSits0pqD3n5r8HSuseQIDAQAB");
|
||||||
|
// 创建客户端
|
||||||
|
Long channelId = RandomUtil.randomLong();
|
||||||
|
payClientFactory.createOrUpdatePayClient(channelId, PayChannelEnum.ALIPAY_QR.getCode(), config);
|
||||||
|
PayClient client = payClientFactory.getPayClient(channelId);
|
||||||
|
// 发起支付
|
||||||
|
PayOrderUnifiedReqDTO reqDTO = buildPayOrderUnifiedReqDTO();
|
||||||
|
CommonResult<?> result = client.unifiedOrder(reqDTO);
|
||||||
|
System.out.println(JsonUtils.toJsonString(result));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@link AlipayWapPayClient}
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void testCreatePayClient_ALIPAY_WAP() {
|
||||||
|
// 创建配置
|
||||||
|
AlipayPayClientConfig config = new AlipayPayClientConfig();
|
||||||
|
config.setAppId("2021000118634035");
|
||||||
|
config.setServerUrl(AlipayPayClientConfig.SERVER_URL_SANDBOX);
|
||||||
|
config.setSignType(AlipayPayClientConfig.SIGN_TYPE_DEFAULT);
|
||||||
|
config.setPrivateKey("MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCHsEV1cDupwJv890x84qbppUtRIfhaKSwSVN0thCcsDCaAsGR5MZslDkO8NCT9V4r2SVXjyY7eJUZlZd1M0C8T01Tg4UOx5LUbic0O3A1uJMy6V1n9IyYwbAW3AEZhBd5bSbPgrqvmv3NeWSTQT6Anxnllf+2iDH6zyA2fPl7cYyQtbZoDJQFGqr4F+cGh2R6akzRKNoBkAeMYwoY6es2lX8sJxCVPWUmxNUoL3tScwlSpd7Bxw0q9c/X01jMwuQ0+Va358zgFiGERTE6yD01eu40OBDXOYO3z++y+TAYHlQQ2toMO63trepo88X3xV3R44/1DH+k2pAm2IF5ixiLrAgMBAAECggEAPx3SoXcseaD7rmcGcE0p4SMfbsUDdkUSmBBbtfF0GzwnqNLkWa+mgE0rWt9SmXngTQH97vByAYmLPl1s3G82ht1V7Sk7yQMe74lhFllr8eEyTjeVx3dTK1EEM4TwN+936DTXdFsr4TELJEcJJdD0KaxcCcfBLRDs2wnitEFZ9N+GoZybVmY8w0e0MI7PLObUZ2l0X4RurQnfG9ZxjXjC7PkeMVv7cGGylpNFi3BbvkRhdhLPDC2E6wqnr9e7zk+hiENivAezXrtxtwKovzCtnWJ1r0IO14Rh47H509Ic0wFnj+o5YyUL4LdmpL7yaaH6fM7zcSLFjNZPHvZCKPwYcQKBgQDQFho98QvnL8ex4v6cry4VitGpjSXm1qP3vmMQk4rTsn8iPWtcxPjqGEqOQJjdi4Mi0VZKQOLFwlH0kl95wNrD/isJ4O1yeYfX7YAXApzHqYNINzM79HemO3Yx1qLMW3okRFJ9pPRzbQ9qkTpsaegsmyX316zOBhzGRYjKbutTYwKBgQCm7phr9XdFW5Vh+XR90mVs483nrLmMiDKg7YKxSLJ8amiDjzPejCn7i95Hah08P+2MIZLIPbh2VLacczR6ltRRzN5bg5etFuqSgfkuHyxpoDmpjbe08+Q2h8JBYqcC5Nhv1AKU4iOUhVLHo/FBAQliMcGc/J3eiYTFC7EsNx382QKBgClb20doe7cttgFTXswBvaUmfFm45kmla924B7SpvrQpDD/f+VDtDZRp05fGmxuduSjYdtA3aVtpLiTwWu22OUUvZZqHDGruYOO4Hvdz23mL5b4ayqImCwoNU4bAZIc9v18p/UNf3/55NNE3oGcf/bev9rH2OjCQ4nM+Ktwhg8CFAoGACSgvbkShzUkv0ZcIf9ppu+ZnJh1AdGgINvGwaJ8vQ0nm/8h8NOoFZ4oNoGc+wU5Ubops7dUM6FjPR5e+OjdJ4E7Xp7d5O4J1TaIZlCEbo5OpdhaTDDcQvrkFu+Z4eN0qzj+YAKjDAOOrXc4tbr5q0FsgXscwtcNfaBuzFVTUrUkCgYEAwzPnMNhWG3zOWLUs2QFA2GP4Y+J8cpUYfj6pbKKzeLwyG9qBwF1NJpN8m+q9q7V9P2LY+9Lp9e1mGsGeqt5HMEA3P6vIpcqLJLqE/4PBLLRzfccTcmqb1m71+erxTRhHBRkGS+I7dZEb3olQfnS1Y1tpMBxiwYwR3LW4oXuJwj8=");
|
||||||
|
config.setAlipayPublicKey("MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAnq90KnF4dTnlzzmxpujbI05OYqi5WxAS6cL0gnZFv2gK51HExF8v/BaP7P979PhFMgWTqmOOI+Dtno5s+yD09XTY1WkshbLk6i4g2Xlr8fyW9ODnkU88RI2w9UdPhQU4cPPwBNlrsYhKkVK2OxwM3kFqjoBBY0CZoZCsSQ3LDH5WeZqPArlsS6xa2zqJBuuoKjMrdpELl3eXSjP8K54eDJCbeetCZNKWLL3DPahTPB7LZikfYmslb0QUvCgGapD0xkS7eVq70NaL1G57MWABs4tbfWgxike4Daj3EfUrzIVspQxj7w8HEj9WozJPgL88kSJSits0pqD3n5r8HSuseQIDAQAB");
|
||||||
|
// 创建客户端
|
||||||
|
Long channelId = RandomUtil.randomLong();
|
||||||
|
payClientFactory.createOrUpdatePayClient(channelId, PayChannelEnum.ALIPAY_WAP.getCode(), config);
|
||||||
|
PayClient client = payClientFactory.getPayClient(channelId);
|
||||||
|
// 发起支付
|
||||||
|
PayOrderUnifiedReqDTO reqDTO = buildPayOrderUnifiedReqDTO();
|
||||||
|
CommonResult<?> result = client.unifiedOrder(reqDTO);
|
||||||
|
System.out.println(JsonUtils.toJsonString(result));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static PayOrderUnifiedReqDTO buildPayOrderUnifiedReqDTO() {
|
||||||
|
PayOrderUnifiedReqDTO reqDTO = new PayOrderUnifiedReqDTO();
|
||||||
|
reqDTO.setAmount(123L);
|
||||||
|
reqDTO.setSubject("IPhone 13");
|
||||||
|
reqDTO.setBody("biubiubiu");
|
||||||
|
reqDTO.setMerchantOrderId(String.valueOf(System.currentTimeMillis()));
|
||||||
|
reqDTO.setUserIp("127.0.0.1");
|
||||||
|
reqDTO.setNotifyUrl("http://127.0.0.1:8080");
|
||||||
|
return reqDTO;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue