@@ -118,7 +118,20 @@ public interface WxMpConfigStorage { | |||
File getTmpDirFile(); | |||
@Deprecated | |||
SSLContext getSSLContext(); | |||
SSLContext getSslContext(); | |||
void setSslContext(SSLContext sslContext); | |||
/** | |||
* 在此之前,必须将partnerId进行赋值 | |||
* | |||
* @param filePath | |||
* apiclient_cert.p12的文件的绝对路径 | |||
*/ | |||
void setSslContextFilePath(String filePath) throws Exception; | |||
/** | |||
* http client builder | |||
@@ -1,14 +1,19 @@ | |||
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.FileInputStream; | |||
import java.security.KeyStore; | |||
import java.util.concurrent.locks.Lock; | |||
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,在实际生产环境中应该将这些配置持久化 | |||
* @author chanjarster | |||
@@ -300,11 +305,33 @@ public class WxMpInMemoryConfigStorage implements WxMpConfigStorage { | |||
public SSLContext getSSLContext() { | |||
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 | |||
public ApacheHttpClientBuilder getApacheHttpClientBuilder() { | |||
return this.apacheHttpClientBuilder; | |||
@@ -197,8 +197,24 @@ public interface WxMpPayService { | |||
* @param request 请求对象 | |||
* @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; | |||
/** | |||
* 发送微信红包给个人用户 | |||
* <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> | |||
* 查询红包记录 | |||
@@ -211,7 +227,22 @@ public interface WxMpPayService { | |||
* @param mchBillNo 商户发放红包的商户订单号,比如10000098201411111234567890 | |||
* @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; | |||
/** | |||
* <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> | |||
@@ -160,6 +160,7 @@ public class WxMpPayServiceImpl implements WxMpPayService { | |||
@Override | |||
@Deprecated | |||
public WxPaySendRedpackResult sendRedpack(WxPaySendRedpackRequest request, File keyFile) | |||
throws WxErrorException { | |||
XStream xstream = XStreamInitializer.getInstance(); | |||
@@ -179,8 +180,30 @@ public class WxMpPayServiceImpl implements WxMpPayService { | |||
this.checkResult(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 | |||
@Deprecated | |||
public WxPayRedpackQueryResult queryRedpack(String mchBillNo, File keyFile) throws WxErrorException { | |||
XStream xstream = XStreamInitializer.getInstance(); | |||
xstream.processAnnotations(WxPayRedpackQueryRequest.class); | |||
@@ -198,6 +221,25 @@ public class WxMpPayServiceImpl implements WxMpPayService { | |||
this.checkResult(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 | |||
public WxPayOrderQueryResult queryOrder(String transactionId, String outTradeNo) throws WxErrorException { | |||
@@ -383,7 +425,8 @@ public class WxMpPayServiceImpl implements WxMpPayService { | |||
httpPost.releaseConnection(); | |||
} | |||
} | |||
@Deprecated | |||
private String executeRequestWithKeyFile(String url, File keyFile, String requestStr, String mchId) throws WxErrorException { | |||
try (FileInputStream inputStream = new FileInputStream(keyFile)) { | |||
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 | |||
public String createSign(Object xmlBean) { | |||
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.methods.CloseableHttpResponse; | |||
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.CloseableHttpClient; | |||
import org.slf4j.Logger; | |||
@@ -33,7 +33,7 @@ import java.util.concurrent.locks.Lock; | |||
public class WxMpServiceImpl implements WxMpService { | |||
private static final JsonParser JSON_PARSER = new JsonParser(); | |||
protected final Logger log = LoggerFactory.getLogger(this.getClass()); | |||
private WxMpConfigStorage configStorage; | |||
@@ -459,12 +459,12 @@ public class WxMpServiceImpl implements WxMpService { | |||
.httpProxyUsername(this.configStorage.getHttpProxyUsername()) | |||
.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) { | |||
this.httpProxy = new HttpHost(this.configStorage.getHttpProxyHost(), this.configStorage.getHttpProxyPort()); | |||