分页查询商户下的优惠券批次

This commit is contained in:
xiafang
2020-11-23 16:14:28 +08:00
parent c51488af18
commit c324f636d5
9 changed files with 275 additions and 99 deletions

View File

@@ -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());
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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");

View File

@@ -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<String, String> 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<String, String> 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<Void> requestEntity = RequestEntity.get(uri).build();
ResponseEntity<ObjectNode> 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");
}
}

View File

@@ -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<ObjectNode> queryMerchantsByStockId(StocksMchQueryParams params) {
public WechatResponseEntity<ObjectNode> queryMerchantsByStockId(StocksQueryParams params) {
WechatResponseEntity<ObjectNode> 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<ObjectNode> queryStocksByMch(StocksQueryParams params) {
WechatResponseEntity<ObjectNode> 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<String, String> 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();

View File

@@ -20,77 +20,10 @@ import java.util.function.Consumer;
* @since 14 :01
*/
@Getter
public class WechatRequestEntity<T> extends RequestEntity<T> {
public class WechatRequestEntity<T> extends RequestEntity<T> {
private final Consumer<ResponseEntity<ObjectNode>> 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<ResponseEntity<ObjectNode>> 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<ResponseEntity<ObjectNode>> 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<ResponseEntity<ObjectNode>> 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<String, String> headers, HttpMethod method, URI url, Consumer<ResponseEntity<ObjectNode>> 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<String, String> headers, HttpMethod method, URI url, Consumer<ResponseEntity<ObjectNode>> responseBodyConsumer) {
super(body, headers, method, url);
this.responseBodyConsumer = responseBodyConsumer;
}
/**
* Instantiates a new Wechat request entity.
*

View File

@@ -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;
}

View File

@@ -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;
}