Commit 097828a9 authored by lijun's avatar lijun

del pay model

parent 2a4c5c1f
......@@ -27,6 +27,5 @@
<module>smart-common-im</module>
<module>smart-common-lock</module>
<module>smart-common-mq</module>
<module>smart-common-pay</module>
</modules>
</project>
......@@ -106,11 +106,6 @@
<artifactId>smart-common-mq</artifactId>
<version>${smart.version}</version>
</dependency>
<dependency>
<groupId>com.smart.hospital</groupId>
<artifactId>smart-common-pay</artifactId>
<version>${smart.version}</version>
</dependency>
<!--mybatis plus extension,包含了mybatis plus core-->
<dependency>
<groupId>com.baomidou</groupId>
......
<?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>
<groupId>com.smart.hospital</groupId>
<artifactId>smart-hospital-common</artifactId>
<version>3.3.0</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<description>支付模块</description>
<artifactId>smart-common-pay</artifactId>
<packaging>jar</packaging>
<properties>
<alipay.version>4.16.57.ALL</alipay.version>
<fastjson.version>1.2.78</fastjson.version>
</properties>
<dependencies>
<dependency>
<groupId>com.smart.hospital</groupId>
<artifactId>smart-common-core</artifactId>
</dependency>
<!--ali sdk-->
<dependency>
<groupId>com.alipay.sdk</groupId>
<artifactId>alipay-sdk-java</artifactId>
<version>${alipay.version}</version>
<exclusions>
<exclusion>
<artifactId>fastjson</artifactId>
<groupId>com.alibaba</groupId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>${fastjson.version}</version>
</dependency>
</dependencies>
</project>
package com.smart.hospital.common.pay;
import com.smart.hospital.common.pay.annotation.ClientScan;
import com.smart.hospital.common.pay.parser.DefaultPayAnnotationParser;
import com.smart.hospital.common.pay.parser.PayAnnotationParser;
import com.smart.hospital.common.pay.prop.WxPayV3Properties;
import com.smart.hospital.common.pay.source.AnnotationPayAttributeSource;
import com.smart.hospital.common.pay.source.PayAttributeSource;
import com.smart.hospital.common.pay.strategy.PayStrategy;
import com.smart.hospital.common.pay.strategy.PayStrategyManager;
import com.smart.hospital.common.pay.strategy.StrategyPropertiesCustomizer;
import com.smart.hospital.common.pay.strategy.builder.HandlerFactoryBuilder;
import com.smart.hospital.common.pay.strategy.builder.PayHandlerFactoryBuilder;
import com.smart.hospital.common.pay.strategy.handler.WxCertV3StrategyHandler;
import com.smart.hospital.common.pay.strategy.handler.WxJsapiV3StrategyHandler;
import com.smart.hospital.common.pay.strategy.handler.StrategyHandler;
import com.smart.hospital.common.pay.strategy.handler.WxRefundStrategyHandler;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingClass;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
import org.springframework.lang.Nullable;
import java.util.List;
/**
* 支付自动配置类
*/
@Order
@Configuration
@EnableConfigurationProperties({WxPayV3Properties.class})
@ClientScan(basePackages = {"com.smart.hospital.common.pay.client"})
public class PayAutoConfiguration {
// @Bean
// 使用该种方法会导致过早得实例化,从而导致配置文件WxPayV3Properties注入得属性都为空
// public static PayClientBeanConfigurer payClientBeanConfigurer(PayAttributeSource payAttributeSource, PayStrategy payStrategyManager) {
// PayClientBeanConfigurer configurer = new PayClientBeanConfigurer();
// configurer.setPayAttributeSource(payAttributeSource);
// configurer.setPayStrategyManager(payStrategyManager);
// configurer.setBasePackage("com.smart.hospital.common.pay.client");
// return configurer;
// }
@Bean
public PayAttributeSource annotationPayAttributeSource(ObjectProvider<List<PayAnnotationParser>> objectProvider) {
return new AnnotationPayAttributeSource(objectProvider.getIfAvailable());
}
@Bean
public PayAnnotationParser defaultPayAnnotationParser() {
return new DefaultPayAnnotationParser();
}
@Bean
@ConditionalOnMissingBean
public HandlerFactoryBuilder payHandlerFactoryBuilder(ObjectProvider<List<StrategyHandler>> objectProvider) {
return new PayHandlerFactoryBuilder(objectProvider.getIfAvailable());
}
@Bean
@ConditionalOnMissingBean
public PayStrategy payStrategyManager(HandlerFactoryBuilder payHandlerFactoryBuilder) {
PayStrategyManager strategyManager = new PayStrategyManager();
strategyManager.setFactoryBuilder(payHandlerFactoryBuilder);
return strategyManager;
}
@Bean
@ConditionalOnMissingClass
public StrategyPropertiesCustomizer strategyPropertiesCustomizer(@Nullable WxPayV3Properties wxPayV3Properties, @Nullable List<StrategyHandler> handlers) {
return new StrategyPropertiesCustomizer(wxPayV3Properties, handlers);
}
@Bean
public StrategyHandler wxJsapiV3StrategyHandler() {
return new WxJsapiV3StrategyHandler();
}
@Bean
public StrategyHandler wxCertV3StrategyHandler() {
return new WxCertV3StrategyHandler();
}
@Bean
public StrategyHandler wxRefundStrategyHandler() {
return new WxRefundStrategyHandler();
}
}
package com.smart.hospital.common.pay.annotation;
import com.smart.hospital.common.pay.constant.Version;
import com.smart.hospital.common.pay.enums.PaymentMethod;
import org.springframework.http.HttpMethod;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* 支付支付
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface ALI {
/**
* 支付方式
*
* @return
*/
PaymentMethod payment() default PaymentMethod.ALI_WEB;
/**
* 版本
*
* @return
*/
String version() default Version.ALI_1_0;
/**
* 请求地址
*
* @return
*/
// String url() default "";
/**
* 请求方式
*
* @return
*/
HttpMethod method() default HttpMethod.POST;
}
package com.smart.hospital.common.pay.annotation;
import com.smart.hospital.common.pay.config.PayClientBeanDefinitionRegister;
import org.springframework.context.annotation.Import;
import java.lang.annotation.*;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Import(PayClientBeanDefinitionRegister.class)
public @interface ClientScan {
String[] basePackages() default {};
}
package com.smart.hospital.common.pay.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* 银联支付
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface UNION {
}
package com.smart.hospital.common.pay.annotation;
import com.smart.hospital.common.pay.constant.Version;
import com.smart.hospital.common.pay.enums.*;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* 微信支付
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface WX {
/**
* 支付方式
*
* @return
*/
PaymentMethod payment() default PaymentMethod.WX_JSAPI;
/**
* 商户平台模式
*
* @return
*/
PayModel model() default PayModel.BUSINESS_MODEL;
/**
* 版本
*
* @return
*/
String version() default Version.WX_V2;
/**
* 请求路径前缀
*
* @return
*/
Domain prefix() default Domain.CHINA;
/**
* 请求路径后缀
*
* @return
*/
Uri suffix();
/**
* 请求方式
*
* @return
*/
RequestMethod method() default RequestMethod.POST;
}
package com.smart.hospital.common.pay.client;
import com.alipay.api.AlipayResponse;
import com.smart.hospital.common.pay.annotation.ALI;
import com.smart.hospital.common.pay.annotation.WX;
import com.smart.hospital.common.pay.constant.Version;
import com.smart.hospital.common.pay.enums.PaymentMethod;
import com.smart.hospital.common.pay.enums.Domain;
import com.smart.hospital.common.pay.enums.RequestMethod;
import com.smart.hospital.common.pay.enums.Uri;
import com.smart.hospital.common.pay.model.*;
import org.springframework.http.HttpMethod;
/**
* 支付客户端
*/
public interface PayClient {
@WX(prefix = Domain.CHINA, suffix = Uri.JS_API_PAY, version = Version.WX_V3, method = RequestMethod.POST, payment = PaymentMethod.WX_JSAPI)
String wxJsapiPay(WxUnifiedOrderModel model);
@WX(prefix = Domain.CHINA, suffix = Uri.REFUND, version = Version.WX_V3, method = RequestMethod.POST, payment = PaymentMethod.WX_REFUND)
String wxRefund(WxRefundModel model);
@WX(prefix = Domain.CHINA, suffix = Uri.GET_CERTIFICATES, version = Version.WX_V3, payment = PaymentMethod.WX_CERT, method = RequestMethod.GET)
WxHttpResponse wxPlatformCert();
@ALI(method = HttpMethod.POST, payment = PaymentMethod.ALI_WEB)
<T extends AlipayResponse> T aliWebPay(AliUnifiedOrderModel<?> model);
}
package com.smart.hospital.common.pay.config;
import com.smart.hospital.common.pay.source.PayAttributeSource;
import com.smart.hospital.common.pay.strategy.PayStrategy;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.util.StringUtils;
public abstract class AbstractBeanConfigurer implements BeanDefinitionRegistryPostProcessor {
private String basePackage;
private PayAttributeSource payAttributeSource;
private PayStrategy payStrategyManager;
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
PayClientRegistryScanner scanner = new PayClientRegistryScanner(registry);
scanner.setPayAttributeSource(payAttributeSource);
scanner.setPayStrategy(payStrategyManager);
scanner.registerFilters();
scanner.scan(StringUtils.tokenizeToStringArray(this.basePackage, ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS));
}
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
// do nothing
}
public String getBasePackage() {
return basePackage;
}
public void setBasePackage(String basePackage) {
this.basePackage = basePackage;
}
public PayAttributeSource getPayAttributeSource() {
return payAttributeSource;
}
public void setPayAttributeSource(PayAttributeSource payAttributeSource) {
this.payAttributeSource = payAttributeSource;
}
public PayStrategy getPayStrategyManager() {
return payStrategyManager;
}
public void setPayStrategyManager(PayStrategy payStrategyManager) {
this.payStrategyManager = payStrategyManager;
}
}
package com.smart.hospital.common.pay.config;
import com.smart.hospital.common.pay.factory.PayFactoryBean;
import com.smart.hospital.common.pay.source.PayAttributeSource;
import com.smart.hospital.common.pay.strategy.PayStrategy;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.AnnotatedBeanDefinition;
import org.springframework.beans.factory.config.BeanDefinitionHolder;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.context.annotation.ClassPathBeanDefinitionScanner;
import java.util.Arrays;
import java.util.Set;
@Slf4j
public abstract class AbstractClientRegistryScanner extends ClassPathBeanDefinitionScanner {
private PayAttributeSource payAttributeSource;
private PayStrategy payStrategy;
protected PayFactoryBean<?> factoryBean = new PayFactoryBean<>();
public AbstractClientRegistryScanner(BeanDefinitionRegistry registry) {
super(registry, false);
}
public PayFactoryBean<?> getFactoryBean() {
return factoryBean;
}
public PayAttributeSource getPayAttributeSource() {
return payAttributeSource;
}
public void setPayAttributeSource(PayAttributeSource payAttributeSource) {
this.payAttributeSource = payAttributeSource;
}
public void setPayStrategy(PayStrategy payStrategy) {
this.payStrategy = payStrategy;
}
public PayStrategy getPayStrategy() {
return payStrategy;
}
@Override
protected Set<BeanDefinitionHolder> doScan(String... basePackages) {
Set<BeanDefinitionHolder> definitions = super.doScan(basePackages);
if (definitions.isEmpty()) {
log.warn("No interface was found in '" + Arrays.toString(basePackages) + "' package. Please check your configuration.");
} else {
processBeanDefinitions(definitions);
}
return definitions;
}
@Override
protected boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition) {
return beanDefinition.getMetadata().isInterface() && beanDefinition.getMetadata().isIndependent();
}
public void registerFilters() {
boolean acceptAllInterfaces = true;
if (acceptAllInterfaces) {
// default include filter that accepts all classes
addIncludeFilter((metadataReader, metadataReaderFactory) -> true);
}
// exclude package-info.java
addExcludeFilter((metadataReader, metadataReaderFactory) -> {
String className = metadataReader.getClassMetadata().getClassName();
return className.endsWith("package-info");
});
}
/**
* 处理Bean
*
* @param beanDefinitions
*/
protected abstract void processBeanDefinitions(Set<BeanDefinitionHolder> beanDefinitions);
}
package com.smart.hospital.common.pay.config;
public class PayClientBeanConfigurer extends AbstractBeanConfigurer {
}
package com.smart.hospital.common.pay.config;
import com.smart.hospital.common.pay.annotation.ClientScan;
import com.smart.hospital.common.pay.source.PayAttributeSource;
import com.smart.hospital.common.pay.strategy.PayStrategy;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
import org.springframework.core.annotation.AnnotationAttributes;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.util.StringUtils;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
public class PayClientBeanDefinitionRegister implements ImportBeanDefinitionRegistrar {
private PayAttributeSource payAttributeSource;
private PayStrategy payStrategyManager;
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
AnnotationAttributes mapperScanAttrs = AnnotationAttributes
.fromMap(importingClassMetadata.getAnnotationAttributes(ClientScan.class.getName()));
if (mapperScanAttrs != null) {
registerBeanDefinitions(mapperScanAttrs, registry);
}
}
void registerBeanDefinitions(AnnotationAttributes annoAttrs, BeanDefinitionRegistry registry) {
List<String> basePackages = Arrays.stream(annoAttrs.getStringArray("basePackages"))
.filter(StringUtils::hasText).collect(Collectors.toList());
PayClientRegistryScanner scanner = new PayClientRegistryScanner(registry);
scanner.setPayAttributeSource(payAttributeSource);
scanner.setPayStrategy(payStrategyManager);
scanner.registerFilters();
scanner.scan(StringUtils.toStringArray(basePackages));
}
public PayAttributeSource getPayAttributeSource() {
return payAttributeSource;
}
public void setPayAttributeSource(PayAttributeSource payAttributeSource) {
this.payAttributeSource = payAttributeSource;
}
public PayStrategy getPayStrategyManager() {
return payStrategyManager;
}
public void setPayStrategyManager(PayStrategy payStrategyManager) {
this.payStrategyManager = payStrategyManager;
}
}
package com.smart.hospital.common.pay.config;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.config.BeanDefinitionHolder;
import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.GenericBeanDefinition;
import java.util.Objects;
import java.util.Set;
@Slf4j
public class PayClientRegistryScanner extends AbstractClientRegistryScanner {
public PayClientRegistryScanner(BeanDefinitionRegistry registry) {
super(registry);
}
@Override
protected void processBeanDefinitions(Set<BeanDefinitionHolder> beanDefinitions) {
GenericBeanDefinition definition;
for (BeanDefinitionHolder holder : beanDefinitions) {
definition = (GenericBeanDefinition) holder.getBeanDefinition();
log.info("Creating FactoryBean with name '" + holder.getBeanName() + "' and '" + definition.getBeanClassName() + "' clientInterface");
// 构造器注入
definition.getConstructorArgumentValues().addGenericArgumentValue(Objects.requireNonNull(definition.getBeanClassName()));
// 属性注入
definition.getPropertyValues().add("payAttributeSource", getPayAttributeSource());
definition.getPropertyValues().add("payStrategy", getPayStrategy());
// 设置FactoryBean类型
definition.setBeanClass(getFactoryBean().getClass());
definition.setPrimary(true);
definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);
}
}
}
package com.smart.hospital.common.pay.constant;
/**
* 请求地址URL
*/
public interface URL {
/**
* 微信jsapi调用接口
*/
String WX_JSAPI = "https://api.mch.weixin.qq.com/v3/pay/transactions/jsapi";
}
package com.smart.hospital.common.pay.constant;
/**
* 支付版本号
*/
public interface Version {
/**
* 微信v3支付
*/
String WX_V3 = "v3";
/**
* 微信v2支付
*/
String WX_V2 = "v2";
/**
* 支付宝1.0支付
*/
String ALI_1_0 = "1.0";
}
package com.smart.hospital.common.pay.enums;
public enum Domain {
/**
* 中国国内
*/
CHINA("https://api.mch.weixin.qq.com"),
/**
* 中国国内(备用域名)
*/
CHINA2("https://api2.mch.weixin.qq.com"),
/**
* 东南亚
*/
HK("https://apihk.mch.weixin.qq.com"),
/**
* 其它
*/
US("https://apius.mch.weixin.qq.com"),
/**
* 获取公钥
*/
FRAUD("https://fraud.mch.weixin.qq.com"),
/**
* 活动
*/
ACTION("https://action.weixin.qq.com"),
/**
* 刷脸支付
* PAY_APP
*/
PAY_APP("https://payapp.weixin.qq.com");
/**
* 域名
*/
private final String type;
Domain(String type) {
this.type = type;
}
public String getType() {
return type;
}
}
package com.smart.hospital.common.pay.enums;
/**
* <p>IJPay 让支付触手可及,封装了微信支付、支付宝支付、银联支付常用的支付方式以及各种常用的接口。</p>
*
* <p>不依赖任何第三方 mvc 框架,仅仅作为工具使用简单快速完成支付模块的开发,可轻松嵌入到任何系统里。 </p>
*
* <p>IJPay 交流群: 723992875</p>
*
* <p>Node.js 版: https://gitee.com/javen205/TNWX</p>
*
* <p>商户平台模式</p>
*
* @author Javen
*/
public enum PayModel {
/**
* 商户模式
*/
BUSINESS_MODEL("BUSINESS_MODEL"),
/**
* 服务商模式
*/
SERVICE_MODE("SERVICE_MODE");
PayModel(String payModel) {
this.payModel = payModel;
}
/**
* 商户模式
*/
private final String payModel;
public String getPayModel() {
return payModel;
}
}
package com.smart.hospital.common.pay.enums;
public enum PayStatus {
PAY_ING(1,"微信接口调用前"),
PAY_SUC(2,"微信接口调用成功"),
PAY_FAIL(-1,"微信接口调用失败"),
;
private int code;
private String msg;
PayStatus(int code, String msg) {
this.code = code;
this.msg = msg;
}
public int getCode() {
return code;
}
public void setCode(int code) {
this.code = code;
}
public String getMsg() {
return msg;
}
public void setMsg(String msg) {
this.msg = msg;
}
}
package com.smart.hospital.common.pay.enums;
/**
* 支付方式枚举
*/
public enum PaymentMethod {
WX_JSAPI("WX_JSAPI", "小程序/公众号支付"),
WX_APP("WX_APP", "APP支付"),
WX_NATIVE("WX_NATIVE", "二维码支付"),
WX_MICRO("WX_MICRO", "付款码支付"),
WX_REFUND("WX_REFUND", "退款"),
WX_CERT("WX_CERT", "微信获取平台证书"),
ALI_FACE("ALI_FACE", "当面付"),
ALI_APP("ALI_APP", "App支付"),
ALI_WEB("ALI_WEB", "手机网站支付"),
ALI_WAP("ALI_WAP", "电脑网站支付"),
ALI_SWIPING("ALI_SWIPING", "刷脸付"),
;
private String name;
private String describe;
PaymentMethod(String name, String describe) {
this.name = name;
this.describe = describe;
}
public String getDescribe() {
return describe;
}
public void setDescribe(String describe) {
this.describe = describe;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
package com.smart.hospital.common.pay.enums;
/**
* <p>IJPay 让支付触手可及,封装了微信支付、支付宝支付、银联支付常用的支付方式以及各种常用的接口。</p>
*
* <p>不依赖任何第三方 mvc 框架,仅仅作为工具使用简单快速完成支付模块的开发,可轻松嵌入到任何系统里。 </p>
*
* <p>IJPay 交流群: 723992875</p>
*
* <p>Node.js 版: https://gitee.com/javen205/TNWX</p>
*
* <p>HTTP 请求的方法</p>
*
* @author Javen
*/
public enum RequestMethod {
/**
* 上传实质是 post 请求
*/
UPLOAD("POST"),
/**
* post 请求
*/
POST("POST"),
/**
* get 请求
*/
GET("GET"),
/**
* put 请求
*/
PUT("PUT"),
/**
* delete 请求
*/
DELETE("DELETE"),
/**
* options 请求
*/
OPTIONS("OPTIONS"),
/**
* head 请求
*/
HEAD("HEAD"),
/**
* trace 请求
*/
TRACE("TRACE"),
/**
* connect 请求
*/
CONNECT("CONNECT");
private final String method;
RequestMethod(String method) {
this.method = method;
}
@Override
public String toString() {
return this.method;
}
}
package com.smart.hospital.common.pay.enums;
/**
* <p>IJPay 让支付触手可及,封装了微信支付、支付宝支付、银联支付常用的支付方式以及各种常用的接口。</p>
*
* <p>不依赖任何第三方 mvc 框架,仅仅作为工具使用简单快速完成支付模块的开发,可轻松嵌入到任何系统里。 </p>
*
* <p>IJPay 交流群: 723992875</p>
*
* <p>Node.js 版: https://gitee.com/javen205/TNWX</p>
*
* <p>签名方式</p>
*
* @author Javen
*/
public enum SignType {
/**
* HMAC-SHA256 加密
*/
HMACSHA256("HMAC-SHA256"),
/**
* MD5 加密
*/
MD5("MD5"),
/**
* RSA
*/
RSA("RSA");
SignType(String type) {
this.type = type;
}
private final String type;
public String getType() {
return type;
}
@Override
public String toString() {
return type;
}
}
package com.smart.hospital.common.pay.exception;
/**
* 没有适用的支付处理器
*/
public class NoApplyHandlerException extends RuntimeException {
public NoApplyHandlerException() {
}
public NoApplyHandlerException(String message) {
super(message);
}
public NoApplyHandlerException(String message, Throwable cause) {
super(message, cause);
}
public NoApplyHandlerException(Throwable cause) {
super(cause);
}
}
package com.smart.hospital.common.pay.exception;
public class PayException extends RuntimeException {
public PayException() {
}
public PayException(String message) {
super(message);
}
public PayException(String message, Throwable cause) {
super(message, cause);
}
}
package com.smart.hospital.common.pay.factory;
import com.smart.hospital.common.pay.source.PayAttribute;
import com.smart.hospital.common.pay.source.PayAttributeSource;
import com.smart.hospital.common.pay.strategy.PayStrategy;
import lombok.extern.slf4j.Slf4j;
import org.springframework.lang.UsesJava7;
import java.io.Serializable;
import java.lang.invoke.MethodHandles;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
@Slf4j
public class PayClientProxy<T> implements InvocationHandler, Serializable {
private static final long serialVersionUID = 1L;
/**
* 代理的接口类
*/
private final Class<T> clientInterface;
/**
* 注解解析器
*/
private final PayAttributeSource payAttributeSource;
/**
* 支付策略
*/
private final PayStrategy payStrategy;
public PayClientProxy(Class<T> clientInterface, PayAttributeSource payAttributeSource, PayStrategy payStrategy) {
this.clientInterface = clientInterface;
this.payAttributeSource = payAttributeSource;
this.payStrategy = payStrategy;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if (Object.class.equals(method.getDeclaringClass())) {
return method.invoke(this, args);
} else if (isDefaultMethod(method)) {
return invokeDefaultMethod(proxy, method, args);
}
// 解析注解信息
PayAttribute attr = parseAnnotationMetaInfo(method);
// 调用支付
return getPayStrategy().doExecute(attr, clientInterface, method, args);
}
@UsesJava7
private Object invokeDefaultMethod(Object proxy, Method method, Object[] args)
throws Throwable {
final Constructor<MethodHandles.Lookup> constructor = MethodHandles.Lookup.class
.getDeclaredConstructor(Class.class, int.class);
if (!constructor.isAccessible()) {
constructor.setAccessible(true);
}
final Class<?> declaringClass = method.getDeclaringClass();
return constructor
.newInstance(declaringClass,
MethodHandles.Lookup.PRIVATE | MethodHandles.Lookup.PROTECTED
| MethodHandles.Lookup.PACKAGE | MethodHandles.Lookup.PUBLIC)
.unreflectSpecial(method, declaringClass).bindTo(proxy).invokeWithArguments(args);
}
/**
* Backport of java.lang.reflect.Method#isDefault()
*/
private boolean isDefaultMethod(Method method) {
return (method.getModifiers()
& (Modifier.ABSTRACT | Modifier.PUBLIC | Modifier.STATIC)) == Modifier.PUBLIC
&& method.getDeclaringClass().isInterface();
}
private PayAttribute parseAnnotationMetaInfo(Method method) {
return getPayAttributeSource().getPayAttribute(method, clientInterface);
}
public PayAttributeSource getPayAttributeSource() {
return payAttributeSource;
}
public PayStrategy getPayStrategy() {
return payStrategy;
}
}
package com.smart.hospital.common.pay.factory;
import org.springframework.beans.factory.FactoryBean;
public class PayFactoryBean<T> extends PayFactoryBeanSupport implements FactoryBean<T> {
private Class<T> clientInterface;
public PayFactoryBean() {
}
public PayFactoryBean(Class<T> clientInterface) {
this.clientInterface = clientInterface;
}
@Override
public T getObject() throws Exception {
return registerClient();
}
@Override
public Class<?> getObjectType() {
return this.clientInterface;
}
@Override
public boolean isSingleton() {
return true;
}
private T registerClient() {
PayProxyFactory<T> payProxyFactory = new PayProxyFactory<>(this.clientInterface);
return payProxyFactory.newInstance(getPayAttributeSource(), getPayStrategy());
}
}
package com.smart.hospital.common.pay.factory;
import com.smart.hospital.common.pay.source.PayAttributeSource;
import com.smart.hospital.common.pay.strategy.PayStrategy;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import static org.springframework.util.Assert.notNull;
public abstract class PayFactoryBeanSupport implements InitializingBean, ApplicationContextAware {
ApplicationContext applicationContext;
/**
* 资源解析器
*/
private PayAttributeSource payAttributeSource;
/**
* 支付策略
*/
private PayStrategy payStrategy;
public PayAttributeSource getPayAttributeSource() {
return payAttributeSource;
}
public void setPayAttributeSource(PayAttributeSource payAttributeSource) {
this.payAttributeSource = payAttributeSource;
}
public PayStrategy getPayStrategy() {
return payStrategy;
}
public void setPayStrategy(PayStrategy payStrategy) {
this.payStrategy = payStrategy;
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
@Override
public void afterPropertiesSet() throws Exception {
// notNull(this.payAttributeSource, "Property 'payAttributeSource' are required");
if (this.payAttributeSource == null) {
this.payAttributeSource = applicationContext.getBean(PayAttributeSource.class);
notNull(this.payAttributeSource, "Property 'payAttributeSource' are required");
}
if (this.payStrategy == null) {
this.payStrategy = applicationContext.getBean(PayStrategy.class);
}
}
}
package com.smart.hospital.common.pay.factory;
import com.smart.hospital.common.pay.source.PayAttributeSource;
import com.smart.hospital.common.pay.strategy.PayStrategy;
import java.lang.reflect.Proxy;
public class PayProxyFactory<T> {
private final Class<T> clientInterface;
public PayProxyFactory(Class<T> clientInterface) {
this.clientInterface = clientInterface;
}
@SuppressWarnings("unchecked")
protected T newInstance(PayClientProxy<T> payClientProxy) {
return (T) Proxy.newProxyInstance(clientInterface.getClassLoader(), new Class[]{clientInterface}, payClientProxy);
}
protected T newInstance(PayAttributeSource payAttributeSource, PayStrategy payStrategy) {
PayClientProxy<T> payClientProxy = new PayClientProxy<>(clientInterface, payAttributeSource,payStrategy);
return newInstance(payClientProxy);
}
}
package com.smart.hospital.common.pay.model;
import com.alipay.api.AlipayObject;
import lombok.Data;
import lombok.experimental.Accessors;
/**
* 阿里下单模型
*/
@Data
@Accessors(chain = true)
public class AliUnifiedOrderModel<T extends AlipayObject> {
/**
* 支付宝官方下单模型
*/
private T alipayObject;
/**
* 交易订单号
*/
private String outTradeNo;
}
package com.smart.hospital.common.pay.model;
import lombok.Data;
import lombok.experimental.Accessors;
@Data
@Accessors(chain = true)
public class Amount {
/**
* 总金额
*/
private int total;
/**
* 货币类型
*/
private String currency;
}
package com.smart.hospital.common.pay.model;
import lombok.Data;
import lombok.experimental.Accessors;
import java.util.List;
@Data
@Accessors(chain = true)
public class Detail {
/**
* 订单原价
*/
private int cost_price;
/**
* 商品小票ID
*/
private String invoice_id;
/**
* 单品列表
*/
private List<GoodsDetail> goods_detail;
}
package com.smart.hospital.common.pay.model;
import lombok.Data;
import lombok.experimental.Accessors;
@Data
@Accessors(chain = true)
public class GoodsDetail {
/**
* 商户侧商品编码
*/
private String merchant_goods_id;
/**
* 微信侧商品编码
*/
private String wechatpay_goods_id;
/**
* 商品名称
*/
private String goods_name;
/**
* 商品数量
*/
private int quantity;
/**
* 商品单价
*/
private int unit_price;
}
package com.smart.hospital.common.pay.model;
import lombok.Data;
import lombok.experimental.Accessors;
@Data
@Accessors(chain = true)
public class H5Info {
/**
* 场景类型
*/
private String type;
/**
* 应用名称
*/
private String app_name;
/**
* 网站URL
*/
private String app_url;
/**
* iOS 平台 BundleID
*/
private String bundle_id;
/**
* Android 平台 PackageName
*/
private String package_name;
}
package com.smart.hospital.common.pay.model;
import lombok.Data;
import lombok.experimental.Accessors;
@Data
@Accessors(chain = true)
public class Payer {
/**
* 用户标识
*/
private String openid;
/**
* 用户服务标识
*/
private String sp_openid;
/**
* 用户子标识
*/
private String sub_openid;
}
package com.smart.hospital.common.pay.model;
import lombok.Data;
import lombok.experimental.Accessors;
@Data
@Accessors(chain = true)
public class SceneInfo {
/**
* 用户终端IP
*/
private String payer_client_ip;
/**
* 商户端设备号
*/
private String device_id;
/**
* 商户门店信息
*/
private StoreInfo store_info;
/**
* H5 场景信息
*/
private H5Info h5_info;
}
package com.smart.hospital.common.pay.model;
import lombok.Data;
import lombok.experimental.Accessors;
@Data
@Accessors(chain = true)
public class SettleInfo {
/**
* 是否指定分账
*/
private boolean profit_sharing;
/**
* 补差金额
*/
private int subsidy_amount;
}
package com.smart.hospital.common.pay.model;
import lombok.Data;
import lombok.experimental.Accessors;
@Data
@Accessors(chain = true)
public class StoreInfo {
/**
* 门店编号
*/
private String id;
/**
* 门店名称
*/
private String name;
/**
* 地区编码
*/
private String area_code;
/**
* 详细地址
*/
private String address;
}
package com.smart.hospital.common.pay.model;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.map.CaseInsensitiveMap;
import cn.hutool.core.util.StrUtil;
import java.io.Serializable;
import java.util.List;
import java.util.Map;
public class WxHttpResponse implements Serializable {
private static final long serialVersionUID = 1L;
/**
* 响应体
*/
private String body;
/**
* 响应状态
*/
private int status;
/**
* 响应头
*/
private Map<String, List<String>> headers;
public WxHttpResponse() {
}
public WxHttpResponse(String body, int status, Map<String, List<String>> headers) {
this.body = body;
this.status = status;
this.headers = headers;
}
public String getBody() {
return body;
}
public void setBody(String body) {
this.body = body;
}
public int getStatus() {
return status;
}
public void setStatus(int status) {
this.status = status;
}
public Map<String, List<String>> getHeaders() {
return headers;
}
public void setHeaders(Map<String, List<String>> headers) {
this.headers = headers;
}
public String getHeader(String name) {
List<String> values = this.headerList(name);
return CollectionUtil.isEmpty(values) ? null : values.get(0);
}
private List<String> headerList(String name) {
if (StrUtil.isBlank(name)) {
return null;
} else {
CaseInsensitiveMap<String, List<String>> headersIgnoreCase = new CaseInsensitiveMap<>(getHeaders());
return headersIgnoreCase.get(name.trim());
}
}
@Override
public String toString() {
return "WxHttpResponse{" +
"body='" + body + '\'' +
", status=" + status +
", headers=" + headers +
'}';
}
}
package com.smart.hospital.common.pay.model;
import lombok.Data;
import lombok.experimental.Accessors;
/**
* V3 微信申请退款-金额信息
*/
@Data
@Accessors(chain = true)
public class WxRefundAmount {
/**
* 总金额
*/
private int total;
/**
* 货币类型
*/
private String currency;
/**
* 退款金额
*/
private int refund;
}
package com.smart.hospital.common.pay.model;
import lombok.Data;
import lombok.experimental.Accessors;
/**
* 微信退款详情
*/
@Data
@Accessors(chain = true)
public class WxRefundGoodsDetail {
/**
* 商户侧商品编码
*/
private String merchant_goods_id;
/**
* 微信侧商品编码
*/
private String wechatpay_goods_id;
/**
* 商品名称
*/
private String goods_name;
/**
* 商品单价
*/
private int unit_price;
/**
* 商品退款金额
*/
private int refund_amount;
/**
* 商品退货数量
*/
private int refund_quantity;
}
package com.smart.hospital.common.pay.model;
import lombok.Data;
import lombok.experimental.Accessors;
import java.util.List;
/**
* V3 微信申请退款 Model
* 参数说明:https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter3_1_9.shtml
*/
@Data
@Accessors(chain = true)
public class WxRefundModel {
/**
* 微信支付订单号
*/
private String transaction_id;
/**
* 商户订单号
*/
private String out_trade_no;
/**
* 商户退款单号
*/
private String out_refund_no;
/**
* 退款原因
*/
private String reason;
/**
* 退款结果回调url
*/
private String notify_url;
/**
* 退款资金来源
*/
private String funds_account;
/**
* 金额信息
*/
private WxRefundAmount amount;
/**
* 退款商品
*/
private List<WxRefundGoodsDetail> goods_detail;
}
package com.smart.hospital.common.pay.model;
import lombok.Data;
import lombok.experimental.Accessors;
@Data
@Accessors(chain = true)
public class WxUnifiedOrderModel {
/**
* 公众号ID
*/
private String appid;
/**
* 服务商公众号ID
*/
private String sp_appid;
/**
* 直连商户号
*/
private String mchid;
/**
* 服务商户号
*/
private String sp_mchid;
/**
* 子商户公众号ID
*/
private String sub_appid;
/**
* 子商户号
*/
private String sub_mchid;
/**
* 商品描述
*/
private String description;
/**
* 商户订单号
*/
private String out_trade_no;
/**
* 交易结束时间
*/
private String time_expire;
/**
* 附加数据
*/
private String attach;
/**
* 通知地址
*/
private String notify_url;
/**
* 订单优惠标记
*/
private String goods_tag;
/**
* 结算信息
*/
private SettleInfo settle_info;
/**
* 订单金额
*/
private Amount amount;
/**
* 支付者
*/
private Payer payer;
/**
* 优惠功能
*/
private Detail detail;
/**
* 场景信息
*/
private SceneInfo scene_info;
}
package com.smart.hospital.common.pay.parser;
import com.smart.hospital.common.pay.annotation.ALI;
import com.smart.hospital.common.pay.annotation.UNION;
import com.smart.hospital.common.pay.annotation.WX;
import com.smart.hospital.common.pay.source.PayAttribute;
import org.springframework.core.annotation.AnnotatedElementUtils;
import org.springframework.core.annotation.AnnotationAttributes;
import java.lang.annotation.Annotation;
import java.lang.reflect.AnnotatedElement;
public abstract class AbstractPayAnnotationParser implements PayAnnotationParser {
/**
* 注解中的参数信息
*/
protected final String PAYMENT_METHOD_NAME = "payment";
protected final String VERSION_NAME = "version";
protected final String URL_PREFIX_NAME = "prefix";
protected final String URL_SUFFIX_NAME = "suffix";
protected final String REQUEST_METHOD_NAME = "method";
protected final String MODEL_NAME = "model";
/**
* 所有的所注解类型
*/
private static final Class<? extends Annotation>[] PAY_ANNOTATION_CLASSES = new Class[]{WX.class, ALI.class, UNION.class};
@Override
public PayAttribute parsePayAnnotation(AnnotatedElement element) {
for (Class<? extends Annotation> clazz : PAY_ANNOTATION_CLASSES) {
AnnotationAttributes attributes = AnnotatedElementUtils.findMergedAnnotationAttributes(element, clazz, false, false);
if (attributes != null) {
return parsePayAnnotation(attributes);
}
}
return null;
}
protected abstract PayAttribute parsePayAnnotation(AnnotationAttributes attributes);
}
package com.smart.hospital.common.pay.parser;
import com.smart.hospital.common.pay.enums.*;
import com.smart.hospital.common.pay.source.DefaultPayAttribute;
import com.smart.hospital.common.pay.source.PayAttribute;
import org.springframework.core.annotation.AnnotationAttributes;
public class DefaultPayAnnotationParser extends AbstractPayAnnotationParser {
@Override
protected PayAttribute parsePayAnnotation(AnnotationAttributes attributes) {
// 支付方式
PaymentMethod paymentMethod = attributes.getEnum(PAYMENT_METHOD_NAME);
// 支付版本号
String version = attributes.getString(VERSION_NAME);
// 请求路径前缀
Domain prefix = null;
if (attributes.containsKey(URL_PREFIX_NAME)) {
prefix = attributes.getEnum(URL_PREFIX_NAME);
}
// 请求路径前缀
Uri suffix = null;
if (attributes.containsKey(URL_SUFFIX_NAME)) {
suffix = attributes.getEnum(URL_SUFFIX_NAME);
}
// 请求方式
RequestMethod requestMethod = attributes.getEnum(REQUEST_METHOD_NAME);
// 商户平台模式
PayModel model = attributes.getEnum(MODEL_NAME);
return new DefaultPayAttribute(paymentMethod, version, prefix, suffix, requestMethod, model);
}
}
package com.smart.hospital.common.pay.parser;
import com.smart.hospital.common.pay.source.PayAttribute;
import java.lang.reflect.AnnotatedElement;
public interface PayAnnotationParser {
/**
* 解析指定方法或则类的注解信息
*
* @param element
* @return
*/
PayAttribute parsePayAnnotation(AnnotatedElement element);
}
package com.smart.hospital.common.pay.source;
import org.springframework.core.MethodClassKey;
import org.springframework.lang.Nullable;
import java.lang.reflect.Method;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
public abstract class AbstractPayAttributeSource implements PayAttributeSource {
private final Map<Object, PayAttribute> attributeCache = new ConcurrentHashMap<>(16);
@Override
public PayAttribute getPayAttribute(Method method, Class<?> targetClass) {
if (method.getDeclaringClass() == Object.class) {
return null;
}
// 从本地缓存中读取
Object cacheKey = getCacheKey(method, targetClass);
PayAttribute payAttribute = this.attributeCache.get(cacheKey);
if (payAttribute != null) {
return payAttribute;
}
// 解析注解信息
PayAttribute attr = computePayAttribute(method, targetClass);
if (attr != null) {
// 将信息放入本地缓存
this.attributeCache.put(cacheKey, attr);
}
return attr;
}
protected PayAttribute computePayAttribute(Method method, Class<?> targetClass) {
PayAttribute payAttribute = findPayAttribute(method);
if (payAttribute != null) {
return payAttribute;
}
return null;
}
protected abstract PayAttribute findPayAttribute(Method method);
protected Object getCacheKey(Method method, @Nullable Class<?> targetClass) {
return new MethodClassKey(method, targetClass);
}
}
package com.smart.hospital.common.pay.source;
import java.lang.reflect.Method;
/**
* 支付属性资源解析器接口
*/
public interface PayAttributeSource {
/**
* 从方法上获取注解属性信息
*
* @param method
* @param targetClass
* @return
*/
PayAttribute getPayAttribute(Method method, Class<?> targetClass);
}
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.smart.hospital.common.pay.PayAutoConfiguration
fxkcfxkcfxkcfxkcfxkcfxkcfxkcfxkc
\ No newline at end of file
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment