mirror of
https://github.com/yin5980280/easy.git
synced 2026-09-03 07:26:58 +08:00
【U】新增支付拆安好相关代码
This commit is contained in:
@@ -58,5 +58,8 @@ spring boot starter,提供springcloud统一数据解析和异常传递。
|
||||
### easy-spring-boot-wr-separation
|
||||
该模块提供简单的读写分离
|
||||
|
||||
### easy-payment 聚合支付支付插件化代码
|
||||
该模块提供通用的支付插件化协议分发
|
||||
|
||||
## 升级日志
|
||||
升级日志 [https://github.com/yin5980280/easy/blob/develop/doc/update-log.md]
|
||||
|
||||
@@ -173,6 +173,12 @@
|
||||
<version>${revision}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>cn.org.easysite</groupId>
|
||||
<artifactId>easy-payment</artifactId>
|
||||
<version>${revision}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>cn.org.easysite</groupId>
|
||||
<artifactId>easy-maven-plugins</artifactId>
|
||||
|
||||
22
easy-payment/pom.xml
Normal file
22
easy-payment/pom.xml
Normal file
@@ -0,0 +1,22 @@
|
||||
<?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>easy-parent</artifactId>
|
||||
<groupId>cn.org.easysite</groupId>
|
||||
<version>${revision}</version>
|
||||
<relativePath>../easy-parent</relativePath>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<artifactId>easy-payment</artifactId>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>cn.org.easysite</groupId>
|
||||
<artifactId>easy-framework</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* @Copyright : Copyright (c) 2020
|
||||
* @Company : EasySite Technology 阿富汗 Co. Ltd.
|
||||
*/
|
||||
package cn.org.easysite.payment.plugin.exception;
|
||||
|
||||
import cn.org.easysite.framework.exception.ErrorCode;
|
||||
|
||||
/**
|
||||
* @author : 潘多拉
|
||||
* @version : 1.0
|
||||
* @date : 2020/12/28 3:38 下午
|
||||
* @link : cn.org.easysite.payment.plugin.exception.PaymentErrorCode
|
||||
*/
|
||||
public enum PaymentErrorCode implements ErrorCode {
|
||||
|
||||
/**
|
||||
* 支付网关未找到插件实现类
|
||||
*/
|
||||
PAYMENT_PAY_CHANNEL_PLUGIN_NOT_FOUND_ERROR("10005", "未找到该渠道的插件实现类"),
|
||||
|
||||
/**
|
||||
* 未在当前插件下找到该支付方式的实现
|
||||
*/
|
||||
PAYMENT_PAY_CHANNEL_PLUGIN_PAY_TYPE_NOT_FOUND_ERROR("10006", "未在当前插件下找到该支付方式的实现"),
|
||||
;
|
||||
|
||||
private String errorCode;
|
||||
|
||||
private String message;
|
||||
|
||||
PaymentErrorCode(String errorCode, String message) {
|
||||
this.errorCode = errorCode;
|
||||
this.message = message;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getErrorCode() {
|
||||
return this.errorCode;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getMessage() {
|
||||
return this.message;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
/*
|
||||
* @Copyright : Copyright (c) 2020
|
||||
* @Company : EasySite Technology 阿富汗 Co. Ltd.
|
||||
*/
|
||||
package cn.org.easysite.payment.plugin.exception;
|
||||
|
||||
|
||||
import cn.org.easysite.framework.exception.BaseException;
|
||||
import cn.org.easysite.framework.exception.ErrorCode;
|
||||
|
||||
/**
|
||||
* @author : 潘多拉
|
||||
* @version : 1.0
|
||||
* @date : 2020/12/27 12:58 下午
|
||||
* @link : cn.org.easysite.payment.plugin.exception.PaymentException
|
||||
*/
|
||||
public class PaymentException extends BaseException {
|
||||
|
||||
public PaymentException(ErrorCode errorCode) {
|
||||
super(errorCode);
|
||||
}
|
||||
public PaymentException(String code, Object[] args) {
|
||||
super("payment", code, args);
|
||||
}
|
||||
|
||||
public PaymentException(String defaultMessage) {
|
||||
super(defaultMessage);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,90 @@
|
||||
/*
|
||||
* @Copyright : Copyright (c) 2020
|
||||
* @Company : EasySite Technology 阿富汗 Co. Ltd.
|
||||
*/
|
||||
package cn.org.easysite.payment.plugin.factory;
|
||||
|
||||
import org.apache.commons.lang3.ClassUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import cn.org.easysite.payment.plugin.exception.PaymentException;
|
||||
import cn.org.easysite.payment.plugin.spi.annotation.Channel;
|
||||
import cn.org.easysite.payment.plugin.spi.annotation.Plugin;
|
||||
import cn.org.easysite.payment.plugin.spi.basic.PaymentPlugin;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
/**
|
||||
* @author : 潘多拉
|
||||
* @version : 1.0
|
||||
* @date : 2020/12/27 2:08 下午
|
||||
* @link : cn.org.easysite.payment.plugin.factory.PaymentFactory
|
||||
*/
|
||||
@Slf4j
|
||||
public class PaymentFactory {
|
||||
|
||||
private PaymentFactory() {}
|
||||
|
||||
private static final Map<Class<? extends PaymentPlugin>, Map<String, PaymentPlugin>> CONTAINER = new HashMap<>();
|
||||
|
||||
public static synchronized void register(PaymentPlugin paymentPlugin) {
|
||||
if (paymentPlugin == null) {
|
||||
throw new PaymentException("插件BEAN为空无法注册");
|
||||
}
|
||||
Class<? extends PaymentPlugin> type = getPluginType(paymentPlugin);
|
||||
String channelCode = getChannelCode(paymentPlugin);
|
||||
Map<String, PaymentPlugin> plugins = CONTAINER.computeIfAbsent(type, k -> new HashMap<>());
|
||||
if (plugins.get(channelCode) != null) {
|
||||
throw new IllegalArgumentException("该渠道CODE[" + channelCode + "]实现已被[" + plugins.get(channelCode).getClass().getName() + "]注册,请检查并修改@Channel值");
|
||||
}
|
||||
log.info("正在注册[{}]类型的插件,渠道渠道编码为[{}]", type.getName(), channelCode);
|
||||
plugins.put(channelCode, paymentPlugin);
|
||||
}
|
||||
|
||||
private static Class<? extends PaymentPlugin> getPluginType(PaymentPlugin paymentPlugin) {
|
||||
List<Class<?>> classes = ClassUtils.getAllInterfaces(paymentPlugin.getClass());
|
||||
if (classes == null || classes.isEmpty()) {
|
||||
throw new IllegalArgumentException("该渠道插件[" + paymentPlugin.getClass().getName() + "]实现有误");
|
||||
}
|
||||
for (Class<?> clazz : classes) {
|
||||
if (clazz.isAnnotationPresent(Plugin.class)) {
|
||||
Plugin plugin = clazz.getDeclaredAnnotation(Plugin.class);
|
||||
log.info("正在注册类型为[{}]的插件", plugin.value());
|
||||
return (Class<? extends PaymentPlugin>) clazz;
|
||||
}
|
||||
}
|
||||
throw new IllegalArgumentException("该渠道插件[" + paymentPlugin.getClass().getName() + "]的聚合插件接口未标示@Plugin");
|
||||
}
|
||||
|
||||
private static String getChannelCode(PaymentPlugin paymentPlugin) {
|
||||
Channel channel = paymentPlugin.getClass().getDeclaredAnnotation(Channel.class);
|
||||
if (channel == null || StringUtils.isEmpty(channel.value())) {
|
||||
log.warn("支付渠道实现配置错误,必须标注@PayChannel 并设置value, 未配置无法分发");
|
||||
}
|
||||
if (channel == null || StringUtils.isBlank(channel.value())) {
|
||||
throw new IllegalArgumentException("该渠道插件[" + paymentPlugin.getClass().getName() + "]未配置渠道值");
|
||||
}
|
||||
return channel.value();
|
||||
}
|
||||
|
||||
public static PaymentPlugin getPaymentPlugin(Class<? extends PaymentPlugin> clazz, String channelCode) {
|
||||
if (clazz == null) {
|
||||
throw new PaymentException("插件类型不能为空");
|
||||
}
|
||||
if (channelCode == null) {
|
||||
throw new PaymentException("渠道编码不能为空");
|
||||
}
|
||||
Map<String, PaymentPlugin> plugins = CONTAINER.get(clazz);
|
||||
if (plugins == null) {
|
||||
throw new PaymentException("该类型插件[" + clazz.getName() + "]尚未支持");
|
||||
}
|
||||
PaymentPlugin paymentPlugin = plugins.get(channelCode);
|
||||
if (paymentPlugin == null) {
|
||||
throw new PaymentException("该类型插件[" + clazz.getName() + "]下该渠道[" + channelCode + "]尚未支持");
|
||||
}
|
||||
return paymentPlugin;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
package cn.org.easysite.payment.plugin.spi.annotation;
|
||||
|
||||
import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* @author : yinlin
|
||||
* @version : 1.0
|
||||
* @date : 2019/7/23 7:00 PM
|
||||
* @Description : 支付渠道注解
|
||||
* @Copyright : Copyright (c) 2018
|
||||
* @Company : IOT Technology Chengdu Co. Ltd.
|
||||
* @link : cn.org.easysite.payment.plugin.spi.annotation.PayChannel
|
||||
*/
|
||||
@Target({ElementType.TYPE})
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Documented
|
||||
public @interface Channel {
|
||||
|
||||
/**
|
||||
* 支付渠道CODE 支付渠道在系统启动时需要获取CODE
|
||||
* @return
|
||||
*/
|
||||
String value() default "";
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
package cn.org.easysite.payment.plugin.spi.annotation;
|
||||
|
||||
import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* @author : yinlin
|
||||
* @version : 1.0
|
||||
* @date : 2019/7/23 7:00 PM
|
||||
* @Description : 支付方式注解
|
||||
* @Copyright : Copyright (c) 2018
|
||||
* @Company : IOT Technology Chengdu Co. Ltd.
|
||||
* @link : cn.org.easysite.payment.plugin.spi.annotation.PayType
|
||||
*/
|
||||
@Target({ElementType.METHOD, ElementType.TYPE})
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Documented
|
||||
public @interface PayType {
|
||||
|
||||
/**
|
||||
* 支付方式code
|
||||
* @return
|
||||
*/
|
||||
String value() default "";
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
package cn.org.easysite.payment.plugin.spi.annotation;
|
||||
|
||||
import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* @author : yinlin
|
||||
* @version : 1.0
|
||||
* @date : 2019/7/23 7:00 PM
|
||||
* @Description : 支付渠道注解
|
||||
* @Copyright : Copyright (c) 2018
|
||||
* @Company : IOT Technology Chengdu Co. Ltd.
|
||||
* @link : cn.org.easysite.payment.plugin.spi.annotation.PayChannel
|
||||
*/
|
||||
@Target({ElementType.TYPE})
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Documented
|
||||
public @interface Plugin {
|
||||
|
||||
/**
|
||||
* 支付渠道在系统启动时需要获取CODE
|
||||
* @return
|
||||
*/
|
||||
String value() default "";
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
package cn.org.easysite.payment.plugin.spi.basic;
|
||||
|
||||
import cn.org.easysite.payment.plugin.spi.model.base.parameter.PayeePayParameter;
|
||||
import cn.org.easysite.payment.plugin.spi.model.base.result.PaymentBaseResult;
|
||||
|
||||
/**
|
||||
* 回调通知基础插件类
|
||||
* @author yinlin
|
||||
*/
|
||||
public interface NotifyPlugin<T extends PayeePayParameter, E extends PaymentBaseResult> extends PaymentPlugin {
|
||||
|
||||
/**
|
||||
* 回调抽象接口
|
||||
* @param notify 回调参数实体
|
||||
* @return 返回回调解析结果
|
||||
*/
|
||||
E notify(T notify);
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
package cn.org.easysite.payment.plugin.spi.basic;
|
||||
|
||||
/**
|
||||
* 支付插件基础类
|
||||
* @author yinlin
|
||||
*/
|
||||
public interface PaymentPlugin {
|
||||
|
||||
/**
|
||||
* 插件初始化
|
||||
*/
|
||||
void init();
|
||||
|
||||
/**
|
||||
* 当前插件是否可用,留给接口或者插件实现类自己实现
|
||||
* @return true 可用 false 不可用
|
||||
*/
|
||||
boolean usable();
|
||||
|
||||
/**
|
||||
* 插件销毁
|
||||
*/
|
||||
void destroy();
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
package cn.org.easysite.payment.plugin.spi.basic;
|
||||
|
||||
import cn.org.easysite.payment.plugin.spi.model.base.parameter.PayeePayParameter;
|
||||
import cn.org.easysite.payment.plugin.spi.model.base.result.PaymentBaseResult;
|
||||
|
||||
/**
|
||||
* 支付查询基础类
|
||||
* @author yinlin
|
||||
*/
|
||||
public interface QueryPlugin<T extends PayeePayParameter, E extends PaymentBaseResult> extends PaymentPlugin {
|
||||
|
||||
/**
|
||||
* 查询第三方订单状态
|
||||
* @param parameter 支付参数
|
||||
* @return 返回三方订单详情
|
||||
*/
|
||||
E query(T parameter);
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* @Copyright : Copyright (c) 2020
|
||||
* @Company : EasySite Technology 阿富汗 Co. Ltd.
|
||||
*/
|
||||
package cn.org.easysite.payment.plugin.spi.model.base.parameter;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import cn.org.easysite.commons.base.BaseObject;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
/**
|
||||
* @author : 潘多拉
|
||||
* @version : 1.0
|
||||
* @date : 2020/12/27 12:02 下午
|
||||
* @link : cn.org.easysite.payment.plugin.spi.model.base.parameter.BasePayParameter
|
||||
*/
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
public class BasePayParameter extends BaseObject {
|
||||
/**
|
||||
* 支付渠道编码
|
||||
*/
|
||||
private String payChannel;
|
||||
|
||||
/**
|
||||
* 支付机构编码
|
||||
*/
|
||||
private String payOrg;
|
||||
|
||||
/**
|
||||
* 支付类型编码
|
||||
*/
|
||||
private String payType;
|
||||
|
||||
/**
|
||||
* 支付参数扩展参数
|
||||
*/
|
||||
private Map<String, Object> basePayExtraParams;
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
package cn.org.easysite.payment.plugin.spi.model.base.parameter;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
/**
|
||||
* @author : 盘达天神
|
||||
* @version : 1.0
|
||||
* @date : 2020/2/14 9:22 上午
|
||||
* @Description :
|
||||
* @Copyright : Copyright (c) 2019
|
||||
* @Company : EasySite Technology 阿富汗 Co. Ltd.
|
||||
* @link : cn.org.easysite.payment.plugin.spi.model.PaymentPayeeParameter
|
||||
*/
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
public class PayeePayParameter extends BasePayParameter {
|
||||
|
||||
/**
|
||||
* 支付渠道商户支付参数JSON字符串,需要使用方自己解析
|
||||
*/
|
||||
private Map<String, String> payeeParams;
|
||||
|
||||
/**
|
||||
* 支付参数扩展参数
|
||||
*/
|
||||
private Map<String, Object> payeeExtraParams;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
package cn.org.easysite.payment.plugin.spi.model.base.result;
|
||||
|
||||
import cn.org.easysite.commons.base.BaseObject;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* @author : 盘达天神
|
||||
* @version : 1.0
|
||||
* @date : 2020/2/14 10:19 上午
|
||||
* @Description :
|
||||
* @Copyright : Copyright (c) 2019
|
||||
* @Company : EasySite Technology 阿富汗 Co. Ltd.
|
||||
* @link : cn.org.easysite.payment.plugin.spi.model.base.result.PaymentBaseResult
|
||||
*/
|
||||
@Data
|
||||
public class PaymentBaseResult extends BaseObject {
|
||||
|
||||
/**
|
||||
* 状态描述啥的
|
||||
*/
|
||||
private String message;
|
||||
|
||||
/**
|
||||
* 请求是否成功
|
||||
*/
|
||||
private Boolean success;
|
||||
|
||||
protected String code;
|
||||
|
||||
/**
|
||||
* 回调通知返回CODE
|
||||
*/
|
||||
private String notifyReturnCode = "SUCCESS";
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* @Copyright : Copyright (c) 2020
|
||||
* @Company : EasySite Technology 阿富汗 Co. Ltd.
|
||||
*/
|
||||
package cn.org.easysite.payment.plugin.spi.model.pay.parameter;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
/**
|
||||
* @author : 潘多拉
|
||||
* @version : 1.0
|
||||
* @date : 2020/12/27 10:53 上午
|
||||
* @link : cn.org.easysite.payment.plugin.spi.model.pay.parameter.PaymentPayNotifyParameter
|
||||
*/
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
public class NotifyPayParameter extends PayParameter {
|
||||
|
||||
/**
|
||||
* 通知回调HttpServletRequest.getHeaderMap 所有回调参数map
|
||||
*/
|
||||
private Map<String, String> notifyRequestHeaderMap;
|
||||
|
||||
/**
|
||||
* 通知回调HttpServletRequest.getRequestParam 所有回调参数map
|
||||
*/
|
||||
private Map<String, String> notifyRequestMap;
|
||||
|
||||
/**
|
||||
* 通知回调请求body的字符串,有的回调可能是流也需要转成String
|
||||
*/
|
||||
private String notifyRequestBody;
|
||||
|
||||
/**
|
||||
* 退款扩展参数
|
||||
*/
|
||||
private Map<String, Object> refundExtraParams;
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
/*
|
||||
* @Copyright : Copyright (c) 2020
|
||||
* @Company : EasySite Technology 阿富汗 Co. Ltd.
|
||||
*/
|
||||
package cn.org.easysite.payment.plugin.spi.model.pay.parameter;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Map;
|
||||
|
||||
import cn.org.easysite.payment.plugin.spi.model.base.parameter.PayeePayParameter;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
/**
|
||||
* @author : 潘多拉
|
||||
* @version : 1.0
|
||||
* @date : 2020/12/27 10:51 上午
|
||||
* @link : cn.org.easysite.payment.plugin.spi.model.pay.parameter.PaymentPayParameter
|
||||
*/
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
public class PayParameter extends PayeePayParameter {
|
||||
/**
|
||||
* 系统支付单号
|
||||
*/
|
||||
private String payOrderNo;
|
||||
|
||||
/**
|
||||
* 第三方交易编号(查询时传递)
|
||||
*/
|
||||
private String orgPayTransactionNo;
|
||||
|
||||
/**
|
||||
* 渠道方支付单号
|
||||
*/
|
||||
private String channelPayOrderNo;
|
||||
|
||||
/**
|
||||
* 商品名称
|
||||
**/
|
||||
private String productName;
|
||||
|
||||
/**
|
||||
* 商品描述
|
||||
**/
|
||||
private String productDesc;
|
||||
|
||||
/**
|
||||
* 订单描述
|
||||
*/
|
||||
private String orderDesc;
|
||||
|
||||
/**
|
||||
* 客户被扫时展示的付款码转换的支付编号
|
||||
*/
|
||||
private String authCode;
|
||||
|
||||
/**
|
||||
* 支付金额
|
||||
*/
|
||||
private BigDecimal payAmount;
|
||||
|
||||
/**
|
||||
* 客户端Ip地址
|
||||
*/
|
||||
private String ipAddress;
|
||||
|
||||
/**
|
||||
* 支付用户编号 支付宝buyerId 微信对应openid 出款方payer
|
||||
*/
|
||||
private String payerOpenid;
|
||||
|
||||
/**
|
||||
* 币种,默认CNY
|
||||
*/
|
||||
private String currency = "CNY";
|
||||
|
||||
/**
|
||||
* 支付扩展参数
|
||||
*/
|
||||
private Map<String, Object> payExtraParams;
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
package cn.org.easysite.payment.plugin.spi.model.pay.result;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import cn.org.easysite.payment.plugin.spi.model.base.result.PaymentBaseResult;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.ToString;
|
||||
|
||||
/**
|
||||
* @author : yinlin
|
||||
* @version : 1.0
|
||||
* @date : 2019/7/23 4:36 PM
|
||||
* @Description : 预下单/一码付返回
|
||||
* @Copyright : Copyright (c) 2018
|
||||
* @Company : IOT Technology Chengdu Co. Ltd.
|
||||
* @link : cn.org.easysite.payment.plugin.spi.model.pay.result.PaymentPrepayResultPayment
|
||||
*/
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
@Builder
|
||||
@ToString
|
||||
public class PayPreCreateResult extends PaymentBaseResult {
|
||||
|
||||
private Boolean form;
|
||||
|
||||
private Boolean qrcode;
|
||||
|
||||
/**
|
||||
* 支付渠道方订单号
|
||||
*/
|
||||
private String channelPayOrderNo;
|
||||
|
||||
/**
|
||||
* 参数
|
||||
*/
|
||||
private Map<String, String> prepayParam;
|
||||
|
||||
/**
|
||||
* 原生二维码CODE字符串
|
||||
*/
|
||||
private String qrcodeUrl;
|
||||
|
||||
/**
|
||||
* FORM
|
||||
*/
|
||||
private String formBody;
|
||||
}
|
||||
@@ -0,0 +1,109 @@
|
||||
package cn.org.easysite.payment.plugin.spi.model.pay.result;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import cn.org.easysite.payment.plugin.spi.model.base.result.PaymentBaseResult;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
/**
|
||||
* @author : 盘达天神
|
||||
* @version : 1.0
|
||||
* @date : 2020/2/14 10:19 上午
|
||||
* @Description :
|
||||
* @Copyright : Copyright (c) 2019
|
||||
* @Company : EasySite Technology 阿富汗 Co. Ltd.
|
||||
* @link : cn.org.easysite.payment.plugin.spi.model.pay.result.PaymentPayResult
|
||||
*/
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
@Builder
|
||||
public class PayResult extends PaymentBaseResult {
|
||||
/**
|
||||
* 支付状态:1未支付,3支付成功,9支付失败
|
||||
*/
|
||||
private Integer payStatus;
|
||||
/**
|
||||
* 支付单号
|
||||
*/
|
||||
private String payOrderNo;
|
||||
|
||||
/**
|
||||
* 支付时间
|
||||
*/
|
||||
private Date payTime;
|
||||
|
||||
/**
|
||||
* 支付平台交易号(支付宝/微信返回)
|
||||
*/
|
||||
private String orgTransactionNo;
|
||||
|
||||
/**
|
||||
* 支付渠道支付单号
|
||||
*/
|
||||
private String channelPayOrderNo;
|
||||
|
||||
/**
|
||||
* 支付金额 单位(元)
|
||||
*/
|
||||
private BigDecimal payAmount;
|
||||
|
||||
/**
|
||||
* 实际支付金额 单位(元)
|
||||
*/
|
||||
private BigDecimal actual;
|
||||
|
||||
/**
|
||||
* 支付平台减免金额 单位(元)
|
||||
*/
|
||||
private BigDecimal coupon;
|
||||
|
||||
/**
|
||||
* 商户实际收款金额 单位(元)
|
||||
*/
|
||||
private BigDecimal receipt;
|
||||
|
||||
/**
|
||||
* 手续费 单位(元)
|
||||
*/
|
||||
private BigDecimal serviceFee;
|
||||
|
||||
/**
|
||||
* 收款账户(暂时没用)
|
||||
*/
|
||||
private String payeeAccount;
|
||||
|
||||
/**
|
||||
* 第三方平台卖家用户id
|
||||
*/
|
||||
private String payeeOpenid;
|
||||
|
||||
/**
|
||||
* 第三方平台用户id,支付宝16位,微信128位
|
||||
*/
|
||||
private String payerOpenid;
|
||||
|
||||
/**
|
||||
* 支付账号
|
||||
*/
|
||||
private String payerAccount;
|
||||
|
||||
/**
|
||||
* 第三方优惠信息列表
|
||||
*/
|
||||
private List<PayThirdCouponResult> coupons;
|
||||
|
||||
/**
|
||||
* 第三方货币单位
|
||||
*/
|
||||
private String thirdCurrency;
|
||||
|
||||
private Map<String, Object> payResultExtraParams;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
package cn.org.easysite.payment.plugin.spi.model.pay.result;
|
||||
|
||||
import cn.org.easysite.payment.plugin.spi.model.base.result.PaymentBaseResult;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
/**
|
||||
* @author : yinlin
|
||||
* @version : 1.0
|
||||
* @date : 2019/7/23 4:41 PM
|
||||
* @Description : 用户被扫/查询/notify返回支付结果
|
||||
* @Copyright : Copyright (c) 2018
|
||||
* @Company : IOT Technology Chengdu Co. Ltd.
|
||||
* @link : cn.org.easysite.payment.plugin.spi.model.pay.result.PaymentThirdCouponResultPayment
|
||||
*/
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class PayThirdCouponResult extends PaymentBaseResult {
|
||||
|
||||
/**
|
||||
* 优惠券编码
|
||||
*/
|
||||
private String code;
|
||||
|
||||
/**
|
||||
* 优惠券名称
|
||||
*/
|
||||
private String name;
|
||||
|
||||
/**
|
||||
* 优惠券类型
|
||||
*/
|
||||
private String type;
|
||||
/**
|
||||
* 金额 单位(分)
|
||||
*/
|
||||
private Long amount;
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
package cn.org.easysite.payment.plugin.spi.model.refund.parameter;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
/**
|
||||
* @author : 盘达天神
|
||||
* @version : 1.0
|
||||
* @date : 2020/2/14 9:32 上午
|
||||
* @Description :
|
||||
* @Copyright : Copyright (c) 2019
|
||||
* @Company : EasySite Technology 阿富汗 Co. Ltd.
|
||||
* @link : cn.org.easysite.payment.plugin.spi.model.refund.parameter.RefundNotifyPayParameter
|
||||
*/
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
public class RefundNotifyPayParameter extends RefundPayParameter {
|
||||
/**
|
||||
* 通知回调HttpServletRequest.getRequestParam 所有回调参数map json
|
||||
*/
|
||||
private Map<String, String> notifyRequestMap;
|
||||
|
||||
/**
|
||||
* request headers
|
||||
*/
|
||||
private Map<String, String> requestHeaderMap;
|
||||
|
||||
/**
|
||||
* 通知回调请求body的字符串,有的回调可能是流也需要转成String
|
||||
*/
|
||||
private String notifyRequestBody;
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
/*
|
||||
* @Copyright : Copyright (c) 2020
|
||||
* @Company : EasySite Technology 阿富汗 Co. Ltd.
|
||||
*/
|
||||
package cn.org.easysite.payment.plugin.spi.model.refund.parameter;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Map;
|
||||
|
||||
import cn.org.easysite.payment.plugin.spi.model.pay.parameter.PayParameter;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
/**
|
||||
* @author : 潘多拉
|
||||
* @version : 1.0
|
||||
* @date : 2020/12/27 10:55 上午
|
||||
* @link : cn.org.easysite.payment.plugin.spi.model.refund.parameter.PaymentPayRefundParameter
|
||||
*/
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
public class RefundPayParameter extends PayParameter {
|
||||
/**
|
||||
* 系统退款单号
|
||||
*/
|
||||
private String refundOrderNo;
|
||||
|
||||
/**
|
||||
* 渠道退款单号
|
||||
*/
|
||||
private String channelRefundOrderNo;
|
||||
|
||||
/**
|
||||
* 机构交易号
|
||||
*/
|
||||
private String orgRefundTransactionNo;
|
||||
|
||||
/**
|
||||
* 退款总额
|
||||
*/
|
||||
private BigDecimal refundAmount;
|
||||
|
||||
/**
|
||||
* 退款原因
|
||||
*/
|
||||
private String refundReason;
|
||||
|
||||
/**
|
||||
* 退款扩展参数
|
||||
*/
|
||||
private Map<String, Object> refundExtraParams;
|
||||
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
package cn.org.easysite.payment.plugin.spi.model.refund.result;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Date;
|
||||
|
||||
import cn.org.easysite.payment.plugin.spi.model.base.result.PaymentBaseResult;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
/**
|
||||
* @author : yinlin
|
||||
* @version : 1.0
|
||||
* @date : 2019/7/23 4:57 PM
|
||||
* @Description : 退款返回结果实体
|
||||
* @Copyright : Copyright (c) 2018
|
||||
* @Company : IOT Technology Chengdu Co. Ltd.
|
||||
* @link : cn.org.easysite.payment.plugin.spi.model.refund.result.PaymentRefundResult
|
||||
*/
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
@Builder
|
||||
public class PayRefundResult extends PaymentBaseResult {
|
||||
/**
|
||||
* 退款单号
|
||||
*/
|
||||
private String refundOrderNo;
|
||||
/**
|
||||
* 退款金额,单位分
|
||||
*/
|
||||
private BigDecimal refundAmount;
|
||||
|
||||
/**
|
||||
* 商户退款金额,单位分
|
||||
*/
|
||||
private BigDecimal actualRefundAmount;
|
||||
|
||||
private BigDecimal couponRefundAmount;
|
||||
|
||||
/**
|
||||
* 退款状态 1- 退款中 2-退款成功 3-退款失败
|
||||
*/
|
||||
private Integer refundStatus;
|
||||
|
||||
/**
|
||||
* 第三方退款单号
|
||||
*/
|
||||
private String channelRefundOrderNo;
|
||||
|
||||
/**
|
||||
* 退款时间
|
||||
*/
|
||||
private Date refundTime;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
package cn.org.easysite.payment.plugin.spi.pay;
|
||||
|
||||
import cn.org.easysite.payment.plugin.spi.annotation.Plugin;
|
||||
import cn.org.easysite.payment.plugin.spi.basic.NotifyPlugin;
|
||||
import cn.org.easysite.payment.plugin.spi.basic.QueryPlugin;
|
||||
import cn.org.easysite.payment.plugin.spi.model.pay.parameter.NotifyPayParameter;
|
||||
import cn.org.easysite.payment.plugin.spi.model.pay.parameter.PayParameter;
|
||||
import cn.org.easysite.payment.plugin.spi.model.pay.result.PayPreCreateResult;
|
||||
import cn.org.easysite.payment.plugin.spi.model.pay.result.PayResult;
|
||||
|
||||
/**
|
||||
* @author : yinlin
|
||||
* @version : 1.0
|
||||
* @date : 2019/7/23 3:38 PM
|
||||
* @Description :
|
||||
* @Copyright : Copyright (c) 2018
|
||||
* @Company : IOT Technology Chengdu Co. Ltd.
|
||||
* @link : cn.org.easysite.payment.plugin.spi.pay.PayAggregationPlugin
|
||||
*/
|
||||
@Plugin("支付")
|
||||
public interface PayAggregationPlugin extends
|
||||
PreCreatePlugin<PayParameter, PayPreCreateResult>,
|
||||
PayPlugin<PayParameter, PayResult>,
|
||||
QueryPlugin<PayParameter, PayResult>,
|
||||
NotifyPlugin<NotifyPayParameter, PayResult> {
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
package cn.org.easysite.payment.plugin.spi.pay;
|
||||
|
||||
import cn.org.easysite.payment.plugin.spi.basic.PaymentPlugin;
|
||||
import cn.org.easysite.payment.plugin.spi.model.base.parameter.PayeePayParameter;
|
||||
import cn.org.easysite.payment.plugin.spi.model.base.result.PaymentBaseResult;
|
||||
|
||||
/**
|
||||
* @author : 盘达天神
|
||||
* @version : 1.0
|
||||
* @date : 2020/2/14 11:17 上午
|
||||
* @Description :
|
||||
* @Copyright : Copyright (c) 2019
|
||||
* @Company : EasySite Technology 阿富汗 Co. Ltd.
|
||||
* @link : cn.org.easysite.payment.plugin.spi.payPayPlugin
|
||||
*/
|
||||
public interface PayPlugin<T extends PayeePayParameter, E extends PaymentBaseResult> extends PaymentPlugin {
|
||||
|
||||
/**
|
||||
* 支付被扫/刷卡等方法调用
|
||||
*
|
||||
* @param parameter
|
||||
* @return
|
||||
*/
|
||||
E pay(T parameter);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
package cn.org.easysite.payment.plugin.spi.pay;
|
||||
|
||||
import cn.org.easysite.payment.plugin.spi.basic.PaymentPlugin;
|
||||
import cn.org.easysite.payment.plugin.spi.model.base.parameter.PayeePayParameter;
|
||||
import cn.org.easysite.payment.plugin.spi.model.base.result.PaymentBaseResult;
|
||||
|
||||
/**
|
||||
* @author : 盘达天神
|
||||
* @version : 1.0
|
||||
* @date : 2020/2/14 10:35 上午
|
||||
* @Description :
|
||||
* @Copyright : Copyright (c) 2019
|
||||
* @Company : EasySite Technology 阿富汗 Co. Ltd.
|
||||
* @link : cn.org.easysite.payment.plugin.spi.pay.PreCreatePlugin
|
||||
*/
|
||||
public interface PreCreatePlugin<T extends PayeePayParameter, E extends PaymentBaseResult> extends PaymentPlugin {
|
||||
|
||||
/**
|
||||
* 下单模式
|
||||
* @param parameter
|
||||
* @return
|
||||
*/
|
||||
E preCreate(T parameter);
|
||||
}
|
||||
@@ -0,0 +1,145 @@
|
||||
package cn.org.easysite.payment.plugin.spi.pay.impl;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.core.annotation.AnnotationUtils;
|
||||
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
import javax.annotation.PostConstruct;
|
||||
import javax.annotation.PreDestroy;
|
||||
|
||||
import cn.org.easysite.framework.exception.BaseException;
|
||||
import cn.org.easysite.payment.plugin.exception.PaymentException;
|
||||
import cn.org.easysite.payment.plugin.spi.annotation.Channel;
|
||||
import cn.org.easysite.payment.plugin.spi.annotation.PayType;
|
||||
import cn.org.easysite.payment.plugin.spi.model.pay.parameter.PayParameter;
|
||||
import cn.org.easysite.payment.plugin.spi.model.pay.result.PayPreCreateResult;
|
||||
import cn.org.easysite.payment.plugin.spi.model.pay.result.PayResult;
|
||||
import cn.org.easysite.payment.plugin.spi.pay.PayAggregationPlugin;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import static cn.org.easysite.payment.plugin.exception.PaymentErrorCode.PAYMENT_PAY_CHANNEL_PLUGIN_PAY_TYPE_NOT_FOUND_ERROR;
|
||||
|
||||
/**
|
||||
* @author : yinlin
|
||||
* @version : 1.0
|
||||
* @date : 2019/7/23 3:35 PM
|
||||
* @Description : 支付正向流程抽象
|
||||
* @Copyright : Copyright (c) 2018
|
||||
* @Company : IOT Technology ChengDu Co. Ltd.
|
||||
* @link : cn.org.easysite.payment.plugin.spi.pay.impl.AbstractPaymentPayPlugin
|
||||
*/
|
||||
@Slf4j
|
||||
public abstract class AbstractPayAggregationPlugin implements PayAggregationPlugin {
|
||||
|
||||
|
||||
private String PAY_NOTIFY_URL_PATTERN = "pay/notify/{channelCode}/{payOrderNo}";
|
||||
|
||||
/**
|
||||
* 项目启动支付渠道插件bean启动时注册到factory中
|
||||
*/
|
||||
@PostConstruct
|
||||
@Override
|
||||
public void init() {
|
||||
String code = this.getChannelCode();
|
||||
//注册实现
|
||||
if (code != null && usable()) {
|
||||
// PaymentPayPluginFactory.register(code, this);
|
||||
log.info(getChannelCode());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public PayPreCreateResult preCreate(PayParameter parameter) {
|
||||
if (!this.usable()) {
|
||||
//抛出异常
|
||||
}
|
||||
Method method = this.findMethodByPayType(parameter.getPayType());
|
||||
try {
|
||||
return (PayPreCreateResult) method.invoke(this, parameter);
|
||||
} catch (IllegalAccessException e) {
|
||||
log.error("AbstractPayAggregationPlugin#preCreate(PayParameter) 该支付方式当前插件不支持=[{}]", parameter.getPayType());
|
||||
throw new PaymentException(PAYMENT_PAY_CHANNEL_PLUGIN_PAY_TYPE_NOT_FOUND_ERROR);
|
||||
} catch (InvocationTargetException e) {
|
||||
if (e.getTargetException() instanceof BaseException) {
|
||||
throw ((BaseException) e.getTargetException());
|
||||
}
|
||||
log.error("AbstractPayAggregationPlugin#preCreate(PayParameter) 执行出错,未捕获的异常[{}]", e.getTargetException().getMessage());
|
||||
throw new PaymentException(PAYMENT_PAY_CHANNEL_PLUGIN_PAY_TYPE_NOT_FOUND_ERROR);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public PayResult pay(PayParameter parameter) {
|
||||
if (!this.usable()) {
|
||||
//抛出异常
|
||||
}
|
||||
Method method = this.findMethodByPayType(parameter.getPayType());
|
||||
try {
|
||||
return (PayResult) method.invoke(this, parameter);
|
||||
} catch (IllegalAccessException e) {
|
||||
log.error("AbstractPayAggregationPlugin#pay(PayParameter) 该支付方式当前插件不支持=[{}]", parameter.getPayType());
|
||||
throw new PaymentException(PAYMENT_PAY_CHANNEL_PLUGIN_PAY_TYPE_NOT_FOUND_ERROR);
|
||||
} catch (InvocationTargetException e) {
|
||||
if (e.getTargetException() instanceof BaseException) {
|
||||
throw ((BaseException) e.getTargetException());
|
||||
}
|
||||
log.error("AbstractPayAggregationPlugin#pay(PayParameter) 执行出错,未捕获的异常{}", e.getTargetException().getMessage());
|
||||
throw new PaymentException(PAYMENT_PAY_CHANNEL_PLUGIN_PAY_TYPE_NOT_FOUND_ERROR);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 反射查找有注解 @PayType("PAY_MODE_SHOWCODE") 的方法 由于annotationUtils已经给我们做了缓存,该方法不做缓存处理
|
||||
* @param payType
|
||||
* @return
|
||||
*/
|
||||
private Method findMethodByPayType(String payType) {
|
||||
Method[] methods = this.getClass().getDeclaredMethods();
|
||||
for (Method method : methods) {
|
||||
PayType type = AnnotationUtils.findAnnotation(method, PayType.class);
|
||||
if (type != null && type.value().equalsIgnoreCase(payType)) {
|
||||
return method;
|
||||
}
|
||||
}
|
||||
log.error("AbstractPayAggregationPlugin#findMethodByPayType(String) 该支付方式[{}]当前插件不支持", payType);
|
||||
throw new PaymentException(PAYMENT_PAY_CHANNEL_PLUGIN_PAY_TYPE_NOT_FOUND_ERROR);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取支付渠道编码
|
||||
* @return 返回注册在支付渠道实现类上的channelCode也可以自定义
|
||||
*/
|
||||
protected String getChannelCode() {
|
||||
Channel channel = this.getClass().getDeclaredAnnotation(Channel.class);
|
||||
if (channel == null || StringUtils.isEmpty(channel.value())) {
|
||||
log.warn("支付渠道实现配置错误,必须标注@Channel 并设置value, 未配置无法分发");
|
||||
}
|
||||
return channel == null ? null : channel.value();
|
||||
}
|
||||
|
||||
/**
|
||||
* 表示该插件是否可用
|
||||
* @return
|
||||
*/
|
||||
@Override
|
||||
public boolean usable() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@PreDestroy
|
||||
@Override
|
||||
public void destroy() {
|
||||
log.warn("当前支付渠道[{}]支付插件被卸载", getChannelCode());
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取异步通知地址
|
||||
* @param payParameter 支付参数
|
||||
* @return 返回组装好的支付回调地址
|
||||
*/
|
||||
protected String getNotifyUrl(PayParameter payParameter) {
|
||||
return PAY_NOTIFY_URL_PATTERN.replace("{channelCode}", payParameter.getPayChannel()).replace("{payOrderNo}", payParameter.getPayOrderNo());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
package cn.org.easysite.payment.plugin.spi.refund;
|
||||
|
||||
import cn.org.easysite.payment.plugin.spi.annotation.Plugin;
|
||||
import cn.org.easysite.payment.plugin.spi.basic.NotifyPlugin;
|
||||
import cn.org.easysite.payment.plugin.spi.basic.QueryPlugin;
|
||||
import cn.org.easysite.payment.plugin.spi.model.refund.parameter.RefundNotifyPayParameter;
|
||||
import cn.org.easysite.payment.plugin.spi.model.refund.parameter.RefundPayParameter;
|
||||
import cn.org.easysite.payment.plugin.spi.model.refund.result.PayRefundResult;
|
||||
|
||||
/**
|
||||
* @author : yinlin
|
||||
* @version : 1.0
|
||||
* @date : 2019/7/23 3:39 PM
|
||||
* @Description : 支付退款插件集成接口
|
||||
* @Copyright : Copyright (c) 2018
|
||||
* @Company : IOT Technology Chengdu Co. Ltd.
|
||||
* @link : cn.org.easysite.payment.plugin.spi.refund.PayRefundAggregationPlugin
|
||||
*/
|
||||
@Plugin("退款")
|
||||
public interface PayRefundAggregationPlugin extends
|
||||
RefundPlugin<RefundPayParameter, PayRefundResult>,
|
||||
QueryPlugin<RefundPayParameter, PayRefundResult>,
|
||||
NotifyPlugin<RefundNotifyPayParameter, PayRefundResult> {
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
package cn.org.easysite.payment.plugin.spi.refund;
|
||||
|
||||
import cn.org.easysite.payment.plugin.spi.basic.PaymentPlugin;
|
||||
import cn.org.easysite.payment.plugin.spi.model.base.parameter.PayeePayParameter;
|
||||
import cn.org.easysite.payment.plugin.spi.model.base.result.PaymentBaseResult;
|
||||
|
||||
/**
|
||||
* 退款基础插件
|
||||
* @author yinlin
|
||||
*/
|
||||
public interface RefundPlugin<T extends PayeePayParameter, E extends PaymentBaseResult> extends PaymentPlugin {
|
||||
|
||||
/**
|
||||
* 支付退款方法
|
||||
* @param parameter 支付退款参数
|
||||
* @return 支付退款返回值
|
||||
*/
|
||||
E refund(T parameter);
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
/*
|
||||
* @Copyright : Copyright (c) 2020
|
||||
* @Company : EasySite Technology 阿富汗 Co. Ltd.
|
||||
*/
|
||||
package cn.org.easysite.payment.plugin.spi.refund.impl;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import javax.annotation.PostConstruct;
|
||||
import javax.annotation.PreDestroy;
|
||||
|
||||
import cn.org.easysite.payment.plugin.spi.annotation.Channel;
|
||||
import cn.org.easysite.payment.plugin.spi.refund.PayRefundAggregationPlugin;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
/**
|
||||
* @author : 潘多拉
|
||||
* @version : 1.0
|
||||
* @date : 2020/12/27 4:17 下午
|
||||
* @link : cn.org.easysite.payment.plugin.spi.refund.impl
|
||||
* .AbstractPayRefundAggregationPlugin
|
||||
*/
|
||||
@Slf4j
|
||||
public abstract class AbstractPayRefundAggregationPlugin implements PayRefundAggregationPlugin {
|
||||
/**
|
||||
* 项目启动支付渠道插件bean启动时注册到factory中
|
||||
*/
|
||||
@PostConstruct
|
||||
@Override
|
||||
public void init() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean usable() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@PreDestroy
|
||||
@Override
|
||||
public void destroy() {
|
||||
log.info("当前支付渠道[{}]退款插件被卸载", getChannelCode());
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取支付渠道编码
|
||||
* @return 返回注册在支付渠道实现类上的channelCode也可以自定义
|
||||
*/
|
||||
protected String getChannelCode() {
|
||||
Channel channel = this.getClass().getDeclaredAnnotation(Channel.class);
|
||||
if (channel == null || StringUtils.isEmpty(channel.value())) {
|
||||
log.warn("支付渠道实现配置错误,必须标注@Channel 并设置value, 未配置无法分发");
|
||||
}
|
||||
return channel == null ? null : channel.value();
|
||||
}
|
||||
|
||||
/**
|
||||
* 组装退款url
|
||||
* @param outTradeNo
|
||||
* @return
|
||||
*/
|
||||
protected String getNotifyUrl(String outTradeNo) {
|
||||
return "";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
/**
|
||||
* @author : 潘多拉
|
||||
* @date : 2020/12/27 3:16 下午
|
||||
* @version : 1.0
|
||||
* @link : cn.org.easysite.payment.plugin.support.package-info
|
||||
*/
|
||||
package cn.org.easysite.payment.plugin.support;
|
||||
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
* @Copyright : Copyright (c) 2020
|
||||
* @Company : EasySite Technology 阿富汗 Co. Ltd.
|
||||
*/
|
||||
package cn.org.easysite.payment.plugin.support.spring.annotation;
|
||||
|
||||
import org.springframework.context.annotation.Import;
|
||||
|
||||
import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
import cn.org.easysite.payment.plugin.support.spring.register.ChannelPluginsRegister;
|
||||
import cn.org.easysite.payment.plugin.support.spring.register.ChannelPluginsScannerRegister;
|
||||
|
||||
/**
|
||||
* @author : 潘多拉
|
||||
* @version : 1.0
|
||||
* @date : 2020/12/27 3:21 下午
|
||||
* @link : cn.org.easysite.payment.plugin.support.spring.annotation.EnableChannelPlugins
|
||||
*/
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.TYPE)
|
||||
@Documented
|
||||
@Import({ChannelPluginsScannerRegister.class, ChannelPluginsRegister.class})
|
||||
public @interface EnablePaymentPluginRegistries {
|
||||
|
||||
String[] basePackages() default {};
|
||||
|
||||
Class<?>[] basePackageClasses() default {};
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
/**
|
||||
* @author : 潘多拉
|
||||
* @date : 2020/12/27 3:20 下午
|
||||
* @version : 1.0
|
||||
* @link : cn.org.easysite.payment.plugin.support.spring.package-info
|
||||
*/
|
||||
package cn.org.easysite.payment.plugin.support.spring;
|
||||
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
* @Copyright : Copyright (c) 2020
|
||||
* @Company : EasySite Technology 阿富汗 Co. Ltd.
|
||||
*/
|
||||
package cn.org.easysite.payment.plugin.support.spring.register;
|
||||
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.ApplicationContextAware;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import cn.org.easysite.payment.plugin.factory.PaymentFactory;
|
||||
import cn.org.easysite.payment.plugin.spi.basic.PaymentPlugin;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
/**
|
||||
* @author : 潘多拉
|
||||
* @version : 1.0
|
||||
* @date : 2020/12/27 3:59 下午
|
||||
* @link : cn.org.easysite.payment.plugin.support.spring.register.ChannelPluginsRegister
|
||||
*/
|
||||
@Slf4j
|
||||
public class ChannelPluginsRegister implements ApplicationContextAware {
|
||||
@Override
|
||||
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
|
||||
Map<String, PaymentPlugin> map = applicationContext.getBeansOfType(PaymentPlugin.class);
|
||||
if (map.isEmpty()) {
|
||||
log.error("PaymentPlugin Spring Bean not found");
|
||||
return;
|
||||
}
|
||||
map.forEach((k, v) -> PaymentFactory.register(v));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,148 @@
|
||||
/*
|
||||
* @Copyright : Copyright (c) 2020
|
||||
* @Company : EasySite Technology 阿富汗 Co. Ltd.
|
||||
*/
|
||||
package cn.org.easysite.payment.plugin.support.spring.register;
|
||||
|
||||
import org.springframework.beans.factory.BeanClassLoaderAware;
|
||||
import org.springframework.beans.factory.annotation.AnnotatedBeanDefinition;
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
|
||||
import org.springframework.context.EnvironmentAware;
|
||||
import org.springframework.context.ResourceLoaderAware;
|
||||
import org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider;
|
||||
import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
|
||||
import org.springframework.core.env.Environment;
|
||||
import org.springframework.core.io.ResourceLoader;
|
||||
import org.springframework.core.type.AnnotationMetadata;
|
||||
import org.springframework.core.type.filter.AnnotationTypeFilter;
|
||||
import org.springframework.util.ClassUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import cn.org.easysite.payment.plugin.spi.annotation.Channel;
|
||||
import cn.org.easysite.payment.plugin.support.spring.annotation.EnablePaymentPluginRegistries;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
/**
|
||||
* @author : 潘多拉
|
||||
* @version : 1.0
|
||||
* @date : 2020/12/27 3:22 下午
|
||||
* @link : cn.org.easysite.payment.plugin.support.spring.register.ChannelPluginsScannerRegister
|
||||
*/
|
||||
@Slf4j
|
||||
public class ChannelPluginsScannerRegister implements ImportBeanDefinitionRegistrar, ResourceLoaderAware, BeanClassLoaderAware, EnvironmentAware {
|
||||
|
||||
private ResourceLoader resourceLoader;
|
||||
|
||||
private ClassLoader classLoader;
|
||||
|
||||
private Environment environment;
|
||||
|
||||
@Override
|
||||
public void setBeanClassLoader(ClassLoader classLoader) {
|
||||
this.classLoader = classLoader;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setEnvironment(Environment environment) {
|
||||
this.environment = environment;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setResourceLoader(ResourceLoader resourceLoader) {
|
||||
this.resourceLoader = resourceLoader;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
|
||||
|
||||
Set<String> basePackages = getBasePackages(importingClassMetadata);
|
||||
ClassPathScanningCandidateComponentProvider scanner = getScanner();
|
||||
scanner.setResourceLoader(this.resourceLoader);
|
||||
scanner.addIncludeFilter(new AnnotationTypeFilter(Channel.class));
|
||||
|
||||
for (String basePackage : basePackages) {
|
||||
Set<BeanDefinition> candidateComponents = scanner.findCandidateComponents(basePackage);
|
||||
for (BeanDefinition candidateComponent : candidateComponents) {
|
||||
if (candidateComponent instanceof AnnotatedBeanDefinition) {
|
||||
AnnotatedBeanDefinition beanDefinition = (AnnotatedBeanDefinition) candidateComponent;
|
||||
AnnotationMetadata annotationMetadata = beanDefinition.getMetadata();
|
||||
this.registerPluginBean(registry, annotationMetadata);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 注册bean
|
||||
* @param registry
|
||||
* @param annotationMetadata
|
||||
*/
|
||||
private void registerPluginBean(BeanDefinitionRegistry registry, AnnotationMetadata annotationMetadata) {
|
||||
try {
|
||||
String className = annotationMetadata.getClassName();
|
||||
Class<?> beanClazz = Class.forName(className);
|
||||
if (!beanClazz.isAnnotationPresent(Channel.class)) {
|
||||
throw new RuntimeException("@Channel is required!");
|
||||
}
|
||||
BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(beanClazz);
|
||||
BeanDefinition definition = builder.getRawBeanDefinition();
|
||||
registry.registerBeanDefinition(beanClazz.getSimpleName(), definition);
|
||||
} catch (ClassNotFoundException e) {
|
||||
log.error("Could not register target class: " + annotationMetadata.getClassName(), e);
|
||||
}
|
||||
}
|
||||
|
||||
protected Set<String> getBasePackages(AnnotationMetadata importingClassMetadata) {
|
||||
Map<String, Object> attributes = importingClassMetadata.getAnnotationAttributes(EnablePaymentPluginRegistries.class.getCanonicalName());
|
||||
|
||||
Set<String> basePackages = new HashSet<>();
|
||||
|
||||
for (String pkg : (String[]) attributes.get("basePackages")) {
|
||||
if (StringUtils.hasText(pkg)) {
|
||||
basePackages.add(pkg);
|
||||
}
|
||||
}
|
||||
|
||||
for (Class<?> clazz : (Class[]) attributes.get("basePackageClasses")) {
|
||||
basePackages.add(ClassUtils.getPackageName(clazz));
|
||||
}
|
||||
|
||||
if (basePackages.isEmpty()) {
|
||||
basePackages.add(ClassUtils.getPackageName(importingClassMetadata.getClassName()));
|
||||
}
|
||||
return basePackages;
|
||||
}
|
||||
|
||||
protected ClassPathScanningCandidateComponentProvider getScanner() {
|
||||
return new ClassPathScanningCandidateComponentProvider(false, this.environment) {
|
||||
|
||||
@Override
|
||||
protected boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition) {
|
||||
if (beanDefinition.getMetadata().isIndependent()) {
|
||||
if (
|
||||
beanDefinition.getMetadata().isInterface()
|
||||
&& beanDefinition.getMetadata().getInterfaceNames().length == 1
|
||||
&& Annotation.class.getName().equals(beanDefinition.getMetadata().getInterfaceNames()[0])) {
|
||||
try {
|
||||
Class<?> target = ClassUtils.forName(
|
||||
beanDefinition.getMetadata().getClassName(),
|
||||
ChannelPluginsScannerRegister.this.classLoader);
|
||||
return !target.isAnnotation();
|
||||
} catch (Exception ex) {
|
||||
this.logger.error("Could not load target class: " + beanDefinition.getMetadata().getClassName(), ex);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -25,5 +25,12 @@
|
||||
<groupId>org.aspectj</groupId>
|
||||
<artifactId>aspectjweaver</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-test</artifactId>
|
||||
<optional>true</optional>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
|
||||
@@ -1,11 +0,0 @@
|
||||
package cn.org.easysite;
|
||||
|
||||
/**
|
||||
* Unit test for simple App.
|
||||
*/
|
||||
public class AppTest
|
||||
{
|
||||
/**
|
||||
* Rigorous Test :-)
|
||||
*/
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
/*
|
||||
* @Copyright : Copyright (c) 2020
|
||||
* @Company : EasySite Technology 阿富汗 Co. Ltd.
|
||||
*/
|
||||
package cn.org.easysite.spring.boot.distributed.lock.test;
|
||||
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.context.annotation.EnableAspectJAutoProxy;
|
||||
|
||||
/**
|
||||
* @author : 潘多拉
|
||||
* @version : 1.0
|
||||
* @date : 2020/10/20 10:22 上午
|
||||
* @link : cn.org.easysite.spring.boot.distributed.lock.test.DistributedApplicationBootstrap
|
||||
*/
|
||||
@SpringBootApplication
|
||||
@EnableAspectJAutoProxy
|
||||
public class DistributedApplicationBootstrap {
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
/*
|
||||
* @Copyright : Copyright (c) 2020
|
||||
* @Company : EasySite Technology 阿富汗 Co. Ltd.
|
||||
*/
|
||||
package cn.org.easysite.spring.boot.distributed.lock.test;
|
||||
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.context.annotation.EnableAspectJAutoProxy;
|
||||
|
||||
/**
|
||||
* @author : 潘多拉
|
||||
* @version : 1.0
|
||||
* @date : 2020/10/20 10:22 上午
|
||||
* @link : cn.org.easysite.spring.boot.distributed.lock.test.DistributedApplicationBootstrap
|
||||
*/
|
||||
@SpringBootApplication
|
||||
@EnableAspectJAutoProxy
|
||||
public class DistributedApplicationBootstrap {
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(DistributedApplicationBootstrap.class, args);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user