1
0
mirror of synced 2025-12-19 14:48:02 +08:00

Compare commits

..

26 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
5e7d1ebbbd upgrade to 1.3.1 2015-10-16 10:07:40 +08:00
Daniel Qian
d87fe63591 Merge pull request #214 from videome/develop
Improve prepay API to support all parameters.
2015-10-14 19:43:52 +08:00
Kane Zhu
cc3714c599 Improve prepay API to support all parameters. 2015-10-14 16:36:55 +08:00
Daniel Qian
fe8c70849a fix #211 上传素材时如果文件名是中文会乱码 2015-10-03 10:46:27 +08:00
Daniel Qian
a25d2edc2f issue #204, 因为getAccessToken()方法可能返回过期数据,导致在executeInternal内部容易出现
无限递归产生StackoverflowException
2015-09-14 12:38:29 +08:00
Daniel Qian
c722496167 #204 2015-09-14 12:35:39 +08:00
Daniel Qian
a6db21fbc8 fix #207 2015-09-11 14:04:34 +08:00
Daniel Qian
9bbdc3b2a0 promote to 1.3.1-SNAPSHOT 2015-09-11 09:10:43 +08:00
Daniel Qian
293ef256b6 Merge branch 'develop' 2015-09-11 09:09:36 +08:00
20 changed files with 453 additions and 78 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.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>
```

View File

@@ -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>

View File

@@ -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>

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

@@ -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());

View File

@@ -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>

View File

@@ -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);
}

View File

@@ -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>

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;
@@ -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 交易类型 JSAPINATIVEAPPWAP
* @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 交易类型 JSAPINATIVEAPPWAP
* @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 交易类型 JSAPINATIVEAPPWAP
* @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 交易类型 JSAPINATIVEAPPWAP
* @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;
}

View File

@@ -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);
}
}
}

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

@@ -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;
}
}

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

@@ -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));

View File

@@ -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"));

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());
}
}