新增企业付款查询接口 & 对多个公众号的接入支持

This commit is contained in:
jinyu 2015-06-23 22:20:24 +08:00
parent 37503acac6
commit bb79042d4e
27 changed files with 667 additions and 136 deletions

View File

@ -341,4 +341,10 @@
* 2015-06-22 * 2015-06-22
+ **weixin4j-qy**: 新增企业号[第三方应用代理](weixin4j-qy/src/main/java/com/foxinmy/weixin4j/qy/WeixinSuiteProxy.java)。 + **weixin4j-qy**: 新增企业号[第三方应用代理](weixin4j-qy/src/main/java/com/foxinmy/weixin4j/qy/WeixinSuiteProxy.java)。
* 2015-06-23
+ **weixin4j-mp**: 新增企业付款查询接口
+ **weixin4j-server**: 对多个公众号的接入支持

11
pom.xml
View File

@ -110,17 +110,6 @@
</plugins> </plugins>
<pluginManagement> <pluginManagement>
<plugins> <plugins>
<!-- <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-shade-plugin</artifactId>
<version>2.3</version> <executions> <execution> <id>standard</id> <phase>package</phase>
<goals> <goal>shade</goal> </goals> <configuration> <createSourcesJar>false</createSourcesJar>
<artifactSet> <includes> <include>com.foxinmy:weixin4j-base</include> </includes>
</artifactSet> <createDependencyReducedPom>false</createDependencyReducedPom>
</configuration> </execution> <execution> <id>full</id> <phase>package</phase>
<goals> <goal>shade</goal> </goals> <configuration> <createSourcesJar>false</createSourcesJar>
<artifactSet> <includes> <include>com.foxinmy:weixin4j-base</include> <include>com.alibaba:fastjson</include>
</includes> </artifactSet> <finalName>${project.artifactId}-${project.version}-full</finalName>
<createDependencyReducedPom>false</createDependencyReducedPom> </configuration>
</execution> </executions> </plugin> -->
<plugin> <plugin>
<groupId>org.apache.maven.plugins</groupId> <groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-source-plugin</artifactId> <artifactId>maven-source-plugin</artifactId>

View File

@ -112,4 +112,8 @@
* 2015-06-04 * 2015-06-04
+ 新增查询红包接口 + 新增查询红包接口
* 2015-06-23
+ 新增企业付款查询接口

View File

@ -18,6 +18,7 @@ import com.foxinmy.weixin4j.mp.payment.coupon.CouponResult;
import com.foxinmy.weixin4j.mp.payment.coupon.CouponStock; import com.foxinmy.weixin4j.mp.payment.coupon.CouponStock;
import com.foxinmy.weixin4j.mp.payment.v3.ApiResult; import com.foxinmy.weixin4j.mp.payment.v3.ApiResult;
import com.foxinmy.weixin4j.mp.payment.v3.MPPayment; import com.foxinmy.weixin4j.mp.payment.v3.MPPayment;
import com.foxinmy.weixin4j.mp.payment.v3.MPPaymentRecord;
import com.foxinmy.weixin4j.mp.payment.v3.MPPaymentResult; import com.foxinmy.weixin4j.mp.payment.v3.MPPaymentResult;
import com.foxinmy.weixin4j.mp.payment.v3.Redpacket; import com.foxinmy.weixin4j.mp.payment.v3.Redpacket;
import com.foxinmy.weixin4j.mp.payment.v3.RedpacketRecord; import com.foxinmy.weixin4j.mp.payment.v3.RedpacketRecord;
@ -30,7 +31,6 @@ import com.foxinmy.weixin4j.mp.type.IdType;
import com.foxinmy.weixin4j.mp.type.RefundType; import com.foxinmy.weixin4j.mp.type.RefundType;
import com.foxinmy.weixin4j.token.TokenHolder; import com.foxinmy.weixin4j.token.TokenHolder;
import com.foxinmy.weixin4j.token.TokenStorager; import com.foxinmy.weixin4j.token.TokenStorager;
import com.foxinmy.weixin4j.util.ConfigUtil;
/** /**
* 微信支付接口实现 * 微信支付接口实现
@ -239,9 +239,8 @@ public class WeixinPayProxy {
IdQuery idQuery, String outRefundNo, double totalFee, IdQuery idQuery, String outRefundNo, double totalFee,
double refundFee, String opUserId, String opUserPasswd) double refundFee, String opUserId, String opUserPasswd)
throws WeixinException { throws WeixinException {
File caFile = new File(ConfigUtil.getClassPathValue("ca_file")); return refundV2(PayApi.DEFAULT_CA_FILE, idQuery, outRefundNo, totalFee,
return refundV2(caFile, idQuery, outRefundNo, totalFee, refundFee, refundFee, opUserId, opUserPasswd);
opUserId, opUserPasswd);
} }
/** /**
@ -358,9 +357,8 @@ public class WeixinPayProxy {
public com.foxinmy.weixin4j.mp.payment.v3.RefundResult refundV3( public com.foxinmy.weixin4j.mp.payment.v3.RefundResult refundV3(
IdQuery idQuery, String outRefundNo, double totalFee, IdQuery idQuery, String outRefundNo, double totalFee,
double refundFee, String opUserId) throws WeixinException { double refundFee, String opUserId) throws WeixinException {
File caFile = new File(ConfigUtil.getClassPathValue("ca_file")); return pay3Api.refund(PayApi.DEFAULT_CA_FILE, idQuery, outRefundNo,
return pay3Api.refund(caFile, idQuery, outRefundNo, totalFee, totalFee, refundFee, CurrencyType.CNY, opUserId);
refundFee, CurrencyType.CNY, opUserId);
} }
/** /**
@ -444,8 +442,7 @@ public class WeixinPayProxy {
* @throws WeixinException * @throws WeixinException
*/ */
public ApiResult reverse(IdQuery idQuery) throws WeixinException { public ApiResult reverse(IdQuery idQuery) throws WeixinException {
File caFile = new File(ConfigUtil.getClassPathValue("ca_file")); return payApi.reverse(PayApi.DEFAULT_CA_FILE, idQuery);
return payApi.reverse(caFile, idQuery);
} }
/** /**
@ -552,9 +549,8 @@ public class WeixinPayProxy {
*/ */
public CouponResult sendCoupon(String couponStockId, String partnerTradeNo, public CouponResult sendCoupon(String couponStockId, String partnerTradeNo,
String openId) throws WeixinException { String openId) throws WeixinException {
File caFile = new File(ConfigUtil.getClassPathValue("ca_file")); return couponApi.sendCoupon(PayApi.DEFAULT_CA_FILE, couponStockId,
return couponApi.sendCoupon(caFile, couponStockId, partnerTradeNo, partnerTradeNo, openId, null);
openId, null);
} }
/** /**
@ -618,8 +614,7 @@ public class WeixinPayProxy {
*/ */
public RedpacketSendResult sendRedpack(Redpacket redpacket) public RedpacketSendResult sendRedpack(Redpacket redpacket)
throws WeixinException { throws WeixinException {
File caFile = new File(ConfigUtil.getClassPathValue("ca_file")); return cashApi.sendRedpack(PayApi.DEFAULT_CA_FILE, redpacket);
return cashApi.sendRedpack(caFile, redpacket);
} }
/** /**
@ -648,8 +643,7 @@ public class WeixinPayProxy {
*/ */
public RedpacketRecord queryRedpack(String outTradeNo) public RedpacketRecord queryRedpack(String outTradeNo)
throws WeixinException { throws WeixinException {
File caFile = new File(ConfigUtil.getClassPathValue("ca_file")); return cashApi.queryRedpack(PayApi.DEFAULT_CA_FILE, outTradeNo);
return cashApi.queryRedpack(caFile, outTradeNo);
} }
/** /**
@ -679,7 +673,35 @@ public class WeixinPayProxy {
*/ */
public MPPaymentResult mpPayment(MPPayment mpPayment) public MPPaymentResult mpPayment(MPPayment mpPayment)
throws WeixinException { throws WeixinException {
File caFile = new File(ConfigUtil.getClassPathValue("ca_file")); return cashApi.mpPayment(PayApi.DEFAULT_CA_FILE, mpPayment);
return cashApi.mpPayment(caFile, mpPayment); }
/**
* 企业付款查询 用于商户的企业付款操作进行结果查询返回付款操作详细结果
*
* @param caFile
* 证书文件(V3版本后缀为*.p12)
* @param outTradeNo
* 商户调用企业付款API时使用的商户订单号
* @return 付款记录
* @see com.foxinmy.weixin4j.mp.api.CashApi
* @see com.foxinmy.weixin4j.mp.payment.v3.MPPaymentRecord
* @see <a
* href="http://pay.weixin.qq.com/wiki/doc/api/mch_pay.php?chapter=14_3">企业付款查询</a>
* @throws WeixinException
*/
public MPPaymentRecord mpPaymentQuery(File caFile, String outTradeNo)
throws WeixinException {
return cashApi.mpPaymentQuery(caFile, outTradeNo);
}
/**
* 企业付款查询采用properties中配置的ca文件
*
* @see {@link com.foxinmy.weixin4j.mp.WeixinPayProxy#mpPaymentQuery(File, String)}
*/
public MPPaymentRecord mpPaymentQuery(String outTradeNo)
throws WeixinException {
return cashApi.mpPaymentQuery(PayApi.DEFAULT_CA_FILE, outTradeNo);
} }
} }

View File

@ -16,6 +16,7 @@ import com.foxinmy.weixin4j.http.weixin.WeixinResponse;
import com.foxinmy.weixin4j.mp.model.WeixinMpAccount; import com.foxinmy.weixin4j.mp.model.WeixinMpAccount;
import com.foxinmy.weixin4j.mp.payment.PayUtil; import com.foxinmy.weixin4j.mp.payment.PayUtil;
import com.foxinmy.weixin4j.mp.payment.v3.MPPayment; import com.foxinmy.weixin4j.mp.payment.v3.MPPayment;
import com.foxinmy.weixin4j.mp.payment.v3.MPPaymentRecord;
import com.foxinmy.weixin4j.mp.payment.v3.MPPaymentResult; import com.foxinmy.weixin4j.mp.payment.v3.MPPaymentResult;
import com.foxinmy.weixin4j.mp.payment.v3.Redpacket; import com.foxinmy.weixin4j.mp.payment.v3.Redpacket;
import com.foxinmy.weixin4j.mp.payment.v3.RedpacketRecord; import com.foxinmy.weixin4j.mp.payment.v3.RedpacketRecord;
@ -194,4 +195,52 @@ public class CashApi extends MpApi {
.replaceFirst("</mchid>", "</mch_id>"); .replaceFirst("</mchid>", "</mch_id>");
return XmlStream.fromXML(text, MPPaymentResult.class); return XmlStream.fromXML(text, MPPaymentResult.class);
} }
/**
* 企业付款查询 用于商户的企业付款操作进行结果查询返回付款操作详细结果
*
* @param caFile
* 证书文件(V3版本后缀为*.p12)
* @param outTradeNo
* 商户调用企业付款API时使用的商户订单号
* @return 付款记录
* @see com.foxinmy.weixin4j.mp.payment.v3.MPPaymentRecord
* @see <a
* href="http://pay.weixin.qq.com/wiki/doc/api/mch_pay.php?chapter=14_3">企业付款查询</a>
* @throws WeixinException
*/
public MPPaymentRecord mpPaymentQuery(File caFile, String outTradeNo)
throws WeixinException {
JSONObject obj = new JSONObject();
obj.put("nonce_str", RandomUtil.generateString(16));
obj.put("mch_id", weixinAccount.getMchId());
obj.put("appid", weixinAccount.getId());
obj.put("partner_trade_no", outTradeNo);
String sign = PayUtil.paysignMd5(obj, weixinAccount.getPaySignKey());
obj.put("sign", sign);
String param = XmlStream.map2xml(obj);
String mp_payquery_uri = getRequestUri("mp_payquery_uri");
WeixinResponse response = null;
InputStream ca = null;
try {
ca = new FileInputStream(caFile);
SSLHttpClinet request = new SSLHttpClinet(weixinAccount.getMchId(),
ca);
response = request.post(mp_payquery_uri, param);
} catch (WeixinException e) {
throw e;
} catch (IOException e) {
throw new WeixinException(e.getMessage());
} finally {
if (ca != null) {
try {
ca.close();
} catch (IOException e) {
;
}
}
}
return response.getAsObject(new TypeReference<MPPaymentRecord>() {
});
}
} }

View File

@ -18,13 +18,13 @@ import com.foxinmy.weixin4j.util.ConfigUtil;
* @see <a href="http://mp.weixin.qq.com/wiki/index.php">api文档</a> * @see <a href="http://mp.weixin.qq.com/wiki/index.php">api文档</a>
*/ */
public class MpApi extends BaseApi { public class MpApi extends BaseApi {
private final static ResourceBundle WEIXIN_BUNDLE; private final static ResourceBundle WEIXIN_BUNDLE;
/** /**
* 默认使用weixin4j.properties文件中的公众号信息 * 默认使用weixin4j.properties文件中的公众号信息
*/ */
public final static WeixinMpAccount DEFAULT_WEIXIN_ACCOUNT; public final static WeixinMpAccount DEFAULT_WEIXIN_ACCOUNT;
static { static {
WEIXIN_BUNDLE = ResourceBundle WEIXIN_BUNDLE = ResourceBundle
.getBundle("com/foxinmy/weixin4j/mp/api/weixin"); .getBundle("com/foxinmy/weixin4j/mp/api/weixin");

View File

@ -17,6 +17,7 @@ import com.foxinmy.weixin4j.mp.type.BillType;
import com.foxinmy.weixin4j.mp.type.IdQuery; import com.foxinmy.weixin4j.mp.type.IdQuery;
import com.foxinmy.weixin4j.mp.type.SignType; import com.foxinmy.weixin4j.mp.type.SignType;
import com.foxinmy.weixin4j.token.TokenHolder; import com.foxinmy.weixin4j.token.TokenHolder;
import com.foxinmy.weixin4j.util.ConfigUtil;
import com.foxinmy.weixin4j.util.DateUtil; import com.foxinmy.weixin4j.util.DateUtil;
/** /**
@ -31,6 +32,12 @@ import com.foxinmy.weixin4j.util.DateUtil;
*/ */
public abstract class PayApi extends MpApi { public abstract class PayApi extends MpApi {
/**
* 默认的证书文件
*/
public final static File DEFAULT_CA_FILE = new File(
ConfigUtil.getClassPathValue("ca_file"));
protected final WeixinMpAccount weixinAccount; protected final WeixinMpAccount weixinAccount;
protected final TokenHolder tokenHolder; protected final TokenHolder tokenHolder;
@ -93,8 +100,9 @@ public abstract class PayApi extends MpApi {
throws WeixinException { throws WeixinException {
String payfeedback_update_uri = getRequestUri("payfeedback_update_uri"); String payfeedback_update_uri = getRequestUri("payfeedback_update_uri");
Token token = tokenHolder.getToken(); Token token = tokenHolder.getToken();
WeixinResponse response = weixinClient.get(String.format(payfeedback_update_uri, WeixinResponse response = weixinClient.get(String.format(
token.getAccessToken(), openId, feedbackId)); payfeedback_update_uri, token.getAccessToken(), openId,
feedbackId));
return response.getAsJsonResult(); return response.getAsJsonResult();
} }

View File

@ -180,4 +180,6 @@ redpack_send_uri={mch_base_url}/mmpaymkttransfers/sendredpack
# \u67e5\u8be2\u73b0\u91d1\u7ea2\u5305 # \u67e5\u8be2\u73b0\u91d1\u7ea2\u5305
redpack_query_uri={mch_base_url}/mmpaymkttransfers/gethbinfo redpack_query_uri={mch_base_url}/mmpaymkttransfers/gethbinfo
# \u4f01\u4e1a\u5411\u4e2a\u4eba\u4ed8\u6b3e # \u4f01\u4e1a\u5411\u4e2a\u4eba\u4ed8\u6b3e
mp_payment_uri={mch_base_url}/mmpaymkttransfers/promotion/transfers mp_payment_uri={mch_base_url}/mmpaymkttransfers/promotion/transfers
# \u4f01\u4e1a\u4ed8\u6b3e\u67e5\u8be2
mp_payquery_uri={mch_base_url}/mmpaymkttransfers/gettransferinfo

View File

@ -0,0 +1,193 @@
package com.foxinmy.weixin4j.mp.payment.v3;
import java.util.Date;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
import com.alibaba.fastjson.annotation.JSONField;
import com.foxinmy.weixin4j.mp.type.MPPaymentCheckNameType;
import com.foxinmy.weixin4j.util.DateUtil;
/**
* 企业付款记录
*
* @className MPPaymentRecord
* @author jy
* @date 2015年6月23日
* @since JDK 1.7
* @see
*/
@XmlRootElement
@XmlAccessorType(XmlAccessType.FIELD)
public class MPPaymentRecord extends ApiResult {
private static final long serialVersionUID = -1926873539419750498L;
/**
* 微信订单订单号
*/
@JSONField(name = "detail_id")
@XmlElement(name = "detail_id")
private String transactionId;
/**
* 商户订单号
*/
@JSONField(name = "partner_trade_no")
@XmlElement(name = "partner_trade_no")
private String outTradeNo;
/**
* 交易状态 SUCCESS:转账成功 FAILED:转账失败
*/
@JSONField(name = "status")
@XmlElement(name = "status")
private String transactionStatus;
/**
* 如果失败则应该有原因
*/
@JSONField(name = "reason")
@XmlElement(name = "reason")
private String failureReason;
/**
* 收款用户openid
*/
private String openid;
/**
* 收款用户姓名
*/
@JSONField(name = "transfer_name")
@XmlElement(name = "transfer_name")
private String transferName;
/**
* 付款金额(单位为分)
*/
@JSONField(name = "payment_amount")
@XmlElement(name = "payment_amount")
private int paymentAmount;
/**
* 转账时间
*/
@JSONField(name = "transfer_time")
@XmlElement(name = "transfer_time")
private String transferTime;
/**
* 校验用户姓名选项
*
* @see com.foxinmy.weixin4j.mp.type.MPPaymentCheckNameType
*/
@XmlElement(name = "check_name")
@JSONField(name = "check_name")
private MPPaymentCheckNameType checkNameType;
/**
* 企业付款描述信息
*/
private String desc;
/**
* 实名验证结果 PASS:通过 FAILED:不通过
*/
@JSONField(name = "check_name_result")
@XmlElement(name = "check_name_result")
private String checkNameResult;
protected MPPaymentRecord() {
// jaxb required
}
public String getTransactionId() {
return transactionId;
}
public String getOutTradeNo() {
return outTradeNo;
}
public String getTransactionStatus() {
return transactionStatus;
}
/**
* 格式化交易状态
*
* @return
*/
@JSONField(serialize = false)
public boolean getFormatTransactionStatus() {
return "success".equalsIgnoreCase(transactionStatus);
}
public String getFailureReason() {
return failureReason;
}
public String getOpenid() {
return openid;
}
public String getTransferName() {
return transferName;
}
public int getPaymentAmount() {
return paymentAmount;
}
/**
* <font color="red">调用接口获取单位为分,get方法转换为元方便使用</font>
*
* @return 元单位
*/
@JSONField(serialize = false)
public double getFormatPaymentAmount() {
return paymentAmount / 100d;
}
public String getTransferTime() {
return transferTime;
}
/**
* 格式化转账时间
*
* @return
*/
@JSONField(serialize = false)
public Date getFormatTransferTime() {
return DateUtil.parse2yyyyMMddHHmmss(transferTime);
}
public MPPaymentCheckNameType getCheckNameType() {
return checkNameType;
}
public String getDesc() {
return desc;
}
public String getCheckNameResult() {
return checkNameResult;
}
/**
* 格式化交易状态
*
* @return
*/
@JSONField(serialize = false)
public boolean getFormatCheckNameResult() {
return "pass".equalsIgnoreCase(checkNameResult);
}
@Override
public String toString() {
return "MPPaymentRecord [transactionId=" + transactionId
+ ", outTradeNo=" + outTradeNo + ", transactionStatus="
+ getFormatTransactionStatus() + ", failureReason="
+ failureReason + ", openid=" + openid + ", transferName="
+ transferName + ", paymentAmount=" + getFormatPaymentAmount()
+ ", transferTime=" + transferTime + ", checkNameType="
+ checkNameType + ", desc=" + desc + ", checkNameResult="
+ getFormatCheckNameResult() + ", " + super.toString() + "]";
}
}

View File

@ -48,7 +48,7 @@ public class SuiteTicketHolder {
* @param suiteTicket * @param suiteTicket
* @throws WeixinException * @throws WeixinException
*/ */
public void cachingTicket(SuiteTicketMessage suiteTicket) public void cachingTicket(WeixinSuiteMessage suiteTicket)
throws WeixinException { throws WeixinException {
Token token = new Token(suiteTicket.getSuiteTicket()); Token token = new Token(suiteTicket.getSuiteTicket());
token.setExpiresIn(-1); token.setExpiresIn(-1);

View File

@ -7,9 +7,18 @@ import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElement; import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement; import javax.xml.bind.annotation.XmlRootElement;
/**
* 套件消息
*
* @className WeixinSuiteMessage
* @author jy
* @date 2015年6月23日
* @since JDK 1.7
* @see
*/
@XmlRootElement(name = "xml") @XmlRootElement(name = "xml")
@XmlAccessorType(XmlAccessType.FIELD) @XmlAccessorType(XmlAccessType.FIELD)
public class SuiteTicketMessage implements Serializable { public class WeixinSuiteMessage implements Serializable {
private static final long serialVersionUID = 6457919241019021514L; private static final long serialVersionUID = 6457919241019021514L;
/** /**
@ -60,7 +69,7 @@ public class SuiteTicketMessage implements Serializable {
@Override @Override
public String toString() { public String toString() {
return "SuiteTicketMessage [suiteId=" + suiteId + ", eventType=" return "WeixinSuiteMessage [suiteId=" + suiteId + ", eventType="
+ eventType + ", timeStamp=" + timeStamp + ", SuiteTicket=" + eventType + ", timeStamp=" + timeStamp + ", SuiteTicket="
+ SuiteTicket + ", authCorpId=" + authCorpId + "]"; + SuiteTicket + ", authCorpId=" + authCorpId + "]";
} }

View File

@ -3,6 +3,7 @@ package com.foxinmy.weixin4j.request;
import java.io.Serializable; import java.io.Serializable;
import com.foxinmy.weixin4j.type.EncryptType; import com.foxinmy.weixin4j.type.EncryptType;
import com.foxinmy.weixin4j.util.AesToken;
/** /**
* 微信请求 * 微信请求
@ -60,10 +61,15 @@ public class WeixinRequest implements Serializable, Cloneable {
* xml消息密文主体(AES时存在) * xml消息密文主体(AES时存在)
*/ */
private String encryptContent; private String encryptContent;
/**
* aes & token
*/
private AesToken aesToken;
public WeixinRequest(String method, EncryptType encryptType, public WeixinRequest(String method, EncryptType encryptType,
String echoStr, String timeStamp, String nonce, String signature, String echoStr, String timeStamp, String nonce, String signature,
String msgSignature, String originalContent, String encryptContent) { String msgSignature, String originalContent, String encryptContent,
AesToken aesToken) {
this.method = method; this.method = method;
this.encryptType = encryptType; this.encryptType = encryptType;
this.echoStr = echoStr; this.echoStr = echoStr;
@ -73,6 +79,7 @@ public class WeixinRequest implements Serializable, Cloneable {
this.msgSignature = msgSignature; this.msgSignature = msgSignature;
this.originalContent = originalContent; this.originalContent = originalContent;
this.encryptContent = encryptContent; this.encryptContent = encryptContent;
this.aesToken = aesToken;
} }
public String getMethod() { public String getMethod() {
@ -111,12 +118,17 @@ public class WeixinRequest implements Serializable, Cloneable {
return encryptContent; return encryptContent;
} }
public AesToken getAesToken() {
return aesToken;
}
@Override @Override
public String toString() { public String toString() {
return "WeixinRequest [encryptContent=" + encryptContent return "WeixinRequest [encryptContent=" + encryptContent
+ ", encryptType=" + encryptType + ", echoStr=" + echoStr + ", encryptType=" + encryptType + ", echoStr=" + echoStr
+ ", timeStamp=" + timeStamp + ", nonce=" + nonce + ", timeStamp=" + timeStamp + ", nonce=" + nonce
+ ", signature=" + signature + ", originalContent=" + ", signature=" + signature + ", originalContent="
+ originalContent + ", method=" + method + "]"; + originalContent + ", method=" + method + ", aesToken="
+ aesToken + "]";
} }
} }

View File

@ -0,0 +1,19 @@
package com.foxinmy.weixin4j.response;
/**
* 单一的字符串回复,如回复SUCCESS
*
* @className SingleResponse
* @author jy
* @date 2015年6月23日
* @since JDK 1.7
* @see
*/
public interface SingleResponse {
/**
* 回复内容
*
* @return
*/
public String toContent();
}

View File

@ -1,5 +1,6 @@
package com.foxinmy.weixin4j.response; package com.foxinmy.weixin4j.response;
/** /**
* 微信被动消息回复 * 微信被动消息回复
* *
@ -20,18 +21,11 @@ package com.foxinmy.weixin4j.response;
* @see <a * @see <a
* href="http://qydev.weixin.qq.com/wiki/index.php?title=%E8%A2%AB%E5%8A%A8%E5%93%8D%E5%BA%94%E6%B6%88%E6%81%AF">企业号的被动响应消息</a> * href="http://qydev.weixin.qq.com/wiki/index.php?title=%E8%A2%AB%E5%8A%A8%E5%93%8D%E5%BA%94%E6%B6%88%E6%81%AF">企业号的被动响应消息</a>
*/ */
public interface WeixinResponse { public interface WeixinResponse extends SingleResponse {
/** /**
* 消息类型 * 回复的消息类型
* *
* @return * @return
*/ */
public String getMsgType(); public String getMsgType();
/**
* 消息内容
*
* @return
*/
public String toContent();
} }

View File

@ -0,0 +1,62 @@
package com.foxinmy.weixin4j.socket;
import java.io.Serializable;
import com.foxinmy.weixin4j.type.EncryptType;
import com.foxinmy.weixin4j.util.AesToken;
/**
* 消息传递
*
* @className MessageTransfer
* @author jy
* @date 2015年6月23日
* @since JDK 1.7
* @see
*/
public class MessageTransfer implements Serializable {
private static final long serialVersionUID = 7779948135156353261L;
/**
* aes & token
*/
private AesToken aesToken;
/**
* 加密类型
*/
private EncryptType encryptType;
/**
* 消息接收方
*/
private String toUserName;
/**
* 消息发送方
*/
private String fromUserName;
public MessageTransfer(AesToken aesToken, EncryptType encryptType,
String toUserName, String fromUserName) {
this.aesToken = aesToken;
this.encryptType = encryptType;
this.toUserName = toUserName;
this.fromUserName = fromUserName;
}
public AesToken getAesToken() {
return aesToken;
}
public EncryptType getEncryptType() {
return encryptType;
}
public String getToUserName() {
return toUserName;
}
public String getFromUserName() {
return fromUserName;
}
}

View File

@ -35,10 +35,10 @@ public class WeixinMessageDecoder extends
private final InternalLogger logger = InternalLoggerFactory private final InternalLogger logger = InternalLoggerFactory
.getInstance(getClass()); .getInstance(getClass());
private AesToken aesToken; private Map<String, AesToken> aesTokenMap;
public WeixinMessageDecoder(AesToken aesToken) { public WeixinMessageDecoder(Map<String, AesToken> aesTokenMap) {
this.aesToken = aesToken; this.aesTokenMap = aesTokenMap;
} }
@Override @Override
@ -47,8 +47,9 @@ public class WeixinMessageDecoder extends
String content = req.content().toString(Consts.UTF_8); String content = req.content().toString(Consts.UTF_8);
QueryStringDecoder queryDecoder = new QueryStringDecoder(req.getUri(), QueryStringDecoder queryDecoder = new QueryStringDecoder(req.getUri(),
true); true);
String methodName = req.getMethod().name();
logger.info("decode request:{} use {} method invoking", req.getUri(), logger.info("decode request:{} use {} method invoking", req.getUri(),
req.getMethod().name()); methodName);
Map<String, List<String>> parameters = queryDecoder.parameters(); Map<String, List<String>> parameters = queryDecoder.parameters();
EncryptType encryptType = parameters.containsKey("encrypt_type") ? EncryptType EncryptType encryptType = parameters.containsKey("encrypt_type") ? EncryptType
.valueOf(parameters.get("encrypt_type").get(0).toUpperCase()) .valueOf(parameters.get("encrypt_type").get(0).toUpperCase())
@ -63,22 +64,23 @@ public class WeixinMessageDecoder extends
.get("signature").get(0) : ""; .get("signature").get(0) : "";
String msgSignature = parameters.containsKey("msg_signature") ? parameters String msgSignature = parameters.containsKey("msg_signature") ? parameters
.get("msg_signature").get(0) : ""; .get("msg_signature").get(0) : "";
String weixinId = parameters.containsKey("weixin_id") ? parameters.get(
"weixin_id").get(0) : null;
AesToken aesToken = aesTokenMap.get(weixinId);
String originalContent = content; String originalContent = content;
String encryptContent = null; String encryptContent = null;
if (!content.isEmpty()) { if (!content.isEmpty() && encryptType == EncryptType.AES) {
if (encryptType == EncryptType.AES) { if (StringUtil.isBlank(aesToken.getAesKey())
if (StringUtil.isBlank(aesToken.getAesKey()) || StringUtil.isBlank(aesToken.getWeixinId())) {
|| StringUtil.isBlank(aesToken.getAppid())) { throw new WeixinException(
throw new WeixinException( "AESEncodingKey or WeixinId not be null in AES mode");
"AESEncodingKey or AppId not be null in AES mode");
}
encryptContent = EncryptMessageHandler.parser(content);
originalContent = MessageUtil.aesDecrypt(aesToken.getAppid(),
aesToken.getAesKey(), encryptContent);
} }
encryptContent = EncryptMessageHandler.parser(content);
originalContent = MessageUtil.aesDecrypt(aesToken.getWeixinId(),
aesToken.getAesKey(), encryptContent);
} }
out.add(new WeixinRequest(req.getMethod().name(), encryptType, echoStr, out.add(new WeixinRequest(methodName, encryptType, echoStr, timeStamp,
timeStamp, nonce, signature, msgSignature, originalContent, nonce, signature, msgSignature, originalContent,
encryptContent)); encryptContent, aesToken));
} }
} }

View File

@ -18,7 +18,6 @@ import com.foxinmy.weixin4j.util.AesToken;
import com.foxinmy.weixin4j.util.Consts; import com.foxinmy.weixin4j.util.Consts;
import com.foxinmy.weixin4j.util.HttpUtil; import com.foxinmy.weixin4j.util.HttpUtil;
import com.foxinmy.weixin4j.util.MessageUtil; import com.foxinmy.weixin4j.util.MessageUtil;
import com.foxinmy.weixin4j.util.StringUtil;
import com.foxinmy.weixin4j.xml.CruxMessageHandler; import com.foxinmy.weixin4j.xml.CruxMessageHandler;
/** /**
@ -34,12 +33,11 @@ public class WeixinRequestHandler extends
SimpleChannelInboundHandler<WeixinRequest> { SimpleChannelInboundHandler<WeixinRequest> {
private final InternalLogger logger = InternalLoggerFactory private final InternalLogger logger = InternalLoggerFactory
.getInstance(getClass()); .getInstance(getClass());
private final AesToken aesToken;
private final WeixinMessageDispatcher messageDispatcher; private final WeixinMessageDispatcher messageDispatcher;
public WeixinRequestHandler(AesToken aesToken, public WeixinRequestHandler(WeixinMessageDispatcher messageDispatcher)
WeixinMessageDispatcher messageDispatcher) throws WeixinException { throws WeixinException {
this.aesToken = aesToken;
this.messageDispatcher = messageDispatcher; this.messageDispatcher = messageDispatcher;
} }
@ -56,6 +54,7 @@ public class WeixinRequestHandler extends
@Override @Override
protected void channelRead0(ChannelHandlerContext ctx, WeixinRequest request) protected void channelRead0(ChannelHandlerContext ctx, WeixinRequest request)
throws WeixinException { throws WeixinException {
final AesToken aesToken = request.getAesToken();
if (request.getMethod().equals(HttpMethod.GET.name())) { if (request.getMethod().equals(HttpMethod.GET.name())) {
if (MessageUtil.signature(aesToken.getToken(), if (MessageUtil.signature(aesToken.getToken(),
request.getTimeStamp(), request.getNonce()).equals( request.getTimeStamp(), request.getNonce()).equals(
@ -98,14 +97,10 @@ public class WeixinRequestHandler extends
} }
CruxMessageHandler cruxMessage = CruxMessageHandler.parser(request CruxMessageHandler cruxMessage = CruxMessageHandler.parser(request
.getOriginalContent()); .getOriginalContent());
ctx.channel().attr(Consts.ENCRYPTTYPE_KEY) MessageTransfer messageTransfer = new MessageTransfer(aesToken,
.set(request.getEncryptType()); request.getEncryptType(), cruxMessage.getToUserName(),
ctx.channel().attr(Consts.USEROPENID_KEY) cruxMessage.getFromUserName());
.set(cruxMessage.getFromUserName()); ctx.channel().attr(Consts.MESSAGE_TRANSFER_KEY).set(messageTransfer);
if (StringUtil.isBlank(aesToken.getAppid())) {
ctx.channel().attr(Consts.ACCOUNTOPENID_KEY)
.set(cruxMessage.getToUserName());
}
messageDispatcher.doDispatch(ctx, request, cruxMessage); messageDispatcher.doDispatch(ctx, request, cruxMessage);
} }
} }

View File

@ -36,22 +36,16 @@ public class WeixinResponseEncoder extends
private final InternalLogger logger = InternalLoggerFactory private final InternalLogger logger = InternalLoggerFactory
.getInstance(getClass()); .getInstance(getClass());
private final AesToken aesToken;
public WeixinResponseEncoder(AesToken aesToken) {
this.aesToken = aesToken;
}
@Override @Override
protected void encode(ChannelHandlerContext ctx, WeixinResponse response, protected void encode(ChannelHandlerContext ctx, WeixinResponse response,
List<Object> out) throws WeixinException { List<Object> out) throws WeixinException {
EncryptType encryptType = ctx.channel().attr(Consts.ENCRYPTTYPE_KEY) MessageTransfer messageTransfer = ctx.channel()
.get(); .attr(Consts.MESSAGE_TRANSFER_KEY).get();
String userOpenId = ctx.channel().attr(Consts.USEROPENID_KEY).get(); AesToken aesToken = messageTransfer.getAesToken();
String accountOpenId = ctx.channel().attr(Consts.ACCOUNTOPENID_KEY) EncryptType encryptType = messageTransfer.getEncryptType();
.get(); String weixinId = aesToken.getWeixinId();
if (StringUtil.isBlank(accountOpenId)) { if (StringUtil.isBlank(weixinId)) {
accountOpenId = aesToken.getAppid(); weixinId = messageTransfer.getToUserName();
} }
StringBuilder content = new StringBuilder(); StringBuilder content = new StringBuilder();
if (response instanceof BlankResponse) { if (response instanceof BlankResponse) {
@ -59,10 +53,10 @@ public class WeixinResponseEncoder extends
} else { } else {
content.append("<xml>"); content.append("<xml>");
content.append(String.format( content.append(String.format(
"<ToUserName><![CDATA[%s]]></ToUserName>", userOpenId)); "<ToUserName><![CDATA[%s]]></ToUserName>",
messageTransfer.getFromUserName()));
content.append(String.format( content.append(String.format(
"<FromUserName><![CDATA[%s]]></FromUserName>", "<FromUserName><![CDATA[%s]]></FromUserName>", weixinId));
accountOpenId));
content.append(String.format( content.append(String.format(
"<CreateTime><![CDATA[%d]]></CreateTime>", "<CreateTime><![CDATA[%d]]></CreateTime>",
System.currentTimeMillis() / 1000l)); System.currentTimeMillis() / 1000l));
@ -74,7 +68,7 @@ public class WeixinResponseEncoder extends
String nonce = RandomUtil.generateString(32); String nonce = RandomUtil.generateString(32);
String timestamp = String String timestamp = String
.valueOf(System.currentTimeMillis() / 1000l); .valueOf(System.currentTimeMillis() / 1000l);
String encrtypt = MessageUtil.aesEncrypt(accountOpenId, String encrtypt = MessageUtil.aesEncrypt(weixinId,
aesToken.getAesKey(), content.toString()); aesToken.getAesKey(), content.toString());
String msgSignature = MessageUtil.signature( String msgSignature = MessageUtil.signature(
aesToken.getToken(), nonce, timestamp, encrtypt); aesToken.getToken(), nonce, timestamp, encrtypt);

View File

@ -6,6 +6,8 @@ import io.netty.channel.socket.SocketChannel;
import io.netty.handler.codec.http.HttpObjectAggregator; import io.netty.handler.codec.http.HttpObjectAggregator;
import io.netty.handler.codec.http.HttpServerCodec; import io.netty.handler.codec.http.HttpServerCodec;
import java.util.Map;
import com.foxinmy.weixin4j.dispatcher.WeixinMessageDispatcher; import com.foxinmy.weixin4j.dispatcher.WeixinMessageDispatcher;
import com.foxinmy.weixin4j.exception.WeixinException; import com.foxinmy.weixin4j.exception.WeixinException;
import com.foxinmy.weixin4j.util.AesToken; import com.foxinmy.weixin4j.util.AesToken;
@ -21,15 +23,15 @@ import com.foxinmy.weixin4j.util.AesToken;
*/ */
public class WeixinServerInitializer extends ChannelInitializer<SocketChannel> { public class WeixinServerInitializer extends ChannelInitializer<SocketChannel> {
private final AesToken aesToken; private final Map<String, AesToken> aesTokenMap;
private final WeixinMessageDispatcher messageDispatcher; private final WeixinMessageDispatcher messageDispatcher;
public WeixinServerInitializer(AesToken aesToken, public WeixinServerInitializer(Map<String, AesToken> aesTokenMap,
WeixinMessageDispatcher messageDispatcher) throws WeixinException { WeixinMessageDispatcher messageDispatcher) throws WeixinException {
if (aesToken == null) { if (aesTokenMap.isEmpty()) {
throw new WeixinException("AesToken not be null."); throw new WeixinException("AesToken not be null.");
} }
this.aesToken = aesToken; this.aesTokenMap = aesTokenMap;
this.messageDispatcher = messageDispatcher; this.messageDispatcher = messageDispatcher;
} }
@ -38,8 +40,8 @@ public class WeixinServerInitializer extends ChannelInitializer<SocketChannel> {
ChannelPipeline pipeline = channel.pipeline(); ChannelPipeline pipeline = channel.pipeline();
pipeline.addLast(new HttpServerCodec()); pipeline.addLast(new HttpServerCodec());
pipeline.addLast(new HttpObjectAggregator(65536)); pipeline.addLast(new HttpObjectAggregator(65536));
pipeline.addLast(new WeixinMessageDecoder(aesToken)); pipeline.addLast(new WeixinMessageDecoder(aesTokenMap));
pipeline.addLast(new WeixinResponseEncoder(aesToken)); pipeline.addLast(new WeixinResponseEncoder());
pipeline.addLast(new WeixinRequestHandler(aesToken, messageDispatcher)); pipeline.addLast(new WeixinRequestHandler(messageDispatcher));
} }
} }

View File

@ -11,8 +11,10 @@ import io.netty.util.internal.logging.InternalLogger;
import io.netty.util.internal.logging.InternalLoggerFactory; import io.netty.util.internal.logging.InternalLoggerFactory;
import java.util.Arrays; import java.util.Arrays;
import java.util.HashMap;
import java.util.LinkedList; import java.util.LinkedList;
import java.util.List; import java.util.List;
import java.util.Map;
import com.foxinmy.weixin4j.dispatcher.BeanFactory; import com.foxinmy.weixin4j.dispatcher.BeanFactory;
import com.foxinmy.weixin4j.dispatcher.DefaultMessageMatcher; import com.foxinmy.weixin4j.dispatcher.DefaultMessageMatcher;
@ -54,7 +56,7 @@ public final class WeixinServerBootstrap {
/** /**
* 服务启动的默认端口 * 服务启动的默认端口
*/ */
public final static int DEFAULT_SERVERPORT = 30000; public final static int DEFAULT_SERVERPORT = 80;
/** /**
* 消息分发器 * 消息分发器
*/ */
@ -73,16 +75,19 @@ public final class WeixinServerBootstrap {
* aes and token * aes and token
* *
*/ */
private final AesToken aesToken; private final Map<String, AesToken> aesTokenMap;
/** /**
* 明文模式 * 明文模式
* *
* * @param token 开发者token * @param openid
* 微信号(原始ID)
* @param token
* 开发者token
* *
*/ */
public WeixinServerBootstrap(String token) { public WeixinServerBootstrap(String openid, String token) {
this(new AesToken(token)); this(openid, token, null);
} }
/** /**
@ -105,12 +110,32 @@ public final class WeixinServerBootstrap {
public WeixinServerBootstrap(AesToken aesToken, public WeixinServerBootstrap(AesToken aesToken,
WeixinMessageMatcher messageMatcher) { WeixinMessageMatcher messageMatcher) {
this.aesToken = aesToken; this.aesTokenMap = new HashMap<String, AesToken>();
this.aesTokenMap.put(aesToken.getWeixinId(), aesToken);
this.aesTokenMap.put(null, aesToken);
this.messageHandlerList = new LinkedList<WeixinMessageHandler>(); this.messageHandlerList = new LinkedList<WeixinMessageHandler>();
this.messageInterceptorList = new LinkedList<WeixinMessageInterceptor>(); this.messageInterceptorList = new LinkedList<WeixinMessageInterceptor>();
this.messageDispatcher = new WeixinMessageDispatcher(messageMatcher); this.messageDispatcher = new WeixinMessageDispatcher(messageMatcher);
} }
/**
* 多个公众号的支持
* <p>
* <font color="red">请注意需在服务接收事件的URL中附加一个名为wexin_id的参数,其值视加密模式而定,
* 如为明文模式weixin_id则填写公众号的微信号(即原始ID),如为AES加密模式weixin_id则填写公众号的应用ID(即appid)
* </font>
* <p>
*
* @param aesTokens
* @return
*/
public WeixinServerBootstrap multAesToken(AesToken... aesTokens) {
for (AesToken aesToken : aesTokens) {
this.aesTokenMap.put(aesToken.getWeixinId(), aesToken);
}
return this;
}
/** /**
* 默认端口启动服务 * 默认端口启动服务
* *
@ -144,7 +169,7 @@ public final class WeixinServerBootstrap {
.channel(NioServerSocketChannel.class) .channel(NioServerSocketChannel.class)
.handler(new LoggingHandler()) .handler(new LoggingHandler())
.childHandler( .childHandler(
new WeixinServerInitializer(aesToken, new WeixinServerInitializer(aesTokenMap,
messageDispatcher)); messageDispatcher));
Channel ch = b.bind(serverPort).sync().channel(); Channel ch = b.bind(serverPort).sync().channel();
logger.info("weixin4j server startup OK:{}", serverPort); logger.info("weixin4j server startup OK:{}", serverPort);

View File

@ -0,0 +1,26 @@
package com.foxinmy.weixin4j.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,23 @@
package com.foxinmy.weixin4j.suite;
import com.foxinmy.weixin4j.response.SingleResponse;
/**
* 处理第三方应用套件请求
*
* @className SuiteMessageHandler
* @author jy
* @date 2015年6月23日
* @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 interface SuiteMessageHandler {
/**
* 处理套件消息
*
* @param suiteMessage
* @return
*/
public SingleResponse handle(WeixinSuiteMessage suiteMessage);
}

View File

@ -0,0 +1,76 @@
package com.foxinmy.weixin4j.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;
/**
* 套件消息
*
* @className WeixinSuiteMessage
* @author jy
* @date 2015年6月23日
* @since JDK 1.7
* @see
*/
@XmlRootElement(name = "xml")
@XmlAccessorType(XmlAccessType.FIELD)
public class WeixinSuiteMessage 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 "WeixinSuiteMessage [suiteId=" + suiteId + ", eventType="
+ eventType + ", timeStamp=" + timeStamp + ", SuiteTicket="
+ SuiteTicket + ", authCorpId=" + authCorpId + "]";
}
}

View File

@ -16,9 +16,9 @@ public class AesToken implements Serializable {
private static final long serialVersionUID = -6001008896414323534L; private static final long serialVersionUID = -6001008896414323534L;
/** /**
* 账号ID * 账号ID(原始ID或者appid)
*/ */
private String appid; private String weixinId;
/** /**
* 开发者的token * 开发者的token
*/ */
@ -28,18 +28,36 @@ public class AesToken implements Serializable {
*/ */
private String aesKey; private String aesKey;
public AesToken(String token) { /**
this.token = token; * 一般为明文模式
*
* @param openid
* 微信号(原始ID)
* @param token
* 开发者的Token
*/
public AesToken(String openid, String token) {
this(openid, token, null);
} }
/**
* 一般为AES加密模式
*
* @param appid
* 应用ID
* @param token
* 开发者Token
* @param aesKey
* 解密的EncodingAESKey
*/
public AesToken(String appid, String token, String aesKey) { public AesToken(String appid, String token, String aesKey) {
this.appid = appid; this.weixinId = appid;
this.token = token; this.token = token;
this.aesKey = aesKey; this.aesKey = aesKey;
} }
public String getAppid() { public String getWeixinId() {
return appid; return weixinId;
} }
public String getToken() { public String getToken() {

View File

@ -4,7 +4,7 @@ import io.netty.util.AttributeKey;
import java.nio.charset.Charset; import java.nio.charset.Charset;
import com.foxinmy.weixin4j.type.EncryptType; import com.foxinmy.weixin4j.socket.MessageTransfer;
/** /**
* 常量类 * 常量类
@ -35,10 +35,6 @@ public final class Consts {
public static final String CONTENTTYPE$APPLICATION_XML = "application/xml"; public static final String CONTENTTYPE$APPLICATION_XML = "application/xml";
public static final String CONTENTTYPE$TEXT_PLAIN = "text/plain"; public static final String CONTENTTYPE$TEXT_PLAIN = "text/plain";
public static final AttributeKey<EncryptType> ENCRYPTTYPE_KEY = AttributeKey public static final AttributeKey<MessageTransfer> MESSAGE_TRANSFER_KEY = AttributeKey
.valueOf("ENCRYPTTYPE"); .valueOf("$_MESSAGETRANSFER");
public static final AttributeKey<String> ACCOUNTOPENID_KEY = AttributeKey
.valueOf("ACCOUNTOPENID");
public static final AttributeKey<String> USEROPENID_KEY = AttributeKey
.valueOf("USEROPENID");
} }

View File

@ -23,11 +23,13 @@ import com.foxinmy.weixin4j.util.Consts;
*/ */
public class EncryptMessageHandler extends DefaultHandler { public class EncryptMessageHandler extends DefaultHandler {
private String toUserName;
private String encryptContent; private String encryptContent;
private String content; private String content;
@Override @Override
public void startDocument() throws SAXException { public void startDocument() throws SAXException {
toUserName = null;
encryptContent = null; encryptContent = null;
} }
@ -42,6 +44,8 @@ public class EncryptMessageHandler extends DefaultHandler {
throws SAXException { throws SAXException {
if (localName.equalsIgnoreCase("encrypt")) { if (localName.equalsIgnoreCase("encrypt")) {
encryptContent = content; encryptContent = content;
} else if (localName.equalsIgnoreCase("tousername")) {
toUserName = content;
} }
} }
@ -51,6 +55,10 @@ public class EncryptMessageHandler extends DefaultHandler {
this.content = new String(ch, start, length); this.content = new String(ch, start, length);
} }
public String getToUserName() {
return toUserName;
}
public String getEncryptContent() { public String getEncryptContent() {
return encryptContent; return encryptContent;
} }

View File

@ -2,8 +2,6 @@ package com.foxinmy.weixin4j.server.test;
import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelHandlerContext;
import java.math.BigDecimal;
import com.foxinmy.weixin4j.exception.WeixinException; import com.foxinmy.weixin4j.exception.WeixinException;
import com.foxinmy.weixin4j.handler.BlankMessageHandler; import com.foxinmy.weixin4j.handler.BlankMessageHandler;
import com.foxinmy.weixin4j.handler.DebugMessageHandler; import com.foxinmy.weixin4j.handler.DebugMessageHandler;
@ -27,6 +25,8 @@ import com.foxinmy.weixin4j.startup.WeixinServerBootstrap;
*/ */
public class MessageServerStartup { public class MessageServerStartup {
// 微信号(原始ID)
final String openid = "gh_22b350df957b";
// 应用ID // 应用ID
final String appid = "wx4ab8f8de58159a57"; final String appid = "wx4ab8f8de58159a57";
// 开发者token // 开发者token
@ -41,8 +41,8 @@ public class MessageServerStartup {
*/ */
public void test1() throws WeixinException { public void test1() throws WeixinException {
// 所有请求都回复调试的文本消息 // 所有请求都回复调试的文本消息
new WeixinServerBootstrap(token).addHandler(DebugMessageHandler.global) new WeixinServerBootstrap(openid, token).addHandler(
.startup(); DebugMessageHandler.global).startup();
} }
/** /**
@ -78,8 +78,8 @@ public class MessageServerStartup {
public void test4() throws WeixinException { public void test4() throws WeixinException {
// 扫描包加载消息处理器 // 扫描包加载消息处理器
String packageToScan = "com.foxinmy.weixin4j.handler"; String packageToScan = "com.foxinmy.weixin4j.handler";
new WeixinServerBootstrap(token).handlerPackagesToScan(packageToScan) new WeixinServerBootstrap(openid, token).handlerPackagesToScan(
.startup(); packageToScan).startup();
} }
public void test5() throws WeixinException { public void test5() throws WeixinException {
@ -109,14 +109,11 @@ public class MessageServerStartup {
System.err.println("请求处理完毕"); System.err.println("请求处理完毕");
} }
}; };
new WeixinServerBootstrap(token).addInterceptor(interceptor) new WeixinServerBootstrap(openid, token).addInterceptor(interceptor)
.addHandler(BlankMessageHandler.global).startup(); .addHandler(BlankMessageHandler.global).startup();
} }
public static void main(String[] args) throws Exception { public static void main(String[] args) throws Exception {
new MessageServerStartup().test1();
System.err.println(new BigDecimal(new Long(14212345l)).divide(
new BigDecimal("100000"))
.toString());
} }
} }