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 d93c3080..b9b11fd7 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 @@ -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 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 61b75fbb..9fec33f8 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 @@ -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; diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpPayService.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpPayService.java index afe888f8..b4dd3b29 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpPayService.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpPayService.java @@ -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; + /** + * 发送微信红包给个人用户 + *
+ * 文档详见: + * 发送普通红包 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 + *+ * + * @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; + /** *
* 查询红包记录 @@ -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; + + /** + *+ * 查询红包记录 + * 用于商户对已发放的红包进行查询红包的具体信息,可支持普通红包和裂变包。 + * 请求Url https://api.mch.weixin.qq.com/mmpaymkttransfers/gethbinfo + * 是否需要证书 是(证书及使用说明详见商户证书) + * 请求方式 POST + *+ * + * @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; /** *diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/impl/WxMpPayServiceImpl.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/impl/WxMpPayServiceImpl.java index 9bd4dad2..8751fc6c 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/impl/WxMpPayServiceImpl.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/impl/WxMpPayServiceImpl.java @@ -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()); diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/impl/WxMpServiceImpl.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/impl/WxMpServiceImpl.java index ccb0bd2e..0008e20d 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/impl/WxMpServiceImpl.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/impl/WxMpServiceImpl.java @@ -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());