Compare commits
31 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
42418a2d0d | ||
|
|
8d95028658 | ||
|
|
d3d61d753e | ||
|
|
4bee1ba2eb | ||
|
|
f30211ce98 | ||
|
|
6239abcbcc | ||
|
|
1ade007250 | ||
|
|
ee2cf21efc | ||
|
|
d707140d06 | ||
|
|
0491e23456 | ||
|
|
4da9ea5dd5 | ||
|
|
f107bd2f44 | ||
|
|
50949b1ff5 | ||
|
|
bb22974d65 | ||
|
|
622094bcdf | ||
|
|
bd3fb69bef | ||
|
|
f0432e6930 | ||
|
|
72db0a2a76 | ||
|
|
f7b5b52808 | ||
|
|
ab916ddd71 | ||
|
|
29cc3f6558 | ||
|
|
214b969f4d | ||
|
|
5aba724633 | ||
|
|
b3110991a7 | ||
|
|
fcd3a9d76b | ||
|
|
ffc0fc442d | ||
|
|
6f3dfc7740 | ||
|
|
e84ca8dab9 | ||
|
|
eccbf08809 | ||
|
|
b2179faf3e | ||
|
|
0cd6033205 |
44
.gitignore
vendored
44
.gitignore
vendored
@@ -1,21 +1,23 @@
|
||||
*.class
|
||||
test-output
|
||||
|
||||
# Mobile Tools for Java (J2ME)
|
||||
.mtj.tmp/
|
||||
|
||||
# Package Files #
|
||||
*.jar
|
||||
*.war
|
||||
*.ear
|
||||
|
||||
# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
|
||||
hs_err_pid*
|
||||
|
||||
target
|
||||
.project
|
||||
.classpath
|
||||
|
||||
sw-pom.xml
|
||||
*.iml
|
||||
test-config.xml
|
||||
*.class
|
||||
test-output
|
||||
|
||||
# Mobile Tools for Java (J2ME)
|
||||
.mtj.tmp/
|
||||
|
||||
# Package Files #
|
||||
*.jar
|
||||
*.war
|
||||
*.ear
|
||||
|
||||
# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
|
||||
hs_err_pid*
|
||||
|
||||
target
|
||||
.project
|
||||
.classpath
|
||||
.settings
|
||||
|
||||
sw-pom.xml
|
||||
*.iml
|
||||
test-config.xml
|
||||
.idea
|
||||
|
||||
@@ -17,7 +17,7 @@ weixin-java-tools
|
||||
<dependency>
|
||||
<groupId>me.chanjar</groupId>
|
||||
<artifactId>weixin-java-mp</artifactId>
|
||||
<version>1.1.4</version>
|
||||
<version>1.1.8</version>
|
||||
</dependency>
|
||||
```
|
||||
|
||||
@@ -27,7 +27,7 @@ weixin-java-tools
|
||||
<dependency>
|
||||
<groupId>me.chanjar</groupId>
|
||||
<artifactId>weixin-java-cp</artifactId>
|
||||
<version>1.1.4</version>
|
||||
<version>1.1.8</version>
|
||||
</dependency>
|
||||
```
|
||||
|
||||
|
||||
12
pom.xml
12
pom.xml
@@ -5,7 +5,7 @@
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<groupId>me.chanjar</groupId>
|
||||
<artifactId>weixin-java-parent</artifactId>
|
||||
<version>1.1.4</version>
|
||||
<version>1.1.8</version>
|
||||
<packaging>pom</packaging>
|
||||
<name>WeiXin Java Tools - Parent</name>
|
||||
<description>微信公众号、企业号上级POM</description>
|
||||
@@ -225,6 +225,16 @@
|
||||
<goals>deploy</goals>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<version>2.3.2</version>
|
||||
<configuration>
|
||||
<source>1.7</source>
|
||||
<target>1.7</target>
|
||||
<encoding>UTF-8</encoding>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
<parent>
|
||||
<groupId>me.chanjar</groupId>
|
||||
<artifactId>weixin-java-parent</artifactId>
|
||||
<version>1.1.4</version>
|
||||
<version>1.1.8</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>weixin-java-common</artifactId>
|
||||
|
||||
@@ -105,7 +105,7 @@ public class WxConsts {
|
||||
///////////////////////
|
||||
public static final String FILE_JPG = "jpeg";
|
||||
public static final String FILE_MP3 = "mp3";
|
||||
public static final String FILE_ARM = "arm";
|
||||
public static final String FILE_AMR = "amr";
|
||||
public static final String FILE_MP4 = "mp4";
|
||||
|
||||
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
package me.chanjar.weixin.common.util.crypto;
|
||||
|
||||
import org.apache.commons.codec.digest.DigestUtils;
|
||||
|
||||
import java.security.MessageDigest;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.util.Arrays;
|
||||
@@ -21,7 +23,7 @@ public class SHA1 {
|
||||
for (String a : arr) {
|
||||
sb.append(a);
|
||||
}
|
||||
return genStr(sb.toString());
|
||||
return DigestUtils.sha1Hex(sb.toString());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -40,25 +42,6 @@ public class SHA1 {
|
||||
sb.append('&');
|
||||
}
|
||||
}
|
||||
return genStr(sb.toString());
|
||||
return DigestUtils.sha1Hex(sb.toString());
|
||||
}
|
||||
|
||||
public static String genStr(String str) throws NoSuchAlgorithmException {
|
||||
MessageDigest sha1 = MessageDigest.getInstance("SHA1");
|
||||
sha1.update(str.getBytes());
|
||||
byte[] output = sha1.digest();
|
||||
return bytesToHex(output);
|
||||
}
|
||||
|
||||
protected static String bytesToHex(byte[] b) {
|
||||
char hexDigit[] = { '0', '1', '2', '3', '4', '5', '6', '7',
|
||||
'8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };
|
||||
StringBuffer buf = new StringBuffer();
|
||||
for (int j = 0; j < b.length; j++) {
|
||||
buf.append(hexDigit[(b[j] >> 4) & 0x0f]);
|
||||
buf.append(hexDigit[b[j] & 0x0f]);
|
||||
}
|
||||
return buf.toString();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
package me.chanjar.weixin.common.util.crypto;
|
||||
|
||||
import org.apache.commons.codec.binary.Base64;
|
||||
import org.apache.commons.codec.digest.DigestUtils;
|
||||
import org.w3c.dom.Document;
|
||||
import org.w3c.dom.Element;
|
||||
import org.xml.sax.InputSource;
|
||||
@@ -27,8 +28,7 @@ import javax.xml.parsers.ParserConfigurationException;
|
||||
import java.io.StringReader;
|
||||
import java.nio.charset.Charset;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.util.Arrays;
|
||||
import java.util.Random;
|
||||
import java.util.*;
|
||||
|
||||
public class WxCryptUtil {
|
||||
|
||||
@@ -224,6 +224,36 @@ public class WxCryptUtil {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 微信公众号支付签名算法(详见:http://pay.weixin.qq.com/wiki/doc/api/index.php?chapter=4_3)
|
||||
* @param packageParams 原始参数
|
||||
* @param signKey 加密Key(即 商户Key)
|
||||
* @param charset 编码
|
||||
* @return 签名字符串
|
||||
*/
|
||||
public static String createSign(Map<String, String> packageParams, String signKey) {
|
||||
SortedMap<String, String> sortedMap = new TreeMap<String, String>();
|
||||
sortedMap.putAll(packageParams);
|
||||
|
||||
List<String> keys = new ArrayList<String>(packageParams.keySet());
|
||||
Collections.sort(keys);
|
||||
|
||||
|
||||
StringBuffer toSign = new StringBuffer();
|
||||
for (String key : keys) {
|
||||
String value = packageParams.get(key);
|
||||
if (null != value && !"".equals(value) && !"sign".equals(key)
|
||||
&& !"key".equals(key)) {
|
||||
toSign.append(key + "=" + value + "&");
|
||||
}
|
||||
}
|
||||
toSign.append("key=" + signKey);
|
||||
System.out.println(toSign.toString());
|
||||
String sign = DigestUtils.md5Hex(toSign.toString())
|
||||
.toUpperCase();
|
||||
return sign;
|
||||
}
|
||||
|
||||
/**
|
||||
* 将一个数字转换成生成4个字节的网络字节序bytes数组
|
||||
*
|
||||
|
||||
@@ -7,18 +7,25 @@ import java.io.InputStream;
|
||||
|
||||
public class FileUtils {
|
||||
|
||||
|
||||
/**
|
||||
* 创建临时文件
|
||||
* @param inputStream
|
||||
* @param name 文件名
|
||||
* @param ext 扩展名
|
||||
* @param tmpDirFile 临时文件夹目录
|
||||
* @return
|
||||
* @throws IOException
|
||||
*/
|
||||
public static File createTmpFile(InputStream inputStream, String name, String ext) throws IOException {
|
||||
public static File createTmpFile(InputStream inputStream, String name, String ext, File tmpDirFile) throws IOException {
|
||||
FileOutputStream fos = null;
|
||||
try {
|
||||
File tmpFile = File.createTempFile(name, '.' + ext);
|
||||
File tmpFile;
|
||||
if (tmpDirFile == null) {
|
||||
tmpFile = File.createTempFile(name, '.' + ext);
|
||||
} else {
|
||||
tmpFile = File.createTempFile(name, '.' + ext, tmpDirFile);
|
||||
}
|
||||
tmpFile.deleteOnExit();
|
||||
fos = new FileOutputStream(tmpFile);
|
||||
int read = 0;
|
||||
@@ -43,5 +50,17 @@ public class FileUtils {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建临时文件
|
||||
* @param inputStream
|
||||
* @param name 文件名
|
||||
* @param ext 扩展名
|
||||
* @return
|
||||
* @throws IOException
|
||||
*/
|
||||
public static File createTmpFile(InputStream inputStream, String name, String ext) throws IOException {
|
||||
return createTmpFile(inputStream, name, ext, null);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -25,6 +25,18 @@ import java.util.regex.Pattern;
|
||||
*
|
||||
*/
|
||||
public class MediaDownloadRequestExecutor implements RequestExecutor<File, String> {
|
||||
|
||||
private File tmpDirFile;
|
||||
|
||||
public MediaDownloadRequestExecutor() {
|
||||
super();
|
||||
}
|
||||
|
||||
public MediaDownloadRequestExecutor(File tmpDirFile) {
|
||||
super();
|
||||
this.tmpDirFile = tmpDirFile;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public File execute(CloseableHttpClient httpclient, HttpHost httpProxy, String uri, String queryParam) throws WxErrorException, ClientProtocolException, IOException {
|
||||
@@ -41,26 +53,29 @@ public class MediaDownloadRequestExecutor implements RequestExecutor<File, Strin
|
||||
httpGet.setConfig(config);
|
||||
}
|
||||
|
||||
CloseableHttpResponse response = httpclient.execute(httpGet);
|
||||
try (CloseableHttpResponse response = httpclient.execute(httpGet)) {
|
||||
|
||||
Header[] contentTypeHeader = response.getHeaders("Content-Type");
|
||||
if (contentTypeHeader != null && contentTypeHeader.length > 0) {
|
||||
// 下载媒体文件出错
|
||||
if (ContentType.TEXT_PLAIN.getMimeType().equals(contentTypeHeader[0].getValue())) {
|
||||
String responseContent = Utf8ResponseHandler.INSTANCE.handleResponse(response);
|
||||
throw new WxErrorException(WxError.fromJson(responseContent));
|
||||
Header[] contentTypeHeader = response.getHeaders("Content-Type");
|
||||
if (contentTypeHeader != null && contentTypeHeader.length > 0) {
|
||||
// 下载媒体文件出错
|
||||
if (ContentType.TEXT_PLAIN.getMimeType().equals(contentTypeHeader[0].getValue())) {
|
||||
String responseContent = Utf8ResponseHandler.INSTANCE.handleResponse(response);
|
||||
throw new WxErrorException(WxError.fromJson(responseContent));
|
||||
}
|
||||
}
|
||||
InputStream inputStream = InputStreamResponseHandler.INSTANCE.handleResponse(response);
|
||||
|
||||
// 视频文件不支持下载
|
||||
String fileName = getFileName(response);
|
||||
if (StringUtils.isBlank(fileName)) {
|
||||
return null;
|
||||
}
|
||||
String[] name_ext = fileName.split("\\.");
|
||||
File localFile = FileUtils.createTmpFile(inputStream, name_ext[0], name_ext[1], tmpDirFile);
|
||||
return localFile;
|
||||
|
||||
}
|
||||
InputStream inputStream = InputStreamResponseHandler.INSTANCE.handleResponse(response);
|
||||
|
||||
// 视频文件不支持下载
|
||||
String fileName = getFileName(response);
|
||||
if (StringUtils.isBlank(fileName)) {
|
||||
return null;
|
||||
}
|
||||
String[] name_ext = fileName.split("\\.");
|
||||
File localFile = FileUtils.createTmpFile(inputStream, name_ext[0], name_ext[1]);
|
||||
return localFile;
|
||||
|
||||
}
|
||||
|
||||
protected String getFileName(CloseableHttpResponse response) {
|
||||
|
||||
@@ -39,13 +39,14 @@ public class MediaUploadRequestExecutor implements RequestExecutor<WxMediaUpload
|
||||
httpPost.setEntity(entity);
|
||||
httpPost.setHeader("Content-Type", ContentType.MULTIPART_FORM_DATA.toString());
|
||||
}
|
||||
CloseableHttpResponse response = httpclient.execute(httpPost);
|
||||
String responseContent = Utf8ResponseHandler.INSTANCE.handleResponse(response);
|
||||
WxError error = WxError.fromJson(responseContent);
|
||||
if (error.getErrorCode() != 0) {
|
||||
throw new WxErrorException(error);
|
||||
try (CloseableHttpResponse response = httpclient.execute(httpPost)) {
|
||||
String responseContent = Utf8ResponseHandler.INSTANCE.handleResponse(response);
|
||||
WxError error = WxError.fromJson(responseContent);
|
||||
if (error.getErrorCode() != 0) {
|
||||
throw new WxErrorException(error);
|
||||
}
|
||||
return WxMediaUploadResult.fromJson(responseContent);
|
||||
}
|
||||
return WxMediaUploadResult.fromJson(responseContent);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -33,13 +33,14 @@ public class SimpleGetRequestExecutor implements RequestExecutor<String, String>
|
||||
httpGet.setConfig(config);
|
||||
}
|
||||
|
||||
CloseableHttpResponse response = httpclient.execute(httpGet);
|
||||
String responseContent = Utf8ResponseHandler.INSTANCE.handleResponse(response);
|
||||
WxError error = WxError.fromJson(responseContent);
|
||||
if (error.getErrorCode() != 0) {
|
||||
throw new WxErrorException(error);
|
||||
try (CloseableHttpResponse response = httpclient.execute(httpGet)) {
|
||||
String responseContent = Utf8ResponseHandler.INSTANCE.handleResponse(response);
|
||||
WxError error = WxError.fromJson(responseContent);
|
||||
if (error.getErrorCode() != 0) {
|
||||
throw new WxErrorException(error);
|
||||
}
|
||||
return responseContent;
|
||||
}
|
||||
return responseContent;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -40,13 +40,14 @@ public class SimplePostRequestExecutor implements RequestExecutor<String, String
|
||||
httpPost.setEntity(entity);
|
||||
}
|
||||
|
||||
CloseableHttpResponse response = httpclient.execute(httpPost);
|
||||
String responseContent = Utf8ResponseHandler.INSTANCE.handleResponse(response);
|
||||
WxError error = WxError.fromJson(responseContent);
|
||||
if (error.getErrorCode() != 0) {
|
||||
throw new WxErrorException(error);
|
||||
try (CloseableHttpResponse response = httpclient.execute(httpPost)) {
|
||||
String responseContent = Utf8ResponseHandler.INSTANCE.handleResponse(response);
|
||||
WxError error = WxError.fromJson(responseContent);
|
||||
if (error.getErrorCode() != 0) {
|
||||
throw new WxErrorException(error);
|
||||
}
|
||||
return responseContent;
|
||||
}
|
||||
return responseContent;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
<parent>
|
||||
<groupId>me.chanjar</groupId>
|
||||
<artifactId>weixin-java-parent</artifactId>
|
||||
<version>1.1.4</version>
|
||||
<version>1.1.8</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>weixin-java-cp</artifactId>
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
package me.chanjar.weixin.cp.api;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
import me.chanjar.weixin.common.bean.WxAccessToken;
|
||||
|
||||
/**
|
||||
@@ -58,5 +60,7 @@ public interface WxCpConfigStorage {
|
||||
public String getHttp_proxy_username();
|
||||
|
||||
public String getHttp_proxy_password();
|
||||
|
||||
public File getTmpDirFile();
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
package me.chanjar.weixin.cp.api;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
import me.chanjar.weixin.common.bean.WxAccessToken;
|
||||
|
||||
/**
|
||||
@@ -28,6 +30,8 @@ public class WxCpInMemoryConfigStorage implements WxCpConfigStorage {
|
||||
protected volatile String jsapiTicket;
|
||||
protected volatile long jsapiTicketExpiresTime;
|
||||
|
||||
protected volatile File tmpDirFile;
|
||||
|
||||
public String getAccessToken() {
|
||||
return this.accessToken;
|
||||
}
|
||||
@@ -189,7 +193,16 @@ public class WxCpInMemoryConfigStorage implements WxCpConfigStorage {
|
||||
", http_proxy_password='" + http_proxy_password + '\'' +
|
||||
", jsapiTicket='" + jsapiTicket + '\'' +
|
||||
", jsapiTicketExpiresTime='" + jsapiTicketExpiresTime + '\'' +
|
||||
", tmpDirFile='" + tmpDirFile + '\'' +
|
||||
'}';
|
||||
}
|
||||
|
||||
public File getTmpDirFile() {
|
||||
return tmpDirFile;
|
||||
}
|
||||
|
||||
public void setTmpDirFile(File tmpDirFile) {
|
||||
this.tmpDirFile = tmpDirFile;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -399,7 +399,7 @@ public interface WxCpService {
|
||||
* @param tagId
|
||||
* @param userIds
|
||||
*/
|
||||
void tagAddUsers(String tagId, List<String> userIds) throws WxErrorException;
|
||||
void tagAddUsers(String tagId, List<String> userIds, List<String> partyIds) throws WxErrorException;
|
||||
|
||||
/**
|
||||
* <pre>
|
||||
|
||||
@@ -78,6 +78,11 @@ public class WxCpServiceImpl implements WxCpService {
|
||||
|
||||
protected WxSessionManager sessionManager = new StandardSessionManager();
|
||||
|
||||
/**
|
||||
* 临时文件目录
|
||||
*/
|
||||
protected File tmpDirFile;
|
||||
|
||||
public boolean checkSignature(String msgSignature, String timestamp, String nonce, String data) {
|
||||
try {
|
||||
return SHA1.gen(wxCpConfigStorage.getToken(), timestamp, nonce, data).equals(msgSignature);
|
||||
@@ -112,8 +117,10 @@ public class WxCpServiceImpl implements WxCpService {
|
||||
httpGet.setConfig(config);
|
||||
}
|
||||
CloseableHttpClient httpclient = getHttpclient();
|
||||
CloseableHttpResponse response = httpclient.execute(httpGet);
|
||||
String resultContent = new BasicResponseHandler().handleResponse(response);
|
||||
String resultContent = null;
|
||||
try (CloseableHttpResponse response = httpclient.execute(httpGet)) {
|
||||
resultContent = new BasicResponseHandler().handleResponse(response);
|
||||
}
|
||||
WxError error = WxError.fromJson(resultContent);
|
||||
if (error.getErrorCode() != 0) {
|
||||
throw new WxErrorException(error);
|
||||
@@ -236,7 +243,8 @@ public class WxCpServiceImpl implements WxCpService {
|
||||
|
||||
public File mediaDownload(String media_id) throws WxErrorException {
|
||||
String url = "https://qyapi.weixin.qq.com/cgi-bin/media/get";
|
||||
return execute(new MediaDownloadRequestExecutor(), url, "media_id=" + media_id);
|
||||
|
||||
return execute(new MediaDownloadRequestExecutor(wxCpConfigStorage.getTmpDirFile()), url, "media_id=" + media_id);
|
||||
}
|
||||
|
||||
|
||||
@@ -271,7 +279,8 @@ public class WxCpServiceImpl implements WxCpService {
|
||||
return WxCpGsonBuilder.INSTANCE.create()
|
||||
.fromJson(
|
||||
tmpJsonElement.getAsJsonObject().get("department"),
|
||||
new TypeToken<List<WxCpDepart>>() { }.getType()
|
||||
new TypeToken<List<WxCpDepart>>() {
|
||||
}.getType()
|
||||
);
|
||||
}
|
||||
|
||||
@@ -389,7 +398,8 @@ public class WxCpServiceImpl implements WxCpService {
|
||||
return WxCpGsonBuilder.INSTANCE.create()
|
||||
.fromJson(
|
||||
tmpJsonElement.getAsJsonObject().get("taglist"),
|
||||
new TypeToken<List<WxCpTag>>() { }.getType()
|
||||
new TypeToken<List<WxCpTag>>() {
|
||||
}.getType()
|
||||
);
|
||||
}
|
||||
|
||||
@@ -406,15 +416,24 @@ public class WxCpServiceImpl implements WxCpService {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void tagAddUsers(String tagId, List<String> userIds) throws WxErrorException {
|
||||
public void tagAddUsers(String tagId, List<String> userIds, List<String> 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());
|
||||
}
|
||||
|
||||
@@ -627,6 +646,14 @@ public class WxCpServiceImpl implements WxCpService {
|
||||
public void setSessionManager(WxSessionManager sessionManager) {
|
||||
this.sessionManager = sessionManager;
|
||||
}
|
||||
|
||||
public File getTmpDirFile() {
|
||||
return tmpDirFile;
|
||||
}
|
||||
|
||||
public void setTmpDirFile(File tmpDirFile) {
|
||||
this.tmpDirFile = tmpDirFile;
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
Float a = 3.1f;
|
||||
|
||||
@@ -42,7 +42,7 @@ public class WxCpTagAPITest {
|
||||
public void testTagAddUsers() throws Exception {
|
||||
List<String> userIds = new ArrayList<String>();
|
||||
userIds.add(((ApiTestModule.WxXmlCpInMemoryConfigStorage)configStorage).getUserId());
|
||||
wxService.tagAddUsers(tagId, userIds);
|
||||
wxService.tagAddUsers(tagId, userIds, null);
|
||||
}
|
||||
|
||||
@Test(dependsOnMethods = "testTagAddUsers")
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
<parent>
|
||||
<groupId>me.chanjar</groupId>
|
||||
<artifactId>weixin-java-parent</artifactId>
|
||||
<version>1.1.4</version>
|
||||
<version>1.1.8</version>
|
||||
</parent>
|
||||
<artifactId>weixin-java-mp</artifactId>
|
||||
<name>WeiXin Java Tools - MP</name>
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
package me.chanjar.weixin.mp.api;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
import me.chanjar.weixin.common.bean.WxAccessToken;
|
||||
|
||||
/**
|
||||
@@ -50,6 +52,10 @@ public interface WxMpConfigStorage {
|
||||
|
||||
public String getSecret();
|
||||
|
||||
public String getPartnerId();
|
||||
|
||||
public String getPartnerKey();
|
||||
|
||||
public String getToken();
|
||||
|
||||
public String getAesKey();
|
||||
@@ -64,7 +70,8 @@ public interface WxMpConfigStorage {
|
||||
|
||||
public String getHttp_proxy_username();
|
||||
|
||||
|
||||
public String getHttp_proxy_password();
|
||||
|
||||
public File getTmpDirFile();
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
package me.chanjar.weixin.mp.api;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
import me.chanjar.weixin.common.bean.WxAccessToken;
|
||||
|
||||
/**
|
||||
@@ -11,6 +13,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;
|
||||
@@ -26,6 +30,11 @@ public class WxMpInMemoryConfigStorage implements WxMpConfigStorage {
|
||||
protected volatile String jsapiTicket;
|
||||
protected volatile long jsapiTicketExpiresTime;
|
||||
|
||||
/**
|
||||
* 临时文件目录
|
||||
*/
|
||||
protected volatile File tmpDirFile;
|
||||
|
||||
public String getAccessToken() {
|
||||
return this.accessToken;
|
||||
}
|
||||
@@ -168,6 +177,8 @@ public class WxMpInMemoryConfigStorage implements WxMpConfigStorage {
|
||||
"appId='" + appId + '\'' +
|
||||
", secret='" + secret + '\'' +
|
||||
", token='" + token + '\'' +
|
||||
", partnerId='" + partnerId + '\'' +
|
||||
", partnerKey='" + partnerKey + '\'' +
|
||||
", accessToken='" + accessToken + '\'' +
|
||||
", aesKey='" + aesKey + '\'' +
|
||||
", expiresTime=" + expiresTime +
|
||||
@@ -177,6 +188,35 @@ public class WxMpInMemoryConfigStorage implements WxMpConfigStorage {
|
||||
", http_proxy_password='" + http_proxy_password + '\'' +
|
||||
", jsapiTicket='" + jsapiTicket + '\'' +
|
||||
", jsapiTicketExpiresTime='" + jsapiTicketExpiresTime + '\'' +
|
||||
", tmpDirFile='" + tmpDirFile + '\'' +
|
||||
'}';
|
||||
}
|
||||
|
||||
@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;
|
||||
}
|
||||
|
||||
@Override
|
||||
public File getTmpDirFile() {
|
||||
return this.tmpDirFile;
|
||||
}
|
||||
|
||||
public void setTmpDirFile(File tmpDirFile) {
|
||||
this.tmpDirFile = tmpDirFile;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
/**
|
||||
* <pre>
|
||||
* 换取永久字符串二维码ticket
|
||||
* 详情请见: http://mp.weixin.qq.com/wiki/index.php?title=生成带参数的二维码
|
||||
* </pre>
|
||||
*
|
||||
* @param scene_str 参数。字符串类型长度现在为1到64
|
||||
* @return
|
||||
* @throws WxErrorException
|
||||
*/
|
||||
public WxMpQrCodeTicket qrCodeCreateLastTicket(String scene_str) throws WxErrorException;
|
||||
|
||||
/**
|
||||
* <pre>
|
||||
* 换取二维码图片文件,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<String, String> getJSSDKPayInfo(String openId, String outTradeNo, double amt, String body, String tradeType, String ip, String notifyUrl);
|
||||
|
||||
}
|
||||
|
||||
@@ -6,6 +6,8 @@ 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 +19,17 @@ 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 +38,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 +51,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 {
|
||||
|
||||
@@ -103,8 +110,7 @@ public class WxMpServiceImpl implements WxMpService {
|
||||
RequestConfig config = RequestConfig.custom().setProxy(httpProxy).build();
|
||||
httpGet.setConfig(config);
|
||||
}
|
||||
CloseableHttpClient httpclient = getHttpclient();
|
||||
CloseableHttpResponse response = httpclient.execute(httpGet);
|
||||
CloseableHttpResponse response = getHttpclient().execute(httpGet);
|
||||
String resultContent = new BasicResponseHandler().handleResponse(response);
|
||||
WxError error = WxError.fromJson(resultContent);
|
||||
if (error.getErrorCode() != 0) {
|
||||
@@ -209,7 +215,7 @@ public class WxMpServiceImpl implements WxMpService {
|
||||
|
||||
public File mediaDownload(String media_id) throws WxErrorException {
|
||||
String url = "http://file.api.weixin.qq.com/cgi-bin/media/get";
|
||||
return execute(new MediaDownloadRequestExecutor(), url, "media_id=" + media_id);
|
||||
return execute(new MediaDownloadRequestExecutor(wxMpConfigStorage.getTmpDirFile()), url, "media_id=" + media_id);
|
||||
}
|
||||
|
||||
public WxMpMassUploadResult massNewsUpload(WxMpMassNews news) throws WxErrorException {
|
||||
@@ -332,6 +338,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 +632,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<String, String> packageParams = new TreeMap<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);
|
||||
|
||||
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>";
|
||||
|
||||
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 = getHttpclient().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<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);
|
||||
String prepayId = wxMpPrepayIdResult.getPrepay_id();
|
||||
if (prepayId == null || prepayId.equals("")) {
|
||||
throw new RuntimeException("get prepayid error");
|
||||
}
|
||||
|
||||
Map<String, String> payInfo = new HashMap<String, String>();
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,98 @@
|
||||
package me.chanjar.weixin.mp.bean.result;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* <pre>
|
||||
* 群发消息一发送就返回的结果
|
||||
*
|
||||
* 真正的群发消息是否发送成功要看
|
||||
* 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
|
||||
*
|
||||
* </pre>
|
||||
*
|
||||
* @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;
|
||||
}
|
||||
}
|
||||
@@ -47,20 +47,21 @@ public class QrCodeRequestExecutor implements RequestExecutor<File, WxMpQrCodeTi
|
||||
httpGet.setConfig(config);
|
||||
}
|
||||
|
||||
CloseableHttpResponse response = httpclient.execute(httpGet);
|
||||
|
||||
Header[] contentTypeHeader = response.getHeaders("Content-Type");
|
||||
if (contentTypeHeader != null && contentTypeHeader.length > 0) {
|
||||
// 出错
|
||||
if (ContentType.TEXT_PLAIN.getMimeType().equals(contentTypeHeader[0].getValue())) {
|
||||
String responseContent = Utf8ResponseHandler.INSTANCE.handleResponse(response);
|
||||
throw new WxErrorException(WxError.fromJson(responseContent));
|
||||
try (CloseableHttpResponse response = httpclient.execute(httpGet)) {
|
||||
Header[] contentTypeHeader = response.getHeaders("Content-Type");
|
||||
if (contentTypeHeader != null && contentTypeHeader.length > 0) {
|
||||
// 出错
|
||||
if (ContentType.TEXT_PLAIN.getMimeType().equals(contentTypeHeader[0].getValue())) {
|
||||
String responseContent = Utf8ResponseHandler.INSTANCE.handleResponse(response);
|
||||
throw new WxErrorException(WxError.fromJson(responseContent));
|
||||
}
|
||||
}
|
||||
InputStream inputStream = InputStreamResponseHandler.INSTANCE.handleResponse(response);
|
||||
|
||||
File localFile = FileUtils.createTmpFile(inputStream, UUID.randomUUID().toString(), "jpg");
|
||||
return localFile;
|
||||
}
|
||||
InputStream inputStream = InputStreamResponseHandler.INSTANCE.handleResponse(response);
|
||||
|
||||
File localFile = FileUtils.createTmpFile(inputStream, UUID.randomUUID().toString(), "jpg");
|
||||
return localFile;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user