| @@ -4,14 +4,13 @@ import me.chanjar.weixin.common.bean.WxAccessToken; | |||
| import me.chanjar.weixin.common.util.http.ApacheHttpClientBuilder; | |||
| import javax.net.ssl.SSLContext; | |||
| import java.io.File; | |||
| import java.util.concurrent.locks.Lock; | |||
| /** | |||
| * 微信客户端配置存储 | |||
| * @author chanjarster | |||
| * | |||
| * @author chanjarster | |||
| */ | |||
| public interface WxMpConfigStorage { | |||
| @@ -28,13 +27,15 @@ public interface WxMpConfigStorage { | |||
| /** | |||
| * 应该是线程安全的 | |||
| * | |||
| * @param accessToken 要更新的WxAccessToken对象 | |||
| */ | |||
| void updateAccessToken(WxAccessToken accessToken); | |||
| /** | |||
| * 应该是线程安全的 | |||
| * @param accessToken 新的accessToken值 | |||
| * | |||
| * @param accessToken 新的accessToken值 | |||
| * @param expiresInSeconds 过期时间,以秒为单位 | |||
| */ | |||
| void updateAccessToken(String accessToken, int expiresInSeconds); | |||
| @@ -52,7 +53,8 @@ public interface WxMpConfigStorage { | |||
| /** | |||
| * 应该是线程安全的 | |||
| * @param jsapiTicket 新的jsapi ticket值 | |||
| * | |||
| * @param jsapiTicket 新的jsapi ticket值 | |||
| * @param expiresInSeconds 过期时间,以秒为单位 | |||
| */ | |||
| void updateJsapiTicket(String jsapiTicket, int expiresInSeconds); | |||
| @@ -70,7 +72,8 @@ public interface WxMpConfigStorage { | |||
| /** | |||
| * 应该是线程安全的 | |||
| * @param cardApiTicket 新的cardApi ticket值 | |||
| * | |||
| * @param cardApiTicket 新的cardApi ticket值 | |||
| * @param expiresInSeconds 过期时间,以秒为单位 | |||
| */ | |||
| void updateCardApiTicket(String cardApiTicket, int expiresInSeconds); | |||
| @@ -82,21 +85,23 @@ public interface WxMpConfigStorage { | |||
| String getPartnerId(); | |||
| String getPartnerKey(); | |||
| /** | |||
| * 微信支付异步回掉地址,通知url必须为直接可访问的url,不能携带参数。 | |||
| * @since 2.5.0 | |||
| * | |||
| * @return | |||
| * @since 2.5.0 | |||
| */ | |||
| String getNotifyURL(); | |||
| /** | |||
| * 交易类型 | |||
| * <pre> | |||
| * JSAPI--公众号支付、NATIVE--原生扫码支付、APP--app支付 | |||
| * </pre> | |||
| * @since 2.5.0 | |||
| * | |||
| * @return | |||
| * @since 2.5.0 | |||
| */ | |||
| String getTradeType(); | |||
| @@ -118,23 +123,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; | |||
| /** | |||
| * 在此之前,必须将partnerId进行赋值 | |||
| * | |||
| * @param filePath apiclient_cert.p12的文件的绝对路径 | |||
| */ | |||
| void setSslContextFilePath(String filePath) throws Exception; | |||
| /** | |||
| * http client builder | |||
| * | |||
| * @return ApacheHttpClientBuilder | |||
| */ | |||
| ApacheHttpClientBuilder getApacheHttpClientBuilder(); | |||
| @@ -143,5 +145,5 @@ public interface WxMpConfigStorage { | |||
| * 是否自动刷新token | |||
| */ | |||
| boolean autoRefreshToken(); | |||
| } | |||
| @@ -1,23 +1,21 @@ | |||
| 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 org.apache.http.ssl.SSLContexts; | |||
| 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 | |||
| * | |||
| * @author chanjarster | |||
| */ | |||
| public class WxMpInMemoryConfigStorage implements WxMpConfigStorage { | |||
| @@ -63,6 +61,10 @@ public class WxMpInMemoryConfigStorage implements WxMpConfigStorage { | |||
| return this.accessToken; | |||
| } | |||
| public void setAccessToken(String accessToken) { | |||
| this.accessToken = accessToken; | |||
| } | |||
| @Override | |||
| public Lock getAccessTokenLock() { | |||
| return this.accessTokenLock; | |||
| @@ -94,15 +96,15 @@ public class WxMpInMemoryConfigStorage implements WxMpConfigStorage { | |||
| return this.jsapiTicket; | |||
| } | |||
| public void setJsapiTicket(String jsapiTicket) { | |||
| this.jsapiTicket = jsapiTicket; | |||
| } | |||
| @Override | |||
| public Lock getJsapiTicketLock() { | |||
| return this.jsapiTicketLock; | |||
| } | |||
| public void setJsapiTicket(String jsapiTicket) { | |||
| this.jsapiTicket = jsapiTicket; | |||
| } | |||
| public long getJsapiTicketExpiresTime() { | |||
| return this.jsapiTicketExpiresTime; | |||
| } | |||
| @@ -163,31 +165,35 @@ public class WxMpInMemoryConfigStorage implements WxMpConfigStorage { | |||
| return this.appId; | |||
| } | |||
| public void setAppId(String appId) { | |||
| this.appId = appId; | |||
| } | |||
| @Override | |||
| public String getSecret() { | |||
| return this.secret; | |||
| } | |||
| public void setSecret(String secret) { | |||
| this.secret = secret; | |||
| } | |||
| @Override | |||
| public String getToken() { | |||
| return this.token; | |||
| } | |||
| public void setToken(String token) { | |||
| this.token = token; | |||
| } | |||
| @Override | |||
| public long getExpiresTime() { | |||
| return this.expiresTime; | |||
| } | |||
| public void setAppId(String appId) { | |||
| this.appId = appId; | |||
| } | |||
| public void setSecret(String secret) { | |||
| this.secret = secret; | |||
| } | |||
| public void setToken(String token) { | |||
| this.token = token; | |||
| public void setExpiresTime(long expiresTime) { | |||
| this.expiresTime = expiresTime; | |||
| } | |||
| @Override | |||
| @@ -199,14 +205,6 @@ public class WxMpInMemoryConfigStorage implements WxMpConfigStorage { | |||
| this.aesKey = aesKey; | |||
| } | |||
| public void setAccessToken(String accessToken) { | |||
| this.accessToken = accessToken; | |||
| } | |||
| public void setExpiresTime(long expiresTime) { | |||
| this.expiresTime = expiresTime; | |||
| } | |||
| @Override | |||
| public String getOauth2redirectUri() { | |||
| return this.oauth2redirectUri; | |||
| @@ -259,40 +257,40 @@ public class WxMpInMemoryConfigStorage implements WxMpConfigStorage { | |||
| @Override | |||
| public String getPartnerId() { | |||
| return this.partnerId; | |||
| return this.partnerId; | |||
| } | |||
| public void setPartnerId(String partnerId) { | |||
| this.partnerId = partnerId; | |||
| this.partnerId = partnerId; | |||
| } | |||
| @Override | |||
| public String getPartnerKey() { | |||
| return this.partnerKey; | |||
| return this.partnerKey; | |||
| } | |||
| public void setPartnerKey(String partnerKey) { | |||
| this.partnerKey = partnerKey; | |||
| this.partnerKey = partnerKey; | |||
| } | |||
| public String getNotifyURL() { | |||
| return notifyURL; | |||
| } | |||
| public String getNotifyURL() { | |||
| return notifyURL; | |||
| } | |||
| public void setNotifyURL(String notifyURL) { | |||
| this.notifyURL = notifyURL; | |||
| } | |||
| public void setNotifyURL(String notifyURL) { | |||
| this.notifyURL = notifyURL; | |||
| } | |||
| public String getTradeType() { | |||
| return tradeType; | |||
| } | |||
| public String getTradeType() { | |||
| return tradeType; | |||
| } | |||
| public void setTradeType(String tradeType) { | |||
| this.tradeType = tradeType; | |||
| } | |||
| public void setTradeType(String tradeType) { | |||
| this.tradeType = tradeType; | |||
| } | |||
| @Override | |||
| @Override | |||
| public File getTmpDirFile() { | |||
| return this.tmpDirFile; | |||
| } | |||
| @@ -302,47 +300,43 @@ public class WxMpInMemoryConfigStorage implements WxMpConfigStorage { | |||
| } | |||
| @Override | |||
| public SSLContext getSSLContext() { | |||
| public SSLContext getSslContext() { | |||
| return this.sslContext; | |||
| } | |||
| @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; | |||
| } | |||
| public void setSslContext(SSLContext context) { | |||
| this.sslContext = context; | |||
| } | |||
| @Override | |||
| public ApacheHttpClientBuilder getApacheHttpClientBuilder() { | |||
| return this.apacheHttpClientBuilder; | |||
| 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 boolean autoRefreshToken() { | |||
| return true; | |||
| public ApacheHttpClientBuilder getApacheHttpClientBuilder() { | |||
| return this.apacheHttpClientBuilder; | |||
| } | |||
| public void setApacheHttpClientBuilder(ApacheHttpClientBuilder apacheHttpClientBuilder) { | |||
| this.apacheHttpClientBuilder = apacheHttpClientBuilder; | |||
| } | |||
| @Override | |||
| public boolean autoRefreshToken() { | |||
| return true; | |||
| } | |||
| } | |||
| @@ -7,7 +7,6 @@ import me.chanjar.weixin.mp.bean.pay.request.WxPaySendRedpackRequest; | |||
| import me.chanjar.weixin.mp.bean.pay.request.WxPayUnifiedOrderRequest; | |||
| import me.chanjar.weixin.mp.bean.pay.result.*; | |||
| import java.io.File; | |||
| import java.util.Map; | |||
| /** | |||
| @@ -76,10 +75,9 @@ public interface WxMpPayService { | |||
| * </pre> | |||
| * | |||
| * @param request 请求对象 | |||
| * @param keyFile 证书文件对象(即apiclient_cert.p12 商户证书文件,详细参考https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=4_3) | |||
| * @return 退款操作结果 | |||
| */ | |||
| WxPayRefundResult refund(WxPayRefundRequest request, File keyFile) throws WxErrorException; | |||
| WxPayRefundResult refund(WxPayRefundRequest request) throws WxErrorException; | |||
| /** | |||
| * <pre> | |||
| @@ -128,7 +126,7 @@ public interface WxMpPayService { | |||
| /** | |||
| * 微信公众号支付签名算法(详见:https://pay.weixin.qq.com/wiki/doc/api/tools/cash_coupon.php?chapter=4_3) | |||
| * | |||
| * @param prams 参数信息,默认使用配置中的PartnerKey进行签名 | |||
| * @param prams 参数信息,默认使用配置中的PartnerKey进行签名 | |||
| * @return 签名字符串 | |||
| * @see #createSign(Map, String) | |||
| */ | |||
| @@ -176,7 +174,7 @@ public interface WxMpPayService { | |||
| /** | |||
| * 校验签名是否正确 | |||
| * | |||
| * @param params 需要校验的参数Map | |||
| * @param params 需要校验的参数Map | |||
| * @param signKey 校验的签名Key | |||
| * @return true - 签名校验成功,false - 签名校验失败 | |||
| * @see #checkSign(Map, String) | |||
| @@ -195,41 +193,9 @@ public interface WxMpPayService { | |||
| * </pre> | |||
| * | |||
| * @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> | |||
| * 查询红包记录 | |||
| * 用于商户对已发放的红包进行查询红包的具体信息,可支持普通红包和裂变包。 | |||
| * 请求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) | |||
| */ | |||
| @Deprecated | |||
| WxPayRedpackQueryResult queryRedpack(String mchBillNo, File keyFile) throws WxErrorException; | |||
| /** | |||
| * <pre> | |||
| * 查询红包记录 | |||
| @@ -240,7 +206,6 @@ public interface WxMpPayService { | |||
| * </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; | |||
| @@ -255,9 +220,8 @@ public interface WxMpPayService { | |||
| * </pre> | |||
| * | |||
| * @param request 请求对象 | |||
| * @param keyFile 证书文件对象(即apiclient_cert.p12 商户证书文件,详细参考https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=4_3) | |||
| */ | |||
| WxEntPayResult entPay(WxEntPayRequest request, File keyFile) throws WxErrorException; | |||
| WxEntPayResult entPay(WxEntPayRequest request) throws WxErrorException; | |||
| /** | |||
| * <pre> | |||
| @@ -268,8 +232,7 @@ public interface WxMpPayService { | |||
| * </pre> | |||
| * | |||
| * @param partnerTradeNo 商户订单号 | |||
| * @param keyFile 证书文件对象(即apiclient_cert.p12 商户证书文件,详细参考https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=4_3) | |||
| */ | |||
| WxEntPayQueryResult queryEntPay(String partnerTradeNo, File keyFile) throws WxErrorException; | |||
| WxEntPayQueryResult queryEntPay(String partnerTradeNo) throws WxErrorException; | |||
| } | |||
| @@ -23,16 +23,12 @@ import org.apache.http.conn.ssl.SSLConnectionSocketFactory; | |||
| import org.apache.http.entity.StringEntity; | |||
| import org.apache.http.impl.client.CloseableHttpClient; | |||
| import org.apache.http.impl.client.HttpClients; | |||
| import org.apache.http.ssl.SSLContexts; | |||
| import org.apache.http.util.EntityUtils; | |||
| import org.slf4j.Logger; | |||
| import org.slf4j.LoggerFactory; | |||
| import javax.net.ssl.SSLContext; | |||
| import java.io.File; | |||
| import java.io.FileInputStream; | |||
| import java.io.IOException; | |||
| import java.security.KeyStore; | |||
| import java.util.*; | |||
| /** | |||
| @@ -44,7 +40,7 @@ public class WxMpPayServiceImpl implements WxMpPayService { | |||
| private static final String PAY_BASE_URL = "https://api.mch.weixin.qq.com"; | |||
| private static final String[] TRADE_TYPES = new String[]{"JSAPI", "NATIVE", "APP"}; | |||
| private static final String[] REFUND_ACCOUNT = new String[]{"REFUND_SOURCE_RECHARGE_FUNDS","REFUND_SOURCE_UNSETTLED_FUNDS"}; | |||
| private static final String[] REFUND_ACCOUNT = new String[]{"REFUND_SOURCE_RECHARGE_FUNDS", "REFUND_SOURCE_UNSETTLED_FUNDS"}; | |||
| private final Logger log = LoggerFactory.getLogger(this.getClass()); | |||
| private WxMpService wxMpService; | |||
| @@ -57,20 +53,20 @@ public class WxMpPayServiceImpl implements WxMpPayService { | |||
| } | |||
| @Override | |||
| public WxPayRefundResult refund(WxPayRefundRequest request, File keyFile) throws WxErrorException { | |||
| public WxPayRefundResult refund(WxPayRefundRequest request) throws WxErrorException { | |||
| XStream xstream = XStreamInitializer.getInstance(); | |||
| xstream.processAnnotations(WxPayRefundRequest.class); | |||
| xstream.processAnnotations(WxPayRefundResult.class); | |||
| initRequest(request); | |||
| if(StringUtils.isBlank(request.getOpUserId())){ | |||
| if (StringUtils.isBlank(request.getOpUserId())) { | |||
| request.setOpUserId(getConfig().getPartnerId()); | |||
| } | |||
| checkParameters(request); | |||
| request.setSign(this.createSign(request)); | |||
| String url = PAY_BASE_URL + "/secapi/pay/refund"; | |||
| String responseContent = this.executeRequestWithKeyFile(url, keyFile, xstream.toXML(request), request.getMchId()); | |||
| String responseContent = this.executeRequestWithKeyFile(url, xstream.toXML(request)); | |||
| WxPayRefundResult result = (WxPayRefundResult) xstream.fromXML(responseContent); | |||
| this.checkResult(result); | |||
| return result; | |||
| @@ -158,10 +154,8 @@ public class WxMpPayServiceImpl implements WxMpPayService { | |||
| } | |||
| } | |||
| @Override | |||
| @Deprecated | |||
| public WxPaySendRedpackResult sendRedpack(WxPaySendRedpackRequest request, File keyFile) | |||
| public WxPaySendRedpackResult sendRedpack(WxPaySendRedpackRequest request) | |||
| throws WxErrorException { | |||
| XStream xstream = XStreamInitializer.getInstance(); | |||
| xstream.processAnnotations(WxPaySendRedpackRequest.class); | |||
| @@ -175,36 +169,14 @@ public class WxMpPayServiceImpl implements WxMpPayService { | |||
| //裂变红包 | |||
| url = PAY_BASE_URL + "/mmpaymkttransfers/sendgroupredpack"; | |||
| } | |||
| String responseContent = this.executeRequestWithKeyFile(url, keyFile, xstream.toXML(request), request.getMchId()); | |||
| String responseContent = this.executeRequestWithKeyFile(url, xstream.toXML(request)); | |||
| WxPaySendRedpackResult result = (WxPaySendRedpackResult) xstream.fromXML(responseContent); | |||
| 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 { | |||
| public WxPayRedpackQueryResult queryRedpack(String mchBillNo) throws WxErrorException { | |||
| XStream xstream = XStreamInitializer.getInstance(); | |||
| xstream.processAnnotations(WxPayRedpackQueryRequest.class); | |||
| xstream.processAnnotations(WxPayRedpackQueryResult.class); | |||
| @@ -216,30 +188,11 @@ public class WxMpPayServiceImpl implements WxMpPayService { | |||
| request.setSign(this.createSign(request)); | |||
| String url = PAY_BASE_URL + "/mmpaymkttransfers/gethbinfo"; | |||
| String responseContent = this.executeRequestWithKeyFile(url, keyFile, xstream.toXML(request), request.getMchId()); | |||
| String responseContent = this.executeRequestWithKeyFile(url, xstream.toXML(request)); | |||
| WxPayRedpackQueryResult result = (WxPayRedpackQueryResult) xstream.fromXML(responseContent); | |||
| 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 { | |||
| @@ -296,17 +249,17 @@ public class WxMpPayServiceImpl implements WxMpPayService { | |||
| XStream xstream = XStreamInitializer.getInstance(); | |||
| xstream.processAnnotations(WxPayUnifiedOrderRequest.class); | |||
| xstream.processAnnotations(WxPayUnifiedOrderResult.class); | |||
| initRequest(request); | |||
| if(StringUtils.isBlank(request.getNotifyURL())){ | |||
| if (StringUtils.isBlank(request.getNotifyURL())) { | |||
| request.setNotifyURL(getConfig().getNotifyURL()); | |||
| } | |||
| if(StringUtils.isBlank(request.getTradeType())){ | |||
| if (StringUtils.isBlank(request.getTradeType())) { | |||
| request.setTradeType(getConfig().getTradeType()); | |||
| } | |||
| checkParameters(request);//校验参数 | |||
| request.setSign(this.createSign(request)); | |||
| String url = PAY_BASE_URL + "/pay/unifiedorder"; | |||
| String xmlParam = xstream.toXML(request); | |||
| log.debug("微信统一下单接口,URL:{},参数:{}", url, xmlParam); | |||
| @@ -317,15 +270,15 @@ public class WxMpPayServiceImpl implements WxMpPayService { | |||
| this.checkResult(result); | |||
| return result; | |||
| } | |||
| private void initRequest(WxPayBaseRequest request){ | |||
| if(StringUtils.isBlank(request.getAppid())){ | |||
| private void initRequest(WxPayBaseRequest request) { | |||
| if (StringUtils.isBlank(request.getAppid())) { | |||
| request.setAppid(getConfig().getAppId()); | |||
| } | |||
| if(StringUtils.isBlank(request.getMchId())){ | |||
| if (StringUtils.isBlank(request.getMchId())) { | |||
| request.setMchId(getConfig().getPartnerId()); | |||
| } | |||
| if(StringUtils.isBlank(request.getNonceStr())){ | |||
| if (StringUtils.isBlank(request.getNonceStr())) { | |||
| request.setNonceStr(String.valueOf(System.currentTimeMillis())); | |||
| } | |||
| } | |||
| @@ -370,25 +323,25 @@ public class WxMpPayServiceImpl implements WxMpPayService { | |||
| } | |||
| @Override | |||
| public WxEntPayResult entPay(WxEntPayRequest request, File keyFile) throws WxErrorException { | |||
| public WxEntPayResult entPay(WxEntPayRequest request) throws WxErrorException { | |||
| XStream xstream = XStreamInitializer.getInstance(); | |||
| xstream.processAnnotations(WxEntPayRequest.class); | |||
| xstream.processAnnotations(WxEntPayResult.class); | |||
| initRequest(request); | |||
| BeanUtils.checkRequiredFields(request); | |||
| request.setSign(this.createSign(request)); | |||
| String url = PAY_BASE_URL + "/mmpaymkttransfers/promotion/transfers"; | |||
| String responseContent = this.executeRequestWithKeyFile(url, keyFile, xstream.toXML(request), request.getMchId()); | |||
| String responseContent = this.executeRequestWithKeyFile(url, xstream.toXML(request)); | |||
| WxEntPayResult result = (WxEntPayResult) xstream.fromXML(responseContent); | |||
| this.checkResult(result); | |||
| return result; | |||
| } | |||
| @Override | |||
| public WxEntPayQueryResult queryEntPay(String partnerTradeNo, File keyFile) throws WxErrorException { | |||
| public WxEntPayQueryResult queryEntPay(String partnerTradeNo) throws WxErrorException { | |||
| XStream xstream = XStreamInitializer.getInstance(); | |||
| xstream.processAnnotations(WxEntPayQueryRequest.class); | |||
| xstream.processAnnotations(WxEntPayQueryResult.class); | |||
| @@ -398,7 +351,7 @@ public class WxMpPayServiceImpl implements WxMpPayService { | |||
| request.setSign(this.createSign(request)); | |||
| String url = PAY_BASE_URL + "/mmpaymkttransfers/gettransferinfo"; | |||
| String responseContent = this.executeRequestWithKeyFile(url, keyFile, xstream.toXML(request), request.getMchId()); | |||
| String responseContent = this.executeRequestWithKeyFile(url, xstream.toXML(request)); | |||
| WxEntPayQueryResult result = (WxEntPayQueryResult) xstream.fromXML(responseContent); | |||
| this.checkResult(result); | |||
| return result; | |||
| @@ -425,15 +378,15 @@ 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"); | |||
| keyStore.load(inputStream, mchId.toCharArray()); | |||
| SSLContext sslcontext = SSLContexts.custom().loadKeyMaterial(keyStore, mchId.toCharArray()).build(); | |||
| SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(sslcontext, new String[]{"TLSv1"}, null, | |||
| private String executeRequestWithKeyFile(String url, String requestStr) throws WxErrorException { | |||
| try { | |||
| SSLContext sslContext = getConfig().getSslContext(); | |||
| if (null == sslContext) { | |||
| throw new Exception("请将配置类(即WxMpConfigStorage的实现类)中的SSLContext初始化"); | |||
| } | |||
| SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(sslContext, new String[]{"TLSv1"}, null, | |||
| new DefaultHostnameVerifier()); | |||
| HttpPost httpPost = new HttpPost(url); | |||
| @@ -456,38 +409,7 @@ public class WxMpPayServiceImpl implements WxMpPayService { | |||
| throw new WxErrorException(WxError.newBuilder().setErrorCode(-1).setErrorMsg(e.getMessage()).build(), e); | |||
| } | |||
| } | |||
| 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,6 @@ 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.impl.client.BasicResponseHandler; | |||
| import org.apache.http.impl.client.CloseableHttpClient; | |||
| import org.slf4j.Logger; | |||
| @@ -30,55 +28,39 @@ import org.slf4j.LoggerFactory; | |||
| import java.io.IOException; | |||
| import java.util.concurrent.locks.Lock; | |||
| //import org.apache.http.conn.ssl.DefaultHostnameVerifier; | |||
| //import org.apache.http.conn.ssl.SSLConnectionSocketFactory; | |||
| public class WxMpServiceImpl implements WxMpService { | |||
| private static final JsonParser JSON_PARSER = new JsonParser(); | |||
| protected final Logger log = LoggerFactory.getLogger(this.getClass()); | |||
| protected final Logger log = LoggerFactory.getLogger(this.getClass()); | |||
| protected WxSessionManager sessionManager = new StandardSessionManager(); | |||
| private WxMpConfigStorage configStorage; | |||
| private WxMpKefuService kefuService = new WxMpKefuServiceImpl(this); | |||
| private WxMpMaterialService materialService = new WxMpMaterialServiceImpl(this); | |||
| private WxMpMenuService menuService = new WxMpMenuServiceImpl(this); | |||
| private WxMpUserService userService = new WxMpUserServiceImpl(this); | |||
| private WxMpUserTagService tagService = new WxMpUserTagServiceImpl(this); | |||
| private WxMpQrcodeService qrCodeService = new WxMpQrcodeServiceImpl(this); | |||
| private WxMpCardService cardService = new WxMpCardServiceImpl(this); | |||
| private WxMpPayService payService = new WxMpPayServiceImpl(this); | |||
| private WxMpStoreService storeService = new WxMpStoreServiceImpl(this); | |||
| private WxMpDataCubeService dataCubeService = new WxMpDataCubeServiceImpl(this); | |||
| private WxMpUserBlacklistService blackListService = new WxMpUserBlacklistServiceImpl(this); | |||
| private WxMpTemplateMsgService templateMsgService = new WxMpTemplateMsgServiceImpl(this); | |||
| private WxMpDeviceService deviceService = new WxMpDeviceServiceImpl(this); | |||
| private CloseableHttpClient httpClient; | |||
| private HttpHost httpProxy; | |||
| private int retrySleepMillis = 1000; | |||
| private int maxRetryTimes = 5; | |||
| protected WxSessionManager sessionManager = new StandardSessionManager(); | |||
| @Override | |||
| public boolean checkSignature(String timestamp, String nonce, String signature) { | |||
| try { | |||
| return SHA1.gen(this.configStorage.getToken(), timestamp, nonce) | |||
| .equals(signature); | |||
| .equals(signature); | |||
| } catch (Exception e) { | |||
| return false; | |||
| } | |||
| @@ -101,8 +83,8 @@ public class WxMpServiceImpl implements WxMpService { | |||
| if (this.configStorage.isAccessTokenExpired()) { | |||
| String url = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential" + | |||
| "&appid=" + this.configStorage.getAppId() + "&secret=" | |||
| + this.configStorage.getSecret(); | |||
| "&appid=" + this.configStorage.getAppId() + "&secret=" | |||
| + this.configStorage.getSecret(); | |||
| try { | |||
| HttpGet httpGet = new HttpGet(url); | |||
| if (this.httpProxy != null) { | |||
| @@ -117,8 +99,8 @@ public class WxMpServiceImpl implements WxMpService { | |||
| } | |||
| WxAccessToken accessToken = WxAccessToken.fromJson(resultContent); | |||
| this.configStorage.updateAccessToken(accessToken.getAccessToken(), | |||
| accessToken.getExpiresIn()); | |||
| }finally { | |||
| accessToken.getExpiresIn()); | |||
| } finally { | |||
| httpGet.releaseConnection(); | |||
| } | |||
| } catch (IOException e) { | |||
| @@ -167,7 +149,7 @@ public class WxMpServiceImpl implements WxMpService { | |||
| String noncestr = RandomUtils.getRandomStr(); | |||
| String jsapiTicket = getJsapiTicket(false); | |||
| String signature = SHA1.genWithAmple("jsapi_ticket=" + jsapiTicket, | |||
| "noncestr=" + noncestr, "timestamp=" + timestamp, "url=" + url); | |||
| "noncestr=" + noncestr, "timestamp=" + timestamp, "url=" + url); | |||
| WxJsapiSignature jsapiSignature = new WxJsapiSignature(); | |||
| jsapiSignature.setAppid(this.configStorage.getAppId()); | |||
| jsapiSignature.setTimestamp(timestamp); | |||
| @@ -247,7 +229,7 @@ public class WxMpServiceImpl implements WxMpService { | |||
| @Override | |||
| public String buildQrConnectUrl(String redirectURI, String scope, | |||
| String state) { | |||
| String state) { | |||
| StringBuilder url = new StringBuilder(); | |||
| url.append("https://open.weixin.qq.com/connect/qrconnect?"); | |||
| url.append("appid=").append(this.configStorage.getAppId()); | |||
| @@ -366,7 +348,7 @@ public class WxMpServiceImpl implements WxMpService { | |||
| do { | |||
| try { | |||
| T result = executeInternal(executor, uri, data); | |||
| this.log.debug("\n[URL]: {}\n[PARAMS]: {}\n[RESPONSE]: {}",uri, data, result); | |||
| this.log.debug("\n[URL]: {}\n[PARAMS]: {}\n[RESPONSE]: {}", uri, data, result); | |||
| return result; | |||
| } catch (WxErrorException e) { | |||
| if (retryTimes + 1 > this.maxRetryTimes) { | |||
| @@ -416,7 +398,7 @@ public class WxMpServiceImpl implements WxMpService { | |||
| if (error.getErrorCode() == 42001 || error.getErrorCode() == 40001) { | |||
| // 强制设置wxMpConfigStorage它的access token过期了,这样在下一次请求里就会刷新access token | |||
| this.configStorage.expireAccessToken(); | |||
| if(this.configStorage.autoRefreshToken()){ | |||
| if (this.configStorage.autoRefreshToken()) { | |||
| return this.execute(executor, uri, data); | |||
| } | |||
| } | |||
| @@ -441,30 +423,24 @@ public class WxMpServiceImpl implements WxMpService { | |||
| return this.httpClient; | |||
| } | |||
| @Override | |||
| public void setWxMpConfigStorage(WxMpConfigStorage wxConfigProvider) { | |||
| this.configStorage = wxConfigProvider; | |||
| this.initHttpClient(); | |||
| } | |||
| private void initHttpClient() { | |||
| ApacheHttpClientBuilder apacheHttpClientBuilder = this.configStorage | |||
| .getApacheHttpClientBuilder(); | |||
| .getApacheHttpClientBuilder(); | |||
| if (null == apacheHttpClientBuilder) { | |||
| apacheHttpClientBuilder = DefaultApacheHttpClientBuilder.get(); | |||
| } | |||
| apacheHttpClientBuilder.httpProxyHost(this.configStorage.getHttpProxyHost()) | |||
| .httpProxyPort(this.configStorage.getHttpProxyPort()) | |||
| .httpProxyUsername(this.configStorage.getHttpProxyUsername()) | |||
| .httpProxyPassword(this.configStorage.getHttpProxyPassword()); | |||
| .httpProxyPort(this.configStorage.getHttpProxyPort()) | |||
| .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()); | |||
| @@ -478,6 +454,12 @@ public class WxMpServiceImpl implements WxMpService { | |||
| return this.configStorage; | |||
| } | |||
| @Override | |||
| public void setWxMpConfigStorage(WxMpConfigStorage wxConfigProvider) { | |||
| this.configStorage = wxConfigProvider; | |||
| this.initHttpClient(); | |||
| } | |||
| @Override | |||
| public void setRetrySleepMillis(int retrySleepMillis) { | |||
| this.retrySleepMillis = retrySleepMillis; | |||
| @@ -34,7 +34,7 @@ public class WxMpPayServiceImplTest { | |||
| } | |||
| /** | |||
| * Test method for {@link me.chanjar.weixin.mp.api.impl.WxMpPayServiceImpl#refund(WxPayRefundRequest, File)} . | |||
| * Test method for {@link me.chanjar.weixin.mp.api.impl.WxMpPayServiceImpl#refund(WxPayRefundRequest)} . | |||
| */ | |||
| @Test | |||
| public void testRefund() throws Exception { | |||
| @@ -43,8 +43,7 @@ public class WxMpPayServiceImplTest { | |||
| request.setOutTradeNo("1111"); | |||
| request.setTotalFee(1222); | |||
| request.setRefundFee(111); | |||
| File keyFile = new File("E:\\dlt.p12"); | |||
| WxPayRefundResult result = this.wxService.getPayService().refund(request, keyFile); | |||
| WxPayRefundResult result = this.wxService.getPayService().refund(request); | |||
| System.err.println(result); | |||
| } | |||
| @@ -72,7 +71,7 @@ public class WxMpPayServiceImplTest { | |||
| } | |||
| /** | |||
| * Test method for {@link me.chanjar.weixin.mp.api.impl.WxMpPayServiceImpl#sendRedpack(WxPaySendRedpackRequest, File)} . | |||
| * Test method for {@link me.chanjar.weixin.mp.api.impl.WxMpPayServiceImpl#sendRedpack(WxPaySendRedpackRequest)} . | |||
| */ | |||
| @Test | |||
| public void testSendRedpack() throws Exception { | |||
| @@ -82,18 +81,17 @@ public class WxMpPayServiceImplTest { | |||
| request.setMchBillNo("aaaa"); | |||
| request | |||
| .setReOpenid(((WxXmlMpInMemoryConfigStorage) this.wxService.getWxMpConfigStorage()).getOpenid()); | |||
| File keyFile = new File("E:\\dlt.p12"); | |||
| WxPaySendRedpackResult redpackResult = this.wxService.getPayService().sendRedpack(request, keyFile); | |||
| WxPaySendRedpackResult redpackResult = this.wxService.getPayService().sendRedpack(request); | |||
| System.err.println(redpackResult); | |||
| } | |||
| /** | |||
| * Test method for {@link me.chanjar.weixin.mp.api.impl.WxMpPayServiceImpl#queryRedpack(String, File)}. | |||
| * Test method for {@link me.chanjar.weixin.mp.api.impl.WxMpPayServiceImpl#queryRedpack(String)}. | |||
| */ | |||
| @Test | |||
| public void testQueryRedpack() throws Exception { | |||
| File keyFile = new File("E:\\dlt.p12"); | |||
| WxPayRedpackQueryResult redpackResult = this.wxService.getPayService().queryRedpack("aaaa", keyFile); | |||
| WxPayRedpackQueryResult redpackResult = this.wxService.getPayService().queryRedpack("aaaa"); | |||
| System.err.println(redpackResult); | |||
| } | |||
| @@ -128,21 +126,19 @@ public class WxMpPayServiceImplTest { | |||
| } | |||
| /** | |||
| * Test method for {@link me.chanjar.weixin.mp.api.impl.WxMpPayServiceImpl#entPay(WxEntPayRequest, File)}. | |||
| * Test method for {@link me.chanjar.weixin.mp.api.impl.WxMpPayServiceImpl#entPay(WxEntPayRequest)}. | |||
| */ | |||
| @Test | |||
| public final void testEntPay() throws WxErrorException { | |||
| File keyFile = new File("E:\\dlt.p12"); | |||
| WxEntPayRequest request = new WxEntPayRequest(); | |||
| System.err.println(this.wxService.getPayService().entPay(request, keyFile)); | |||
| System.err.println(this.wxService.getPayService().entPay(request)); | |||
| } | |||
| /** | |||
| * Test method for {@link me.chanjar.weixin.mp.api.impl.WxMpPayServiceImpl#queryEntPay(String, File)}. | |||
| * Test method for {@link me.chanjar.weixin.mp.api.impl.WxMpPayServiceImpl#queryEntPay(String)}. | |||
| */ | |||
| @Test | |||
| public final void testQueryEntPay() throws WxErrorException { | |||
| File keyFile = new File("E:\\dlt.p12"); | |||
| System.err.println(this.wxService.getPayService().queryEntPay("11212121", keyFile)); | |||
| System.err.println(this.wxService.getPayService().queryEntPay("11212121")); | |||
| } | |||
| } | |||