Wechattty Project是一個基於JAVA的微信公衆號(包括服務號和訂閱號)和微信企業號的開發框架,封裝良好的API讓開發者可以專注於業務邏輯的開發,提高開發效率。
簡單使用教程
引入依賴
這裏使用maven來引入依賴。
<dependency>
<groupId>space.chensheng.wechatty</groupId>
<artifactId>wechatty-mp</artifactId>
<version>2.0.0</version>
</dependency>
初始化
MpAppContext
是公衆號API的統一調用入口,使用WechatMpBootstrap
對其進行初始化。
WechatMpBootstrap bootstrap = new WechatMpBootstrap();
bootstrap.addMsgListener(new TextMessageListener());
MpAppContext mpAppContext = bootstrap.build();
如果項目使用spring來管理,可實現一個FactoryBean
來初始化MpAppContext
,以便後續引用。
@Component
public class MpAppContextFactoryBean implements FactoryBean<MpAppContext> {
@Override
public MpAppContext getObject() throws Exception {
WechatMpBootstrap bootstrap = new WechatMpBootstrap();
bootstrap.addMsgListener(new TextMessageListener());
return bootstrap.build();
}
@Override
public Class<?> getObjectType() {
return MpAppContext.class;
}
@Override
public boolean isSingleton() {
return true;
}
}
配置
配置方式有兩種,一種是配置文件
,另一種是 JAVA代碼配置
。其中JAVA代碼配置
的優先級高於配置文件
。
配置文件
新建配置文件wechat-mp.properties, 將該文件放在項目類路徑下。比如maven項目,可將該文件放在src/main/resources
目錄下。一般的配置如下:
token=thisIsToken
aesKey=thisIsAesKey
appId=thisIsYourAppId
appSecret=thisIsAppSecret
JAVA代碼配置
在MpAppConetxt
初始化時,調用WechatMpBootstrap
的customizeWechatContext
方法來進行配置。
WechatMpBootstrap bootstrap = new WechatMpBootstrap();
bootstrap.customizeWechatContext(new MpWechatContextCustomizer() {
@Override
public void customize(MpWechatContext wechatContext) {
wechatContext.setToken("thisIsToken");
wechatContext.setAesKey("thisIsAeskey");
wechatContext.setAppId("thisIsAppId");
wechatContext.setAppSecret("thisIsAppSecret");
}
});
配置參數說明
必填參數 | 說明 |
---|---|
token | 公衆號的token,可在公衆號後臺查看. |
aesKey | 加密用的key, 可在公衆號後臺查看. |
appId | 公衆號appId,可在公衆號後臺查看。 |
appSecret | 公衆號的appSecret,可在公衆號後臺查看。 |
可選參數 | 說明 |
---|---|
enableCryptedMode | 是否開啓回調加密模式,默認true。如果開啓則要下載JCE無限制權限策略文件,覆蓋jdk中的相關文件,具體可查看微信常見錯誤舉例。 |
autoUpdateAccessToken | 出現access_token相關錯誤時是否自動更新access_token,默認false,應用可自己通過定時任務來更新,後面將詳細介紹。 |
accessTokenStrategyClass | access_token存取策略,默認是space.chensheng.wechatty.common.http.MemoryAccessTokenStrategy,將access_token存在內存中,應用可實現自己的存取策略,比如存在數據庫中,後面將詳細介紹。 |
payKey | 微信支付key |
payCertFile | 微信支付證書文件路徑 |
payCertPassword | 微信支付證書密碼 |
payMchId | 微信支付商戶id |
payClientIp | 調用支付的機器ip |
poolingHttpProxyEnable | 是否通過代理服務器給微信服務器必請求,默認false |
poolingHttpProxyHostname | 代理服務器的hostname,比如www.chensheng.space |
poolingHttpProxyPort | 代理服務器端口 |
poolingHttpProxyUsername | 代理服務器用戶名 |
poolingHttpProxyPassword | 代理服務器密碼 |
poolingHttpMaxPerRoute | http連接池每條鏈路最大併發連接數,默認爲50 |
poolingHttpMaxTotal | http連接池最大併發連接數,默認200 |
poolingHttpSocketTimeoutMillis | socket超時毫秒數,默認10000 |
poolingHttpConnectTimeoutMillis | 連接到微信服務器超時毫秒數,默認10000 |
poolingHttpConnectionRequestTimeoutMillis | 從htttp連接池獲取連接超時毫秒數,默認10000 |
poolingHttpTcpNoDelay | 是否開啓tpcNoDelay,默認true |
access_token更新問題
- 自動更新:如果開啓了自動更新,則在因爲access_token錯誤而導致請求微信接口失敗的情況下,框架會自動更新access_token。
- 定時更新:在應用中使用定時任務(比如quartz)來定時執行
mpAppContext.getAccessTokenFetcher().updateAccessToken()
,一般每1.5小時執行一次,因爲access_token的過期時間爲2小時。 - 自動更新和定時更新可共存,如果多個線程併發執行更新access_token,只有一個線程會去請求微信服務器來更新access_token,其他線程會立即返回,不執行任何操作。
access_token存取策略問題
- Web應用單機部署:如果您的應用是單機部署,則可直接使用默認的策略,將access_token存儲在內存中。
- Web應用集羣部署:如果您的應用是集羣部署,則要實現自己的access_token存取策略,將access_token存放在集羣共享的媒介(比如數據庫)來達到access_token中控管理的目的。實現完自己的策略類後,要在wechat-mp.properties中添加配置
accessTokenStrategyClass=your.package.name.YourAccessTokenStrategy
。以下是一個accesss_token數據庫存取的策略:
import space.chensheng.wechatty.common.http.AccessTokenStrategy;
//因爲這個策略類的實例化不是通過Spring來管理的,所以在這個類中不能使用Autowired來注入bean,
//要通過ApplicationContext#getBean方法來獲取。
public class DatabaseAccessTokenStrategy implements AccessTokenStrategy{
//將access_token存到數據庫中去
@Override
public void doSave(String accessToken) {
TokenService tokenService = ApplicationContextUtil
.getApplicationContext().getBean(TokenService.class);
tokenService.doSave(accessToken);
}
//從數據庫中取出access_token
@Override
public String doQuery() {
TokenService tokenService = ApplicationContextUtil
.getApplicationContext().getBean(TokenService.class);
return tokenService.doQuery();
}
}
接收消息
在MpAppContext
初始化時,通過WechatMpBootstrap
添加消息監聽器來接收消息(關於message listener會在後面介紹):
WechatMpBootstrap bootstrap = new WechatMpBootstrap();
bootstrap.addMsgListener(new TextMessageListener());
bootstrap.addMsgListener(new SubscribeEventListener());
bootstrap.addMsgListener(new UnsubscribeEventListener());
驗證微信服務器的開啓回調請求
如果你已經在微信公衆號後臺設置了回調URL,微信服務器會向這個URL發送一個GET請求來驗證,開發者需要在Web應用中處理這個請求。以下是一個SpringMVC的驗證例子:
@RestController
@RequestMapping(value = "/wechat-mp")
public class CallbackController extends BaseController{
@Autowired
private MpAppContext mpAppContext;
//驗證請求,並回復字符串
@RequestMapping(value = "/callback", method = RequestMethod.GET)
public String verify(String msg_signature, String timestamp, String nonce, String echostr) {
String reply = mpAppContext.getCallbackModeVerifier().verify(msg_signature, timestamp, nonce, echostr);
return reply;
}
}
消息回調請求處理
驗證完開啓回調請求後,回調模式就真正開啓了。如果用戶發了個消息給公衆號,微信服務器會向回調URL發送一個POST請求,將消息轉發到這個URL上,開發者需要在Web應用中處理這個請求,以下是一個SpringMVC的例子(和前面驗證開啓回調的例子在一個controller中):
@RestController
@RequestMapping(value = "/wechat-mp")
public class CallbackController extends BaseController{
@Autowired
private MpAppContext mpAppContext;
//接收回調消息,並回復相應xml消息
@RequestMapping(value = "/callback", method = RequestMethod.POST)
public String verify(String msg_signature, String timestamp, String nonce) {
//postBody是請求體內容,String格式,開發者可以通過HttpServletRequest來解析
String replyXml = mpAppContext.getMpMessageDispatcher().dispatch(msg_signature(), timestamp, nonce, postBody);
return replyXml;
}
}
回調消息的監聽
開發者可以通過繼承space.chensheng.wechatty.common.message.MessageListener
來監聽特定類型的消息。以下是一個監聽用戶發送的文本消息的例子:
public class TextMessageListener extends MessageListener<TextInboundMessage> {
@Override
protected ReplyMessage onMessage(TextInboundMessage message) {
String content = message.getContent();
//根據消息內容來回複用戶
if ("1".equals(content)) {
TextReplyMessage replyMsg = new TextReplyMessage();
replyMsg.setContent("this is reply message content");
replyMsg.setFromUserName(message.getToUserName());
replyMsg.setToUserName(message.getFromUserName());
replyMsg.setCreateTime(System.currentTimeMillis());
return replyMsg;
}
//返回null表示不回覆用戶
return null;
}
}
可監聽的消息類型
消息 | 說明 |
---|---|
TextInboundMessage | 文本消息 |
ImageInboundMessage | 圖片消息 |
LinkInboundMessage | 跳轉圖文消息 |
LocationInboundMessage | 共享位置消息 |
ShortVideoInboundMessage | 小視頻消息 |
VideoInboundMessage | 視頻消息 |
VoiceInboundMessage | 語音消息 |
ClickEventMessage | 點擊普通菜單消息 |
ViewEventMessage | 點擊跳轉鏈接菜單消息 |
LocationEventMessage | 位置事件消息 |
SubscribeEventMessage | 用戶關注公衆號消息 |
UnsubscribeEventMessage | 用記取消關注公衆號消息 |
ScanEventMessage | 用戶掃描二維碼消息 |
MassSendJobFinishEventMessage | 羣發消息發送完成報告 |
可回覆的消息類型
消息 | 說明 |
---|---|
TextReplyMessage | 文本回復 |
ImageReplyMessage | 圖片回覆 |
MusicReplyMessage | 音樂回覆 |
NewsReplyMessage | 圖文回覆 |
VideoReplyMessage | 視頻回覆 |
VoiceReplyMessage | 語音回覆 |
發送消息
公衆號可以主動發送消息給用戶,包括羣發消息和客服消息兩大類型消息。所有消息統一使用space.chensheng.wechatty.mp.message.MpMessageSender
來發送。
羣發消息
TextMassMessage message = new TextMassMessage();
message.setIsToAll(true);
message.setContent("羣發消息測試");
mpAppContext.getMpMessageSender().send(message, 3);
羣發消息類型 | 說明 |
---|---|
TextMassMessage | 文本羣發 |
ImageMassMessage | 圖片羣發 |
MpnewsMassMessage | 微信內圖文羣發 |
MpvideoMassMessage | 視頻羣發 |
VoiceMassMessage | 語音羣發 |
WxcardMassMessage | 微信卡券羣發 |
客服消息
TextCsMessage message = new TextCsMessage();
message.setToUser("thisIsUserOpenId");
message.setContent("客服消息測試 \n 212");
mpAppContext.getMpMessageSender().send(message, 3);
客服消息類型 | 說明 |
---|---|
TextCsMessage | 文本客服 |
ImageCsMessage | 圖片客服 |
MpnewsCsMessage | 微信內圖文客服 |
NewsCsMessage | 外部圖文客服 |
VideoCsMessage | 視頻客服 |
VoiceCsMessage | 語音客服 |
WxcardCsMessage | 微信卡券客服 |
素材管理
素材管理主要是進行素材的上傳、查詢、修改、刪除,素材類型包括圖片、視頻、語音、圖文。
上傳素材
上傳素材通過操作對應的素材上傳類來完成,下面是一個上傳圖片的例子:
File image = new File("/this/is/image/path.jpg");
ImagePermanentMedia material = new ImagePermanentMedia(mpAppContext, image);
UploadResponse resp = material.upload();
素材上傳類 | 說明 |
---|---|
ImagePermanentMedia | 永久圖片 |
ThumbPermanentMedia | 永久縮略圖 |
VideoPermanentMedia | 永久視頻 |
VoicePermanentMedia | 永久語音 |
PermanentNews | 永久圖文 |
PermanentNewsImg | 永久圖文中的圖片 |
ImageTemporaryMedia | 臨時圖片 |
ThumbTemporaryMedia | 臨時縮略圖 |
VideoTemporaryMedia | 臨時視頻 |
VoiceTemporaryMedia | 臨時語音 |
查詢素材
查詢素材操作通過工具類space.chensheng.wechatty.mp.material.MaterialQuery
和space.chensheng.wechatty.mp.material.MaterialFinder
完成。
- 查詢素材的數量信息:
mpAppContext.getMaterialQuery().count()
- 查詢圖文素材:
mpAppContext.getMaterialQuery().listNews(int offset, int count)
- 查詢其他素材:
mpAppContext.getMaterialQuery().listMedia(MediaType mediaType, int offset, int count)
- 根據mediaId查找圖文:
mpAppContext.getMaterialFinder().findNews(String mediaId)
- 根據mediaId查找永久視頻:
mpAppContext.getMaterialFinder().findPermanentVideo(String mediaId)
- 根據mediaId查找臨時視頻:
mpAppContext.getMaterialFinder().findTemporaryVideo(String mediaId)
- 根據mediaId下載永久素材:
mpAppContext.getMaterialFinder().downloadPermanentMedia(String mediaId, String saveDir, String fileName)
- 根據mediaId下載臨時素材:
mpAppContext.getMaterialFinder().downloadTemporaryMedia(String mediaId, String saveDir, String fileName)
刪除素材
刪除素材操作通過工具類space.chensheng.wechatty.mp.material.MaterialDeleter
完成。
- 根據mediaId刪除素材:
mpAppContext.getMaterialDeleter().delete(String mediaId)
帳號管理
生成帶參數二維碼
生成帶參數二維碼通過工具類space.chensheng.wechatty.mp.account.QRCodeCreator
完成。
- 生成帶參數臨時二維碼:
mpAppContext.getQRCodeCreator().createTemporary(int expireSeconds, int sceneId)
- 生成帶整型參數永久二維碼:
mpAppContext.getQRCodeCreator().createPermanent(int sceneId)
- 生成帶字符串參數永久二維碼:
mpAppContext.getQRCodeCreator().createPermanent(String sceneStr)
查詢用戶信息
查詢用戶信息通過UserInfoQuery
實現。
- 查詢單個用戶信息:
mpAppConext.getUserInfoQuery().get(String openId)
- 批量查詢用戶信息:
mpAppContext.getUserInfoQuery().batchGet(List<String> openIds)
微信授權
用戶授權
用戶授權通過AuthHelper
實現。
- 通過授權鏈接的code獲取
auth access token
:mpAppContext.getAuthHelper().fetchAuthAccessToken(String code)
- 刷新
auth access token
:mpAppContext.getAuthHelper().refreshAuthAccessToken(String refreshAccessToken)
- 通過
auth access token
獲取用戶信息:mpAppContext.getAuthHelper().fetchAuthUserInfo(String authAccessToken, String openId)
以下是一段用戶授權的僞代碼:
public WxAuthLoginDto authAndLogin(String code) {
AuthAccessTokenResponse authResp = mpAppContext.getAuthHelper().fetchAuthAccessToken(code);
if (authResp == null || !authResp.isOk()) {
//授權失敗,執行相應業務邏輯
return new WxAuthLoginDto("fail");
}
String openId = authResp.getOpenId();
AuthUserInfoResponse wxUserInfo = mpAppContext.getAuthHelper().fetchAuthUserInfo(authResp.getAccessToken(), authResp.getOpenId())
//根據微信用戶信息在數據庫裏查找系統對應的用戶,或新建一個用戶
//進行登錄相關業務邏輯處理
return new WxAuthLoginDto("success");
}
jsapi授權
jsapi授權通過JsapiHelper
實現。
- 獲取
jsapi ticket
(可使用定時任務來定時獲取ticket並存於數據庫中):mpAppContext.getJsapiHelper().fetchTicket()
- 生成jsapi簽名信息:
mpAppContext.getJsapiHelper().generateSignature(String jsapiTicket, String nonceStr, long timestamp, String url)
微信支付
初始化MpAppContext
時,調用WechatMpBootstrap
的enablePayCert()
方法來啓用微信支付,並配置相關參數。(具體參數查看配置模塊)
WechatMpBootstrap bootstrap = new WechatMpBootstrap();
bootstrap.enablePayCert();
- 發送普通紅包:
mpAppContext.getPayHelper().sendRedPack(RedPackRequest request)
- 發送羣紅包:
mpAppContext.getPayHelper().sendGroupRedPack(GroupRedPackRequest request)
- 轉賬:
mpAppContext.getPayHelper().transfers(TransfersRequest request)
- 生成預付款訂單:
mpAppContext.getPayHelper().unifiedOrder(UnifiedOrderRequest request)
- 解析支付回調:
mpAppContext.getPayHelper().parsePayNotify(String notifyContent)
- 校驗支付回調:
mpAppContext.getPayHelper().validatePayNotify(PayNotifyResponse response)
- 支付訂單查詢:
mpAppContext.getPayHelper().orderQuery(OrderQueryRequest request)
- 關閉支付訂單:
mpAppContext.getPayHelper().closeOrder(CloseOrderRequest request)
- 生成短鏈接:
mpAppContext.getPayHelper().shortUrl(String longUrl)
- 生成js支付參數:
mpAppContext.getPayHelper().generateJsapiPayParams(String prepayId, PaySignType signType)
- 發起退款:
mpAppContext.getPayHelper().refund(RefundRequest request)
- 解析退款回調:
mpAppContext.getPayHelper().parseRefundNotify(String notifyContent)