合并开发分支,发布正式版

This commit is contained in:
Binary Wang
2019-05-18 17:07:45 +08:00
committed by GitHub
194 changed files with 6446 additions and 1140 deletions

View File

@ -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>

View File

@ -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 失败时抛出,具体错误码请看此接口的注释文档

View File

@ -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>
*/

View File

@ -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();

View File

@ -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);

View File

@ -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")

View File

@ -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;
}
}

View File

@ -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>

View File

@ -104,5 +104,11 @@ public class WxMaUniformMessage implements Serializable {
* 加入此字段是基于微信官方接口变化多端的考虑
*/
private boolean usePath = false;
/**
* 是否使用pagePath否则使用pagepath.
* 加入此字段是基于微信官方接口变化多端的考虑
*/
private boolean usePagePath = false;
}
}

View File

@ -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);

View File

@ -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);
}
}

View File

@ -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);
}

View File

@ -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);
}
}

View File

@ -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");

View File

@ -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();

View File

@ -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();
}
}

View File

@ -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));
}
}

View File

@ -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")