From c324f636d5ff6b1bac5fc8b1d5887b6abebe29e2 Mon Sep 17 00:00:00 2001 From: xiafang Date: Mon, 23 Nov 2020 16:14:28 +0800 Subject: [PATCH] =?UTF-8?q?=E5=88=86=E9=A1=B5=E6=9F=A5=E8=AF=A2=E5=95=86?= =?UTF-8?q?=E6=88=B7=E4=B8=8B=E7=9A=84=E4=BC=98=E6=83=A0=E5=88=B8=E6=89=B9?= =?UTF-8?q?=E6=AC=A1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../wechat/WechatPayConfiguration.java | 15 +++ .../payment/wechat/WechatPayProperties.java | 28 +++++- .../wechat/enumeration/StockStatus.java | 56 ++++++++++++ .../wechat/enumeration/WechatPayV3Type.java | 8 +- ...2AuthorizationRequestRedirectProvider.java | 91 +++++++++++++++++++ .../payment/wechat/v3/WechatPayV3Api.java | 70 +++++++++++--- .../wechat/v3/WechatRequestEntity.java | 69 +------------- .../wechat/v3/model/StocksMchQueryParams.java | 16 ---- .../wechat/v3/model/StocksQueryParams.java | 21 +++++ 9 files changed, 275 insertions(+), 99 deletions(-) create mode 100644 payment-spring-boot-autoconfigure/src/main/java/com/enongm/dianji/payment/wechat/enumeration/StockStatus.java create mode 100644 payment-spring-boot-autoconfigure/src/main/java/com/enongm/dianji/payment/wechat/oauth2/OAuth2AuthorizationRequestRedirectProvider.java delete mode 100644 payment-spring-boot-autoconfigure/src/main/java/com/enongm/dianji/payment/wechat/v3/model/StocksMchQueryParams.java create mode 100644 payment-spring-boot-autoconfigure/src/main/java/com/enongm/dianji/payment/wechat/v3/model/StocksQueryParams.java diff --git a/payment-spring-boot-autoconfigure/src/main/java/com/enongm/dianji/payment/wechat/WechatPayConfiguration.java b/payment-spring-boot-autoconfigure/src/main/java/com/enongm/dianji/payment/wechat/WechatPayConfiguration.java index 1dfc671..c5ea2ad 100644 --- a/payment-spring-boot-autoconfigure/src/main/java/com/enongm/dianji/payment/wechat/WechatPayConfiguration.java +++ b/payment-spring-boot-autoconfigure/src/main/java/com/enongm/dianji/payment/wechat/WechatPayConfiguration.java @@ -1,6 +1,7 @@ package com.enongm.dianji.payment.wechat; +import com.enongm.dianji.payment.wechat.oauth2.OAuth2AuthorizationRequestRedirectProvider; import com.enongm.dianji.payment.wechat.v3.*; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.boot.context.properties.EnableConfigurationProperties; @@ -66,4 +67,18 @@ public class WechatPayConfiguration { public WechatPayV3Api wechatPayV3Api(WechatPayV3Client wechatPayV3Client,WechatMetaBean wechatMetaBean) { return new WechatPayV3Api(wechatPayV3Client,wechatMetaBean); } + + /** + * 公众号授权工具用于获取用户openId,需要配置{@link WechatPayProperties.Mp}. + * + * @param wechatPayProperties the wechat pay properties + * @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()); + } + } diff --git a/payment-spring-boot-autoconfigure/src/main/java/com/enongm/dianji/payment/wechat/WechatPayProperties.java b/payment-spring-boot-autoconfigure/src/main/java/com/enongm/dianji/payment/wechat/WechatPayProperties.java index 3938698..cb34e37 100644 --- a/payment-spring-boot-autoconfigure/src/main/java/com/enongm/dianji/payment/wechat/WechatPayProperties.java +++ b/payment-spring-boot-autoconfigure/src/main/java/com/enongm/dianji/payment/wechat/WechatPayProperties.java @@ -16,6 +16,9 @@ public class WechatPayProperties { @NestedConfigurationProperty private V3 v3; + /** + * wechat pay v3 properties. + */ @Data public static class V3 { /** @@ -27,7 +30,7 @@ public class WechatPayProperties { */ private boolean partnerMode; /** - * app id for wechat pay is required + * app id for wechat pay is required */ private String appId; /** @@ -47,13 +50,34 @@ public class WechatPayProperties { */ private String partnerKey; /** - * wechat pay certificate Path + * wechat pay certificate Path */ private String certPath; /** * your pay server domain */ private String domain; + /** + * wechat mp binding with mch + */ + private Mp mp; + + } + + + /** + * wechat mp for send coupons and notification. + */ + @Data + public static class Mp { + /** + * app id for wechat pay is required + */ + private String appId; + /** + * app secret for wechat pay is required + */ + private String appSecret; } } diff --git a/payment-spring-boot-autoconfigure/src/main/java/com/enongm/dianji/payment/wechat/enumeration/StockStatus.java b/payment-spring-boot-autoconfigure/src/main/java/com/enongm/dianji/payment/wechat/enumeration/StockStatus.java new file mode 100644 index 0000000..8dc9c3e --- /dev/null +++ b/payment-spring-boot-autoconfigure/src/main/java/com/enongm/dianji/payment/wechat/enumeration/StockStatus.java @@ -0,0 +1,56 @@ +package com.enongm.dianji.payment.wechat.enumeration; + +/** + * The enum Stock status. + * + * @author Dax + * @since 15 :17 + */ +public enum StockStatus { + /** + * Unactivated stock status. + */ + UNACTIVATED("unactivated", "未激活"), + /** + * Audit stock status. + */ + AUDIT("audit", "审核中"), + /** + * Running stock status. + */ + RUNNING("running", "运行中"), + /** + * Stoped stock status. + */ + STOPED("stoped", "已停止"), + /** + * Paused stock status. + */ + PAUSED("paused", "暂停发放"); + + private final String value; + private final String description; + + StockStatus(String value, String description) { + this.value = value; + this.description = description; + } + + /** + * Value string. + * + * @return the string + */ + public String value() { + return this.value; + } + + /** + * Description string. + * + * @return the string + */ + public String description() { + return this.description; + } +} diff --git a/payment-spring-boot-autoconfigure/src/main/java/com/enongm/dianji/payment/wechat/enumeration/WechatPayV3Type.java b/payment-spring-boot-autoconfigure/src/main/java/com/enongm/dianji/payment/wechat/enumeration/WechatPayV3Type.java index 2af0af7..d350b53 100644 --- a/payment-spring-boot-autoconfigure/src/main/java/com/enongm/dianji/payment/wechat/enumeration/WechatPayV3Type.java +++ b/payment-spring-boot-autoconfigure/src/main/java/com/enongm/dianji/payment/wechat/enumeration/WechatPayV3Type.java @@ -40,13 +40,17 @@ public enum WechatPayV3Type { */ MARKETING_FAVOR_STOCKS_START(HttpMethod.POST,"%s/v3/marketing/favor/stocks/{stock_id}/start"), /** - * 发放代金券API. + * 发放代金券API & 根据商户号查用户的券. */ MARKETING_FAVOR_USERS_COUPONS(HttpMethod.POST,"%s/v3/marketing/favor/users/{openid}/coupons"), /** * 查询代金券可用商户 */ - MARKETING_FAVOR_STOCKS_MERCHANTS(HttpMethod.GET, "%s/v3/marketing/favor/stocks/{stock_id}/merchants"); + MARKETING_FAVOR_STOCKS_MERCHANTS(HttpMethod.GET, "%s/v3/marketing/favor/stocks/{stock_id}/merchants"), + /** + * 条件查询批次列表API. + */ + MARKETING_FAVOR_STOCKS(HttpMethod.GET, "%s/v3/marketing/favor/stocks"); diff --git a/payment-spring-boot-autoconfigure/src/main/java/com/enongm/dianji/payment/wechat/oauth2/OAuth2AuthorizationRequestRedirectProvider.java b/payment-spring-boot-autoconfigure/src/main/java/com/enongm/dianji/payment/wechat/oauth2/OAuth2AuthorizationRequestRedirectProvider.java new file mode 100644 index 0000000..c2fcdd3 --- /dev/null +++ b/payment-spring-boot-autoconfigure/src/main/java/com/enongm/dianji/payment/wechat/oauth2/OAuth2AuthorizationRequestRedirectProvider.java @@ -0,0 +1,91 @@ +package com.enongm.dianji.payment.wechat.oauth2; + + +import com.enongm.dianji.payment.PayException; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import org.springframework.http.RequestEntity; +import org.springframework.http.ResponseEntity; +import org.springframework.util.Assert; +import org.springframework.util.LinkedMultiValueMap; +import org.springframework.util.MultiValueMap; +import org.springframework.web.client.RestOperations; +import org.springframework.web.client.RestTemplate; +import org.springframework.web.util.UriComponents; +import org.springframework.web.util.UriComponentsBuilder; + +import java.net.URI; +import java.util.Objects; + +/** + * OAuth2 获取用户的公众号授权openid. + * + * @author Dax + * @since 11 :39 + */ +public class OAuth2AuthorizationRequestRedirectProvider { + private static final String AUTHORIZATION_URI = "https://open.weixin.qq.com/connect/oauth2/authorize"; + private static final String TOKEN_URI = "https://api.weixin.qq.com/sns/oauth2/access_token"; + private final RestOperations restOperations = new RestTemplate(); + private final String appId; + private final String secret; + + /** + * Instantiates a new O auth 2 authorization request redirect provider. + * + * @param appId the app id + * @param secret the secret + */ + public OAuth2AuthorizationRequestRedirectProvider(String appId, String secret) { + this.appId = appId; + this.secret = secret; + } + + /** + * 拼接微信公众号授权的url,用以触发用户点击跳转微信授权. + * + * @param phoneNumber the phone number + * @param redirectUri the redirect uri + * @return uri components + */ + public UriComponents redirect(String phoneNumber, String redirectUri) { + MultiValueMap queryParams = new LinkedMultiValueMap<>(); + queryParams.add("appid", appId); + queryParams.add("redirect_uri", redirectUri); + queryParams.add("response_type", "code"); + queryParams.add("scope", "snsapi_base"); + queryParams.add("state", phoneNumber); + return UriComponentsBuilder.fromHttpUrl(AUTHORIZATION_URI).queryParams(queryParams).build(); + } + + + /** + * 微信服务器授权成功后调用redirectUri的处理逻辑. + * + * @param code the code + * @return the string + */ + public String openId(String code) { + Assert.hasText(code, "wechat pay oauth2 code is required"); + MultiValueMap queryParams = new LinkedMultiValueMap<>(); + queryParams.add("appid", appId); + queryParams.add("secret", secret); + queryParams.add("code", code); + queryParams.add("grant_type", "authorization_code"); + URI uri = UriComponentsBuilder.fromHttpUrl(TOKEN_URI) + .queryParams(queryParams) + .build() + .toUri(); + + RequestEntity requestEntity = RequestEntity.get(uri).build(); + ResponseEntity responseEntity = restOperations.exchange(requestEntity, ObjectNode.class); + ObjectNode body = responseEntity.getBody(); + if (Objects.nonNull(body)) { + JsonNode openid = body.get("openid"); + return openid.asText(); + } + throw new PayException("OpenId Not Available"); + } + + +} diff --git a/payment-spring-boot-autoconfigure/src/main/java/com/enongm/dianji/payment/wechat/v3/WechatPayV3Api.java b/payment-spring-boot-autoconfigure/src/main/java/com/enongm/dianji/payment/wechat/v3/WechatPayV3Api.java index 3d6ca5a..b6ecb8c 100644 --- a/payment-spring-boot-autoconfigure/src/main/java/com/enongm/dianji/payment/wechat/v3/WechatPayV3Api.java +++ b/payment-spring-boot-autoconfigure/src/main/java/com/enongm/dianji/payment/wechat/v3/WechatPayV3Api.java @@ -2,10 +2,11 @@ package com.enongm.dianji.payment.wechat.v3; import com.enongm.dianji.payment.PayException; import com.enongm.dianji.payment.wechat.WechatPayProperties; +import com.enongm.dianji.payment.wechat.enumeration.StockStatus; import com.enongm.dianji.payment.wechat.enumeration.WeChatServer; import com.enongm.dianji.payment.wechat.enumeration.WechatPayV3Type; import com.enongm.dianji.payment.wechat.v3.model.AppPayParams; -import com.enongm.dianji.payment.wechat.v3.model.StocksMchQueryParams; +import com.enongm.dianji.payment.wechat.v3.model.StocksQueryParams; import com.enongm.dianji.payment.wechat.v3.model.StocksSendParams; import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.core.JsonProcessingException; @@ -15,11 +16,17 @@ import com.fasterxml.jackson.databind.node.ObjectNode; import org.springframework.http.RequestEntity; import org.springframework.util.LinkedMultiValueMap; import org.springframework.util.MultiValueMap; +import org.springframework.util.StringUtils; +import org.springframework.web.util.UriComponents; import org.springframework.web.util.UriComponentsBuilder; import java.net.URI; +import java.time.LocalDateTime; +import java.time.ZoneOffset; +import java.time.format.DateTimeFormatter; import java.util.HashMap; import java.util.Map; +import java.util.Objects; /** * The type Wechat pay v 3 api. @@ -85,15 +92,15 @@ public class WechatPayV3Api { /** - * 查询代金券可用商户API + * 查询该代金券可用的商户 * * @param params the params * @return the wechat response entity */ - public WechatResponseEntity queryMerchantsByStockId(StocksMchQueryParams params) { + public WechatResponseEntity queryMerchantsByStockId(StocksQueryParams params) { WechatResponseEntity wechatResponseEntity = new WechatResponseEntity<>(); wechatPayV3Client.withType(WechatPayV3Type.MARKETING_FAVOR_STOCKS_MERCHANTS, params) - .function(this::queryMerchantsFunction) + .function(this::queryStocksFunction) .consumer(wechatResponseEntity::convert) .request(); @@ -102,22 +109,63 @@ public class WechatPayV3Api { } - private RequestEntity queryMerchantsFunction(WechatPayV3Type type, StocksMchQueryParams params) { + /** + * 分页查询商户下的代金券批次. + * + * @param params the params + * @return the wechat response entity + */ + public WechatResponseEntity queryStocksByMch(StocksQueryParams params) { + WechatResponseEntity wechatResponseEntity = new WechatResponseEntity<>(); + wechatPayV3Client.withType(WechatPayV3Type.MARKETING_FAVOR_STOCKS, params) + .function(this::queryStocksFunction) + .consumer(wechatResponseEntity::convert) + .request(); + + return wechatResponseEntity; + } + + + private RequestEntity queryStocksFunction(WechatPayV3Type type, StocksQueryParams params) { MultiValueMap queryParams = new LinkedMultiValueMap<>(); queryParams.add("offset", String.valueOf(params.getOffset())); queryParams.add("limit", String.valueOf(params.getLimit())); - queryParams.add("stock_creator_mchid", params.getStockCreatorMchid()); - String httpUrl = type.uri(WeChatServer.CHINA); + WechatPayProperties.V3 v3 = wechatMetaBean.getWechatPayProperties().getV3(); + queryParams.add("stock_creator_mchid", v3.getMchId()); + LocalDateTime createStartTime = params.getCreateStartTime(); + if (Objects.nonNull(createStartTime)) { + //rfc 3339 YYYY-MM-DDTHH:mm:ss.sss+TIMEZONE + queryParams.add("create_start_time", createStartTime.atOffset(ZoneOffset.ofHours(8)) + .format(DateTimeFormatter.ISO_OFFSET_DATE_TIME)); + } + LocalDateTime createEndTime = params.getCreateEndTime(); + if (Objects.nonNull(createEndTime)) { + //rfc 3339 YYYY-MM-DDTHH:mm:ss.sss+TIMEZONE + queryParams.add("create_end_time", createEndTime.atOffset(ZoneOffset.ofHours(8)) + .format(DateTimeFormatter.ISO_OFFSET_DATE_TIME)); + } + StockStatus status = params.getStatus(); + if (Objects.nonNull(status)) { + queryParams.add("status", status.value()); + } - URI uri = UriComponentsBuilder.fromHttpUrl(httpUrl) + String stockId = params.getStockId(); + + UriComponents uriComponents = UriComponentsBuilder.fromHttpUrl(type.uri(WeChatServer.CHINA)) .queryParams(queryParams) - .build() - .expand(params.getStockId()).toUri(); + .build(); + if (StringUtils.hasText(stockId)) { + uriComponents = uriComponents.expand(stockId); + } + + URI uri = uriComponents + .toUri(); return RequestEntity.get(uri).build(); } + /** * 发放代金券API. * @@ -134,7 +182,7 @@ public class WechatPayV3Api { } - private RequestEntity sendStocksFunction(WechatPayV3Type type,StocksSendParams params){ + private RequestEntity sendStocksFunction(WechatPayV3Type type, StocksSendParams params) { String httpUrl = type.uri(WeChatServer.CHINA); URI uri = UriComponentsBuilder.fromHttpUrl(httpUrl).build().expand(params.getOpenid()).toUri(); diff --git a/payment-spring-boot-autoconfigure/src/main/java/com/enongm/dianji/payment/wechat/v3/WechatRequestEntity.java b/payment-spring-boot-autoconfigure/src/main/java/com/enongm/dianji/payment/wechat/v3/WechatRequestEntity.java index be9ae1b..2bc6c33 100644 --- a/payment-spring-boot-autoconfigure/src/main/java/com/enongm/dianji/payment/wechat/v3/WechatRequestEntity.java +++ b/payment-spring-boot-autoconfigure/src/main/java/com/enongm/dianji/payment/wechat/v3/WechatRequestEntity.java @@ -20,77 +20,10 @@ import java.util.function.Consumer; * @since 14 :01 */ @Getter -public class WechatRequestEntity extends RequestEntity { +public class WechatRequestEntity extends RequestEntity { private final Consumer> responseBodyConsumer; - - /** - * Instantiates a new Wechat request entity. - * - * @param method the method - * @param url the url - * @param responseBodyConsumer the response body consumer - */ - public WechatRequestEntity(HttpMethod method, URI url, Consumer> responseBodyConsumer) { - super(method, url); - this.responseBodyConsumer = responseBodyConsumer; - } - - /** - * Instantiates a new Wechat request entity. - * - * @param body the body - * @param method the method - * @param url the url - * @param responseBodyConsumer the response body consumer - */ - public WechatRequestEntity(T body, HttpMethod method, URI url, Consumer> responseBodyConsumer) { - super(body, method, url); - this.responseBodyConsumer = responseBodyConsumer; - } - - /** - * Instantiates a new Wechat request entity. - * - * @param body the body - * @param method the method - * @param url the url - * @param type the type - * @param responseBodyConsumer the response body consumer - */ - public WechatRequestEntity(T body, HttpMethod method, URI url, Type type, Consumer> responseBodyConsumer) { - super(body, method, url, type); - this.responseBodyConsumer = responseBodyConsumer; - } - - /** - * Instantiates a new Wechat request entity. - * - * @param headers the headers - * @param method the method - * @param url the url - * @param responseBodyConsumer the response body consumer - */ - public WechatRequestEntity(MultiValueMap headers, HttpMethod method, URI url, Consumer> responseBodyConsumer) { - super(headers, method, url); - this.responseBodyConsumer = responseBodyConsumer; - } - - /** - * Instantiates a new Wechat request entity. - * - * @param body the body - * @param headers the headers - * @param method the method - * @param url the url - * @param responseBodyConsumer the response body consumer - */ - public WechatRequestEntity(T body, MultiValueMap headers, HttpMethod method, URI url, Consumer> responseBodyConsumer) { - super(body, headers, method, url); - this.responseBodyConsumer = responseBodyConsumer; - } - /** * Instantiates a new Wechat request entity. * diff --git a/payment-spring-boot-autoconfigure/src/main/java/com/enongm/dianji/payment/wechat/v3/model/StocksMchQueryParams.java b/payment-spring-boot-autoconfigure/src/main/java/com/enongm/dianji/payment/wechat/v3/model/StocksMchQueryParams.java deleted file mode 100644 index 5bab7f6..0000000 --- a/payment-spring-boot-autoconfigure/src/main/java/com/enongm/dianji/payment/wechat/v3/model/StocksMchQueryParams.java +++ /dev/null @@ -1,16 +0,0 @@ -package com.enongm.dianji.payment.wechat.v3.model; - -import lombok.Data; - -/** - * @author Dax - * @since 16:22 - */ -@Data -public class StocksMchQueryParams { - private int offset =1; - private int limit = 10; - private String stockCreatorMchid; - private String stockId; - -} diff --git a/payment-spring-boot-autoconfigure/src/main/java/com/enongm/dianji/payment/wechat/v3/model/StocksQueryParams.java b/payment-spring-boot-autoconfigure/src/main/java/com/enongm/dianji/payment/wechat/v3/model/StocksQueryParams.java new file mode 100644 index 0000000..35f7bc5 --- /dev/null +++ b/payment-spring-boot-autoconfigure/src/main/java/com/enongm/dianji/payment/wechat/v3/model/StocksQueryParams.java @@ -0,0 +1,21 @@ +package com.enongm.dianji.payment.wechat.v3.model; + +import com.enongm.dianji.payment.wechat.enumeration.StockStatus; +import lombok.Data; + +import java.time.LocalDateTime; + +/** + * @author Dax + * @since 15:16 + */ +@Data +public class StocksQueryParams { + private int offset =0; + private int limit = 10; + private String stockId; + private LocalDateTime createStartTime; + private LocalDateTime createEndTime; + private StockStatus status; + +}