mirror of
https://gitee.com/binary/weixin-java-tools.git
synced 2025-10-29 01:18:36 +08:00
合并开发分支,发布正式版
This commit is contained in:
@ -7,11 +7,12 @@
|
||||
<parent>
|
||||
<groupId>com.github.binarywang</groupId>
|
||||
<artifactId>wx-java</artifactId>
|
||||
<version>3.3.0</version>
|
||||
<version>3.4.0</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>weixin-java-miniapp</artifactId>
|
||||
<name>WxJava - MiniApp</name>
|
||||
<description>微信小程序Java SDK</description>
|
||||
<name>WxJava - MiniApp Java SDK</name>
|
||||
<description>微信小程序 Java SDK</description>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
@ -70,6 +71,11 @@
|
||||
<groupId>redis.clients</groupId>
|
||||
<artifactId>jedis</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.bouncycastle</groupId>
|
||||
<artifactId>bcpkix-jdk15on</artifactId>
|
||||
<version>1.59</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
|
||||
@ -1,14 +1,10 @@
|
||||
package cn.binarywang.wx.miniapp.api;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import cn.binarywang.wx.miniapp.bean.code.WxMaCategory;
|
||||
import cn.binarywang.wx.miniapp.bean.code.WxMaCodeAuditStatus;
|
||||
import cn.binarywang.wx.miniapp.bean.code.WxMaCodeCommitRequest;
|
||||
import cn.binarywang.wx.miniapp.bean.code.WxMaCodeSubmitAuditRequest;
|
||||
import cn.binarywang.wx.miniapp.bean.code.WxMaCodeVersionDistribution;
|
||||
import cn.binarywang.wx.miniapp.bean.code.*;
|
||||
import me.chanjar.weixin.common.error.WxErrorException;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 小程序代码管理相关 API(大部分只能是第三方平台调用)
|
||||
* 文档:https://open.weixin.qq.com/cgi-bin/showdocument?action=dir_list&t=resource/res_list&verify=1&id=open1489140610_Uavc4&token=&lang=zh_CN
|
||||
@ -18,7 +14,7 @@ import me.chanjar.weixin.common.error.WxErrorException;
|
||||
*/
|
||||
public interface WxMaCodeService {
|
||||
/**
|
||||
* 为授权的小程序帐号上传小程序代码
|
||||
* 为授权的小程序帐号上传小程序代码.
|
||||
*/
|
||||
String COMMIT_URL = "https://api.weixin.qq.com/wxa/commit";
|
||||
String GET_QRCODE_URL = "https://api.weixin.qq.com/wxa/get_qrcode";
|
||||
@ -35,7 +31,7 @@ public interface WxMaCodeService {
|
||||
String UNDO_CODE_AUDIT_URL = "https://api.weixin.qq.com/wxa/undocodeaudit";
|
||||
|
||||
/**
|
||||
* 为授权的小程序帐号上传小程序代码(仅仅支持第三方开放平台)
|
||||
* 为授权的小程序帐号上传小程序代码(仅仅支持第三方开放平台).
|
||||
*
|
||||
* @param commitRequest 参数
|
||||
* @throws WxErrorException 上传失败时抛出,具体错误码请看类注释文档
|
||||
@ -43,19 +39,19 @@ public interface WxMaCodeService {
|
||||
void commit(WxMaCodeCommitRequest commitRequest) throws WxErrorException;
|
||||
|
||||
/**
|
||||
* 获取体验小程序的体验二维码
|
||||
* 获取体验小程序的体验二维码.
|
||||
* 文档地址:
|
||||
* https://open.weixin.qq.com/cgi-bin/showdocument?action=dir_list&t=resource/res_list&verify=1&id=open1489140610_Uavc4&token=&lang=zh_CN
|
||||
*
|
||||
* @param path 指定体验版二维码跳转到某个具体页面(如果不需要的话,则不需要填path参数,可在路径后以“?参数”方式传入参数)
|
||||
* 具体的路径加参数需要urlencode(方法内部处理),比如page/index?action=1编码后得到page%2Findex%3Faction%3D1
|
||||
* 具体的路径加参数需要urlencode(方法内部处理),比如page/index?action=1编码后得到page%2Findex%3Faction%3D1
|
||||
* @return 二维码 bytes
|
||||
* @throws WxErrorException 上传失败时抛出,具体错误码请看类注释文档
|
||||
*/
|
||||
byte[] getQrCode(String path) throws WxErrorException;
|
||||
|
||||
/**
|
||||
* 获取授权小程序帐号的可选类目
|
||||
* 获取授权小程序帐号的可选类目.
|
||||
*
|
||||
* @return List<WxMaCategory>
|
||||
* @throws WxErrorException 获取失败时返回,具体错误码请看此接口的注释文档
|
||||
@ -63,7 +59,7 @@ public interface WxMaCodeService {
|
||||
List<WxMaCategory> getCategory() throws WxErrorException;
|
||||
|
||||
/**
|
||||
* 获取小程序的第三方提交代码的页面配置(仅供第三方开发者代小程序调用)
|
||||
* 获取小程序的第三方提交代码的页面配置(仅供第三方开发者代小程序调用).
|
||||
*
|
||||
* @return page_list 页面配置列表
|
||||
* @throws WxErrorException 获取失败时返回,具体错误码请看此接口的注释文档
|
||||
@ -71,7 +67,7 @@ public interface WxMaCodeService {
|
||||
List<String> getPage() throws WxErrorException;
|
||||
|
||||
/**
|
||||
* 将第三方提交的代码包提交审核(仅供第三方开发者代小程序调用)
|
||||
* 将第三方提交的代码包提交审核(仅供第三方开发者代小程序调用).
|
||||
*
|
||||
* @param auditRequest 提交审核参数
|
||||
* @return 审核编号
|
||||
@ -80,7 +76,7 @@ public interface WxMaCodeService {
|
||||
long submitAudit(WxMaCodeSubmitAuditRequest auditRequest) throws WxErrorException;
|
||||
|
||||
/**
|
||||
* 查询某个指定版本的审核状态(仅供第三方代小程序调用)
|
||||
* 查询某个指定版本的审核状态(仅供第三方代小程序调用).
|
||||
*
|
||||
* @param auditId 提交审核时获得的审核id
|
||||
* @return 审核状态
|
||||
@ -89,7 +85,7 @@ public interface WxMaCodeService {
|
||||
WxMaCodeAuditStatus getAuditStatus(long auditId) throws WxErrorException;
|
||||
|
||||
/**
|
||||
* 查询最新一次提交的审核状态(仅供第三方代小程序调用)
|
||||
* 查询最新一次提交的审核状态(仅供第三方代小程序调用).
|
||||
*
|
||||
* @return 审核状态
|
||||
* @throws WxErrorException 查询失败时返回,具体错误码请看此接口的注释文档
|
||||
@ -97,14 +93,14 @@ public interface WxMaCodeService {
|
||||
WxMaCodeAuditStatus getLatestAuditStatus() throws WxErrorException;
|
||||
|
||||
/**
|
||||
* 发布已通过审核的小程序(仅供第三方代小程序调用)
|
||||
* 发布已通过审核的小程序(仅供第三方代小程序调用).
|
||||
*
|
||||
* @throws WxErrorException 发布失败时抛出,具体错误码请看此接口的注释文档
|
||||
*/
|
||||
void release() throws WxErrorException;
|
||||
|
||||
/**
|
||||
* 修改小程序线上代码的可见状态(仅供第三方代小程序调用)
|
||||
* 修改小程序线上代码的可见状态(仅供第三方代小程序调用).
|
||||
*
|
||||
* @param action 设置可访问状态,发布后默认可访问,close为不可见,open为可见
|
||||
* @throws WxErrorException 发布失败时抛出,具体错误码请看此接口的注释文档
|
||||
@ -112,14 +108,14 @@ public interface WxMaCodeService {
|
||||
void changeVisitStatus(String action) throws WxErrorException;
|
||||
|
||||
/**
|
||||
* 小程序版本回退(仅供第三方代小程序调用)
|
||||
* 小程序版本回退(仅供第三方代小程序调用).
|
||||
*
|
||||
* @throws WxErrorException 失败时抛出,具体错误码请看此接口的注释文档
|
||||
*/
|
||||
void revertCodeRelease() throws WxErrorException;
|
||||
|
||||
/**
|
||||
* 查询当前设置的最低基础库版本及各版本用户占比 (仅供第三方代小程序调用)
|
||||
* 查询当前设置的最低基础库版本及各版本用户占比 (仅供第三方代小程序调用).
|
||||
*
|
||||
* @return 小程序版本分布信息
|
||||
* @throws WxErrorException 失败时抛出,具体错误码请看此接口的注释文档
|
||||
@ -127,7 +123,7 @@ public interface WxMaCodeService {
|
||||
WxMaCodeVersionDistribution getSupportVersion() throws WxErrorException;
|
||||
|
||||
/**
|
||||
* 设置最低基础库版本(仅供第三方代小程序调用)
|
||||
* 设置最低基础库版本(仅供第三方代小程序调用).
|
||||
*
|
||||
* @param version 版本
|
||||
* @throws WxErrorException 失败时抛出,具体错误码请看此接口的注释文档
|
||||
@ -135,7 +131,7 @@ public interface WxMaCodeService {
|
||||
void setSupportVersion(String version) throws WxErrorException;
|
||||
|
||||
/**
|
||||
* 小程序审核撤回
|
||||
* 小程序审核撤回.
|
||||
* 单个帐号每天审核撤回次数最多不超过1次,一个月不超过10次
|
||||
*
|
||||
* @throws WxErrorException 失败时抛出,具体错误码请看此接口的注释文档
|
||||
|
||||
@ -20,7 +20,7 @@ public interface WxMaMsgService {
|
||||
/**
|
||||
* <pre>
|
||||
* 发送客服消息
|
||||
* 详情请见: <a href="https://mp.weixin.qq.com/debug/wxadoc/dev/api/custommsg/conversation.html">发送客服消息</a>
|
||||
* 详情请见: <a href="https://developers.weixin.qq.com/miniprogram/dev/api-backend/customerServiceMessage.send.html">发送客服消息</a>
|
||||
* 接口url格式:https://api.weixin.qq.com/cgi-bin/message/custom/send?access_token=ACCESS_TOKEN
|
||||
* </pre>
|
||||
*/
|
||||
@ -29,7 +29,7 @@ public interface WxMaMsgService {
|
||||
/**
|
||||
* <pre>
|
||||
* 发送模板消息
|
||||
* 详情请见: <a href="https://mp.weixin.qq.com/debug/wxadoc/dev/api/notice.html#接口说明">发送模板消息</a>
|
||||
* 详情请见: <a href="https://developers.weixin.qq.com/miniprogram/dev/api-backend/templateMessage.send.html">发送模板消息</a>
|
||||
* 接口url格式:https://api.weixin.qq.com/cgi-bin/message/wxopen/template/send?access_token=ACCESS_TOKEN
|
||||
* </pre>
|
||||
*/
|
||||
|
||||
@ -17,6 +17,10 @@ public interface WxMaService {
|
||||
String GET_ACCESS_TOKEN_URL = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=%s&secret=%s";
|
||||
|
||||
String JSCODE_TO_SESSION_URL = "https://api.weixin.qq.com/sns/jscode2session";
|
||||
/**
|
||||
* getPaidUnionId
|
||||
*/
|
||||
String GET_PAID_UNION_ID_URL = "https://api.weixin.qq.com/wxa/getpaidunionid";
|
||||
|
||||
/**
|
||||
* 获取登录后的session信息.
|
||||
@ -56,6 +60,22 @@ public interface WxMaService {
|
||||
*/
|
||||
String getAccessToken(boolean forceRefresh) throws WxErrorException;
|
||||
|
||||
/**
|
||||
* <pre>
|
||||
* 用户支付完成后,获取该用户的 UnionId,无需用户授权。本接口支持第三方平台代理查询。
|
||||
*
|
||||
* 注意:调用前需要用户完成支付,且在支付后的五分钟内有效。
|
||||
* 请求地址: GET https://api.weixin.qq.com/wxa/getpaidunionid?access_token=ACCESS_TOKEN&openid=OPENID
|
||||
* 文档地址:https://developers.weixin.qq.com/miniprogram/dev/api/getPaidUnionId.html
|
||||
* </pre>
|
||||
*
|
||||
* @param openid 必填 支付用户唯一标识
|
||||
* @param transactionId 非必填 微信支付订单号
|
||||
* @param mchId 非必填 微信支付分配的商户号,和商户订单号配合使用
|
||||
* @param outTradeNo 非必填 微信支付商户订单号,和商户号配合使用
|
||||
*/
|
||||
String getPaidUnionId(String openid, String transactionId, String mchId, String outTradeNo) throws WxErrorException;
|
||||
|
||||
/**
|
||||
* 当本Service没有实现某个API的时候,可以用这个,针对所有微信API中的GET请求.
|
||||
*/
|
||||
@ -168,18 +188,21 @@ public interface WxMaService {
|
||||
|
||||
/**
|
||||
* 返回分享相关查询服务.
|
||||
*
|
||||
* @return WxMaShareService
|
||||
*/
|
||||
WxMaShareService getShareService();
|
||||
|
||||
/**
|
||||
* 返回微信运动相关接口服务对象.
|
||||
*
|
||||
* @return WxMaShareService
|
||||
*/
|
||||
WxMaRunService getRunService();
|
||||
|
||||
/**
|
||||
* 返回内容安全相关接口服务对象.
|
||||
*
|
||||
* @return WxMaShareService
|
||||
*/
|
||||
WxMaSecCheckService getSecCheckService();
|
||||
|
||||
@ -1,10 +1,22 @@
|
||||
package cn.binarywang.wx.miniapp.api.impl;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.locks.Lock;
|
||||
|
||||
import cn.binarywang.wx.miniapp.api.*;
|
||||
import cn.binarywang.wx.miniapp.bean.WxMaJscode2SessionResult;
|
||||
import cn.binarywang.wx.miniapp.config.WxMaConfig;
|
||||
import com.google.common.base.Joiner;
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.JsonParser;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import me.chanjar.weixin.common.WxType;
|
||||
import me.chanjar.weixin.common.bean.WxAccessToken;
|
||||
import me.chanjar.weixin.common.error.WxError;
|
||||
import me.chanjar.weixin.common.error.WxErrorException;
|
||||
import me.chanjar.weixin.common.util.DataUtils;
|
||||
import me.chanjar.weixin.common.util.crypto.SHA1;
|
||||
import me.chanjar.weixin.common.util.http.*;
|
||||
import me.chanjar.weixin.common.util.http.apache.ApacheHttpClientBuilder;
|
||||
import me.chanjar.weixin.common.util.http.apache.DefaultApacheHttpClientBuilder;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.http.HttpHost;
|
||||
import org.apache.http.client.config.RequestConfig;
|
||||
import org.apache.http.client.methods.CloseableHttpResponse;
|
||||
@ -12,36 +24,10 @@ import org.apache.http.client.methods.HttpGet;
|
||||
import org.apache.http.impl.client.BasicResponseHandler;
|
||||
import org.apache.http.impl.client.CloseableHttpClient;
|
||||
|
||||
import cn.binarywang.wx.miniapp.api.WxMaAnalysisService;
|
||||
import cn.binarywang.wx.miniapp.api.WxMaCodeService;
|
||||
import cn.binarywang.wx.miniapp.api.WxMaJsapiService;
|
||||
import cn.binarywang.wx.miniapp.api.WxMaMediaService;
|
||||
import cn.binarywang.wx.miniapp.api.WxMaMsgService;
|
||||
import cn.binarywang.wx.miniapp.api.WxMaQrcodeService;
|
||||
import cn.binarywang.wx.miniapp.api.WxMaRunService;
|
||||
import cn.binarywang.wx.miniapp.api.WxMaSecCheckService;
|
||||
import cn.binarywang.wx.miniapp.api.WxMaService;
|
||||
import cn.binarywang.wx.miniapp.api.WxMaSettingService;
|
||||
import cn.binarywang.wx.miniapp.api.WxMaShareService;
|
||||
import cn.binarywang.wx.miniapp.api.WxMaTemplateService;
|
||||
import cn.binarywang.wx.miniapp.api.WxMaUserService;
|
||||
import cn.binarywang.wx.miniapp.bean.WxMaJscode2SessionResult;
|
||||
import cn.binarywang.wx.miniapp.config.WxMaConfig;
|
||||
import com.google.common.base.Joiner;
|
||||
import com.google.gson.Gson;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import me.chanjar.weixin.common.bean.WxAccessToken;
|
||||
import me.chanjar.weixin.common.error.WxError;
|
||||
import me.chanjar.weixin.common.error.WxErrorException;
|
||||
import me.chanjar.weixin.common.util.DataUtils;
|
||||
import me.chanjar.weixin.common.util.crypto.SHA1;
|
||||
import me.chanjar.weixin.common.util.http.HttpType;
|
||||
import me.chanjar.weixin.common.util.http.RequestExecutor;
|
||||
import me.chanjar.weixin.common.util.http.RequestHttp;
|
||||
import me.chanjar.weixin.common.util.http.SimpleGetRequestExecutor;
|
||||
import me.chanjar.weixin.common.util.http.SimplePostRequestExecutor;
|
||||
import me.chanjar.weixin.common.util.http.apache.ApacheHttpClientBuilder;
|
||||
import me.chanjar.weixin.common.util.http.apache.DefaultApacheHttpClientBuilder;
|
||||
import java.io.IOException;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.locks.Lock;
|
||||
|
||||
import static cn.binarywang.wx.miniapp.constant.WxMaConstants.ErrorCode.*;
|
||||
|
||||
@ -50,6 +36,7 @@ import static cn.binarywang.wx.miniapp.constant.WxMaConstants.ErrorCode.*;
|
||||
*/
|
||||
@Slf4j
|
||||
public class WxMaServiceImpl implements WxMaService, RequestHttp<CloseableHttpClient, HttpHost> {
|
||||
private static final JsonParser JSON_PARSER = new JsonParser();
|
||||
private CloseableHttpClient httpClient;
|
||||
private HttpHost httpProxy;
|
||||
private WxMaConfig wxMaConfig;
|
||||
@ -114,40 +101,68 @@ public class WxMaServiceImpl implements WxMaService, RequestHttp<CloseableHttpCl
|
||||
|
||||
@Override
|
||||
public String getAccessToken(boolean forceRefresh) throws WxErrorException {
|
||||
Lock lock = this.getWxMaConfig().getAccessTokenLock();
|
||||
try {
|
||||
lock.lock();
|
||||
if (!this.getWxMaConfig().isAccessTokenExpired() && !forceRefresh) {
|
||||
return this.getWxMaConfig().getAccessToken();
|
||||
}
|
||||
|
||||
if (this.getWxMaConfig().isAccessTokenExpired() || forceRefresh) {
|
||||
String url = String.format(WxMaService.GET_ACCESS_TOKEN_URL, this.getWxMaConfig().getAppid(),
|
||||
this.getWxMaConfig().getSecret());
|
||||
try {
|
||||
HttpGet httpGet = new HttpGet(url);
|
||||
if (this.getRequestHttpProxy() != null) {
|
||||
RequestConfig config = RequestConfig.custom().setProxy(this.getRequestHttpProxy()).build();
|
||||
httpGet.setConfig(config);
|
||||
}
|
||||
try (CloseableHttpResponse response = getRequestHttpClient().execute(httpGet)) {
|
||||
String resultContent = new BasicResponseHandler().handleResponse(response);
|
||||
WxError error = WxError.fromJson(resultContent);
|
||||
if (error.getErrorCode() != 0) {
|
||||
throw new WxErrorException(error);
|
||||
}
|
||||
WxAccessToken accessToken = WxAccessToken.fromJson(resultContent);
|
||||
this.getWxMaConfig().updateAccessToken(accessToken.getAccessToken(),
|
||||
accessToken.getExpiresIn());
|
||||
} finally {
|
||||
httpGet.releaseConnection();
|
||||
}
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
Lock lock = this.getWxMaConfig().getAccessTokenLock();
|
||||
lock.lock();
|
||||
try {
|
||||
String url = String.format(WxMaService.GET_ACCESS_TOKEN_URL, this.getWxMaConfig().getAppid(),
|
||||
this.getWxMaConfig().getSecret());
|
||||
try {
|
||||
HttpGet httpGet = new HttpGet(url);
|
||||
if (this.getRequestHttpProxy() != null) {
|
||||
RequestConfig config = RequestConfig.custom().setProxy(this.getRequestHttpProxy()).build();
|
||||
httpGet.setConfig(config);
|
||||
}
|
||||
try (CloseableHttpResponse response = getRequestHttpClient().execute(httpGet)) {
|
||||
String resultContent = new BasicResponseHandler().handleResponse(response);
|
||||
WxError error = WxError.fromJson(resultContent);
|
||||
if (error.getErrorCode() != 0) {
|
||||
throw new WxErrorException(error);
|
||||
}
|
||||
WxAccessToken accessToken = WxAccessToken.fromJson(resultContent);
|
||||
this.getWxMaConfig().updateAccessToken(accessToken.getAccessToken(), accessToken.getExpiresIn());
|
||||
|
||||
return this.getWxMaConfig().getAccessToken();
|
||||
} finally {
|
||||
httpGet.releaseConnection();
|
||||
}
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
|
||||
return this.getWxMaConfig().getAccessToken();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPaidUnionId(String openid, String transactionId, String mchId, String outTradeNo)
|
||||
throws WxErrorException {
|
||||
Map<String, String> params = new HashMap<>(8);
|
||||
params.put("openid", openid);
|
||||
|
||||
if (StringUtils.isNotEmpty(transactionId)) {
|
||||
params.put("transaction_id", transactionId);
|
||||
}
|
||||
|
||||
if (StringUtils.isNotEmpty(mchId)) {
|
||||
params.put("mch_id", mchId);
|
||||
}
|
||||
|
||||
if (StringUtils.isNotEmpty(outTradeNo)) {
|
||||
params.put("out_trade_no", outTradeNo);
|
||||
}
|
||||
|
||||
String responseContent = this.get(GET_PAID_UNION_ID_URL, Joiner.on("&").withKeyValueSeparator("=").join(params));
|
||||
WxError error = WxError.fromJson(responseContent, WxType.MiniApp);
|
||||
if (error.getErrorCode() != 0) {
|
||||
throw new WxErrorException(error);
|
||||
}
|
||||
|
||||
return JSON_PARSER.parse(responseContent).getAsJsonObject().get("unionid").getAsString();
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -168,7 +183,7 @@ public class WxMaServiceImpl implements WxMaService, RequestHttp<CloseableHttpCl
|
||||
try {
|
||||
return SHA1.gen(this.getWxMaConfig().getToken(), timestamp, nonce).equals(signature);
|
||||
} catch (Exception e) {
|
||||
this.log.error("Checking signature failed, and the reason is :" + e.getMessage());
|
||||
log.error("Checking signature failed, and the reason is :" + e.getMessage());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@ -246,7 +261,7 @@ public class WxMaServiceImpl implements WxMaService, RequestHttp<CloseableHttpCl
|
||||
if (error.getErrorCode() == ERR_40001
|
||||
|| error.getErrorCode() == ERR_42001
|
||||
|| error.getErrorCode() == ERR_40014) {
|
||||
// 强制设置wxMpConfigStorage它的access token过期了,这样在下一次请求里就会刷新access token
|
||||
// 强制设置WxMaConfig的access token过期了,这样在下一次请求里就会刷新access token
|
||||
this.getWxMaConfig().expireAccessToken();
|
||||
if (this.getWxMaConfig().autoRefreshToken()) {
|
||||
return this.execute(executor, uri, data);
|
||||
|
||||
@ -41,20 +41,26 @@ public class WxMaKefuMessage implements Serializable {
|
||||
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
public static class KfText {
|
||||
public static class KfText implements Serializable {
|
||||
private static final long serialVersionUID = 151122958720941270L;
|
||||
|
||||
private String content;
|
||||
}
|
||||
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
public static class KfImage {
|
||||
public static class KfImage implements Serializable {
|
||||
private static final long serialVersionUID = -5409342945117300782L;
|
||||
|
||||
@SerializedName("media_id")
|
||||
private String mediaId;
|
||||
}
|
||||
|
||||
@Data
|
||||
@Builder
|
||||
public static class KfLink {
|
||||
public static class KfLink implements Serializable {
|
||||
private static final long serialVersionUID = -6728776817556127413L;
|
||||
|
||||
private String title;
|
||||
private String description;
|
||||
private String url;
|
||||
@ -65,7 +71,9 @@ public class WxMaKefuMessage implements Serializable {
|
||||
|
||||
@Data
|
||||
@Builder
|
||||
public static class KfMaPage {
|
||||
public static class KfMaPage implements Serializable {
|
||||
private static final long serialVersionUID = -5633492281871634466L;
|
||||
|
||||
private String title;
|
||||
|
||||
@SerializedName("pagepath")
|
||||
|
||||
@ -5,7 +5,7 @@ import lombok.NoArgsConstructor;
|
||||
|
||||
/**
|
||||
* <pre>
|
||||
*
|
||||
* 参考文档 https://developers.weixin.qq.com/miniprogram/dev/api-backend/templateMessage.send.html
|
||||
* Created by Binary Wang on 2018/9/23.
|
||||
* </pre>
|
||||
*
|
||||
@ -29,4 +29,5 @@ public class WxMaTemplateData {
|
||||
this.color = color;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@ -13,7 +13,7 @@ import lombok.Setter;
|
||||
|
||||
/**
|
||||
* 模板消息.
|
||||
* 参考 https://mp.weixin.qq.com/debug/wxadoc/dev/api/notice.html#接口说明 模板消息部分
|
||||
* 参考 https://developers.weixin.qq.com/miniprogram/dev/api-backend/templateMessage.send.html
|
||||
*
|
||||
* @author <a href="https://github.com/binarywang">Binary Wang</a>
|
||||
*/
|
||||
@ -75,16 +75,6 @@ public class WxMaTemplateMessage implements Serializable {
|
||||
*/
|
||||
private List<WxMaTemplateData> data;
|
||||
|
||||
/**
|
||||
* 模板内容字体的颜色,不填默认黑色.
|
||||
* <pre>
|
||||
* 参数:color
|
||||
* 是否必填: 否
|
||||
* 描述: 模板内容字体的颜色,不填默认黑色
|
||||
* </pre>
|
||||
*/
|
||||
private String color;
|
||||
|
||||
/**
|
||||
* 模板需要放大的关键词,不填则默认无放大.
|
||||
* <pre>
|
||||
|
||||
@ -104,5 +104,11 @@ public class WxMaUniformMessage implements Serializable {
|
||||
* 加入此字段是基于微信官方接口变化多端的考虑
|
||||
*/
|
||||
private boolean usePath = false;
|
||||
|
||||
/**
|
||||
* 是否使用pagePath,否则使用pagepath.
|
||||
* 加入此字段是基于微信官方接口变化多端的考虑
|
||||
*/
|
||||
private boolean usePagePath = false;
|
||||
}
|
||||
}
|
||||
|
||||
@ -18,18 +18,24 @@ import java.io.Serializable;
|
||||
public class WxMaCodeAuditStatus implements Serializable {
|
||||
private static final long serialVersionUID = 4655119308692217268L;
|
||||
/**
|
||||
* 审核 ID
|
||||
* 审核 ID.
|
||||
*/
|
||||
@SerializedName(value = "auditId", alternate = {"auditid"})
|
||||
private Long auditId;
|
||||
/**
|
||||
* 审核状态,其中0为审核成功,1为审核失败,2为审核中
|
||||
* 审核状态.
|
||||
* 其中0为审核成功,1为审核失败,2为审核中
|
||||
*/
|
||||
private Integer status;
|
||||
/**
|
||||
* 当status=1,审核被拒绝时,返回的拒绝原因
|
||||
* 当status=1,审核被拒绝时,返回的拒绝原因.
|
||||
*/
|
||||
private String reason;
|
||||
/**
|
||||
* 当status=1,审核被拒绝时,会返回审核失败的小程序截图示例。 xxx丨yyy丨zzz是media_id可通过获取永久素材接口 拉取截图内容).
|
||||
*/
|
||||
@SerializedName(value = "screenshot")
|
||||
private String screenShot;
|
||||
|
||||
public static WxMaCodeAuditStatus fromJson(String json) {
|
||||
return WxMaGsonBuilder.create().fromJson(json, WxMaCodeAuditStatus.class);
|
||||
|
||||
@ -1,19 +1,27 @@
|
||||
package cn.binarywang.wx.miniapp.util.crypt;
|
||||
|
||||
import cn.binarywang.wx.miniapp.config.WxMaConfig;
|
||||
import me.chanjar.weixin.common.util.crypto.PKCS7Encoder;
|
||||
import org.apache.commons.codec.binary.Base64;
|
||||
|
||||
import java.nio.charset.Charset;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.security.AlgorithmParameters;
|
||||
import java.security.Key;
|
||||
import java.security.Security;
|
||||
import java.util.Arrays;
|
||||
import javax.crypto.Cipher;
|
||||
import javax.crypto.spec.IvParameterSpec;
|
||||
import javax.crypto.spec.SecretKeySpec;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.security.AlgorithmParameters;
|
||||
|
||||
import org.apache.commons.codec.binary.Base64;
|
||||
import org.bouncycastle.jce.provider.BouncyCastleProvider;
|
||||
|
||||
import cn.binarywang.wx.miniapp.config.WxMaConfig;
|
||||
import me.chanjar.weixin.common.util.crypto.PKCS7Encoder;
|
||||
|
||||
/**
|
||||
* @author <a href="https://github.com/binarywang">Binary Wang</a>
|
||||
*/
|
||||
public class WxMaCryptUtils extends me.chanjar.weixin.common.util.crypto.WxCryptUtil {
|
||||
private static final Charset UTF_8 = StandardCharsets.UTF_8;
|
||||
|
||||
public WxMaCryptUtils(WxMaConfig config) {
|
||||
this.appidOrCorpid = config.getAppid();
|
||||
this.token = config.getToken();
|
||||
@ -21,8 +29,9 @@ public class WxMaCryptUtils extends me.chanjar.weixin.common.util.crypto.WxCrypt
|
||||
}
|
||||
|
||||
/**
|
||||
* AES解密
|
||||
* AES解密.
|
||||
*
|
||||
* @param sessionKey session_key
|
||||
* @param encryptedData 消息密文
|
||||
* @param ivStr iv字符串
|
||||
*/
|
||||
@ -34,9 +43,40 @@ public class WxMaCryptUtils extends me.chanjar.weixin.common.util.crypto.WxCrypt
|
||||
Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding");
|
||||
cipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(Base64.decodeBase64(sessionKey), "AES"), params);
|
||||
|
||||
return new String(PKCS7Encoder.decode(cipher.doFinal(Base64.decodeBase64(encryptedData))), StandardCharsets.UTF_8);
|
||||
return new String(PKCS7Encoder.decode(cipher.doFinal(Base64.decodeBase64(encryptedData))), UTF_8);
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("AES解密失败", e);
|
||||
throw new RuntimeException("AES解密失败!", e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* AES解密.
|
||||
*
|
||||
* @param sessionKey session_key
|
||||
* @param encryptedData 消息密文
|
||||
* @param ivStr iv字符串
|
||||
*/
|
||||
public static String decryptAnotherWay(String sessionKey, String encryptedData, String ivStr) {
|
||||
byte[] keyBytes = Base64.decodeBase64(sessionKey.getBytes(UTF_8));
|
||||
|
||||
int base = 16;
|
||||
if (keyBytes.length % base != 0) {
|
||||
int groups = keyBytes.length / base + (keyBytes.length % base != 0 ? 1 : 0);
|
||||
byte[] temp = new byte[groups * base];
|
||||
Arrays.fill(temp, (byte) 0);
|
||||
System.arraycopy(keyBytes, 0, temp, 0, keyBytes.length);
|
||||
keyBytes = temp;
|
||||
}
|
||||
|
||||
Security.addProvider(new BouncyCastleProvider());
|
||||
Key key = new SecretKeySpec(keyBytes, "AES");
|
||||
try {
|
||||
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS7Padding", "BC");
|
||||
cipher.init(Cipher.DECRYPT_MODE, key, new IvParameterSpec(Base64.decodeBase64(ivStr.getBytes(UTF_8))));
|
||||
return new String(cipher.doFinal(Base64.decodeBase64(encryptedData.getBytes(UTF_8))), UTF_8);
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("AES解密失败!", e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -27,10 +27,6 @@ public class WxMaTemplateMessageGsonAdapter implements JsonSerializer<WxMaTempla
|
||||
messageJson.addProperty("form_id", message.getFormId());
|
||||
}
|
||||
|
||||
if (message.getColor() != null) {
|
||||
messageJson.addProperty("color", message.getColor());
|
||||
}
|
||||
|
||||
if (message.getEmphasisKeyword() != null) {
|
||||
messageJson.addProperty("emphasis_keyword", message.getEmphasisKeyword());
|
||||
}
|
||||
@ -45,9 +41,6 @@ public class WxMaTemplateMessageGsonAdapter implements JsonSerializer<WxMaTempla
|
||||
for (WxMaTemplateData datum : message.getData()) {
|
||||
JsonObject dataJson = new JsonObject();
|
||||
dataJson.addProperty("value", datum.getValue());
|
||||
if (datum.getColor() != null) {
|
||||
dataJson.addProperty("color", datum.getColor());
|
||||
}
|
||||
data.add(datum.getName(), dataJson);
|
||||
}
|
||||
|
||||
|
||||
@ -36,6 +36,8 @@ public class WxMaUniformMessageGsonAdapter implements JsonSerializer<WxMaUniform
|
||||
miniProgramJson.addProperty("appid", miniProgram.getAppid());
|
||||
if (miniProgram.isUsePath()) {
|
||||
miniProgramJson.addProperty("path", miniProgram.getPagePath());
|
||||
} else if (miniProgram.isUsePagePath()) {
|
||||
miniProgramJson.addProperty("pagePath", miniProgram.getPagePath());
|
||||
} else {
|
||||
miniProgramJson.addProperty("pagepath", miniProgram.getPagePath());
|
||||
}
|
||||
@ -79,9 +81,6 @@ public class WxMaUniformMessageGsonAdapter implements JsonSerializer<WxMaUniform
|
||||
for (WxMaTemplateData templateData : message.getData()) {
|
||||
JsonObject dataJson = new JsonObject();
|
||||
dataJson.addProperty("value", templateData.getValue());
|
||||
if (templateData.getColor() != null) {
|
||||
dataJson.addProperty("color", templateData.getColor());
|
||||
}
|
||||
data.add(templateData.getName(), dataJson);
|
||||
}
|
||||
}
|
||||
|
||||
@ -10,9 +10,9 @@ import com.google.gson.JsonParseException;
|
||||
import me.chanjar.weixin.common.util.json.GsonHelper;
|
||||
|
||||
import java.lang.reflect.Type;
|
||||
import java.util.Hashtable;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
/**
|
||||
* @author <a href="https://github.com/charmingoh">Charming</a>
|
||||
@ -36,7 +36,7 @@ public class WxMaVisitDistributionGsonAdapter implements JsonDeserializer<WxMaVi
|
||||
}
|
||||
|
||||
JsonArray listArray = object.getAsJsonArray("list");
|
||||
Map<String, Map<Integer, Integer>> list = new Hashtable<>(listArray.size());
|
||||
Map<String, Map<Integer, Integer>> list = new ConcurrentHashMap<>(listArray.size());
|
||||
for (JsonElement indexElement : listArray) {
|
||||
JsonObject indexObject = indexElement.getAsJsonObject();
|
||||
String index = GsonHelper.getString(indexObject, "index");
|
||||
|
||||
@ -48,10 +48,10 @@ public class WxMaMsgServiceImplTest {
|
||||
.formId("FORMID")
|
||||
.page("index")
|
||||
.data(Lists.newArrayList(
|
||||
new WxMaTemplateData("keyword1", "339208499", "#173177"),
|
||||
new WxMaTemplateData("keyword2", dateFormat.format(new Date()), "#173177"),
|
||||
new WxMaTemplateData("keyword3", "粤海喜来登酒店", "#173177"),
|
||||
new WxMaTemplateData("keyword4", "广州市天河区天河路208号", "#173177")))
|
||||
new WxMaTemplateData("keyword1", "339208499"),
|
||||
new WxMaTemplateData("keyword2", dateFormat.format(new Date())),
|
||||
new WxMaTemplateData("keyword3", "粤海喜来登酒店"),
|
||||
new WxMaTemplateData("keyword4", "广州市天河区天河路208号")))
|
||||
.templateId(config.getTemplateId())
|
||||
.emphasisKeyword("keyword1.DATA")
|
||||
.build();
|
||||
|
||||
@ -1,7 +1,5 @@
|
||||
package cn.binarywang.wx.miniapp.api.impl;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.testng.annotations.*;
|
||||
|
||||
@ -11,6 +9,7 @@ import cn.binarywang.wx.miniapp.test.ApiTestModule;
|
||||
import com.google.inject.Inject;
|
||||
import me.chanjar.weixin.common.error.WxErrorException;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.testng.Assert.*;
|
||||
|
||||
/**
|
||||
@ -33,4 +32,9 @@ public class WxMaServiceImplTest {
|
||||
assertTrue(StringUtils.isNotBlank(after));
|
||||
}
|
||||
|
||||
@Test(expectedExceptions = {WxErrorException.class})
|
||||
public void testGetPaidUnionId() throws WxErrorException {
|
||||
final String unionId = this.wxService.getPaidUnionId("1", null, "3", "4");
|
||||
assertThat(unionId).isNotEmpty();
|
||||
}
|
||||
}
|
||||
|
||||
@ -0,0 +1,35 @@
|
||||
package cn.binarywang.wx.miniapp.util.crypt;
|
||||
|
||||
|
||||
import org.testng.annotations.*;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* <pre>
|
||||
*
|
||||
* Created by Binary Wang on 2018/12/25.
|
||||
* </pre>
|
||||
*
|
||||
* @author <a href="https://github.com/binarywang">Binary Wang</a>
|
||||
*/
|
||||
public class WxMaCryptUtilsTest {
|
||||
@Test
|
||||
public void testDecrypt() {
|
||||
String sessionKey = "7MG7jbTToVVRWRXVA885rg==";
|
||||
String encryptedData = "BY6VOgcWbwGcyrunK0ECWI8rnDsT69DucZ+M78tc1aL9aM/3bEAHFYd4fu7kRjWhD4YfjObw44T9vUqKyHIjbKs6hvtEasZZEIW35x4a91xVgN48ZqZ7MTQqUlP13kDUlkuwYh+/8g8yceu4kNbjowYrhihx+SV7CfjKCveJ7TSepr5Z7aLv1o+rfeelfOwn++WN/YoQsuZ6S3L4fWlWe5DAAUnFUI6cJvxxCohVzbrVXhyH2AqQdSjH2WnMYFeaGFIbcoxMznlk7oEwFn+hBj63dyT/swdYQfEdzuyCBmKXy8d6l1RKVX6Y65coTD8kIlbr+FKsqYrXVUIUBSwehqYuOdhYWZ9Bntl5DWU1oqzAPCnMn2cAIoQpQPKP7IGSxMOvCNAMhVXbE7BvnWuVuGF+AM5tXAa9IVUhcMImGwLQqm4iV5uBd+5OcFObh3A4VJk9iBCBWSkBHa/rV9CVoY0bFv2F9/2Hv82++Ybl274=";
|
||||
String ivStr = "TarMFjnzHVxy8pdS93wQbw==";
|
||||
System.out.println(WxMaCryptUtils.decrypt(sessionKey, encryptedData, ivStr));
|
||||
// System.out.println(WxMaCryptUtils.decryptAnotherWay(sessionKey, encryptedData, ivStr));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDecryptAnotherWay() {
|
||||
String encryptedData = "CiyLU1Aw2KjvrjMdj8YKliAjtP4gsMZMQmRzooG2xrDcvSnxIMXFufNstNGTyaGS9uT5geRa0W4oTOb1WT7fJlAC+oNPdbB+3hVbJSRgv+4lGOETKUQz6OYStslQ142dNCuabNPGBzlooOmB231qMM85d2/fV6ChevvXvQP8Hkue1poOFtnEtpyxVLW1zAo6/1Xx1COxFvrc2d7UL/lmHInNlxuacJXwu0fjpXfz/YqYzBIBzD6WUfTIF9GRHpOn/Hz7saL8xz+W//FRAUid1OksQaQx4CMs8LOddcQhULW4ucetDf96JcR3g0gfRK4PC7E/r7Z6xNrXd2UIeorGj5Ef7b1pJAYB6Y5anaHqZ9J6nKEBvB4DnNLIVWSgARns/8wR2SiRS7MNACwTyrGvt9ts8p12PKFdlqYTopNHR1Vf7XjfhQlVsAJdNiKdYmYVoKlaRv85IfVunYzO0IKXsyl7JCUjCpoG20f0a04COwfneQAGGwd5oa+T8yO5hzuyDb/XcxxmK01EpqOyuxINew==";
|
||||
String ivStr = "r7BXXKkLb8qrSNn05n0qiA==";
|
||||
String sessionKey = "tiihtNczf5v6AKRyjwEUhQ==";
|
||||
|
||||
assertThat(WxMaCryptUtils.decrypt(sessionKey, encryptedData, ivStr))
|
||||
.isEqualTo(WxMaCryptUtils.decryptAnotherWay(sessionKey, encryptedData, ivStr));
|
||||
}
|
||||
}
|
||||
@ -1,10 +1,9 @@
|
||||
package cn.binarywang.wx.miniapp.util.json;
|
||||
|
||||
import org.testng.annotations.*;
|
||||
|
||||
import cn.binarywang.wx.miniapp.bean.WxMaTemplateData;
|
||||
import cn.binarywang.wx.miniapp.bean.WxMaUniformMessage;
|
||||
import com.google.gson.JsonParser;
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
@ -26,7 +25,7 @@ public class WxMaUniformMessageGsonAdapterTest {
|
||||
.appid("APPID")
|
||||
.templateId("TEMPLATE_ID")
|
||||
.url("http://weixin.qq.com/download")
|
||||
.miniProgram(new WxMaUniformMessage.MiniProgram("xiaochengxuappid12345", "index?foo=bar", false))
|
||||
.miniProgram(new WxMaUniformMessage.MiniProgram("xiaochengxuappid12345", "index?foo=bar", false, false))
|
||||
.build();
|
||||
message.addData(new WxMaTemplateData("first", "恭喜你购买成功!", "#173177"))
|
||||
.addData(new WxMaTemplateData("keyword1", "巧克力", "#173177"))
|
||||
@ -72,7 +71,7 @@ public class WxMaUniformMessageGsonAdapterTest {
|
||||
|
||||
@Test
|
||||
public void testSerialize_ma() {
|
||||
WxMaUniformMessage message = WxMaUniformMessage.builder()
|
||||
WxMaUniformMessage message = WxMaUniformMessage.builder()
|
||||
.isMpTemplateMsg(false)
|
||||
.toUser("OPENID")
|
||||
.page("page/page/index")
|
||||
|
||||
Reference in New Issue
Block a user