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