| @@ -40,7 +40,7 @@ | |||
| <property name="allowNonPrintableEscapes" value="true"/> | |||
| </module> | |||
| <module name="LineLength"> | |||
| <property name="max" value="100"/> | |||
| <property name="max" value="120"/> | |||
| <property name="ignorePattern" value="^package.*|^import.*|a href|href|http://|https://|ftp://"/> | |||
| </module> | |||
| <module name="AvoidStarImport"/> | |||
| @@ -52,7 +52,7 @@ | |||
| </module> | |||
| <module name="NeedBraces"/> | |||
| <module name="LeftCurly"> | |||
| <property name="maxLineLength" value="100"/> | |||
| <property name="maxLineLength" value="120"/> | |||
| </module> | |||
| <module name="RightCurly"> | |||
| <property name="id" value="RightCurlySame"/> | |||
| @@ -4,13 +4,13 @@ import java.util.HashMap; | |||
| import java.util.Map; | |||
| /** | |||
| * 微信开发所使用到的常量类 | |||
| * 微信开发所使用到的常量类. | |||
| * | |||
| * @author chanjarster & binarywang | |||
| */ | |||
| public class WxConsts { | |||
| /** | |||
| * 微信推送过来的消息的类型,和发送给微信xml格式消息的消息类型 | |||
| * 微信推送过来的消息的类型,和发送给微信xml格式消息的消息类型. | |||
| */ | |||
| public static class XmlMsgType { | |||
| public static final String TEXT = "text"; | |||
| @@ -31,24 +31,57 @@ public class WxConsts { | |||
| } | |||
| /** | |||
| * 主动发送消息(即客服消息)的消息类型 | |||
| * 主动发送消息(即客服消息)的消息类型. | |||
| */ | |||
| public static class KefuMsgType { | |||
| public static final String TEXT = "text";//文本消息 | |||
| public static final String IMAGE = "image";//图片消息 | |||
| public static final String VOICE = "voice";//语音消息 | |||
| public static final String VIDEO = "video";//视频消息 | |||
| public static final String MUSIC = "music";//音乐消息 | |||
| public static final String NEWS = "news";//图文消息(点击跳转到外链) | |||
| public static final String MPNEWS = "mpnews";//图文消息(点击跳转到图文消息页面) | |||
| public static final String FILE = "file";//发送文件(CP专用) | |||
| public static final String TEXTCARD = "textcard";//文本卡片消息(CP专用) | |||
| public static final String WXCARD = "wxcard";//卡券消息 | |||
| /** | |||
| * 文本消息. | |||
| */ | |||
| public static final String TEXT = "text"; | |||
| /** | |||
| * 图片消息. | |||
| */ | |||
| public static final String IMAGE = "image"; | |||
| /** | |||
| * 语音消息. | |||
| */ | |||
| public static final String VOICE = "voice"; | |||
| /** | |||
| * 视频消息. | |||
| */ | |||
| public static final String VIDEO = "video"; | |||
| /** | |||
| * 音乐消息. | |||
| */ | |||
| public static final String MUSIC = "music"; | |||
| /** | |||
| * 图文消息(点击跳转到外链). | |||
| */ | |||
| public static final String NEWS = "news"; | |||
| /** | |||
| * 图文消息(点击跳转到图文消息页面). | |||
| */ | |||
| public static final String MPNEWS = "mpnews"; | |||
| /** | |||
| * 发送文件(CP专用). | |||
| */ | |||
| public static final String FILE = "file"; | |||
| /** | |||
| * 文本卡片消息(CP专用). | |||
| */ | |||
| public static final String TEXTCARD = "textcard"; | |||
| /** | |||
| * 卡券消息. | |||
| */ | |||
| public static final String WXCARD = "wxcard"; | |||
| /** | |||
| * 转发到客服的消息. | |||
| */ | |||
| public static final String TRANSFER_CUSTOMER_SERVICE = "transfer_customer_service"; | |||
| } | |||
| /** | |||
| * 表示是否是保密消息,0表示否,1表示是,默认0 | |||
| * 表示是否是保密消息,0表示否,1表示是,默认0. | |||
| */ | |||
| public static class KefuMsgSafe { | |||
| public static final String NO = "0"; | |||
| @@ -56,7 +89,7 @@ public class WxConsts { | |||
| } | |||
| /** | |||
| * 群发消息的消息类型 | |||
| * 群发消息的消息类型. | |||
| */ | |||
| public static class MassMsgType { | |||
| public static final String MPNEWS = "mpnews"; | |||
| @@ -67,7 +100,7 @@ public class WxConsts { | |||
| } | |||
| /** | |||
| * 群发消息后微信端推送给服务器的反馈消息 | |||
| * 群发消息后微信端推送给服务器的反馈消息. | |||
| */ | |||
| public static class MassMsgStatus { | |||
| public static final String SEND_SUCCESS = "send success"; | |||
| @@ -83,7 +116,7 @@ public class WxConsts { | |||
| public static final String ERR_21000 = "err(21000)"; | |||
| /** | |||
| * 群发反馈消息代码所对应的文字描述 | |||
| * 群发反馈消息代码所对应的文字描述. | |||
| */ | |||
| public static final Map<String, String> STATUS_DESC = new HashMap<>(); | |||
| @@ -103,7 +136,7 @@ public class WxConsts { | |||
| } | |||
| /** | |||
| * 微信端推送过来的事件类型 | |||
| * 微信端推送过来的事件类型. | |||
| */ | |||
| public static class EventType { | |||
| public static final String SUBSCRIBE = "subscribe"; | |||
| @@ -124,7 +157,7 @@ public class WxConsts { | |||
| } | |||
| /** | |||
| * 上传多媒体(临时素材)文件的类型 | |||
| * 上传多媒体(临时素材)文件的类型. | |||
| */ | |||
| public static class MediaFileType { | |||
| public static final String IMAGE = "image"; | |||
| @@ -135,78 +168,78 @@ public class WxConsts { | |||
| } | |||
| /** | |||
| * 自定义菜单的按钮类型 | |||
| * 自定义菜单的按钮类型. | |||
| */ | |||
| public static class MenuButtonType { | |||
| /** | |||
| * 点击推事件 | |||
| * 点击推事件. | |||
| */ | |||
| public static final String CLICK = "click"; | |||
| /** | |||
| * 跳转URL | |||
| * 跳转URL. | |||
| */ | |||
| public static final String VIEW = "view"; | |||
| /** | |||
| * 跳转到小程序 | |||
| * 跳转到小程序. | |||
| */ | |||
| public static final String MINIPROGRAM = "miniprogram"; | |||
| /** | |||
| * 扫码推事件 | |||
| * 扫码推事件. | |||
| */ | |||
| public static final String SCANCODE_PUSH = "scancode_push"; | |||
| /** | |||
| * 扫码推事件且弹出“消息接收中”提示框 | |||
| * 扫码推事件且弹出“消息接收中”提示框. | |||
| */ | |||
| public static final String SCANCODE_WAITMSG = "scancode_waitmsg"; | |||
| /** | |||
| * 弹出系统拍照发图 | |||
| * 弹出系统拍照发图. | |||
| */ | |||
| public static final String PIC_SYSPHOTO = "pic_sysphoto"; | |||
| /** | |||
| * 弹出拍照或者相册发图 | |||
| * 弹出拍照或者相册发图. | |||
| */ | |||
| public static final String PIC_PHOTO_OR_ALBUM = "pic_photo_or_album"; | |||
| /** | |||
| * 弹出微信相册发图器 | |||
| * 弹出微信相册发图器. | |||
| */ | |||
| public static final String PIC_WEIXIN = "pic_weixin"; | |||
| /** | |||
| * 弹出地理位置选择器 | |||
| * 弹出地理位置选择器. | |||
| */ | |||
| public static final String LOCATION_SELECT = "location_select"; | |||
| /** | |||
| * 下发消息(除文本消息) | |||
| * 下发消息(除文本消息). | |||
| */ | |||
| public static final String MEDIA_ID = "media_id"; | |||
| /** | |||
| * 跳转图文消息URL | |||
| * 跳转图文消息URL. | |||
| */ | |||
| public static final String VIEW_LIMITED = "view_limited"; | |||
| } | |||
| /** | |||
| * oauth2网页授权的scope | |||
| * oauth2网页授权的scope. | |||
| */ | |||
| public static class OAuth2Scope { | |||
| /** | |||
| * 不弹出授权页面,直接跳转,只能获取用户openid | |||
| * 不弹出授权页面,直接跳转,只能获取用户openid. | |||
| */ | |||
| public static final String SNSAPI_BASE = "snsapi_base"; | |||
| /** | |||
| * 弹出授权页面,可通过openid拿到昵称、性别、所在地。并且,即使在未关注的情况下,只要用户授权,也能获取其信息 | |||
| * 弹出授权页面,可通过openid拿到昵称、性别、所在地。并且,即使在未关注的情况下,只要用户授权,也能获取其信息. | |||
| */ | |||
| public static final String SNSAPI_USERINFO = "snsapi_userinfo"; | |||
| } | |||
| /** | |||
| * 网页应用登录授权作用域 | |||
| * 网页应用登录授权作用域. | |||
| */ | |||
| public static class QrConnectScope { | |||
| public static final String SNSAPI_LOGIN = "snsapi_login"; | |||
| } | |||
| /** | |||
| * 永久素材类型 | |||
| * 永久素材类型. | |||
| */ | |||
| public static class MaterialType { | |||
| public static final String NEWS = "news"; | |||
| @@ -3,7 +3,7 @@ package me.chanjar.weixin.common.api; | |||
| import me.chanjar.weixin.common.exception.WxErrorException; | |||
| /** | |||
| * WxErrorException处理器 | |||
| * WxErrorException处理器. | |||
| */ | |||
| public interface WxErrorExceptionHandler { | |||
| @@ -2,22 +2,21 @@ package me.chanjar.weixin.common.api; | |||
| /** | |||
| * <pre> | |||
| * 消息重复检查器 | |||
| * 消息重复检查器. | |||
| * 微信服务器在五秒内收不到响应会断掉连接,并且重新发起请求,总共重试三次 | |||
| * </pre> | |||
| */ | |||
| public interface WxMessageDuplicateChecker { | |||
| /** | |||
| * 判断消息是否重复. | |||
| * <h2>公众号的排重方式</h2> | |||
| * <p> | |||
| * | |||
| * <p>普通消息:关于重试的消息排重,推荐使用msgid排重。<a href="http://mp.weixin.qq.com/wiki/10/79502792eef98d6e0c6e1739da387346.html">文档参考</a>。</p> | |||
| * <p>事件消息:关于重试的消息排重,推荐使用FromUserName + CreateTime 排重。<a href="http://mp.weixin.qq.com/wiki/2/5baf56ce4947d35003b86a9805634b1e.html">文档参考</a></p> | |||
| * <p> | |||
| * | |||
| * <h2>企业号的排重方式</h2> | |||
| * <p> | |||
| * 官方文档完全没有写,参照公众号的方式排重。 | |||
| * <p> | |||
| * <p>官方文档完全没有写,参照公众号的方式排重。</p> | |||
| * <p>或者可以采取更简单的方式,如果有MsgId就用MsgId排重,如果没有就用FromUserName+CreateTime排重</p> | |||
| * | |||
| * @param messageId messageId需要根据上面讲的方式构造 | |||
| @@ -6,46 +6,46 @@ import java.util.concurrent.atomic.AtomicBoolean; | |||
| /** | |||
| * <pre> | |||
| * 默认消息重复检查器 | |||
| * 默认消息重复检查器. | |||
| * 将每个消息id保存在内存里,每隔5秒清理已经过期的消息id,每个消息id的过期时间是15秒 | |||
| * </pre> | |||
| */ | |||
| public class WxMessageInMemoryDuplicateChecker implements WxMessageDuplicateChecker { | |||
| /** | |||
| * 一个消息ID在内存的过期时间:15秒 | |||
| * 一个消息ID在内存的过期时间:15秒. | |||
| */ | |||
| private final Long timeToLive; | |||
| /** | |||
| * 每隔多少周期检查消息ID是否过期:5秒 | |||
| * 每隔多少周期检查消息ID是否过期:5秒. | |||
| */ | |||
| private final Long clearPeriod; | |||
| /** | |||
| * 消息id->消息时间戳的map | |||
| * 消息id->消息时间戳的map. | |||
| */ | |||
| private final ConcurrentHashMap<String, Long> msgId2Timestamp = new ConcurrentHashMap<>(); | |||
| /** | |||
| * 后台清理线程是否已经开启 | |||
| * 后台清理线程是否已经开启. | |||
| */ | |||
| private final AtomicBoolean backgroundProcessStarted = new AtomicBoolean(false); | |||
| /** | |||
| * WxMsgIdInMemoryDuplicateChecker构造函数 | |||
| * 无参构造方法. | |||
| * <pre> | |||
| * 一个消息ID在内存的过期时间:15秒 | |||
| * 每隔多少周期检查消息ID是否过期:5秒 | |||
| * </pre> | |||
| */ | |||
| public WxMessageInMemoryDuplicateChecker() { | |||
| this.timeToLive = 15 * 1000l; | |||
| this.clearPeriod = 5 * 1000l; | |||
| this.timeToLive = 15 * 1000L; | |||
| this.clearPeriod = 5 * 1000L; | |||
| } | |||
| /** | |||
| * WxMsgIdInMemoryDuplicateChecker构造函数 | |||
| * 构造方法. | |||
| * | |||
| * @param timeToLive 一个消息ID在内存的过期时间:毫秒 | |||
| * @param clearPeriod 每隔多少周期检查消息ID是否过期:毫秒 | |||
| @@ -66,7 +66,8 @@ public class WxMessageInMemoryDuplicateChecker implements WxMessageDuplicateChec | |||
| while (true) { | |||
| Thread.sleep(WxMessageInMemoryDuplicateChecker.this.clearPeriod); | |||
| Long now = System.currentTimeMillis(); | |||
| for (Map.Entry<String, Long> entry : WxMessageInMemoryDuplicateChecker.this.msgId2Timestamp.entrySet()) { | |||
| for (Map.Entry<String, Long> entry : | |||
| WxMessageInMemoryDuplicateChecker.this.msgId2Timestamp.entrySet()) { | |||
| if (now - entry.getValue() > WxMessageInMemoryDuplicateChecker.this.timeToLive) { | |||
| WxMessageInMemoryDuplicateChecker.this.msgId2Timestamp.entrySet().remove(entry); | |||
| } | |||
| @@ -11,7 +11,7 @@ import java.util.ArrayList; | |||
| import java.util.List; | |||
| /** | |||
| * 菜单(公众号和企业号共用的) | |||
| * 菜单(公众号和企业号共用的). | |||
| * | |||
| * @author Daniel Qian | |||
| */ | |||
| @@ -36,7 +36,8 @@ public class WxMenu implements Serializable { | |||
| * 相比 http://mp.weixin.qq.com/wiki/13/43de8269be54a0a6f64413e4dfa94f39.html 的格式,外层多套了一个menu | |||
| */ | |||
| public static WxMenu fromJson(InputStream is) { | |||
| return WxGsonBuilder.create().fromJson(new InputStreamReader(is, StandardCharsets.UTF_8), WxMenu.class); | |||
| return WxGsonBuilder.create() | |||
| .fromJson(new InputStreamReader(is, StandardCharsets.UTF_8), WxMenu.class); | |||
| } | |||
| public List<WxMenuButton> getButtons() { | |||
| @@ -12,7 +12,7 @@ public class WxMenuButton implements Serializable { | |||
| /** | |||
| * <pre> | |||
| * 菜单的响应动作类型: | |||
| * 菜单的响应动作类型. | |||
| * view表示网页类型, | |||
| * click表示点击类型, | |||
| * miniprogram表示小程序类型 | |||
| @@ -21,13 +21,13 @@ public class WxMenuButton implements Serializable { | |||
| private String type; | |||
| /** | |||
| * 菜单标题,不超过16个字节,子菜单不超过60个字节 | |||
| * 菜单标题,不超过16个字节,子菜单不超过60个字节. | |||
| */ | |||
| private String name; | |||
| /** | |||
| * <pre> | |||
| * 菜单KEY值,用于消息接口推送,不超过128字节 | |||
| * 菜单KEY值,用于消息接口推送,不超过128字节. | |||
| * click等点击类型必须 | |||
| * </pre> | |||
| */ | |||
| @@ -35,7 +35,8 @@ public class WxMenuButton implements Serializable { | |||
| /** | |||
| * <pre> | |||
| * 网页链接,用户点击菜单可打开链接,不超过1024字节。type为miniprogram时,不支持小程序的老版本客户端将打开本url。 | |||
| * 网页链接. | |||
| * 用户点击菜单可打开链接,不超过1024字节。type为miniprogram时,不支持小程序的老版本客户端将打开本url。 | |||
| * view、miniprogram类型必须 | |||
| * </pre> | |||
| */ | |||
| @@ -43,7 +44,7 @@ public class WxMenuButton implements Serializable { | |||
| /** | |||
| * <pre> | |||
| * 调用新增永久素材接口返回的合法media_id | |||
| * 调用新增永久素材接口返回的合法media_id. | |||
| * media_id类型和view_limited类型必须 | |||
| * </pre> | |||
| */ | |||
| @@ -52,7 +53,7 @@ public class WxMenuButton implements Serializable { | |||
| /** | |||
| * <pre> | |||
| * 小程序的appid | |||
| * 小程序的appid. | |||
| * miniprogram类型必须 | |||
| * </pre> | |||
| */ | |||
| @@ -61,7 +62,7 @@ public class WxMenuButton implements Serializable { | |||
| /** | |||
| * <pre> | |||
| * 小程序的页面路径 | |||
| * 小程序的页面路径. | |||
| * miniprogram类型必须 | |||
| * </pre> | |||
| */ | |||
| @@ -9,7 +9,7 @@ public class WxMenuRule implements Serializable { | |||
| private static final long serialVersionUID = -4587181819499286670L; | |||
| /** | |||
| * 变态的微信接口,反序列化时这里反人类的使用和序列化时不一样的名字 | |||
| * 变态的微信接口,反序列化时这里反人类的使用和序列化时不一样的名字. | |||
| */ | |||
| @SerializedName(value = "tag_id", alternate = "group_id") | |||
| private String tagId; | |||
| @@ -5,7 +5,7 @@ import me.chanjar.weixin.common.util.json.WxGsonBuilder; | |||
| import java.io.Serializable; | |||
| /** | |||
| * 微信错误码说明,请阅读: <a href="http://mp.weixin.qq.com/wiki/10/6380dc743053a91c544ffd2b7c959166.html">全局返回码说明</a> | |||
| * 微信错误码说明,请阅读: <a href="http://mp.weixin.qq.com/wiki/10/6380dc743053a91c544ffd2b7c959166.html">全局返回码说明</a>. | |||
| * | |||
| * @author Daniel Qian | |||
| */ | |||
| @@ -73,6 +73,9 @@ public class WxError implements Serializable { | |||
| return this; | |||
| } | |||
| /** | |||
| * 构造器方法. | |||
| */ | |||
| public WxError build() { | |||
| WxError wxError = new WxError(); | |||
| wxError.setErrorCode(this.errorCode); | |||
| @@ -4,51 +4,41 @@ import org.apache.http.conn.ssl.SSLConnectionSocketFactory; | |||
| import org.apache.http.impl.client.CloseableHttpClient; | |||
| /** | |||
| * httpclient build interface | |||
| * httpclient build interface. | |||
| * | |||
| * @author kakotor | |||
| */ | |||
| public interface ApacheHttpClientBuilder { | |||
| /** | |||
| * 构建httpclient实例 | |||
| * 构建httpclient实例. | |||
| * | |||
| * @return new instance of CloseableHttpClient | |||
| */ | |||
| CloseableHttpClient build(); | |||
| /** | |||
| * 代理服务器地址 | |||
| * | |||
| * @param httpProxyHost | |||
| * 代理服务器地址. | |||
| */ | |||
| ApacheHttpClientBuilder httpProxyHost(String httpProxyHost); | |||
| /** | |||
| * 代理服务器端口 | |||
| * | |||
| * @param httpProxyPort | |||
| * 代理服务器端口. | |||
| */ | |||
| ApacheHttpClientBuilder httpProxyPort(int httpProxyPort); | |||
| /** | |||
| * 代理服务器用户名 | |||
| * | |||
| * @param httpProxyUsername | |||
| * 代理服务器用户名. | |||
| */ | |||
| ApacheHttpClientBuilder httpProxyUsername(String httpProxyUsername); | |||
| /** | |||
| * 代理服务器密码 | |||
| * | |||
| * @param httpProxyPassword | |||
| * 代理服务器密码. | |||
| */ | |||
| ApacheHttpClientBuilder httpProxyPassword(String httpProxyPassword); | |||
| /** | |||
| * ssl连接socket工厂 | |||
| * | |||
| * @param sslConnectionSocketFactory | |||
| * ssl连接socket工厂. | |||
| */ | |||
| ApacheHttpClientBuilder sslConnectionSocketFactory(SSLConnectionSocketFactory sslConnectionSocketFactory); | |||
| } | |||
| @@ -29,9 +29,8 @@ import java.util.concurrent.TimeUnit; | |||
| import java.util.concurrent.atomic.AtomicBoolean; | |||
| /** | |||
| * httpclient 连接管理器 自带DNS解析 | |||
| * <p> | |||
| * 大部分代码拷贝自:DefaultApacheHttpClientBuilder | |||
| * httpclient 连接管理器 自带DNS解析. | |||
| * <p>大部分代码拷贝自:DefaultApacheHttpClientBuilder</p> | |||
| * | |||
| * @author Andy.Huo | |||
| */ | |||
| @@ -64,7 +63,7 @@ public class ApacheHttpDnsClientBuilder implements ApacheHttpClientBuilder { | |||
| private String httpProxyPassword; | |||
| /** | |||
| * 闲置连接监控线程 | |||
| * 闲置连接监控线程. | |||
| */ | |||
| private IdleConnectionMonitorThread idleConnectionMonitorThread; | |||
| private HttpClientBuilder httpClientBuilder; | |||
| @@ -162,7 +161,7 @@ public class ApacheHttpDnsClientBuilder implements ApacheHttpClientBuilder { | |||
| } | |||
| /** | |||
| * 每路的最大链接数,默认10 | |||
| * 每路的最大链接数,默认10. | |||
| * | |||
| * @param maxConnPerHost 每路的最大链接数,默认10 | |||
| */ | |||
| @@ -171,7 +170,7 @@ public class ApacheHttpDnsClientBuilder implements ApacheHttpClientBuilder { | |||
| } | |||
| /** | |||
| * 最大总连接数,默认50 | |||
| * 最大总连接数,默认50. | |||
| * | |||
| * @param maxTotalConn 最大总连接数,默认50 | |||
| */ | |||
| @@ -180,7 +179,7 @@ public class ApacheHttpDnsClientBuilder implements ApacheHttpClientBuilder { | |||
| } | |||
| /** | |||
| * 自定义httpclient的User Agent | |||
| * 自定义httpclient的User Agent. | |||
| * | |||
| * @param userAgent User Agent | |||
| */ | |||
| @@ -196,9 +195,12 @@ public class ApacheHttpDnsClientBuilder implements ApacheHttpClientBuilder { | |||
| if (prepared.get()) { | |||
| return; | |||
| } | |||
| Registry<ConnectionSocketFactory> registry = RegistryBuilder.<ConnectionSocketFactory>create() | |||
| .register("http", this.plainConnectionSocketFactory).register("https", this.sslConnectionSocketFactory) | |||
| .build(); | |||
| Registry<ConnectionSocketFactory> registry = | |||
| RegistryBuilder.<ConnectionSocketFactory>create() | |||
| .register("http", this.plainConnectionSocketFactory) | |||
| .register("https", this.sslConnectionSocketFactory) | |||
| .build(); | |||
| @SuppressWarnings("resource") | |||
| PoolingHttpClientConnectionManager connectionManager; | |||
| @@ -219,8 +221,8 @@ public class ApacheHttpDnsClientBuilder implements ApacheHttpClientBuilder { | |||
| connectionManager | |||
| .setDefaultSocketConfig(SocketConfig.copy(SocketConfig.DEFAULT).setSoTimeout(this.soTimeout).build()); | |||
| this.idleConnectionMonitorThread = new IdleConnectionMonitorThread(connectionManager, this.idleConnTimeout, | |||
| this.checkWaitTime); | |||
| this.idleConnectionMonitorThread = new IdleConnectionMonitorThread( | |||
| connectionManager, this.idleConnTimeout, this.checkWaitTime); | |||
| this.idleConnectionMonitorThread.setDaemon(true); | |||
| this.idleConnectionMonitorThread.start(); | |||
| @@ -234,8 +236,8 @@ public class ApacheHttpDnsClientBuilder implements ApacheHttpClientBuilder { | |||
| if (StringUtils.isNotBlank(this.httpProxyHost) && StringUtils.isNotBlank(this.httpProxyUsername)) { | |||
| // 使用代理服务器 需要用户认证的代理服务器 | |||
| CredentialsProvider provider = new BasicCredentialsProvider(); | |||
| provider.setCredentials(new AuthScope(this.httpProxyHost, this.httpProxyPort), | |||
| new UsernamePasswordCredentials(this.httpProxyUsername, this.httpProxyPassword)); | |||
| provider.setCredentials(new AuthScope(this.httpProxyHost, this.httpProxyPort) | |||
| , new UsernamePasswordCredentials(this.httpProxyUsername, this.httpProxyPassword)); | |||
| this.httpClientBuilder.setDefaultCredentialsProvider(provider); | |||
| } | |||
| @@ -267,8 +269,10 @@ public class ApacheHttpDnsClientBuilder implements ApacheHttpClientBuilder { | |||
| private final int checkWaitTime; | |||
| private volatile boolean shutdown; | |||
| public IdleConnectionMonitorThread(HttpClientConnectionManager connMgr, int idleConnTimeout, | |||
| int checkWaitTime) { | |||
| /** | |||
| * 构造方法. | |||
| */ | |||
| public IdleConnectionMonitorThread(HttpClientConnectionManager connMgr, int idleConnTimeout, int checkWaitTime) { | |||
| super("IdleConnectionMonitorThread"); | |||
| this.connMgr = connMgr; | |||
| this.idleConnTimeout = idleConnTimeout; | |||
| @@ -289,12 +293,18 @@ public class ApacheHttpDnsClientBuilder implements ApacheHttpClientBuilder { | |||
| } | |||
| } | |||
| /** | |||
| * 触发. | |||
| */ | |||
| public void trigger() { | |||
| synchronized (this) { | |||
| notifyAll(); | |||
| } | |||
| } | |||
| /** | |||
| * 关闭. | |||
| */ | |||
| public void shutdown() { | |||
| this.shutdown = true; | |||
| synchronized (this) { | |||
| @@ -2,8 +2,8 @@ package me.chanjar.weixin.common.util.http.okhttp; | |||
| import me.chanjar.weixin.common.bean.result.WxError; | |||
| import me.chanjar.weixin.common.exception.WxErrorException; | |||
| import me.chanjar.weixin.common.util.http.HttpResponseProxy; | |||
| import me.chanjar.weixin.common.util.http.BaseMediaDownloadRequestExecutor; | |||
| import me.chanjar.weixin.common.util.http.HttpResponseProxy; | |||
| import me.chanjar.weixin.common.util.http.RequestHttp; | |||
| import okhttp3.OkHttpClient; | |||
| import okhttp3.Request; | |||
| @@ -28,7 +28,7 @@ public class XStreamTransformer { | |||
| } | |||
| /** | |||
| * 注册扩展消息的解析器 | |||
| * 注册扩展消息的解析器. | |||
| * | |||
| * @param clz 类型 | |||
| * @param xStream xml解析器 | |||
| @@ -3,7 +3,7 @@ package cn.binarywang.wx.miniapp.bean; | |||
| import cn.binarywang.wx.miniapp.util.json.WxMaGsonBuilder; | |||
| /** | |||
| * 微信二维码(小程序码)包装器 | |||
| * 微信二维码(小程序码)包装器. | |||
| * | |||
| * @author Element | |||
| */ | |||
| @@ -15,6 +15,9 @@ public class BaseBuilder<T> { | |||
| return (T) this; | |||
| } | |||
| /** | |||
| * 构造器方法. | |||
| */ | |||
| public WxMaKefuMessage build() { | |||
| WxMaKefuMessage m = new WxMaKefuMessage(); | |||
| m.setMsgType(this.msgType); | |||
| @@ -36,11 +36,9 @@ public class QrCodeRequestExecutor implements RequestExecutor<File, AbstractWxMa | |||
| public File execute(String uri, AbstractWxMaQrcodeWrapper ticket) throws WxErrorException, IOException { | |||
| HttpPost httpPost = new HttpPost(uri); | |||
| if (requestHttp.getRequestHttpProxy() != null) { | |||
| httpPost | |||
| .setConfig(RequestConfig.custom() | |||
| .setProxy(requestHttp.getRequestHttpProxy()) | |||
| .build() | |||
| ); | |||
| httpPost.setConfig( | |||
| RequestConfig.custom().setProxy(requestHttp.getRequestHttpProxy()).build() | |||
| ); | |||
| } | |||
| httpPost.setEntity(new StringEntity(ticket.toString())); | |||
| @@ -48,8 +46,8 @@ public class QrCodeRequestExecutor implements RequestExecutor<File, AbstractWxMa | |||
| InputStream inputStream = InputStreamResponseHandler.INSTANCE.handleResponse(response);) { | |||
| Header[] contentTypeHeader = response.getHeaders("Content-Type"); | |||
| if (contentTypeHeader != null && contentTypeHeader.length > 0 | |||
| && ContentType.APPLICATION_JSON.getMimeType() | |||
| .equals(ContentType.parse(contentTypeHeader[0].getValue()).getMimeType())) { | |||
| && ContentType.APPLICATION_JSON.getMimeType() | |||
| .equals(ContentType.parse(contentTypeHeader[0].getValue()).getMimeType())) { | |||
| String responseContent = Utf8ResponseHandler.INSTANCE.handleResponse(response); | |||
| throw new WxErrorException(WxError.fromJson(responseContent)); | |||
| } | |||
| @@ -18,7 +18,7 @@ public class XStreamTransformer { | |||
| } | |||
| /** | |||
| * xml -> pojo | |||
| * xml -> pojo. | |||
| */ | |||
| @SuppressWarnings("unchecked") | |||
| public static <T> T fromXml(Class<T> clazz, String xml) { | |||
| @@ -33,14 +33,14 @@ public class XStreamTransformer { | |||
| } | |||
| /** | |||
| * pojo -> xml | |||
| * pojo -> xml. | |||
| */ | |||
| public static <T> String toXml(Class<T> clazz, T object) { | |||
| return CLASS_2_XSTREAM_INSTANCE.get(clazz).toXML(object); | |||
| } | |||
| /** | |||
| * 注册扩展消息的解析器 | |||
| * 注册扩展消息的解析器. | |||
| * | |||
| * @param clz 类型 | |||
| * @param xStream xml解析器 | |||
| @@ -50,7 +50,7 @@ public class XStreamTransformer { | |||
| } | |||
| /** | |||
| * 会自动注册该类及其子类 | |||
| * 会自动注册该类及其子类. | |||
| * | |||
| * @param clz 要注册的类 | |||
| */ | |||
| @@ -5,6 +5,8 @@ import me.chanjar.weixin.common.util.json.WxGsonBuilder; | |||
| import java.io.Serializable; | |||
| /** | |||
| * 设备抽象类. | |||
| * | |||
| * @author keungtung | |||
| * @date 14/12/2016 | |||
| */ | |||
| @@ -22,9 +22,13 @@ import java.util.HashMap; | |||
| import java.util.Map; | |||
| /** | |||
| * Created by ecoolper on 2017/5/5. | |||
| * httpclient 实现的素材请求执行器. | |||
| * | |||
| * @author ecoolper | |||
| * @date 2017/5/5 | |||
| */ | |||
| public class ApacheMaterialNewsInfoRequestExecutor extends MaterialNewsInfoRequestExecutor<CloseableHttpClient, HttpHost> { | |||
| public class ApacheMaterialNewsInfoRequestExecutor | |||
| extends MaterialNewsInfoRequestExecutor<CloseableHttpClient, HttpHost> { | |||
| private final Logger logger = LoggerFactory.getLogger(this.getClass()); | |||
| public ApacheMaterialNewsInfoRequestExecutor(RequestHttp requestHttp) { | |||
| @@ -19,16 +19,17 @@ import java.util.TreeMap; | |||
| /** | |||
| * <pre> | |||
| * 签名相关工具类 | |||
| * 签名相关工具类. | |||
| * Created by Binary Wang on 2017-3-23. | |||
| * @author <a href="https://github.com/binarywang">binarywang(Binary Wang)</a> | |||
| * </pre> | |||
| * | |||
| * @author <a href="https://github.com/binarywang">binarywang(Binary Wang)</a> | |||
| */ | |||
| public class SignUtils { | |||
| private static final Logger log = LoggerFactory.getLogger(SignUtils.class); | |||
| /** | |||
| * 请参考并使用 {@link #createSign(Object, String, String, boolean)} | |||
| * 请参考并使用 {@link #createSign(Object, String, String, boolean)}. | |||
| */ | |||
| @Deprecated | |||
| public static String createSign(Object xmlBean, String signKey) { | |||
| @@ -36,7 +37,7 @@ public class SignUtils { | |||
| } | |||
| /** | |||
| * 请参考并使用 {@link #createSign(Map, String, String, boolean)} | |||
| * 请参考并使用 {@link #createSign(Map, String, String, boolean)}. | |||
| */ | |||
| @Deprecated | |||
| public static String createSign(Map<String, String> params, String signKey) { | |||
| @@ -44,7 +45,7 @@ public class SignUtils { | |||
| } | |||
| /** | |||
| * 微信支付签名算法(详见:https://pay.weixin.qq.com/wiki/doc/api/tools/cash_coupon.php?chapter=4_3) | |||
| * 微信支付签名算法(详见:https://pay.weixin.qq.com/wiki/doc/api/tools/cash_coupon.php?chapter=4_3). | |||
| * | |||
| * @param xmlBean Bean里的属性如果存在XML注解,则使用其作为key,否则使用变量名 | |||
| * @param signType 签名类型,如果为空,则默认为MD5 | |||
| @@ -57,27 +58,25 @@ public class SignUtils { | |||
| } | |||
| /** | |||
| * 微信支付签名算法(详见:https://pay.weixin.qq.com/wiki/doc/api/tools/cash_coupon.php?chapter=4_3) | |||
| * 微信支付签名算法(详见:https://pay.weixin.qq.com/wiki/doc/api/tools/cash_coupon.php?chapter=4_3). | |||
| * | |||
| * @param params 参数信息 | |||
| * @param signType 签名类型,如果为空,则默认为MD5 | |||
| * @param signKey 签名Key | |||
| * @param isIgnoreSignType 签名时,是否忽略signType | |||
| * @param params 参数信息 | |||
| * @param signType 签名类型,如果为空,则默认为MD5 | |||
| * @param signKey 签名Key | |||
| * @param ignoreSignType 签名时,是否忽略signType | |||
| * @return 签名字符串 | |||
| */ | |||
| public static String createSign(Map<String, String> params, String signType, String signKey, boolean isIgnoreSignType) { | |||
| public static String createSign(Map<String, String> params, String signType, String signKey, boolean ignoreSignType) { | |||
| SortedMap<String, String> sortedMap = new TreeMap<>(params); | |||
| StringBuilder toSign = new StringBuilder(); | |||
| for (String key : sortedMap.keySet()) { | |||
| String value = params.get(key); | |||
| boolean shouldSign = false; | |||
| if (isIgnoreSignType && "sign_type".equals(key)) { | |||
| if (ignoreSignType && "sign_type".equals(key)) { | |||
| shouldSign = false; | |||
| } else if (StringUtils.isNotEmpty(value) | |||
| && !Lists.newArrayList( | |||
| "sign", "key", "xmlString", "xmlDoc", "couponList").contains(key) | |||
| ) { | |||
| && !Lists.newArrayList("sign", "key", "xmlString", "xmlDoc", "couponList").contains(key)) { | |||
| shouldSign = true; | |||
| } | |||
| @@ -88,18 +87,18 @@ public class SignUtils { | |||
| toSign.append("key=").append(signKey); | |||
| if (SignType.HMAC_SHA256.equals(signType)) { | |||
| return createHMACSha256Sign(toSign.toString(), signKey); | |||
| return createHmacSha256Sign(toSign.toString(), signKey); | |||
| } else { | |||
| return DigestUtils.md5Hex(toSign.toString()).toUpperCase(); | |||
| } | |||
| } | |||
| private static String createHMACSha256Sign(String message, String key) { | |||
| private static String createHmacSha256Sign(String message, String key) { | |||
| try { | |||
| Mac hmacSHA256 = Mac.getInstance("HmacSHA256"); | |||
| Mac sha256 = Mac.getInstance("HmacSHA256"); | |||
| SecretKeySpec secretKeySpec = new SecretKeySpec(key.getBytes(), "HmacSHA256"); | |||
| hmacSHA256.init(secretKeySpec); | |||
| byte[] bytes = hmacSHA256.doFinal(message.getBytes()); | |||
| sha256.init(secretKeySpec); | |||
| byte[] bytes = sha256.doFinal(message.getBytes()); | |||
| return Hex.encodeHexString(bytes).toUpperCase(); | |||
| } catch (NoSuchAlgorithmException | InvalidKeyException e) { | |||
| log.error(e.getMessage(), e); | |||
| @@ -109,7 +108,7 @@ public class SignUtils { | |||
| } | |||
| /** | |||
| * 校验签名是否正确 | |||
| * 校验签名是否正确. | |||
| * | |||
| * @param xmlBean Bean需要标记有XML注解 | |||
| * @param signType 签名类型,如果为空,则默认为MD5 | |||
| @@ -121,7 +120,7 @@ public class SignUtils { | |||
| } | |||
| /** | |||
| * 校验签名是否正确 | |||
| * 校验签名是否正确. | |||
| * | |||
| * @param params 需要校验的参数Map | |||
| * @param signType 签名类型,如果为空,则默认为MD5 | |||
| @@ -2,6 +2,7 @@ package com.github.binarywang.wxpay.service.impl; | |||
| import com.github.binarywang.utils.qrcode.QrcodeUtils; | |||
| import com.github.binarywang.wxpay.bean.coupon.*; | |||
| import com.github.binarywang.wxpay.bean.notify.WxPayOrderNotifyResult; | |||
| import com.github.binarywang.wxpay.bean.order.WxPayAppOrderResult; | |||
| import com.github.binarywang.wxpay.bean.order.WxPayMpOrderResult; | |||
| import com.github.binarywang.wxpay.bean.order.WxPayNativeOrderResult; | |||
| @@ -398,9 +399,43 @@ public class WxPayServiceAbstractImplTest { | |||
| this.logger.info(result); | |||
| } | |||
| /** | |||
| * @see {@link com.github.binarywang.wxpay.bean.notify.WxPayOrderNotifyResultTest} | |||
| * @throws Exception | |||
| */ | |||
| @Test | |||
| public void testParseOrderNotifyResult() throws Exception { | |||
| // 请参考com.github.binarywang.wxpay.bean.notify.WxPayOrderNotifyResultTest里的单元测试 | |||
| // 请参考com.github.binarywang.wxpay.bean.notify.WxPayOrderNotifyResultTest 里的单元测试 | |||
| String xmlString = "<xml>\n" + | |||
| " <appid><![CDATA[wx2421b1c4370ec43b]]></appid>\n" + | |||
| " <attach><![CDATA[支付测试]]></attach>\n" + | |||
| " <bank_type><![CDATA[CFT]]></bank_type>\n" + | |||
| " <fee_type><![CDATA[CNY]]></fee_type>\n" + | |||
| " <is_subscribe><![CDATA[Y]]></is_subscribe>\n" + | |||
| " <mch_id><![CDATA[10000100]]></mch_id>\n" + | |||
| " <nonce_str><![CDATA[5d2b6c2a8db53831f7eda20af46e531c]]></nonce_str>\n" + | |||
| " <openid><![CDATA[oUpF8uMEb4qRXf22hE3X68TekukE]]></openid>\n" + | |||
| " <out_trade_no><![CDATA[1409811653]]></out_trade_no>\n" + | |||
| " <result_code><![CDATA[SUCCESS]]></result_code>\n" + | |||
| " <return_code><![CDATA[SUCCESS]]></return_code>\n" + | |||
| " <sign><![CDATA[B552ED6B279343CB493C5DD0D78AB241]]></sign>\n" + | |||
| " <sub_mch_id><![CDATA[10000100]]></sub_mch_id>\n" + | |||
| " <time_end><![CDATA[20140903131540]]></time_end>\n" + | |||
| " <total_fee>1</total_fee>\n" + | |||
| " <trade_type><![CDATA[JSAPI]]></trade_type>\n" + | |||
| " <transaction_id><![CDATA[1004400740201409030005092168]]></transaction_id>\n" + | |||
| " <coupon_count>2</coupon_count>\n" + | |||
| " <coupon_type_0><![CDATA[CASH]]></coupon_type_0>\n" + | |||
| " <coupon_id_0>10000</coupon_id_0>\n" + | |||
| " <coupon_fee_0>100</coupon_fee_0>\n" + | |||
| " <coupon_type_1><![CDATA[NO_CASH]]></coupon_type_1>\n" + | |||
| " <coupon_id_1>10001</coupon_id_1>\n" + | |||
| " <coupon_fee_1>200</coupon_fee_1>\n" + | |||
| "</xml>"; | |||
| WxPayOrderNotifyResult result = this.payService.parseOrderNotifyResult(xmlString); | |||
| System.out.println(result); | |||
| } | |||
| @Test | |||