mirror of
https://github.com/dromara/payment-spring-boot.git
synced 2026-03-14 22:03:41 +08:00
微信支付文档描述错误导致的问题修复,大坑
This commit is contained in:
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user