diff --git a/weixin-java-common/src/main/java/me/chanjar/weixin/common/error/WxMaErrorMsgEnum.java b/weixin-java-common/src/main/java/me/chanjar/weixin/common/error/WxMaErrorMsgEnum.java
index 10cbe5436..46c1d3d3d 100644
--- a/weixin-java-common/src/main/java/me/chanjar/weixin/common/error/WxMaErrorMsgEnum.java
+++ b/weixin-java-common/src/main/java/me/chanjar/weixin/common/error/WxMaErrorMsgEnum.java
@@ -70,11 +70,10 @@ public enum WxMaErrorMsgEnum {
* appid不正确,或者不符合绑定关系要求.
* 对应操作:sendUniformMessage
* 对应地址:
- * POST https://api.weixin.qq.com/cgi-bin/message/wxopen/template/uniform_send?access_token=ACCESS_TOKEN
- * 参考文档地址: https://developers.weixin.qq.com/miniprogram/dev/api/open-api/uniform-message/sendUniformMessage.html
+ * 参考文档地址: https://developers.weixin.qq.com/miniprogram/dev/OpenApiDoc/openApi-mgnt/clearQuota.html
*
*/
- CODE_40013(40013, "appid不正确,或者不符合绑定关系要求"),
+ CODE_40013(40013, "appid不正确/不合法(避免异常字符,注意大小写),或者不符合绑定关系要求"),
/**
*
* template_id 不正确.
@@ -267,6 +266,50 @@ public enum WxMaErrorMsgEnum {
* activity_id 过期.
*/
CODE_47504(47504, "activity_id 过期"),
+ /**
+ * api 禁止清零调用次数,因为清零次数达到上限
+ *
+ * @see 参考文档
+ */
+ CODE_48006(48006, "api 禁止清零调用次数,因为清零次数达到上限"),
+
+ /**
+ * rid不存在
+ *
+ * @see 参考文档
+ */
+ CODE_76001(76001, "rid不存在"),
+ /**
+ * rid为空或者格式错误
+ *
+ * @see 参考文档
+ */
+ CODE_76002(76002, "rid为空或者格式错误"),
+ /**
+ * 当前账号无权查询该rid,该rid属于其他账号调用所产生
+ *
+ * @see 参考文档
+ */
+ CODE_76003(76003, "当前账号无权查询该rid,该rid属于其他账号调用所产生"),
+ /**
+ * rid过期
+ *
+ * @see 参考文档
+ */
+ CODE_76004(76004, "rid过期,仅支持持续7天内的rid"),
+ /**
+ * cgi_path填错了
+ *
+ * @see 参考文档
+ */
+ CODE_76021(76021, "cgi_path填错了"),
+ /**
+ * 当前调用接口使用的token与api所属账号不符
+ *
+ * @see 参考文档
+ */
+ CODE_76022(76022, "当前调用接口使用的token与api所属账号不符,详情可看注意事项的说明"),
+
/**
* 没有绑定开放平台帐号.
*/
@@ -343,6 +386,17 @@ public enum WxMaErrorMsgEnum {
CODE_91017(91017, "+号规则 不同类型关联名主体不一致"),
CODE_40097(40097, "参数错误"),
+ /**
+ * 缺少 appid 参数
+ * 参考文档
+ */
+ CODE_41002(41002, "缺少 appid 参数"),
+ /**
+ * 缺少 secret 参数
+ * 参考文档
+ */
+ CODE_41004(41004, "缺少 secret 参数"),
+
CODE_41006(41006, "media_id 不能为空"),
diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/WxMaOpenApiService.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/WxMaOpenApiService.java
new file mode 100644
index 000000000..301884362
--- /dev/null
+++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/WxMaOpenApiService.java
@@ -0,0 +1,65 @@
+package cn.binarywang.wx.miniapp.api;
+
+import cn.binarywang.wx.miniapp.bean.openapi.WxMiniGetApiQuotaResult;
+import cn.binarywang.wx.miniapp.bean.openapi.WxMiniGetRidInfoResult;
+import me.chanjar.weixin.common.error.WxErrorException;
+
+/**
+ * openApi管理
+ *
+ * @author shuiyihan12
+ * @see openApi管理 微信文档
+ * @since 2023/7/7 17:07
+ */
+public interface WxMaOpenApiService {
+
+ /**
+ * 本接口用于清空公众号/小程序/第三方平台等接口的每日调用接口次数
+ * HTTP调用:https://api.weixin.qq.com/cgi-bin/clear_quota?access_token=ACCESS_TOKEN
+ *
+ * @return 是否成功
+ * @throws WxErrorException the wx error exception
+ * @apiNote !!! 单小程序直接调用该方法 , 如多个appid调用此方法前请调用 {@link cn.binarywang.wx.miniapp.api.WxMaService#switchoverTo} 切换appid !!!
+ * @code wxMaService.getWxMaOpenApiService().clearQuota() //单个
+ * @code wxMaService.switchoverTo(" appid ").getWxMaOpenApiService().clearQuota() //多个
+ * @see 注意事项参考微信文档
+ */
+ boolean clearQuota() throws WxErrorException;
+
+ /**
+ * 查询API调用额度
+ * HTTP调用:https://api.weixin.qq.com/cgi-bin/openapi/quota/get?access_token=ACCESS_TOKEN
+ *
+ * @param cgiPath api的请求地址,例如"/cgi-bin/message/custom/send";不要前缀“https://api.weixin.qq.com” ,也不要漏了"/",否则都会76003的报错
+ * @return 额度详情
+ * @throws WxErrorException 微信异常
+ * @see 注意事项参考微信文档
+ */
+ WxMiniGetApiQuotaResult getApiQuota(String cgiPath) throws WxErrorException;
+
+ /**
+ * 查询rid信息
+ * HTTP调用:https://api.weixin.qq.com/cgi-bin/openapi/rid/get?access_token=ACCESS_TOKEN
+ *
+ * @param rid 调用接口报错返回的rid
+ * @return 该rid对应的请求详情
+ * @throws WxErrorException 微信异常
+ * @see 注意事项参考微信文档
+ */
+ WxMiniGetRidInfoResult getRidInfo(String rid) throws WxErrorException;
+
+
+ /**
+ * 使用AppSecret重置 API 调用次数
+ * HTTP调用:https://api.weixin.qq.com/cgi-bin/clear_quota/v2
+ *
+ * @return 是否成功
+ * @throws WxErrorException 微信异常
+ * @apiNote !!! 单小程序直接调用该方法 , 如多个appid调用此方法前请调用 {@link cn.binarywang.wx.miniapp.api.WxMaService#switchoverTo} 切换appid!!!
+ * 参考示例
+ * @code wxMaService.getWxMaOpenApiService().clearQuotaByAppSecret() //单个
+ * @code wxMaService.switchoverTo(" appid ").getWxMaOpenApiService().clearQuotaByAppSecret() //多个
+ * @see 注意事项参考微信文档
+ */
+ boolean clearQuotaByAppSecret() throws WxErrorException;
+}
diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/WxMaService.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/WxMaService.java
index ec7423a8b..dd0c5bede 100644
--- a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/WxMaService.java
+++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/WxMaService.java
@@ -523,4 +523,10 @@ public interface WxMaService extends WxService {
* @return getWxMaShopPayService
*/
WxMaShopPayService getWxMaShopPayService();
+
+ /**
+ * 小程序openApi管理
+ * @return getWxMaOpenApiService
+ */
+ WxMaOpenApiService getWxMaOpenApiService();
}
diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/impl/BaseWxMaServiceImpl.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/impl/BaseWxMaServiceImpl.java
index a796b08f6..dd30115ca 100644
--- a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/impl/BaseWxMaServiceImpl.java
+++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/impl/BaseWxMaServiceImpl.java
@@ -85,6 +85,7 @@ public abstract class BaseWxMaServiceImpl implements WxMaService, RequestH
private final WxMaProductOrderService productOrderService = new WxMaProductOrderServiceImpl(this);
private final WxMaShopCouponService wxMaShopCouponService = new WxMaShopCouponServiceImpl(this);
private final WxMaShopPayService wxMaShopPayService = new WxMaShopPayServiceImpl(this);
+ private final WxMaOpenApiService wxMaOpenApiService = new WxMaOpenApiServiceImpl(this);
private Map configMap;
private int retrySleepMillis = 1000;
private int maxRetryTimes = 5;
@@ -634,4 +635,9 @@ public abstract class BaseWxMaServiceImpl implements WxMaService, RequestH
public WxMaShopPayService getWxMaShopPayService() {
return this.wxMaShopPayService;
}
+
+ @Override
+ public WxMaOpenApiService getWxMaOpenApiService() {
+ return this.wxMaOpenApiService;
+ }
}
diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/impl/WxMaOpenApiServiceImpl.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/impl/WxMaOpenApiServiceImpl.java
new file mode 100644
index 000000000..b74b9bfb2
--- /dev/null
+++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/impl/WxMaOpenApiServiceImpl.java
@@ -0,0 +1,81 @@
+package cn.binarywang.wx.miniapp.api.impl;
+
+import cn.binarywang.wx.miniapp.api.WxMaOpenApiService;
+import cn.binarywang.wx.miniapp.api.WxMaService;
+import cn.binarywang.wx.miniapp.bean.openapi.WxMiniGetApiQuotaResult;
+import cn.binarywang.wx.miniapp.bean.openapi.WxMiniGetRidInfoResult;
+import cn.binarywang.wx.miniapp.constant.WxMaApiUrlConstants;
+import cn.binarywang.wx.miniapp.json.WxMaGsonBuilder;
+import com.google.gson.JsonObject;
+import lombok.RequiredArgsConstructor;
+import me.chanjar.weixin.common.enums.WxType;
+import me.chanjar.weixin.common.error.WxError;
+import me.chanjar.weixin.common.error.WxErrorException;
+import me.chanjar.weixin.common.util.json.GsonParser;
+
+import static me.chanjar.weixin.common.api.WxConsts.ERR_CODE;
+
+/**
+ * @author shuiyihan12
+ * @since 2023/7/7 17:08
+ */
+@RequiredArgsConstructor
+public class WxMaOpenApiServiceImpl implements WxMaOpenApiService {
+ private final WxMaService wxMaService;
+
+ private static final String QUOTA = "quota";
+ private static final String REQUEST = "request";
+
+
+ @Override
+ public boolean clearQuota() throws WxErrorException {
+ JsonObject params = new JsonObject();
+ params.addProperty("appid", this.wxMaService.getWxMaConfig().getAppid());
+ String responseContent = this.wxMaService.post(WxMaApiUrlConstants.OpenApi.CLEAR_QUOTA, params.toString());
+ parseErrorResponse(responseContent);
+ return true;
+ }
+
+ @Override
+ public WxMiniGetApiQuotaResult getApiQuota(String cgiPath) throws WxErrorException {
+ JsonObject params = new JsonObject();
+ params.addProperty("cgi_path", cgiPath);
+ String responseContent = this.wxMaService.post(WxMaApiUrlConstants.OpenApi.GET_API_QUOTA, params.toString());
+ parseErrorResponse(responseContent);
+ JsonObject response = GsonParser.parse(responseContent);
+ if (response.has(QUOTA)) {
+ return WxMaGsonBuilder.create().fromJson(response.getAsJsonObject(QUOTA), WxMiniGetApiQuotaResult.class);
+ }
+ return null;
+ }
+
+
+ @Override
+ public WxMiniGetRidInfoResult getRidInfo(String rid) throws WxErrorException {
+ JsonObject params = new JsonObject();
+ params.addProperty("rid", rid);
+ String responseContent = this.wxMaService.post(WxMaApiUrlConstants.OpenApi.GET_RID_INFO, params.toString());
+ parseErrorResponse(responseContent);
+ JsonObject response = GsonParser.parse(responseContent);
+ if (response.has(REQUEST)) {
+ return WxMaGsonBuilder.create().fromJson(response.getAsJsonObject(QUOTA), WxMiniGetRidInfoResult.class);
+ }
+ return null;
+ }
+
+ @Override
+ public boolean clearQuotaByAppSecret() throws WxErrorException {
+ String url = String.format(WxMaApiUrlConstants.OpenApi.CLEAR_QUOTA_BY_APP_SECRET, this.wxMaService.getWxMaConfig().getAppid(), this.wxMaService.getWxMaConfig().getSecret());
+ String responseContent = this.wxMaService.post(url, "");
+ parseErrorResponse(responseContent);
+ return true;
+ }
+
+
+ private void parseErrorResponse(String response) throws WxErrorException {
+ JsonObject jsonObject = GsonParser.parse(response);
+ if (jsonObject.get(ERR_CODE).getAsInt() != 0) {
+ throw new WxErrorException(WxError.fromJson(response, WxType.MiniApp));
+ }
+ }
+}
diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/openapi/WxMiniGetApiQuotaResult.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/openapi/WxMiniGetApiQuotaResult.java
new file mode 100644
index 000000000..be02f6839
--- /dev/null
+++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/openapi/WxMiniGetApiQuotaResult.java
@@ -0,0 +1,28 @@
+package cn.binarywang.wx.miniapp.bean.openapi;
+
+import com.google.gson.annotations.SerializedName;
+import lombok.Data;
+
+/**
+ * 查询API调用额度 返回数据
+ *
+ * @author shuiyihan12
+ * @since 2023/7/10 16:52
+ */
+@Data
+public class WxMiniGetApiQuotaResult {
+
+ /**
+ * 当天该账号可调用该接口的次数
+ */
+ @SerializedName("daily_limit")
+ private Integer dailyLimit;
+ /**
+ * 当天已经调用的次数
+ */
+ private Integer used;
+ /**
+ * 当天剩余调用次数
+ */
+ private Integer remain;
+}
diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/openapi/WxMiniGetRidInfoResult.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/openapi/WxMiniGetRidInfoResult.java
new file mode 100644
index 000000000..d0dabe1c6
--- /dev/null
+++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/openapi/WxMiniGetRidInfoResult.java
@@ -0,0 +1,44 @@
+package cn.binarywang.wx.miniapp.bean.openapi;
+
+import com.google.gson.annotations.SerializedName;
+import lombok.Data;
+
+/**
+ * 查询rid信息 返回数据
+ * @author shuiyihan12
+ * @since 2023/7/10 16:53
+ */
+@Data
+public class WxMiniGetRidInfoResult {
+
+ /**
+ * 发起请求的时间戳
+ */
+ @SerializedName("invoke_time")
+ private Integer invokeTime;
+ /**
+ * 请求毫秒级耗时
+ */
+ @SerializedName("cost_in_ms")
+ private Integer costInMs;
+ /**
+ * 请求的URL参数
+ */
+ @SerializedName("request_url")
+ private String requestUrl;
+ /**
+ * post请求的请求参数
+ */
+ @SerializedName("request_body")
+ private String requestBody;
+ /**
+ * 接口请求返回参数
+ */
+ @SerializedName("response_body")
+ private String responseBody;
+ /**
+ * 接口请求的客户端ip
+ */
+ @SerializedName("client_ip")
+ private String clientIp;
+}
diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/constant/WxMaApiUrlConstants.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/constant/WxMaApiUrlConstants.java
index debfc300e..e4d43fdfd 100644
--- a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/constant/WxMaApiUrlConstants.java
+++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/constant/WxMaApiUrlConstants.java
@@ -10,6 +10,30 @@ import lombok.experimental.UtilityClass;
*/
@UtilityClass
public class WxMaApiUrlConstants {
+
+ /**
+ * openApi管理
+ */
+ public interface OpenApi {
+ /**
+ * 重置API调用次数
+ */
+ String CLEAR_QUOTA = "https://api.weixin.qq.com/cgi-bin/clear_quota";
+ /**
+ * 查询API调用额度
+ */
+ String GET_API_QUOTA = "https://api.weixin.qq.com/cgi-bin/openapi/quota/get";
+ /**
+ * 查询rid信息
+ */
+ String GET_RID_INFO = "https://api.weixin.qq.com/cgi-bin/openapi/rid/get";
+ /**
+ * 使用AppSecret重置 API 调用次数
+ */
+ String CLEAR_QUOTA_BY_APP_SECRET = "https://api.weixin.qq.com/cgi-bin/clear_quota/v2?appid=%s&appsecret=%s";
+
+ }
+
public interface Analysis {
String GET_DAILY_SUMMARY_TREND_URL = "https://api.weixin.qq.com/datacube/getweanalysisappiddailysummarytrend";
String GET_DAILY_VISIT_TREND_URL = "https://api.weixin.qq.com/datacube/getweanalysisappiddailyvisittrend";
diff --git a/weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/api/impl/WxMaOpenApiServiceImplTest.java b/weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/api/impl/WxMaOpenApiServiceImplTest.java
new file mode 100644
index 000000000..409e63e71
--- /dev/null
+++ b/weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/api/impl/WxMaOpenApiServiceImplTest.java
@@ -0,0 +1,48 @@
+package cn.binarywang.wx.miniapp.api.impl;
+
+import cn.binarywang.wx.miniapp.api.WxMaService;
+import cn.binarywang.wx.miniapp.bean.openapi.WxMiniGetApiQuotaResult;
+import cn.binarywang.wx.miniapp.test.ApiTestModule;
+import com.google.gson.Gson;
+import com.google.inject.Inject;
+import me.chanjar.weixin.common.error.WxErrorException;
+import org.testng.annotations.Guice;
+import org.testng.annotations.Test;
+
+import static org.testng.Assert.assertNotNull;
+import static org.testng.AssertJUnit.assertTrue;
+
+/**
+ * openApi管理测试
+ *
+ * @author shuiyihan12
+ * @since 2023/7/7 17:08
+ */
+@Test
+@Guice(modules = ApiTestModule.class)
+public class WxMaOpenApiServiceImplTest {
+
+ @Inject
+ private WxMaService wxMaService;
+
+
+ @Test
+ public void clearQuota() throws WxErrorException {
+ final boolean result = wxMaService.getWxMaOpenApiService().clearQuota();
+ assertTrue(result);
+ }
+
+ @Test
+ public void getApiQuota() throws WxErrorException {
+ String cgiPath = "/cgi-bin/openapi/quota/get";
+ final WxMiniGetApiQuotaResult apiQuota = wxMaService.getWxMaOpenApiService().getApiQuota(cgiPath);
+ assertNotNull(apiQuota);
+ System.out.println(new Gson().toJson(apiQuota));
+ }
+ @Test
+ public void clearQuotaByAppSecret() throws WxErrorException {
+ final boolean result = wxMaService.getWxMaOpenApiService().clearQuotaByAppSecret();
+ assertTrue(result);
+ }
+
+}
diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/impl/BaseWxPayServiceImpl.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/impl/BaseWxPayServiceImpl.java
index 845103c0d..44ee40de3 100644
--- a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/impl/BaseWxPayServiceImpl.java
+++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/impl/BaseWxPayServiceImpl.java
@@ -209,9 +209,11 @@ public abstract class BaseWxPayServiceImpl implements WxPayService {
@Override
public String getPayBaseUrl() {
if (this.getConfig().isUseSandboxEnv()) {
+ if (StringUtils.isNotBlank(this.getConfig().getApiV3Key())) {
+ throw new WxRuntimeException("微信支付V3 目前不支持沙箱模式!");
+ }
return this.getConfig().getPayBaseUrl() + "/xdc/apiv2sandbox";
}
-
return this.getConfig().getPayBaseUrl();
}