18 Commits

Author SHA1 Message Date
Daniel Qian
780074f219 upgrade version 2015-12-03 12:47:38 +08:00
Daniel Qian
59bdad92f6 fix: java doc incorrectness 2015-12-03 12:45:52 +08:00
Daniel Qian
89a260dded Merge pull request #241 from videome/develop
Improve error message of the failure of getting prepayid.
2015-11-30 14:24:41 +08:00
Kane Zhu
b19dc7b0fb Improve error message of the failure of getting prepayid. 2015-11-28 21:49:32 +08:00
Daniel Qian
379aae95f1 issue #209 2015-11-25 08:46:59 +08:00
Daniel Qian
6c45fdacdb Merge branch 'master' into develop 2015-11-24 13:11:24 +08:00
Daniel Qian
5f456eb521 issue #236 2015-11-24 13:10:47 +08:00
Daniel Qian
27ce2a2746 Merge pull request #233 from videome/develop
Support sending redpack.
2015-11-20 14:51:46 +08:00
Daniel Qian
d5b3239a01 Update README.md 2015-11-20 10:07:26 +08:00
Daniel Qian
7972733feb Update README.md 2015-11-19 13:45:13 +08:00
Kane Zhu
30f22896b1 Support sending redpack. 2015-11-17 19:39:59 +08:00
Daniel Qian
d0a452904a Merge pull request #216 from videome/develop
Improve 'templateSend' method to check the response code to see if it…
2015-10-23 15:16:37 +08:00
Daniel Qian
b5f0ee75b0 Merge pull request #218 from ukid/develop
票据验证方法
2015-10-23 15:15:30 +08:00
ukid
123e5ef973 票据验证方法 2015-10-23 14:33:32 +08:00
Kane Zhu
23c96d9076 Improve 'templateSend' method to check the response code to see if it's successful or not. 2015-10-22 18:51:31 +08:00
Daniel Qian
9363a67538 upgrade to 1.3.2-SNAPSHOT 2015-10-16 10:13:37 +08:00
Daniel Qian
7b3a3a4c2f Merge branch 'develop' 2015-10-16 10:08:45 +08:00
Daniel Qian
293ef256b6 Merge branch 'develop' 2015-09-11 09:09:36 +08:00
15 changed files with 308 additions and 26 deletions

View File

@@ -2,6 +2,12 @@ weixin-java-tools
[![Build Status](https://travis-ci.org/chanjarster/weixin-java-tools.svg?branch=develop)](https://travis-ci.org/chanjarster/weixin-java-tools)
![Maven Central](https://img.shields.io/maven-central/v/me.chanjar/weixin-java-parent.svg)
[![Gitter](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/chanjarster/weixin-java-tools?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge)
===========
* 群/聊天室请点击上方的GITTER按钮
* 本项目的发布周期本人子2015年6月开始进入到了一个非常忙碌的状态且公司目前也没有开发微信相关的项目因此本项目的维护基本靠“等”。所以靠人不如靠自己提交pull request吧。
===========
@@ -17,7 +23,7 @@ weixin-java-tools
<dependency>
<groupId>me.chanjar</groupId>
<artifactId>weixin-java-mp</artifactId>
<version>1.3.1</version>
<version>1.3.2</version>
</dependency>
```
@@ -27,7 +33,7 @@ weixin-java-tools
<dependency>
<groupId>me.chanjar</groupId>
<artifactId>weixin-java-cp</artifactId>
<version>1.3.1</version>
<version>1.3.2</version>
</dependency>
```

View File

@@ -5,7 +5,7 @@
<modelVersion>4.0.0</modelVersion>
<groupId>me.chanjar</groupId>
<artifactId>weixin-java-parent</artifactId>
<version>1.3.1</version>
<version>1.3.2</version>
<packaging>pom</packaging>
<name>WeiXin Java Tools - Parent</name>
<description>微信公众号、企业号上级POM</description>

View File

@@ -6,7 +6,7 @@
<parent>
<groupId>me.chanjar</groupId>
<artifactId>weixin-java-parent</artifactId>
<version>1.3.1</version>
<version>1.3.2</version>
</parent>
<artifactId>weixin-java-common</artifactId>

View File

@@ -248,7 +248,6 @@ public class WxCryptUtil {
}
}
toSign.append("key=" + signKey);
System.out.println(toSign.toString());
String sign = DigestUtils.md5Hex(toSign.toString())
.toUpperCase();
return sign;

View File

@@ -6,7 +6,7 @@
<parent>
<groupId>me.chanjar</groupId>
<artifactId>weixin-java-parent</artifactId>
<version>1.3.1</version>
<version>1.3.2</version>
</parent>
<artifactId>weixin-java-cp</artifactId>

View File

@@ -6,7 +6,7 @@
<parent>
<groupId>me.chanjar</groupId>
<artifactId>weixin-java-parent</artifactId>
<version>1.3.1</version>
<version>1.3.2</version>
</parent>
<artifactId>weixin-java-mp</artifactId>
<name>WeiXin Java Tools - MP</name>

View File

@@ -2,6 +2,8 @@ package me.chanjar.weixin.mp.api;
import java.io.File;
import javax.net.ssl.SSLContext;
import me.chanjar.weixin.common.bean.WxAccessToken;
/**
@@ -74,4 +76,5 @@ public interface WxMpConfigStorage {
public File getTmpDirFile();
public SSLContext getSSLContext();
}

View File

@@ -2,6 +2,8 @@ package me.chanjar.weixin.mp.api;
import java.io.File;
import javax.net.ssl.SSLContext;
import me.chanjar.weixin.common.bean.WxAccessToken;
/**
@@ -35,6 +37,8 @@ public class WxMpInMemoryConfigStorage implements WxMpConfigStorage {
*/
protected volatile File tmpDirFile;
protected volatile SSLContext sslContext;
public String getAccessToken() {
return this.accessToken;
}
@@ -219,4 +223,13 @@ public class WxMpInMemoryConfigStorage implements WxMpConfigStorage {
this.tmpDirFile = tmpDirFile;
}
@Override
public SSLContext getSSLContext() {
return sslContext;
}
public void setSSLContext(SSLContext context) {
sslContext = context;
}
}

View File

@@ -11,7 +11,6 @@ import me.chanjar.weixin.mp.bean.result.*;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.math.BigDecimal;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;
@@ -738,4 +737,23 @@ public interface WxMpService {
* @return
*/
WxMpPayCallback getJSSDKCallbackData(String xmlData);
/**
* <pre>
* 计算Map键值对是否和签名相符,
* 按照字段名的 ASCII 码从小到大排序(字典序)后,使用 URL 键值对的 格式(即 key1=value1&key2=value2...)拼接成字符串
* </pre>
* @param kvm
* @param signature
* @return
*/
public boolean checkJSSDKCallbackDataSignature(Map<String, String> kvm, String signature);
/**
* 发送微信红包给个人用户
* @param parameters
* @return
* @throws WxErrorException
*/
public WxRedpackResult sendRedpack(Map<String, String> parameters) throws WxErrorException;
}

View File

@@ -5,8 +5,6 @@ import java.io.IOException;
import java.io.InputStream;
import java.io.StringReader;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
@@ -68,6 +66,7 @@ import me.chanjar.weixin.mp.bean.result.WxMpUser;
import me.chanjar.weixin.mp.bean.result.WxMpUserCumulate;
import me.chanjar.weixin.mp.bean.result.WxMpUserList;
import me.chanjar.weixin.mp.bean.result.WxMpUserSummary;
import me.chanjar.weixin.mp.bean.result.WxRedpackResult;
import me.chanjar.weixin.mp.util.http.MaterialDeleteRequestExecutor;
import me.chanjar.weixin.mp.util.http.MaterialNewsInfoRequestExecutor;
import me.chanjar.weixin.mp.util.http.MaterialUploadRequestExecutor;
@@ -76,7 +75,6 @@ import me.chanjar.weixin.mp.util.http.MaterialVoiceAndImageDownloadRequestExecut
import me.chanjar.weixin.mp.util.http.QrCodeRequestExecutor;
import me.chanjar.weixin.mp.util.json.WxMpGsonBuilder;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.http.Consts;
import org.apache.http.HttpHost;
import org.apache.http.auth.AuthScope;
@@ -87,13 +85,16 @@ import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.BasicCredentialsProvider;
import org.apache.http.impl.client.BasicResponseHandler;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.impl.client.HttpClients;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.slf4j.helpers.MessageFormatter;
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
@@ -507,7 +508,10 @@ public class WxMpServiceImpl implements WxMpService {
String url = "https://api.weixin.qq.com/cgi-bin/message/template/send";
String responseContent = execute(new SimplePostRequestExecutor(), url, templateMessage.toJson());
JsonElement tmpJsonElement = Streams.parse(new JsonReader(new StringReader(responseContent)));
return tmpJsonElement.getAsJsonObject().get("msgid").getAsString();
final JsonObject jsonObject = tmpJsonElement.getAsJsonObject();
if (jsonObject.get("errcode").getAsInt() == 0)
return jsonObject.get("msgid").getAsString();
throw new WxErrorException(WxError.fromJson(responseContent));
}
public WxMpSemanticQueryResult semanticQuery(WxMpSemanticQuery semanticQuery) throws WxErrorException {
@@ -743,6 +747,7 @@ public class WxMpServiceImpl implements WxMpService {
String http_proxy_username = wxMpConfigStorage.getHttp_proxy_username();
String http_proxy_password = wxMpConfigStorage.getHttp_proxy_password();
final HttpClientBuilder builder = HttpClients.custom();
if (StringUtils.isNotBlank(http_proxy_host)) {
// 使用代理服务器
if (StringUtils.isNotBlank(http_proxy_username)) {
@@ -751,18 +756,22 @@ public class WxMpServiceImpl implements WxMpService {
credsProvider.setCredentials(
new AuthScope(http_proxy_host, http_proxy_port),
new UsernamePasswordCredentials(http_proxy_username, http_proxy_password));
httpClient = HttpClients
.custom()
.setDefaultCredentialsProvider(credsProvider)
.build();
builder
.setDefaultCredentialsProvider(credsProvider);
} else {
// 无需用户认证的代理服务器
httpClient = HttpClients.createDefault();
}
httpProxy = new HttpHost(http_proxy_host, http_proxy_port);
} else {
httpClient = HttpClients.createDefault();
}
if (wxConfigProvider.getSSLContext() != null){
SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(
wxConfigProvider.getSSLContext(),
new String[] { "TLSv1" },
null,
SSLConnectionSocketFactory.BROWSER_COMPATIBLE_HOSTNAME_VERIFIER);
builder.setSSLSocketFactory(sslsf);
}
httpClient = builder.build();
}
@Override
@@ -826,9 +835,8 @@ public class WxMpServiceImpl implements WxMpService {
WxMpPrepayIdResult wxMpPrepayIdResult = (WxMpPrepayIdResult) xstream.fromXML(responseContent);
return wxMpPrepayIdResult;
} catch (IOException e) {
e.printStackTrace();
throw new RuntimeException("Failed to get prepay id due to IO exception.", e);
}
return new WxMpPrepayIdResult();
}
final String[] REQUIRED_ORDER_PARAMETERS = new String[] { "appid", "mch_id", "body", "out_trade_no", "total_fee", "spbill_create_ip", "notify_url",
@@ -866,7 +874,7 @@ public class WxMpServiceImpl implements WxMpService {
WxMpPrepayIdResult wxMpPrepayIdResult = getPrepayId(parameters);
String prepayId = wxMpPrepayIdResult.getPrepay_id();
if (prepayId == null || prepayId.equals("")) {
throw new RuntimeException("get prepayid error");
throw new RuntimeException(String.format("Failed to get prepay id due to error code '%s'(%s).", wxMpPrepayIdResult.getErr_code(), wxMpPrepayIdResult.getErr_code_des()));
}
Map<String, String> payInfo = new HashMap<String, String>();
@@ -937,5 +945,49 @@ public class WxMpServiceImpl implements WxMpService {
return new WxMpPayCallback();
}
@Override
public boolean checkJSSDKCallbackDataSignature(Map<String, String> kvm, String signature) {
return signature.equals(WxCryptUtil.createSign(kvm, wxMpConfigStorage.getPartnerKey()));
}
@Override
public WxRedpackResult sendRedpack(Map<String, String> parameters) throws WxErrorException {
String nonce_str = System.currentTimeMillis() + "";
SortedMap<String, String> packageParams = new TreeMap<String, String>(parameters);
packageParams.put("wxappid", wxMpConfigStorage.getAppId());
packageParams.put("mch_id", wxMpConfigStorage.getPartnerId());
packageParams.put("nonce_str", nonce_str);
String sign = WxCryptUtil.createSign(packageParams, wxMpConfigStorage.getPartnerKey());
packageParams.put("sign", sign);
StringBuilder request = new StringBuilder("<xml>");
for (Entry<String, String> para : packageParams.entrySet()) {
request.append(String.format("<%s>%s</%s>", para.getKey(), para.getValue(), para.getKey()));
}
request.append("</xml>");
HttpPost httpPost = new HttpPost("https://api.mch.weixin.qq.com/mmpaymkttransfers/sendredpack");
if (httpProxy != null) {
RequestConfig config = RequestConfig.custom().setProxy(httpProxy).build();
httpPost.setConfig(config);
}
StringEntity entity = new StringEntity(request.toString(), Consts.UTF_8);
httpPost.setEntity(entity);
try {
CloseableHttpResponse response = getHttpclient().execute(httpPost);
String responseContent = Utf8ResponseHandler.INSTANCE.handleResponse(response);
XStream xstream = XStreamInitializer.getInstance();
xstream.processAnnotations(WxRedpackResult.class);
WxRedpackResult wxMpRedpackResult = (WxRedpackResult) xstream.fromXML(responseContent);
return wxMpRedpackResult;
} catch (IOException e) {
log.error(MessageFormatter.format("The exception was happened when sending redpack '{}'.", request.toString()).getMessage(), e);
WxError error = new WxError();
error.setErrorCode(-1);
throw new WxErrorException(error);
}
}
}

View File

@@ -5,7 +5,7 @@ import me.chanjar.weixin.mp.bean.WxMpXmlOutTransferCustomerServiceMessage;
/**
* 客服消息builder
* <pre>
* 用法: WxMpCustomMessage m = WxMpCustomMessage.TRANSFER_CUSTOMER_SERVICE().content(...).toUser(...).build();
* 用法: WxMpCustomMessage m = WxMpXmlOutMessage.TRANSFER_CUSTOMER_SERVICE().content(...).toUser(...).build();
* </pre>
*
* @author chanjarster
@@ -25,4 +25,4 @@ public final class TransferCustomerServiceBuilder extends BaseBuilder<TransferCu
m.setKfAccount(kfAccount);
return m;
}
}
}

View File

@@ -0,0 +1,118 @@
package me.chanjar.weixin.mp.bean.result;
import java.io.Serializable;
import com.thoughtworks.xstream.annotations.XStreamAlias;
/**
* 向微信用户个人发现金红包返回结果
* https://pay.weixin.qq.com/wiki/doc/api/cash_coupon.php?chapter=13_5
* @author kane
*
*/
@XStreamAlias("xml")
public class WxRedpackResult implements Serializable {
/**
*
*/
private static final long serialVersionUID = -4837415036337132073L;
@XStreamAlias("return_code")
String returnCode;
@XStreamAlias("return_msg")
String returnMsg;
@XStreamAlias("sign")
String sign;
@XStreamAlias("result_code")
String resultCode;
@XStreamAlias("err_code")
String errCode;
@XStreamAlias("err_code_des")
String errCodeDes;
@XStreamAlias("mch_billno")
String mchBillno;
@XStreamAlias("mch_id")
String mchId;
@XStreamAlias("wxappid")
String wxappid;
@XStreamAlias("re_openid")
String reOpenid;
@XStreamAlias("total_amount")
int totalAmount;
@XStreamAlias("send_time")
String sendTime;
@XStreamAlias("send_listid")
String sendListid;
public String getErrCode() {
return errCode;
}
public String getErrCodeDes() {
return errCodeDes;
}
public String getReturnCode() {
return returnCode;
}
public String getReturnMsg() {
return returnMsg;
}
public String getSign() {
return sign;
}
public String getResultCode() {
return resultCode;
}
public String getMchBillno() {
return mchBillno;
}
public String getMchId() {
return mchId;
}
public String getWxappid() {
return wxappid;
}
public String getReOpenid() {
return reOpenid;
}
public int getTotalAmount() {
return totalAmount;
}
public String getSendTime() {
return sendTime;
}
public String getSendListid() {
return sendListid;
}
@Override
public String toString() {
return "WxRedpackResult{" +
"returnCode=" + returnCode +
", returnMsg=" + returnMsg +
", sign=" + sign +
", errCode=" + errCode +
", errCodeDes=" + errCodeDes +
", mchBillno=" + mchBillno +
", mchId=" + mchId +
", wxappid=" + wxappid +
", reOpenid=" + reOpenid +
", totalAmount=" + totalAmount +
", sendTime=" + sendTime +
", sendListid=" + sendListid +
'}';
}
}

View File

@@ -22,7 +22,7 @@ public class WxUserListGsonAdapter implements JsonDeserializer<WxMpUserList> {
wxMpUserList.setTotal(GsonHelper.getInteger(o, "total"));
wxMpUserList.setCount(GsonHelper.getInteger(o, "count"));
wxMpUserList.setNextOpenId(GsonHelper.getString(o, "next_openid"));
if (!o.get("data").isJsonNull() && !o.get("data").getAsJsonObject().get("openid").isJsonNull()) {
if (o.get("data") != null && !o.get("data").isJsonNull() && !o.get("data").getAsJsonObject().get("openid").isJsonNull()) {
JsonArray data = o.get("data").getAsJsonObject().get("openid").getAsJsonArray();
for (int i = 0; i < data.size(); i++) {
wxMpUserList.getOpenIds().add(GsonHelper.getAsString(data.get(i)));

View File

@@ -51,6 +51,8 @@ public class XStreamTransformer {
map.put(WxMpXmlOutImageMessage.class, config_WxMpXmlOutImageMessage());
map.put(WxMpXmlOutVideoMessage.class, config_WxMpXmlOutVideoMessage());
map.put(WxMpXmlOutVoiceMessage.class, config_WxMpXmlOutVoiceMessage());
map.put(WxMpXmlOutTransferCustomerServiceMessage.class, config_WxMpXmlOutTransferCustomerServiceMessage());
return map;
}
@@ -111,4 +113,12 @@ public class XStreamTransformer {
return xstream;
}
private static XStream config_WxMpXmlOutTransferCustomerServiceMessage() {
XStream xstream = XStreamInitializer.getInstance();
xstream.processAnnotations(WxMpXmlOutMessage.class);
xstream.processAnnotations(WxMpXmlOutTransferCustomerServiceMessage.class);
xstream.processAnnotations(WxMpXmlOutTransferCustomerServiceMessage.TransInfo.class);
return xstream;
}
}

View File

@@ -0,0 +1,63 @@
package me.chanjar.weixin.mp.bean;
import static org.junit.Assert.assertEquals;
import org.junit.Before;
import org.junit.Test;
import com.thoughtworks.xstream.XStream;
import me.chanjar.weixin.common.util.xml.XStreamInitializer;
import me.chanjar.weixin.mp.bean.result.WxRedpackResult;
public class WxRedpackResultTest {
private XStream xstream;
@Before
public void setup() {
xstream = XStreamInitializer.getInstance();
xstream.processAnnotations(WxRedpackResult.class);
}
@Test public void loadSuccessResult() {
final String successSample = "<xml>\n" +
"<return_code><![CDATA[SUCCESS]]></return_code>\n" +
"<return_msg><![CDATA[发放成功.]]></return_msg>\n" +
"<result_code><![CDATA[SUCCESS]]></result_code>\n" +
"<err_code><![CDATA[0]]></err_code>\n" +
"<err_code_des><![CDATA[发放成功.]]></err_code_des>\n" +
"<mch_billno><![CDATA[0010010404201411170000046545]]></mch_billno>\n" +
"<mch_id>10010404</mch_id>\n" +
"<wxappid><![CDATA[wx6fa7e3bab7e15415]]></wxappid>\n" +
"<re_openid><![CDATA[onqOjjmM1tad-3ROpncN-yUfa6uI]]></re_openid>\n" +
"<total_amount>1</total_amount>\n" +
"<send_listid>100000000020150520314766074200</send_listid>\n" +
"<send_time>20150520102602</send_time>\n" +
"</xml>";
WxRedpackResult wxMpRedpackResult = (WxRedpackResult) xstream.fromXML(successSample);
assertEquals("SUCCESS", wxMpRedpackResult.getReturnCode());
assertEquals("SUCCESS", wxMpRedpackResult.getResultCode());
assertEquals("20150520102602", wxMpRedpackResult.getSendTime());
}
@Test public void loadFailureResult() {
final String failureSample = "<xml>\n" +
"<return_code><![CDATA[FAIL]]></return_code>\n" +
"<return_msg><![CDATA[系统繁忙,请稍后再试.]]></return_msg>\n" +
"<result_code><![CDATA[FAIL]]></result_code>\n" +
"<err_code><![CDATA[268458547]]></err_code>\n" +
"<err_code_des><![CDATA[系统繁忙,请稍后再试.]]></err_code_des>\n" +
"<mch_billno><![CDATA[0010010404201411170000046542]]></mch_billno>\n" +
"<mch_id>10010404</mch_id>\n" +
"<wxappid><![CDATA[wx6fa7e3bab7e15415]]></wxappid>\n" +
"<re_openid><![CDATA[onqOjjmM1tad-3ROpncN-yUfa6uI]]></re_openid>\n" +
"<total_amount>1</total_amount>\n" +
"</xml>";
WxRedpackResult wxMpRedpackResult = (WxRedpackResult) xstream.fromXML(failureSample);
assertEquals("FAIL", wxMpRedpackResult.getReturnCode());
assertEquals("FAIL", wxMpRedpackResult.getResultCode());
assertEquals("onqOjjmM1tad-3ROpncN-yUfa6uI", wxMpRedpackResult.getReOpenid());
assertEquals(1, wxMpRedpackResult.getTotalAmount());
}
}