-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
4 changed files
with
164 additions
and
0 deletions.
There are no files selected for viewing
11 changes: 11 additions & 0 deletions
11
src/main/java/io/github/dbstarll/weixin/sdk/SecretHolder.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
package io.github.dbstarll.weixin.sdk; | ||
|
||
public interface SecretHolder { | ||
/** | ||
* 根据AppId获得Secret. | ||
* | ||
* @param appId appId | ||
* @return appId对应的secret | ||
*/ | ||
String getSecret(String appId); | ||
} |
80 changes: 80 additions & 0 deletions
80
src/main/java/io/github/dbstarll/weixin/sdk/WeChatApi.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,80 @@ | ||
package io.github.dbstarll.weixin.sdk; | ||
|
||
import com.fasterxml.jackson.databind.ObjectMapper; | ||
import com.fasterxml.jackson.databind.node.ObjectNode; | ||
import io.github.dbstarll.utils.http.client.request.RelativeUriResolver; | ||
import io.github.dbstarll.utils.json.jackson.JsonApiClient; | ||
import io.github.dbstarll.utils.net.api.ApiException; | ||
import org.apache.hc.client5.http.classic.HttpClient; | ||
import org.apache.hc.core5.http.ClassicHttpRequest; | ||
import org.apache.hc.core5.http.io.support.ClassicRequestBuilder; | ||
|
||
import java.io.IOException; | ||
|
||
import static org.apache.commons.lang3.Validate.notBlank; | ||
import static org.apache.commons.lang3.Validate.notNull; | ||
|
||
public class WeChatApi extends JsonApiClient { | ||
private final SecretHolder secretHolder; | ||
|
||
/** | ||
* 构造WeChatApi. | ||
* | ||
* @param httpClient httpClient | ||
* @param objectMapper objectMapper | ||
* @param secretHolder SecretHolder | ||
*/ | ||
public WeChatApi(final HttpClient httpClient, final ObjectMapper objectMapper, final SecretHolder secretHolder) { | ||
super(httpClient, true, objectMapper); | ||
this.secretHolder = notNull(secretHolder, "secretHolder not set"); | ||
setUriResolver(new RelativeUriResolver("https://api.weixin.qq.com")); | ||
} | ||
|
||
@Override | ||
protected <T> T postProcessing(final ClassicHttpRequest request, final T executeResult) throws ApiException { | ||
final T superResult = super.postProcessing(request, executeResult); | ||
if (superResult instanceof ObjectNode) { | ||
final ObjectNode node = (ObjectNode) superResult; | ||
final int errcode = node.path("errcode").asInt(0); | ||
if (errcode != 0) { | ||
throw new WeChatResponseException(errcode, node.path("errmsg").asText()); | ||
} | ||
} | ||
return superResult; | ||
} | ||
|
||
/** | ||
* 登录凭证校验. | ||
* | ||
* @param appId 小程序 appId | ||
* @param code 登录时获取的 code | ||
* @return 登录凭证 | ||
* @throws IOException in case of a problem or the connection was aborted | ||
* @throws ApiException in case of an api error | ||
*/ | ||
public ObjectNode session(final String appId, final String code) throws IOException, ApiException { | ||
return execute(auth(post("/sns/jscode2session") | ||
.addParameter("grant_type", "authorization_code") | ||
.addParameter("js_code", notBlank(code, "code not set")), appId), ObjectNode.class); | ||
} | ||
|
||
/** | ||
* 获取小程序全局唯一后台接口调用凭据,token有效期为7200s,开发者需要进行妥善保存. | ||
* | ||
* @param appId 小程序唯一凭证,即 AppID,可在「微信公众平台 - 设置 - 开发设置」页中获得 | ||
* @return 接口调用凭据 | ||
* @throws IOException in case of a problem or the connection was aborted | ||
* @throws ApiException in case of an api error | ||
*/ | ||
public ObjectNode accessToken(final String appId) throws IOException, ApiException { | ||
return execute(auth(get("/cgi-bin/token") | ||
.addParameter("grant_type", "client_credential"), appId), ObjectNode.class); | ||
} | ||
|
||
private ClassicHttpRequest auth(final ClassicRequestBuilder builder, final String appId) { | ||
return builder | ||
.addParameter("appid", notBlank(appId, "appId not set")) | ||
.addParameter("secret", notBlank(secretHolder.getSecret(appId), "secret not found for {}", appId)) | ||
.build(); | ||
} | ||
} |
31 changes: 31 additions & 0 deletions
31
src/main/java/io/github/dbstarll/weixin/sdk/WeChatResponseException.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
package io.github.dbstarll.weixin.sdk; | ||
|
||
import io.github.dbstarll.utils.net.api.ApiResponseException; | ||
import org.apache.commons.lang3.StringUtils; | ||
import org.apache.hc.client5.http.HttpResponseException; | ||
|
||
public class WeChatResponseException extends ApiResponseException { | ||
private static final String RID_SPLIT_TOKEN = " rid: "; | ||
|
||
private final String rid; | ||
|
||
/** | ||
* 构建WeChatResponseException. | ||
* | ||
* @param errCode 错误码 | ||
* @param errMsg 错误信息 | ||
*/ | ||
public WeChatResponseException(final int errCode, final String errMsg) { | ||
super(new HttpResponseException(errCode, StringUtils.substringBefore(errMsg, RID_SPLIT_TOKEN))); | ||
this.rid = StringUtils.substringAfter(errMsg, RID_SPLIT_TOKEN); | ||
} | ||
|
||
/** | ||
* 获得rid信息. | ||
* | ||
* @return rid信息 | ||
*/ | ||
public final String getRid() { | ||
return rid; | ||
} | ||
} |
42 changes: 42 additions & 0 deletions
42
src/test/java/io/github/dbstarll/weixin/sdk/WeChatApiTest.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
package io.github.dbstarll.weixin.sdk; | ||
|
||
import com.fasterxml.jackson.databind.ObjectMapper; | ||
import io.github.dbstarll.utils.http.client.HttpClientFactory; | ||
import org.apache.hc.client5.http.classic.HttpClient; | ||
import org.apache.hc.client5.http.impl.classic.CloseableHttpClient; | ||
import org.junit.jupiter.api.Test; | ||
import org.junit.jupiter.api.function.ThrowingConsumer; | ||
|
||
import static org.junit.jupiter.api.Assertions.assertEquals; | ||
import static org.junit.jupiter.api.Assertions.assertNotNull; | ||
import static org.junit.jupiter.api.Assertions.assertThrowsExactly; | ||
|
||
class WeChatApiTest { | ||
private void useClient(final ThrowingConsumer<HttpClient> consumer) throws Throwable { | ||
try (CloseableHttpClient client = new HttpClientFactory().build()) { | ||
consumer.accept(client); | ||
} | ||
} | ||
|
||
private void useApi(final ThrowingConsumer<WeChatApi> consumer, final SecretHolder secretHolder) throws Throwable { | ||
useClient(httpClient -> consumer.accept(new WeChatApi(httpClient, new ObjectMapper(), secretHolder))); | ||
} | ||
|
||
@Test | ||
void nullSecretHolder() { | ||
final Exception e = assertThrowsExactly(NullPointerException.class, | ||
() -> useApi(api -> { | ||
}, null)); | ||
assertEquals("secretHolder not set", e.getMessage()); | ||
} | ||
|
||
@Test | ||
void accessToken() throws Throwable { | ||
useApi(api -> { | ||
final WeChatResponseException e = assertThrowsExactly(WeChatResponseException.class, () -> api.accessToken("appId")); | ||
assertEquals(40013, e.getStatusCode()); | ||
assertEquals("invalid appid", e.getReasonPhrase()); | ||
assertNotNull(e.getRid()); | ||
}, appId -> "secret"); | ||
} | ||
} |