From 467662716d54711b85c78aa22675ff8948665134 Mon Sep 17 00:00:00 2001 From: xiafang Date: Thu, 3 Dec 2020 18:10:52 +0800 Subject: [PATCH] =?UTF-8?q?=E5=A4=9A=E7=A7=9F=E6=88=B7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../wechat/WechatPayConfiguration.java | 59 ++++++---------- .../payment/wechat/WechatPayProperties.java | 4 +- ...2AuthorizationRequestRedirectProvider.java | 29 ++++---- .../felord/payment/wechat/v3/AbstractApi.java | 21 ++++-- .../payment/wechat/v3/SignatureProvider.java | 39 ++++++----- .../payment/wechat/v3/WechatApiProvider.java | 22 ++++++ .../wechat/v3/WechatMarketingFavorApi.java | 60 +++++++++++------ .../payment/wechat/v3/WechatMetaBean.java | 6 +- .../wechat/v3/WechatMetaContainer.java | 67 +++++++++++++++++++ .../payment/wechat/v3/WechatPayApi.java | 37 +++++++--- .../payment/wechat/v3/WechatPayCallback.java | 5 +- .../payment/wechat/v3/WechatPayClient.java | 16 ++++- .../{AppPayParams.java => PayParams.java} | 2 +- 13 files changed, 255 insertions(+), 112 deletions(-) create mode 100644 payment-spring-boot-autoconfigure/src/main/java/cn/felord/payment/wechat/v3/WechatApiProvider.java create mode 100644 payment-spring-boot-autoconfigure/src/main/java/cn/felord/payment/wechat/v3/WechatMetaContainer.java rename payment-spring-boot-autoconfigure/src/main/java/cn/felord/payment/wechat/v3/model/{AppPayParams.java => PayParams.java} (97%) 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 f83e21d..be988d3 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 @@ -8,6 +8,8 @@ import org.springframework.boot.context.properties.EnableConfigurationProperties import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import java.util.Map; + /** * The type Wechat pay configuration. */ @@ -24,24 +26,32 @@ public class WechatPayConfiguration { * @return the wechat cert bean */ @Bean - WechatMetaBean wechatMetaBean(WechatPayProperties wechatPayProperties) { + WechatMetaContainer wechatMetaContainer(WechatPayProperties wechatPayProperties) { - String certPath = wechatPayProperties.getV3().getCertPath(); - String mchId = wechatPayProperties.getV3().getMchId(); - WechatMetaBean wechatMetaBean = new KeyPairFactory().createPKCS12(certPath, CERT_ALIAS, mchId); - wechatMetaBean.setWechatPayProperties(wechatPayProperties); - return wechatMetaBean; + Map v3Map = wechatPayProperties.getV3(); + WechatMetaContainer container = new WechatMetaContainer(); + v3Map.keySet().forEach(tenantId -> { + WechatPayProperties.V3 v3 = v3Map.get(tenantId); + String certPath = v3.getCertPath(); + String mchId = v3.getMchId(); + WechatMetaBean wechatMetaBean = new KeyPairFactory().createPKCS12(certPath, CERT_ALIAS, mchId); + wechatMetaBean.setV3(v3); + wechatMetaBean.setTenantId(tenantId); + container.addWechatMeta(tenantId, wechatMetaBean); + container.addTenant(tenantId); + }); + return container; } /** * 微信支付V3签名工具. * - * @param wechatMetaBean the wechat meta bean + * @param wechatMetaContainer the wechat meta container * @return the signature provider */ @Bean - SignatureProvider signatureProvider(WechatMetaBean wechatMetaBean) { - return new SignatureProvider(wechatMetaBean); + SignatureProvider signatureProvider(WechatMetaContainer wechatMetaContainer) { + return new SignatureProvider(wechatMetaContainer); } @@ -52,34 +62,10 @@ public class WechatPayConfiguration { * @return the wechat pay service */ @Bean - public WechatPayClient wechatPayService(SignatureProvider signatureProvider) { + public WechatPayClient wechatPayClient(SignatureProvider signatureProvider) { return new WechatPayClient(signatureProvider); } - /** - * 微信支付API. - * - * @param wechatPayClient the wechat pay v 3 client - * @param wechatMetaBean the wechat meta bean - * @return the wechat pay v 3 api - */ - @Bean - public WechatPayApi wechatPayApi(WechatPayClient wechatPayClient, WechatMetaBean wechatMetaBean) { - return new WechatPayApi(wechatPayClient, wechatMetaBean); - } - - /** - * 微信营销API. - * - * @param wechatPayClient the wechat pay client - * @param wechatMetaBean the wechat meta bean - * @return the wechat marketing api - */ - @Bean - public WechatMarketingFavorApi wechatMarketingApi(WechatPayClient wechatPayClient, WechatMetaBean wechatMetaBean) { - return new WechatMarketingFavorApi(wechatPayClient, wechatMetaBean); - } - /** * 微信支付回调工具. * @@ -99,10 +85,9 @@ public class WechatPayConfiguration { * @return the o auth 2 authorization request redirect provider */ @Bean - @ConditionalOnProperty(prefix = "wechat.pay", name = "v3.mp.app-id") public OAuth2AuthorizationRequestRedirectProvider oAuth2Provider(WechatPayProperties wechatPayProperties) { - WechatPayProperties.Mp mp = wechatPayProperties.getV3().getMp(); - return new OAuth2AuthorizationRequestRedirectProvider(mp.getAppId(), mp.getAppSecret()); + Map v3Map = wechatPayProperties.getV3(); + return new OAuth2AuthorizationRequestRedirectProvider(v3Map); } } diff --git a/payment-spring-boot-autoconfigure/src/main/java/cn/felord/payment/wechat/WechatPayProperties.java b/payment-spring-boot-autoconfigure/src/main/java/cn/felord/payment/wechat/WechatPayProperties.java index d56567f..82658e7 100644 --- a/payment-spring-boot-autoconfigure/src/main/java/cn/felord/payment/wechat/WechatPayProperties.java +++ b/payment-spring-boot-autoconfigure/src/main/java/cn/felord/payment/wechat/WechatPayProperties.java @@ -4,6 +4,8 @@ import lombok.Data; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.boot.context.properties.NestedConfigurationProperty; +import java.util.Map; + /** * The type Wechat pay properties. */ @@ -14,7 +16,7 @@ public class WechatPayProperties { * wechat pay V3 properties */ @NestedConfigurationProperty - private V3 v3; + private Map v3; /** * wechat pay v3 properties. diff --git a/payment-spring-boot-autoconfigure/src/main/java/cn/felord/payment/wechat/oauth2/OAuth2AuthorizationRequestRedirectProvider.java b/payment-spring-boot-autoconfigure/src/main/java/cn/felord/payment/wechat/oauth2/OAuth2AuthorizationRequestRedirectProvider.java index be0ca1e..09fdd60 100644 --- a/payment-spring-boot-autoconfigure/src/main/java/cn/felord/payment/wechat/oauth2/OAuth2AuthorizationRequestRedirectProvider.java +++ b/payment-spring-boot-autoconfigure/src/main/java/cn/felord/payment/wechat/oauth2/OAuth2AuthorizationRequestRedirectProvider.java @@ -2,6 +2,7 @@ package cn.felord.payment.wechat.oauth2; import cn.felord.payment.PayException; +import cn.felord.payment.wechat.WechatPayProperties; import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.PropertyNamingStrategy; @@ -18,6 +19,7 @@ import org.springframework.web.util.UriComponentsBuilder; import java.net.URI; import java.net.URLEncoder; import java.nio.charset.StandardCharsets; +import java.util.Map; import java.util.Objects; /** @@ -34,21 +36,18 @@ public class OAuth2AuthorizationRequestRedirectProvider { private static final String TOKEN_URI = "https://api.weixin.qq.com/sns/oauth2/access_token"; private final RestOperations restOperations = new RestTemplate(); private final ObjectMapper objectMapper; - private final String appId; - private final String secret; + private final Map v3Map; /** * Instantiates a new O auth 2 authorization request redirect provider. * - * @param appId the app id - * @param secret the secret + * @param v3Map the v 3 map */ - public OAuth2AuthorizationRequestRedirectProvider(String appId, String secret) { + public OAuth2AuthorizationRequestRedirectProvider(Map v3Map) { this.objectMapper = new ObjectMapper(); objectMapper.setPropertyNamingStrategy(PropertyNamingStrategy.SNAKE_CASE); objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL); - this.appId = appId; - this.secret = secret; + this.v3Map = v3Map; } /** @@ -59,11 +58,12 @@ public class OAuth2AuthorizationRequestRedirectProvider { * @return uri components */ @SneakyThrows - public String redirect(String state, String redirectUri) { + public String redirect(String tenantId,String state, String redirectUri) { Assert.hasText(redirectUri, "redirectUri is required"); String encode = URLEncoder.encode(redirectUri, StandardCharsets.UTF_8.name()); MultiValueMap queryParams = new LinkedMultiValueMap<>(); - queryParams.add("appid", appId); + WechatPayProperties.V3 v3 = v3Map.get(tenantId); + queryParams.add("appid", v3.getMp().getAppId()); queryParams.add("redirect_uri", encode); queryParams.add("response_type", "code"); queryParams.add("scope", "snsapi_base"); @@ -75,16 +75,19 @@ public class OAuth2AuthorizationRequestRedirectProvider { /** * 微信服务器授权成功后调用redirectUri的处理逻辑. * - * @param code the code + * @param code the code + * @param state the state * @return the string */ @SneakyThrows - public OAuth2Exchange exchange(String code, String state) { + public OAuth2Exchange exchange(String tenantId,String code, String state) { Assert.hasText(code, "wechat pay oauth2 code is required"); Assert.hasText(state, "wechat pay oauth2 state is required"); MultiValueMap queryParams = new LinkedMultiValueMap<>(); - queryParams.add("appid", appId); - queryParams.add("secret", secret); + WechatPayProperties.V3 v3 = v3Map.get(tenantId); + WechatPayProperties.Mp mp = v3.getMp(); + queryParams.add("appid", mp.getAppId()); + queryParams.add("secret", mp.getAppSecret()); queryParams.add("code", code); queryParams.add("state", state); queryParams.add("grant_type", "authorization_code"); diff --git a/payment-spring-boot-autoconfigure/src/main/java/cn/felord/payment/wechat/v3/AbstractApi.java b/payment-spring-boot-autoconfigure/src/main/java/cn/felord/payment/wechat/v3/AbstractApi.java index ea24a0d..f4bf203 100644 --- a/payment-spring-boot-autoconfigure/src/main/java/cn/felord/payment/wechat/v3/AbstractApi.java +++ b/payment-spring-boot-autoconfigure/src/main/java/cn/felord/payment/wechat/v3/AbstractApi.java @@ -16,15 +16,22 @@ import java.net.URI; public abstract class AbstractApi { private final ObjectMapper mapper; private final WechatPayClient wechatPayClient; - private final WechatMetaBean wechatMetaBean; + private final String tenantId; - public AbstractApi(WechatPayClient wechatPayClient, WechatMetaBean wechatMetaBean) { + + + public AbstractApi(WechatPayClient wechatPayClient,String tenantId) { this.mapper = new ObjectMapper(); mapper.setPropertyNamingStrategy(PropertyNamingStrategy.SNAKE_CASE); mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL); this.wechatPayClient = wechatPayClient; - this.wechatMetaBean = wechatMetaBean; + + if (!container().getTenantIds().contains(tenantId)) { + throw new PayException("tenantId is not in wechatMetaContainer "); + } + this.tenantId = tenantId; + } public ObjectMapper getMapper() { @@ -35,8 +42,12 @@ public abstract class AbstractApi { return wechatPayClient; } - public WechatMetaBean meta() { - return wechatMetaBean; + public String tenantId() { + return tenantId; + } + + public WechatMetaContainer container() { + return wechatPayClient.signatureProvider().wechatMetaContainer(); } protected RequestEntity post(URI uri, Object params) { 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 930d7b9..03a2dee 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 @@ -51,38 +51,41 @@ public class SignatureProvider { private static final IdGenerator ID_GENERATOR = new AlternativeJdkIdGenerator(); private static final String SCHEMA = "WECHATPAY2-SHA256-RSA2048 "; - private final RestOperations restOperations = new RestTemplate(); /** * The constant TOKEN_PATTERN. */ public static final String TOKEN_PATTERN = "mchid=\"%s\",nonce_str=\"%s\",timestamp=\"%d\",serial_no=\"%s\",signature=\"%s\""; - private final WechatMetaBean wechatMetaBean; /** * 微信平台证书容器 key = 序列号 value = 证书对象 */ private static final Map CERTIFICATE_MAP = new ConcurrentHashMap<>(); + private final RestOperations restOperations = new RestTemplate(); + private final WechatMetaContainer wechatMetaContainer; /** * Instantiates a new Signature provider. * - * @param wechatMetaBean the wechat meta bean + * @param wechatMetaContainer the wechat meta container */ - public SignatureProvider(WechatMetaBean wechatMetaBean) { - this.wechatMetaBean = wechatMetaBean; + public SignatureProvider(WechatMetaContainer wechatMetaContainer) { + this.wechatMetaContainer = wechatMetaContainer; + wechatMetaContainer.getTenantIds().forEach(this::refreshCertificate); } /** * 我方请求时加签,使用API证书. * + * @param tenantId the properties key * @param method the method * @param canonicalUrl the canonical url * @param body the body * @return the string */ @SneakyThrows - public String requestSign(String method, String canonicalUrl, String body) { + public String requestSign(String tenantId,String method, String canonicalUrl, String body) { Signature signer = Signature.getInstance("SHA256withRSA"); + WechatMetaBean wechatMetaBean = wechatMetaContainer.getWechatMeta(tenantId); signer.initSign(wechatMetaBean.getKeyPair().getPrivate()); long timestamp = System.currentTimeMillis() / 1000; @@ -97,7 +100,7 @@ public class SignatureProvider { String serialNo = wechatMetaBean.getSerialNumber(); // 生成token String token = String.format(TOKEN_PATTERN, - wechatMetaBean.getWechatPayProperties().getV3().getMchId(), + wechatMetaBean.getV3().getMchId(), nonceStr, timestamp, serialNo, encode); return SCHEMA.concat(token); } @@ -113,7 +116,7 @@ public class SignatureProvider { String wechatpaySerial = params.getWechatpaySerial(); if (CERTIFICATE_MAP.isEmpty() || !CERTIFICATE_MAP.containsKey(wechatpaySerial)) { - refreshCertificate(); + wechatMetaContainer.getTenantIds().forEach(this::refreshCertificate); } Certificate certificate = CERTIFICATE_MAP.get(wechatpaySerial); @@ -130,7 +133,7 @@ public class SignatureProvider { * 当我方服务器不存在平台证书或者证书同当前响应报文中的证书序列号不一致时应当刷新 调用/v3/certificates */ @SneakyThrows - private synchronized void refreshCertificate() { + private synchronized void refreshCertificate(String propertiesKey) { String url = WechatPayV3Type.CERT.uri(WeChatServer.CHINA); UriComponents uri = UriComponentsBuilder.fromHttpUrl(url).build(); @@ -143,7 +146,7 @@ public class SignatureProvider { } // 签名 HttpMethod httpMethod = WechatPayV3Type.CERT.method(); - String authorization = requestSign(httpMethod.name(), canonicalUrl, ""); + String authorization = requestSign(propertiesKey,httpMethod.name(), canonicalUrl, ""); HttpHeaders headers = new HttpHeaders(); headers.setAccept(Collections.singletonList(MediaType.APPLICATION_JSON)); @@ -166,7 +169,7 @@ public class SignatureProvider { String associatedData = encryptCertificate.get("associated_data").asText(); String nonce = encryptCertificate.get("nonce").asText(); String ciphertext = encryptCertificate.get("ciphertext").asText(); - String publicKey = decryptResponseBody(associatedData, nonce, ciphertext); + String publicKey = decryptResponseBody(propertiesKey,associatedData, nonce, ciphertext); ByteArrayInputStream inputStream = new ByteArrayInputStream(publicKey.getBytes(StandardCharsets.UTF_8)); Certificate certificate = null; @@ -187,15 +190,16 @@ public class SignatureProvider { /** * 解密响应体. * + * @param tenantId the properties key * @param associatedData the associated data * @param nonce the nonce * @param ciphertext the ciphertext * @return the string */ - public String decryptResponseBody(String associatedData, String nonce, String ciphertext) { + public String decryptResponseBody(String tenantId,String associatedData, String nonce, String ciphertext) { try { Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding"); - String apiV3Key = wechatMetaBean.getWechatPayProperties().getV3().getAppV3Secret(); + String apiV3Key = wechatMetaContainer.getWechatMeta(tenantId).getV3().getAppV3Secret(); SecretKeySpec key = new SecretKeySpec(apiV3Key.getBytes(StandardCharsets.UTF_8), "AES"); GCMParameterSpec spec = new GCMParameterSpec(128, nonce.getBytes(StandardCharsets.UTF_8)); @@ -215,13 +219,14 @@ public class SignatureProvider { } } + /** - * Gets wechat meta bean. + * Wechat meta container. * - * @return the wechat meta bean + * @return the wechat meta container */ - public WechatMetaBean getWechatMetaBean() { - return wechatMetaBean; + public WechatMetaContainer wechatMetaContainer() { + return wechatMetaContainer; } /** diff --git a/payment-spring-boot-autoconfigure/src/main/java/cn/felord/payment/wechat/v3/WechatApiProvider.java b/payment-spring-boot-autoconfigure/src/main/java/cn/felord/payment/wechat/v3/WechatApiProvider.java new file mode 100644 index 0000000..02a0256 --- /dev/null +++ b/payment-spring-boot-autoconfigure/src/main/java/cn/felord/payment/wechat/v3/WechatApiProvider.java @@ -0,0 +1,22 @@ +package cn.felord.payment.wechat.v3; + +/** + * @author Dax + * @since 17:32 + */ +public class WechatApiProvider { + private final WechatPayClient wechatPayClient; + + public WechatApiProvider(WechatPayClient wechatPayClient) { + this.wechatPayClient = wechatPayClient; + } + + public WechatMarketingFavorApi favorApi(String tenantId){ + return new WechatMarketingFavorApi(this.wechatPayClient,tenantId); + } + + public WechatPayApi payApi(String tenantId){ + return new WechatPayApi(wechatPayClient,tenantId); + } + +} diff --git a/payment-spring-boot-autoconfigure/src/main/java/cn/felord/payment/wechat/v3/WechatMarketingFavorApi.java b/payment-spring-boot-autoconfigure/src/main/java/cn/felord/payment/wechat/v3/WechatMarketingFavorApi.java index 9f4f7e6..fbf4b07 100644 --- a/payment-spring-boot-autoconfigure/src/main/java/cn/felord/payment/wechat/v3/WechatMarketingFavorApi.java +++ b/payment-spring-boot-autoconfigure/src/main/java/cn/felord/payment/wechat/v3/WechatMarketingFavorApi.java @@ -1,5 +1,6 @@ package cn.felord.payment.wechat.v3; +import cn.felord.payment.PayException; import cn.felord.payment.wechat.WechatPayProperties; import cn.felord.payment.wechat.enumeration.StockStatus; import cn.felord.payment.wechat.enumeration.WeChatServer; @@ -35,16 +36,13 @@ import java.util.function.Consumer; * @since 18 :22 */ public class WechatMarketingFavorApi extends AbstractApi { - /** - * Instantiates a new Wechat marketing api. - * - * @param wechatPayClient the wechat pay client - * @param wechatMetaBean the wechat meta bean - */ - public WechatMarketingFavorApi(WechatPayClient wechatPayClient, WechatMetaBean wechatMetaBean) { - super(wechatPayClient, wechatMetaBean); + + + public WechatMarketingFavorApi(WechatPayClient wechatPayClient, String tenantId) { + super(wechatPayClient,tenantId); } + /** * 创建代金券批次API * @@ -61,9 +59,11 @@ public class WechatMarketingFavorApi extends AbstractApi { } private RequestEntity createStocksFunction(WechatPayV3Type type, StocksCreateParams params) { - WechatPayProperties.V3 v3 = this.meta().getWechatPayProperties().getV3(); + WechatPayProperties.V3 v3 = this.container().getWechatMeta(tenantId()).getV3(); + String mchId = v3.getMchId(); URI uri = UriComponentsBuilder.fromHttpUrl(type.uri(WeChatServer.CHINA)) + .queryParam("pay_tenantId", this.tenantId()) .build() .toUri(); params.setBelongMerchant(mchId); @@ -103,11 +103,12 @@ public class WechatMarketingFavorApi extends AbstractApi { private RequestEntity sendStocksFunction(WechatPayV3Type type, StocksSendParams params) { - WechatPayProperties.V3 v3 = this.meta().getWechatPayProperties().getV3(); + WechatPayProperties.V3 v3 = this.container().getWechatMeta(tenantId()).getV3(); // 服务号 params.setAppid(v3.getMp().getAppId()); params.setStockCreatorMchid(v3.getMchId()); URI uri = UriComponentsBuilder.fromHttpUrl(type.uri(WeChatServer.CHINA)) + .queryParam("pay_tenantId", this.tenantId()) .build() .expand(params.getOpenid()) .toUri(); @@ -146,12 +147,13 @@ public class WechatMarketingFavorApi extends AbstractApi { } private RequestEntity startAndRestartAndPauseStockFunction(WechatPayV3Type type, String stockId) { - WechatPayProperties.V3 v3 = this.meta().getWechatPayProperties().getV3(); + WechatPayProperties.V3 v3 = this.container().getWechatMeta(tenantId()).getV3(); String mchId = v3.getMchId(); Map body = new HashMap<>(); body.put("stock_creator_mchid", mchId); URI uri = UriComponentsBuilder.fromHttpUrl(type.uri(WeChatServer.CHINA)) + .queryParam("pay_tenantId", this.tenantId()) .build() .expand(stockId) .toUri(); @@ -178,9 +180,11 @@ public class WechatMarketingFavorApi extends AbstractApi { private RequestEntity queryStocksFunction(WechatPayV3Type type, StocksQueryParams params) { MultiValueMap queryParams = new LinkedMultiValueMap<>(); + queryParams.add("pay_tenantId", this.tenantId()); queryParams.add("offset", String.valueOf(params.getOffset())); queryParams.add("limit", String.valueOf(params.getLimit())); - WechatPayProperties.V3 v3 = this.meta().getWechatPayProperties().getV3(); + WechatPayProperties.V3 v3 = this.container().getWechatMeta(tenantId()).getV3(); + queryParams.add("stock_creator_mchid", v3.getMchId()); LocalDateTime createStartTime = params.getCreateStartTime(); if (Objects.nonNull(createStartTime)) { @@ -231,10 +235,12 @@ public class WechatMarketingFavorApi extends AbstractApi { private RequestEntity stockDetailFunction(WechatPayV3Type type, String stockId) { - WechatPayProperties.V3 v3 = this.meta().getWechatPayProperties().getV3(); + WechatPayProperties.V3 v3 = this.container().getWechatMeta(tenantId()).getV3(); + MultiValueMap queryParams = new LinkedMultiValueMap<>(); queryParams.add("stock_creator_mchid", v3.getMchId()); + queryParams.add("pay_tenantId", this.tenantId()); URI uri = UriComponentsBuilder.fromHttpUrl(type.uri(WeChatServer.CHINA)) .queryParams(queryParams) @@ -263,10 +269,12 @@ public class WechatMarketingFavorApi extends AbstractApi { private RequestEntity couponDetailFunction(WechatPayV3Type type, CouponDetailsQueryParams params) { - WechatPayProperties.V3 v3 = this.meta().getWechatPayProperties().getV3(); + WechatPayProperties.V3 v3 = this.container().getWechatMeta(tenantId()).getV3(); + MultiValueMap queryParams = new LinkedMultiValueMap<>(); queryParams.add("appid", v3.getMp().getAppId()); + queryParams.add("pay_tenantId", this.tenantId()); MultiValueMap pathParams = new LinkedMultiValueMap<>(); pathParams.add("openid", params.getOpenId()); @@ -329,10 +337,12 @@ public class WechatMarketingFavorApi extends AbstractApi { private RequestEntity queryUserCouponsFunction(WechatPayV3Type type, UserCouponsQueryParams params) { final String ignore = "available_mchid"; - WechatPayProperties.V3 v3 = this.meta().getWechatPayProperties().getV3(); + WechatPayProperties.V3 v3 = this.container().getWechatMeta(tenantId()).getV3(); + MultiValueMap queryParams = new LinkedMultiValueMap<>(); queryParams.add("appid", v3.getMp().getAppId()); + queryParams.add("pay_tenantId", this.tenantId()); String stockId = params.getStockId(); if (StringUtils.hasText(stockId)) { queryParams.add("stock_id", stockId); @@ -398,6 +408,7 @@ public class WechatMarketingFavorApi extends AbstractApi { private RequestEntity downloadFlowFunction(WechatPayV3Type type, String stockId) { URI uri = UriComponentsBuilder.fromHttpUrl(type.uri(WeChatServer.CHINA)) + .queryParam("pay_tenantId", this.tenantId()) .build() .expand(stockId) .toUri(); @@ -428,13 +439,16 @@ public class WechatMarketingFavorApi extends AbstractApi { byte[] digest = SHA256.Digest.getInstance("SHA-256").digest(file.getBytes()); meta.put("sha256", Hex.toHexString(digest)); - String httpUrl = type.uri(WeChatServer.CHINA); - URI uri = UriComponentsBuilder.fromHttpUrl(httpUrl).build().toUri(); MultiValueMap body = new LinkedMultiValueMap<>(); body.add("meta", meta); body.add("file", file.getResource()); // 签名 String metaStr = this.getMapper().writeValueAsString(meta); + + URI uri = UriComponentsBuilder.fromHttpUrl(type.uri(WeChatServer.CHINA)) + .queryParam("pay_tenantId", this.tenantId()) + .build() + .toUri(); return RequestEntity.post(uri) .header("Content-Type", MediaType.MULTIPART_FORM_DATA_VALUE) .header("Meta-Info", metaStr) @@ -443,9 +457,10 @@ public class WechatMarketingFavorApi extends AbstractApi { /** * 代金券核销回调通知API + * * @param notifyUrl the notify url - * @see WechatPayCallback#wechatPayCouponCallback(ResponseSignVerifyParams, Consumer) * @return the wechat response entity + * @see WechatPayCallback#wechatPayCouponCallback(String, ResponseSignVerifyParams, Consumer) WechatPayCallback#wechatPayCouponCallback(ResponseSignVerifyParams, Consumer) */ public WechatResponseEntity setMarketingFavorCallback(String notifyUrl) { WechatResponseEntity wechatResponseEntity = new WechatResponseEntity<>(); @@ -457,13 +472,16 @@ public class WechatMarketingFavorApi extends AbstractApi { } private RequestEntity setMarketingFavorCallbackFunction(WechatPayV3Type type, String notifyUrl) { - WechatPayProperties.V3 v3 = this.meta().getWechatPayProperties().getV3(); + WechatPayProperties.V3 v3 = this.container().getWechatMeta(tenantId()).getV3(); + Map body = new HashMap<>(3); body.put("mchid", v3.getMchId()); body.put("notify_url", notifyUrl); body.put("switch", true); - String httpUrl = type.uri(WeChatServer.CHINA); - URI uri = UriComponentsBuilder.fromHttpUrl(httpUrl).build().toUri(); + URI uri = UriComponentsBuilder.fromHttpUrl(type.uri(WeChatServer.CHINA)) + .queryParam("pay_tenantId", this.tenantId()) + .build() + .toUri(); return post(uri, body); } diff --git a/payment-spring-boot-autoconfigure/src/main/java/cn/felord/payment/wechat/v3/WechatMetaBean.java b/payment-spring-boot-autoconfigure/src/main/java/cn/felord/payment/wechat/v3/WechatMetaBean.java index 8aaccc1..ca0a222 100644 --- a/payment-spring-boot-autoconfigure/src/main/java/cn/felord/payment/wechat/v3/WechatMetaBean.java +++ b/payment-spring-boot-autoconfigure/src/main/java/cn/felord/payment/wechat/v3/WechatMetaBean.java @@ -16,12 +16,14 @@ import java.security.KeyPair; public class WechatMetaBean implements InitializingBean { private KeyPair keyPair; private String serialNumber; - private WechatPayProperties wechatPayProperties; + private String tenantId; + private WechatPayProperties.V3 v3; @Override public void afterPropertiesSet() throws Exception { - Assert.notNull(wechatPayProperties, "wechatPayProperties is required"); + Assert.notNull(v3, "wechatPayProperties.V3 is required"); Assert.notNull(keyPair, "wechat pay p12 certificate is required"); Assert.hasText(serialNumber, "wechat pay p12 certificate SerialNumber is required"); + Assert.hasText(tenantId, "wechat pay tenantId is required"); } } 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 new file mode 100644 index 0000000..2fcdd73 --- /dev/null +++ b/payment-spring-boot-autoconfigure/src/main/java/cn/felord/payment/wechat/v3/WechatMetaContainer.java @@ -0,0 +1,67 @@ +package cn.felord.payment.wechat.v3; + +import lombok.Data; + +import java.util.*; + +/** + * 配置容器 + * + * @author Dax + * @since 15 :18 + */ +public class WechatMetaContainer { + private final Map wechatMetaBeanMap = new HashMap<>(); + private final Set tenantIds = new HashSet<>(); + + + /** + * Add wechat meta boolean. + * + * @param tenantId the tenantId + * @param wechatMetaBean the wechat meta bean + * @return the boolean + */ + public boolean addWechatMeta(String tenantId, WechatMetaBean wechatMetaBean) { + return Objects.nonNull(this.wechatMetaBeanMap.put(tenantId, wechatMetaBean)); + } + + /** + * Remove wechat meta wechat meta bean. + * + * @param tenantId the tenantId + * @return the wechat meta bean + */ + public WechatMetaBean removeWechatMeta(String tenantId) { + return this.wechatMetaBeanMap.remove(tenantId); + } + + /** + * Gets wechat meta. + * + * @param tenantId the tenantId + * @return the wechat meta + */ + public WechatMetaBean getWechatMeta(String tenantId) { + return Objects.requireNonNull(this.wechatMetaBeanMap.get(tenantId)); + } + + /** + * Add key boolean. + * + * @param tenantId the tenant id + * @return the boolean + */ + public boolean addTenant(String tenantId) { + return tenantIds.add(tenantId); + } + + /** + * Gets properties keys. + * + * @return the properties keys + */ + public Set getTenantIds() { + return tenantIds; + } +} diff --git a/payment-spring-boot-autoconfigure/src/main/java/cn/felord/payment/wechat/v3/WechatPayApi.java b/payment-spring-boot-autoconfigure/src/main/java/cn/felord/payment/wechat/v3/WechatPayApi.java index f76083f..4765539 100644 --- a/payment-spring-boot-autoconfigure/src/main/java/cn/felord/payment/wechat/v3/WechatPayApi.java +++ b/payment-spring-boot-autoconfigure/src/main/java/cn/felord/payment/wechat/v3/WechatPayApi.java @@ -1,9 +1,10 @@ package cn.felord.payment.wechat.v3; +import cn.felord.payment.PayException; import cn.felord.payment.wechat.enumeration.WeChatServer; import cn.felord.payment.wechat.WechatPayProperties; import cn.felord.payment.wechat.enumeration.WechatPayV3Type; -import cn.felord.payment.wechat.v3.model.AppPayParams; +import cn.felord.payment.wechat.v3.model.PayParams; import com.fasterxml.jackson.databind.node.ObjectNode; import org.springframework.http.RequestEntity; import org.springframework.web.util.UriComponentsBuilder; @@ -18,18 +19,17 @@ import java.net.URI; */ public class WechatPayApi extends AbstractApi { - - public WechatPayApi(WechatPayClient wechatPayClient, WechatMetaBean wechatMetaBean) { - super(wechatPayClient, wechatMetaBean); + public WechatPayApi(WechatPayClient wechatPayClient, String tenantId) { + super(wechatPayClient, tenantId); } /** - * APP下单API. + * APP下单API * * @param payParams the pay params * @return the wechat response entity */ - public WechatResponseEntity appPay(AppPayParams payParams) { + public WechatResponseEntity app(PayParams payParams) { WechatResponseEntity wechatResponseEntity = new WechatResponseEntity<>(); this.client().withType(WechatPayV3Type.APP, payParams) .function(this::appPayFunction) @@ -38,12 +38,29 @@ public class WechatPayApi extends AbstractApi { return wechatResponseEntity; } - private RequestEntity appPayFunction(WechatPayV3Type type, AppPayParams payParams) { - WechatPayProperties.V3 v3 = this.meta().getWechatPayProperties().getV3(); + /** + * JSAPI/小程序下单API + * + * @param payParams the pay params + * @return wechat response entity + */ + public WechatResponseEntity js(PayParams payParams) { + WechatResponseEntity wechatResponseEntity = new WechatResponseEntity<>(); + this.client().withType(WechatPayV3Type.JSAPI, payParams) + .function(this::appPayFunction) + .consumer(wechatResponseEntity::convert) + .request(); + return wechatResponseEntity; + } + + private RequestEntity appPayFunction(WechatPayV3Type type, PayParams payParams) { + WechatPayProperties.V3 v3 = this.container().getWechatMeta(tenantId()).getV3(); payParams.setAppid(v3.getAppId()); payParams.setMchid(v3.getMchId()); - String httpUrl = type.uri(WeChatServer.CHINA); - URI uri = UriComponentsBuilder.fromHttpUrl(httpUrl).build().toUri(); + URI uri = UriComponentsBuilder.fromHttpUrl(type.uri(WeChatServer.CHINA)) + .queryParam("pay_tenantId", this.tenantId()) + .build() + .toUri(); return post(uri, payParams); } } diff --git a/payment-spring-boot-autoconfigure/src/main/java/cn/felord/payment/wechat/v3/WechatPayCallback.java b/payment-spring-boot-autoconfigure/src/main/java/cn/felord/payment/wechat/v3/WechatPayCallback.java index 2cdf3f4..804de61 100644 --- a/payment-spring-boot-autoconfigure/src/main/java/cn/felord/payment/wechat/v3/WechatPayCallback.java +++ b/payment-spring-boot-autoconfigure/src/main/java/cn/felord/payment/wechat/v3/WechatPayCallback.java @@ -44,12 +44,13 @@ public class WechatPayCallback { /** * 微信支付代金券核销回调工具. * + * @param tenantId the tenant id * @param params the params * @param couponConsumeDataConsumer the coupon consume data consumer * @return the map */ @SneakyThrows - public Map wechatPayCouponCallback(ResponseSignVerifyParams params, Consumer couponConsumeDataConsumer) { + public Map wechatPayCouponCallback(String tenantId, ResponseSignVerifyParams params, Consumer couponConsumeDataConsumer) { if (signatureProvider.responseSignVerify(params)) { CallbackParams callbackParams = MAPPER.readValue(params.getBody(), CallbackParams.class); @@ -58,7 +59,7 @@ public class WechatPayCallback { String associatedData = resource.getAssociatedData(); String nonce = resource.getNonce(); String ciphertext = resource.getCiphertext(); - String data = signatureProvider.decryptResponseBody(associatedData, nonce, ciphertext); + String data = signatureProvider.decryptResponseBody(tenantId,associatedData, nonce, ciphertext); Assert.hasText(data, "decryptData is required"); CouponConsumeData couponConsumeData = MAPPER.readValue(data, CouponConsumeData.class); couponConsumeDataConsumer.accept(couponConsumeData); diff --git a/payment-spring-boot-autoconfigure/src/main/java/cn/felord/payment/wechat/v3/WechatPayClient.java b/payment-spring-boot-autoconfigure/src/main/java/cn/felord/payment/wechat/v3/WechatPayClient.java index 25d0fa5..0f41c16 100644 --- a/payment-spring-boot-autoconfigure/src/main/java/cn/felord/payment/wechat/v3/WechatPayClient.java +++ b/payment-spring-boot-autoconfigure/src/main/java/cn/felord/payment/wechat/v3/WechatPayClient.java @@ -11,6 +11,7 @@ import org.springframework.http.*; import org.springframework.http.converter.HttpMessageConverter; import org.springframework.http.converter.support.AllEncompassingFormHttpMessageConverter; import org.springframework.util.Assert; +import org.springframework.util.MultiValueMap; import org.springframework.web.client.DefaultResponseErrorHandler; import org.springframework.web.client.RestOperations; import org.springframework.web.client.RestTemplate; @@ -148,17 +149,22 @@ public class WechatPayClient { } // 签名 HttpMethod httpMethod = requestEntity.getMethod(); + Assert.notNull(httpMethod, "httpMethod is required"); + HttpHeaders headers = requestEntity.getHeaders(); T entityBody = requestEntity.getBody(); String body = requestEntity.hasBody() ? Objects.requireNonNull(entityBody).toString() : ""; if (WechatPayV3Type.MARKETING_IMAGE_UPLOAD.pattern().contains(canonicalUrl)) { - body = Objects.requireNonNull(requestEntity.getHeaders().get("Meta-Info")).get(0); + body = Objects.requireNonNull(headers.get("Meta-Info")).get(0); } - String authorization = signatureProvider.requestSign(httpMethod.name(), canonicalUrl, body); + MultiValueMap queryParams = uri.getQueryParams(); + String tenantId = queryParams.getFirst("pay_tenantId"); + Assert.notNull(tenantId, "tenantId is required"); + String authorization = signatureProvider.requestSign(tenantId,httpMethod.name(), canonicalUrl, body); HttpHeaders httpHeaders = new HttpHeaders(); - httpHeaders.addAll(requestEntity.getHeaders()); + httpHeaders.addAll(headers); httpHeaders.setAccept(Collections.singletonList(MediaType.APPLICATION_JSON)); // 兼容图片上传,自定义优先级最高 if (Objects.isNull(httpHeaders.getContentType())) { @@ -211,6 +217,10 @@ public class WechatPayClient { } + public SignatureProvider signatureProvider() { + return signatureProvider; + } + private void applyDefaultRestTemplate() { RestTemplate restTemplate = new RestTemplate(); DefaultResponseErrorHandler errorHandler = new WechatPayResponseErrorHandler(); diff --git a/payment-spring-boot-autoconfigure/src/main/java/cn/felord/payment/wechat/v3/model/AppPayParams.java b/payment-spring-boot-autoconfigure/src/main/java/cn/felord/payment/wechat/v3/model/PayParams.java similarity index 97% rename from payment-spring-boot-autoconfigure/src/main/java/cn/felord/payment/wechat/v3/model/AppPayParams.java rename to payment-spring-boot-autoconfigure/src/main/java/cn/felord/payment/wechat/v3/model/PayParams.java index b062db0..76fc7bc 100644 --- a/payment-spring-boot-autoconfigure/src/main/java/cn/felord/payment/wechat/v3/model/AppPayParams.java +++ b/payment-spring-boot-autoconfigure/src/main/java/cn/felord/payment/wechat/v3/model/PayParams.java @@ -10,7 +10,7 @@ import lombok.Data; * @since 17 :10 */ @Data -public class AppPayParams { +public class PayParams { private String appid; private String mchid; /**