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
在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 |
mpAppContext.getAccessTokenFetcher().updateAccessToken()
,一般每1.5小時執行一次,因為access_token的過期時間為2小時。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)
mpAppContext.getMaterialFinder().findNews(String mediaId)
mpAppContext.getMaterialFinder().findPermanentVideo(String mediaId)
mpAppContext.getMaterialFinder().findTemporaryVideo(String mediaId)
mpAppContext.getMaterialFinder().downloadPermanentMedia(String mediaId, String saveDir, String fileName)
mpAppContext.getMaterialFinder().downloadTemporaryMedia(String mediaId, String saveDir, String fileName)
刪除素材操作透過工具類別space.chensheng.wechatty.mp.material.MaterialDeleter
完成。
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
實作。
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授權透過JsapiHelper
實作。
jsapi ticket
(可使用定時任務來定時取得ticket並存於資料庫): mpAppContext.getJsapiHelper().fetchTicket()
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)
mpAppContext.getPayHelper().generateJsapiPayParams(String prepayId, PaySignType signType)
mpAppContext.getPayHelper().refund(RefundRequest request)
mpAppContext.getPayHelper().parseRefundNotify(String notifyContent)