diff --git a/pay-java-common/pom.xml b/pay-java-common/pom.xml index 75f73e9..9595f5f 100644 --- a/pay-java-common/pom.xml +++ b/pay-java-common/pom.xml @@ -41,7 +41,11 @@ core 3.3.0 - + + com.alipay + app + 1.0 + \ No newline at end of file diff --git a/pay-java-common/src/main/java/in/egan/pay/common/api/BasePayConfigStorage.java b/pay-java-common/src/main/java/in/egan/pay/common/api/BasePayConfigStorage.java index 62732dc..ef213ba 100644 --- a/pay-java-common/src/main/java/in/egan/pay/common/api/BasePayConfigStorage.java +++ b/pay-java-common/src/main/java/in/egan/pay/common/api/BasePayConfigStorage.java @@ -7,9 +7,9 @@ import java.util.concurrent.locks.ReentrantLock; /** * 支付基础配置存储 - * @author egan + * @author: egan * @email egzosn@gmail.com - * @date 2016-5-18 14:09:01 + * @date 2017/3/5 20:33 */ public abstract class BasePayConfigStorage implements PayConfigStorage{ @@ -31,13 +31,11 @@ public abstract class BasePayConfigStorage implements PayConfigStorage{ //支付类型 aliPay 支付宝, wxPay微信..等等,开发者自定义,唯一 protected volatile String payType; + /** * 消息来源类型 - * @see PayConsts#MSG_XML - * @see PayConsts#MSG_TEXT - * @see PayConsts#MSG_JSON */ - protected volatile MsgType msgType; + protected volatile MsgType msgType; // 访问令牌 每次请求其他方法都要传入的值 @@ -47,98 +45,6 @@ public abstract class BasePayConfigStorage implements PayConfigStorage{ //授权码锁 protected Lock accessTokenLock = new ReentrantLock(); - - protected volatile String httpProxyHost; - protected volatile int httpProxyPort; - protected volatile String httpProxyUsername; - protected volatile String httpProxyPassword; - - - /** - * 合作商唯一标识 - * @see #getPartner 代替者 - */ - public String getPid(){ - throw null; - } - - @Override - public String getInputCharset() { - return inputCharset; - } - - public void setInputCharset(String inputCharset) { - this.inputCharset = inputCharset; - } - - - - @Override - public String getNotifyUrl() { - return notifyUrl; - } - - public void setNotifyUrl(String notifyUrl) { - this.notifyUrl = notifyUrl; - } - - @Override - public String getReturnUrl() { - return returnUrl; - } - - public void setReturnUrl(String returnUrl) { - this.returnUrl = returnUrl; - } - - public void setSignType(String signType) { - this.signType = signType; - } - - @Override - public String getSignType() { - return signType; - } - - - - @Override - public String getHttpProxyHost() { - return httpProxyHost; - } - - public void setHttpProxyHost(String httpProxyHost) { - this.httpProxyHost = httpProxyHost; - } - - @Override - public int getHttpProxyPort() { - return httpProxyPort; - } - - public void setHttpProxyPort(int httpProxyPort) { - this.httpProxyPort = httpProxyPort; - } - - @Override - public String getHttpProxyUsername() { - return httpProxyUsername; - } - - public void setHttpProxyUsername(String httpProxyUsername) { - this.httpProxyUsername = httpProxyUsername; - } - - @Override - public String getHttpProxyPassword() { - return httpProxyPassword; - } - - - public void setHttpProxyPassword(String httpProxyPassword) { - this.httpProxyPassword = httpProxyPassword; - } - @Override public String getKeyPrivate() { return keyPrivate; @@ -157,19 +63,43 @@ public abstract class BasePayConfigStorage implements PayConfigStorage{ this.keyPublic = keyPublic; } - - @Override - public String getToken() { - return null; + public String getNotifyUrl() { + return notifyUrl; } + public void setNotifyUrl(String notifyUrl) { + this.notifyUrl = notifyUrl; + } - /** - * 支付类型 自定义 - * 这里暂定 aliPay 支付宝, wxPay微信支付 - * @return - */ + @Override + public String getReturnUrl() { + return returnUrl; + } + + public void setReturnUrl(String returnUrl) { + this.returnUrl = returnUrl; + } + + @Override + public String getSignType() { + return signType; + } + + public void setSignType(String signType) { + this.signType = signType; + } + + @Override + public String getInputCharset() { + return inputCharset; + } + + public void setInputCharset(String inputCharset) { + this.inputCharset = inputCharset; + } + + @Override public String getPayType() { return payType; } @@ -187,7 +117,6 @@ public abstract class BasePayConfigStorage implements PayConfigStorage{ this.msgType = msgType; } - @Override public String getAccessToken() { return this.accessToken; @@ -226,4 +155,9 @@ public abstract class BasePayConfigStorage implements PayConfigStorage{ this.expiresTime = 0; } + @Override + public String getToken() { + return null; + } + } diff --git a/pay-java-common/src/main/java/in/egan/pay/common/api/BasePayService.java b/pay-java-common/src/main/java/in/egan/pay/common/api/BasePayService.java index 56f53bf..1bdfa94 100644 --- a/pay-java-common/src/main/java/in/egan/pay/common/api/BasePayService.java +++ b/pay-java-common/src/main/java/in/egan/pay/common/api/BasePayService.java @@ -1,107 +1,75 @@ package in.egan.pay.common.api; -import in.egan.pay.common.exception.PayErrorException; -import in.egan.pay.common.util.str.StringUtils; -import org.apache.http.HttpHost; -import org.apache.http.auth.AuthScope; -import org.apache.http.auth.UsernamePasswordCredentials; -import org.apache.http.client.CredentialsProvider; -import org.apache.http.impl.client.BasicCredentialsProvider; -import org.apache.http.impl.client.CloseableHttpClient; -import org.apache.http.impl.client.HttpClients; +import in.egan.pay.common.http.HttpConfigStorage; +import in.egan.pay.common.http.HttpRequestTemplate; +import in.egan.pay.common.util.sign.SignUtils; -import java.io.IOException; +import java.util.Map; /** + * 支付基础服务 * @author: egan * @email egzosn@gmail.com - * @date 2017/1/12 20:09 - * @source chanjarster/weixin-java-tools + * @date 2017/3/5 20:36 */ public abstract class BasePayService implements PayService { protected PayConfigStorage payConfigStorage; - protected CloseableHttpClient httpClient; - - protected HttpHost httpProxy; - + protected HttpRequestTemplate requestTemplate; protected int retrySleepMillis = 1000; protected int maxRetryTimes = 5; - /** - * - * @param executor - * @param uri - * @param data - * @param - * @param - * @return - * @throws PayErrorException - */ - protected T executeInternal(RequestExecutor executor, String uri, E data) throws PayErrorException { - - try { - return executor.execute(getHttpClient(), httpProxy, uri, data); - } catch (IOException e) { - throw new RuntimeException(e); - } - } - - public HttpHost getHttpProxy() { - return httpProxy; - } - - public CloseableHttpClient getHttpClient() { - return httpClient; - } - /** * 设置支付配置 * @param payConfigStorage 支付配置 */ - public void setPayConfigStorage(PayConfigStorage payConfigStorage) { + public BasePayService setPayConfigStorage(PayConfigStorage payConfigStorage) { this.payConfigStorage = payConfigStorage; - - String http_proxy_host = payConfigStorage.getHttpProxyHost(); - int http_proxy_port = payConfigStorage.getHttpProxyPort(); - String http_proxy_username = payConfigStorage.getHttpProxyUsername(); - String http_proxy_password = payConfigStorage.getHttpProxyPassword(); - - if (StringUtils.isNotBlank(http_proxy_host)) { - // 使用代理服务器 - if (StringUtils.isNotBlank(http_proxy_username)) { - // 需要用户认证的代理服务器 - CredentialsProvider credsProvider = new BasicCredentialsProvider(); - credsProvider.setCredentials( - new AuthScope(http_proxy_host, http_proxy_port), - new UsernamePasswordCredentials(http_proxy_username, http_proxy_password)); - httpClient = HttpClients - .custom() - .setDefaultCredentialsProvider(credsProvider) - .build(); - } else { - // 无需用户认证的代理服务器 - httpClient = HttpClients.createDefault(); - } - httpProxy = new HttpHost(http_proxy_host, http_proxy_port); - } else { - httpClient = HttpClients.createDefault(); - } + return this; } @Override public PayConfigStorage getPayConfigStorage() { return payConfigStorage; } + @Override + public HttpRequestTemplate getHttpRequestTemplate() { + return requestTemplate; + } - public BasePayService() { + /** + * 设置并创建请求模版, 代理请求配置这里是否合理??, + * @param configStorage http请求配置 + * @return + */ + @Override + public BasePayService setRequestTemplateConfigStorage(HttpConfigStorage configStorage) { + this.requestTemplate = new HttpRequestTemplate(configStorage); + return this; } public BasePayService(PayConfigStorage payConfigStorage) { - setPayConfigStorage(payConfigStorage); + this(payConfigStorage, null); } + public BasePayService(PayConfigStorage payConfigStorage, HttpConfigStorage configStorage) { + setPayConfigStorage(payConfigStorage); + setRequestTemplateConfigStorage(configStorage); + } + @Override + public String createSign(String content, String characterEncoding) { + + return SignUtils.valueOf(payConfigStorage.getSignType()).createSign(content, payConfigStorage.getKeyPrivate(),characterEncoding); + } + + @Override + public String createSign(Map content, String characterEncoding) { + return SignUtils.valueOf(payConfigStorage.getSignType()).sign(content, payConfigStorage.getKeyPrivate(),characterEncoding); + } + + + } diff --git a/pay-java-common/src/main/java/in/egan/pay/common/api/Callback.java b/pay-java-common/src/main/java/in/egan/pay/common/api/Callback.java new file mode 100644 index 0000000..1b390f9 --- /dev/null +++ b/pay-java-common/src/main/java/in/egan/pay/common/api/Callback.java @@ -0,0 +1,32 @@ +/* + * Copyright 2002-2017 the original huodull or egan. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +package in.egan.pay.common.api; + +import java.util.Map; + +/** + * 回调,可用于类型转换 + * @author: egan + * @email egzosn@gmail.com + * @date 2017/3/7 18:55 + */ +public interface Callback { + + T perform(Map map); + +} diff --git a/pay-java-common/src/main/java/in/egan/pay/common/api/PayConfigStorage.java b/pay-java-common/src/main/java/in/egan/pay/common/api/PayConfigStorage.java index ff9d74f..6b9e8f7 100644 --- a/pay-java-common/src/main/java/in/egan/pay/common/api/PayConfigStorage.java +++ b/pay-java-common/src/main/java/in/egan/pay/common/api/PayConfigStorage.java @@ -81,10 +81,7 @@ import java.util.concurrent.locks.Lock; * 消息类型 * @see #getMsgType * @see MsgType - * @return "text" 或者 "xml" - * @see MsgType#text - * @see MsgType#xml - * @see MsgType#json + * @return "text" 或者 "xml",json */ MsgType getMsgType(); @@ -129,29 +126,4 @@ import java.util.concurrent.locks.Lock; */ void updateAccessToken(String accessToken, long expiresTime); - - /** - * http代理地址 - * @return - */ - String getHttpProxyHost(); - - /** - * 代理端口 - * @return - */ - int getHttpProxyPort(); - - /** - * 代理用户名 - * @return - */ - String getHttpProxyUsername(); - - /** - * 代理密码 - * @return - */ - String getHttpProxyPassword(); - } diff --git a/pay-java-common/src/main/java/in/egan/pay/common/api/PayErrorExceptionHandler.java b/pay-java-common/src/main/java/in/egan/pay/common/api/PayErrorExceptionHandler.java index 7c517d9..83e0539 100644 --- a/pay-java-common/src/main/java/in/egan/pay/common/api/PayErrorExceptionHandler.java +++ b/pay-java-common/src/main/java/in/egan/pay/common/api/PayErrorExceptionHandler.java @@ -3,8 +3,6 @@ package in.egan.pay.common.api; import in.egan.pay.common.exception.PayErrorException; - - /** * PayErrorExceptionHandler处理器 * @@ -14,6 +12,10 @@ import in.egan.pay.common.exception.PayErrorException; */ public interface PayErrorExceptionHandler { - public void handle(PayErrorException e); + /** + * 异常统一处理器 + * @param e + */ + void handle(PayErrorException e); } diff --git a/pay-java-common/src/main/java/in/egan/pay/common/api/PayMessageHandler.java b/pay-java-common/src/main/java/in/egan/pay/common/api/PayMessageHandler.java index cba2092..991acd7 100644 --- a/pay-java-common/src/main/java/in/egan/pay/common/api/PayMessageHandler.java +++ b/pay-java-common/src/main/java/in/egan/pay/common/api/PayMessageHandler.java @@ -3,6 +3,7 @@ package in.egan.pay.common.api; import in.egan.pay.common.bean.PayMessage; import in.egan.pay.common.bean.PayOutMessage; import in.egan.pay.common.exception.PayErrorException; + import java.util.Map; @@ -22,8 +23,8 @@ public interface PayMessageHandler { * @return xml,text格式的消息,如果在异步规则里处理的话,可以返回null */ public PayOutMessage handle(PayMessage payMessage, - Map context, - PayService payService - ) throws PayErrorException; + Map context, + PayService payService + ) throws PayErrorException; } diff --git a/pay-java-common/src/main/java/in/egan/pay/common/api/PayMessageRouter.java b/pay-java-common/src/main/java/in/egan/pay/common/api/PayMessageRouter.java index f15d3cb..b518550 100644 --- a/pay-java-common/src/main/java/in/egan/pay/common/api/PayMessageRouter.java +++ b/pay-java-common/src/main/java/in/egan/pay/common/api/PayMessageRouter.java @@ -21,7 +21,7 @@ import java.util.concurrent.Future; * 1. 配置路由规则时要按照从细到粗的原则,否则可能消息可能会被提前处理 * 2. 默认情况下消息只会被处理一次,除非使用 {@link PayMessageRouterRule#next()} * 3. 规则的结束必须用{@link PayMessageRouterRule#end()}或者{@link PayMessageRouterRule#next()},否则不会生效 - * + * * 使用方法: * PayMessageRouter router = new PayMessageRouter(); * router @@ -33,10 +33,10 @@ import java.util.concurrent.Future; * // 另外一个匹配规则 * .end() * ; - * + * * // 将PayMessage交给消息路由器 * router.route(message); - * + * * * @source chanjarster/weixin-java-tools * @source Daniel Qian diff --git a/pay-java-common/src/main/java/in/egan/pay/common/api/PayMessageRouterRule.java b/pay-java-common/src/main/java/in/egan/pay/common/api/PayMessageRouterRule.java index cb31b04..bb18a55 100644 --- a/pay-java-common/src/main/java/in/egan/pay/common/api/PayMessageRouterRule.java +++ b/pay-java-common/src/main/java/in/egan/pay/common/api/PayMessageRouterRule.java @@ -21,6 +21,7 @@ import java.util.regex.Pattern; */ public class PayMessageRouterRule { + private final PayMessageRouter routerBuilder; private boolean async = true; @@ -29,9 +30,9 @@ public class PayMessageRouterRule { private String msgType; - private String event; + private String payType; - private String eventKey; + private String[] transactionType; private String discount; @@ -75,24 +76,24 @@ public class PayMessageRouterRule { } /** - * 如果event等于某值 + * 如果payType等于某值 * - * @param event + * @param payType * @return */ - public PayMessageRouterRule event(String event) { - this.event = event; + public PayMessageRouterRule payType(String payType) { + this.payType = payType; return this; } /** - * 如果eventKey等于某值 + * 如果transactionType等于某值 * - * @param eventKey + * @param transactionType * @return */ - public PayMessageRouterRule eventKey(String eventKey) { - this.eventKey = eventKey; + public PayMessageRouterRule transactionType(String ... transactionType) { + this.transactionType = transactionType; return this; } @@ -238,9 +239,9 @@ public class PayMessageRouterRule { && (this.msgType == null || this.msgType.toLowerCase().equals((payMessage.getMsgType() ==null?null:payMessage.getMsgType().toLowerCase()))) && - (this.event == null || this.event.equals((payMessage.getEvent() == null ? null : payMessage.getEvent()))) + (this.payType == null || this.payType.equals((payMessage.getPayType() == null ? null : payMessage.getPayType()))) && - (this.eventKey == null || this.eventKey.toLowerCase().equals((payMessage.getEventKey()==null?null:payMessage.getEventKey().toLowerCase()))) + (this.transactionType == null || equalsTransactionType(payMessage.getTransactionType())) && (this.discount == null || this.discount .equals(payMessage.getDiscount() == null ? null : payMessage.getDiscount().trim())) @@ -257,6 +258,27 @@ public class PayMessageRouterRule { ; } + /** + * 匹配交易类型 + * @param transactionType 交易类型 + * @return + */ + public boolean equalsTransactionType(String transactionType) { + if (null == transactionType){ + return false; + } + + for (String type :this.getTransactionType()){ + if (type.toLowerCase().equals((transactionType.toLowerCase()))){ + return true; + } + } + return false; + + } + + + /** * 处理支付回调过来的消息 * @@ -319,20 +341,20 @@ public class PayMessageRouterRule { this.msgType = msgType; } - public String getEvent() { - return event; + public String getPayType() { + return payType; } - public void setEvent(String event) { - this.event = event; + public void setPayType(String payType) { + this.payType = payType; } - public String getEventKey() { - return eventKey; + public String[] getTransactionType() { + return transactionType; } - public void setEventKey(String eventKey) { - this.eventKey = eventKey; + public void setTransactionType(String[] transactionType) { + this.transactionType = transactionType; } public String getDiscount() { diff --git a/pay-java-common/src/main/java/in/egan/pay/common/api/PayService.java b/pay-java-common/src/main/java/in/egan/pay/common/api/PayService.java index 1df174f..61fce52 100644 --- a/pay-java-common/src/main/java/in/egan/pay/common/api/PayService.java +++ b/pay-java-common/src/main/java/in/egan/pay/common/api/PayService.java @@ -3,86 +3,87 @@ package in.egan.pay.common.api; import in.egan.pay.common.bean.MethodType; import in.egan.pay.common.bean.PayOrder; import in.egan.pay.common.bean.PayOutMessage; -import in.egan.pay.common.exception.PayErrorException; +import in.egan.pay.common.bean.TransactionType; +import in.egan.pay.common.http.HttpConfigStorage; +import in.egan.pay.common.http.HttpRequestTemplate; import java.awt.image.BufferedImage; import java.io.InputStream; -import java.util.ArrayList; import java.util.Date; -import java.util.List; import java.util.Map; /** - * 支付通知 + * 支付服务 * * @author egan * @email egzosn@gmail.com * @date 2016-5-18 14:09:01 */ - public interface PayService { +public interface PayService { - /** - * 回调校验URL - * @return - */ - String getHttpsVerifyUrl(); /** * 设置支付配置 + * * @param payConfigStorage */ - void setPayConfigStorage(PayConfigStorage payConfigStorage); + PayService setPayConfigStorage(PayConfigStorage payConfigStorage); /** * 获取支付配置 + * * @return */ - PayConfigStorage getPayConfigStorage(); + PayConfigStorage getPayConfigStorage(); + /** + * 获取http请求工具 + * + * @return + */ + HttpRequestTemplate getHttpRequestTemplate(); + + /** + * 设置 请求工具配置 + * @param configStorage http请求配置 + * @return + */ + PayService setRequestTemplateConfigStorage(HttpConfigStorage configStorage); /** * 回调校验 + * * @param params 回调回来的参数集 * @return */ - boolean verify(Map params); + boolean verify(Map params); /** * 签名校验 + * * @param params 参数集 - * @param sign 签名 + * @param sign 签名 * @return */ - boolean getSignVerify(Map params, String sign); + boolean signVerify(Map params, String sign); + /** - * URL校验 - * @param notify_id + * 校验数据来源 + * + * @param id 业务id, 数据的真实性. * @return - * @throws PayErrorException */ - String verifyUrl(String notify_id) throws PayErrorException; + boolean verifySource(String id); - /** - * 请求接口 - * @param executor 请求的具体执行者 - * @param uri 请求地址 - * @param data 请求数据 - * @param 返回类型 - * @param 请求数据类型 - * @return - * @throws PayErrorException - * @source - */ - T execute(RequestExecutor executor, String uri, E data) throws PayErrorException; /** * 返回创建的订单信息 * * @param order 支付订单 * @return - * @see in.egan.pay.common.bean.PayOrder + * @see PayOrder */ - Map orderInfo(PayOrder order); + Map orderInfo(PayOrder order); /** * 创建签名 @@ -91,18 +92,29 @@ import java.util.Map; * @param characterEncoding 字符编码 * @return */ - String createSign(String content, String characterEncoding); + String createSign(String content, String characterEncoding); + + /** + * 创建签名 + * + * @param content 需要签名的内容 + * @param characterEncoding 字符编码 + * @return + */ + String createSign(Map content, String characterEncoding); /** * 将请求参数或者请求流转化为 Map + * * @param parameterMap 请求参数 * @param is 请求流 * @return */ - Map getParameter2Map(Map parameterMap, InputStream is); + Map getParameter2Map(Map parameterMap, InputStream is); /** * 获取输出消息,用户返回给支付端 + * * @param code * @param message * @return @@ -111,59 +123,133 @@ import java.util.Map; /** * 获取输出消息,用户返回给支付端, 针对于web端 + * * @param orderInfo 发起支付的订单信息 - * @param method 请求方式 "post" "get", - * @see in.egan.pay.common.bean.MethodType + * @param method 请求方式 "post" "get", * @return + * @see MethodType */ - String buildRequest(Map orderInfo, MethodType method); + String buildRequest(Map orderInfo, MethodType method); /** * 获取输出二维码,用户返回给支付端, + * * @param orderInfo 发起支付的订单信息 * @return */ BufferedImage genQrPay(Map orderInfo); /** - * 交易查询接口 - * @param tradeNo 支付平台订单号 + * 交易查询接口 + * + * @param tradeNo 支付平台订单号 * @param outTradeNo 商户单号 * @return */ Map query(String tradeNo, String outTradeNo); /** - * 交易关闭接口 - * @param tradeNo 支付平台订单号 + * 交易查询接口 + * @param tradeNo 支付平台订单号 + * @param outTradeNo 商户单号 + * @param callback 处理器 + * @param 返回类型 + * @return + */ + T query(String tradeNo, String outTradeNo, Callback callback); + + /** + * 交易关闭接口 + * + * @param tradeNo 支付平台订单号 * @param outTradeNo 商户单号 * @return */ Map close(String tradeNo, String outTradeNo); /** - * 交易关闭接口 - * @param tradeNo 支付平台订单号 + * 交易关闭接口 + * + * @param tradeNo 支付平台订单号 + * @param outTradeNo 商户单号 + * @param callback 处理器 + * @param 返回类型 + * @return + */ + T close(String tradeNo, String outTradeNo, Callback callback); + + /** + * 交易关闭接口 + * + * @param tradeNo 支付平台订单号 * @param outTradeNo 商户单号 * @return */ Map refund(String tradeNo, String outTradeNo); + /** + * 交易关闭接口 + * + * @param tradeNo 支付平台订单号 + * @param outTradeNo 商户单号 + * @param callback 处理器 + * @param 返回类型 + * @return + */ + T refund(String tradeNo, String outTradeNo, Callback callback); /** - * 查询退款 - * @param tradeNo 支付平台订单号 + * 查询退款 + * + * @param tradeNo 支付平台订单号 * @param outTradeNo 商户单号 * @return */ Map refundquery(String tradeNo, String outTradeNo); - /** - * 下载对账单 - * @param billDate 支付平台订单号 - * @param billType 商户单号 + * 查询退款 + * + * @param tradeNo 支付平台订单号 + * @param outTradeNo 商户单号 + * @param callback 处理器 + * @param 返回类型 * @return */ - Object downloadbill(Date billDate, String billType); + T refundquery(String tradeNo, String outTradeNo, Callback callback); + + /** + * 下载对账单 + * + * @param billDate 账单类型,商户通过接口或商户经开放平台授权后其所属服务商通过接口可以获取以下账单类型:trade、signcustomer;trade指商户基于支付宝交易收单的业务账单;signcustomer是指基于商户支付宝余额收入及支出等资金变动的帐务账单; + * @param billType 账单时间:日账单格式为yyyy-MM-dd,月账单格式为yyyy-MM。 + * @return + */ + Object downloadbill(Date billDate, String billType); + + /** + * 下载对账单 + * + * @param billDate 账单时间:具体请查看对应支付平台 + * @param billType 账单类型,具体请查看对应支付平台 + * @param callback 处理器 + * @param 返回类型 + * @return + */ + T downloadbill(Date billDate, String billType, Callback callback); + + /** + * 交易辅助接口 + * + * @param tradeNoOrBillDate 支付平台订单号或者账单类型, 具体请 + * 类型为{@link String }或者 {@link Date },类型须强制限制,类型不对应则抛出异常{@link in.egan.pay.common.exception.PayErrorException} + * @param outTradeNoBillType 商户单号或者 账单类型 + * @param transactionType 交易类型 + * @param callback 处理器 + * @param 返回类型 + * @return + */ + T secondaryInterface(Object tradeNoOrBillDate, String outTradeNoBillType, TransactionType transactionType, Callback callback); + + } diff --git a/pay-java-common/src/main/java/in/egan/pay/common/bean/PayMessage.java b/pay-java-common/src/main/java/in/egan/pay/common/bean/PayMessage.java index e818299..5a21510 100644 --- a/pay-java-common/src/main/java/in/egan/pay/common/bean/PayMessage.java +++ b/pay-java-common/src/main/java/in/egan/pay/common/bean/PayMessage.java @@ -1,10 +1,10 @@ package in.egan.pay.common.bean; -import in.egan.pay.common.api.PayConsts; import java.io.Serializable; import java.math.BigDecimal; import java.text.ParseException; import java.text.SimpleDateFormat; +import java.util.Arrays; import java.util.Date; import java.util.Map; @@ -13,14 +13,13 @@ import java.util.Map; * 基础实现,具体可根据具体支付回调的消息去实现 * @author egan * @email egzosn@gmail.com - * @date 2016-6-1 14:2:3 - * @source chanjarster/weixin-java-tools + * @date 2017/3/7 16:37 */ public class PayMessage implements Serializable { private Map payMessage = null; private String msgType; - private String event; - private String eventKey; + private String payType; + private String transactionType; private String fromPay; private String describe; @@ -28,12 +27,20 @@ public class PayMessage implements Serializable { this.payMessage = payMessage; } - public PayMessage(Map payMessage, String event, String msgType) { + public PayMessage(Map payMessage, String payType, String msgType) { this(payMessage); - this.event = event; + this.payType = payType; this.msgType = msgType; } + + public PayMessage(Map payMessage, String msgType, String payType, String transactionType) { + this.payMessage = payMessage; + this.msgType = msgType; + this.payType = payType; + this.transactionType = transactionType; + } + public String getMsgType() { return msgType; } @@ -42,20 +49,21 @@ public class PayMessage implements Serializable { this.msgType = msgType; } - public String getEvent() { - return event; + + public String getPayType() { + return payType; } - public void setEvent(String event) { - this.event = event; + public void setPayType(String payType) { + this.payType = payType; } - public String getEventKey() { - return eventKey; + public String getTransactionType() { + return transactionType; } - public void setEventKey(String eventKey) { - this.eventKey = eventKey; + public void setTransactionType(String transactionType) { + this.transactionType = transactionType; } public String getFromPay() { @@ -288,4 +296,5 @@ public class PayMessage implements Serializable { } + } diff --git a/pay-java-common/src/main/java/in/egan/pay/common/bean/outbuilder/PayJsonOutMessage.java b/pay-java-common/src/main/java/in/egan/pay/common/bean/outbuilder/PayJsonOutMessage.java index f91ab35..0dce6c3 100644 --- a/pay-java-common/src/main/java/in/egan/pay/common/bean/outbuilder/PayJsonOutMessage.java +++ b/pay-java-common/src/main/java/in/egan/pay/common/bean/outbuilder/PayJsonOutMessage.java @@ -1,6 +1,7 @@ package in.egan.pay.common.bean.outbuilder; -import in.egan.pay.common.api.PayConsts; +import in.egan.pay.common.bean.MsgType; +import in.egan.pay.common.before.api.PayConsts; import in.egan.pay.common.bean.PayOutMessage; /** @@ -11,11 +12,13 @@ import in.egan.pay.common.bean.PayOutMessage; public class PayJsonOutMessage extends PayOutMessage{ public PayJsonOutMessage() { - this.msgType = PayConsts.OUT_MSG_JSON; + this.msgType = MsgType.json.name(); } @Override public String toMessage() { return getContent(); } + + } diff --git a/pay-java-common/src/main/java/in/egan/pay/common/bean/outbuilder/PayTextOutMessage.java b/pay-java-common/src/main/java/in/egan/pay/common/bean/outbuilder/PayTextOutMessage.java index 8dbb3a9..23042fc 100644 --- a/pay-java-common/src/main/java/in/egan/pay/common/bean/outbuilder/PayTextOutMessage.java +++ b/pay-java-common/src/main/java/in/egan/pay/common/bean/outbuilder/PayTextOutMessage.java @@ -1,6 +1,7 @@ package in.egan.pay.common.bean.outbuilder; -import in.egan.pay.common.api.PayConsts; +import in.egan.pay.common.bean.MsgType; +import in.egan.pay.common.before.api.PayConsts; import in.egan.pay.common.bean.PayOutMessage; /** @@ -11,7 +12,7 @@ import in.egan.pay.common.bean.PayOutMessage; public class PayTextOutMessage extends PayOutMessage{ public PayTextOutMessage() { - this.msgType = PayConsts.OUT_MSG_TEXT; + this.msgType = MsgType.text.name(); } @Override diff --git a/pay-java-common/src/main/java/in/egan/pay/common/bean/outbuilder/PayXmlOutMessage.java b/pay-java-common/src/main/java/in/egan/pay/common/bean/outbuilder/PayXmlOutMessage.java index e6631e1..96fa73c 100644 --- a/pay-java-common/src/main/java/in/egan/pay/common/bean/outbuilder/PayXmlOutMessage.java +++ b/pay-java-common/src/main/java/in/egan/pay/common/bean/outbuilder/PayXmlOutMessage.java @@ -1,6 +1,7 @@ package in.egan.pay.common.bean.outbuilder; -import in.egan.pay.common.api.PayConsts; +import in.egan.pay.common.bean.MsgType; +import in.egan.pay.common.before.api.PayConsts; import in.egan.pay.common.bean.PayOutMessage; /** @@ -13,7 +14,7 @@ public class PayXmlOutMessage extends PayOutMessage{ private String code; public PayXmlOutMessage() { - this.msgType = PayConsts.OUT_MSG_XML; + this.msgType = MsgType.xml.name(); } public String getCode() { diff --git a/pay-java-common/src/main/java/in/egan/pay/common/bean/result/PayError.java b/pay-java-common/src/main/java/in/egan/pay/common/bean/result/PayError.java index 23c2a40..2c5aeaa 100644 --- a/pay-java-common/src/main/java/in/egan/pay/common/bean/result/PayError.java +++ b/pay-java-common/src/main/java/in/egan/pay/common/bean/result/PayError.java @@ -1,102 +1,32 @@ package in.egan.pay.common.bean.result; -import com.alibaba.fastjson.JSON; -import com.alibaba.fastjson.JSONObject; -import in.egan.pay.common.util.XML; - -import java.io.Serializable; -import java.util.Map; - /** * 支付错误码说明 - * @author Daniel Qian - * @dete 2017/1/12 9:57 - * @author: egan - * @source chanjarster/weixin-java-tools + * + * @author egan + * @email egzosn@gmail.com + * @date 2017-03-02 22:28:01 */ +public interface PayError { -public class PayError implements Serializable { + /** + * 获取错误码 + * + * @return + */ + String getErrorCode(); - private int errorCode; + /** + * 获取错误消息 + * + * @return + */ + String getErrorMsg(); - private String errorMsg; - - private JSONObject json; - private String responseContent; - - public PayError() { - } - public PayError(int errorCode, String errorMsg) { - this.errorCode = errorCode; - this.errorMsg = errorMsg; - } - - - public PayError(int errorCode, String errorMsg, String responseContent) { - this(errorCode, errorMsg); - this.responseContent = responseContent; - } - - - public int getErrorCode() { - return errorCode; - } - - public void setErrorCode(int errorCode) { - this.errorCode = errorCode; - } - - public String getErrorMsg() { - return errorMsg; - } - - public void setErrorMsg(String errorMsg) { - this.errorMsg = errorMsg; - } - - public JSONObject getJson() { - return json; - } - - public void setJson(JSONObject json) { - this.json = json; - } - - public String getResponseContent() { - return responseContent; - } - - public void setResponseContent(String responseContent) { - this.responseContent = responseContent; - } - - public static PayError fromJson(String json) { - - JSONObject jsonObject = JSON.parseObject(json); - PayError error = jsonObject.toJavaObject(PayError.class); - error.setJson(jsonObject); - error.setResponseContent(json); - return error; - } - - public static PayError fromXml(String xml) { - JSONObject jsonObject = XML.toJSONObject(xml); - if (null == jsonObject.get("return_code")){ - PayError error = new PayError(403, null == jsonObject.get("return_msg") ? "未知错误!" : jsonObject.get("return_msg").toString()); - return error; - } - - if ("FAIL".equals( jsonObject.get("return_code"))){ - PayError error = new PayError(-1, jsonObject.get("return_msg").toString()); - return error; - } - return null; - } - - - @Override - public String toString() { - return "支付错误: errcode=" + errorCode + ", errmsg=" + errorMsg + "\njson:" + json; - } + /** + * 获取异常信息 + * @return + */ + String getString(); } diff --git a/pay-java-common/src/main/java/in/egan/pay/common/bean/result/PayException.java b/pay-java-common/src/main/java/in/egan/pay/common/bean/result/PayException.java new file mode 100644 index 0000000..13a7c0e --- /dev/null +++ b/pay-java-common/src/main/java/in/egan/pay/common/bean/result/PayException.java @@ -0,0 +1,55 @@ +/* + * Copyright 2002-2017 the original huodull or egan. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +package in.egan.pay.common.bean.result; + +/** + * @author: egan + * @email egzosn@gmail.com + * @date 2017/3/7 12:32 + */ +public class PayException implements PayError { + private String errorCode; + private String errorMsg; + private String content; + + @Override + public String getErrorCode() { + return errorCode; + } + + @Override + public String getErrorMsg() { + return errorMsg; + } + + public PayException(String errorCode, String errorMsg) { + this.errorCode = errorCode; + this.errorMsg = errorMsg; + } + + public PayException(String errorCode, String errorMsg, String content) { + this.errorCode = errorCode; + this.errorMsg = errorMsg; + this.content = content; + } + + @Override + public String getString() { + return "支付错误: errcode=" + errorCode + ", errmsg=" + errorMsg + (null == content ? "" : "\n content:" + content); + } +} diff --git a/pay-java-common/src/main/java/in/egan/pay/common/before/api/BasePayConfigStorage.java b/pay-java-common/src/main/java/in/egan/pay/common/before/api/BasePayConfigStorage.java new file mode 100644 index 0000000..4fad378 --- /dev/null +++ b/pay-java-common/src/main/java/in/egan/pay/common/before/api/BasePayConfigStorage.java @@ -0,0 +1,230 @@ +package in.egan.pay.common.before.api; + +import in.egan.pay.common.bean.MsgType; + +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReentrantLock; + +/** + * 支付基础配置存储 + * @author egan + * @email egzosn@gmail.com + * @date 2016-5-18 14:09:01 + */ +@Deprecated +public abstract class BasePayConfigStorage implements PayConfigStorage{ + + + // ali rsa_private 商户私钥,pkcs8格式 + //wx api_key 商户密钥 + protected volatile String keyPrivate ; + // 支付公钥 + protected volatile String keyPublic; + //异步回调地址 + protected volatile String notifyUrl; + //同步回调地址 + protected volatile String returnUrl;; + //签名加密类型 + protected volatile String signType; + //字符类型 + protected volatile String inputCharset; + + + //支付类型 aliPay 支付宝, wxPay微信..等等,开发者自定义,唯一 + protected volatile String payType; + /** + * 消息来源类型 + * @see PayConsts#MSG_XML + * @see PayConsts#MSG_TEXT + * @see PayConsts#MSG_JSON + */ + protected volatile MsgType msgType; + + + // 访问令牌 每次请求其他方法都要传入的值 + protected volatile String accessToken; + // access token 到期时间时间戳 + protected volatile long expiresTime; + //授权码锁 + protected Lock accessTokenLock = new ReentrantLock(); + + + protected volatile String httpProxyHost; + protected volatile int httpProxyPort; + protected volatile String httpProxyUsername; + protected volatile String httpProxyPassword; + + + /** + * 合作商唯一标识 + * @see #getPartner 代替者 + */ + public String getPid(){ + throw null; + } + + @Override + public String getInputCharset() { + return inputCharset; + } + + public void setInputCharset(String inputCharset) { + this.inputCharset = inputCharset; + } + + + + @Override + public String getNotifyUrl() { + return notifyUrl; + } + + public void setNotifyUrl(String notifyUrl) { + this.notifyUrl = notifyUrl; + } + + @Override + public String getReturnUrl() { + return returnUrl; + } + + public void setReturnUrl(String returnUrl) { + this.returnUrl = returnUrl; + } + + public void setSignType(String signType) { + this.signType = signType; + } + + @Override + public String getSignType() { + return signType; + } + + + + @Override + public String getHttpProxyHost() { + return httpProxyHost; + } + + public void setHttpProxyHost(String httpProxyHost) { + this.httpProxyHost = httpProxyHost; + } + + @Override + public int getHttpProxyPort() { + return httpProxyPort; + } + + public void setHttpProxyPort(int httpProxyPort) { + this.httpProxyPort = httpProxyPort; + } + + @Override + public String getHttpProxyUsername() { + return httpProxyUsername; + } + + public void setHttpProxyUsername(String httpProxyUsername) { + this.httpProxyUsername = httpProxyUsername; + } + + @Override + public String getHttpProxyPassword() { + return httpProxyPassword; + } + + + public void setHttpProxyPassword(String httpProxyPassword) { + this.httpProxyPassword = httpProxyPassword; + } + + @Override + public String getKeyPrivate() { + return keyPrivate; + } + + public void setKeyPrivate(String keyPrivate) { + this.keyPrivate = keyPrivate; + } + + @Override + public String getKeyPublic() { + return keyPublic; + } + + public void setKeyPublic(String keyPublic) { + this.keyPublic = keyPublic; + } + + + + @Override + public String getToken() { + return null; + } + + + /** + * 支付类型 自定义 + * 这里暂定 aliPay 支付宝, wxPay微信支付 + * @return + */ + public String getPayType() { + return payType; + } + + public void setPayType(String payType) { + this.payType = payType; + } + + @Override + public MsgType getMsgType() { + return msgType; + } + + public void setMsgType(MsgType msgType) { + this.msgType = msgType; + } + + + @Override + public String getAccessToken() { + return this.accessToken; + } + + @Override + public Lock getAccessTokenLock() { + return this.accessTokenLock; + } + + @Override + public long getExpiresTime() { + return expiresTime; + } + + @Override + public boolean isAccessTokenExpired() { + return System.currentTimeMillis() > this.expiresTime; + } + + + @Override + public synchronized void updateAccessToken(String accessToken, int expiresInSeconds) { + this.accessToken = accessToken; + this.expiresTime = System.currentTimeMillis() + (expiresInSeconds - 600) * 1000L; + } + + @Override + public synchronized void updateAccessToken(String accessToken, long expiresTime) { + this.accessToken = accessToken; + this.expiresTime = expiresTime; + } + + @Override + public void expireAccessToken() { + this.expiresTime = 0; + } + +} diff --git a/pay-java-common/src/main/java/in/egan/pay/common/before/api/BasePayService.java b/pay-java-common/src/main/java/in/egan/pay/common/before/api/BasePayService.java new file mode 100644 index 0000000..f350cc6 --- /dev/null +++ b/pay-java-common/src/main/java/in/egan/pay/common/before/api/BasePayService.java @@ -0,0 +1,109 @@ +package in.egan.pay.common.before.api; + +import in.egan.pay.common.exception.PayErrorException; +import in.egan.pay.common.util.str.StringUtils; +import org.apache.http.HttpHost; +import org.apache.http.auth.AuthScope; +import org.apache.http.auth.UsernamePasswordCredentials; +import org.apache.http.client.CredentialsProvider; +import org.apache.http.impl.client.BasicCredentialsProvider; +import org.apache.http.impl.client.CloseableHttpClient; +import org.apache.http.impl.client.HttpClients; + +import java.io.IOException; + +/** + * @author: egan + * @email egzosn@gmail.com + * @date 2017/1/12 20:09 + * @source chanjarster/weixin-java-tools + */ +@Deprecated +public abstract class BasePayService implements PayService { + + protected PayConfigStorage payConfigStorage; + + protected CloseableHttpClient httpClient; + + protected HttpHost httpProxy; + + protected int retrySleepMillis = 1000; + + protected int maxRetryTimes = 5; + + /** + * + * @param executor + * @param uri + * @param data + * @param + * @param + * @return + * @throws PayErrorException + */ + protected T executeInternal(RequestExecutor executor, String uri, E data) throws PayErrorException { + + try { + return executor.execute(getHttpClient(), httpProxy, uri, data); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + public HttpHost getHttpProxy() { + return httpProxy; + } + + public CloseableHttpClient getHttpClient() { + return httpClient; + } + + /** + * 设置支付配置 + * @param payConfigStorage 支付配置 + */ + @Deprecated + public void setPayConfigStorage(PayConfigStorage payConfigStorage) { + this.payConfigStorage = payConfigStorage; + + String http_proxy_host = payConfigStorage.getHttpProxyHost(); + int http_proxy_port = payConfigStorage.getHttpProxyPort(); + String http_proxy_username = payConfigStorage.getHttpProxyUsername(); + String http_proxy_password = payConfigStorage.getHttpProxyPassword(); + + if (StringUtils.isNotBlank(http_proxy_host)) { + // 使用代理服务器 + if (StringUtils.isNotBlank(http_proxy_username)) { + // 需要用户认证的代理服务器 + CredentialsProvider credsProvider = new BasicCredentialsProvider(); + credsProvider.setCredentials( + new AuthScope(http_proxy_host, http_proxy_port), + new UsernamePasswordCredentials(http_proxy_username, http_proxy_password)); + httpClient = HttpClients + .custom() + .setDefaultCredentialsProvider(credsProvider) + .build(); + } else { + // 无需用户认证的代理服务器 + httpClient = HttpClients.createDefault(); + } + httpProxy = new HttpHost(http_proxy_host, http_proxy_port); + } else { + httpClient = HttpClients.createDefault(); + } + } + + @Override + public PayConfigStorage getPayConfigStorage() { + return payConfigStorage; + } + + public BasePayService() { + } + + + public BasePayService(PayConfigStorage payConfigStorage) { + setPayConfigStorage(payConfigStorage); + } + +} diff --git a/pay-java-common/src/main/java/in/egan/pay/common/before/api/PayConfigStorage.java b/pay-java-common/src/main/java/in/egan/pay/common/before/api/PayConfigStorage.java new file mode 100644 index 0000000..13ab2e3 --- /dev/null +++ b/pay-java-common/src/main/java/in/egan/pay/common/before/api/PayConfigStorage.java @@ -0,0 +1,167 @@ +package in.egan.pay.common.before.api; + +import in.egan.pay.common.bean.MsgType; + +import java.util.concurrent.locks.Lock; + +/** + * 支付客户端配置存储 + * @author egan + * @email egzosn@gmail.com + * @date 2016-5-18 14:09:01 + * @source chanjarster/weixin-java-tools + * @see in.egan.pay.common.api.PayConfigStorage + */ +@Deprecated + public interface PayConfigStorage { + + /* + * 应用id + */ + String getAppid(); + /** + * 合作商唯一标识 + */ + @Deprecated + String getPartner(); + /** + * 合作商唯一标识 + * @see #getPartner 代替者 + */ + String getPid(); + + /** + * 获取收款账号 + */ + String getSeller(); + + /** + * 授权令牌 + */ + String getToken(); + + + /** + * 服务端异步回调Url + */ + String getNotifyUrl(); + /** + * 服务端同步回调Url + */ + String getReturnUrl(); + /** + * 签名方式 + */ + String getSignType(); + // 字符编码格式 目前支持 gbk 或 utf-8 + String getInputCharset(); + /** + * 获取密钥 与 #getKeyPrivate 类似 + */ + String getSecretKey(); + + /** + * 公钥 + * @return + */ + String getKeyPublic(); + + /** + * 私钥 + * @return + */ + String getKeyPrivate(); + + /** + * 支付类型 自定义 + * 这里暂定 aliPay 支付宝, wxPay微信支付 + * @return + */ + String getPayType(); + + /** + * 消息类型 + * @see #getMsgType + * @see MsgType + * @return "text" 或者 "xml" + * @see MsgType#text + * @see MsgType#xml + * @see MsgType#json + */ + MsgType getMsgType(); + + + /** + * 获取访问令牌 + * @return + */ + String getAccessToken(); + + /** + * 访问令牌是否过期 + * @return + */ + boolean isAccessTokenExpired(); + /** + * 获取access token锁 + * @return + */ + Lock getAccessTokenLock(); + + /** + * 强制将access token过期掉 + */ + void expireAccessToken(); + /** + * 强制将access token过期掉 + */ + long getExpiresTime(); + + /** + * 应该是线程安全的 + * @param accessToken 新的accessToken值 + * @param expiresInSeconds 过期时间,以秒为单位 多少秒 + */ + void updateAccessToken(String accessToken, int expiresInSeconds); + + /** + * 应该是线程安全的 + * @param accessToken 新的accessToken值 + * @param expiresTime 过期时间,时间戳 + */ + void updateAccessToken(String accessToken, long expiresTime); + + + /** + * http代理地址 + * @return + * @see in.egan.pay.common.http.HttpConfigStorage#getHttpProxyHost() + */ + @Deprecated + String getHttpProxyHost(); + + /** + * 代理端口 + * @return + * @see in.egan.pay.common.http.HttpConfigStorage#getHttpProxyPort() + */ + @Deprecated + int getHttpProxyPort(); + + /** + * 代理用户名 + * @return + * @see in.egan.pay.common.http.HttpConfigStorage#getHttpProxyUsername() + */ + @Deprecated + String getHttpProxyUsername(); + + /** + * 代理密码 + * @return + * @see in.egan.pay.common.http.HttpConfigStorage#getHttpProxyPassword() + */ + @Deprecated + String getHttpProxyPassword(); + +} diff --git a/pay-java-common/src/main/java/in/egan/pay/common/api/PayConsts.java b/pay-java-common/src/main/java/in/egan/pay/common/before/api/PayConsts.java similarity index 95% rename from pay-java-common/src/main/java/in/egan/pay/common/api/PayConsts.java rename to pay-java-common/src/main/java/in/egan/pay/common/before/api/PayConsts.java index ff0d55c..c907fc3 100644 --- a/pay-java-common/src/main/java/in/egan/pay/common/api/PayConsts.java +++ b/pay-java-common/src/main/java/in/egan/pay/common/before/api/PayConsts.java @@ -1,4 +1,4 @@ -package in.egan.pay.common.api; +package in.egan.pay.common.before.api; /** * 支付宝支付通知 diff --git a/pay-java-common/src/main/java/in/egan/pay/common/before/api/PayMessageHandler.java b/pay-java-common/src/main/java/in/egan/pay/common/before/api/PayMessageHandler.java new file mode 100644 index 0000000..41ceb03 --- /dev/null +++ b/pay-java-common/src/main/java/in/egan/pay/common/before/api/PayMessageHandler.java @@ -0,0 +1,31 @@ +package in.egan.pay.common.before.api; + +import in.egan.pay.common.bean.PayMessage; +import in.egan.pay.common.bean.PayOutMessage; +import in.egan.pay.common.before.api.*; +import in.egan.pay.common.exception.PayErrorException; +import java.util.Map; + + +/** + * 处理支付回调消息的处理器接口 + * @source Daniel Qian + * @author egan + * @email egzosn@gmail.com + * @date 2016-6-1 11:40:30 + */ +@Deprecated +public interface PayMessageHandler { + + /** + * @param payMessage + * @param context 上下文,如果handler或interceptor之间有信息要传递,可以用这个 + * @param payService + * @return xml,text格式的消息,如果在异步规则里处理的话,可以返回null + */ + public PayOutMessage handle(PayMessage payMessage, + Map context, + in.egan.pay.common.before.api.PayService payService + ) throws PayErrorException; + +} diff --git a/pay-java-common/src/main/java/in/egan/pay/common/before/api/PayMessageInterceptor.java b/pay-java-common/src/main/java/in/egan/pay/common/before/api/PayMessageInterceptor.java new file mode 100644 index 0000000..0a63e6f --- /dev/null +++ b/pay-java-common/src/main/java/in/egan/pay/common/before/api/PayMessageInterceptor.java @@ -0,0 +1,29 @@ +package in.egan.pay.common.before.api; + + +import in.egan.pay.common.bean.PayMessage; +import in.egan.pay.common.exception.PayErrorException; + +import java.util.Map; + +/** + * 支付消息拦截器,可以用来做验证 + * @author Daniel Qian + */ +@Deprecated +public interface PayMessageInterceptor { + + /** + * 拦截支付消息 + * + * @param wxMessage + * @param context 上下文,如果handler或interceptor之间有信息要传递,可以用这个 + * @param payService + * @return true代表OK,false代表不OK + */ + public boolean intercept(PayMessage wxMessage, + Map context, + PayService payService + ) throws PayErrorException; + +} diff --git a/pay-java-common/src/main/java/in/egan/pay/common/before/api/PayMessageRouter.java b/pay-java-common/src/main/java/in/egan/pay/common/before/api/PayMessageRouter.java new file mode 100644 index 0000000..893bc59 --- /dev/null +++ b/pay-java-common/src/main/java/in/egan/pay/common/before/api/PayMessageRouter.java @@ -0,0 +1,166 @@ +package in.egan.pay.common.before.api; + +import in.egan.pay.common.api.PayErrorExceptionHandler; +import in.egan.pay.common.bean.PayMessage; +import in.egan.pay.common.bean.PayOutMessage; +import in.egan.pay.common.util.LogExceptionHandler; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; + +/** + *
+ * 支付消息路由器,通过代码化的配置,把来自支付的消息交给handler处理
+ * 
+ * 说明:
+ * 1. 配置路由规则时要按照从细到粗的原则,否则可能消息可能会被提前处理
+ * 2. 默认情况下消息只会被处理一次,除非使用 {@link PayMessageRouterRule#next()}
+ * 3. 规则的结束必须用{@link PayMessageRouterRule#end()}或者{@link PayMessageRouterRule#next()},否则不会生效
+ * 
+ * 使用方法:
+ * PayMessageRouter router = new PayMessageRouter();
+ * router
+ *   .rule()
+ *       .msgType("MSG_TYPE").event("EVENT").eventKey("EVENT_KEY").content("CONTENT")
+ *       .interceptor(interceptor, ...).handler(handler, ...)
+ *   .end()
+ *   .rule()
+ *       // 另外一个匹配规则
+ *   .end()
+ * ;
+ * 
+ * // 将PayMessage交给消息路由器
+ * router.route(message);
+ * 
+ * 
+ * @source chanjarster/weixin-java-tools + * @source Daniel Qian + * @author egan + * + */ +@Deprecated +public class PayMessageRouter { + + protected final Log log = LogFactory.getLog(PayMessageRouter.class); + + private static final int DEFAULT_THREAD_POOL_SIZE = 100; + + private final List rules = new ArrayList(); + + private final in.egan.pay.common.before.api.PayService payService; + + private ExecutorService executorService; + + private PayErrorExceptionHandler exceptionHandler; + + public PayMessageRouter(in.egan.pay.common.before.api.PayService payService) { + this.payService = payService; + this.executorService = Executors.newFixedThreadPool(DEFAULT_THREAD_POOL_SIZE); + this.exceptionHandler = new LogExceptionHandler(); + } + + /** + *
+   * 设置自定义的 {@link ExecutorService}
+   * 如果不调用该方法,默认使用 Executors.newFixedThreadPool(100)
+   * 
+ * @param executorService + */ + public void setExecutorService(ExecutorService executorService) { + this.executorService = executorService; + } + + + + /** + *
+   * 设置自定义的{@link PayErrorExceptionHandler}
+   * 如果不调用该方法,默认使用 {@link LogExceptionHandler}
+   * 
+ * @param exceptionHandler + */ + public void setExceptionHandler(PayErrorExceptionHandler exceptionHandler) { + this.exceptionHandler = exceptionHandler; + } + + List getRules() { + return this.rules; + } + + /** + * 开始一个新的Route规则 + * @return + */ + public PayMessageRouterRule rule() { + return new PayMessageRouterRule(this); + } + + /** + * 处理支付消息 + * @param payMessage + */ + public PayOutMessage route(final PayMessage payMessage) { + + final List matchRules = new ArrayList(); + // 收集匹配的规则 + for (final PayMessageRouterRule rule : rules) { + if (rule.test(payMessage)) { + matchRules.add(rule); + if(!rule.isReEnter()) { + break; + } + } + } + + if (matchRules.size() == 0) { + return null; + } + + PayOutMessage res = null; + final List futures = new ArrayList(); + for (final PayMessageRouterRule rule : matchRules) { + // 返回最后一个非异步的rule的执行结果 + if(rule.isAsync()) { + futures.add( + executorService.submit(new Runnable() { + public void run() { + rule.service(payMessage, payService, exceptionHandler); + } + }) + ); + } else { + res = rule.service(payMessage, payService, exceptionHandler); + // 在同步操作结束,session访问结束 + log.debug("End session access: async=false, fromPay=" + payMessage.getFromPay()); + } + } + + if (futures.size() > 0) { + executorService.submit(new Runnable() { + @Override + public void run() { + for (Future future : futures) { + try { + future.get(); + log.debug("End session access: async=true, fromPay=" + payMessage.getFromPay()); + + } catch (InterruptedException e) { + log.error("Error happened when wait task finish", e); + } catch (ExecutionException e) { + log.error("Error happened when wait task finish", e); + } + } + } + }); + } + return res; + } + + +} diff --git a/pay-java-common/src/main/java/in/egan/pay/common/before/api/PayMessageRouterRule.java b/pay-java-common/src/main/java/in/egan/pay/common/before/api/PayMessageRouterRule.java new file mode 100644 index 0000000..12ba493 --- /dev/null +++ b/pay-java-common/src/main/java/in/egan/pay/common/before/api/PayMessageRouterRule.java @@ -0,0 +1,414 @@ +package in.egan.pay.common.before.api; + + +import in.egan.pay.common.api.PayErrorExceptionHandler; +import in.egan.pay.common.bean.PayMessage; +import in.egan.pay.common.bean.PayOutMessage; +import in.egan.pay.common.exception.PayErrorException; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.regex.Pattern; + + +/** + * + * @author egan + * @email egzosn@gmail.com + * @date 2016-6-1 11:28:01 + * @source chanjarster/weixin-java-tools + */ +@Deprecated +public class PayMessageRouterRule { + + private final PayMessageRouter routerBuilder; + + private boolean async = true; + + private String fromPay; + + private String msgType; + + private String payType; + + private String[] transactionType; + + private String discount; + + private String rDiscount; + + private String subject; + + private String rSubject; + + + private boolean reEnter = false; + + private List handlers = new ArrayList(); + + private List interceptors = new ArrayList(); + + public PayMessageRouterRule(PayMessageRouter routerBuilder) { + this.routerBuilder = routerBuilder; + } + + /** + * 设置是否异步执行,默认是true + * + * @param async + * @return + */ + public PayMessageRouterRule async(boolean async) { + this.async = async; + return this; + } + + /** + * 如果msgType等于某值 + * + * @param msgType + * @return + */ + public PayMessageRouterRule msgType(String msgType) { + this.msgType = msgType; + return this; + } + + /** + * 如果payType等于某值 + * + * @param payType + * @return + */ + public PayMessageRouterRule payType(String payType) { + this.payType = payType; + return this; + } + + /** + * 如果transactionType等于某值 + * + * @param transactionType + * @return + */ + public PayMessageRouterRule transactionType(String... transactionType) { + this.transactionType = transactionType; + return this; + } + + /** + * 如果discount等于某值 + * + * @param discount + * @return + */ + public PayMessageRouterRule discount(String discount) { + this.discount = discount; + return this; + } + + /** + * 如果discount匹配该正则表达式 + * + * @param regex + * @return + */ + public PayMessageRouterRule rDiscount(String regex) { + this.rDiscount = regex; + return this; + } + + /** + * 如果discount等于某值 + * + * @param subject + * @return + */ + public PayMessageRouterRule subject(String subject) { + this.subject = subject; + return this; + } + + /** + * 如果discount匹配该正则表达式 + * + * @param regex + * @return + */ + public PayMessageRouterRule rSubject(String regex) { + this.rSubject = regex; + return this; + } + + + /** + * 如果消息匹配某个matcher,用在用户需要自定义更复杂的匹配规则的时候 + * + * @param matcher + * @return + */ + /* public PayMessageRouterRule matcher(WxMpMessageMatcher matcher) { + this.matcher = matcher; + return this; + }*/ + + /** + * 设置微信消息拦截器 + * + * @param interceptor + * @return + */ + public PayMessageRouterRule interceptor(PayMessageInterceptor interceptor) { + return interceptor(interceptor, (PayMessageInterceptor[]) null); + } + + /** + * 设置微信消息拦截器 + * + * @param interceptor + * @param otherInterceptors + * @return + */ + public PayMessageRouterRule interceptor(PayMessageInterceptor interceptor, PayMessageInterceptor... otherInterceptors) { + this.interceptors.add(interceptor); + if (otherInterceptors != null && otherInterceptors.length > 0) { + for (PayMessageInterceptor i : otherInterceptors) { + this.interceptors.add(i); + } + } + return this; + } + + /** + * 设置微信消息处理器 + * + * @param handler + * @return + */ + public PayMessageRouterRule handler(PayMessageHandler handler) { + return handler(handler, (PayMessageHandler[]) null); + } + + /** + * 设置微信消息处理器 + * + * @param handler + * @param otherHandlers + * @return + */ + public PayMessageRouterRule handler(PayMessageHandler handler, PayMessageHandler... otherHandlers) { + this.handlers.add(handler); + if (otherHandlers != null && otherHandlers.length > 0) { + for (PayMessageHandler i : otherHandlers) { + this.handlers.add(i); + } + } + return this; + } + + /** + * 规则结束,代表如果一个消息匹配该规则,那么它将不再会进入其他规则 + * + * @return + */ + public PayMessageRouter end() { + this.routerBuilder.getRules().add(this); + return this.routerBuilder; + } + + /** + * 规则结束,但是消息还会进入其他规则 + * + * @return + */ + public PayMessageRouter next() { + this.reEnter = true; + return end(); + } + + /** + * 将支付事件修正为不区分大小写, + * 比如框架定义的事件常量为 + * @param payMessage + * @return + */ + protected boolean test(PayMessage payMessage) { + return ( + (this.fromPay == null || this.fromPay.toLowerCase().equals((payMessage.getFromPay() ==null?null:payMessage.getFromPay().toLowerCase()))) + && + (this.msgType == null || this.msgType.toLowerCase().equals((payMessage.getMsgType() ==null?null:payMessage.getMsgType().toLowerCase()))) + && + (this.payType == null || this.payType.equals((payMessage.getPayType() == null ? null : payMessage.getPayType()))) + && + (this.transactionType == null || equalsTransactionType(payMessage.getTransactionType()) ) + && + (this.discount == null || this.discount.equals(payMessage.getDiscount() == null ? null : payMessage.getDiscount().trim())) + && + (this.rDiscount == null || Pattern + .matches(this.rDiscount, payMessage.getDiscount() == null ? "" : payMessage.getDiscount().trim())) + && + (this.subject == null || this.subject + .equals(payMessage.getSubject() == null ? null : payMessage.getSubject().trim())) + && + (this.rSubject == null || Pattern + .matches(this.rSubject, payMessage.getSubject() == null ? "" : payMessage.getSubject().trim())) + ) + ; + } + + + /** + * 匹配交易类型 + * @param transactionType 交易类型 + * @return + */ + public boolean equalsTransactionType(String transactionType) { + if (null == transactionType){ + return false; + } + + for (String type :this.getTransactionType()){ + if (type.toLowerCase().equals((transactionType.toLowerCase()))){ + return true; + } + } + return false; + + } + + /** + * 处理支付回调过来的消息 + * + * @param payService + * @return true 代表继续执行别的router,false 代表停止执行别的router + */ + protected PayOutMessage service(PayMessage payMessage, + in.egan.pay.common.before.api.PayService payService, + PayErrorExceptionHandler exceptionHandler) { + + try { + + Map context = new HashMap(); + // 如果拦截器不通过 + for (PayMessageInterceptor interceptor : this.interceptors) { + if (!interceptor.intercept(payMessage, context, payService)) { + return null; + } + } + + // 交给handler处理 + PayOutMessage res = null; + for (PayMessageHandler handler : this.handlers) { + // 返回最后handler的结果 + res = handler.handle(payMessage, context, payService); + } + return res; + } catch (PayErrorException e) { + exceptionHandler.handle(e); + } + return null; + + } + + public PayMessageRouter getRouterBuilder() { + return routerBuilder; + } + + public boolean isAsync() { + return async; + } + + public void setAsync(boolean async) { + this.async = async; + } + + public String getFromPay() { + return fromPay; + } + + public void setFromPay(String fromPay) { + this.fromPay = fromPay; + } + + public String getMsgType() { + return msgType; + } + + public void setMsgType(String msgType) { + this.msgType = msgType; + } + + public String getPayType() { + return payType; + } + + public void setPayType(String payType) { + this.payType = payType; + } + + public String[] getTransactionType() { + return transactionType; + } + + public void setTransactionType(String[] transactionType) { + this.transactionType = transactionType; + } + + public String getDiscount() { + return discount; + } + + public void setDiscount(String discount) { + this.discount = discount; + } + + public String getrDiscount() { + return rDiscount; + } + + public void setrDiscount(String rDiscount) { + this.rDiscount = rDiscount; + } + + public String getSubject() { + return subject; + } + + public void setSubject(String subject) { + this.subject = subject; + } + + public String getrSubject() { + return rSubject; + } + + public void setrSubject(String rSubject) { + this.rSubject = rSubject; + } + + public boolean isReEnter() { + return reEnter; + } + + public void setReEnter(boolean reEnter) { + this.reEnter = reEnter; + } + + public List getHandlers() { + return handlers; + } + + public void setHandlers(List handlers) { + this.handlers = handlers; + } + + public List getInterceptors() { + return interceptors; + } + + public void setInterceptors(List interceptors) { + this.interceptors = interceptors; + } +} diff --git a/pay-java-common/src/main/java/in/egan/pay/common/before/api/PayService.java b/pay-java-common/src/main/java/in/egan/pay/common/before/api/PayService.java new file mode 100644 index 0000000..2fecb6d --- /dev/null +++ b/pay-java-common/src/main/java/in/egan/pay/common/before/api/PayService.java @@ -0,0 +1,169 @@ +package in.egan.pay.common.before.api; + +import in.egan.pay.common.bean.MethodType; +import in.egan.pay.common.bean.PayOrder; +import in.egan.pay.common.bean.PayOutMessage; +import in.egan.pay.common.exception.PayErrorException; + +import java.awt.image.BufferedImage; +import java.io.InputStream; +import java.util.Date; +import java.util.Map; + +/** + * 支付服务 + * + * @author egan + * @email egzosn@gmail.com + * @date 2016-5-18 14:09:01 + * @see in.egan.pay.common.api.PayService + */ +@Deprecated + public interface PayService { + + /** + * 回调校验URL + * @return + */ + String getHttpsVerifyUrl(); + + /** + * 设置支付配置 + * @param payConfigStorage + */ + void setPayConfigStorage(PayConfigStorage payConfigStorage); + + /** + * 获取支付配置 + * @return + */ + PayConfigStorage getPayConfigStorage(); + + /** + * 回调校验 + * @param params 回调回来的参数集 + * @return + */ + boolean verify(Map params); + + /** + * 签名校验 + * @param params 参数集 + * @param sign 签名 + * @return + */ + boolean getSignVerify(Map params, String sign); + + /** + * URL校验 + * @param notify_id + * @return + * @throws PayErrorException + */ + String verifyUrl(String notify_id) throws PayErrorException; + + /** + * 请求接口 + * @param executor 请求的具体执行者 + * @param uri 请求地址 + * @param data 请求数据 + * @param 返回类型 + * @param 请求数据类型 + * @return + * @throws PayErrorException + * @source + */ + T execute(RequestExecutor executor, String uri, E data) throws PayErrorException; + + /** + * 返回创建的订单信息 + * + * @param order 支付订单 + * @return + * @see PayOrder + */ + Map orderInfo(PayOrder order); + + /** + * 创建签名 + * + * @param content 需要签名的内容 + * @param characterEncoding 字符编码 + * @return + */ + String createSign(String content, String characterEncoding); + + /** + * 将请求参数或者请求流转化为 Map + * @param parameterMap 请求参数 + * @param is 请求流 + * @return + */ + Map getParameter2Map(Map parameterMap, InputStream is); + + /** + * 获取输出消息,用户返回给支付端 + * @param code + * @param message + * @return + */ + PayOutMessage getPayOutMessage(String code, String message); + + /** + * 获取输出消息,用户返回给支付端, 针对于web端 + * @param orderInfo 发起支付的订单信息 + * @param method 请求方式 "post" "get", + * @see MethodType + * @return + */ + String buildRequest(Map orderInfo, MethodType method); + + /** + * 获取输出二维码,用户返回给支付端, + * @param orderInfo 发起支付的订单信息 + * @return + */ + BufferedImage genQrPay(Map orderInfo); + + /** + * 交易查询接口 + * @param tradeNo 支付平台订单号 + * @param outTradeNo 商户单号 + * @return + */ + Map query(String tradeNo, String outTradeNo); + + /** + * 交易关闭接口 + * @param tradeNo 支付平台订单号 + * @param outTradeNo 商户单号 + * @return + */ + Map close(String tradeNo, String outTradeNo); + + /** + * 交易关闭接口 + * @param tradeNo 支付平台订单号 + * @param outTradeNo 商户单号 + * @return + */ + Map refund(String tradeNo, String outTradeNo); + + /** + * 查询退款 + * @param tradeNo 支付平台订单号 + * @param outTradeNo 商户单号 + * @return + */ + Map refundquery(String tradeNo, String outTradeNo); + + /** + * 下载对账单 + * @param billDate 账单类型,商户通过接口或商户经开放平台授权后其所属服务商通过接口可以获取以下账单类型:trade、signcustomer;trade指商户基于支付宝交易收单的业务账单;signcustomer是指基于商户支付宝余额收入及支出等资金变动的帐务账单; + * @param billType 账单时间:日账单格式为yyyy-MM-dd,月账单格式为yyyy-MM。 + * @return + */ + Object downloadbill(Date billDate, String billType); + + +} diff --git a/pay-java-common/src/main/java/in/egan/pay/common/api/RequestExecutor.java b/pay-java-common/src/main/java/in/egan/pay/common/before/api/RequestExecutor.java similarity index 89% rename from pay-java-common/src/main/java/in/egan/pay/common/api/RequestExecutor.java rename to pay-java-common/src/main/java/in/egan/pay/common/before/api/RequestExecutor.java index 5812f4b..3164cc4 100644 --- a/pay-java-common/src/main/java/in/egan/pay/common/api/RequestExecutor.java +++ b/pay-java-common/src/main/java/in/egan/pay/common/before/api/RequestExecutor.java @@ -1,9 +1,8 @@ -package in.egan.pay.common.api; +package in.egan.pay.common.before.api; import in.egan.pay.common.exception.PayErrorException; import org.apache.http.HttpHost; -import org.apache.http.client.ClientProtocolException; import org.apache.http.impl.client.CloseableHttpClient; import java.io.IOException; @@ -15,6 +14,7 @@ import java.io.IOException; * @param 请求参数类型 * @source chanjarster/weixin-java-tools */ +@Deprecated public interface RequestExecutor { /** diff --git a/pay-java-common/src/main/java/in/egan/pay/common/before/bean/result/PayError.java b/pay-java-common/src/main/java/in/egan/pay/common/before/bean/result/PayError.java new file mode 100644 index 0000000..c0af030 --- /dev/null +++ b/pay-java-common/src/main/java/in/egan/pay/common/before/bean/result/PayError.java @@ -0,0 +1,107 @@ +package in.egan.pay.common.before.bean.result; + +import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.JSONObject; +import in.egan.pay.common.util.XML; + +import java.io.Serializable; + +/** + * 支付错误码说明 + * @author Daniel Qian + * @dete 2017/1/12 9:57 + * @author: egan + * @source chanjarster/weixin-java-tools + * @see in.egan.pay.common.bean.result.PayError + */ +@Deprecated +public class PayError implements in.egan.pay.common.bean.result.PayError, Serializable { + + private int errorCode; + + private String errorMsg; + + private JSONObject json; + private String responseContent; + + public PayError() { + } + public PayError(int errorCode, String errorMsg) { + this.errorCode = errorCode; + this.errorMsg = errorMsg; + } + + + public PayError(int errorCode, String errorMsg, String responseContent) { + this(errorCode, errorMsg); + this.responseContent = responseContent; + } + + + public String getErrorCode() { + return errorCode + ""; + } + + public void setErrorCode(int errorCode) { + this.errorCode = errorCode; + } + + public String getErrorMsg() { + return errorMsg; + } + + @Override + public String getString() { + return "支付错误: errcode=" + errorCode + ", errmsg=" + errorMsg + "\njson:" + json; + } + + public void setErrorMsg(String errorMsg) { + this.errorMsg = errorMsg; + } + + public JSONObject getJson() { + return json; + } + + public void setJson(JSONObject json) { + this.json = json; + } + + public String getResponseContent() { + return responseContent; + } + + public void setResponseContent(String responseContent) { + this.responseContent = responseContent; + } + + public static PayError fromJson(String json) { + + JSONObject jsonObject = JSON.parseObject(json); + PayError error = jsonObject.toJavaObject(PayError.class); + error.setJson(jsonObject); + error.setResponseContent(json); + return error; + } + + public static PayError fromXml(String xml) { + JSONObject jsonObject = XML.toJSONObject(xml); + if (null == jsonObject.get("return_code")){ + PayError error = new PayError(403, null == jsonObject.get("return_msg") ? "未知错误!" : jsonObject.get("return_msg").toString()); + return error; + } + + if ("FAIL".equals( jsonObject.get("return_code"))){ + PayError error = new PayError(-1, jsonObject.get("return_msg").toString()); + return error; + } + return null; + } + + + @Override + public String toString() { + return "支付错误: errcode=" + errorCode + ", errmsg=" + errorMsg + "\njson:" + json; + } + +} diff --git a/pay-java-common/src/main/java/in/egan/pay/common/util/http/SimpleGetRequestExecutor.java b/pay-java-common/src/main/java/in/egan/pay/common/before/util/http/SimpleGetRequestExecutor.java similarity index 88% rename from pay-java-common/src/main/java/in/egan/pay/common/util/http/SimpleGetRequestExecutor.java rename to pay-java-common/src/main/java/in/egan/pay/common/before/util/http/SimpleGetRequestExecutor.java index 1483399..b3a734f 100644 --- a/pay-java-common/src/main/java/in/egan/pay/common/util/http/SimpleGetRequestExecutor.java +++ b/pay-java-common/src/main/java/in/egan/pay/common/before/util/http/SimpleGetRequestExecutor.java @@ -1,4 +1,4 @@ -package in.egan.pay.common.util.http; +package in.egan.pay.common.before.util.http; /** * @author egan @@ -7,8 +7,7 @@ package in.egan.pay.common.util.http; * @source chanjarster/weixin-java-tools */ -import in.egan.pay.common.api.RequestExecutor; -import in.egan.pay.common.bean.result.PayError; +import in.egan.pay.common.before.api.RequestExecutor; import in.egan.pay.common.exception.PayErrorException; import org.apache.http.HttpHost; import org.apache.http.client.config.RequestConfig; @@ -18,10 +17,6 @@ import org.apache.http.impl.client.CloseableHttpClient; import java.io.IOException; - - -import org.apache.http.impl.client.CloseableHttpClient; - /** * 简单的GET请求执行器,请求的参数是String, 返回的结果也是String * @author Daniel Qian diff --git a/pay-java-common/src/main/java/in/egan/pay/common/util/http/SimplePostRequestExecutor.java b/pay-java-common/src/main/java/in/egan/pay/common/before/util/http/SimplePostRequestExecutor.java similarity index 94% rename from pay-java-common/src/main/java/in/egan/pay/common/util/http/SimplePostRequestExecutor.java rename to pay-java-common/src/main/java/in/egan/pay/common/before/util/http/SimplePostRequestExecutor.java index 88b63f3..aa49df6 100644 --- a/pay-java-common/src/main/java/in/egan/pay/common/util/http/SimplePostRequestExecutor.java +++ b/pay-java-common/src/main/java/in/egan/pay/common/before/util/http/SimplePostRequestExecutor.java @@ -1,7 +1,6 @@ -package in.egan.pay.common.util.http; +package in.egan.pay.common.before.util.http; -import in.egan.pay.common.api.RequestExecutor; -import in.egan.pay.common.bean.result.PayError; +import in.egan.pay.common.before.api.RequestExecutor; import in.egan.pay.common.exception.PayErrorException; import org.apache.http.Consts; import org.apache.http.HttpHost; diff --git a/pay-java-common/src/main/java/in/egan/pay/common/util/http/Utf8ResponseHandler.java b/pay-java-common/src/main/java/in/egan/pay/common/before/util/http/Utf8ResponseHandler.java similarity index 88% rename from pay-java-common/src/main/java/in/egan/pay/common/util/http/Utf8ResponseHandler.java rename to pay-java-common/src/main/java/in/egan/pay/common/before/util/http/Utf8ResponseHandler.java index a5d74d0..405e267 100644 --- a/pay-java-common/src/main/java/in/egan/pay/common/util/http/Utf8ResponseHandler.java +++ b/pay-java-common/src/main/java/in/egan/pay/common/before/util/http/Utf8ResponseHandler.java @@ -1,4 +1,4 @@ -package in.egan.pay.common.util.http; +package in.egan.pay.common.before.util.http; import org.apache.http.Consts; import org.apache.http.HttpEntity; @@ -24,7 +24,7 @@ public class Utf8ResponseHandler implements ResponseHandler { public String handleResponse(final HttpResponse response) throws IOException { final StatusLine statusLine = response.getStatusLine(); final HttpEntity entity = response.getEntity(); - if (statusLine.getStatusCode() >= 300) { + if (statusLine.getStatusCode() >= 300 && statusLine.getStatusCode() != 304) { EntityUtils.consume(entity); throw new HttpResponseException(statusLine.getStatusCode(), statusLine.getReasonPhrase()); } diff --git a/pay-java-common/src/main/java/in/egan/pay/common/exception/PayErrorException.java b/pay-java-common/src/main/java/in/egan/pay/common/exception/PayErrorException.java index 96a132c..cc43837 100644 --- a/pay-java-common/src/main/java/in/egan/pay/common/exception/PayErrorException.java +++ b/pay-java-common/src/main/java/in/egan/pay/common/exception/PayErrorException.java @@ -1,23 +1,24 @@ package in.egan.pay.common.exception; +import com.alibaba.fastjson.JSON; import in.egan.pay.common.bean.result.PayError; /** * @author egan * @email egzosn@gmail.com * @date 2016-5-18 14:09:01 - * @source chanjarster/weixin-java-tools */ -public class PayErrorException extends Exception { +public class PayErrorException extends RuntimeException { private PayError error; public PayErrorException(PayError error) { - super(error.toString()); + super(error.getString()); this.error = error; } - public PayError getError() { + + public PayError getPayError() { return error; } } diff --git a/pay-java-common/src/main/java/in/egan/pay/common/http/ClientHttpRequest.java b/pay-java-common/src/main/java/in/egan/pay/common/http/ClientHttpRequest.java new file mode 100644 index 0000000..5194c62 --- /dev/null +++ b/pay-java-common/src/main/java/in/egan/pay/common/http/ClientHttpRequest.java @@ -0,0 +1,220 @@ +package in.egan.pay.common.http; + +import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.JSONException; +import in.egan.pay.common.bean.MethodType; +import in.egan.pay.common.bean.result.PayException; +import in.egan.pay.common.exception.PayErrorException; +import in.egan.pay.common.util.XML; +import org.apache.http.*; +import org.apache.http.client.ClientProtocolException; +import org.apache.http.client.HttpResponseException; +import org.apache.http.client.config.RequestConfig; +import org.apache.http.client.methods.HttpEntityEnclosingRequestBase; +import org.apache.http.entity.ContentType; +import org.apache.http.entity.StringEntity; +import org.apache.http.util.EntityUtils; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.UnsupportedEncodingException; +import java.net.URI; +import java.net.URLEncoder; +import java.util.List; +import java.util.Map; + +/** + * 一个HTTP请求的客户端 + * @author: egan + * @email egzosn@gmail.com + * @date 2017/3/4 17:56 + */ +public class ClientHttpRequest extends HttpEntityEnclosingRequestBase implements org.apache.http.client.ResponseHandler{ + //http请求 + private MethodType method; + //响应类型 + private Class responseType; + + + public ClientHttpRequest setResponseType(Class responseType) { + this.responseType = responseType; + return this; + } + + public ClientHttpRequest() { + } + + public ClientHttpRequest(URI uri, MethodType method, Object request) { + this.setURI(uri); + this.method = method; + setParameters(request); + } + public ClientHttpRequest(URI uri, MethodType method) { + this.setURI(uri); + this.method = method; + } + public ClientHttpRequest(URI uri) { + this.setURI(uri); + } + + public ClientHttpRequest(String uri) { + this.setURI(URI.create(uri)); + } + public ClientHttpRequest(String uri, MethodType method) { + this.setURI(URI.create(uri)); + this.method = method; + } + + public ClientHttpRequest(String uri, MethodType method, Object request) { + this.setURI(URI.create(uri)); + this.method = method; + setParameters(request); + } + + public void setMethod(MethodType method) { + this.method = method; + } + + @Override + public String getMethod() { + return method.name(); + } + + /** + * 设置代理 + * @param httpProxy http代理配置信息 + * @return + */ + public ClientHttpRequest setProxy(HttpHost httpProxy){ + if (httpProxy != null) { + RequestConfig config = RequestConfig.custom().setProxy(httpProxy).build(); + setConfig(config); + } + return this; + } + + /** + * Map转化为对应得参数字符串 + * @param pe + * @return + */ + public static String getMapToParameters(Map pe){ + StringBuilder builder = new StringBuilder(); + for (Object key : pe.keySet()) { + Object o = pe.get(key); + + if (null == o) { + continue; + } + + if (o instanceof List) { + o = ((List) o).toArray(); + } + try { + if (o instanceof Object[]) { + Object[] os = (Object[]) o; + String valueStr = ""; + for (int i = 0, len = os.length; i < len; i++) { + if (null == os[i]) { + continue; + } + String value = os[i].toString().trim(); + valueStr += (i == len - 1) ? value : value + ","; + } + builder.append(key).append("=").append(URLEncoder.encode(valueStr, "utf-8")).append("&"); + + continue; + } + builder.append(key).append("=").append(URLEncoder.encode((String) pe.get(key), "utf-8")).append("&"); + } catch (UnsupportedEncodingException e) { + e.printStackTrace(); + } + } + if (builder.length() > 1) { + builder.deleteCharAt(builder.length() - 1); + } + return builder.toString(); + } + + /** + * 设置请求参数 + * + * @param request 请求参数 + * @return + */ + public ClientHttpRequest setParameters(Object request) { + if (null == request){ + return this; + } + if (request instanceof Map) { + StringEntity entity = new StringEntity(getMapToParameters((Map) request), Consts.UTF_8); + setEntity(entity); + } else if (request instanceof String) { + StringEntity entity = new StringEntity((String) request, Consts.UTF_8); + setEntity(entity); + } else { + StringEntity entity = new StringEntity(JSON.toJSONString(request), ContentType.APPLICATION_JSON); + setEntity(entity); + } + + return this; + + } + + @Override + public T handleResponse(HttpResponse response) throws ClientProtocolException, IOException { + final StatusLine statusLine = response.getStatusLine(); + final HttpEntity entity = response.getEntity(); + + if (statusLine.getStatusCode() >= 300 && statusLine.getStatusCode() != 304) { + EntityUtils.consume(entity); + throw new HttpResponseException(statusLine.getStatusCode(), statusLine.getReasonPhrase()); + } + if (null == responseType){ + responseType = (Class) String.class; + } + + + String[] value = entity.getContentType().getValue().split(";"); + + if (ContentType.APPLICATION_OCTET_STREAM.getMimeType().equals(value[0])){ + + if (responseType.isAssignableFrom(InputStream.class)){ + return (T)entity.getContent(); + } + if (responseType.isAssignableFrom(OutputStream.class)){ + try { + T t = responseType.newInstance(); + entity.writeTo((OutputStream)t); + return t; + } catch (InstantiationException e) { + e.printStackTrace(); + } catch (IllegalAccessException e) { + e.printStackTrace(); + } + throw new HttpResponseException(statusLine.getStatusCode(), responseType + " 无法进行类型转换"); + } + } + + String result = EntityUtils.toString(entity, value[1].substring( value[1].indexOf("=") + 1)); + String frist = result.substring(0, 1); + if (ContentType.APPLICATION_JSON.getMimeType().equals( value[0]) || "{[".indexOf(frist) >= 0){ + try { + return JSON.parseObject(result, responseType); + }catch (JSONException e){ + throw new PayErrorException(new PayException("failure", "类型转化异常,contentType:" + entity.getContentType().getValue(), result)); + } + } + + if (ContentType.APPLICATION_XML.getMimeType().equals( value[0])){ + return XML.toJSONObject(result).toJavaObject(responseType); + } + if (!responseType.isAssignableFrom(String.class)){ + throw new PayErrorException(new PayException("failure", "类型转化异常,contentType:" + entity.getContentType().getValue(), result)); + } + + return (T)result; + + } +} diff --git a/pay-java-common/src/main/java/in/egan/pay/common/http/HttpConfigStorage.java b/pay-java-common/src/main/java/in/egan/pay/common/http/HttpConfigStorage.java new file mode 100644 index 0000000..f0e9d0b --- /dev/null +++ b/pay-java-common/src/main/java/in/egan/pay/common/http/HttpConfigStorage.java @@ -0,0 +1,70 @@ +package in.egan.pay.common.http; + + +/** + * HTTP 配置 + * @author: egan + * @email egzosn@gmail.com + * @date 2017/3/3 20:48 + */ +public class HttpConfigStorage { + //http代理地址 + protected String httpProxyHost; + //代理端口 + protected int httpProxyPort; + //代理用户名 + protected String httpProxyUsername; + //代理密码 + protected String httpProxyPassword; + + + /** + * http代理地址 + * @return + */ + public String getHttpProxyHost() { + return httpProxyHost; + } + + + public void setHttpProxyHost(String httpProxyHost) { + this.httpProxyHost = httpProxyHost; + } + + /** + * 代理端口 + * @return + */ + public int getHttpProxyPort() { + return httpProxyPort; + } + + public void setHttpProxyPort(int httpProxyPort) { + this.httpProxyPort = httpProxyPort; + } + /** + * 代理用户名 + * @return + */ + public String getHttpProxyUsername() { + return httpProxyUsername; + } + + public void setHttpProxyUsername(String httpProxyUsername) { + this.httpProxyUsername = httpProxyUsername; + } + + /** + * 代理密码 + * @return + */ + public String getHttpProxyPassword() { + return httpProxyPassword; + } + + public void setHttpProxyPassword(String httpProxyPassword) { + this.httpProxyPassword = httpProxyPassword; + } + + +} diff --git a/pay-java-common/src/main/java/in/egan/pay/common/util/XML.java b/pay-java-common/src/main/java/in/egan/pay/common/util/XML.java index 560f872..a1295b4 100644 --- a/pay-java-common/src/main/java/in/egan/pay/common/util/XML.java +++ b/pay-java-common/src/main/java/in/egan/pay/common/util/XML.java @@ -13,7 +13,6 @@ import java.io.InputStream; import java.util.Iterator; import java.util.List; import java.util.Map; -import java.util.SortedMap; /** diff --git a/pay-java-common/src/main/java/in/egan/pay/common/util/sign/SignUtils.java b/pay-java-common/src/main/java/in/egan/pay/common/util/sign/SignUtils.java index a7dd0c3..f392f19 100644 --- a/pay-java-common/src/main/java/in/egan/pay/common/util/sign/SignUtils.java +++ b/pay-java-common/src/main/java/in/egan/pay/common/util/sign/SignUtils.java @@ -71,7 +71,7 @@ public enum SignUtils { * @param separator 分隔符 * @return 去掉空值与签名参数后的新签名,拼接后字符串 */ - public static String parameterText(Map parameters, String separator) { + /* public static String parameterText(Map parameters, String separator) { if(parameters == null){ return ""; } @@ -107,8 +107,7 @@ public enum SignUtils { for (int i = 0; i < values.length; i++) { String value = values[i].trim(); if ("".equals(value)){ continue;} - valueStr = (i == values.length - 1) ? valueStr + value - : valueStr + value + ","; + valueStr += (i == values.length - 1) ? value : value + ","; } } else if (o != null) { valueStr = o.toString(); @@ -122,6 +121,79 @@ public enum SignUtils { sb.deleteCharAt(sb.length() - 1); } return sb.toString(); + }*/ + + /** + * + * 把数组所有元素排序,并按照“参数=参数值”的模式用“@param separator”字符拼接成字符串 + * @param parameters 参数 + * @param separator 分隔符 + * @return 去掉空值与签名参数后的新签名,拼接后字符串 + */ + public static String parameterText(Map parameters, String separator) { + return parameterText(parameters, separator, "sign", "key", "appId", "sign_type"); + } + + /** + * + * 把数组所有元素排序,并按照“参数=参数值”的模式用“@param separator”字符拼接成字符串 + * @param parameters 参数 + * @param separator 分隔符 + * @param ignoreKey 需要忽略添加的key + * @return 去掉空值与签名参数后的新签名,拼接后字符串 + */ + public static String parameterText(Map parameters, String separator, String... ignoreKey ) { + if(parameters == null){ + return ""; + } + StringBuffer sb = new StringBuffer(); + if (null != ignoreKey){ + Arrays.sort(ignoreKey); + } + // TODO 2016/11/11 10:14 author: egan 已经排序好处理 + if (parameters instanceof SortedMap) { + for (String k : ((Set) parameters.keySet())) { + Object v = parameters.get(k); + if (null == v || "".equals(v.toString().trim()) || (null != ignoreKey && Arrays.binarySearch(ignoreKey, k ) >= 0)) { + continue; + } + sb.append(k ).append("=").append( v.toString().trim()).append(separator); + } + if (sb.length() > 0 && !"".equals(separator)) { + sb.deleteCharAt(sb.length() - 1); + } + return sb.toString(); + + } + + + // TODO 2016/11/11 10:14 author: egan 未排序须处理 + List keys = new ArrayList(parameters.keySet()); + //排序 + Collections.sort(keys); + for (String k : keys) { + String valueStr = ""; + Object o = parameters.get(k); + if (o instanceof String[]) { + String[] values = (String[]) o; + if (null == values){continue;} + for (int i = 0; i < values.length; i++) { + String value = values[i].trim(); + if ("".equals(value)){ continue;} + valueStr += (i == values.length - 1) ? value : value + ","; + } + } else if (o != null) { + valueStr = o.toString(); + } + if (null == valueStr || "".equals(valueStr.toString().trim()) || (null != ignoreKey && Arrays.binarySearch(ignoreKey, k ) >= 0)) { + continue; + } + sb.append(k ).append("=").append( valueStr).append(separator); + } + if (sb.length() > 0) { + sb.deleteCharAt(sb.length() - 1); + } + return sb.toString(); } /** diff --git a/pay-java-wx-youdian/src/main/java/in/egan/pay/wx/youdian/utils/SimpleGetRequestExecutor.java b/pay-java-wx-youdian/src/main/java/in/egan/pay/wx/youdian/utils/SimpleGetRequestExecutor.java deleted file mode 100644 index 29df307..0000000 --- a/pay-java-wx-youdian/src/main/java/in/egan/pay/wx/youdian/utils/SimpleGetRequestExecutor.java +++ /dev/null @@ -1,56 +0,0 @@ -package in.egan.pay.wx.youdian.utils; - - - -import com.alibaba.fastjson.JSON; -import com.alibaba.fastjson.JSONObject; -import in.egan.pay.common.api.RequestExecutor; -import in.egan.pay.common.bean.result.PayError; -import in.egan.pay.common.exception.PayErrorException; -import in.egan.pay.common.util.http.Utf8ResponseHandler; -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.impl.client.CloseableHttpClient; -import java.io.IOException; - - -/** - * 简单的GET请求执行器,请求的参数是String, 返回的结果JSONObject - * @author egan - * @email egzosn@gmail.com - * @date 2017/01/12 22:58 - */ -public class SimpleGetRequestExecutor implements RequestExecutor { - - @Override - public JSONObject execute(CloseableHttpClient httpclient, HttpHost httpProxy, String uri, String queryParam) throws IOException, PayErrorException { - if (queryParam != null) { - if (uri.indexOf('?') == -1) { - uri += '?'; - } - uri += uri.endsWith("?") ? queryParam : '&' + queryParam; - } - HttpGet httpGet = new HttpGet(uri); - if (httpProxy != null) { - RequestConfig config = RequestConfig.custom().setProxy(httpProxy).build(); - httpGet.setConfig(config); - } - - try (CloseableHttpResponse response = httpclient.execute(httpGet)) { - String text = Utf8ResponseHandler.INSTANCE.handleResponse(response); - JSONObject jsonObject = JSON.parseObject(text); - PayError payError = new PayError(jsonObject.getIntValue("errorcode"), jsonObject.getString("msg"), text); - if (0 != payError.getErrorCode()){ - throw new PayErrorException(payError); - } - return jsonObject; - }finally { - httpGet.releaseConnection(); - } - - - } - -}