partyIds) throws WxErrorException;
/**
*
diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpServiceImpl.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpServiceImpl.java
index c04d7533..0ef29b55 100644
--- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpServiceImpl.java
+++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpServiceImpl.java
@@ -271,7 +271,8 @@ public class WxCpServiceImpl implements WxCpService {
return WxCpGsonBuilder.INSTANCE.create()
.fromJson(
tmpJsonElement.getAsJsonObject().get("department"),
- new TypeToken>() { }.getType()
+ new TypeToken>() {
+ }.getType()
);
}
@@ -389,7 +390,8 @@ public class WxCpServiceImpl implements WxCpService {
return WxCpGsonBuilder.INSTANCE.create()
.fromJson(
tmpJsonElement.getAsJsonObject().get("taglist"),
- new TypeToken>() { }.getType()
+ new TypeToken>() {
+ }.getType()
);
}
@@ -406,15 +408,24 @@ public class WxCpServiceImpl implements WxCpService {
}
@Override
- public void tagAddUsers(String tagId, List userIds) throws WxErrorException {
+ public void tagAddUsers(String tagId, List userIds, List partyIds) throws WxErrorException {
String url = "https://qyapi.weixin.qq.com/cgi-bin/tag/addtagusers";
JsonObject jsonObject = new JsonObject();
jsonObject.addProperty("tagid", tagId);
- JsonArray jsonArray = new JsonArray();
- for (String userId : userIds) {
- jsonArray.add(new JsonPrimitive(userId));
+ if (userIds != null) {
+ JsonArray jsonArray = new JsonArray();
+ for (String userId : userIds) {
+ jsonArray.add(new JsonPrimitive(userId));
+ }
+ jsonObject.add("userlist", jsonArray);
+ }
+ if (partyIds != null) {
+ JsonArray jsonArray = new JsonArray();
+ for (String userId : partyIds) {
+ jsonArray.add(new JsonPrimitive(userId));
+ }
+ jsonObject.add("partylist", jsonArray);
}
- jsonObject.add("userlist", jsonArray);
post(url, jsonObject.toString());
}
diff --git a/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/WxCpTagAPITest.java b/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/WxCpTagAPITest.java
index 0bb7dba9..b101eeb6 100644
--- a/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/WxCpTagAPITest.java
+++ b/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/WxCpTagAPITest.java
@@ -42,7 +42,7 @@ public class WxCpTagAPITest {
public void testTagAddUsers() throws Exception {
List userIds = new ArrayList();
userIds.add(((ApiTestModule.WxXmlCpInMemoryConfigStorage)configStorage).getUserId());
- wxService.tagAddUsers(tagId, userIds);
+ wxService.tagAddUsers(tagId, userIds, null);
}
@Test(dependsOnMethods = "testTagAddUsers")
diff --git a/weixin-java-mp/pom.xml b/weixin-java-mp/pom.xml
index bfb653ec..3893b1cc 100644
--- a/weixin-java-mp/pom.xml
+++ b/weixin-java-mp/pom.xml
@@ -6,7 +6,7 @@
me.chanjar
weixin-java-parent
- 1.1.4
+ 1.1.5
weixin-java-mp
WeiXin Java Tools - MP
diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpConfigStorage.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpConfigStorage.java
index c54a119d..46d58bc3 100644
--- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpConfigStorage.java
+++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpConfigStorage.java
@@ -50,6 +50,10 @@ public interface WxMpConfigStorage {
public String getSecret();
+ public String getPartnerId();
+
+ public String getPartnerKey();
+
public String getToken();
public String getAesKey();
diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpInMemoryConfigStorage.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpInMemoryConfigStorage.java
index 3033d178..c14241f4 100644
--- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpInMemoryConfigStorage.java
+++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpInMemoryConfigStorage.java
@@ -11,6 +11,8 @@ public class WxMpInMemoryConfigStorage implements WxMpConfigStorage {
protected volatile String appId;
protected volatile String secret;
+ protected volatile String partnerId;
+ protected volatile String partnerKey;
protected volatile String token;
protected volatile String accessToken;
protected volatile String aesKey;
@@ -168,6 +170,8 @@ public class WxMpInMemoryConfigStorage implements WxMpConfigStorage {
"appId='" + appId + '\'' +
", secret='" + secret + '\'' +
", token='" + token + '\'' +
+ ", partnerId='" + partnerId + '\'' +
+ ", partnerKey='" + partnerKey + '\'' +
", accessToken='" + accessToken + '\'' +
", aesKey='" + aesKey + '\'' +
", expiresTime=" + expiresTime +
@@ -179,4 +183,22 @@ public class WxMpInMemoryConfigStorage implements WxMpConfigStorage {
", jsapiTicketExpiresTime='" + jsapiTicketExpiresTime + '\'' +
'}';
}
+
+ @Override
+ public String getPartnerId() {
+ return partnerId;
+ }
+
+ public void setPartnerId(String partnerId) {
+ this.partnerId = partnerId;
+ }
+
+ @Override
+ public String getPartnerKey() {
+ return partnerKey;
+ }
+
+ public void setPartnerKey(String partnerKey) {
+ this.partnerKey = partnerKey;
+ }
}
diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpService.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpService.java
index 3205adbe..45de6f8c 100644
--- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpService.java
+++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpService.java
@@ -11,9 +11,11 @@ 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;
+import java.util.Map;
/**
* 微信API的Service
@@ -332,6 +334,18 @@ public interface WxMpService {
*/
public WxMpQrCodeTicket qrCodeCreateLastTicket(int scene_id) throws WxErrorException;
+ /**
+ *
+ * 换取永久字符串二维码ticket
+ * 详情请见: http://mp.weixin.qq.com/wiki/index.php?title=生成带参数的二维码
+ *
+ *
+ * @param scene_str 参数。字符串类型长度现在为1到64
+ * @return
+ * @throws WxErrorException
+ */
+ public WxMpQrCodeTicket qrCodeCreateLastTicket(String scene_str) throws WxErrorException;
+
/**
*
* 换取二维码图片文件,jpg格式
@@ -528,4 +542,32 @@ public interface WxMpService {
*/
void setMaxRetryTimes(int maxRetryTimes);
+ /**
+ * 统一下单(详见http://pay.weixin.qq.com/wiki/doc/api/index.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);
+
+ /**
+ * 该接口调用“统一下单”接口,并拼装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 getJSSDKPayInfo(String openId, String outTradeNo, double amt, String body, String tradeType, String ip, String notifyUrl);
+
}
diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpServiceImpl.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpServiceImpl.java
index e41a26e5..1e08a69f 100644
--- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpServiceImpl.java
+++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpServiceImpl.java
@@ -6,6 +6,7 @@ import com.google.gson.JsonObject;
import com.google.gson.internal.Streams;
import com.google.gson.reflect.TypeToken;
import com.google.gson.stream.JsonReader;
+import com.thoughtworks.xstream.XStream;
import me.chanjar.weixin.common.bean.WxAccessToken;
import me.chanjar.weixin.common.bean.WxMenu;
import me.chanjar.weixin.common.bean.WxJsapiSignature;
@@ -17,13 +18,16 @@ import me.chanjar.weixin.common.session.WxSessionManager;
import me.chanjar.weixin.common.util.RandomUtils;
import me.chanjar.weixin.common.util.StringUtils;
import me.chanjar.weixin.common.util.crypto.SHA1;
+import me.chanjar.weixin.common.util.crypto.WxCryptUtil;
import me.chanjar.weixin.common.util.fs.FileUtils;
import me.chanjar.weixin.common.util.http.*;
import me.chanjar.weixin.common.util.json.GsonHelper;
+import me.chanjar.weixin.common.util.xml.XStreamInitializer;
import me.chanjar.weixin.mp.bean.*;
import me.chanjar.weixin.mp.bean.result.*;
import me.chanjar.weixin.mp.util.http.QrCodeRequestExecutor;
import me.chanjar.weixin.mp.util.json.WxMpGsonBuilder;
+import org.apache.http.Consts;
import org.apache.http.HttpHost;
import org.apache.http.auth.AuthScope;
import org.apache.http.auth.UsernamePasswordCredentials;
@@ -32,6 +36,8 @@ import org.apache.http.client.CredentialsProvider;
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.entity.StringEntity;
import org.apache.http.impl.client.BasicCredentialsProvider;
import org.apache.http.impl.client.BasicResponseHandler;
import org.apache.http.impl.client.CloseableHttpClient;
@@ -43,10 +49,9 @@ import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.StringReader;
+import java.math.BigDecimal;
import java.security.NoSuchAlgorithmException;
-import java.util.Date;
-import java.util.List;
-import java.util.UUID;
+import java.util.*;
public class WxMpServiceImpl implements WxMpService {
@@ -332,6 +337,19 @@ public class WxMpServiceImpl implements WxMpService {
String responseContent = execute(new SimplePostRequestExecutor(), url, json.toString());
return WxMpQrCodeTicket.fromJson(responseContent);
}
+
+ public WxMpQrCodeTicket qrCodeCreateLastTicket(String scene_str) throws WxErrorException {
+ String url = "https://api.weixin.qq.com/cgi-bin/qrcode/create";
+ JsonObject json = new JsonObject();
+ json.addProperty("action_name", "QR_LIMIT_STR_SCENE");
+ JsonObject actionInfo = new JsonObject();
+ JsonObject scene = new JsonObject();
+ scene.addProperty("scene_str", scene_str);
+ actionInfo.add("scene", scene);
+ json.add("action_info", actionInfo);
+ String responseContent = execute(new SimplePostRequestExecutor(), url, json.toString());
+ return WxMpQrCodeTicket.fromJson(responseContent);
+ }
public File qrCodePicture(WxMpQrCodeTicket ticket) throws WxErrorException {
String url = "https://mp.weixin.qq.com/cgi-bin/showqrcode";
@@ -613,4 +631,77 @@ public class WxMpServiceImpl implements WxMpService {
this.maxRetryTimes = maxRetryTimes;
}
+ @Override
+ public WxMpPrepayIdResult getPrepayId(String openId, String outTradeNo, double amt, String body, String tradeType, String ip, String callbackUrl) {
+ String nonce_str = System.currentTimeMillis() + "";
+
+ SortedMap packageParams = new TreeMap();
+ 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);
+
+ String sign = WxCryptUtil.createSign(packageParams, wxMpConfigStorage.getPartnerKey());
+ String xml = "" +
+ "" + wxMpConfigStorage.getAppId() + "" +
+ "" + wxMpConfigStorage.getPartnerId() + "" +
+ "" + nonce_str + "" +
+ "" + sign + "" +
+ "" +
+ "" + outTradeNo + "" +
+ "" + packageParams.get("total_fee") + "" +
+ "" + ip + "" +
+ "" + callbackUrl + "" +
+ "" + tradeType + "" +
+ "" + openId + "" +
+ "";
+
+ HttpPost httpPost = new HttpPost("https://api.mch.weixin.qq.com/pay/unifiedorder");
+ if (httpProxy != null) {
+ RequestConfig config = RequestConfig.custom().setProxy(httpProxy).build();
+ httpPost.setConfig(config);
+ }
+
+ StringEntity entity = new StringEntity(xml, Consts.UTF_8);
+ httpPost.setEntity(entity);
+ try {
+ CloseableHttpResponse response = httpClient.execute(httpPost);
+ String responseContent = Utf8ResponseHandler.INSTANCE.handleResponse(response);
+ XStream xstream = XStreamInitializer.getInstance();
+ xstream.alias("xml", WxMpPrepayIdResult.class);
+ WxMpPrepayIdResult wxMpPrepayIdResult = (WxMpPrepayIdResult) xstream.fromXML(responseContent);
+ return wxMpPrepayIdResult;
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ return new WxMpPrepayIdResult();
+ }
+
+ @Override
+ public Map 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);
+ String prepayId = wxMpPrepayIdResult.getPrepay_id();
+ if (prepayId == null || prepayId.equals("")) {
+ throw new RuntimeException("get prepayid error");
+ }
+
+ Map payInfo = new HashMap();
+ payInfo.put("appId", wxMpConfigStorage.getAppId());
+ // 支付签名时间戳,注意微信jssdk中的所有使用timestamp字段均为小写。但最新版的支付后台生成签名使用的timeStamp字段名需大写其中的S字符
+ payInfo.put("timeStamp", String.valueOf(System.currentTimeMillis() / 1000));
+ payInfo.put("nonceStr", System.currentTimeMillis() + "");
+ payInfo.put("package", "prepay_id=" + prepayId);
+ payInfo.put("signType", "MD5");
+
+ String finalSign = WxCryptUtil.createSign(payInfo, wxMpConfigStorage.getPartnerKey());
+ payInfo.put("sign", finalSign);
+ return payInfo;
+ }
}
diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/result/WxMpPrepayIdResult.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/result/WxMpPrepayIdResult.java
new file mode 100644
index 00000000..25286168
--- /dev/null
+++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/result/WxMpPrepayIdResult.java
@@ -0,0 +1,98 @@
+package me.chanjar.weixin.mp.bean.result;
+
+import java.io.Serializable;
+
+/**
+ *
+ * 群发消息一发送就返回的结果
+ *
+ * 真正的群发消息是否发送成功要看
+ * http://mp.weixin.qq.com/wiki/index.php?title=高级群发接口#.E4.BA.8B.E4.BB.B6.E6.8E.A8.E9.80.81.E7.BE.A4.E5.8F.91.E7.BB.93.E6.9E.9C
+ *
+ *
+ *
+ * @author chanjarster
+ */
+public class WxMpPrepayIdResult implements Serializable {
+ private String return_code;
+ private String return_msg;
+ private String appid;
+ private String mch_id;
+ private String nonce_str;
+ private String sign;
+ private String result_code;
+ private String prepay_id;
+ private String trade_type;
+
+ public String getReturn_code() {
+ return return_code;
+ }
+
+ public void setReturn_code(String return_code) {
+ this.return_code = return_code;
+ }
+
+ public String getReturn_msg() {
+ return return_msg;
+ }
+
+ public void setReturn_msg(String return_msg) {
+ this.return_msg = return_msg;
+ }
+
+ public String getAppid() {
+ return appid;
+ }
+
+ public void setAppid(String appid) {
+ this.appid = appid;
+ }
+
+ public String getMch_id() {
+ return mch_id;
+ }
+
+ public void setMch_id(String mch_id) {
+ this.mch_id = mch_id;
+ }
+
+ public String getNonce_str() {
+ return nonce_str;
+ }
+
+ public void setNonce_str(String nonce_str) {
+ this.nonce_str = nonce_str;
+ }
+
+ public String getSign() {
+ return sign;
+ }
+
+ public void setSign(String sign) {
+ this.sign = sign;
+ }
+
+ public String getResult_code() {
+ return result_code;
+ }
+
+ public void setResult_code(String result_code) {
+ this.result_code = result_code;
+ }
+
+ public String getPrepay_id() {
+ return prepay_id;
+ }
+
+ public void setPrepay_id(String prepay_id) {
+ this.prepay_id = prepay_id;
+ }
+
+ public String getTrade_type() {
+ return trade_type;
+ }
+
+ public void setTrade_type(String trade_type) {
+ this.trade_type = trade_type;
+ }
+}