diff --git a/README.md b/README.md index a31cf77..dba8693 100644 --- a/README.md +++ b/README.md @@ -1,25 +1,21 @@ #pay-java-parent -##整合支付模块(微信支付,支付宝) +## 整合支付模块 声明: 本项目最初想法自 https://github.com/chanjarster/weixin-java-tools, 15年1月左右关注chanjarster/weixin-java-tools,并将其回调处理修改并进行使用。 开发版 https://git.oschina.net/egzosn/pay-java-parent/tree/develop -##### 详细文档请看 [wiki](https://gitee.com/egzosn/pay-java-parent/wikis/Home)。 - ### 特性 - - 1. 不依赖任何 mvc 框架 2. 也不依赖 servlet,仅仅作为工具使用,可轻松嵌入到任何系统里(项目例子利用spring mvc的 @PathVariable进行,推荐使用类似的框架) 3. 支付请求调用支持HTTP和异步、支持http代理 4. 控制层统一异常处理 5. LogBack日志记录 6. 简单快速完成支付模块的开发 - 7. 支持多种支付类型多支付账户扩展(目前已支持微信支付,支付宝支付,友店支付) + 7. 支持多种支付类型多支付账户扩展 ### 本项目包含 3 个部分: @@ -59,8 +55,8 @@ ###### 单一支付教程 - [基础模块支付宝微信讲解](https://gitee.com/egzosn/pay-java-parent/wikis/Home) - + [基础模块支付宝微信讲解](pay-java-parent/wikis/Home) + [友店微信](pay-java-wx-youdian?dir=1&filepath=pay-java-youdian) [富友](pay-java-fuiou?dir=1&filepath=pay-java-fuiou) diff --git a/pay-java-ali/README.md b/pay-java-ali/README.md index 7a13917..8cec50e 100644 --- a/pay-java-ali/README.md +++ b/pay-java-ali/README.md @@ -1,8 +1,8 @@ -##支付宝支付简单例子 +## 支付宝支付简单例子 -####支付配置 +#### 支付配置 ```java @@ -49,7 +49,7 @@ ``` -####创建支付服务 +#### 创建支付服务 ```java @@ -61,7 +61,7 @@ ``` -####创建支付订单信息 +#### 创建支付订单信息 ```java @@ -70,7 +70,7 @@ ``` -####扫码付 +#### 扫码付 ```java @@ -83,7 +83,7 @@ ``` -####APP支付 +#### APP支付 ```java @@ -95,7 +95,7 @@ ``` -####即时到帐 WAP 网页支付 +#### 即时到帐 WAP 网页支付 ```java @@ -111,7 +111,7 @@ ``` -####条码付 声波付 +#### 条码付 声波付 ```java @@ -127,7 +127,7 @@ ``` -####回调处理 +#### 回调处理 ```java @@ -146,7 +146,7 @@ ``` -####支付订单查询 +#### 支付订单查询 ```java @@ -155,7 +155,7 @@ ``` -####交易关闭接口 +#### 交易关闭接口 ```java Map result = service..query("支付宝单号", "我方系统单号"); @@ -163,7 +163,7 @@ ``` -####申请退款接口 +#### 申请退款接口 ```java Map result = service.refund("支付宝单号", "我方系统单号", "退款金额", "订单总金额"); @@ -171,14 +171,14 @@ ``` -####查询退款 +#### 查询退款 ```java Map result = service.refundquery("支付宝单号", "我方系统单号"); ``` -####下载对账单 +#### 下载对账单 ```java Map result = service.downloadbill("账单时间:日账单格式为yyyy-MM-dd,月账单格式为yyyy-MM", "账单类型"); diff --git a/pay-java-ali/src/main/java/com/egzosn/pay/ali/api/AliPayConfigStorage.java b/pay-java-ali/src/main/java/com/egzosn/pay/ali/api/AliPayConfigStorage.java index 8598b24..fad78e7 100644 --- a/pay-java-ali/src/main/java/com/egzosn/pay/ali/api/AliPayConfigStorage.java +++ b/pay-java-ali/src/main/java/com/egzosn/pay/ali/api/AliPayConfigStorage.java @@ -4,34 +4,26 @@ import com.egzosn.pay.common.api.BasePayConfigStorage; /** * 支付配置存储 - * author egan + * @author egan * * email egzosn@gmail.com * date 2016-5-18 14:09:01 */ public class AliPayConfigStorage extends BasePayConfigStorage { - // 商户PID - public volatile String appId ; - // 商户签约拿到的pid,partner_id的简称,合作伙伴身份等同于 partner - public volatile String pid ; - //partner_id的简称,合作伙伴身份 -// public volatile String partner ; + /** + * 商户应用id + */ + private volatile String appId ; + /** + * 商户签约拿到的pid,partner_id的简称,合作伙伴身份等同于 partner + */ + private volatile String pid ; - // 商户收款账号 - public volatile String seller; - //公钥 - private volatile String aliPublicKey; - - - public String getAliPublicKey() { - return aliPublicKey; - } - - public void setAliPublicKey(String aliPublicKey) { - setKeyPublic(aliPublicKey); - this.aliPublicKey = aliPublicKey; - } + /** + * 商户收款账号 + */ + private volatile String seller; @@ -44,26 +36,6 @@ public class AliPayConfigStorage extends BasePayConfigStorage { return appId; } - /** - * @see #getPid() - * @return 合作者id - */ - @Deprecated - @Override - public String getPartner() { - return pid; - } - - - /** - * 设置合作者id - * @see #setPid(String) - * @param partner 合作者id - */ - @Deprecated - public void setPartner(String partner) { - this.pid = partner; - } @Override public String getPid() { @@ -74,6 +46,7 @@ public class AliPayConfigStorage extends BasePayConfigStorage { this.pid = pid; } + @Override public String getSeller() { return seller; } diff --git a/pay-java-ali/src/main/java/com/egzosn/pay/ali/api/AliPayService.java b/pay-java-ali/src/main/java/com/egzosn/pay/ali/api/AliPayService.java index 68c94bb..d5d832c 100644 --- a/pay-java-ali/src/main/java/com/egzosn/pay/ali/api/AliPayService.java +++ b/pay-java-ali/src/main/java/com/egzosn/pay/ali/api/AliPayService.java @@ -163,11 +163,6 @@ public class AliPayService extends BasePayService { */ private Map getOrder(PayOrder order) { - //兼容上一版本 即时收款 - /* if (AliTransactionType.DIRECT == order.getTransactionType() || AliTransactionType.MOBILE == order.getTransactionType() || AliTransactionType.WAPPAY == order.getTransactionType()){ - return getOrderBefore(order); - } -*/ Map orderInfo = getPublicParameters(order.getTransactionType()); @@ -185,7 +180,6 @@ public class AliPayService extends BasePayService { case DIRECT: bizContent.put("product_code", "FAST_INSTANT_TRADE_PAY"); orderInfo.put("return_url", payConfigStorage.getReturnUrl()); -// bizContent.remove("seller_id"); break; case WAP: bizContent.put("product_code", "QUICK_WAP_PAY"); @@ -245,7 +239,6 @@ public class AliPayService extends BasePayService { valueStr += (i == len - 1) ? values[i] : values[i] + ","; } //乱码解决,这段代码在出现乱码时使用。如果mysign和sign不相等也可以使用这段代码转化 - //valueStr = new String(valueStr.getBytes("ISO-8859-1"), "gbk"); if (!valueStr.matches("\\w+")){ try { if(valueStr.equals(new String(valueStr.getBytes("iso8859-1"), "iso8859-1"))){ @@ -313,8 +306,7 @@ public class AliPayService extends BasePayService { Map orderInfo = orderInfo(order); -// Map content = new HashMap<>(1); -// content.put("biz_content", orderInfo.remove("biz_content")); + //预订单 JSONObject result = getHttpRequestTemplate().postForObject(getReqUrl() + "?" + UriVariables.getMapToParameters(orderInfo), null, JSONObject.class); JSONObject response = result.getJSONObject("alipay_trade_precreate_response"); diff --git a/pay-java-ali/src/main/java/com/egzosn/pay/ali/bean/AliTransactionType.java b/pay-java-ali/src/main/java/com/egzosn/pay/ali/bean/AliTransactionType.java index df56276..57c4ea2 100644 --- a/pay-java-ali/src/main/java/com/egzosn/pay/ali/bean/AliTransactionType.java +++ b/pay-java-ali/src/main/java/com/egzosn/pay/ali/bean/AliTransactionType.java @@ -19,16 +19,54 @@ import com.egzosn.pay.common.bean.TransactionType; * date 2016/10/19 22:58 */ public enum AliTransactionType implements TransactionType { - //即时到帐 //手机网站支付 - DIRECT("alipay.trade.page.pay"),APP("alipay.trade.app.pay"),WAP("alipay.trade.wap.pay") - // TODO 2017/2/23 20:26 author: egan 以下三个为主动交易类型 暂未测试, - //扫码付 //条码付 // 声波付 - ,SWEEPPAY("alipay.trade.precreate"),BAR_CODE("alipay.trade.pay"),WAVE_CODE("alipay.trade.pay") - //交易辅助接口 - ,QUERY("alipay.trade.query"),CLOSE("alipay.trade.close"),REFUND("alipay.trade.refund"),REFUNDQUERY("alipay.trade.fastpay.refund.query"),DOWNLOADBILL("alipay.data.dataservice.bill.downloadurl.query") + /** + * 即时到帐 + */ + DIRECT("alipay.trade.page.pay"), + /** + * APP支付 + */ + APP("alipay.trade.app.pay"), + /** + * 手机网站支付 + */ + WAP("alipay.trade.wap.pay") + + /** + * 扫码付 + */ + ,SWEEPPAY("alipay.trade.precreate"), + /** + * 条码付 + */ + BAR_CODE("alipay.trade.pay"), + /** + * 声波付 + */ + WAVE_CODE("alipay.trade.pay") + //交易辅助接口 + + /** + * 交易订单查询 + */ + ,QUERY("alipay.trade.query"), + /** + * 交易订单关闭 + */ + CLOSE("alipay.trade.close"), + /** + * 退款 + */ + REFUND("alipay.trade.refund"), + /** + * 退款查询 + */ + REFUNDQUERY("alipay.trade.fastpay.refund.query"), + /** + * 下载对账单 + */ + DOWNLOADBILL("alipay.data.dataservice.bill.downloadurl.query") - //回调通知,回调后不清楚交易类型,以此定义 - ,UNAWARE("UNAWARE") ; @@ -44,7 +82,7 @@ public enum AliTransactionType implements TransactionType { return this.name(); } - /** + /* * * 获取接口名称 * @return 接口名称 */ diff --git a/pay-java-ali/src/main/java/com/egzosn/pay/ali/before/api/AliPayService.java b/pay-java-ali/src/main/java/com/egzosn/pay/ali/before/api/AliPayService.java index 263d014..8aa2b58 100644 --- a/pay-java-ali/src/main/java/com/egzosn/pay/ali/before/api/AliPayService.java +++ b/pay-java-ali/src/main/java/com/egzosn/pay/ali/before/api/AliPayService.java @@ -33,11 +33,11 @@ import java.util.*; * @see com.egzosn.pay.ali.api.AliPayService */ public class AliPayService extends BasePayService { - protected final Log log = LogFactory.getLog(AliPayService.class); + protected final Log LOG = LogFactory.getLog(AliPayService.class); - private String httpsReqUrl = "https://mapi.alipay.com/gateway.do"; - private String queryReqUrl = "https://openapi.alipay.com/gateway.do"; + private static final String HTTPS_REQ_URL = "https://mapi.alipay.com/gateway.do"; + private static final String QUERY_REQ_URL = "https://openapi.alipay.com/gateway.do"; public AliPayService(PayConfigStorage payConfigStorage) { super(payConfigStorage); @@ -49,7 +49,7 @@ public class AliPayService extends BasePayService { public String getHttpsVerifyUrl() { - return httpsReqUrl + "?service=notify_verify"; + return HTTPS_REQ_URL + "?service=notify_verify"; } /** @@ -62,14 +62,14 @@ public class AliPayService extends BasePayService { public boolean verify(Map params) { if (params.get("sign") == null || params.get("notify_id") == null) { - log.debug("支付宝支付异常:params:" + params); + LOG.debug("支付宝支付异常:params:" + params); return false; } try { return signVerify(params, (String) params.get("sign")) && verifySource((String) params.get("notify_id")); } catch (PayErrorException e) { - e.printStackTrace(); + LOG.error(e); } return false; @@ -164,7 +164,7 @@ public class AliPayService extends BasePayService { } private String getOrderInfo(PayOrder order) { - String orderInfo = "partner=\"" + this.payConfigStorage.getPartner() + "\""; + String orderInfo = "partner=\"" + this.payConfigStorage.getPid() + "\""; orderInfo = orderInfo + "&seller_id=\"" + this.payConfigStorage.getSeller() + "\""; orderInfo = orderInfo + "&out_trade_no=\"" + order.getOutTradeNo() + "\""; orderInfo = orderInfo + "&subject=\"" + order.getSubject() + "\""; @@ -190,7 +190,7 @@ public class AliPayService extends BasePayService { private Map getOrder(PayOrder order) { Map orderInfo = new TreeMap<>(); // 签约合作者身份ID - orderInfo.put("partner", payConfigStorage.getPartner()); + orderInfo.put("partner", payConfigStorage.getPid()); // 签约卖家支付宝账号 orderInfo.put("seller_id", payConfigStorage.getSeller()); // 商户网站唯一订单号 @@ -299,7 +299,7 @@ public class AliPayService extends BasePayService { StringBuffer formHtml = new StringBuffer(); formHtml.append("
rules = new ArrayList(); - + /** + * 支付服务 + */ private final PayService payService; - + /** + * 异步线程处理器 + */ private ExecutorService executorService; - + /** + * 支付异常处理器 + */ private PayErrorExceptionHandler exceptionHandler; + /** + * 根据支付服务创建路由 + * @param payService 支付服务 + */ public PayMessageRouter(PayService payService) { this.payService = payService; this.executorService = Executors.newFixedThreadPool(DEFAULT_THREAD_POOL_SIZE); @@ -130,6 +145,7 @@ public class PayMessageRouter { if(rule.isAsync()) { futures.add( executorService.submit(new Runnable() { + @Override public void run() { rule.service(payMessage, payService, exceptionHandler); } @@ -138,7 +154,7 @@ public class PayMessageRouter { } else { res = rule.service(payMessage, payService, exceptionHandler); // 在同步操作结束,session访问结束 - log.debug("End session access: async=false, fromPay=" + payMessage.getFromPay()); + LOG.debug("End session access: async=false, fromPay=" + payMessage.getFromPay()); } } @@ -149,12 +165,12 @@ public class PayMessageRouter { for (Future future : futures) { try { future.get(); - log.debug("End session access: async=true, fromPay=" + payMessage.getFromPay()); + LOG.debug("End session access: async=true, fromPay=" + payMessage.getFromPay()); } catch (InterruptedException e) { - log.error("Error happened when wait task finish", e); + LOG.error("Error happened when wait task finish", e); } catch (ExecutionException e) { - log.error("Error happened when wait task finish", e); + LOG.error("Error happened when wait task finish", e); } } } diff --git a/pay-java-common/src/main/java/com/egzosn/pay/common/api/PayMessageRouterRule.java b/pay-java-common/src/main/java/com/egzosn/pay/common/api/PayMessageRouterRule.java index 7c45d8b..97bd210 100644 --- a/pay-java-common/src/main/java/com/egzosn/pay/common/api/PayMessageRouterRule.java +++ b/pay-java-common/src/main/java/com/egzosn/pay/common/api/PayMessageRouterRule.java @@ -27,25 +27,39 @@ public class PayMessageRouterRule { private final PayMessageRouter routerBuilder; + /** + * 默认同步 + */ + private boolean async = false; - 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 String key; + /** + * 匹配的键名称对应的值 正则 + */ + private String rValue; private boolean reEnter = false; @@ -101,27 +115,7 @@ public class PayMessageRouterRule { return this; } - /** - * 如果discount等于某值 - * - * @param discount discount等于某值 - * @return Route规则 - */ - public PayMessageRouterRule discount(String discount) { - this.discount = discount; - return this; - } - /** - * 如果discount匹配该正则表达式 - * - * @param regex discount匹配该正则表达式 - * @return Route规则 - */ - public PayMessageRouterRule rDiscount(String regex) { - this.rDiscount = regex; - return this; - } /** * 如果subject等于某值 @@ -144,6 +138,18 @@ public class PayMessageRouterRule { this.rSubject = regex; return this; } + /** + * 如果subject匹配该正则表达式 + * + * @param key 需要匹配支付消息内键的名字 + * @param regex key值对应的正则 + * @return Route规则 + */ + public PayMessageRouterRule key2RValue(String key, String regex) { + this.key = key; + this.rValue = regex; + return this; + } /** @@ -228,19 +234,14 @@ public class PayMessageRouterRule { */ 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.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.key == null ||this.rValue == null || Pattern + .matches(this.rValue, payMessage.getPayMessage().get(key) == null ? "" : payMessage.getPayMessage().get(key).toString().trim())) && (this.subject == null || this.subject .equals(payMessage.getSubject() == null ? null : payMessage.getSubject().trim())) @@ -319,13 +320,6 @@ public class PayMessageRouterRule { this.async = async; } - public String getFromPay() { - return fromPay; - } - - public void setFromPay(String fromPay) { - this.fromPay = fromPay; - } public String getMsgType() { return msgType; @@ -351,20 +345,20 @@ public class PayMessageRouterRule { this.transactionType = transactionType; } - public String getDiscount() { - return discount; + public String getKey() { + return key; } - public void setDiscount(String discount) { - this.discount = discount; + public void setKey(String key) { + this.key = key; } - public String getrDiscount() { - return rDiscount; + public String getrValue() { + return rValue; } - public void setrDiscount(String rDiscount) { - this.rDiscount = rDiscount; + public void setrValue(String rValue) { + this.rValue = rValue; } public String getSubject() { diff --git a/pay-java-common/src/main/java/com/egzosn/pay/common/bean/MethodType.java b/pay-java-common/src/main/java/com/egzosn/pay/common/bean/MethodType.java index 387bd63..36b02f5 100644 --- a/pay-java-common/src/main/java/com/egzosn/pay/common/bean/MethodType.java +++ b/pay-java-common/src/main/java/com/egzosn/pay/common/bean/MethodType.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2017 the original huodull or egan. + * Copyright 2002-2017 the original egan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -23,6 +23,7 @@ package com.egzosn.pay.common.bean; * email egzosn@gmail.com * date 2017/2/7 9:52 * + * 请求方式 */ public enum MethodType { GET, POST diff --git a/pay-java-common/src/main/java/com/egzosn/pay/common/bean/PayOrder.java b/pay-java-common/src/main/java/com/egzosn/pay/common/bean/PayOrder.java index b73169e..06373b9 100644 --- a/pay-java-common/src/main/java/com/egzosn/pay/common/bean/PayOrder.java +++ b/pay-java-common/src/main/java/com/egzosn/pay/common/bean/PayOrder.java @@ -22,11 +22,17 @@ public class PayOrder { private String outTradeNo; //银行卡类型 private String bankType; - //设备号 + //设备信息, private String deviceInfo; + //支付创建ip + private String spbillCreateIp; //付款条码串 与设备号类似??? private String authCode; - // + //WAP支付链接 + private String wapUrl; + //WAP支付网页名称 + private String wapName; + //微信会员唯一标识 private String openid; //交易类型 private TransactionType transactionType; @@ -119,6 +125,14 @@ public class PayOrder { this.bankType = bankType; } + public String getSpbillCreateIp() { + return spbillCreateIp; + } + + public void setSpbillCreateIp(String spbillCreateIp) { + this.spbillCreateIp = spbillCreateIp; + } + public String getAuthCode() { return authCode; } @@ -153,6 +167,22 @@ public class PayOrder { this.outTradeNo = outTradeNo; } + public String getWapUrl() { + return wapUrl; + } + + public void setWapUrl(String wapUrl) { + this.wapUrl = wapUrl; + } + + public String getWapName() { + return wapName; + } + + public void setWapName(String wapName) { + this.wapName = wapName; + } + public String getOpenid() { return openid; } diff --git a/pay-java-common/src/main/java/com/egzosn/pay/common/http/ClientHttpRequest.java b/pay-java-common/src/main/java/com/egzosn/pay/common/http/ClientHttpRequest.java index a0673b8..9018abb 100644 --- a/pay-java-common/src/main/java/com/egzosn/pay/common/http/ClientHttpRequest.java +++ b/pay-java-common/src/main/java/com/egzosn/pay/common/http/ClientHttpRequest.java @@ -32,9 +32,13 @@ import static com.egzosn.pay.common.http.UriVariables.getMapToParameters; * */ public class ClientHttpRequest extends HttpEntityEnclosingRequestBase implements org.apache.http.client.ResponseHandler{ - //http请求 + /** + * http请求方式 get pos + */ private MethodType method; - //响应类型 + /** + * 响应类型 + */ private Class responseType; @@ -43,40 +47,80 @@ public class ClientHttpRequest extends HttpEntityEnclosingRequestBase impleme return this; } + /** + * 空构造 + */ public ClientHttpRequest() { } + /** + * 根据请求地址 请求方法,请求内容对象 + * @param uri 请求地址 + * @param method 请求方法 + * @param request 请求内容 + */ public ClientHttpRequest(URI uri, MethodType method, Object request) { - this.setURI(uri); - this.method = method; + this(uri, method); setParameters(request); } + /** + * 根据请求地址 请求方法 + * @param uri 请求地址 + * @param method 请求方法 + */ public ClientHttpRequest(URI uri, MethodType method) { this.setURI(uri); this.method = method; } + + /** + * 根据请求地址 + * @param uri 请求地址 + */ public ClientHttpRequest(URI uri) { this.setURI(uri); } - + /** + * 根据请求地址 + * @param uri 请求地址 + */ public ClientHttpRequest(String uri) { this.setURI(URI.create(uri)); } + /** + * 根据请求地址 请求方法 + * @param uri 请求地址 + * @param method 请求方法 + */ public ClientHttpRequest(String uri, MethodType method) { this.setURI(URI.create(uri)); this.method = method; } - + /** + * 根据请求地址 请求方法,请求内容对象 + * @param uri 请求地址 + * @param method 请求方法 + * @param request 请求内容 + */ public ClientHttpRequest(String uri, MethodType method, Object request) { - this.setURI(URI.create(uri)); - this.method = method; + this(uri, method); setParameters(request); } + /** + * 设置请求方式 + * + * @param method 请求方式 + * {@link com.egzosn.pay.common.bean.MethodType} 请求方式 + */ public void setMethod(MethodType method) { this.method = method; } + /** + * 获取请求方式 + * @return 请求方式 + */ @Override public String getMethod() { return method.name(); @@ -153,11 +197,11 @@ public class ClientHttpRequest extends HttpEntityEnclosingRequestBase impleme entity.writeTo((OutputStream)t); return t; } catch (InstantiationException e) { - e.printStackTrace(); + throw new PayErrorException(new PayException("InstantiationException", e.getMessage())); } catch (IllegalAccessException e) { - e.printStackTrace(); + throw new PayErrorException(new PayException("IllegalAccessException", e.getMessage())); } - throw new HttpResponseException(statusLine.getStatusCode(), responseType + " 无法进行类型转换"); + } } String charset = "UTF-8"; @@ -174,7 +218,7 @@ public class ClientHttpRequest extends HttpEntityEnclosingRequestBase impleme try { return JSON.parseObject(result, responseType); }catch (JSONException e){ - throw new PayErrorException(new PayException("failure", "类型转化异常,contentType:" + entity.getContentType().getValue(), result)); + throw new PayErrorException(new PayException("failure", String.format("类型转化异常,contentType: %s\n%s", entity.getContentType().getValue(), e.getMessage() ), result)); } } diff --git a/pay-java-common/src/main/java/com/egzosn/pay/common/util/XML.java b/pay-java-common/src/main/java/com/egzosn/pay/common/util/XML.java index c3a565a..56cf186 100644 --- a/pay-java-common/src/main/java/com/egzosn/pay/common/util/XML.java +++ b/pay-java-common/src/main/java/com/egzosn/pay/common/util/XML.java @@ -2,6 +2,8 @@ package com.egzosn.pay.common.util; import com.alibaba.fastjson.JSONObject; +import com.egzosn.pay.common.bean.result.PayException; +import com.egzosn.pay.common.exception.PayErrorException; import org.jdom2.Document; import org.jdom2.Element; import org.jdom2.JDOMException; @@ -38,9 +40,9 @@ public class XML { try (InputStream in = new ByteArrayInputStream(content.getBytes("UTF-8"))){ return (JSONObject) inputStream2Map(in, null); } catch (IOException e) { - e.printStackTrace(); + throw new PayErrorException(new PayException("IOException", e.getMessage())); } - return null; + } @@ -58,9 +60,8 @@ public class XML { try { return (JSONObject)inputStream2Map(in, null); } catch (IOException e) { - e.printStackTrace(); + throw new PayErrorException(new PayException("IOException", e.getMessage())); } - return null; } @@ -95,7 +96,7 @@ public class XML { m.put(k, v); } } catch (JDOMException e) { - e.printStackTrace(); + throw new PayErrorException(new PayException("JDOMException", e.getMessage())); } return m; } diff --git a/pay-java-common/src/main/java/com/egzosn/pay/common/util/sign/SignUtils.java b/pay-java-common/src/main/java/com/egzosn/pay/common/util/sign/SignUtils.java index 6e846b1..6b4da14 100644 --- a/pay-java-common/src/main/java/com/egzosn/pay/common/util/sign/SignUtils.java +++ b/pay-java-common/src/main/java/com/egzosn/pay/common/util/sign/SignUtils.java @@ -39,6 +39,7 @@ public enum SignUtils { * @param characterEncoding 编码格式 * @return 签名结果 */ + @Override public boolean verify(String text, String sign, String key, String characterEncoding) { return com.egzosn.pay.common.util.sign.encrypt.MD5.verify(text, sign, key, characterEncoding); } diff --git a/pay-java-common/src/main/java/com/egzosn/pay/common/util/sign/encrypt/Base64.java b/pay-java-common/src/main/java/com/egzosn/pay/common/util/sign/encrypt/Base64.java index 9222b13..9a07160 100644 --- a/pay-java-common/src/main/java/com/egzosn/pay/common/util/sign/encrypt/Base64.java +++ b/pay-java-common/src/main/java/com/egzosn/pay/common/util/sign/encrypt/Base64.java @@ -1,14 +1,84 @@ - +/* + * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved. + * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms. + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + */ package com.egzosn.pay.common.util.sign.encrypt; -import sun.misc.BASE64Decoder; -import sun.misc.BASE64Encoder; - +import java.io.FilterOutputStream; +import java.io.InputStream; import java.io.IOException; +import java.io.OutputStream; +import java.nio.ByteBuffer; +import java.nio.charset.StandardCharsets; +import java.util.Arrays; +import java.util.Objects; -public final class Base64 { +/** + * This class consists exclusively of static methods for obtaining + * encoders and decoders for the Base64 encoding scheme. The + * implementation of this class supports the following types of Base64 + * as specified in + * RFC 4648 and + * RFC 2045. + * + *
    + *
  • Basic + *

    Uses "The Base64 Alphabet" as specified in Table 1 of + * RFC 4648 and RFC 2045 for encoding and decoding operation. + * The encoder does not add any line feed (line separator) + * character. The decoder rejects data that contains characters + * outside the base64 alphabet.

  • + * + *
  • URL and Filename safe + *

    Uses the "URL and Filename safe Base64 Alphabet" as specified + * in Table 2 of RFC 4648 for encoding and decoding. The + * encoder does not add any line feed (line separator) character. + * The decoder rejects data that contains characters outside the + * base64 alphabet.

  • + * + *
  • MIME + *

    Uses the "The Base64 Alphabet" as specified in Table 1 of + * RFC 2045 for encoding and decoding operation. The encoded output + * must be represented in lines of no more than 76 characters each + * and uses a carriage return {@code '\r'} followed immediately by + * a linefeed {@code '\n'} as the line separator. No line separator + * is added to the end of the encoded output. All line separators + * or other characters not found in the base64 alphabet table are + * ignored in decoding operation.

  • + *
+ * + *

Unless otherwise noted, passing a {@code null} argument to a + * method of this class will cause a {@link java.lang.NullPointerException + * NullPointerException} to be thrown. + * + * @author Xueming Shen + * @since 1.8 + */ +public class Base64 { + + private Base64() {} /** * Encodes hex octects into Base64 * @@ -16,7 +86,7 @@ public final class Base64 { * @return Encoded Base64 array */ public static String encode(byte[] binaryData) { - return new String( new BASE64Encoder().encode(binaryData)); + return Base64.getEncoder().encodeToString(binaryData); } /** @@ -26,14 +96,925 @@ public final class Base64 { * @return Array containind decoded data. */ public static byte[] decode(String encoded) { - - - try { - return new BASE64Decoder().decodeBuffer(encoded); - } catch (IOException e) { - throw new RuntimeException(e.getMessage()); - } - + return Base64.getDecoder().decode(encoded); } + + /** + * Returns a {@link Encoder} that encodes using the + * Basic type base64 encoding scheme. + * + * @return A Base64 encoder. + */ + public static Encoder getEncoder() { + return Encoder.RFC4648; + } + + /** + * Returns a {@link Encoder} that encodes using the + * URL and Filename safe type base64 + * encoding scheme. + * + * @return A Base64 encoder. + */ + public static Encoder getUrlEncoder() { + return Encoder.RFC4648_URLSAFE; + } + + /** + * Returns a {@link Encoder} that encodes using the + * MIME type base64 encoding scheme. + * + * @return A Base64 encoder. + */ + public static Encoder getMimeEncoder() { + return Encoder.RFC2045; + } + + /** + * Returns a {@link Encoder} that encodes using the + * MIME type base64 encoding scheme + * with specified line length and line separators. + * + * @param lineLength + * the length of each output line (rounded down to nearest multiple + * of 4). If {@code lineLength <= 0} the output will not be separated + * in lines + * @param lineSeparator + * the line separator for each output line + * + * @return A Base64 encoder. + * + * @throws IllegalArgumentException if {@code lineSeparator} includes any + * character of "The Base64 Alphabet" as specified in Table 1 of + * RFC 2045. + */ + public static Encoder getMimeEncoder(int lineLength, byte[] lineSeparator) { + Objects.requireNonNull(lineSeparator); + int[] base64 = Decoder.fromBase64; + for (byte b : lineSeparator) { + if (base64[b & 0xff] != -1) + throw new IllegalArgumentException( + "Illegal base64 line separator character 0x" + Integer.toString(b, 16)); + } + if (lineLength <= 0) { + return Encoder.RFC4648; + } + return new Encoder(false, lineSeparator, lineLength >> 2 << 2, true); + } + + /** + * Returns a {@link Decoder} that decodes using the + * Basic type base64 encoding scheme. + * + * @return A Base64 decoder. + */ + public static Decoder getDecoder() { + return Decoder.RFC4648; + } + + /** + * Returns a {@link Decoder} that decodes using the + * URL and Filename safe type base64 + * encoding scheme. + * + * @return A Base64 decoder. + */ + public static Decoder getUrlDecoder() { + return Decoder.RFC4648_URLSAFE; + } + + /** + * Returns a {@link Decoder} that decodes using the + * MIME type base64 decoding scheme. + * + * @return A Base64 decoder. + */ + public static Decoder getMimeDecoder() { + return Decoder.RFC2045; + } + + /** + * This class implements an encoder for encoding byte data using + * the Base64 encoding scheme as specified in RFC 4648 and RFC 2045. + * + *

Instances of {@link Encoder} class are safe for use by + * multiple concurrent threads. + * + *

Unless otherwise noted, passing a {@code null} argument to + * a method of this class will cause a + * {@link java.lang.NullPointerException NullPointerException} to + * be thrown. + * + * @see Decoder + * @since 1.8 + */ + public static class Encoder { + + private final byte[] newline; + private final int linemax; + private final boolean isURL; + private final boolean doPadding; + + private Encoder(boolean isURL, byte[] newline, int linemax, boolean doPadding) { + this.isURL = isURL; + this.newline = newline; + this.linemax = linemax; + this.doPadding = doPadding; + } + + /** + * This array is a lookup table that translates 6-bit positive integer + * index values into their "Base64 Alphabet" equivalents as specified + * in "Table 1: The Base64 Alphabet" of RFC 2045 (and RFC 4648). + */ + private static final char[] toBase64 = { + 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', + 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', + 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', + 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', + '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/' + }; + + /** + * It's the lookup table for "URL and Filename safe Base64" as specified + * in Table 2 of the RFC 4648, with the '+' and '/' changed to '-' and + * '_'. This table is used when BASE64_URL is specified. + */ + private static final char[] toBase64URL = { + 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', + 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', + 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', + 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', + '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '-', '_' + }; + + private static final int MIMELINEMAX = 76; + private static final byte[] CRLF = new byte[] {'\r', '\n'}; + + static final Encoder RFC4648 = new Encoder(false, null, -1, true); + static final Encoder RFC4648_URLSAFE = new Encoder(true, null, -1, true); + static final Encoder RFC2045 = new Encoder(false, CRLF, MIMELINEMAX, true); + + private final int outLength(int srclen) { + int len = 0; + if (doPadding) { + len = 4 * ((srclen + 2) / 3); + } else { + int n = srclen % 3; + len = 4 * (srclen / 3) + (n == 0 ? 0 : n + 1); + } + if (linemax > 0) // line separators + len += (len - 1) / linemax * newline.length; + return len; + } + + /** + * Encodes all bytes from the specified byte array into a newly-allocated + * byte array using the {@link Base64} encoding scheme. The returned byte + * array is of the length of the resulting bytes. + * + * @param src + * the byte array to encode + * @return A newly-allocated byte array containing the resulting + * encoded bytes. + */ + public byte[] encode(byte[] src) { + int len = outLength(src.length); // dst array size + byte[] dst = new byte[len]; + int ret = encode0(src, 0, src.length, dst); + if (ret != dst.length) + return Arrays.copyOf(dst, ret); + return dst; + } + + /** + * Encodes all bytes from the specified byte array using the + * {@link Base64} encoding scheme, writing the resulting bytes to the + * given output byte array, starting at offset 0. + * + *

It is the responsibility of the invoker of this method to make + * sure the output byte array {@code dst} has enough space for encoding + * all bytes from the input byte array. No bytes will be written to the + * output byte array if the output byte array is not big enough. + * + * @param src + * the byte array to encode + * @param dst + * the output byte array + * @return The number of bytes written to the output byte array + * + * @throws IllegalArgumentException if {@code dst} does not have enough + * space for encoding all input bytes. + */ + public int encode(byte[] src, byte[] dst) { + int len = outLength(src.length); // dst array size + if (dst.length < len) + throw new IllegalArgumentException( + "Output byte array is too small for encoding all input bytes"); + return encode0(src, 0, src.length, dst); + } + + /** + * Encodes the specified byte array into a String using the {@link Base64} + * encoding scheme. + * + *

This method first encodes all input bytes into a base64 encoded + * byte array and then constructs a new String by using the encoded byte + * array and the {@link java.nio.charset.StandardCharsets#ISO_8859_1 + * ISO-8859-1} charset. + * + *

In other words, an invocation of this method has exactly the same + * effect as invoking + * {@code new String(encode(src), StandardCharsets.ISO_8859_1)}. + * + * @param src + * the byte array to encode + * @return A String containing the resulting Base64 encoded characters + */ + @SuppressWarnings("deprecation") + public String encodeToString(byte[] src) { + byte[] encoded = encode(src); + return new String(encoded, 0, 0, encoded.length); + } + + /** + * Encodes all remaining bytes from the specified byte buffer into + * a newly-allocated ByteBuffer using the {@link Base64} encoding + * scheme. + * + * Upon return, the source buffer's position will be updated to + * its limit; its limit will not have been changed. The returned + * output buffer's position will be zero and its limit will be the + * number of resulting encoded bytes. + * + * @param buffer + * the source ByteBuffer to encode + * @return A newly-allocated byte buffer containing the encoded bytes. + */ + public ByteBuffer encode(ByteBuffer buffer) { + int len = outLength(buffer.remaining()); + byte[] dst = new byte[len]; + int ret = 0; + if (buffer.hasArray()) { + ret = encode0(buffer.array(), + buffer.arrayOffset() + buffer.position(), + buffer.arrayOffset() + buffer.limit(), + dst); + buffer.position(buffer.limit()); + } else { + byte[] src = new byte[buffer.remaining()]; + buffer.get(src); + ret = encode0(src, 0, src.length, dst); + } + if (ret != dst.length) + dst = Arrays.copyOf(dst, ret); + return ByteBuffer.wrap(dst); + } + + /** + * Wraps an output stream for encoding byte data using the {@link Base64} + * encoding scheme. + * + *

It is recommended to promptly close the returned output stream after + * use, during which it will flush all possible leftover bytes to the underlying + * output stream. Closing the returned output stream will close the underlying + * output stream. + * + * @param os + * the output stream. + * @return the output stream for encoding the byte data into the + * specified Base64 encoded format + */ + public OutputStream wrap(OutputStream os) { + Objects.requireNonNull(os); + return new EncOutputStream(os, isURL ? toBase64URL : toBase64, + newline, linemax, doPadding); + } + + /** + * Returns an encoder instance that encodes equivalently to this one, + * but without adding any padding character at the end of the encoded + * byte data. + * + *

The encoding scheme of this encoder instance is unaffected by + * this invocation. The returned encoder instance should be used for + * non-padding encoding operation. + * + * @return an equivalent encoder that encodes without adding any + * padding character at the end + */ + public Encoder withoutPadding() { + if (!doPadding) + return this; + return new Encoder(isURL, newline, linemax, false); + } + + private int encode0(byte[] src, int off, int end, byte[] dst) { + char[] base64 = isURL ? toBase64URL : toBase64; + int sp = off; + int slen = (end - off) / 3 * 3; + int sl = off + slen; + if (linemax > 0 && slen > linemax / 4 * 3) + slen = linemax / 4 * 3; + int dp = 0; + while (sp < sl) { + int sl0 = Math.min(sp + slen, sl); + for (int sp0 = sp, dp0 = dp ; sp0 < sl0; ) { + int bits = (src[sp0++] & 0xff) << 16 | + (src[sp0++] & 0xff) << 8 | + (src[sp0++] & 0xff); + dst[dp0++] = (byte)base64[(bits >>> 18) & 0x3f]; + dst[dp0++] = (byte)base64[(bits >>> 12) & 0x3f]; + dst[dp0++] = (byte)base64[(bits >>> 6) & 0x3f]; + dst[dp0++] = (byte)base64[bits & 0x3f]; + } + int dlen = (sl0 - sp) / 3 * 4; + dp += dlen; + sp = sl0; + if (dlen == linemax && sp < end) { + for (byte b : newline){ + dst[dp++] = b; + } + } + } + if (sp < end) { // 1 or 2 leftover bytes + int b0 = src[sp++] & 0xff; + dst[dp++] = (byte)base64[b0 >> 2]; + if (sp == end) { + dst[dp++] = (byte)base64[(b0 << 4) & 0x3f]; + if (doPadding) { + dst[dp++] = '='; + dst[dp++] = '='; + } + } else { + int b1 = src[sp++] & 0xff; + dst[dp++] = (byte)base64[(b0 << 4) & 0x3f | (b1 >> 4)]; + dst[dp++] = (byte)base64[(b1 << 2) & 0x3f]; + if (doPadding) { + dst[dp++] = '='; + } + } + } + return dp; + } + } + + /** + * This class implements a decoder for decoding byte data using the + * Base64 encoding scheme as specified in RFC 4648 and RFC 2045. + * + *

The Base64 padding character {@code '='} is accepted and + * interpreted as the end of the encoded byte data, but is not + * required. So if the final unit of the encoded byte data only has + * two or three Base64 characters (without the corresponding padding + * character(s) padded), they are decoded as if followed by padding + * character(s). If there is a padding character present in the + * final unit, the correct number of padding character(s) must be + * present, otherwise {@code IllegalArgumentException} ( + * {@code IOException} when reading from a Base64 stream) is thrown + * during decoding. + * + *

Instances of {@link Decoder} class are safe for use by + * multiple concurrent threads. + * + *

Unless otherwise noted, passing a {@code null} argument to + * a method of this class will cause a + * {@link java.lang.NullPointerException NullPointerException} to + * be thrown. + * + * @see Encoder + * @since 1.8 + */ + public static class Decoder { + + private final boolean isURL; + private final boolean isMIME; + + private Decoder(boolean isURL, boolean isMIME) { + this.isURL = isURL; + this.isMIME = isMIME; + } + + /** + * Lookup table for decoding unicode characters drawn from the + * "Base64 Alphabet" (as specified in Table 1 of RFC 2045) into + * their 6-bit positive integer equivalents. Characters that + * are not in the Base64 alphabet but fall within the bounds of + * the array are encoded to -1. + * + */ + private static final int[] fromBase64 = new int[256]; + static { + Arrays.fill(fromBase64, -1); + for (int i = 0; i < Encoder.toBase64.length; i++) + fromBase64[Encoder.toBase64[i]] = i; + fromBase64['='] = -2; + } + + /** + * Lookup table for decoding "URL and Filename safe Base64 Alphabet" + * as specified in Table2 of the RFC 4648. + */ + private static final int[] fromBase64URL = new int[256]; + + static { + Arrays.fill(fromBase64URL, -1); + for (int i = 0; i < Encoder.toBase64URL.length; i++) + fromBase64URL[Encoder.toBase64URL[i]] = i; + fromBase64URL['='] = -2; + } + + static final Decoder RFC4648 = new Decoder(false, false); + static final Decoder RFC4648_URLSAFE = new Decoder(true, false); + static final Decoder RFC2045 = new Decoder(false, true); + + /** + * Decodes all bytes from the input byte array using the {@link Base64} + * encoding scheme, writing the results into a newly-allocated output + * byte array. The returned byte array is of the length of the resulting + * bytes. + * + * @param src + * the byte array to decode + * + * @return A newly-allocated byte array containing the decoded bytes. + * + * @throws IllegalArgumentException + * if {@code src} is not in valid Base64 scheme + */ + public byte[] decode(byte[] src) { + byte[] dst = new byte[outLength(src, 0, src.length)]; + int ret = decode0(src, 0, src.length, dst); + if (ret != dst.length) { + dst = Arrays.copyOf(dst, ret); + } + return dst; + } + + /** + * Decodes a Base64 encoded String into a newly-allocated byte array + * using the {@link Base64} encoding scheme. + * + *

An invocation of this method has exactly the same effect as invoking + * {@code decode(src.getBytes(StandardCharsets.ISO_8859_1))} + * + * @param src + * the string to decode + * + * @return A newly-allocated byte array containing the decoded bytes. + * + * @throws IllegalArgumentException + * if {@code src} is not in valid Base64 scheme + */ + public byte[] decode(String src) { + return decode(src.getBytes(StandardCharsets.ISO_8859_1)); + } + + /** + * Decodes all bytes from the input byte array using the {@link Base64} + * encoding scheme, writing the results into the given output byte array, + * starting at offset 0. + * + *

It is the responsibility of the invoker of this method to make + * sure the output byte array {@code dst} has enough space for decoding + * all bytes from the input byte array. No bytes will be be written to + * the output byte array if the output byte array is not big enough. + * + *

If the input byte array is not in valid Base64 encoding scheme + * then some bytes may have been written to the output byte array before + * IllegalargumentException is thrown. + * + * @param src + * the byte array to decode + * @param dst + * the output byte array + * + * @return The number of bytes written to the output byte array + * + * @throws IllegalArgumentException + * if {@code src} is not in valid Base64 scheme, or {@code dst} + * does not have enough space for decoding all input bytes. + */ + public int decode(byte[] src, byte[] dst) { + int len = outLength(src, 0, src.length); + if (dst.length < len) + throw new IllegalArgumentException( + "Output byte array is too small for decoding all input bytes"); + return decode0(src, 0, src.length, dst); + } + + /** + * Decodes all bytes from the input byte buffer using the {@link Base64} + * encoding scheme, writing the results into a newly-allocated ByteBuffer. + * + *

Upon return, the source buffer's position will be updated to + * its limit; its limit will not have been changed. The returned + * output buffer's position will be zero and its limit will be the + * number of resulting decoded bytes + * + *

{@code IllegalArgumentException} is thrown if the input buffer + * is not in valid Base64 encoding scheme. The position of the input + * buffer will not be advanced in this case. + * + * @param buffer + * the ByteBuffer to decode + * + * @return A newly-allocated byte buffer containing the decoded bytes + * + * @throws IllegalArgumentException + * if {@code src} is not in valid Base64 scheme. + */ + public ByteBuffer decode(ByteBuffer buffer) { + int pos0 = buffer.position(); + try { + byte[] src; + int sp, sl; + if (buffer.hasArray()) { + src = buffer.array(); + sp = buffer.arrayOffset() + buffer.position(); + sl = buffer.arrayOffset() + buffer.limit(); + buffer.position(buffer.limit()); + } else { + src = new byte[buffer.remaining()]; + buffer.get(src); + sp = 0; + sl = src.length; + } + byte[] dst = new byte[outLength(src, sp, sl)]; + return ByteBuffer.wrap(dst, 0, decode0(src, sp, sl, dst)); + } catch (IllegalArgumentException iae) { + buffer.position(pos0); + throw iae; + } + } + + /** + * Returns an input stream for decoding {@link Base64} encoded byte stream. + * + *

The {@code read} methods of the returned {@code InputStream} will + * throw {@code IOException} when reading bytes that cannot be decoded. + * + *

Closing the returned input stream will close the underlying + * input stream. + * + * @param is + * the input stream + * + * @return the input stream for decoding the specified Base64 encoded + * byte stream + */ + public InputStream wrap(InputStream is) { + Objects.requireNonNull(is); + return new DecInputStream(is, isURL ? fromBase64URL : fromBase64, isMIME); + } + + private int outLength(byte[] src, int sp, int sl) { + int[] base64 = isURL ? fromBase64URL : fromBase64; + int paddings = 0; + int len = sl - sp; + if (len == 0) + return 0; + if (len < 2) { + if (isMIME && base64[0] == -1) + return 0; + throw new IllegalArgumentException( + "Input byte[] should at least have 2 bytes for base64 bytes"); + } + if (isMIME) { + // scan all bytes to fill out all non-alphabet. a performance + // trade-off of pre-scan or Arrays.copyOf + int n = 0; + while (sp < sl) { + int b = src[sp++] & 0xff; + if (b == '=') { + len -= (sl - sp + 1); + break; + } + if ((b = base64[b]) == -1) + n++; + } + len -= n; + } else { + if (src[sl - 1] == '=') { + paddings++; + if (src[sl - 2] == '=') + paddings++; + } + } + if (paddings == 0 && (len & 0x3) != 0) + paddings = 4 - (len & 0x3); + return 3 * ((len + 3) / 4) - paddings; + } + + private int decode0(byte[] src, int sp, int sl, byte[] dst) { + int[] base64 = isURL ? fromBase64URL : fromBase64; + int dp = 0; + int bits = 0; + int shiftto = 18; // pos of first byte of 4-byte atom + while (sp < sl) { + int b = src[sp++] & 0xff; + if ((b = base64[b]) < 0) { + if (b == -2) { // padding byte '=' + // = shiftto==18 unnecessary padding + // x= shiftto==12 a dangling single x + // x to be handled together with non-padding case + // xx= shiftto==6&&sp==sl missing last = + // xx=y shiftto==6 last is not = + if (shiftto == 6 && (sp == sl || src[sp++] != '=') || + shiftto == 18) { + throw new IllegalArgumentException( + "Input byte array has wrong 4-byte ending unit"); + } + break; + } + if (isMIME) // skip if for rfc2045 + continue; + else + throw new IllegalArgumentException( + "Illegal base64 character " + + Integer.toString(src[sp - 1], 16)); + } + bits |= (b << shiftto); + shiftto -= 6; + if (shiftto < 0) { + dst[dp++] = (byte)(bits >> 16); + dst[dp++] = (byte)(bits >> 8); + dst[dp++] = (byte)(bits); + shiftto = 18; + bits = 0; + } + } + // reached end of byte array or hit padding '=' characters. + if (shiftto == 6) { + dst[dp++] = (byte)(bits >> 16); + } else if (shiftto == 0) { + dst[dp++] = (byte)(bits >> 16); + dst[dp++] = (byte)(bits >> 8); + } else if (shiftto == 12) { + // dangling single "x", incorrectly encoded. + throw new IllegalArgumentException( + "Last unit does not have enough valid bits"); + } + // anything left is invalid, if is not MIME. + // if MIME, ignore all non-base64 character + while (sp < sl) { + if (isMIME && base64[src[sp++]] < 0) + continue; + throw new IllegalArgumentException( + "Input byte array has incorrect ending byte at " + sp); + } + return dp; + } + } + + /* + * An output stream for encoding bytes into the Base64. + */ + private static class EncOutputStream extends FilterOutputStream { + + private int leftover = 0; + private int b0, b1, b2; + private boolean closed = false; + + private final char[] base64; // byte->base64 mapping + private final byte[] newline; // line separator, if needed + private final int linemax; + private final boolean doPadding;// whether or not to pad + private int linepos = 0; + + EncOutputStream(OutputStream os, char[] base64, + byte[] newline, int linemax, boolean doPadding) { + super(os); + this.base64 = base64; + this.newline = newline; + this.linemax = linemax; + this.doPadding = doPadding; + } + + @Override + public void write(int b) throws IOException { + byte[] buf = new byte[1]; + buf[0] = (byte)(b & 0xff); + write(buf, 0, 1); + } + + private void checkNewline() throws IOException { + if (linepos == linemax) { + out.write(newline); + linepos = 0; + } + } + + @Override + public void write(byte[] b, int off, int len) throws IOException { + if (closed) + throw new IOException("Stream is closed"); + if (off < 0 || len < 0 || off + len > b.length) + throw new ArrayIndexOutOfBoundsException(); + if (len == 0) + return; + if (leftover != 0) { + if (leftover == 1) { + b1 = b[off++] & 0xff; + len--; + if (len == 0) { + leftover++; + return; + } + } + b2 = b[off++] & 0xff; + len--; + checkNewline(); + out.write(base64[b0 >> 2]); + out.write(base64[(b0 << 4) & 0x3f | (b1 >> 4)]); + out.write(base64[(b1 << 2) & 0x3f | (b2 >> 6)]); + out.write(base64[b2 & 0x3f]); + linepos += 4; + } + int nBits24 = len / 3; + leftover = len - (nBits24 * 3); + while (nBits24-- > 0) { + checkNewline(); + int bits = (b[off++] & 0xff) << 16 | + (b[off++] & 0xff) << 8 | + (b[off++] & 0xff); + out.write(base64[(bits >>> 18) & 0x3f]); + out.write(base64[(bits >>> 12) & 0x3f]); + out.write(base64[(bits >>> 6) & 0x3f]); + out.write(base64[bits & 0x3f]); + linepos += 4; + } + if (leftover == 1) { + b0 = b[off++] & 0xff; + } else if (leftover == 2) { + b0 = b[off++] & 0xff; + b1 = b[off++] & 0xff; + } + } + + @Override + public void close() throws IOException { + if (!closed) { + closed = true; + if (leftover == 1) { + checkNewline(); + out.write(base64[b0 >> 2]); + out.write(base64[(b0 << 4) & 0x3f]); + if (doPadding) { + out.write('='); + out.write('='); + } + } else if (leftover == 2) { + checkNewline(); + out.write(base64[b0 >> 2]); + out.write(base64[(b0 << 4) & 0x3f | (b1 >> 4)]); + out.write(base64[(b1 << 2) & 0x3f]); + if (doPadding) { + out.write('='); + } + } + leftover = 0; + out.close(); + } + } + } + + /* + * An input stream for decoding Base64 bytes + */ + private static class DecInputStream extends InputStream { + + private final InputStream is; + private final boolean isMIME; + private final int[] base64; // base64 -> byte mapping + private int bits = 0; // 24-bit buffer for decoding + private int nextin = 18; // next available "off" in "bits" for input; + // -> 18, 12, 6, 0 + private int nextout = -8; // next available "off" in "bits" for output; + // -> 8, 0, -8 (no byte for output) + private boolean eof = false; + private boolean closed = false; + + DecInputStream(InputStream is, int[] base64, boolean isMIME) { + this.is = is; + this.base64 = base64; + this.isMIME = isMIME; + } + + private byte[] sbBuf = new byte[1]; + + @Override + public int read() throws IOException { + return read(sbBuf, 0, 1) == -1 ? -1 : sbBuf[0] & 0xff; + } + + @Override + public int read(byte[] b, int off, int len) throws IOException { + if (closed) + throw new IOException("Stream is closed"); + if (eof && nextout < 0) // eof and no leftover + return -1; + if (off < 0 || len < 0 || len > b.length - off) + throw new IndexOutOfBoundsException(); + int oldOff = off; + if (nextout >= 0) { // leftover output byte(s) in bits buf + do { + if (len == 0) + return off - oldOff; + b[off++] = (byte)(bits >> nextout); + len--; + nextout -= 8; + } while (nextout >= 0); + bits = 0; + } + while (len > 0) { + int v = is.read(); + if (v == -1) { + eof = true; + if (nextin != 18) { + if (nextin == 12) + throw new IOException("Base64 stream has one un-decoded dangling byte."); + // treat ending xx/xxx without padding character legal. + // same logic as v == '=' below + b[off++] = (byte)(bits >> (16)); + len--; + if (nextin == 0) { // only one padding byte + if (len == 0) { // no enough output space + bits >>= 8; // shift to lowest byte + nextout = 0; + } else { + b[off++] = (byte) (bits >> 8); + } + } + } + if (off == oldOff) + return -1; + else + return off - oldOff; + } + if (v == '=') { // padding byte(s) + // = shiftto==18 unnecessary padding + // x= shiftto==12 dangling x, invalid unit + // xx= shiftto==6 && missing last '=' + // xx=y or last is not '=' + if (nextin == 18 || nextin == 12 || + nextin == 6 && is.read() != '=') { + throw new IOException("Illegal base64 ending sequence:" + nextin); + } + b[off++] = (byte)(bits >> (16)); + len--; + if (nextin == 0) { // only one padding byte + if (len == 0) { // no enough output space + bits >>= 8; // shift to lowest byte + nextout = 0; + } else { + b[off++] = (byte) (bits >> 8); + } + } + eof = true; + break; + } + if ((v = base64[v]) == -1) { + if (isMIME) // skip if for rfc2045 + continue; + else + throw new IOException("Illegal base64 character " + + Integer.toString(v, 16)); + } + bits |= (v << nextin); + if (nextin == 0) { + nextin = 18; // clear for next + nextout = 16; + while (nextout >= 0) { + b[off++] = (byte)(bits >> nextout); + len--; + nextout -= 8; + if (len == 0 && nextout >= 0) { // don't clean "bits" + return off - oldOff; + } + } + bits = 0; + } else { + nextin -= 6; + } + } + return off - oldOff; + } + + @Override + public int available() throws IOException { + if (closed) + throw new IOException("Stream is closed"); + return is.available(); // TBD: + } + + @Override + public void close() throws IOException { + if (!closed) { + closed = true; + is.close(); + } + } + } } diff --git a/pay-java-demo/README.md b/pay-java-demo/README.md index 6b3cb84..fe23432 100644 --- a/pay-java-demo/README.md +++ b/pay-java-demo/README.md @@ -1,6 +1,6 @@ ### 快速入门 -#####1.支付整合配置 +##### 1.支付整合配置 ```java @@ -220,7 +220,7 @@ public class PayResponse { ``` -#####2.支付处理器与拦截器简单实现 +##### 2.支付处理器与拦截器简单实现 ```java /** @@ -271,7 +271,7 @@ public class PayResponse { ``` -#####3.支付响应PayResponse的获取 +##### 3.支付响应PayResponse的获取 ```java @@ -318,7 +318,7 @@ public class ApyAccountService { ``` -#####4.根据账户id与业务id,组拼订单信息(支付宝、微信支付订单)获取支付信息所需的数据 +##### 4.根据账户id与业务id,组拼订单信息(支付宝、微信支付订单)获取支付信息所需的数据 ```java @@ -466,7 +466,7 @@ public class ApyAccountService { ``` -#####5.支付回调 +##### 5.支付回调 ```java diff --git a/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/PayController.java b/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/PayController.java index bc9e1f8..0a10ba9 100644 --- a/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/PayController.java +++ b/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/PayController.java @@ -20,6 +20,7 @@ import com.egzosn.pay.wx.bean.WxTransactionType; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.servlet.ModelAndView; import javax.annotation.Resource; import javax.imageio.ImageIO; @@ -47,6 +48,10 @@ public class PayController { @Resource private ApyAccountService service; + @RequestMapping("/") + public ModelAndView index(){ + return new ModelAndView("/index.html"); + } /** * 这里模拟账户信息增加 @@ -76,11 +81,19 @@ public class PayController { * @return 跳到支付页面 */ @RequestMapping(value = "toPay.html", produces = "text/html;charset=UTF-8") - public String toPay(Integer payId, String transactionType, String bankType, BigDecimal price) { + public String toPay(HttpServletRequest request,Integer payId, String transactionType, String bankType, BigDecimal price) { //获取对应的支付账户操作工具(可根据账户id) PayResponse payResponse = service.getPayResponse(payId); PayOrder order = new PayOrder("订单title", "摘要", null == price ? new BigDecimal(0.01) : price, UUID.randomUUID().toString().replace("-", ""), PayType.valueOf(payResponse.getStorage().getPayType()).getTransactionType(transactionType)); + // ------ 微信H5使用---- + order.setSpbillCreateIp(request.getHeader("X-Real-IP")); + StringBuffer requestURL = request.getRequestURL(); + //设置网页地址 + order.setWapUrl(requestURL.substring(0, requestURL.indexOf("/") > 0 ? requestURL.indexOf("/") : requestURL.length() )); + //设置网页名称 + order.setWapName("在线充值"); + // ------ 微信H5使用---- //此处只有刷卡支付(银行卡支付)时需要 if (StringUtils.isNotEmpty(bankType)) { @@ -90,6 +103,28 @@ public class PayController { return payResponse.getService().buildRequest(orderInfo, MethodType.POST); } + /** + * 跳到支付页面 + * 针对实时支付,即时付款 + * @return 跳到支付页面 + */ + @RequestMapping(value = "toWxPay.html", produces = "text/html;charset=UTF-8") + public String toWxPay(HttpServletRequest request) { + //获取对应的支付账户操作工具(可根据账户id) + PayResponse payResponse = service.getPayResponse(2); + + PayOrder order = new PayOrder("订单title", "摘要", new BigDecimal(0.01) , UUID.randomUUID().toString().replace("-", ""), WxTransactionType.MWEB); + order.setSpbillCreateIp(request.getHeader("X-Real-IP")); + StringBuffer requestURL = request.getRequestURL(); + //设置网页地址 + order.setWapUrl(requestURL.substring(0, requestURL.indexOf("/") > 0 ? requestURL.indexOf("/") : requestURL.length() )); + //设置网页名称 + order.setWapName("在线充值"); + + Map orderInfo = payResponse.getService().orderInfo(order); + return payResponse.getService().buildRequest(orderInfo, MethodType.POST); + } + /** * 公众号支付 @@ -98,7 +133,7 @@ public class PayController { * @param payId 账户id * @param openid openid * @param price 金额 - * @return 跳到支付页面 + * @return 返回jsapi所需参数 */ @RequestMapping(value = "jsapi" ) public Map toPay(Integer payId, String openid, BigDecimal price) { @@ -133,7 +168,7 @@ public class PayController { order.setAuthCode(authCode); //支付结果 Map params = payResponse.getService().microPay(order); - PayConfigStorage storage = payResponse.getStorage(); + PayConfigStorage storage = payResponse.getService().getPayConfigStorage(); //校验 if (payResponse.getService().verify(params)) { PayMessage message = new PayMessage(params, storage.getPayType(), storage.getMsgType().name()); diff --git a/pay-java-demo/src/main/java/com/egzosn/pay/demo/dao/ApyAccountRepository.java b/pay-java-demo/src/main/java/com/egzosn/pay/demo/dao/ApyAccountRepository.java index e535a56..4fa8ae2 100644 --- a/pay-java-demo/src/main/java/com/egzosn/pay/demo/dao/ApyAccountRepository.java +++ b/pay-java-demo/src/main/java/com/egzosn/pay/demo/dao/ApyAccountRepository.java @@ -48,21 +48,21 @@ public class ApyAccountRepository { ApyAccount apyAccount2 = new ApyAccount(); apyAccount2.setPayId(2); - apyAccount2.setPartner("2088102169916436"); - apyAccount2.setAppid("2016080400165436"); + apyAccount2.setPartner("1469188802"); + apyAccount2.setAppid("wx3344f4aed352deae"); // TODO 2017/2/9 16:20 author: egan sign_type只有单一key时public_key与private_key相等,比如sign_type=MD5的情况 - apyAccount2.setPublicKey("MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAtIlhP1ju6g7fMIcp5cR+v7bk7RUI+irR3HUm0en39K/UnGByAulGFLZU9//IwIu3xCTfGkRHWV8v9iMJhNmxWqbxwehblTCl4f4uEDz9nECc1QzOesGPx6nMsbAx8+3j/Z2p1OCk+Gszs/nUBEjVY/l8NOQoE5kENyWl0WBTFPPONWm8V02YO8dNx0u29egh0dk17OeS+i5G0F+OY7xWnfjSrOqPYAtoo9ccEQUIbhiWz5X3TqmkzjNL3JojsvrymLym97z0COYfl1RlP8tqYhNKLM0FmVndRfcO9HgTx1mO4tj2hsDZN/AnAb3vULaMlESxIg+i6yUBpEOiwj+zLQIDAQAB"); - apyAccount2.setPrivateKey("MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCKA8XdHGExkLnb2Z3mF+6HQUorznXT0XE0GDiRtR+bIM/lE1o6UvAi2vSL5VT1Ob2+yzaSB2zDbfZJrzWcWveU8emN/xVd/yodUbzWnSQkI0k7suLeZDDCcxjT3g5JFSbcWmUec+OFt4n3Ymvrz0rriXfMV4MYlAmru+2UpiyA18SPXUiWWtHXhM4mJ5qAK6Xy2Xpg8fbqcbBLnV5GMipzno2VU0K1WKSe0qhOjPN7vvyZcRXRo5HGd76/Dhc/LhWgM/Ff2oaqQWhSo0tj33/2xmOLxueZaJw5Tx6sBxaBpIaX1huASmeFNgRNb1hSUwbVqkJKuvyEMQvTcz+9wqd9AgMBAAECggEAbtec33ndbUtImZLqx1LNFP+QrCICa324JxiUxiDrI1F05gtm2ZSmm7ex03D5jIE1LLhUOW+RIFt+A6udqNc+I/ctlgcZiAZYMnfk65AE6KkWNcXzgpyAtIpvlCiw3G0KasqGj7CPOGMxgOcguaAgEY60Ef7uoJy0L6KcqlfTQh3X26lHSyduTT/eFQmGstNnfZSnoSgdWoGYJcYwxeI/FZM/AF+qQxqurrB/MWihA+41dko2s+pG/gsnv3JtPiU7uxmzIhkKKo34bNgne6YtLibzamSe2RQNnqRykasrKEibtrUtFoyyzHRhUEjhW4la9je0H3weeyEIqYYrwq3kmQKBgQDUpKh2rmj54ovN7YAk0RkFj495od7IKN4EXRfjeiZHVrsJ97LNzYh2ldh8LRuHOGNP1jI5lBA6UL+VdjObtAiP0a3i5mLgREQ6vU4aQVEgVXiFTiFpgiwURHk3uX7S7DiOolvChmO9UTR91vaATMsTo4/uBLAkC0tQhxLrP/LAIwKBgQCmJ7tBQ9PgKeZCVcfEJQTSUwYeQD+TozjmkBCyn4S6U3pntZAj9XTUVKRiu1pvRCZZ8xjSHQ+xut8wcO4cBCUxSslDWD84HAAj5Aae80Ws7vBq6gS9G3ko1kVxLOOfA/74rB49BUEOcx9P6E2MWVSYhvtzyPvYi0PvLDQGRx2j3wKBgCRzPTVq3C/Jd7GK/qZQ+XsMismMx8WDy9rvokKqE5my8kjZHttMWhIJyZwvl0Jslgl/bAiWqtl0MgMKyfnsuHL+vFHisBxFV3TCMnspqyBhxlEDfZK6b5fhzO2SbHz3ZRJ+HkCQDNTM8LSJfqOrhjwNk4R3ZUIodXaOUN3mjlQFAoGASUJ7betaIBxZSiZITlOELLgummf5oD73d7FNq3RqMT1dWxzS0QgI2xX25RF2bli+ECr/ZqUpplOe1Nz2H6Q0QeeXlfny5epypWCFCtB4iCdSGdHVBQx3/2l6dMw2EIbShRJewsjuRlC8HZ9vkdJRWm8pr4OOh4vgCDSVO69fgkECgYEA0sjX815xMCwO1PDf+7/DNfJ5a2bYdMkVRe6y6a/Dzf83bJDdYZsOKlmSim49kPKEYU9MAT6Mst0Et9GpbzYDIZ4wj6aL01ckYvI42nhn2iylzYz+rOsg8mircFVcwiOh27BoNm8xpMFNzaY743gn37B8n3PaFboXWMO96y2rl8k="); - apyAccount2.setNotifyUrl("http://pay.egan.in/payBack1.json"); + apyAccount2.setPublicKey("991ded0800933ccf8736ab4f7fc61095"); + apyAccount2.setPrivateKey("991ded0800933ccf8736ab4f7fc61095"); + apyAccount2.setNotifyUrl("http://pay.egzosn.com/payBack2.json"); // 无需同步回调可不填 - apyAccount2.setReturnUrl("http://192.168.1.58:9096/payBack2.json"); + apyAccount2.setReturnUrl("http://pay.egzosn.com"); apyAccount2.setInputCharset("UTF-8"); - apyAccount2.setSeller("2088102169916436"); - apyAccount2.setSignType(SignUtils.RSA2.name()); - apyAccount2.setPayType(PayType.aliPay); - apyAccount2.setMsgType(MsgType.text); + apyAccount2.setSeller("1469188802"); + apyAccount2.setSignType(SignUtils.MD5.name()); + apyAccount2.setPayType(PayType.wxPay); + apyAccount2.setMsgType(MsgType.xml); //设置测试环境 - apyAccount2.setTest(true); + apyAccount2.setTest(false); apyAccounts.put(apyAccount2.getPayId(), apyAccount2); ApyAccount apyAccount3 = new ApyAccount(); diff --git a/pay-java-demo/src/main/java/com/egzosn/pay/demo/entity/PayType.java b/pay-java-demo/src/main/java/com/egzosn/pay/demo/entity/PayType.java index 5a5175d..cf2f078 100644 --- a/pay-java-demo/src/main/java/com/egzosn/pay/demo/entity/PayType.java +++ b/pay-java-demo/src/main/java/com/egzosn/pay/demo/entity/PayType.java @@ -42,7 +42,7 @@ public enum PayType implements BasePayType { AliPayConfigStorage aliPayConfigStorage = new AliPayConfigStorage(); aliPayConfigStorage.setPid(apyAccount.getPartner()); aliPayConfigStorage.setAppId(apyAccount.getAppid()); - aliPayConfigStorage.setAliPublicKey(apyAccount.getPublicKey()); + aliPayConfigStorage.setKeyPublic(apyAccount.getPublicKey()); aliPayConfigStorage.setKeyPrivate(apyAccount.getPrivateKey()); aliPayConfigStorage.setNotifyUrl(apyAccount.getNotifyUrl()); aliPayConfigStorage.setReturnUrl(apyAccount.getReturnUrl()); @@ -73,6 +73,7 @@ public enum PayType implements BasePayType { wxPayConfigStorage.setAppid(apyAccount.getAppid()); wxPayConfigStorage.setKeyPrivate(apyAccount.getPrivateKey()); wxPayConfigStorage.setNotifyUrl(apyAccount.getNotifyUrl()); + wxPayConfigStorage.setReturnUrl(apyAccount.getReturnUrl()); wxPayConfigStorage.setSignType(apyAccount.getSignType()); wxPayConfigStorage.setPayType(apyAccount.getPayType().toString()); wxPayConfigStorage.setMsgType(apyAccount.getMsgType()); diff --git a/pay-java-demo/src/main/java/com/egzosn/pay/demo/service/ApyAccountService.java b/pay-java-demo/src/main/java/com/egzosn/pay/demo/service/ApyAccountService.java index 7c497c1..496d52c 100644 --- a/pay-java-demo/src/main/java/com/egzosn/pay/demo/service/ApyAccountService.java +++ b/pay-java-demo/src/main/java/com/egzosn/pay/demo/service/ApyAccountService.java @@ -23,7 +23,9 @@ public class ApyAccountService { @Resource private AutowireCapableBeanFactory spring; - //缓存 + /** + * 缓存 + */ private final static Map payResponses = new HashMap(); /** diff --git a/pay-java-demo/src/main/java/com/egzosn/pay/demo/service/PayResponse.java b/pay-java-demo/src/main/java/com/egzosn/pay/demo/service/PayResponse.java index a47c66b..d422fcb 100644 --- a/pay-java-demo/src/main/java/com/egzosn/pay/demo/service/PayResponse.java +++ b/pay-java-demo/src/main/java/com/egzosn/pay/demo/service/PayResponse.java @@ -85,27 +85,27 @@ public class PayResponse { router = new PayMessageRouter(this.service); router .rule() - .async(false) - .msgType(MsgType.text.name()) //消息类型 - .payType(PayType.aliPay.name()) //支付账户事件类型 - .interceptor(new AliPayMessageInterceptor()) //拦截器 - .handler(autowire(new AliPayMessageHandler(payId))) //处理器 + //消息类型 + .msgType(MsgType.text.name()) + //支付账户事件类型 + .payType(PayType.aliPay.name()) + //拦截器 + .interceptor(new AliPayMessageInterceptor()) + //处理器 + .handler(autowire(new AliPayMessageHandler(payId))) .end() .rule() - .async(false) .msgType(MsgType.xml.name()) .payType(PayType.wxPay.name()) .handler(autowire(new WxPayMessageHandler(payId))) .end() .rule() - .async(false) .msgType(MsgType.json.name()) .payType(PayType.youdianPay.name()) .interceptor(new YoudianPayMessageInterceptor()) //拦截器 .handler(autowire(new YouDianPayMessageHandler(payId))) .end() .rule() - .async(false) .msgType(MsgType.xml.name()) .payType(PayType.fuiou.name()) .handler(autowire(new FuiouPayMessageHandler(payId))) diff --git a/pay-java-demo/src/main/java/com/egzosn/pay/demo/service/handler/AliPayMessageHandler.java b/pay-java-demo/src/main/java/com/egzosn/pay/demo/service/handler/AliPayMessageHandler.java index 80ec804..2d09214 100644 --- a/pay-java-demo/src/main/java/com/egzosn/pay/demo/service/handler/AliPayMessageHandler.java +++ b/pay-java-demo/src/main/java/com/egzosn/pay/demo/service/handler/AliPayMessageHandler.java @@ -3,6 +3,7 @@ package com.egzosn.pay.demo.service.handler; import com.egzosn.pay.common.api.PayService; import com.egzosn.pay.common.bean.PayMessage; import com.egzosn.pay.common.bean.PayOutMessage; +import com.egzosn.pay.common.bean.outbuilder.TextBuilder; import com.egzosn.pay.common.exception.PayErrorException; import java.math.BigDecimal; @@ -42,6 +43,7 @@ public class AliPayMessageHandler extends BasePayMessageHandler { }/* else if ("WAIT_BUYER_PAY".equals(trade_status) || "TRADE_CLOSED".equals(trade_status)) { }*/ + return payService.getPayOutMessage("fail", "失败"); } } diff --git a/pay-java-demo/src/main/webapp/index.html b/pay-java-demo/src/main/webapp/index.html index 4880d1a..35c9b1b 100644 --- a/pay-java-demo/src/main/webapp/index.html +++ b/pay-java-demo/src/main/webapp/index.html @@ -2,7 +2,7 @@ - TEST + Egan 充值 @@ -69,7 +69,8 @@

APP提交(返回对应的json,具体实现,app端demo暂时未实现) - + 账户id
金额 diff --git a/pay-java-fuiou/README.md b/pay-java-fuiou/README.md index ccd1d66..f799eb4 100644 --- a/pay-java-fuiou/README.md +++ b/pay-java-fuiou/README.md @@ -1,8 +1,8 @@ -##富友支付简单例子 +## 富友支付简单例子 -####支付配置 +#### 支付配置 ```java @@ -22,7 +22,7 @@ ``` -####创建支付服务 +#### 创建支付服务 ```java @@ -31,7 +31,7 @@ ``` -####创建支付订单信息 +#### 创建支付订单信息 ```java @@ -41,7 +41,7 @@ ``` -####网页支付 +#### 网页支付 ```java @@ -57,7 +57,7 @@ ``` -####回调处理 +#### 回调处理 ```java diff --git a/pay-java-fuiou/src/main/java/com/egzosn/pay/fuiou/api/FuiouPayConfigStorage.java b/pay-java-fuiou/src/main/java/com/egzosn/pay/fuiou/api/FuiouPayConfigStorage.java index b10e78c..9b350ed 100644 --- a/pay-java-fuiou/src/main/java/com/egzosn/pay/fuiou/api/FuiouPayConfigStorage.java +++ b/pay-java-fuiou/src/main/java/com/egzosn/pay/fuiou/api/FuiouPayConfigStorage.java @@ -21,20 +21,10 @@ public class FuiouPayConfigStorage extends BasePayConfigStorage { return null; } - /** - * 合作商唯一标识 - * - * @see #getPid() 代替者 - */ - @Override - public String getPartner () { - return mchntCd; - } /** * 合作商唯一标识 * - * @see #getPartner() 代替者 */ @Override public String getPid () { diff --git a/pay-java-wx-youdian/README.md b/pay-java-wx-youdian/README.md index ebef4f8..4017d50 100644 --- a/pay-java-wx-youdian/README.md +++ b/pay-java-wx-youdian/README.md @@ -1,8 +1,8 @@ -##友店支付简单例子 +## 友店支付简单例子 -####支付配置 +#### 支付配置 ```java @@ -22,7 +22,7 @@ ``` -####创建支付服务 +#### 创建支付服务 ```java @@ -31,7 +31,7 @@ ``` -####创建支付订单信息 +#### 创建支付订单信息 ```java @@ -41,7 +41,7 @@ ``` -####扫码付 +#### 扫码付 ```java @@ -54,7 +54,7 @@ ``` -####回调处理 +#### 回调处理 ```java diff --git a/pay-java-wx-youdian/src/main/java/com/egzosn/pay/wx/youdian/api/WxYouDianPayConfigStorage.java b/pay-java-wx-youdian/src/main/java/com/egzosn/pay/wx/youdian/api/WxYouDianPayConfigStorage.java index e42d1cb..8523223 100644 --- a/pay-java-wx-youdian/src/main/java/com/egzosn/pay/wx/youdian/api/WxYouDianPayConfigStorage.java +++ b/pay-java-wx-youdian/src/main/java/com/egzosn/pay/wx/youdian/api/WxYouDianPayConfigStorage.java @@ -12,7 +12,9 @@ import com.egzosn.pay.common.api.BasePayConfigStorage; public class WxYouDianPayConfigStorage extends BasePayConfigStorage { - //账号 + /** + * 账号 + */ public volatile String seller; @@ -26,11 +28,6 @@ public class WxYouDianPayConfigStorage extends BasePayConfigStorage { } - @Override - public String getPartner() { - return null; - } - @Override public String getPid() { return null; diff --git a/pay-java-wx-youdian/src/main/java/com/egzosn/pay/wx/youdian/api/WxYouDianPayService.java b/pay-java-wx-youdian/src/main/java/com/egzosn/pay/wx/youdian/api/WxYouDianPayService.java index 39155db..c588d1c 100644 --- a/pay-java-wx-youdian/src/main/java/com/egzosn/pay/wx/youdian/api/WxYouDianPayService.java +++ b/pay-java-wx-youdian/src/main/java/com/egzosn/pay/wx/youdian/api/WxYouDianPayService.java @@ -32,15 +32,23 @@ import java.util.concurrent.locks.Lock; * date 2017/01/12 22:58 */ public class WxYouDianPayService extends BasePayService { - protected final Log log = LogFactory.getLog(WxYouDianPayService.class); - //登录获取授权码 - public final static String loginUrl = "http://life.51youdian.com/Api/CheckoutCounter/login"; - //刷新授权码 - public final static String resetLoginUrl = "http://life.51youdian.com/Api/CheckoutCounter/resetLogin"; - //查看付款订单状态 - public final static String unifiedorderStatusUrl = "http://life.51youdian.com/Api/CheckoutCounter/unifiedorderStatus"; - //预下单链接 - public final static String unifiedOrderUrl = "http://life.51youdian.com/Api/CheckoutCounter/unifiedorder"; + protected static final Log LOG = LogFactory.getLog(WxYouDianPayService.class); + /** + * 登录获取授权码 + */ + public final static String LOGIN_URL = "http://life.51youdian.com/Api/CheckoutCounter/login"; + /** + * 刷新授权码 + */ + public final static String RESET_LOGIN_URL = "http://life.51youdian.com/Api/CheckoutCounter/resetLogin"; + /** + * 查看付款订单状态 + */ + public final static String UNIFIEDORDER_STATUS_URL = "http://life.51youdian.com/Api/CheckoutCounter/unifiedorderStatus"; + /** + * 预下单链接 + */ + public final static String UNIFIED_ORDER_URL = "http://life.51youdian.com/Api/CheckoutCounter/unifiedorder"; /** @@ -51,9 +59,8 @@ public class WxYouDianPayService extends BasePayService { try { return getAccessToken(false); } catch (PayErrorException e) { - e.printStackTrace(); + throw e; } - return null; } /** @@ -80,7 +87,7 @@ public class WxYouDianPayService extends BasePayService { StringBuilder param = new StringBuilder().append("access_token=").append(payConfigStorage.getAccessToken()); String sign = createSign(param.toString() + apbNonce, payConfigStorage.getInputCharset()); param.append("&apb_nonce=").append(apbNonce).append("&sign=").append(sign); - JSONObject json = execute(resetLoginUrl + "?" + param.toString(), MethodType.GET, null ); + JSONObject json = execute(RESET_LOGIN_URL + "?" + param.toString(), MethodType.GET, null ); int errorcode = json.getIntValue("errorcode"); if (0 == errorcode){ payConfigStorage.updateAccessToken(payConfigStorage.getAccessToken(), 7200); @@ -112,7 +119,7 @@ public class WxYouDianPayService extends BasePayService { String sign = createSign(SignUtils.parameterText(data, "") + apbNonce, payConfigStorage.getInputCharset()); String queryParam = SignUtils.parameterText(data) + "&apb_nonce=" + apbNonce + "&sign=" + sign; - JSONObject json = execute(loginUrl + "?" + queryParam, MethodType.GET, null); + JSONObject json = execute(LOGIN_URL + "?" + queryParam, MethodType.GET, null); payConfigStorage.updateAccessToken(json.getString("access_token"), json.getLongValue("viptime")); return json; } @@ -124,7 +131,7 @@ public class WxYouDianPayService extends BasePayService { * @return 请求地址 */ public String getHttpsVerifyUrl() { - return unifiedorderStatusUrl; + return UNIFIEDORDER_STATUS_URL; } /** * 回调校验 @@ -135,15 +142,15 @@ public class WxYouDianPayService extends BasePayService { @Override public boolean verify(Map params) { if (!"SUCCESS".equals(params.get("return_code"))){ - log.debug(String.format("友店微信支付异常:return_code=%s,参数集=%s", params.get("return_code"), params)); + LOG.debug(String.format("友店微信支付异常:return_code=%s,参数集=%s", params.get("return_code"), params)); return false; } - if(params.get("sign") == null) {log.debug("友店微信支付异常:签名为空!out_trade_no=" + params.get("out_trade_no"));} + if(params.get("sign") == null) {LOG.debug("友店微信支付异常:签名为空!out_trade_no=" + params.get("out_trade_no"));} try { return signVerify(params, (String) params.get("sign")) && verifySource((String)params.get("out_trade_no")); } catch (PayErrorException e) { - e.printStackTrace(); + LOG.error(e.getMessage()); } return false; } @@ -217,7 +224,7 @@ public class WxYouDianPayService extends BasePayService { if ("401".equals(error.getErrorCode()) || "500".equals(error.getErrorCode())) { try { int sleepMillis = retrySleepMillis * (1 << retryTimes); - log.debug(String.format("友店微信系统繁忙,(%s)ms 后重试(第%s次)", sleepMillis, retryTimes + 1)); + LOG.debug(String.format("友店微信系统繁忙,(%s)ms 后重试(第%s次)", sleepMillis, retryTimes + 1)); Thread.sleep(sleepMillis); } catch (InterruptedException e1) { throw new PayErrorException(new YdPayError(-1, "友店支付服务端重试失败", e1.getMessage())); @@ -255,14 +262,13 @@ public class WxYouDianPayService extends BasePayService { data.put("PayMoney", data.remove("paymoney")); String params = SignUtils.parameterText(data) + "&apb_nonce=" + apbNonce + "&sign=" + sign; try { - JSONObject json = execute(unifiedOrderUrl+ "?" + params, MethodType.GET, null); + JSONObject json = execute(UNIFIED_ORDER_URL+ "?" + params, MethodType.GET, null); //友店比较特殊,需要在下完预订单后,自己存储 order_sn 对应 微信官方文档 out_trade_no order.setOutTradeNo(json.getString("order_sn")); return json; } catch (PayErrorException e) { - e.printStackTrace(); + throw e; } - return null; } @@ -312,14 +318,16 @@ public class WxYouDianPayService extends BasePayService { /** * 具体需要返回的数据为 - *return_code 返回码只有SUCCESS和FAIL - *return_msg 返回具体信息 - *nonce_str 您的服务器新生成随机生成32位字符串 - *sign 为签名,签名规则是您需要发送的所有数据(除了sign)按照字典升序排列后加上&key=xxxxxxxx您的密钥后md5加密,最后转成小写 - *最后把得到的所有需要返回的数据用json格式化成json对象格式如下 - *{"return_code":"SUCCESS","return_msg":"ok","nonce_str":"dddddddddddddddddddd’,’sign’:’sdddddddddddddddddd"} - * @param code return_code + * return_code 返回码只有SUCCESS和FAIL + * return_msg 返回具体信息 + * nonce_str 您的服务器新生成随机生成32位字符串 + * sign 为签名,签名规则是您需要发送的所有数据(除了sign)按照字典升序排列后加上&key=xxxxxxxx您的密钥后md5加密,最后转成小写 + * 最后把得到的所有需要返回的数据用json格式化成json对象格式如下 + * {"return_code":"SUCCESS","return_msg":"ok","nonce_str":"dddddddddddddddddddd’,’sign’:’sdddddddddddddddddd"} + * + * @param code return_code * @param message return_msg + * * @return 返回输出消息 */ @Override diff --git a/pay-java-wx-youdian/src/main/java/com/egzosn/pay/wx/youdian/bean/YoudianTransactionType.java b/pay-java-wx-youdian/src/main/java/com/egzosn/pay/wx/youdian/bean/YoudianTransactionType.java index a25008d..31fa49a 100644 --- a/pay-java-wx-youdian/src/main/java/com/egzosn/pay/wx/youdian/bean/YoudianTransactionType.java +++ b/pay-java-wx-youdian/src/main/java/com/egzosn/pay/wx/youdian/bean/YoudianTransactionType.java @@ -11,9 +11,13 @@ import com.egzosn.pay.common.bean.TransactionType; */ public enum YoudianTransactionType implements TransactionType { - //扫码付 + /** + * 扫码付 + */ NATIVE("unifiedorder"), - //刷卡付 + /** + * 刷卡付 + */ MICROPAY("micropay");//暂未接触 private String method; diff --git a/pay-java-wx/README.md b/pay-java-wx/README.md index 3d53f39..9dc3e90 100644 --- a/pay-java-wx/README.md +++ b/pay-java-wx/README.md @@ -1,8 +1,8 @@ -##微信支付简单例子 +## 微信支付简单例子 -####支付配置 +#### 支付配置 ```java @@ -45,7 +45,7 @@ ``` -####创建支付服务 +#### 创建支付服务 ```java @@ -57,7 +57,7 @@ ``` -####创建支付订单信息 +#### 创建支付订单信息 ```java @@ -66,7 +66,7 @@ ``` -####扫码付 +#### 扫码付 ```java @@ -80,7 +80,7 @@ ``` -####APP支付 +#### APP支付 ```java @@ -92,7 +92,7 @@ ``` -####网页支付 +#### 网页支付 ```java @@ -107,7 +107,7 @@ ``` -####条码付 刷卡付 +#### 条码付 刷卡付 ```java @@ -121,7 +121,7 @@ ``` -####回调处理 +#### 回调处理 ```java @@ -139,7 +139,7 @@ ``` -####支付订单查询 +#### 支付订单查询 ```java @@ -148,7 +148,7 @@ ``` -####交易关闭接口 +#### 交易关闭接口 ```java Map result = service..query("微信单号", "我方系统单号"); @@ -156,7 +156,7 @@ ``` -####申请退款接口 +#### 申请退款接口 ```java Map result = service.refund("微信单号", "我方系统单号", "退款金额", "订单总金额"); @@ -164,14 +164,14 @@ ``` -####查询退款 +#### 查询退款 ```java Map result = service.refundquery("微信单号", "我方系统单号"); ``` -####下载对账单 +#### 下载对账单 ```java Map result = service.downloadbill("账单时间:日账单格式为yyyy-MM-dd,月账单格式为yyyy-MM", "账单类型"); diff --git a/pay-java-wx/src/main/java/com/egzosn/pay/wx/api/WxPayConfigStorage.java b/pay-java-wx/src/main/java/com/egzosn/pay/wx/api/WxPayConfigStorage.java index a4afd52..54184b5 100644 --- a/pay-java-wx/src/main/java/com/egzosn/pay/wx/api/WxPayConfigStorage.java +++ b/pay-java-wx/src/main/java/com/egzosn/pay/wx/api/WxPayConfigStorage.java @@ -14,17 +14,18 @@ import com.egzosn.pay.common.api.BasePayConfigStorage; public class WxPayConfigStorage extends BasePayConfigStorage { - public String appSecret; - public String appid ; - // 商户号 合作者id - public String mchId; + /** + * 应用id + */ + private String appid ; + /** + * 商户号 合作者id + */ + private String mchId; - public void setAppSecret(String appSecret) { - this.appSecret = appSecret; - } @Override public String getAppid() { @@ -35,17 +36,11 @@ public class WxPayConfigStorage extends BasePayConfigStorage { this.appid = appid; } - /** - * 合作商唯一标识 - * @see #getPid() 代替者 - */ - public String getPartner() { - return mchId; - } + + /** * 合作商唯一标识 - * @see #getPartner() 代替者 */ @Override public String getPid() { @@ -53,6 +48,15 @@ public class WxPayConfigStorage extends BasePayConfigStorage { } + /** + * 合作商唯一标识 + */ + public void setPid(String mchId) { + this.mchId = mchId; + } + + + @Override public String getSeller() { return null; diff --git a/pay-java-wx/src/main/java/com/egzosn/pay/wx/api/WxPayService.java b/pay-java-wx/src/main/java/com/egzosn/pay/wx/api/WxPayService.java index 8264e88..7b9f5c1 100644 --- a/pay-java-wx/src/main/java/com/egzosn/pay/wx/api/WxPayService.java +++ b/pay-java-wx/src/main/java/com/egzosn/pay/wx/api/WxPayService.java @@ -10,12 +10,12 @@ import com.egzosn.pay.common.exception.PayErrorException; import com.egzosn.pay.common.http.HttpConfigStorage; import com.egzosn.pay.common.util.MatrixToImageWriter; import com.egzosn.pay.common.util.sign.SignUtils; +import com.egzosn.pay.common.util.str.StringUtils; import com.egzosn.pay.wx.bean.WxPayError; import com.egzosn.pay.wx.bean.WxTransactionType; import com.egzosn.pay.common.util.XML; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; - import java.awt.image.BufferedImage; import java.io.IOException; import java.io.InputStream; @@ -34,18 +34,29 @@ import java.util.*; * */ public class WxPayService extends BasePayService { - protected final Log log = LogFactory.getLog(WxPayService.class); + protected final Log LOG = LogFactory.getLog(WxPayService.class); - public final static String uri = "https://api.mch.weixin.qq.com/"; + /** + * 微信请求地址 + */ + public final static String URI = "https://api.mch.weixin.qq.com/"; // public final static String unifiedOrderUrl = "https://api.mch.weixin.qq.com/pay/unifiedorder"; // public final static String orderqueryUrl = "https://api.mch.weixin.qq.com/pay/orderquery"; + /** + * 创建支付服务 + * @param payConfigStorage 微信对应的支付配置 + */ public WxPayService(PayConfigStorage payConfigStorage) { super(payConfigStorage); } - + /** + * 创建支付服务 + * @param payConfigStorage 微信对应的支付配置 + * @param configStorage 微信对应的网络配置,包含代理配置、ssl证书配置 + */ public WxPayService(PayConfigStorage payConfigStorage, HttpConfigStorage configStorage) { super(payConfigStorage, configStorage); } @@ -58,7 +69,7 @@ public class WxPayService extends BasePayService { */ private String getUrl(TransactionType transactionType) { - return uri + transactionType.getMethod(); + return URI + transactionType.getMethod(); } /** @@ -69,20 +80,20 @@ public class WxPayService extends BasePayService { */ @Override public boolean verify(Map params) { - if (!"SUCCESS".equals(params.get("return_code"))) { - log.debug(String.format("微信支付异常:return_code=%s,参数集=%s", params.get("return_code"), params)); + if (!"SUCCESS".equals(params.get("return_code"))){ + LOG.debug(String.format("微信支付异常:return_code=%s,参数集=%s", params.get("return_code"), params)); return false; } - if (null == params.get("sign")) { - log.debug("微信支付异常:签名为空!out_trade_no=" + params.get("out_trade_no")); + if(null == params.get("sign")) { + LOG.debug("微信支付异常:签名为空!out_trade_no=" + params.get("out_trade_no")); return false; } try { return signVerify(params, (String) params.get("sign")) && verifySource((String) params.get("out_trade_no")); } catch (PayErrorException e) { - e.printStackTrace(); + LOG.error(e); } return false; } @@ -107,6 +118,7 @@ public class WxPayService extends BasePayService { * @param sign 比对的签名结果 * @return 生成的签名结果 */ + @Override public boolean signVerify(Map params, String sign) { return SignUtils.valueOf(payConfigStorage.getSignType()).verify(params, sign, "&key=" + payConfigStorage.getKeyPublic(), payConfigStorage.getInputCharset()); } @@ -164,17 +176,16 @@ public class WxPayService extends BasePayService { parameters.put("product_id", order.getOutTradeNo()); break; case MWEB: - //h5支付固定传"h5_info" - String sceneInfo = "{\"h5_info\": " + - //场景类型 - " {\"type\": \"Wap\", " + - //WAP网站URL地址 同步回调地址 - " \"wap_url\": \"" + payConfigStorage.getReturnUrl() + "\"," + - //WAP 网站名 - " \"wap_name\": \"支付充值\"}}"; - - parameters.put("scene_info", sceneInfo); + //H5支付专用 + LinkedHashMap value = new LinkedHashMap(); + value.put("type", "Wap"); + value.put("wap_url", order.getWapUrl());////WAP网站URL地址 + value.put("wap_name", order.getWapName());//WAP 网站名 + JSONObject sceneInfo = new JSONObject(); + sceneInfo.put("h5_info", value); + parameters.put("scene_info", sceneInfo.toJSONString()); break; + default: } @@ -182,7 +193,7 @@ public class WxPayService extends BasePayService { parameters.put("sign", sign); String requestXML = XML.getMap2Xml(parameters); - log.debug("requestXML:" + requestXML); + LOG.debug("requestXML:" + requestXML); //调起支付的参数列表 JSONObject result = requestTemplate.postForObject(getUrl(order.getTransactionType()), requestXML, JSONObject.class); @@ -273,9 +284,9 @@ public class WxPayService extends BasePayService { try { return XML.inputStream2Map(is, map); } catch (IOException e) { - e.printStackTrace(); + throw new PayErrorException(new PayException("IOException", e.getMessage())); } - return map; + } /** @@ -554,34 +565,34 @@ public class WxPayService extends BasePayService { * @return 返回支付方对应接口的结果 */ @Override - public T secondaryInterface(Object transactionIdOrBillDate, String outTradeNoBillType, TransactionType transactionType, Callback callback) { + public T secondaryInterface(Object transactionIdOrBillDate, String outTradeNoBillType, TransactionType transactionType, Callback callback) { if (transactionType == WxTransactionType.REFUND) { throw new PayErrorException(new PayException("failure", "通用接口不支持:" + transactionType)); } - if (transactionType == WxTransactionType.DOWNLOADBILL) { - if (transactionIdOrBillDate instanceof Date) { + if (transactionType == WxTransactionType.DOWNLOADBILL){ + if (transactionIdOrBillDate instanceof Date){ return downloadbill((Date) transactionIdOrBillDate, outTradeNoBillType, callback); } throw new PayErrorException(new PayException("failure", "非法类型异常:" + transactionIdOrBillDate.getClass())); } - if (!(transactionIdOrBillDate instanceof String)) { + if (!(null == transactionIdOrBillDate || transactionIdOrBillDate instanceof String)){ throw new PayErrorException(new PayException("failure", "非法类型异常:" + transactionIdOrBillDate.getClass())); } //获取公共参数 - Map parameters = getPublicParameters(); - if (null == transactionIdOrBillDate || "".equals(transactionIdOrBillDate)) { + Map parameters = new HashMap<>();//getPublicParameters(); + if (StringUtils.isEmpty((String)transactionIdOrBillDate)){ parameters.put("out_trade_no", outTradeNoBillType); - } else { + }else { parameters.put("transaction_id", transactionIdOrBillDate); } //设置签名 setSign(parameters); - return callback.perform(requestTemplate.postForObject(getUrl(transactionType), XML.getMap2Xml(parameters), JSONObject.class)); + return callback.perform(requestTemplate.postForObject(getUrl(transactionType), XML.getMap2Xml(parameters) , JSONObject.class)); } diff --git a/pay-java-wx/src/main/java/com/egzosn/pay/wx/bean/WxTransactionType.java b/pay-java-wx/src/main/java/com/egzosn/pay/wx/bean/WxTransactionType.java index 52c770e..cd6ff79 100644 --- a/pay-java-wx/src/main/java/com/egzosn/pay/wx/bean/WxTransactionType.java +++ b/pay-java-wx/src/main/java/com/egzosn/pay/wx/bean/WxTransactionType.java @@ -10,28 +10,47 @@ import com.egzosn.pay.common.bean.TransactionType; * date 2016/10/19 22:58 */ public enum WxTransactionType implements TransactionType { - //公众号支付 + /** + * 公众号支付 + */ JSAPI("pay/unifiedorder"), - //扫码付 + /** + * 扫码付 + */ NATIVE("pay/unifiedorder"), - //移动支付 + /** + * 移动支付 + */ APP("pay/unifiedorder"), + /** + * H5支付 + */ MWEB("pay/unifiedorder"), - //刷卡付,暂未接触 + /** + * 刷卡付 + */ MICROPAY("pay/micropay"), // TODO 2017/3/8 19:14 author: egan 交易辅助接口 - //查询订单 + /** + * 查询订单 + */ QUERY("pay/orderquery"), - //关闭订单 + /** + * 关闭订单 + */ CLOSE("pay/closeorder"), - //申请退款 + /** + * 申请退款 + */ REFUND("secapi/pay/refund"), - //查询退款 + /** + * 查询退款 + */ REFUNDQUERY("pay/refundquery"), - //下载对账单 - DOWNLOADBILL("pay/downloadbill"), - //不知道交易类型,主要用于回调通知,回调后不清楚交易类型,以此定义 - UNAWARE("UNAWARE") + /** + * 下载对账单 + */ + DOWNLOADBILL("pay/downloadbill") ; WxTransactionType(String method) {