Compare commits
26 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
780074f219 | ||
|
|
59bdad92f6 | ||
|
|
89a260dded | ||
|
|
b19dc7b0fb | ||
|
|
379aae95f1 | ||
|
|
6c45fdacdb | ||
|
|
5f456eb521 | ||
|
|
27ce2a2746 | ||
|
|
d5b3239a01 | ||
|
|
7972733feb | ||
|
|
30f22896b1 | ||
|
|
d0a452904a | ||
|
|
b5f0ee75b0 | ||
|
|
123e5ef973 | ||
|
|
23c96d9076 | ||
|
|
9363a67538 | ||
|
|
7b3a3a4c2f | ||
|
|
5e7d1ebbbd | ||
|
|
d87fe63591 | ||
|
|
cc3714c599 | ||
|
|
fe8c70849a | ||
|
|
a25d2edc2f | ||
|
|
c722496167 | ||
|
|
a6db21fbc8 | ||
|
|
9bbdc3b2a0 | ||
|
|
293ef256b6 |
10
README.md
10
README.md
@@ -2,6 +2,12 @@ weixin-java-tools
|
||||
|
||||
[](https://travis-ci.org/chanjarster/weixin-java-tools)
|
||||

|
||||
[](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.0</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.0</version>
|
||||
<version>1.3.2</version>
|
||||
</dependency>
|
||||
```
|
||||
|
||||
|
||||
2
pom.xml
2
pom.xml
@@ -5,7 +5,7 @@
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<groupId>me.chanjar</groupId>
|
||||
<artifactId>weixin-java-parent</artifactId>
|
||||
<version>1.3.0</version>
|
||||
<version>1.3.2</version>
|
||||
<packaging>pom</packaging>
|
||||
<name>WeiXin Java Tools - Parent</name>
|
||||
<description>微信公众号、企业号上级POM</description>
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
<parent>
|
||||
<groupId>me.chanjar</groupId>
|
||||
<artifactId>weixin-java-parent</artifactId>
|
||||
<version>1.3.0</version>
|
||||
<version>1.3.2</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>weixin-java-common</artifactId>
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -11,6 +11,7 @@ import org.apache.http.client.config.RequestConfig;
|
||||
import org.apache.http.client.methods.CloseableHttpResponse;
|
||||
import org.apache.http.client.methods.HttpPost;
|
||||
import org.apache.http.entity.ContentType;
|
||||
import org.apache.http.entity.mime.HttpMultipartMode;
|
||||
import org.apache.http.entity.mime.MultipartEntityBuilder;
|
||||
|
||||
import me.chanjar.weixin.common.bean.result.WxError;
|
||||
@@ -35,6 +36,7 @@ public class MediaUploadRequestExecutor implements RequestExecutor<WxMediaUpload
|
||||
HttpEntity entity = MultipartEntityBuilder
|
||||
.create()
|
||||
.addBinaryBody("media", file)
|
||||
.setMode(HttpMultipartMode.RFC6532)
|
||||
.build();
|
||||
httpPost.setEntity(entity);
|
||||
httpPost.setHeader("Content-Type", ContentType.MULTIPART_FORM_DATA.toString());
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
<parent>
|
||||
<groupId>me.chanjar</groupId>
|
||||
<artifactId>weixin-java-parent</artifactId>
|
||||
<version>1.3.0</version>
|
||||
<version>1.3.2</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>weixin-java-cp</artifactId>
|
||||
|
||||
@@ -555,7 +555,7 @@ public class WxCpServiceImpl implements WxCpService {
|
||||
throw new RuntimeException("微信服务端异常,超出重试次数");
|
||||
}
|
||||
|
||||
protected <T, E> T executeInternal(RequestExecutor<T, E> executor, String uri, E data) throws WxErrorException {
|
||||
protected synchronized <T, E> T executeInternal(RequestExecutor<T, E> executor, String uri, E data) throws WxErrorException {
|
||||
if (uri.indexOf("access_token=") != -1) {
|
||||
throw new IllegalArgumentException("uri参数中不允许有access_token: " + uri);
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
<parent>
|
||||
<groupId>me.chanjar</groupId>
|
||||
<artifactId>weixin-java-parent</artifactId>
|
||||
<version>1.3.0</version>
|
||||
<version>1.3.2</version>
|
||||
</parent>
|
||||
<artifactId>weixin-java-mp</artifactId>
|
||||
<name>WeiXin Java Tools - MP</name>
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
@@ -671,33 +670,57 @@ public interface WxMpService {
|
||||
*/
|
||||
void setMaxRetryTimes(int maxRetryTimes);
|
||||
|
||||
/**
|
||||
* 统一下单(详见http://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_1)
|
||||
* 在发起微信支付前,需要调用统一下单接口,获取"预支付交易会话标识"
|
||||
* @param openId 支付人openId
|
||||
* @param outTradeNo 商户端对应订单号
|
||||
* @param amt 金额(单位元)
|
||||
* @param body 商品描述
|
||||
* @param tradeType 交易类型 JSAPI,NATIVE,APP,WAP
|
||||
* @param ip 发起支付的客户端IP
|
||||
* @param notifyUrl 通知地址
|
||||
* @return
|
||||
*/
|
||||
WxMpPrepayIdResult getPrepayId(String openId, String outTradeNo, double amt, String body, String tradeType, String ip, String notifyUrl);
|
||||
/**
|
||||
* 统一下单(详见http://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_1)
|
||||
* 在发起微信支付前,需要调用统一下单接口,获取"预支付交易会话标识"
|
||||
* @param openId 支付人openId
|
||||
* @param outTradeNo 商户端对应订单号
|
||||
* @param amt 金额(单位元)
|
||||
* @param body 商品描述
|
||||
* @param tradeType 交易类型 JSAPI,NATIVE,APP,WAP
|
||||
* @param ip 发起支付的客户端IP
|
||||
* @param notifyUrl 通知地址
|
||||
* @return
|
||||
* @deprecated Use me.chanjar.weixin.mp.api.WxMpService.getPrepayId(Map<String, String>) instead
|
||||
*/
|
||||
@Deprecated
|
||||
WxMpPrepayIdResult getPrepayId(String openId, String outTradeNo, double amt, String body, String tradeType, String ip, String notifyUrl);
|
||||
|
||||
/**
|
||||
* 该接口调用“统一下单”接口,并拼装JSSDK发起支付请求需要的参数
|
||||
* 详见http://mp.weixin.qq.com/wiki/7/aaa137b55fb2e0456bf8dd9148dd613f.html#.E5.8F.91.E8.B5.B7.E4.B8.80.E4.B8.AA.E5.BE.AE.E4.BF.A1.E6.94.AF.E4.BB.98.E8.AF.B7.E6.B1.82
|
||||
* @param openId 支付人openId
|
||||
* @param outTradeNo 商户端对应订单号
|
||||
* @param amt 金额(单位元)
|
||||
* @param body 商品描述
|
||||
* @param tradeType 交易类型 JSAPI,NATIVE,APP,WAP
|
||||
* @param ip 发起支付的客户端IP
|
||||
* @param notifyUrl 通知地址
|
||||
* @return
|
||||
*/
|
||||
Map<String, String> getJSSDKPayInfo(String openId, String outTradeNo, double amt, String body, String tradeType, String ip, String notifyUrl);
|
||||
/**
|
||||
* 统一下单(详见http://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_1)
|
||||
* 在发起微信支付前,需要调用统一下单接口,获取"预支付交易会话标识"
|
||||
*
|
||||
* @param parameters
|
||||
* All required/optional parameters for weixin payment
|
||||
* @return
|
||||
* @throws IllegalArgumentException
|
||||
*/
|
||||
WxMpPrepayIdResult getPrepayId(Map<String, String> parameters);
|
||||
|
||||
/**
|
||||
* 该接口调用“统一下单”接口,并拼装JSSDK发起支付请求需要的参数
|
||||
* 详见http://mp.weixin.qq.com/wiki/7/aaa137b55fb2e0456bf8dd9148dd613f.html#.E5.8F.91.E8.B5.B7.E4.B8.80.E4.B8.AA.E5.BE.AE.E4.BF.A1.E6.94.AF.E4.BB.98.E8.AF.B7.E6.B1.82
|
||||
* @param parameters
|
||||
* the required or optional parameters
|
||||
* @return
|
||||
*/
|
||||
Map<String, String> getJSSDKPayInfo(Map<String, String> parameters);
|
||||
|
||||
/**
|
||||
* 该接口调用“统一下单”接口,并拼装JSSDK发起支付请求需要的参数
|
||||
* 详见http://mp.weixin.qq.com/wiki/7/aaa137b55fb2e0456bf8dd9148dd613f.html#.E5.8F.91.E8.B5.B7.E4.B8.80.E4.B8.AA.E5.BE.AE.E4.BF.A1.E6.94.AF.E4.BB.98.E8.AF.B7.E6.B1.82
|
||||
* @param openId 支付人openId
|
||||
* @param outTradeNo 商户端对应订单号
|
||||
* @param amt 金额(单位元)
|
||||
* @param body 商品描述
|
||||
* @param tradeType 交易类型 JSAPI,NATIVE,APP,WAP
|
||||
* @param ip 发起支付的客户端IP
|
||||
* @param notifyUrl 通知地址
|
||||
* @return
|
||||
* @deprecated Use me.chanjar.weixin.mp.api.WxMpService.getJSSDKPayInfo(Map<String, String>) instead
|
||||
*/
|
||||
@Deprecated
|
||||
Map<String, String> getJSSDKPayInfo(String openId, String outTradeNo, double amt, String body, String tradeType, String ip, String notifyUrl);
|
||||
|
||||
/**
|
||||
* 该接口提供所有微信支付订单的查询,当支付通知处理异常戒丢失的情冴,商户可以通过该接口查询订单支付状态。
|
||||
@@ -714,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;
|
||||
}
|
||||
|
||||
@@ -5,12 +5,11 @@ 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;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.SortedMap;
|
||||
import java.util.TreeMap;
|
||||
import java.util.UUID;
|
||||
@@ -67,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;
|
||||
@@ -75,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;
|
||||
@@ -86,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;
|
||||
@@ -506,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 {
|
||||
@@ -634,8 +639,9 @@ public class WxMpServiceImpl implements WxMpService {
|
||||
param.addProperty("end_date", SIMPLE_DATE_FORMAT.format(endDate));
|
||||
String responseContent = post(url, param.toString());
|
||||
JsonElement tmpJsonElement = Streams.parse(new JsonReader(new StringReader(responseContent)));
|
||||
return WxMpGsonBuilder.INSTANCE.create().fromJson(tmpJsonElement.getAsJsonObject().get("list"), new TypeToken<List<WxMpUserSummary>>() {
|
||||
}.getType());
|
||||
return WxMpGsonBuilder.INSTANCE.create().fromJson(tmpJsonElement.getAsJsonObject().get("list"),
|
||||
new TypeToken<List<WxMpUserSummary>>() {
|
||||
}.getType());
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -695,7 +701,7 @@ public class WxMpServiceImpl implements WxMpService {
|
||||
throw new RuntimeException("微信服务端异常,超出重试次数");
|
||||
}
|
||||
|
||||
protected <T, E> T executeInternal(RequestExecutor<T, E> executor, String uri, E data) throws WxErrorException {
|
||||
protected synchronized <T, E> T executeInternal(RequestExecutor<T, E> executor, String uri, E data) throws WxErrorException {
|
||||
if (uri.indexOf("access_token=") != -1) {
|
||||
throw new IllegalArgumentException("uri参数中不允许有access_token: " + uri);
|
||||
}
|
||||
@@ -741,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)) {
|
||||
@@ -749,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
|
||||
@@ -776,35 +787,37 @@ public class WxMpServiceImpl implements WxMpService {
|
||||
|
||||
@Override
|
||||
public WxMpPrepayIdResult getPrepayId(String openId, String outTradeNo, double amt, String body, String tradeType, String ip, String callbackUrl) {
|
||||
String nonce_str = System.currentTimeMillis() + "";
|
||||
|
||||
SortedMap<String, String> packageParams = new TreeMap<String, String>();
|
||||
Map<String, String> packageParams = new HashMap<String, String>();
|
||||
packageParams.put("appid", wxMpConfigStorage.getAppId());
|
||||
packageParams.put("mch_id", wxMpConfigStorage.getPartnerId());
|
||||
packageParams.put("nonce_str", nonce_str);
|
||||
packageParams.put("body", body);
|
||||
packageParams.put("out_trade_no", outTradeNo);
|
||||
|
||||
packageParams.put("total_fee", (int) (amt * 100) + "");
|
||||
packageParams.put("spbill_create_ip", ip);
|
||||
packageParams.put("notify_url", callbackUrl);
|
||||
packageParams.put("trade_type", tradeType);
|
||||
packageParams.put("openid", openId);
|
||||
|
||||
return getPrepayId(packageParams);
|
||||
}
|
||||
|
||||
public WxMpPrepayIdResult getPrepayId(final Map<String, String> parameters) {
|
||||
String nonce_str = System.currentTimeMillis() + "";
|
||||
|
||||
final SortedMap<String, String> packageParams = new TreeMap<String, String>(parameters);
|
||||
packageParams.put("appid", wxMpConfigStorage.getAppId());
|
||||
packageParams.put("mch_id", wxMpConfigStorage.getPartnerId());
|
||||
packageParams.put("nonce_str", nonce_str);
|
||||
checkParameters(packageParams);
|
||||
|
||||
String sign = WxCryptUtil.createSign(packageParams, wxMpConfigStorage.getPartnerKey());
|
||||
String xml = "<xml>" +
|
||||
"<appid>" + wxMpConfigStorage.getAppId() + "</appid>" +
|
||||
"<mch_id>" + wxMpConfigStorage.getPartnerId() + "</mch_id>" +
|
||||
"<nonce_str>" + nonce_str + "</nonce_str>" +
|
||||
"<sign>" + sign + "</sign>" +
|
||||
"<body><![CDATA[" + body + "]]></body>" +
|
||||
"<out_trade_no>" + outTradeNo + "</out_trade_no>" +
|
||||
"<total_fee>" + packageParams.get("total_fee") + "</total_fee>" +
|
||||
"<spbill_create_ip>" + ip + "</spbill_create_ip>" +
|
||||
"<notify_url>" + callbackUrl + "</notify_url>" +
|
||||
"<trade_type>" + tradeType + "</trade_type>" +
|
||||
"<openid>" + openId + "</openid>" +
|
||||
"</xml>";
|
||||
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/pay/unifiedorder");
|
||||
if (httpProxy != null) {
|
||||
@@ -812,7 +825,7 @@ public class WxMpServiceImpl implements WxMpService {
|
||||
httpPost.setConfig(config);
|
||||
}
|
||||
|
||||
StringEntity entity = new StringEntity(xml, Consts.UTF_8);
|
||||
StringEntity entity = new StringEntity(request.toString(), Consts.UTF_8);
|
||||
httpPost.setEntity(entity);
|
||||
try {
|
||||
CloseableHttpResponse response = getHttpclient().execute(httpPost);
|
||||
@@ -822,17 +835,46 @@ 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",
|
||||
"trade_type", };
|
||||
|
||||
private void checkParameters(Map<String, String> parameters) {
|
||||
for (String para : REQUIRED_ORDER_PARAMETERS) {
|
||||
if (!parameters.containsKey(para))
|
||||
throw new IllegalArgumentException("Reqiured argument '" + para + "' is missing.");
|
||||
}
|
||||
if ("JSAPI".equals(parameters.get("trade_type")) && !parameters.containsKey("openid"))
|
||||
throw new IllegalArgumentException("Reqiured argument 'openid' is missing when trade_type is 'JSAPI'.");
|
||||
if ("NATIVE".equals(parameters.get("trade_type")) && !parameters.containsKey("product_id"))
|
||||
throw new IllegalArgumentException("Reqiured argument 'product_id' is missing when trade_type is 'NATIVE'.");
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, String> getJSSDKPayInfo(String openId, String outTradeNo, double amt, String body, String tradeType, String ip, String callbackUrl) {
|
||||
WxMpPrepayIdResult wxMpPrepayIdResult = getPrepayId(openId, outTradeNo, amt, body, tradeType, ip, callbackUrl);
|
||||
Map<String, String> packageParams = new HashMap<String, String>();
|
||||
packageParams.put("appid", wxMpConfigStorage.getAppId());
|
||||
packageParams.put("mch_id", wxMpConfigStorage.getPartnerId());
|
||||
packageParams.put("body", body);
|
||||
packageParams.put("out_trade_no", outTradeNo);
|
||||
packageParams.put("total_fee", (int) (amt * 100) + "");
|
||||
packageParams.put("spbill_create_ip", ip);
|
||||
packageParams.put("notify_url", callbackUrl);
|
||||
packageParams.put("trade_type", tradeType);
|
||||
packageParams.put("openid", openId);
|
||||
|
||||
return getJSSDKPayInfo(packageParams);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, String> getJSSDKPayInfo(Map<String, String> parameters) {
|
||||
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>();
|
||||
@@ -903,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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,6 +22,9 @@ public class WxMpPrepayIdResult implements Serializable {
|
||||
private String result_code;
|
||||
private String prepay_id;
|
||||
private String trade_type;
|
||||
private String err_code;
|
||||
private String err_code_des;
|
||||
private String code_url;
|
||||
|
||||
public String getReturn_code() {
|
||||
return return_code;
|
||||
@@ -94,4 +97,28 @@ public class WxMpPrepayIdResult implements Serializable {
|
||||
public void setTrade_type(String trade_type) {
|
||||
this.trade_type = trade_type;
|
||||
}
|
||||
|
||||
public String getErr_code() {
|
||||
return err_code;
|
||||
}
|
||||
|
||||
public void setErr_code(String err_code) {
|
||||
this.err_code = err_code;
|
||||
}
|
||||
|
||||
public String getErr_code_des() {
|
||||
return err_code_des;
|
||||
}
|
||||
|
||||
public void setErr_code_des(String err_code_des) {
|
||||
this.err_code_des = err_code_des;
|
||||
}
|
||||
|
||||
public String getCode_url() {
|
||||
return code_url;
|
||||
}
|
||||
|
||||
public void setCode_url(String code_url) {
|
||||
this.code_url = code_url;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
@@ -13,6 +13,7 @@ import org.apache.http.client.config.RequestConfig;
|
||||
import org.apache.http.client.methods.CloseableHttpResponse;
|
||||
import org.apache.http.client.methods.HttpPost;
|
||||
import org.apache.http.entity.ContentType;
|
||||
import org.apache.http.entity.mime.HttpMultipartMode;
|
||||
import org.apache.http.entity.mime.MultipartEntityBuilder;
|
||||
import org.apache.http.entity.mime.content.InputStreamBody;
|
||||
import org.apache.http.impl.client.CloseableHttpClient;
|
||||
@@ -36,7 +37,9 @@ public class MaterialUploadRequestExecutor implements RequestExecutor<WxMpMateri
|
||||
}
|
||||
BufferedInputStream bufferedInputStream = new BufferedInputStream(new FileInputStream(file));
|
||||
MultipartEntityBuilder multipartEntityBuilder = MultipartEntityBuilder.create();
|
||||
multipartEntityBuilder.addPart("media", new InputStreamBody(bufferedInputStream, material.getName()));
|
||||
multipartEntityBuilder
|
||||
.addPart("media", new InputStreamBody(bufferedInputStream, material.getName()))
|
||||
.setMode(HttpMultipartMode.RFC6532);
|
||||
Map<String, String> form = material.getForm();
|
||||
if (material.getForm() != null) {
|
||||
multipartEntityBuilder.addTextBody("description", WxGsonBuilder.create().toJson(form));
|
||||
|
||||
@@ -19,7 +19,10 @@ public class WxMpUserGsonAdapter implements JsonDeserializer<WxMpUser> {
|
||||
public WxMpUser deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
|
||||
JsonObject o = json.getAsJsonObject();
|
||||
WxMpUser wxMpUser = new WxMpUser();
|
||||
wxMpUser.setSubscribe(new Integer(0).equals(GsonHelper.getInteger(o, "subscribe")) ? false : true);
|
||||
Integer subscribe = GsonHelper.getInteger(o, "subscribe");
|
||||
if (subscribe != null) {
|
||||
wxMpUser.setSubscribe(new Integer(0).equals(subscribe) ? false : true);
|
||||
}
|
||||
wxMpUser.setCity(GsonHelper.getString(o, "city"));
|
||||
wxMpUser.setCountry(GsonHelper.getString(o, "country"));
|
||||
wxMpUser.setHeadImgUrl(GsonHelper.getString(o, "headimgurl"));
|
||||
|
||||
@@ -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)));
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user