| @@ -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()); | |||