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;
|
package cn.felord.payment.wechat;
|
||||||
|
|
||||||
|
|
||||||
import cn.felord.payment.wechat.oauth2.OAuth2AuthorizationRequestRedirectProvider;
|
|
||||||
import cn.felord.payment.wechat.v3.*;
|
import cn.felord.payment.wechat.v3.*;
|
||||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
|
||||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||||
import org.springframework.context.annotation.Bean;
|
import org.springframework.context.annotation.Bean;
|
||||||
import org.springframework.context.annotation.Configuration;
|
import org.springframework.context.annotation.Configuration;
|
||||||
@@ -86,18 +84,4 @@ public class WechatPayConfiguration {
|
|||||||
public WechatPayCallback wechatPayCallback(SignatureProvider signatureProvider) {
|
public WechatPayCallback wechatPayCallback(SignatureProvider signatureProvider) {
|
||||||
return new WechatPayCallback(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;
|
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.
|
* wechat mp for send coupons and notification.
|
||||||
*/
|
*/
|
||||||
@Data
|
@Data
|
||||||
public static class Mp {
|
public static class App {
|
||||||
/**
|
/**
|
||||||
* app id for wechat pay is required
|
* app id
|
||||||
*/
|
*/
|
||||||
private String appId;
|
private String appId;
|
||||||
/**
|
/**
|
||||||
* app secret for wechat pay is required
|
* app secret
|
||||||
*/
|
*/
|
||||||
private String appSecret;
|
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) {
|
private RequestEntity<?> sendStocksFunction(WechatPayV3Type type, StocksSendParams params) {
|
||||||
WechatPayProperties.V3 v3 = this.container().getWechatMeta(tenantId()).getV3();
|
WechatPayProperties.V3 v3 = this.container().getWechatMeta(tenantId()).getV3();
|
||||||
// 服务号
|
|
||||||
params.setAppid(v3.getMp().getAppId());
|
params.setAppid(v3.getApp().getAppId());
|
||||||
params.setStockCreatorMchid(v3.getMchId());
|
params.setStockCreatorMchid(v3.getMchId());
|
||||||
URI uri = UriComponentsBuilder.fromHttpUrl(type.uri(WeChatServer.CHINA))
|
URI uri = UriComponentsBuilder.fromHttpUrl(type.uri(WeChatServer.CHINA))
|
||||||
.build()
|
.build()
|
||||||
@@ -268,7 +268,7 @@ public class WechatMarketingFavorApi extends AbstractApi {
|
|||||||
|
|
||||||
|
|
||||||
MultiValueMap<String, String> queryParams = new LinkedMultiValueMap<>();
|
MultiValueMap<String, String> queryParams = new LinkedMultiValueMap<>();
|
||||||
queryParams.add("appid", v3.getMp().getAppId());
|
queryParams.add("appid", v3.getApp().getAppId());
|
||||||
|
|
||||||
|
|
||||||
MultiValueMap<String, String> pathParams = new LinkedMultiValueMap<>();
|
MultiValueMap<String, String> pathParams = new LinkedMultiValueMap<>();
|
||||||
@@ -331,21 +331,12 @@ public class WechatMarketingFavorApi extends AbstractApi {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private RequestEntity<?> queryUserCouponsFunction(WechatPayV3Type type, UserCouponsQueryParams params) {
|
private RequestEntity<?> queryUserCouponsFunction(WechatPayV3Type type, UserCouponsQueryParams params) {
|
||||||
final String ignore = "available_mchid";
|
|
||||||
WechatPayProperties.V3 v3 = this.container().getWechatMeta(tenantId()).getV3();
|
WechatPayProperties.V3 v3 = this.container().getWechatMeta(tenantId()).getV3();
|
||||||
|
|
||||||
MultiValueMap<String, String> queryParams = new LinkedMultiValueMap<>();
|
MultiValueMap<String, String> queryParams = new LinkedMultiValueMap<>();
|
||||||
queryParams.add("appid", v3.getMp().getAppId());
|
queryParams.add("appid", v3.getApp().getAppId());
|
||||||
String stockId = params.getStockId();
|
queryParams.add("creator_mchid", v3.getMchId());
|
||||||
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);
|
|
||||||
}
|
|
||||||
String senderMchId = params.getSenderMchId();
|
String senderMchId = params.getSenderMchId();
|
||||||
if (StringUtils.hasText(senderMchId)) {
|
if (StringUtils.hasText(senderMchId)) {
|
||||||
queryParams.add("sender_mchid", senderMchId);
|
queryParams.add("sender_mchid", senderMchId);
|
||||||
@@ -353,11 +344,19 @@ public class WechatMarketingFavorApi extends AbstractApi {
|
|||||||
String availableMchId = params.getAvailableMchId();
|
String availableMchId = params.getAvailableMchId();
|
||||||
if (StringUtils.hasText(availableMchId)) {
|
if (StringUtils.hasText(availableMchId)) {
|
||||||
queryParams.add("available_mchid", 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))
|
URI uri = UriComponentsBuilder.fromHttpUrl(type.uri(WeChatServer.CHINA))
|
||||||
.queryParams(queryParams)
|
.queryParams(queryParams)
|
||||||
|
|||||||
Reference in New Issue
Block a user