主要新增了企业号的第三方应用API

This commit is contained in:
jinyu 2015-06-21 22:20:19 +08:00
parent 692a2b8129
commit dd1fcaa598
44 changed files with 850 additions and 250 deletions

View File

@ -331,4 +331,10 @@
+ 修缮token实现机制
+ **weixin4j-qy**: 新增企业号[登陆授权](weixin4j-qy/src/main/java/com/foxinmy/weixin4j/qy/api/OauthApi.java)API
+ **weixin4j-qy**: 新增企业号[登陆授权](weixin4j-qy/src/main/java/com/foxinmy/weixin4j/qy/api/OauthApi.java)API
* 2015-06-21
+ 新增了默认常量对象在BaseApi.java类
+ **weixin4j-qy**:新增企业号[第三方应用API](weixin4j-qy/src/main/java/com/foxinmy/weixin4j/qy/suite)。

View File

@ -72,8 +72,6 @@ weixin4j
------
* 公众号第三方服务应用
* 企业号第三方应用
* 硬件设备 & 摇一摇周边
* 微信小店

View File

@ -1,10 +1,11 @@
package com.foxinmy.weixin4j.api;
import java.util.ResourceBundle;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import com.foxinmy.weixin4j.http.weixin.WeixinHttpClient;
import com.foxinmy.weixin4j.token.FileTokenStorager;
import com.foxinmy.weixin4j.token.TokenStorager;
/**
* API基础
@ -17,9 +18,10 @@ import com.foxinmy.weixin4j.http.weixin.WeixinHttpClient;
* @see <a href="http://qydev.weixin.qq.com/wiki/index.php">微信企业号API文档</a>
*/
public abstract class BaseApi {
protected final WeixinHttpClient weixinClient = new WeixinHttpClient();
protected abstract ResourceBundle getWeixinBundle();
protected abstract String getConfigValue(String key);
protected String getRequestUri(String key) {
String url = getConfigValue(key);
@ -36,7 +38,8 @@ public abstract class BaseApi {
return sb.toString();
}
protected String getConfigValue(String key) {
return getWeixinBundle().getString(key);
}
/**
* 默认token使用File的方式存储
*/
public final static TokenStorager DEFAULT_TOKEN_STORAGER = new FileTokenStorager();
}

View File

@ -486,10 +486,14 @@
<code>43011</code>
<text>需要企业授权</text>
</error>
<error>
<code>43013</code>
<text>应用对成员不可见</text>
</error>
<error>
<code>44001</code>
<desc>empty media data</desc>
<text>空白的二进制数据</text>
<text>多媒体文件为</text>
</error>
<error>
<code>44002</code>
@ -859,7 +863,7 @@
</error>
<error>
<code>60125</code>
<text>部门名字长度超过限制</text>
<text>非法部门名字长度超过限制、重名等</text>
</error>
<error>
<code>60126</code>

View File

@ -27,41 +27,4 @@ public final class Consts {
public static final String SHA1 = "SHA-1";
public static final String PROTOCOL_FILE = "file";
public static final String PROTOCOL_JAR = "jar";
/**
* 公众平台获取token的url
*/
public static final String MP_ASSESS_TOKEN_URL = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=%s&secret=%s";
/**
* 企业号获取token的url
*/
public static final String QY_ASSESS_TOKEN_URL = "https://qyapi.weixin.qq.com/cgi-bin/gettoken?corpid=%s&corpsecret=%s";
/**
* 企业号提供商获取token的url
*/
public static final String QY_PROVIDER_TOKEN_URL = "https://qyapi.weixin.qq.com/cgi-bin/service/get_provider_token";
/**
* 公众平台jssdk获取token的url
*/
public static final String MP_JS_TICKET_URL = "https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token=%s&type=jsapi";
/**
* 企业号jssdk获取token的url
*/
public static final String QY_JS_TICKET_URL = "https://qyapi.weixin.qq.com/cgi-bin/get_jsapi_ticket?access_token=%s";
/**
* 商户平台下统一订单生成的url
*/
public static final String UNIFIEDORDER = "https://api.mch.weixin.qq.com/pay/unifiedorder";
/**
* 商户平台下刷卡支付的url
*/
public static final String MICROPAYURL = "https://api.mch.weixin.qq.com/pay/micropay";
/**
* V2支付下natvie支付的url
*/
public static final String NATIVEURLV2 = "weixin://wxpay/bizpayurl?sign=%s&appid=%s&productid=%s&timestamp=%s&noncestr=%s";
/**
* 商户平台(V3)下native支付的url
*/
public static final String NATIVEURLV3 = "weixin://wxpay/bizpayurl?sign=%s&appid=%s&mch_id=%s&product_id=%s&time_stamp=%s&nonce_str=%s";
}

View File

@ -32,7 +32,7 @@ public class Token implements Serializable {
@JSONField(name = "expires_in")
private int expiresIn;
/**
* token创建的时间 只在FileTokenHolder模式下有效
* token创建的时间 只在FileTokenStorager模式下有效
*/
private long time;

View File

@ -51,7 +51,7 @@ public class FileTokenStorager implements TokenStorager {
}
@Override
public void cachingToken(Token token, String cacheKey)
public void cachingToken(String cacheKey, Token token)
throws WeixinException {
try {
XmlStream.toXML(

View File

@ -15,21 +15,50 @@ import com.foxinmy.weixin4j.model.Token;
*/
public final class TokenHolder {
/**
* token的创建
*/
private final TokenCreator tokenCreator;
/**
* token的存储
*/
private final TokenStorager tokenStorager;
/**
*
* @param tokenCreator
* token创建器
* @param tokenStorager
* token保存器
*/
public TokenHolder(TokenCreator tokenCreator, TokenStorager tokenStorager) {
this.tokenCreator = tokenCreator;
this.tokenStorager = tokenStorager;
}
/**
* 获取token对象
*
* @return
* @throws WeixinException
*/
public Token getToken() throws WeixinException {
String cacheKey = tokenCreator.getCacheKey();
Token token = tokenStorager.lookupToken(cacheKey);
if (token == null) {
token = tokenCreator.createToken();
tokenStorager.cachingToken(token, cacheKey);
tokenStorager.cachingToken(cacheKey, token);
}
return token;
}
/**
* 获取token字符串
*
* @return
* @throws WeixinException
*/
public String getAccessToken() throws WeixinException {
return getToken().getAccessToken();
}
}

View File

@ -28,12 +28,13 @@ public interface TokenStorager {
/**
* 缓存新的token
*
* @param token
* 新产生的token
* @param cacheKey
* 缓存的名称
*
* @param token
* 新产生的token
* @throws WeixinException
*/
public void cachingToken(Token token, String cacheKey)
public void cachingToken(String cacheKey, Token token)
throws WeixinException;
}

View File

@ -3,12 +3,12 @@ package com.foxinmy.weixin4j.mp;
import java.io.File;
import java.util.Date;
import com.alibaba.fastjson.JSON;
import com.foxinmy.weixin4j.exception.WeixinException;
import com.foxinmy.weixin4j.http.weixin.JsonResult;
import com.foxinmy.weixin4j.http.weixin.XmlResult;
import com.foxinmy.weixin4j.mp.api.CashApi;
import com.foxinmy.weixin4j.mp.api.CouponApi;
import com.foxinmy.weixin4j.mp.api.MpApi;
import com.foxinmy.weixin4j.mp.api.Pay2Api;
import com.foxinmy.weixin4j.mp.api.Pay3Api;
import com.foxinmy.weixin4j.mp.api.PayApi;
@ -28,7 +28,6 @@ import com.foxinmy.weixin4j.mp.type.CurrencyType;
import com.foxinmy.weixin4j.mp.type.IdQuery;
import com.foxinmy.weixin4j.mp.type.IdType;
import com.foxinmy.weixin4j.mp.type.RefundType;
import com.foxinmy.weixin4j.token.FileTokenStorager;
import com.foxinmy.weixin4j.token.TokenHolder;
import com.foxinmy.weixin4j.token.TokenStorager;
import com.foxinmy.weixin4j.util.ConfigUtil;
@ -52,32 +51,35 @@ public class WeixinPayProxy {
private final CouponApi couponApi;
private final CashApi cashApi;
private final TokenHolder tokenHolder;
/**
* 默认使用文件保存token使用weixin4j.properties配置的账号信息
*/
public WeixinPayProxy() {
this(new FileTokenStorager());
this(MpApi.DEFAULT_TOKEN_STORAGER);
}
/**
* 使用weixin4j.properties配置的账号信息
*/
public WeixinPayProxy(TokenStorager tokenStorager) {
this(tokenStorager, JSON.parseObject(ConfigUtil.getValue("account"),
WeixinMpAccount.class));
this(MpApi.DEFAULT_WEIXIN_ACCOUNT, tokenStorager);
}
/**
*
* @param weixinAccount
* 公众号账号信息
*
* @param tokenStorager
* token的存储策略
* @param weixinAccount
* 公众号账号信息
*/
public WeixinPayProxy(TokenStorager tokenStorager,
WeixinMpAccount weixinAccount) {
TokenHolder tokenHolder = new TokenHolder(new WeixinTokenCreator(
weixinAccount), tokenStorager);
public WeixinPayProxy(WeixinMpAccount weixinAccount,
TokenStorager tokenStorager) {
this.tokenHolder = new TokenHolder(new WeixinTokenCreator(
weixinAccount.getId(), weixinAccount.getSecret()),
tokenStorager);
this.pay2Api = new Pay2Api(weixinAccount, tokenHolder);
this.pay3Api = new Pay3Api(weixinAccount, tokenHolder);
int version = weixinAccount.getVersion();
@ -92,6 +94,10 @@ public class WeixinPayProxy {
this.cashApi = new CashApi(weixinAccount);
}
public TokenHolder getTokenHolder() {
return this.tokenHolder;
}
/**
* 发货通知
*

View File

@ -8,7 +8,6 @@ import java.util.List;
import com.foxinmy.weixin4j.exception.WeixinException;
import com.foxinmy.weixin4j.http.weixin.JsonResult;
import com.foxinmy.weixin4j.model.Button;
import com.foxinmy.weixin4j.model.WeixinAccount;
import com.foxinmy.weixin4j.mp.api.CustomApi;
import com.foxinmy.weixin4j.mp.api.DataApi;
import com.foxinmy.weixin4j.mp.api.GroupApi;
@ -16,6 +15,7 @@ import com.foxinmy.weixin4j.mp.api.HelperApi;
import com.foxinmy.weixin4j.mp.api.MassApi;
import com.foxinmy.weixin4j.mp.api.MediaApi;
import com.foxinmy.weixin4j.mp.api.MenuApi;
import com.foxinmy.weixin4j.mp.api.MpApi;
import com.foxinmy.weixin4j.mp.api.NotifyApi;
import com.foxinmy.weixin4j.mp.api.QrApi;
import com.foxinmy.weixin4j.mp.api.TmplApi;
@ -40,7 +40,6 @@ import com.foxinmy.weixin4j.mp.token.WeixinTokenCreator;
import com.foxinmy.weixin4j.mp.type.DatacubeType;
import com.foxinmy.weixin4j.mp.type.IndustryType;
import com.foxinmy.weixin4j.mp.type.Lang;
import com.foxinmy.weixin4j.token.FileTokenStorager;
import com.foxinmy.weixin4j.token.TokenHolder;
import com.foxinmy.weixin4j.token.TokenStorager;
import com.foxinmy.weixin4j.tuple.MassTuple;
@ -48,7 +47,6 @@ import com.foxinmy.weixin4j.tuple.MpArticle;
import com.foxinmy.weixin4j.tuple.Tuple;
import com.foxinmy.weixin4j.tuple.Video;
import com.foxinmy.weixin4j.type.MediaType;
import com.foxinmy.weixin4j.util.ConfigUtil;
/**
* 微信公众平台接口实现
@ -73,19 +71,23 @@ public class WeixinProxy {
private final HelperApi helperApi;
private final DataApi dataApi;
private final TokenHolder tokenHolder;
/**
* 默认使用文件方式保存token使用weixin4j.properties配置的账号信息
*/
public WeixinProxy() {
this(new FileTokenStorager());
this(MpApi.DEFAULT_TOKEN_STORAGER);
}
/**
* 默认使用weixin4j.properties配置的账号信息
*
* @param tokenStorager
*/
public WeixinProxy(TokenStorager tokenStorager) {
this(tokenStorager, ConfigUtil.getWeixinAccount());
this(MpApi.DEFAULT_WEIXIN_ACCOUNT.getId(), MpApi.DEFAULT_WEIXIN_ACCOUNT
.getSecret(), tokenStorager);
}
/**
@ -94,19 +96,13 @@ public class WeixinProxy {
* @param appsecret
*/
public WeixinProxy(String appid, String appsecret) {
this(new FileTokenStorager(), new WeixinAccount(appid, appsecret));
this(appid, appsecret, MpApi.DEFAULT_TOKEN_STORAGER);
}
/**
*
* @param tokenStorager
* token存储策略
* @param weixinAccount
* 公众号账号信息
*/
public WeixinProxy(TokenStorager tokenStorager, WeixinAccount weixinAccount) {
TokenHolder tokenHolder = new TokenHolder(new WeixinTokenCreator(
weixinAccount), tokenStorager);
public WeixinProxy(String appid, String appsecret,
TokenStorager tokenStorager) {
this.tokenHolder = new TokenHolder(new WeixinTokenCreator(appid,
appsecret), tokenStorager);
this.mediaApi = new MediaApi(tokenHolder);
this.notifyApi = new NotifyApi(tokenHolder);
this.customApi = new CustomApi(tokenHolder);
@ -120,6 +116,10 @@ public class WeixinProxy {
this.dataApi = new DataApi(tokenHolder);
}
public TokenHolder getTokenHolder() {
return this.tokenHolder;
}
/**
* 上传媒体文件
*

View File

@ -2,7 +2,10 @@ package com.foxinmy.weixin4j.mp.api;
import java.util.ResourceBundle;
import com.alibaba.fastjson.JSON;
import com.foxinmy.weixin4j.api.BaseApi;
import com.foxinmy.weixin4j.mp.model.WeixinMpAccount;
import com.foxinmy.weixin4j.util.ConfigUtil;
/**
* 微信公众平台API
@ -15,14 +18,22 @@ import com.foxinmy.weixin4j.api.BaseApi;
* @see <a href="http://mp.weixin.qq.com/wiki/index.php">api文档</a>
*/
public class MpApi extends BaseApi {
private final static ResourceBundle weixinBundle;
private final static ResourceBundle WEIXIN_BUNDLE;
/**
* 默认使用weixin4j.properties文件中的公众号信息
*/
public final static WeixinMpAccount DEFAULT_WEIXIN_ACCOUNT;
static {
weixinBundle = ResourceBundle
WEIXIN_BUNDLE = ResourceBundle
.getBundle("com/foxinmy/weixin4j/mp/api/weixin");
DEFAULT_WEIXIN_ACCOUNT = JSON.parseObject(
ConfigUtil.getValue("account"), WeixinMpAccount.class);
}
@Override
protected ResourceBundle getWeixinBundle() {
return weixinBundle;
protected String getConfigValue(String key) {
return WEIXIN_BUNDLE.getString(key);
}
}

View File

@ -7,7 +7,6 @@ import com.alibaba.fastjson.TypeReference;
import com.foxinmy.weixin4j.exception.WeixinException;
import com.foxinmy.weixin4j.http.weixin.WeixinResponse;
import com.foxinmy.weixin4j.model.Consts;
import com.foxinmy.weixin4j.model.WeixinAccount;
import com.foxinmy.weixin4j.mp.model.OauthToken;
import com.foxinmy.weixin4j.mp.model.User;
import com.foxinmy.weixin4j.util.ConfigUtil;
@ -24,13 +23,14 @@ import com.foxinmy.weixin4j.util.StringUtil;
* href="https://open.weixin.qq.com/cgi-bin/showdocument?action=dir_list&t=resource/res_list&verify=1&lang=zh_CN">微信登陆</a>
*/
public class OauthApi extends MpApi {
/**
* @see {@link com.foxinmy.weixin4j.mp.api.OauthApi#getAuthorizeURL(String, String,String)}
*
* @return 请求授权的URL
*/
public String getAuthorizeURL() {
String appId = ConfigUtil.getWeixinAccount().getId();
String appId = DEFAULT_WEIXIN_ACCOUNT.getId();
String redirectUri = ConfigUtil.getValue("redirect_uri");
return getAuthorizeURL(appId, redirectUri, "state", "snsapi_login");
}
@ -65,8 +65,8 @@ public class OauthApi extends MpApi {
* @return
*/
public OauthToken getOauthToken(String code) throws WeixinException {
WeixinAccount account = ConfigUtil.getWeixinAccount();
return getOauthToken(code, account.getId(), account.getSecret());
return getOauthToken(code, DEFAULT_WEIXIN_ACCOUNT.getId(),
DEFAULT_WEIXIN_ACCOUNT.getSecret());
}
/**
@ -98,8 +98,7 @@ public class OauthApi extends MpApi {
* @return
*/
public OauthToken refreshToken(String refreshToken) throws WeixinException {
WeixinAccount account = ConfigUtil.getWeixinAccount();
return refreshToken(account.getId(), refreshToken);
return refreshToken(DEFAULT_WEIXIN_ACCOUNT.getId(), refreshToken);
}
/**

View File

@ -19,6 +19,7 @@ import com.foxinmy.weixin4j.mp.payment.v3.PayRequestV3;
import com.foxinmy.weixin4j.mp.payment.v3.PrePay;
import com.foxinmy.weixin4j.mp.type.SignType;
import com.foxinmy.weixin4j.mp.type.TradeType;
import com.foxinmy.weixin4j.mp.type.URLConsts;
import com.foxinmy.weixin4j.util.ConfigUtil;
import com.foxinmy.weixin4j.util.DateUtil;
import com.foxinmy.weixin4j.util.DigestUtil;
@ -228,7 +229,7 @@ public class PayUtil {
}
String payJsRequestXml = XmlStream.toXML(payPackage);
try {
WeixinResponse response = httpClient.post(Consts.UNIFIEDORDER,
WeixinResponse response = httpClient.post(URLConsts.UNIFIEDORDER,
payJsRequestXml);
PrePay prePay = response.getAsObject(new TypeReference<PrePay>() {
});
@ -303,7 +304,7 @@ public class PayUtil {
map.put("productid", productId);
map.put("appkey", weixinAccount.getPaySignKey());
String sign = paysignSha(map);
return String.format(Consts.NATIVEURLV2, sign, weixinAccount.getId(),
return String.format(URLConsts.NATIVEURLV2, sign, weixinAccount.getId(),
productId, timestamp, noncestr);
}
@ -328,7 +329,7 @@ public class PayUtil {
map.put("nonce_str", noncestr);
map.put("product_id", productId);
String sign = paysignMd5(map, weixinAccount.getPaySignKey());
return String.format(Consts.NATIVEURLV3, sign, weixinAccount.getId(),
return String.format(URLConsts.NATIVEURLV3, sign, weixinAccount.getId(),
weixinAccount.getMchId(), productId, timestamp, noncestr);
}
@ -408,7 +409,7 @@ public class PayUtil {
String sign = paysignMd5(payPackage, weixinAccount.getPaySignKey());
payPackage.setSign(sign);
String para = XmlStream.toXML(payPackage);
WeixinResponse response = httpClient.post(Consts.MICROPAYURL, para);
WeixinResponse response = httpClient.post(URLConsts.MICROPAYURL, para);
return response
.getAsObject(new TypeReference<com.foxinmy.weixin4j.mp.payment.v3.Order>() {
});

View File

@ -88,7 +88,7 @@ public class RefundRecord extends ApiResult {
*
* @see com.foxinmy.weixin4j.mp.payment.v3.RefundDetail
*/
@ListsuffixResult({ "out_refund_no(_\\d)$", "^refund_.*(_\\d)$" })
@ListsuffixResult({ "^out_refund_no(_\\d)$", "^refund_.*(_\\d)$" })
private List<RefundDetail> refundList;
protected RefundRecord() {

View File

@ -4,11 +4,10 @@ import com.alibaba.fastjson.JSONObject;
import com.foxinmy.weixin4j.exception.WeixinException;
import com.foxinmy.weixin4j.http.weixin.WeixinHttpClient;
import com.foxinmy.weixin4j.http.weixin.WeixinResponse;
import com.foxinmy.weixin4j.model.Consts;
import com.foxinmy.weixin4j.model.Token;
import com.foxinmy.weixin4j.mp.type.URLConsts;
import com.foxinmy.weixin4j.token.TokenCreator;
import com.foxinmy.weixin4j.token.TokenHolder;
import com.foxinmy.weixin4j.util.ConfigUtil;
/**
* 微信公众平台JSTICKET创建者
@ -27,21 +26,11 @@ public class WeixinJSTicketCreator implements TokenCreator {
private final TokenHolder weixinTokenHolder;
private final WeixinHttpClient httpClient;
/**
* jssdk
*
* @param weixinTokenHolder
* <font color="red">公众平台的access_token</font>
*/
public WeixinJSTicketCreator(TokenHolder weixinTokenHolder) {
this(ConfigUtil.getWeixinAccount().getId(), weixinTokenHolder);
}
/**
* jssdk
*
* @param appid
* appid
* 公众号的appid
* @param weixinTokenHolder
* <font color="red">公众平台的access_token</font>
*/
@ -59,7 +48,7 @@ public class WeixinJSTicketCreator implements TokenCreator {
@Override
public Token createToken() throws WeixinException {
WeixinResponse response = httpClient.get(String.format(
Consts.MP_JS_TICKET_URL, weixinTokenHolder.getToken()
URLConsts.JS_TICKET_URL, weixinTokenHolder.getToken()
.getAccessToken()));
JSONObject result = response.getAsJson();
Token token = new Token(result.getString("ticket"));

View File

@ -4,11 +4,9 @@ import com.alibaba.fastjson.TypeReference;
import com.foxinmy.weixin4j.exception.WeixinException;
import com.foxinmy.weixin4j.http.weixin.WeixinHttpClient;
import com.foxinmy.weixin4j.http.weixin.WeixinResponse;
import com.foxinmy.weixin4j.model.Consts;
import com.foxinmy.weixin4j.model.Token;
import com.foxinmy.weixin4j.model.WeixinAccount;
import com.foxinmy.weixin4j.mp.type.URLConsts;
import com.foxinmy.weixin4j.token.TokenCreator;
import com.foxinmy.weixin4j.util.ConfigUtil;
/**
* 微信公众平台TOKEN创建者
@ -27,14 +25,13 @@ public class WeixinTokenCreator implements TokenCreator {
private final String appid;
private final String secret;
public WeixinTokenCreator() {
this(ConfigUtil.getWeixinAccount());
}
public WeixinTokenCreator(WeixinAccount weixinAccount) {
this(weixinAccount.getId(), weixinAccount.getSecret());
}
/**
*
* @param appid
* 公众号ID
* @param secret
* 公众号secret
*/
public WeixinTokenCreator(String appid, String secret) {
this.appid = appid;
this.secret = secret;
@ -48,7 +45,7 @@ public class WeixinTokenCreator implements TokenCreator {
@Override
public Token createToken() throws WeixinException {
String tokenUrl = String.format(Consts.MP_ASSESS_TOKEN_URL, appid,
String tokenUrl = String.format(URLConsts.ASSESS_TOKEN_URL, appid,
secret);
WeixinResponse response = httpClient.get(tokenUrl);
Token token = response.getAsObject(new TypeReference<Token>() {

View File

@ -0,0 +1,38 @@
package com.foxinmy.weixin4j.mp.type;
/**
* URL常量类
*
* @className URLConsts
* @author jy
* @date 2014年12月3日
* @since JDK 1.7
* @see
*/
public final class URLConsts {
/**
* 公众平台获取token的url
*/
public static final String ASSESS_TOKEN_URL = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=%s&secret=%s";
/**
* 公众平台jssdk获取token的url
*/
public static final String JS_TICKET_URL = "https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token=%s&type=jsapi";
/**
* 商户平台下统一订单生成的url
*/
public static final String UNIFIEDORDER = "https://api.mch.weixin.qq.com/pay/unifiedorder";
/**
* 商户平台下刷卡支付的url
*/
public static final String MICROPAYURL = "https://api.mch.weixin.qq.com/pay/micropay";
/**
* V2支付下natvie支付的url
*/
public static final String NATIVEURLV2 = "weixin://wxpay/bizpayurl?sign=%s&appid=%s&productid=%s&timestamp=%s&noncestr=%s";
/**
* 商户平台(V3)下native支付的url
*/
public static final String NATIVEURLV3 = "weixin://wxpay/bizpayurl?sign=%s&appid=%s&mch_id=%s&product_id=%s&time_stamp=%s&nonce_str=%s";
}

View File

@ -9,7 +9,7 @@ account={"id":"wx4ab8f8de58159a57","secret":"1d4eb0f4bf556aaed539f30ed05ca795",\
"partnerKey":"V2\u7248\u672c\u4e0b\u7684\u8d22\u4ed8\u901a\u5546\u6237\u6743\u9650\u5bc6\u94a5Key \u670d\u52a1\u53f7\u652f\u4ed8\u65f6\u9700\u8981\u586b\u5165",\
"paySignKey":"\u5fae\u4fe1\u652f\u4ed8\u4e2d\u8c03\u7528API\u7684\u5bc6\u94a5 \u670d\u52a1\u53f7\u652f\u4ed8\u65f6\u9700\u8981\u586b\u5165"}
# \u4f7f\u7528FileTokenHolder\u65f6token\u7684\u5b58\u653e\u8def\u5f84
# \u4f7f\u7528FileTokenStorager\u65f6token\u7684\u5b58\u653e\u8def\u5f84
token_path=/tmp/weixin4j/token
# \u4e8c\u7ef4\u7801\u4fdd\u5b58\u8def\u5f84
qr_path=/tmp/weixin4j/qrcode

View File

@ -32,7 +32,7 @@ public class CouponTest {
ACCOUNT = new WeixinMpAccount("appid",
"appsecret",
"paysign", "mchid");
WEIXINPAY = new WeixinPayProxy(new FileTokenStorager(), ACCOUNT);
WEIXINPAY = new WeixinPayProxy(ACCOUNT, new FileTokenStorager());
}
protected final File caFile = new File("证书文件路径(*.p12)");

View File

@ -30,10 +30,10 @@ public class PayTest {
static {
ACCOUNT2 = new WeixinMpAccount("请填入v2版本的appid", "请填入v2版本的appSecret",
"请填入v3版本的paysignkey", "请填入v2版本的partnerId", "请填入v2版本的partnerKey");
PAY2 = new WeixinPayProxy(new FileTokenStorager(), ACCOUNT2);
PAY2 = new WeixinPayProxy(ACCOUNT2, new FileTokenStorager());
ACCOUNT3 = new WeixinMpAccount("请填入v3版本的appid", "请填入v3版本的appSecret",
"请填入v3版本的paysignkey", "请填入v3版本的mchid");
PAY3 = new WeixinPayProxy(new FileTokenStorager(), ACCOUNT3);
PAY3 = new WeixinPayProxy(ACCOUNT3, new FileTokenStorager());
}
@Test

View File

@ -5,9 +5,11 @@ import org.junit.Before;
import org.junit.Test;
import com.foxinmy.weixin4j.exception.WeixinException;
import com.foxinmy.weixin4j.model.WeixinAccount;
import com.foxinmy.weixin4j.mp.token.WeixinTokenCreator;
import com.foxinmy.weixin4j.token.FileTokenStorager;
import com.foxinmy.weixin4j.token.TokenHolder;
import com.foxinmy.weixin4j.util.ConfigUtil;
/**
* token测试
@ -23,7 +25,9 @@ public class TokenTest {
@Before
public void setUp() {
tokenHolder = new TokenHolder(new WeixinTokenCreator(),
WeixinAccount weixinAccount = ConfigUtil.getWeixinAccount();
tokenHolder = new TokenHolder(new WeixinTokenCreator(
weixinAccount.getId(), weixinAccount.getSecret()),
new FileTokenStorager());
}

View File

@ -106,7 +106,6 @@ public class XmlstreamTest {
// System.err.println(xml2refundRecordV2());
xml2refundRecordV3();
// object2xmlWithoutRootElement();
/*RefundRecord refundRecord = xml2refundRecordV2();
System.err.println(refundRecord);
String sign = refundRecord.getSign();
@ -115,6 +114,5 @@ public class XmlstreamTest {
System.err.println("sign=" + sign + ",validSign=" + validSign);
System.err.println(ListsuffixResultSerializer
.serializeToXML(refundRecord));*/
}
}

View File

@ -52,4 +52,8 @@
* 2015-06-12
+ 新增企业号[登陆授权](src/main/java/com/foxinmy/weixin4j/qy/api/OauthApi.java)API
+ 新增企业号[登陆授权](src/main/java/com/foxinmy/weixin4j/qy/api/OauthApi.java)API
* 2015-06-21
+ **weixin4j-qy**:新增企业号[第三方应用API](src/main/java/com/foxinmy/weixin4j/qy/suite)。

View File

@ -8,7 +8,6 @@ import com.alibaba.fastjson.JSONObject;
import com.foxinmy.weixin4j.exception.WeixinException;
import com.foxinmy.weixin4j.http.weixin.JsonResult;
import com.foxinmy.weixin4j.model.Button;
import com.foxinmy.weixin4j.model.WeixinAccount;
import com.foxinmy.weixin4j.qy.api.AgentApi;
import com.foxinmy.weixin4j.qy.api.BatchApi;
import com.foxinmy.weixin4j.qy.api.HelperApi;
@ -16,6 +15,7 @@ import com.foxinmy.weixin4j.qy.api.MediaApi;
import com.foxinmy.weixin4j.qy.api.MenuApi;
import com.foxinmy.weixin4j.qy.api.NotifyApi;
import com.foxinmy.weixin4j.qy.api.PartyApi;
import com.foxinmy.weixin4j.qy.api.QyApi;
import com.foxinmy.weixin4j.qy.api.TagApi;
import com.foxinmy.weixin4j.qy.api.UserApi;
import com.foxinmy.weixin4j.qy.message.NotifyMessage;
@ -31,11 +31,9 @@ import com.foxinmy.weixin4j.qy.model.User;
import com.foxinmy.weixin4j.qy.token.WeixinTokenCreator;
import com.foxinmy.weixin4j.qy.type.InviteType;
import com.foxinmy.weixin4j.qy.type.UserStatus;
import com.foxinmy.weixin4j.token.FileTokenStorager;
import com.foxinmy.weixin4j.token.TokenHolder;
import com.foxinmy.weixin4j.token.TokenStorager;
import com.foxinmy.weixin4j.type.MediaType;
import com.foxinmy.weixin4j.util.ConfigUtil;
/**
* 微信企业号接口实现
@ -47,6 +45,7 @@ import com.foxinmy.weixin4j.util.ConfigUtil;
* @see <a href="http://qydev.weixin.qq.com/wiki/index.php">api文档</a>
*/
public class WeixinProxy {
private final MediaApi mediaApi;
private final MenuApi menuApi;
private final NotifyApi notifyApi;
@ -57,20 +56,24 @@ public class WeixinProxy {
private final AgentApi agentApi;
private final BatchApi batchApi;
private final TokenHolder tokenHolder;
/**
* 默认使用文件方式保存token使用weixin4j.properties配置的账号信息
*/
public WeixinProxy() {
this(new FileTokenStorager());
this(QyApi.DEFAULT_TOKEN_STORAGER);
}
/**
* 默认使用weixin4j.properties配置的账号信息
*
* @param tokenStorager token存储策略
* @param tokenStorager
* token存储策略
*/
public WeixinProxy(TokenStorager tokenStorager) {
this(tokenStorager, ConfigUtil.getWeixinAccount());
this(QyApi.DEFAULT_WEIXIN_ACCOUNT.getId(), QyApi.DEFAULT_WEIXIN_ACCOUNT
.getSecret(), tokenStorager);
}
/**
@ -80,19 +83,22 @@ public class WeixinProxy {
* @param corpsecret
*/
public WeixinProxy(String corpid, String corpsecret) {
this(new FileTokenStorager(), new WeixinAccount(corpid, corpsecret));
this(corpid, corpsecret, QyApi.DEFAULT_TOKEN_STORAGER);
}
/**
*
* @param corpid
* 企业号ID
* @param corpsecret
* 企业号secret
* @param tokenStorager
* token存储策略
* @param weixinAccount
* 企业号账号信息
* 企业号token存储器
*/
public WeixinProxy(TokenStorager tokenStorager, WeixinAccount weixinAccount) {
TokenHolder tokenHolder = new TokenHolder(new WeixinTokenCreator(
weixinAccount), tokenStorager);
public WeixinProxy(String corpid, String corpsecret,
TokenStorager tokenStorager) {
this.tokenHolder = new TokenHolder(new WeixinTokenCreator(corpid,
corpsecret), tokenStorager);
this.partyApi = new PartyApi(tokenHolder);
this.userApi = new UserApi(tokenHolder);
this.tagApi = new TagApi(tokenHolder);
@ -104,6 +110,10 @@ public class WeixinProxy {
this.mediaApi = new MediaApi(tokenHolder);
}
public TokenHolder getTokenHolder() {
return this.tokenHolder;
}
/**
* 发送消息(需要管理员对应用有使用权限对收件人tousertopartytotag有查看权限否则本次调用失败)
* <p>

View File

@ -10,11 +10,6 @@ import com.foxinmy.weixin4j.http.weixin.WeixinResponse;
import com.foxinmy.weixin4j.model.Consts;
import com.foxinmy.weixin4j.qy.model.Corpinfo;
import com.foxinmy.weixin4j.qy.model.OUserInfo;
import com.foxinmy.weixin4j.qy.model.WeixinQyAccount;
import com.foxinmy.weixin4j.qy.token.WeixinProviderTokenCreator;
import com.foxinmy.weixin4j.token.FileTokenStorager;
import com.foxinmy.weixin4j.token.TokenHolder;
import com.foxinmy.weixin4j.token.TokenStorager;
import com.foxinmy.weixin4j.util.ConfigUtil;
/**
@ -26,63 +21,26 @@ import com.foxinmy.weixin4j.util.ConfigUtil;
* @since JDK 1.7
* @see <a
* href="http://qydev.weixin.qq.com/wiki/index.php?title=%E4%BC%81%E4%B8%9A%E5%8F%B7%E7%99%BB%E5%BD%95%E6%8E%88%E6%9D%83">企业号登录授权说明</a>
* @see <a
* href="http://qydev.weixin.qq.com/wiki/index.php?title=%E7%AC%AC%E4%B8%89%E6%96%B9%E5%BA%94%E7%94%A8%E6%8E%88%E6%9D%83">第三方应用授权说明</a>
*/
public class OauthApi extends QyApi {
private final TokenHolder providerTokenHolder;
/**
* 默认使用文件方式保存token使用weixin4j.properties配置的账号信息
*/
public OauthApi() {
this(new FileTokenStorager());
}
/**
* 默认使用weixin4j.properties配置的账号信息
* 企业号登陆授权
*
* @param tokenStorager
*/
public OauthApi(TokenStorager tokenStorager) {
this(tokenStorager, JSON.parseObject(ConfigUtil.getValue("account"),
WeixinQyAccount.class));
}
/**
*
* @param appid
* @param appsecret
*/
public OauthApi(String corpid, String providerSecret) {
providerTokenHolder = new TokenHolder(new WeixinProviderTokenCreator(
corpid, providerSecret), new FileTokenStorager());
}
/**
*
* @param tokenStorager
* token存储策略
* @param weixinAccount
* 公众号账号信息
*/
public OauthApi(TokenStorager tokenStorager, WeixinQyAccount qyAccount) {
providerTokenHolder = new TokenHolder(new WeixinProviderTokenCreator(
qyAccount), tokenStorager);
}
/**
* @see {@link com.foxinmy.weixin4j.qy.api.OauthApi#getAuthorizeURL(String, String,String)}
*
* @return 请求授权的URL
*/
public String getAuthorizeURL() {
String corpId = ConfigUtil.getWeixinAccount().getId();
String corpId = DEFAULT_WEIXIN_ACCOUNT.getId();
String redirectUri = ConfigUtil.getValue("redirect_uri");
return getAuthorizeURL(corpId, redirectUri, "state");
}
/**
* oauth授权
* 企业号登陆授权
*
* @param corpId
* 企业号提供商的corpid
@ -91,6 +49,8 @@ public class OauthApi extends QyApi {
* @param state
* 用于保持请求和回调的状态授权请求后原样带回给第三方
* @return 请求授权的URL
* @see <a
* href="http://qydev.weixin.qq.com/wiki/index.php?title=%E4%BC%81%E4%B8%9A%E5%8F%B7%E7%99%BB%E5%BD%95%E6%8E%88%E6%9D%83">企业号登陆授权</a>
*/
public String getAuthorizeURL(String corpId, String redirectUri,
String state) {
@ -107,6 +67,8 @@ public class OauthApi extends QyApi {
/**
* 获取企业号管理员登录信息
*
* @param providerToken
* 提供商的token
* @param authCode
* oauth2.0授权企业号管理员登录产生的code
* @return 登陆信息
@ -115,12 +77,12 @@ public class OauthApi extends QyApi {
* @see com.foxinmy.weixin4j.qy.model.OUserInfo
* @throws WeixinException
*/
public OUserInfo getOUserInfo(String authCode) throws WeixinException {
public OUserInfo getOUserInfo(String providerToken, String authCode)
throws WeixinException {
String oauth_logininfo_uri = getRequestUri("oauth_logininfo_uri");
WeixinResponse response = weixinClient.post(String.format(
oauth_logininfo_uri, providerTokenHolder.getToken()
.getAccessToken()), String.format(
"{\"auth_code\":\"%s\"}", authCode));
WeixinResponse response = weixinClient.post(
String.format(oauth_logininfo_uri, providerToken),
String.format("{\"auth_code\":\"%s\"}", authCode));
return JSON.parseObject(response.getAsString(), OUserInfo.class,
new ExtraProcessor() {
@ -142,4 +104,44 @@ public class OauthApi extends QyApi {
}
});
}
/**
* @see {@link com.foxinmy.weixin4j.qy.api.OauthApi#getSuiteAuthorizeURL(String,String, String,String)}
* @param preAuthCode
* 预授权码
* @return
*/
public String getSuiteAuthorizeURL(String preAuthCode) {
String suiteId = DEFAULT_WEIXIN_ACCOUNT.getSuiteId();
String redirectUri = ConfigUtil.getValue("suite_redirect_uri");
return getSuiteAuthorizeURL(suiteId, preAuthCode, redirectUri, "state");
}
/**
* 应用套件授权
*
* @param suiteId
* 套件ID
* @param preAuthCode
* 预授权码
* @param redirectUri
* 授权后重定向url
* @param state
* 回调后原样返回
* @return 请求授权的URL <a
* href=""http://qydev.weixin.qq.com/wiki/index.php?title
* =%E4%BC%81%E4%B8%9A%E5%8F%B7%E7%AE%A1%E7%90%86%E5%91%98%E6%
* 8E%88%E6%9D%83%E5%BA%94%E7%94%A8>应用套件授权</a>
*/
public String getSuiteAuthorizeURL(String suiteId, String preAuthCode,
String redirectUri, String state) {
String suite_oauth_uri = getRequestUri("suite_oauth_uri");
try {
return String.format(suite_oauth_uri, suiteId, preAuthCode,
URLEncoder.encode(redirectUri, Consts.UTF_8.name()), state);
} catch (UnsupportedEncodingException e) {
;
}
return "";
}
}

View File

@ -2,7 +2,12 @@ package com.foxinmy.weixin4j.qy.api;
import java.util.ResourceBundle;
import com.alibaba.fastjson.JSON;
import com.foxinmy.weixin4j.api.BaseApi;
import com.foxinmy.weixin4j.qy.model.WeixinQyAccount;
import com.foxinmy.weixin4j.qy.suite.FileTicketProcessor;
import com.foxinmy.weixin4j.qy.suite.SuiteTicketProcessor;
import com.foxinmy.weixin4j.util.ConfigUtil;
/**
* 微信企业号API
@ -15,14 +20,27 @@ import com.foxinmy.weixin4j.api.BaseApi;
* @see <a href="http://qydev.weixin.qq.com/wiki/index.php">api文档</a>
*/
public class QyApi extends BaseApi {
private final static ResourceBundle weixinBundle;
private final static ResourceBundle WEIXIN_BUNDLE;
/**
* 默认使用weixin4j.properties文件中的企业号信息
*/
public final static WeixinQyAccount DEFAULT_WEIXIN_ACCOUNT;
/**
* 默认使用File的方法读取套件ticket
*/
public final static SuiteTicketProcessor DEFAULT_TICKET_PROCESSOR;
static {
weixinBundle = ResourceBundle
WEIXIN_BUNDLE = ResourceBundle
.getBundle("com/foxinmy/weixin4j/qy/api/weixin");
DEFAULT_WEIXIN_ACCOUNT = JSON.parseObject(
ConfigUtil.getValue("account"), WeixinQyAccount.class);
DEFAULT_TICKET_PROCESSOR = new FileTicketProcessor();
}
@Override
protected ResourceBundle getWeixinBundle() {
return weixinBundle;
protected String getConfigValue(String key) {
return WEIXIN_BUNDLE.getString(key);
}
}

View File

@ -0,0 +1,101 @@
package com.foxinmy.weixin4j.qy.api;
import com.alibaba.fastjson.JSONObject;
import com.foxinmy.weixin4j.exception.WeixinException;
import com.foxinmy.weixin4j.http.weixin.JsonResult;
import com.foxinmy.weixin4j.http.weixin.WeixinResponse;
import com.foxinmy.weixin4j.qy.suite.SuiteTicketProcessor;
import com.foxinmy.weixin4j.qy.suite.WeixinSuitePreCodeCreator;
import com.foxinmy.weixin4j.qy.suite.WeixinSuiteTokenCreator;
import com.foxinmy.weixin4j.token.TokenHolder;
import com.foxinmy.weixin4j.token.TokenStorager;
/**
* 第三方应用套件
*
* @className SuiteApi
* @author jy
* @date 2015年6月17日
* @since JDK 1.7
* @see <a
* href="http://qydev.weixin.qq.com/wiki/index.php?title=%E7%AC%AC%E4%B8%89%E6%96%B9%E5%BA%94%E7%94%A8%E6%8E%88%E6%9D%83">第三方应用授权</a>
*/
public class SuiteApi extends QyApi {
/**
* 应用套件token
*/
private final TokenHolder suiteTokenHolder;
private final TokenHolder suiteTicketHolder;
public SuiteApi() throws WeixinException {
this(DEFAULT_WEIXIN_ACCOUNT.getSuiteId(), DEFAULT_WEIXIN_ACCOUNT
.getSuiteSecret());
}
public SuiteApi(String suiteId, String suiteSecret) throws WeixinException {
this(suiteId, suiteSecret, DEFAULT_TICKET_PROCESSOR,
DEFAULT_TOKEN_STORAGER);
}
/**
*
* @param suiteId
* 应用ID
* @param suiteSecret
* 应用secret
* @param ticketReader
* 应用ticket读取器
* @param tokenStorager
* 应用token存储器
* @throws WeixinException
*/
public SuiteApi(String suiteId, String suiteSecret,
SuiteTicketProcessor ticketReader, TokenStorager tokenStorager)
throws WeixinException {
this.suiteTokenHolder = new TokenHolder(new WeixinSuiteTokenCreator(
suiteId, suiteSecret, ticketReader), tokenStorager);
this.suiteTicketHolder = new TokenHolder(new WeixinSuitePreCodeCreator(
suiteTokenHolder, suiteId), tokenStorager);
}
/**
* 应用套件token
*
* @return
*/
public TokenHolder getTokenHolder() {
return this.suiteTokenHolder;
}
/**
* 应用套件ticket
*
* @return
*/
public TokenHolder getTicketHolder() {
return this.suiteTicketHolder;
}
/**
* 设置套件授权配置:如果需要对某次授权进行配置则调用本接口目前仅可以设置哪些应用可以授权不调用则默认允许所有应用进行授权
*
* @param appids
* 允许进行授权的应用id如123
* @return 处理结果
* @throws WeixinException
* <a href=
* "http://qydev.weixin.qq.com/wiki/index.php?title=%E7%AC%AC%E4%B8%89%E6%96%B9%E5%BA%94%E7%94%A8%E6%8E%A5%E5%8F%A3%E8%AF%B4%E6%98%8E#.E8.AE.BE.E7.BD.AE.E6.8E.88.E6.9D.83.E9.85.8D.E7.BD.AE"
* >设置套件授权配置</a>
*/
public JsonResult setSuiteSession(int... appids) throws WeixinException {
String suite_set_session_uri = getRequestUri("suite_set_session_uri");
JSONObject para = new JSONObject();
para.put("pre_auth_code", suiteTicketHolder.getAccessToken());
para.put("session_info", appids);
WeixinResponse response = weixinClient
.post(String.format(suite_set_session_uri,
suiteTokenHolder.getAccessToken()), para.toJSONString());
return response.getAsJsonResult();
}
}

View File

@ -83,4 +83,10 @@ batch_getresult_uri={api_base_url}/batch/getresult?access_token=%s&jobid=%s
# \u63d0\u4f9b\u5546oauth\u6388\u6743
provider_oauth_uri=https://qy.weixin.qq.com/cgi-bin/loginpage?corp_id=%s&redirect_uri=%s&state=%s
# \u4f01\u4e1a\u53f7\u7ba1\u7406\u5458\u767b\u5f55\u4fe1\u606f
oauth_logininfo_uri={api_base_url}/service/get_login_info?provider_access_token=%s
oauth_logininfo_uri={api_base_url}/service/get_login_info?provider_access_token=%s
# \u5e94\u7528\u5957\u4ef6oauth\u6388\u6743
suite_oauth_uri=https://qy.weixin.qq.com/cgi-bin/loginpage?suite_id=%s&pre_auth_code=%s&redirect_uri=%s&state=%s
# \u5e94\u7528\u5957\u4ef6\u8bbe\u7f6e\u6388\u6743\u914d\u7f6e
suite_set_session_uri={api_base_url}/service/set_session_info?suite_access_token=%s
# \u83b7\u53d6\u4f01\u4e1a\u53f7\u7684\u6c38\u4e45\u6388\u6743\u7801
suite_get_permanent_uri={api_base_url}/service/get_permanent_code?suite_access_token=%s

View File

@ -34,6 +34,11 @@ public class Corpinfo extends AgentOverview {
*/
@JSONField(name = "corp_agent_max")
private int corpAgentMax;
/**
* 授权方企业号二维码
*/
@JSONField(name = "corp_wxqrcode")
private String corpWxqrcode;
public String getCorpid() {
return corpid;
@ -67,10 +72,19 @@ public class Corpinfo extends AgentOverview {
this.corpAgentMax = corpAgentMax;
}
public String getCorpWxqrcode() {
return corpWxqrcode;
}
public void setCorpWxqrcode(String corpWxqrcode) {
this.corpWxqrcode = corpWxqrcode;
}
@Override
public String toString() {
return "Corpinfo [corpid=" + corpid + ", corpType=" + corpType
+ ", corpUserMax=" + corpUserMax + ", corpAgentMax="
+ corpAgentMax + ", " + super.toString() + "]";
+ corpAgentMax + ", corpWxqrcode=" + corpWxqrcode + ", "
+ super.toString() + "]";
}
}

View File

@ -30,11 +30,61 @@ public class WeixinQyAccount extends WeixinAccount {
super(corpid, corpsecret);
}
/**
* 应用套件id
*/
private String suiteId;
/**
* 应用套件secret
*/
private String suiteSecret;
/**
* 应用套件token,用于生成签名,校验回调请求的合法性后续所有托管的企业产生的回调消息都使用该值来解密
*/
private String suiteToken;
/**
* 应用套件encodingAesKey,回调消息加解密参数是AES密钥的Base64编码用于解密回调消息内容对应的密文
* 后续所有托管的企业产生的回调消息都使用该值来解密
*/
private String suiteEncodingAesKey;
/**
* 提供商的secret
*/
private String providerSecret;
public String getSuiteId() {
return suiteId;
}
public void setSuiteId(String suiteId) {
this.suiteId = suiteId;
}
public String getSuiteSecret() {
return suiteSecret;
}
public void setSuiteSecret(String suiteSecret) {
this.suiteSecret = suiteSecret;
}
public String getSuiteToken() {
return suiteToken;
}
public void setSuiteToken(String suiteToken) {
this.suiteToken = suiteToken;
}
public String getSuiteEncodingAesKey() {
return suiteEncodingAesKey;
}
public void setSuiteEncodingAesKey(String suiteEncodingAesKey) {
this.suiteEncodingAesKey = suiteEncodingAesKey;
}
public String getProviderSecret() {
return providerSecret;
}
@ -45,7 +95,9 @@ public class WeixinQyAccount extends WeixinAccount {
@Override
public String toString() {
return "WeixinQyAccount [" + super.toString() + ", providerSecret="
+ providerSecret + "]";
return "WeixinQyAccount [" + super.toString() + ", suiteId=" + suiteId
+ ", suiteSecret=" + suiteSecret + ", suiteToken=" + suiteToken
+ ", suiteEncodingAesKey=" + suiteEncodingAesKey
+ ", providerSecret=" + providerSecret + "]";
}
}

View File

@ -0,0 +1,61 @@
package com.foxinmy.weixin4j.qy.suite;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import com.foxinmy.weixin4j.exception.WeixinException;
import com.foxinmy.weixin4j.util.ConfigUtil;
import com.foxinmy.weixin4j.xml.XmlStream;
/**
* 用file存储ticket
*
* @className FileTicketProcessor
* @author jy
* @date 2015年6月21日
* @since JDK 1.7
* @see
*/
public class FileTicketProcessor implements SuiteTicketProcessor {
private final String ticketPath;
public FileTicketProcessor() {
this.ticketPath = ConfigUtil.getValue("ticket_path");
}
public FileTicketProcessor(String ticketPath) {
this.ticketPath = ticketPath;
}
@Override
public void write(SuiteTicketMessage suiteTicket) throws WeixinException {
try {
XmlStream
.toXML(suiteTicket,
new FileOutputStream(new File(String.format(
"%s/%s.xml", ticketPath,
getCacheKey(suiteTicket.getSuiteId())))));
} catch (IOException e) {
throw new WeixinException(e.getMessage());
}
}
private String getCacheKey(String suiteId) {
return String.format("qy_suite_ticket_%s", suiteId);
}
@Override
public SuiteTicketMessage read(String suiteId) throws WeixinException {
File ticket_file = new File(String.format("%s/%s.xml", ticketPath,
getCacheKey(suiteId)));
try {
return XmlStream.fromXML(new FileInputStream(ticket_file),
SuiteTicketMessage.class);
} catch (IOException e) {
throw new WeixinException(e.getMessage());
}
}
}

View File

@ -0,0 +1,26 @@
package com.foxinmy.weixin4j.qy.suite;
/**
* 应用套件回调事件
*
* @className SuiteEventType
* @author jy
* @date 2015年6月21日
* @since JDK 1.7
* @see <a
* href="http://qydev.weixin.qq.com/wiki/index.php?title=%E7%AC%AC%E4%B8%89%E6%96%B9%E5%9B%9E%E8%B0%83%E5%8D%8F%E8%AE%AE">第三方回调协议</a>
*/
public enum SuiteEventType {
/**
* 推送ticket
*/
suite_ticket,
/**
* 变更授权
*/
change_auth,
/**
* 取消授权
*/
cancel_auth;
}

View File

@ -0,0 +1,67 @@
package com.foxinmy.weixin4j.qy.suite;
import java.io.Serializable;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
@XmlRootElement(name = "xml")
@XmlAccessorType(XmlAccessType.FIELD)
public class SuiteTicketMessage implements Serializable {
private static final long serialVersionUID = 6457919241019021514L;
/**
* 应用套件的SuiteId
*/
@XmlElement(name = "SuiteId")
private String suiteId;
/**
* 事件类型
*/
@XmlElement(name = "InfoType")
private SuiteEventType eventType;
/**
* 时间戳
*/
@XmlElement(name = "TimeStamp")
private long timeStamp;
/**
* Ticket内容
*/
@XmlElement(name = "SuiteTicket")
private String SuiteTicket;
/**
* 授权方企业号的corpid
*/
@XmlElement(name = "AuthCorpId")
private String authCorpId;
public String getSuiteId() {
return suiteId;
}
public SuiteEventType getEventType() {
return eventType;
}
public long getTimeStamp() {
return timeStamp;
}
public String getSuiteTicket() {
return SuiteTicket;
}
public String getAuthCorpId() {
return authCorpId;
}
@Override
public String toString() {
return "SuiteTicketMessage [suiteId=" + suiteId + ", eventType="
+ eventType + ", timeStamp=" + timeStamp + ", SuiteTicket="
+ SuiteTicket + ", authCorpId=" + authCorpId + "]";
}
}

View File

@ -0,0 +1,31 @@
package com.foxinmy.weixin4j.qy.suite;
import com.foxinmy.weixin4j.exception.WeixinException;
/**
* 对套件ticket的处理写入与读取
*
* @className SuiteTicketProcessor
* @author jy
* @date 2015年6月18日
* @since JDK 1.7
* @see
*/
public interface SuiteTicketProcessor {
/**
* 写入微信推送过来的suite_ticket
*
* @param suiteTicket
* @throws WeixinException
*/
public void write(SuiteTicketMessage suiteTicket) throws WeixinException;
/**
* 读取最新的suite_ticket
*
* @param suiteId
* @return 最新的ticket
* @throws WeixinException
*/
public SuiteTicketMessage read(String suiteId) throws WeixinException;
}

View File

@ -0,0 +1,60 @@
package com.foxinmy.weixin4j.qy.suite;
import com.alibaba.fastjson.JSONObject;
import com.foxinmy.weixin4j.exception.WeixinException;
import com.foxinmy.weixin4j.http.weixin.WeixinHttpClient;
import com.foxinmy.weixin4j.http.weixin.WeixinResponse;
import com.foxinmy.weixin4j.model.Token;
import com.foxinmy.weixin4j.qy.type.URLConsts;
import com.foxinmy.weixin4j.token.TokenCreator;
import com.foxinmy.weixin4j.token.TokenHolder;
/**
* 微信企业号应用套件预授权码创建
*
* @className WeixinTokenCreator
* @author jy
* @date 2015年6月17日
* @since JDK 1.7
* @see <a
* href="http://qydev.weixin.qq.com/wiki/index.php?title=%E7%AC%AC%E4%B8%89%E6%96%B9%E5%BA%94%E7%94%A8%E6%8E%A5%E5%8F%A3%E8%AF%B4%E6%98%8E#.E8.8E.B7.E5.8F.96.E9.A2.84.E6.8E.88.E6.9D.83.E7.A0.81">获取应用套件预授权码</a>
* @see com.foxinmy.weixin4j.model.Token
*/
public class WeixinSuitePreCodeCreator implements TokenCreator {
private final WeixinHttpClient httpClient;
private final TokenHolder suiteTokenHolder;
private final String suiteId;
/**
*
* @param suiteTokenHolder
* 应用套件的token
* @param suiteId
* 应用套件ID
*/
public WeixinSuitePreCodeCreator(TokenHolder suiteTokenHolder,
String suiteId) {
this.suiteTokenHolder = suiteTokenHolder;
this.suiteId = suiteId;
this.httpClient = new WeixinHttpClient();
}
@Override
public String getCacheKey() {
return String.format("qy_suite_precode_%s", suiteId);
}
@Override
public Token createToken() throws WeixinException {
WeixinResponse response = httpClient.post(
String.format(URLConsts.SUITE_PRE_CODE_URL,
suiteTokenHolder.getAccessToken()),
String.format("{\"suite_id\":\"%s\"}", suiteId));
JSONObject result = response.getAsJson();
Token token = new Token(result.getString("pre_auth_code"));
token.setExpiresIn(result.getIntValue("expires_in"));
token.setTime(System.currentTimeMillis());
return token;
}
}

View File

@ -0,0 +1,65 @@
package com.foxinmy.weixin4j.qy.suite;
import com.alibaba.fastjson.JSONObject;
import com.foxinmy.weixin4j.exception.WeixinException;
import com.foxinmy.weixin4j.http.weixin.WeixinHttpClient;
import com.foxinmy.weixin4j.http.weixin.WeixinResponse;
import com.foxinmy.weixin4j.model.Token;
import com.foxinmy.weixin4j.qy.type.URLConsts;
import com.foxinmy.weixin4j.token.TokenCreator;
/**
* 微信企业号应用套件凭证创建
*
* @className WeixinTokenCreator
* @author jy
* @date 2015年6月17日
* @since JDK 1.7
* @see <a
* href="http://qydev.weixin.qq.com/wiki/index.php?title=%E7%AC%AC%E4%B8%89%E6%96%B9%E5%BA%94%E7%94%A8%E6%8E%A5%E5%8F%A3%E8%AF%B4%E6%98%8E#.E8.8E.B7.E5.8F.96.E5.BA.94.E7.94.A8.E5.A5.97.E4.BB.B6.E4.BB.A4.E7.89.8C">获取应用套件凭证</a>
* @see com.foxinmy.weixin4j.model.Token
*/
public class WeixinSuiteTokenCreator implements TokenCreator {
private final WeixinHttpClient httpClient;
private final String suiteId;
private final String suiteSecret;
private final SuiteTicketProcessor ticketProcessor;
/**
*
* @param suiteId
* 套件ID
* @param suiteSecret
* 套件secret
* @param ticketReader
* 套件ticket读取器
*/
public WeixinSuiteTokenCreator(String suiteId, String suiteSecret,
SuiteTicketProcessor ticketProcessor) {
this.suiteId = suiteId;
this.suiteSecret = suiteSecret;
this.ticketProcessor = ticketProcessor;
this.httpClient = new WeixinHttpClient();
}
@Override
public String getCacheKey() {
return String.format("qy_suite_token_%s", suiteId);
}
@Override
public Token createToken() throws WeixinException {
JSONObject obj = new JSONObject();
obj.put("suite_id", suiteId);
obj.put("suite_secret", suiteSecret);
obj.put("suite_ticket", ticketProcessor.read(suiteId).getSuiteTicket());
WeixinResponse response = httpClient.post(URLConsts.SUITE_TOKEN_URL,
obj.toJSONString());
obj = response.getAsJson();
Token token = new Token(obj.getString("suite_access_token"));
token.setExpiresIn(obj.getIntValue("expires_in"));
token.setTime(System.currentTimeMillis());
return token;
}
}

View File

@ -4,11 +4,10 @@ import com.alibaba.fastjson.JSONObject;
import com.foxinmy.weixin4j.exception.WeixinException;
import com.foxinmy.weixin4j.http.weixin.WeixinHttpClient;
import com.foxinmy.weixin4j.http.weixin.WeixinResponse;
import com.foxinmy.weixin4j.model.Consts;
import com.foxinmy.weixin4j.model.Token;
import com.foxinmy.weixin4j.qy.type.URLConsts;
import com.foxinmy.weixin4j.token.TokenCreator;
import com.foxinmy.weixin4j.token.TokenHolder;
import com.foxinmy.weixin4j.util.ConfigUtil;
/**
* 微信企业号JSTICKET创建
@ -28,19 +27,11 @@ public class WeixinJSTicketCreator implements TokenCreator {
private final WeixinHttpClient httpClient;
/**
* jssdk
*
* @param weixinTokenHolder
* <font color="red">公众平台的access_token</font>
*/
public WeixinJSTicketCreator(TokenHolder weixinTokenHolder) {
this(ConfigUtil.getWeixinAccount().getId(), weixinTokenHolder);
}
/**
* <font color="red">企业号的的access_token</font>
*
* @param corpid
* 企业号ID
* @param weixinTokenHolder
* <font color="red">企业号的的access_token</font>
*/
public WeixinJSTicketCreator(String corpid, TokenHolder weixinTokenHolder) {
this.corpid = corpid;
@ -56,7 +47,7 @@ public class WeixinJSTicketCreator implements TokenCreator {
@Override
public Token createToken() throws WeixinException {
WeixinResponse response = httpClient.get(String.format(
Consts.QY_JS_TICKET_URL, weixinTokenHolder.getToken()
URLConsts.JS_TICKET_URL, weixinTokenHolder.getToken()
.getAccessToken()));
JSONObject result = response.getAsJson();
Token token = new Token(result.getString("ticket"));

View File

@ -1,15 +1,12 @@
package com.foxinmy.weixin4j.qy.token;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.foxinmy.weixin4j.exception.WeixinException;
import com.foxinmy.weixin4j.http.weixin.WeixinHttpClient;
import com.foxinmy.weixin4j.http.weixin.WeixinResponse;
import com.foxinmy.weixin4j.model.Consts;
import com.foxinmy.weixin4j.model.Token;
import com.foxinmy.weixin4j.qy.model.WeixinQyAccount;
import com.foxinmy.weixin4j.qy.type.URLConsts;
import com.foxinmy.weixin4j.token.TokenCreator;
import com.foxinmy.weixin4j.util.ConfigUtil;
/**
* 微信企业号应用提供商凭证创建
@ -28,15 +25,13 @@ public class WeixinProviderTokenCreator implements TokenCreator {
private final String corpid;
private final String providersecret;
public WeixinProviderTokenCreator() {
this(JSON.parseObject(ConfigUtil.getValue("account"),
WeixinQyAccount.class));
}
public WeixinProviderTokenCreator(WeixinQyAccount qyAccount) {
this(qyAccount.getId(), qyAccount.getProviderSecret());
}
/**
*
* @param corpid
* 企业号ID
* @param providersecret
* 企业号提供商的secret
*/
public WeixinProviderTokenCreator(String corpid, String providersecret) {
this.corpid = corpid;
this.providersecret = providersecret;
@ -53,7 +48,7 @@ public class WeixinProviderTokenCreator implements TokenCreator {
JSONObject obj = new JSONObject();
obj.put("corpid", corpid);
obj.put("provider_secret", providersecret);
WeixinResponse response = httpClient.post(Consts.QY_PROVIDER_TOKEN_URL,
WeixinResponse response = httpClient.post(URLConsts.PROVIDER_TOKEN_URL,
obj.toJSONString());
obj = response.getAsJson();
Token token = new Token(obj.getString("provider_access_token"));

View File

@ -4,11 +4,9 @@ import com.alibaba.fastjson.TypeReference;
import com.foxinmy.weixin4j.exception.WeixinException;
import com.foxinmy.weixin4j.http.weixin.WeixinHttpClient;
import com.foxinmy.weixin4j.http.weixin.WeixinResponse;
import com.foxinmy.weixin4j.model.Consts;
import com.foxinmy.weixin4j.model.Token;
import com.foxinmy.weixin4j.model.WeixinAccount;
import com.foxinmy.weixin4j.qy.type.URLConsts;
import com.foxinmy.weixin4j.token.TokenCreator;
import com.foxinmy.weixin4j.util.ConfigUtil;
/**
* 微信企业号TOKEN创建
@ -27,14 +25,13 @@ public class WeixinTokenCreator implements TokenCreator {
private final String corpid;
private final String corpsecret;
public WeixinTokenCreator() {
this(ConfigUtil.getWeixinAccount());
}
public WeixinTokenCreator(WeixinAccount weixinAccount) {
this(weixinAccount.getId(), weixinAccount.getSecret());
}
/**
*
* @param corpid
* 企业号ID
* @param corpsecret
* 企业号secret
*/
public WeixinTokenCreator(String corpid, String corpsecret) {
this.corpid = corpid;
this.corpsecret = corpsecret;
@ -48,7 +45,7 @@ public class WeixinTokenCreator implements TokenCreator {
@Override
public Token createToken() throws WeixinException {
String tokenUrl = String.format(Consts.QY_ASSESS_TOKEN_URL, corpid,
String tokenUrl = String.format(URLConsts.ASSESS_TOKEN_URL, corpid,
corpsecret);
WeixinResponse response = httpClient.get(tokenUrl);
Token token = response.getAsObject(new TypeReference<Token>() {

View File

@ -0,0 +1,43 @@
package com.foxinmy.weixin4j.qy.type;
/**
* URL常量类
*
* @className URLConsts
* @author jy
* @date 2014年12月3日
* @since JDK 1.7
* @see
*/
public final class URLConsts {
/**
*
*/
public static final String BASE_URL = "https://qyapi.weixin.qq.com/cgi-bin";
/**
* 企业号获取token的url
*/
public static final String ASSESS_TOKEN_URL = BASE_URL
+ "/gettoken?corpid=%s&corpsecret=%s";
/**
* 企业号提供商获取token的url
*/
public static final String PROVIDER_TOKEN_URL = BASE_URL
+ "/service/get_provider_token";
/**
* 企业号jssdk获取token的url
*/
public static final String JS_TICKET_URL = BASE_URL
+ "/get_jsapi_ticket?access_token=%s";
/**
* 企业号第三方应用套件获取token的url
*/
public static final String SUITE_TOKEN_URL = BASE_URL
+ "/service/get_suite_token";
/**
* 企业号第三方应用套件获取预授权码的url
*/
public static final String SUITE_PRE_CODE_URL = BASE_URL
+ "/service/get_pre_auth_code?suite_access_token=%s";
}

View File

@ -3,12 +3,18 @@
account={"id":"wxf10bce209c91d0e2","secret":"cW0OtP7-7YJ7jHKFZaJW2skJHE9bLHadxOswBdVdI2walRBSPfTSA6QqD_QXw8JZ",\
"token":"gp2eGT5mIpngr",\
"encodingAesKey":"BRYfV4zPFUJb3v3MySNBg1ERKE3vyyMRoScu76vFySv",\
"suiteId":"\u5e94\u7528\u5957\u4ef6\u7684id","suiteSecret":"\u5e94\u7528\u5957\u4ef6\u7684secret",\
"suiteToken":"\u5e94\u7528\u5957\u4ef6\u7684token",\
"suiteEncodingAesKey":"\u5e94\u7528\u5957\u4ef6\u7684encodingAesKey",\
"providerSecret":"\u63d0\u4f9b\u5546\u7684secret"}
# \u4f7f\u7528FileTokenHolder\u65f6token\u7684\u5b58\u653e\u8def\u5f84
# \u4f7f\u7528FileTokenStorager\u65f6token\u7684\u5b58\u653e\u8def\u5f84
token_path=/tmp/weixin4j/token
# \u5a92\u4f53\u6587\u4ef6\u4fdd\u5b58\u8def\u5f84
media_path=/tmp/weixin4j/media
# oauth\u6388\u6743\u540e\u91cd\u5b9a\u5411\u7684url
redirect_uri=
# \u4f01\u4e1a\u53f7\u767b\u9646\u6388\u6743\u540e\u91cd\u5b9a\u5411\u7684url
redirect_uri=
# \u4f01\u4e1a\u53f7\u7b2c\u4e09\u65b9\u5e94\u7528\u5957\u4ef6\u6388\u6743\u540e\u91cd\u5b9a\u5411\u7684url
suite_redirect_uri=

View File

@ -5,9 +5,11 @@ import org.junit.Before;
import org.junit.Test;
import com.foxinmy.weixin4j.exception.WeixinException;
import com.foxinmy.weixin4j.model.WeixinAccount;
import com.foxinmy.weixin4j.qy.token.WeixinTokenCreator;
import com.foxinmy.weixin4j.token.FileTokenStorager;
import com.foxinmy.weixin4j.token.TokenHolder;
import com.foxinmy.weixin4j.util.ConfigUtil;
/**
* token测试
@ -23,7 +25,9 @@ public class TokenTest {
@Before
public void setUp() {
tokenHolder = new TokenHolder(new WeixinTokenCreator(),
WeixinAccount weixinAccount = ConfigUtil.getWeixinAccount();
tokenHolder = new TokenHolder(new WeixinTokenCreator(
weixinAccount.getId(), weixinAccount.getSecret()),
new FileTokenStorager());
}

View File

@ -17,12 +17,12 @@ public class TransferCustomerResponse implements WeixinResponse {
*/
private String kfAccount;
public String getKfAccount() {
return kfAccount;
public TransferCustomerResponse(String kfAccount) {
this.kfAccount = kfAccount;
}
public void setKfAccount(String kfAccount) {
this.kfAccount = kfAccount;
public String getKfAccount() {
return kfAccount;
}
@Override