主要新增了企业号的第三方应用API
This commit is contained in:
@@ -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;
|
||||
}
|
||||
|
||||
/**
|
||||
* 发货通知
|
||||
*
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
/**
|
||||
* 上传媒体文件
|
||||
*
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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>() {
|
||||
});
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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"));
|
||||
|
||||
@@ -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>() {
|
||||
|
||||
@@ -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×tamp=%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";
|
||||
}
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user