mirror of
https://github.com/dromara/payment-spring-boot.git
synced 2026-03-14 13:53:42 +08:00
enhance: 增加抽象接口WechatTenantService负责从配置文件或者其它数据源检索租户配置信息,提供默认实现InMemoryWechatTenantService(可被覆盖)
Closes #88
This commit is contained in:
@@ -0,0 +1,60 @@
|
||||
/*
|
||||
* Copyright 2019-2022 felord.cn
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
* Website:
|
||||
* https://felord.cn
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package cn.felord.payment.wechat;
|
||||
|
||||
import cn.felord.payment.wechat.v3.KeyPairFactory;
|
||||
import cn.felord.payment.wechat.v3.WechatMetaBean;
|
||||
import lombok.AllArgsConstructor;
|
||||
import org.springframework.core.io.ClassPathResource;
|
||||
import org.springframework.core.io.FileSystemResource;
|
||||
import org.springframework.core.io.Resource;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* @author xiafang
|
||||
* @since 2023/2/3 11:40
|
||||
*/
|
||||
@AllArgsConstructor
|
||||
public class InMemoryWechatTenantService implements WechatTenantService {
|
||||
private final WechatPayProperties wechatPayProperties;
|
||||
|
||||
@Override
|
||||
public Set<WechatMetaBean> getAllTenants() {
|
||||
Map<String, WechatPayProperties.V3> v3Map = wechatPayProperties.getV3();
|
||||
KeyPairFactory keyPairFactory = new KeyPairFactory();
|
||||
return v3Map.entrySet()
|
||||
.stream()
|
||||
.map(entry -> {
|
||||
WechatPayProperties.V3 v3 = entry.getValue();
|
||||
String tenantId = entry.getKey();
|
||||
String certPath = v3.getCertPath();
|
||||
String certAbsolutePath = v3.getCertAbsolutePath();
|
||||
String mchId = v3.getMchId();
|
||||
Resource resource = certAbsolutePath != null ? new FileSystemResource(certAbsolutePath) :
|
||||
new ClassPathResource(certPath == null ? "wechat/apiclient_cert.p12" : certPath);
|
||||
WechatMetaBean wechatMetaBean = keyPairFactory.initWechatMetaBean(resource, mchId);
|
||||
wechatMetaBean.setV3(v3);
|
||||
wechatMetaBean.setTenantId(tenantId);
|
||||
return wechatMetaBean;
|
||||
})
|
||||
.collect(Collectors.toSet());
|
||||
}
|
||||
}
|
||||
@@ -17,17 +17,13 @@
|
||||
package cn.felord.payment.wechat;
|
||||
|
||||
|
||||
import cn.felord.payment.wechat.v3.*;
|
||||
import cn.felord.payment.wechat.v3.SignatureProvider;
|
||||
import cn.felord.payment.wechat.v3.WechatApiProvider;
|
||||
import cn.felord.payment.wechat.v3.WechatMetaContainer;
|
||||
import cn.felord.payment.wechat.v3.WechatPayClient;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Conditional;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.core.io.ClassPathResource;
|
||||
import org.springframework.core.io.FileSystemResource;
|
||||
import org.springframework.core.io.Resource;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* The type Wechat pay configuration.
|
||||
@@ -36,39 +32,19 @@ import java.util.Map;
|
||||
* @since 1.0.0.RELEASE
|
||||
*/
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@Conditional(WechatPayConfiguredCondition.class)
|
||||
@EnableConfigurationProperties(WechatPayProperties.class)
|
||||
public class WechatPayConfiguration {
|
||||
/**
|
||||
* The constant CERT_ALIAS.
|
||||
*/
|
||||
private static final String CERT_ALIAS = "Tenpay Certificate";
|
||||
|
||||
/**
|
||||
* 微信支付公私钥 以及序列号等元数据.
|
||||
*
|
||||
* @param wechatPayProperties the wechat pay properties
|
||||
* @param wechatTenantService the wechat tenant service
|
||||
* @return the wechat cert bean
|
||||
*/
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
WechatMetaContainer wechatMetaContainer(WechatPayProperties wechatPayProperties) {
|
||||
|
||||
Map<String, WechatPayProperties.V3> v3Map = wechatPayProperties.getV3();
|
||||
WechatMetaContainer wechatMetaContainer(WechatTenantService wechatTenantService) {
|
||||
WechatMetaContainer container = new WechatMetaContainer();
|
||||
KeyPairFactory keyPairFactory = new KeyPairFactory();
|
||||
v3Map.keySet().forEach(tenantId -> {
|
||||
WechatPayProperties.V3 v3 = v3Map.get(tenantId);
|
||||
String certPath = v3.getCertPath();
|
||||
String certAbsolutePath = v3.getCertAbsolutePath();
|
||||
String mchId = v3.getMchId();
|
||||
Resource resource = certAbsolutePath != null ? new FileSystemResource(certAbsolutePath) :
|
||||
new ClassPathResource(certPath == null ? "wechat/apiclient_cert.p12" : certPath);
|
||||
WechatMetaBean wechatMetaBean = keyPairFactory.initWechatMetaBean(resource, CERT_ALIAS, mchId);
|
||||
wechatMetaBean.setV3(v3);
|
||||
wechatMetaBean.setTenantId(tenantId);
|
||||
container.addWechatMeta(tenantId, wechatMetaBean);
|
||||
});
|
||||
container.addWechatMetas(wechatTenantService.getAllTenants());
|
||||
return container;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
* Copyright 2019-2022 felord.cn
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
* Website:
|
||||
* https://felord.cn
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package cn.felord.payment.wechat;
|
||||
|
||||
import cn.felord.payment.wechat.v3.WechatMetaBean;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* @author xiafang
|
||||
* @since 2023/2/3 11:37
|
||||
*/
|
||||
public interface WechatTenantService {
|
||||
Set<WechatMetaBean> getAllTenants();
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
* Copyright 2019-2022 felord.cn
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
* Website:
|
||||
* https://felord.cn
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package cn.felord.payment.wechat;
|
||||
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Conditional;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
/**
|
||||
* The type Wechat tenant service configuration.
|
||||
*
|
||||
* @author xiafang
|
||||
* @since 2023 /2/3 12:32
|
||||
*/
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@Conditional(WechatPayConfiguredCondition.class)
|
||||
@EnableConfigurationProperties(WechatPayProperties.class)
|
||||
public class WechatTenantServiceConfiguration {
|
||||
|
||||
/**
|
||||
* Wechat tenant service wechat tenant service.
|
||||
*
|
||||
* @param wechatPayProperties the wechat pay properties
|
||||
* @return the wechat tenant service
|
||||
*/
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public WechatTenantService wechatTenantService(WechatPayProperties wechatPayProperties) {
|
||||
return new InMemoryWechatTenantService(wechatPayProperties);
|
||||
}
|
||||
}
|
||||
@@ -18,11 +18,13 @@ package cn.felord.payment.wechat.v3;
|
||||
|
||||
|
||||
import cn.felord.payment.PayException;
|
||||
import org.springframework.core.io.ClassPathResource;
|
||||
import org.springframework.core.io.FileSystemResource;
|
||||
import org.springframework.core.io.Resource;
|
||||
|
||||
import java.security.*;
|
||||
import java.security.KeyPair;
|
||||
import java.security.KeyStore;
|
||||
import java.security.KeyStoreException;
|
||||
import java.security.PrivateKey;
|
||||
import java.security.PublicKey;
|
||||
import java.security.cert.X509Certificate;
|
||||
|
||||
/**
|
||||
@@ -32,17 +34,20 @@ import java.security.cert.X509Certificate;
|
||||
* @since 1.0.0.RELEASE
|
||||
*/
|
||||
public class KeyPairFactory {
|
||||
|
||||
private static final String CERT_ALIAS = "Tenpay Certificate";
|
||||
private static final KeyStore PKCS12_KEY_STORE;
|
||||
|
||||
static {
|
||||
try {
|
||||
PKCS12_KEY_STORE = KeyStore.getInstance("PKCS12");
|
||||
} catch (KeyStoreException e) {
|
||||
throw new PayException(" wechat pay keystore initialization failed");
|
||||
}
|
||||
}
|
||||
static {
|
||||
try {
|
||||
PKCS12_KEY_STORE = KeyStore.getInstance("PKCS12");
|
||||
} catch (KeyStoreException e) {
|
||||
throw new PayException(" wechat pay keystore initialization failed");
|
||||
}
|
||||
}
|
||||
|
||||
public WechatMetaBean initWechatMetaBean(Resource resource, String keyPass) {
|
||||
return this.initWechatMetaBean(resource, CERT_ALIAS, keyPass);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取公私钥.
|
||||
|
||||
@@ -187,7 +187,7 @@ public class SignatureProvider {
|
||||
return CERTIFICATE_SET.stream()
|
||||
.filter(cert -> Objects.equals(wechatpaySerial, cert.getWechatPaySerial()))
|
||||
.findAny()
|
||||
.orElseThrow(()->new PayException("cannot obtain the certificate"));
|
||||
.orElseThrow(() -> new PayException("cannot obtain the certificate"));
|
||||
});
|
||||
|
||||
|
||||
@@ -235,8 +235,8 @@ public class SignatureProvider {
|
||||
}
|
||||
ArrayNode certificates = bodyObjectNode.withArray("data");
|
||||
if (certificates.isArray() && certificates.size() > 0) {
|
||||
CERTIFICATE_SET.forEach( x509WechatCertificateInfo -> {
|
||||
if (Objects.equals(tenantId,x509WechatCertificateInfo.getTenantId())){
|
||||
CERTIFICATE_SET.forEach(x509WechatCertificateInfo -> {
|
||||
if (Objects.equals(tenantId, x509WechatCertificateInfo.getTenantId())) {
|
||||
CERTIFICATE_SET.remove(x509WechatCertificateInfo);
|
||||
}
|
||||
});
|
||||
@@ -257,7 +257,7 @@ public class SignatureProvider {
|
||||
x509WechatCertificateInfo.setWechatPaySerial(responseSerialNo);
|
||||
x509WechatCertificateInfo.setTenantId(tenantId);
|
||||
x509WechatCertificateInfo.setX509Certificate((X509Certificate) certificate);
|
||||
CERTIFICATE_SET.add( x509WechatCertificateInfo);
|
||||
CERTIFICATE_SET.add(x509WechatCertificateInfo);
|
||||
} catch (CertificateException e) {
|
||||
throw new PayException("An error occurred while generating the wechat v3 certificate, reason : " + e.getMessage());
|
||||
}
|
||||
@@ -359,7 +359,7 @@ public class SignatureProvider {
|
||||
*/
|
||||
public X509WechatCertificateInfo getCertificate(String tenantId) {
|
||||
|
||||
return CERTIFICATE_SET.stream()
|
||||
return CERTIFICATE_SET.stream()
|
||||
.filter(cert -> Objects.equals(tenantId, cert.getTenantId()))
|
||||
.findAny()
|
||||
.orElseGet(() -> {
|
||||
|
||||
@@ -18,6 +18,8 @@ package cn.felord.payment.wechat.v3;
|
||||
|
||||
|
||||
import java.util.*;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ConcurrentSkipListSet;
|
||||
|
||||
/**
|
||||
* 配置容器
|
||||
@@ -26,22 +28,26 @@ import java.util.*;
|
||||
* @since 1.0.0.RELEASE
|
||||
*/
|
||||
public class WechatMetaContainer {
|
||||
private final Map<String, WechatMetaBean> wechatMetaBeanMap = new HashMap<>();
|
||||
private final Set<String> tenantIds = new HashSet<>();
|
||||
private final Map<String, WechatMetaBean> wechatMetaBeanMap = new ConcurrentHashMap<>();
|
||||
private final Set<String> tenantIds = new ConcurrentSkipListSet<>();
|
||||
|
||||
|
||||
/**
|
||||
* Add wechat meta boolean.
|
||||
* Add wechat metas.
|
||||
*
|
||||
* @param tenantId the tenantId
|
||||
* @param wechatMetaBean the wechat meta bean
|
||||
* @return the boolean
|
||||
* @param wechatMetaBeans the wechat meta beans
|
||||
*/
|
||||
public WechatMetaBean addWechatMeta(String tenantId, WechatMetaBean wechatMetaBean) {
|
||||
tenantIds.add(tenantId);
|
||||
return this.wechatMetaBeanMap.put(tenantId, wechatMetaBean);
|
||||
public void addWechatMetas(Collection<WechatMetaBean> wechatMetaBeans) {
|
||||
wechatMetaBeans.forEach(this::addMeta);
|
||||
}
|
||||
|
||||
private void addMeta(WechatMetaBean wechatMetaBean) {
|
||||
String tenantId = wechatMetaBean.getTenantId();
|
||||
tenantIds.add(tenantId);
|
||||
wechatMetaBeanMap.put(tenantId, wechatMetaBean);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Remove wechat meta wechat meta bean.
|
||||
*
|
||||
|
||||
Reference in New Issue
Block a user