@@ -118,7 +118,20 @@ public interface WxMpConfigStorage { | |||||
File getTmpDirFile(); | File getTmpDirFile(); | ||||
@Deprecated | |||||
SSLContext getSSLContext(); | SSLContext getSSLContext(); | ||||
SSLContext getSslContext(); | |||||
void setSslContext(SSLContext sslContext); | |||||
/** | |||||
* 在此之前,必须将partnerId进行赋值 | |||||
* | |||||
* @param filePath | |||||
* apiclient_cert.p12的文件的绝对路径 | |||||
*/ | |||||
void setSslContextFilePath(String filePath) throws Exception; | |||||
/** | /** | ||||
* http client builder | * http client builder | ||||
@@ -1,14 +1,19 @@ | |||||
package me.chanjar.weixin.mp.api; | package me.chanjar.weixin.mp.api; | ||||
import me.chanjar.weixin.common.bean.WxAccessToken; | |||||
import me.chanjar.weixin.common.util.ToStringUtils; | |||||
import me.chanjar.weixin.common.util.http.ApacheHttpClientBuilder; | |||||
import javax.net.ssl.SSLContext; | |||||
import java.io.File; | import java.io.File; | ||||
import java.io.FileInputStream; | |||||
import java.security.KeyStore; | |||||
import java.util.concurrent.locks.Lock; | import java.util.concurrent.locks.Lock; | ||||
import java.util.concurrent.locks.ReentrantLock; | import java.util.concurrent.locks.ReentrantLock; | ||||
import javax.net.ssl.SSLContext; | |||||
import org.apache.http.ssl.SSLContexts; | |||||
import me.chanjar.weixin.common.bean.WxAccessToken; | |||||
import me.chanjar.weixin.common.util.ToStringUtils; | |||||
import me.chanjar.weixin.common.util.http.ApacheHttpClientBuilder; | |||||
/** | /** | ||||
* 基于内存的微信配置provider,在实际生产环境中应该将这些配置持久化 | * 基于内存的微信配置provider,在实际生产环境中应该将这些配置持久化 | ||||
* @author chanjarster | * @author chanjarster | ||||
@@ -300,11 +305,33 @@ public class WxMpInMemoryConfigStorage implements WxMpConfigStorage { | |||||
public SSLContext getSSLContext() { | public SSLContext getSSLContext() { | ||||
return this.sslContext; | return this.sslContext; | ||||
} | } | ||||
public void setSSLContext(SSLContext context) { | |||||
this.sslContext = context; | |||||
@Override | |||||
public SSLContext getSslContext() { | |||||
return this.sslContext; | |||||
} | } | ||||
@Override | |||||
public void setSslContextFilePath(String filePath) throws Exception { | |||||
if (null == partnerId) { | |||||
throw new Exception("请先将partnerId进行赋值"); | |||||
} | |||||
File file = new File(filePath); | |||||
if (!file.exists()) { | |||||
throw new RuntimeException(file.getPath() + ":文件不存在!在设置SSLContext的时候"); | |||||
} | |||||
FileInputStream inputStream = new FileInputStream(file); | |||||
KeyStore keystore = KeyStore.getInstance("PKCS12"); | |||||
char[] partnerId2charArray = partnerId.toCharArray(); | |||||
keystore.load(inputStream, partnerId2charArray); | |||||
this.sslContext = SSLContexts.custom().loadKeyMaterial(keystore, partnerId2charArray).build(); | |||||
} | |||||
@Override | |||||
public void setSslContext(SSLContext context) { | |||||
this.sslContext = context; | |||||
} | |||||
@Override | @Override | ||||
public ApacheHttpClientBuilder getApacheHttpClientBuilder() { | public ApacheHttpClientBuilder getApacheHttpClientBuilder() { | ||||
return this.apacheHttpClientBuilder; | return this.apacheHttpClientBuilder; | ||||
@@ -197,8 +197,24 @@ public interface WxMpPayService { | |||||
* @param request 请求对象 | * @param request 请求对象 | ||||
* @param keyFile 证书文件对象(即apiclient_cert.p12 商户证书文件,详细参考https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=4_3) | * @param keyFile 证书文件对象(即apiclient_cert.p12 商户证书文件,详细参考https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=4_3) | ||||
*/ | */ | ||||
@Deprecated | |||||
WxPaySendRedpackResult sendRedpack(WxPaySendRedpackRequest request, File keyFile) throws WxErrorException; | WxPaySendRedpackResult sendRedpack(WxPaySendRedpackRequest request, File keyFile) throws WxErrorException; | ||||
/** | |||||
* 发送微信红包给个人用户 | |||||
* <pre> | |||||
* 文档详见: | |||||
* 发送普通红包 https://pay.weixin.qq.com/wiki/doc/api/tools/cash_coupon.php?chapter=13_4&index=3 | |||||
* 接口地址:https://api.mch.weixin.qq.com/mmpaymkttransfers/sendredpack | |||||
* 发送裂变红包 https://pay.weixin.qq.com/wiki/doc/api/tools/cash_coupon.php?chapter=13_5&index=4 | |||||
* 接口地址:https://api.mch.weixin.qq.com/mmpaymkttransfers/sendgroupredpack | |||||
* </pre> | |||||
* | |||||
* @param request 请求对象 | |||||
* @param keyFile 证书文件对象(即apiclient_cert.p12 商户证书文件,详细参考https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=4_3) | |||||
*/ | |||||
WxPaySendRedpackResult sendRedpack(WxPaySendRedpackRequest request) throws WxErrorException; | |||||
/** | /** | ||||
* <pre> | * <pre> | ||||
* 查询红包记录 | * 查询红包记录 | ||||
@@ -211,7 +227,22 @@ public interface WxMpPayService { | |||||
* @param mchBillNo 商户发放红包的商户订单号,比如10000098201411111234567890 | * @param mchBillNo 商户发放红包的商户订单号,比如10000098201411111234567890 | ||||
* @param keyFile 证书文件对象(即apiclient_cert.p12 商户证书文件,详细参考https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=4_3) | * @param keyFile 证书文件对象(即apiclient_cert.p12 商户证书文件,详细参考https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=4_3) | ||||
*/ | */ | ||||
@Deprecated | |||||
WxPayRedpackQueryResult queryRedpack(String mchBillNo, File keyFile) throws WxErrorException; | WxPayRedpackQueryResult queryRedpack(String mchBillNo, File keyFile) throws WxErrorException; | ||||
/** | |||||
* <pre> | |||||
* 查询红包记录 | |||||
* 用于商户对已发放的红包进行查询红包的具体信息,可支持普通红包和裂变包。 | |||||
* 请求Url https://api.mch.weixin.qq.com/mmpaymkttransfers/gethbinfo | |||||
* 是否需要证书 是(证书及使用说明详见商户证书) | |||||
* 请求方式 POST | |||||
* </pre> | |||||
* | |||||
* @param mchBillNo 商户发放红包的商户订单号,比如10000098201411111234567890 | |||||
* @param keyFile 证书文件对象(即apiclient_cert.p12 商户证书文件,详细参考https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=4_3) | |||||
*/ | |||||
WxPayRedpackQueryResult queryRedpack(String mchBillNo) throws WxErrorException; | |||||
/** | /** | ||||
* <pre> | * <pre> | ||||
@@ -160,6 +160,7 @@ public class WxMpPayServiceImpl implements WxMpPayService { | |||||
@Override | @Override | ||||
@Deprecated | |||||
public WxPaySendRedpackResult sendRedpack(WxPaySendRedpackRequest request, File keyFile) | public WxPaySendRedpackResult sendRedpack(WxPaySendRedpackRequest request, File keyFile) | ||||
throws WxErrorException { | throws WxErrorException { | ||||
XStream xstream = XStreamInitializer.getInstance(); | XStream xstream = XStreamInitializer.getInstance(); | ||||
@@ -179,8 +180,30 @@ public class WxMpPayServiceImpl implements WxMpPayService { | |||||
this.checkResult(result); | this.checkResult(result); | ||||
return result; | return result; | ||||
} | } | ||||
@Override | |||||
public WxPaySendRedpackResult sendRedpack(WxPaySendRedpackRequest request) | |||||
throws WxErrorException { | |||||
XStream xstream = XStreamInitializer.getInstance(); | |||||
xstream.processAnnotations(WxPaySendRedpackRequest.class); | |||||
xstream.processAnnotations(WxPaySendRedpackResult.class); | |||||
initRequest(request); | |||||
request.setSign(this.createSign(request)); | |||||
String url = PAY_BASE_URL + "/mmpaymkttransfers/sendredpack"; | |||||
if (request.getAmtType() != null) { | |||||
//裂变红包 | |||||
url = PAY_BASE_URL + "/mmpaymkttransfers/sendgroupredpack"; | |||||
} | |||||
String responseContent = this.executeRequestWithKeyFile(url, xstream.toXML(request)); | |||||
WxPaySendRedpackResult result = (WxPaySendRedpackResult) xstream.fromXML(responseContent); | |||||
this.checkResult(result); | |||||
return result; | |||||
} | |||||
@Override | @Override | ||||
@Deprecated | |||||
public WxPayRedpackQueryResult queryRedpack(String mchBillNo, File keyFile) throws WxErrorException { | public WxPayRedpackQueryResult queryRedpack(String mchBillNo, File keyFile) throws WxErrorException { | ||||
XStream xstream = XStreamInitializer.getInstance(); | XStream xstream = XStreamInitializer.getInstance(); | ||||
xstream.processAnnotations(WxPayRedpackQueryRequest.class); | xstream.processAnnotations(WxPayRedpackQueryRequest.class); | ||||
@@ -198,6 +221,25 @@ public class WxMpPayServiceImpl implements WxMpPayService { | |||||
this.checkResult(result); | this.checkResult(result); | ||||
return result; | return result; | ||||
} | } | ||||
@Override | |||||
public WxPayRedpackQueryResult queryRedpack(String mchBillNo) throws WxErrorException { | |||||
XStream xstream = XStreamInitializer.getInstance(); | |||||
xstream.processAnnotations(WxPayRedpackQueryRequest.class); | |||||
xstream.processAnnotations(WxPayRedpackQueryResult.class); | |||||
WxPayRedpackQueryRequest request = new WxPayRedpackQueryRequest(); | |||||
request.setMchBillNo(mchBillNo); | |||||
request.setBillType("MCHT"); | |||||
initRequest(request); | |||||
request.setSign(this.createSign(request)); | |||||
String url = PAY_BASE_URL + "/mmpaymkttransfers/gethbinfo"; | |||||
String responseContent = this.executeRequestWithKeyFile(url, xstream.toXML(request)); | |||||
WxPayRedpackQueryResult result = (WxPayRedpackQueryResult) xstream.fromXML(responseContent); | |||||
this.checkResult(result); | |||||
return result; | |||||
} | |||||
@Override | @Override | ||||
public WxPayOrderQueryResult queryOrder(String transactionId, String outTradeNo) throws WxErrorException { | public WxPayOrderQueryResult queryOrder(String transactionId, String outTradeNo) throws WxErrorException { | ||||
@@ -383,7 +425,8 @@ public class WxMpPayServiceImpl implements WxMpPayService { | |||||
httpPost.releaseConnection(); | httpPost.releaseConnection(); | ||||
} | } | ||||
} | } | ||||
@Deprecated | |||||
private String executeRequestWithKeyFile(String url, File keyFile, String requestStr, String mchId) throws WxErrorException { | private String executeRequestWithKeyFile(String url, File keyFile, String requestStr, String mchId) throws WxErrorException { | ||||
try (FileInputStream inputStream = new FileInputStream(keyFile)) { | try (FileInputStream inputStream = new FileInputStream(keyFile)) { | ||||
KeyStore keyStore = KeyStore.getInstance("PKCS12"); | KeyStore keyStore = KeyStore.getInstance("PKCS12"); | ||||
@@ -414,6 +457,37 @@ public class WxMpPayServiceImpl implements WxMpPayService { | |||||
} | } | ||||
} | } | ||||
private String executeRequestWithKeyFile(String url, String requestStr) throws WxErrorException { | |||||
try { | |||||
SSLContext sslcontext = getConfig().getSSLContext(); | |||||
if(null==sslcontext){ | |||||
throw new Exception("请将配置类(WxMpInMemoryConfigStorage)中的SSLContext初始化"); | |||||
} | |||||
SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(sslcontext, new String[]{"TLSv1"}, null, | |||||
new DefaultHostnameVerifier()); | |||||
HttpPost httpPost = new HttpPost(url); | |||||
if (this.wxMpService.getHttpProxy() != null) { | |||||
httpPost.setConfig(RequestConfig.custom().setProxy(this.wxMpService.getHttpProxy()).build()); | |||||
} | |||||
try (CloseableHttpClient httpclient = HttpClients.custom().setSSLSocketFactory(sslsf).build()) { | |||||
httpPost.setEntity(new StringEntity(new String(requestStr.getBytes("UTF-8"), "ISO-8859-1"))); | |||||
try (CloseableHttpResponse response = httpclient.execute(httpPost)) { | |||||
String result = EntityUtils.toString(response.getEntity(), Consts.UTF_8); | |||||
this.log.debug("\n[URL]: {}\n[PARAMS]: {}\n[RESPONSE]: {}", url, requestStr, result); | |||||
return result; | |||||
} | |||||
} finally { | |||||
httpPost.releaseConnection(); | |||||
} | |||||
} catch (Exception e) { | |||||
this.log.error("\n[URL]: {}\n[PARAMS]: {}\n[EXCEPTION]: {}", url, requestStr, e.getMessage()); | |||||
throw new WxErrorException(WxError.newBuilder().setErrorCode(-1).setErrorMsg(e.getMessage()).build(), e); | |||||
} | |||||
} | |||||
@Override | @Override | ||||
public String createSign(Object xmlBean) { | public String createSign(Object xmlBean) { | ||||
return createSign(BeanUtils.xmlBean2Map(xmlBean), getConfig().getPartnerKey()); | return createSign(BeanUtils.xmlBean2Map(xmlBean), getConfig().getPartnerKey()); | ||||
@@ -20,8 +20,8 @@ import org.apache.http.HttpHost; | |||||
import org.apache.http.client.config.RequestConfig; | import org.apache.http.client.config.RequestConfig; | ||||
import org.apache.http.client.methods.CloseableHttpResponse; | import org.apache.http.client.methods.CloseableHttpResponse; | ||||
import org.apache.http.client.methods.HttpGet; | import org.apache.http.client.methods.HttpGet; | ||||
import org.apache.http.conn.ssl.DefaultHostnameVerifier; | |||||
import org.apache.http.conn.ssl.SSLConnectionSocketFactory; | |||||
//import org.apache.http.conn.ssl.DefaultHostnameVerifier; | |||||
//import org.apache.http.conn.ssl.SSLConnectionSocketFactory; | |||||
import org.apache.http.impl.client.BasicResponseHandler; | import org.apache.http.impl.client.BasicResponseHandler; | ||||
import org.apache.http.impl.client.CloseableHttpClient; | import org.apache.http.impl.client.CloseableHttpClient; | ||||
import org.slf4j.Logger; | import org.slf4j.Logger; | ||||
@@ -33,7 +33,7 @@ import java.util.concurrent.locks.Lock; | |||||
public class WxMpServiceImpl implements WxMpService { | public class WxMpServiceImpl implements WxMpService { | ||||
private static final JsonParser JSON_PARSER = new JsonParser(); | private static final JsonParser JSON_PARSER = new JsonParser(); | ||||
protected final Logger log = LoggerFactory.getLogger(this.getClass()); | protected final Logger log = LoggerFactory.getLogger(this.getClass()); | ||||
private WxMpConfigStorage configStorage; | private WxMpConfigStorage configStorage; | ||||
@@ -459,12 +459,12 @@ public class WxMpServiceImpl implements WxMpService { | |||||
.httpProxyUsername(this.configStorage.getHttpProxyUsername()) | .httpProxyUsername(this.configStorage.getHttpProxyUsername()) | ||||
.httpProxyPassword(this.configStorage.getHttpProxyPassword()); | .httpProxyPassword(this.configStorage.getHttpProxyPassword()); | ||||
if (this.configStorage.getSSLContext() != null) { | |||||
SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory( | |||||
this.configStorage.getSSLContext(), new String[] { "TLSv1" }, null, | |||||
new DefaultHostnameVerifier()); | |||||
apacheHttpClientBuilder.sslConnectionSocketFactory(sslsf); | |||||
} | |||||
// if (this.configStorage.getSSLContext() != null) { | |||||
// SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory( | |||||
// this.configStorage.getSSLContext(), new String[] { "TLSv1" }, null, | |||||
// new DefaultHostnameVerifier()); | |||||
// apacheHttpClientBuilder.sslConnectionSocketFactory(sslsf); | |||||
// } | |||||
if (this.configStorage.getHttpProxyHost() != null && this.configStorage.getHttpProxyPort() > 0) { | if (this.configStorage.getHttpProxyHost() != null && this.configStorage.getHttpProxyPort() > 0) { | ||||
this.httpProxy = new HttpHost(this.configStorage.getHttpProxyHost(), this.configStorage.getHttpProxyPort()); | this.httpProxy = new HttpHost(this.configStorage.getHttpProxyHost(), this.configStorage.getHttpProxyPort()); | ||||