diff --git a/payment-spring-boot-autoconfigure/src/main/java/cn/felord/payment/wechat/InMemoryWechatTenantService.java b/payment-spring-boot-autoconfigure/src/main/java/cn/felord/payment/wechat/InMemoryWechatTenantService.java new file mode 100644 index 0000000..aa2af98 --- /dev/null +++ b/payment-spring-boot-autoconfigure/src/main/java/cn/felord/payment/wechat/InMemoryWechatTenantService.java @@ -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 getAllTenants() { + Map 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()); + } +} diff --git a/payment-spring-boot-autoconfigure/src/main/java/cn/felord/payment/wechat/WechatPayConfiguration.java b/payment-spring-boot-autoconfigure/src/main/java/cn/felord/payment/wechat/WechatPayConfiguration.java index be33287..73d19d6 100644 --- a/payment-spring-boot-autoconfigure/src/main/java/cn/felord/payment/wechat/WechatPayConfiguration.java +++ b/payment-spring-boot-autoconfigure/src/main/java/cn/felord/payment/wechat/WechatPayConfiguration.java @@ -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 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; } diff --git a/payment-spring-boot-autoconfigure/src/main/java/cn/felord/payment/wechat/WechatTenantService.java b/payment-spring-boot-autoconfigure/src/main/java/cn/felord/payment/wechat/WechatTenantService.java new file mode 100644 index 0000000..8269096 --- /dev/null +++ b/payment-spring-boot-autoconfigure/src/main/java/cn/felord/payment/wechat/WechatTenantService.java @@ -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 getAllTenants(); +} diff --git a/payment-spring-boot-autoconfigure/src/main/java/cn/felord/payment/wechat/WechatTenantServiceConfiguration.java b/payment-spring-boot-autoconfigure/src/main/java/cn/felord/payment/wechat/WechatTenantServiceConfiguration.java new file mode 100644 index 0000000..f3bf990 --- /dev/null +++ b/payment-spring-boot-autoconfigure/src/main/java/cn/felord/payment/wechat/WechatTenantServiceConfiguration.java @@ -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); + } +} diff --git a/payment-spring-boot-autoconfigure/src/main/java/cn/felord/payment/wechat/v3/KeyPairFactory.java b/payment-spring-boot-autoconfigure/src/main/java/cn/felord/payment/wechat/v3/KeyPairFactory.java index a1ad6db..255abc8 100644 --- a/payment-spring-boot-autoconfigure/src/main/java/cn/felord/payment/wechat/v3/KeyPairFactory.java +++ b/payment-spring-boot-autoconfigure/src/main/java/cn/felord/payment/wechat/v3/KeyPairFactory.java @@ -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); + } /** * 获取公私钥. diff --git a/payment-spring-boot-autoconfigure/src/main/java/cn/felord/payment/wechat/v3/SignatureProvider.java b/payment-spring-boot-autoconfigure/src/main/java/cn/felord/payment/wechat/v3/SignatureProvider.java index f0c1a5a..2a88ef8 100644 --- a/payment-spring-boot-autoconfigure/src/main/java/cn/felord/payment/wechat/v3/SignatureProvider.java +++ b/payment-spring-boot-autoconfigure/src/main/java/cn/felord/payment/wechat/v3/SignatureProvider.java @@ -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(() -> { diff --git a/payment-spring-boot-autoconfigure/src/main/java/cn/felord/payment/wechat/v3/WechatMetaContainer.java b/payment-spring-boot-autoconfigure/src/main/java/cn/felord/payment/wechat/v3/WechatMetaContainer.java index fe988e6..5109bec 100644 --- a/payment-spring-boot-autoconfigure/src/main/java/cn/felord/payment/wechat/v3/WechatMetaContainer.java +++ b/payment-spring-boot-autoconfigure/src/main/java/cn/felord/payment/wechat/v3/WechatMetaContainer.java @@ -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 wechatMetaBeanMap = new HashMap<>(); - private final Set tenantIds = new HashSet<>(); + private final Map wechatMetaBeanMap = new ConcurrentHashMap<>(); + private final Set 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 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. *