微信支付文档描述错误导致的问题修复,大坑

This commit is contained in:
xiafang
2020-12-08 16:40:43 +08:00
parent ea05ff96d6
commit 2ab466c745
6 changed files with 23 additions and 233 deletions

View File

@@ -1,9 +1,7 @@
package cn.felord.payment.wechat;
import cn.felord.payment.wechat.oauth2.OAuth2AuthorizationRequestRedirectProvider;
import cn.felord.payment.wechat.v3.*;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@@ -86,18 +84,4 @@ public class WechatPayConfiguration {
public WechatPayCallback wechatPayCallback(SignatureProvider signatureProvider) {
return new WechatPayCallback(signatureProvider);
}
/**
* 公众号授权工具用于获取用户openId需要配置{@link WechatPayProperties.Mp}.
*
* @param wechatPayProperties the wechat pay properties
* @return the o auth 2 authorization request redirect provider
*/
@Bean
public OAuth2AuthorizationRequestRedirectProvider oAuth2Provider(WechatPayProperties wechatPayProperties) {
Map<String, WechatPayProperties.V3> v3Map = wechatPayProperties.getV3();
return new OAuth2AuthorizationRequestRedirectProvider(v3Map);
}
}

View File

@@ -52,9 +52,9 @@ public class WechatPayProperties {
*/
private String domain;
/**
* wechat mp binding with mch
* app in winxin open platform
*/
private Mp mp;
private App app;
}
@@ -63,13 +63,13 @@ public class WechatPayProperties {
* wechat mp for send coupons and notification.
*/
@Data
public static class Mp {
public static class App {
/**
* app id for wechat pay is required
* app id
*/
private String appId;
/**
* app secret for wechat pay is required
* app secret
*/
private String appSecret;
}

View File

@@ -1,110 +0,0 @@
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;
import lombok.SneakyThrows;
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.UriComponentsBuilder;
import java.net.URI;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.Map;
import java.util.Objects;
/**
* OAuth2 获取用户的公众号授权openid.
* 1.需要微信公众号服务号
* 2.需要微信公众号服务号绑定微信开放平台
* 3.需要正确设置授权回调
*
* @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 ObjectMapper objectMapper;
private final Map<String, WechatPayProperties.V3> v3Map;
/**
* Instantiates a new O auth 2 authorization request redirect provider.
*
* @param v3Map the v 3 map
*/
public OAuth2AuthorizationRequestRedirectProvider(Map<String, WechatPayProperties.V3> v3Map) {
this.objectMapper = new ObjectMapper();
objectMapper.setPropertyNamingStrategy(PropertyNamingStrategy.SNAKE_CASE);
objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
this.v3Map = v3Map;
}
/**
* 拼接微信公众号授权的url,用以触发用户点击跳转微信授权.
*
* @param state the state
* @param redirectUri the redirect uri
* @return uri components
*/
@SneakyThrows
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<String, String> queryParams = new LinkedMultiValueMap<>();
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");
queryParams.add("state", state);
return UriComponentsBuilder.fromHttpUrl(AUTHORIZATION_URI).queryParams(queryParams).build().toUriString() + "#wechat_redirect";
}
/**
* 微信服务器授权成功后调用redirectUri的处理逻辑.
*
* @param code the code
* @param state the state
* @return the string
*/
@SneakyThrows
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<String, String> queryParams = new LinkedMultiValueMap<>();
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");
URI uri = UriComponentsBuilder.fromHttpUrl(TOKEN_URI)
.queryParams(queryParams)
.build()
.toUri();
RequestEntity<Void> requestEntity = RequestEntity.get(uri).build();
ResponseEntity<String> responseEntity = restOperations.exchange(requestEntity, String.class);
String body = responseEntity.getBody();
if (Objects.nonNull(body)) {
return objectMapper.readValue(body, OAuth2Exchange.class);
}
throw new PayException("Wechat OAuth2 Authorization failed");
}
}

View File

@@ -1,17 +0,0 @@
package cn.felord.payment.wechat.oauth2;
import lombok.Data;
/**
* OAuth2 Exchange
* @author Dax
* @since 13:14
*/
@Data
public class OAuth2Exchange {
private String accessToken;
private String refreshToken;
private Long expiresIn;
private String openid;
private String scope;
}

View File

@@ -1,66 +0,0 @@
package cn.felord.payment.wechat.v3;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.JsonNodeFactory;
import com.fasterxml.jackson.databind.node.ObjectNode;
import org.springframework.http.HttpInputMessage;
import org.springframework.http.HttpOutputMessage;
import org.springframework.http.MediaType;
import org.springframework.http.converter.*;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.util.MimeType;
import org.springframework.util.StreamUtils;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Type;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.Objects;
/**
* @author Dax
* @since 12:46
*/
public class DownloadHttpMessageConverter extends AbstractHttpMessageConverter<ObjectNode> {
public DownloadHttpMessageConverter() {
}
public DownloadHttpMessageConverter(MediaType supportedMediaType) {
super(supportedMediaType);
}
@Override
protected boolean supports(Class<?> clazz) {
return clazz.isAssignableFrom(ObjectNode.class);
}
@Override
protected ObjectNode readInternal(Class<? extends ObjectNode> clazz, HttpInputMessage inputMessage) throws IOException, HttpMessageNotReadableException {
Charset charset = getContentTypeCharset(inputMessage.getHeaders().getContentType());
String s = StreamUtils.copyToString(inputMessage.getBody(), charset);
return new ObjectNode(JsonNodeFactory.withExactBigDecimals(true)).put("result", s);
}
@Override
protected void writeInternal(ObjectNode jsonNodes, HttpOutputMessage outputMessage) throws IOException, HttpMessageNotWritableException {
}
private Charset getContentTypeCharset(@Nullable MediaType contentType) {
if (contentType != null && contentType.getCharset() != null) {
return contentType.getCharset();
}
else if (contentType != null && contentType.isCompatibleWith(MediaType.APPLICATION_JSON)) {
// Matching to AbstractJackson2HttpMessageConverter#DEFAULT_CHARSET
return StandardCharsets.UTF_8;
}
else {
Charset charset = getDefaultCharset();
Assert.state(charset != null, "No default charset");
return charset;
}
}
}

View File

@@ -103,8 +103,8 @@ public class WechatMarketingFavorApi extends AbstractApi {
private RequestEntity<?> sendStocksFunction(WechatPayV3Type type, StocksSendParams params) {
WechatPayProperties.V3 v3 = this.container().getWechatMeta(tenantId()).getV3();
// 服务号
params.setAppid(v3.getMp().getAppId());
params.setAppid(v3.getApp().getAppId());
params.setStockCreatorMchid(v3.getMchId());
URI uri = UriComponentsBuilder.fromHttpUrl(type.uri(WeChatServer.CHINA))
.build()
@@ -268,7 +268,7 @@ public class WechatMarketingFavorApi extends AbstractApi {
MultiValueMap<String, String> queryParams = new LinkedMultiValueMap<>();
queryParams.add("appid", v3.getMp().getAppId());
queryParams.add("appid", v3.getApp().getAppId());
MultiValueMap<String, String> pathParams = new LinkedMultiValueMap<>();
@@ -331,21 +331,12 @@ public class WechatMarketingFavorApi extends AbstractApi {
}
private RequestEntity<?> queryUserCouponsFunction(WechatPayV3Type type, UserCouponsQueryParams params) {
final String ignore = "available_mchid";
WechatPayProperties.V3 v3 = this.container().getWechatMeta(tenantId()).getV3();
MultiValueMap<String, String> queryParams = new LinkedMultiValueMap<>();
queryParams.add("appid", v3.getMp().getAppId());
String stockId = params.getStockId();
if (StringUtils.hasText(stockId)) {
queryParams.add("stock_id", stockId);
}
String status = Objects.nonNull(params.getStatus()) ? params.getStatus().name() : ignore;
queryParams.add("status", status);
String creatorMchId = params.getCreatorMchId();
if (StringUtils.hasText(creatorMchId)) {
queryParams.add("creator_mchid", creatorMchId);
}
queryParams.add("appid", v3.getApp().getAppId());
queryParams.add("creator_mchid", v3.getMchId());
String senderMchId = params.getSenderMchId();
if (StringUtils.hasText(senderMchId)) {
queryParams.add("sender_mchid", senderMchId);
@@ -353,11 +344,19 @@ public class WechatMarketingFavorApi extends AbstractApi {
String availableMchId = params.getAvailableMchId();
if (StringUtils.hasText(availableMchId)) {
queryParams.add("available_mchid", availableMchId);
} else {
String offset = Objects.isNull(params.getOffset()) ? null : params.getOffset().toString();
queryParams.add("offset", offset);
String limit = Objects.isNull(params.getLimit()) ? null : params.getLimit().toString();
queryParams.add("limit", limit);
String status = Objects.nonNull(params.getStatus()) ? params.getStatus().name() : null;
queryParams.add("status", status);
String stockId = params.getStockId();
if (StringUtils.hasText(stockId)) {
queryParams.add("stock_id", stockId);
}
}
String offset = Objects.isNull(params.getOffset()) ? ignore : params.getOffset().toString();
queryParams.add("offset", offset);
String limit = Objects.isNull(params.getLimit()) ? ignore : params.getLimit().toString();
queryParams.add("limit", limit);
URI uri = UriComponentsBuilder.fromHttpUrl(type.uri(WeChatServer.CHINA))
.queryParams(queryParams)