From d19037a8569ac9456932e19c2d62ab3983f6672a Mon Sep 17 00:00:00 2001 From: egan Date: Fri, 15 Feb 2019 23:18:08 +0800 Subject: [PATCH 001/299] =?UTF-8?q?=E7=89=88=E6=9C=AC=E5=8F=91=E5=B8=83?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pay-java-ali/pom.xml | 2 +- pay-java-common/pom.xml | 2 +- pay-java-demo/pom.xml | 2 +- pay-java-fuiou/pom.xml | 2 +- pay-java-payoneer/pom.xml | 2 +- pay-java-paypal/pom.xml | 2 +- pay-java-union/pom.xml | 2 +- .../egzosn/pay/union/bean/UnionPayOrder.java | 49 ------------------- pay-java-wx-youdian/pom.xml | 2 +- pay-java-wx/pom.xml | 2 +- pom.xml | 10 ++-- 11 files changed, 14 insertions(+), 63 deletions(-) delete mode 100644 pay-java-union/src/main/java/com/egzosn/pay/union/bean/UnionPayOrder.java diff --git a/pay-java-ali/pom.xml b/pay-java-ali/pom.xml index 9720685..71ecd45 100644 --- a/pay-java-ali/pom.xml +++ b/pay-java-ali/pom.xml @@ -5,7 +5,7 @@ pay-java-parent com.egzosn - 2.12.6-SNAPSHOT + 2.12.6 4.0.0 pay-java-ali diff --git a/pay-java-common/pom.xml b/pay-java-common/pom.xml index 3f34fa4..d5bf681 100644 --- a/pay-java-common/pom.xml +++ b/pay-java-common/pom.xml @@ -5,7 +5,7 @@ pay-java-parent com.egzosn - 2.12.6-SNAPSHOT + 2.12.6 4.0.0 jar diff --git a/pay-java-demo/pom.xml b/pay-java-demo/pom.xml index 568501f..dcb9ed4 100644 --- a/pay-java-demo/pom.xml +++ b/pay-java-demo/pom.xml @@ -5,7 +5,7 @@ pay-java-parent com.egzosn - 2.12.6-SNAPSHOT + 2.12.6 4.0.0 war diff --git a/pay-java-fuiou/pom.xml b/pay-java-fuiou/pom.xml index e46c5ae..6a1dd2d 100644 --- a/pay-java-fuiou/pom.xml +++ b/pay-java-fuiou/pom.xml @@ -5,7 +5,7 @@ pay-java-parent com.egzosn - 2.12.6-SNAPSHOT + 2.12.6 4.0.0 pay-java-fuiou diff --git a/pay-java-payoneer/pom.xml b/pay-java-payoneer/pom.xml index 84b050b..12c760a 100644 --- a/pay-java-payoneer/pom.xml +++ b/pay-java-payoneer/pom.xml @@ -5,7 +5,7 @@ pay-java-parent com.egzosn - 2.12.6-SNAPSHOT + 2.12.6 4.0.0 pay-java-payoneer diff --git a/pay-java-paypal/pom.xml b/pay-java-paypal/pom.xml index 2a2f2b3..c1aa1e0 100644 --- a/pay-java-paypal/pom.xml +++ b/pay-java-paypal/pom.xml @@ -5,7 +5,7 @@ pay-java-parent com.egzosn - 2.12.6-SNAPSHOT + 2.12.6 4.0.0 diff --git a/pay-java-union/pom.xml b/pay-java-union/pom.xml index 919403a..c8476c5 100644 --- a/pay-java-union/pom.xml +++ b/pay-java-union/pom.xml @@ -5,7 +5,7 @@ pay-java-parent com.egzosn - 2.12.6-SNAPSHOT + 2.12.6 4.0.0 diff --git a/pay-java-union/src/main/java/com/egzosn/pay/union/bean/UnionPayOrder.java b/pay-java-union/src/main/java/com/egzosn/pay/union/bean/UnionPayOrder.java deleted file mode 100644 index 153570c..0000000 --- a/pay-java-union/src/main/java/com/egzosn/pay/union/bean/UnionPayOrder.java +++ /dev/null @@ -1,49 +0,0 @@ -package com.egzosn.pay.union.bean; - -import com.egzosn.pay.common.bean.PayOrder; -import com.egzosn.pay.common.bean.TransactionType; - -import java.math.BigDecimal; - -/** - * 银联订单实体 - * @author Actinia - * @create 2019-02-13 23:39 - */ -public class UnionPayOrder extends PayOrder { - - //请求方保留域(透传字段) - private String reqReserved; - //风控信息域 - private String riskRateInfo; - - public UnionPayOrder(String subject, String body, BigDecimal price, String outTradeNo, TransactionType transactionType) { - setSubject(subject); - setBody(body); - setPrice(price); - setOutTradeNo(outTradeNo); - setTransactionType(transactionType); - } - public UnionPayOrder(String subject, String body, BigDecimal price, String outTradeNo) { - setSubject(subject); - setBody(body); - setPrice(price); - setOutTradeNo(outTradeNo); - } - - public String getReqReserved() { - return reqReserved; - } - - public void setReqReserved(String reqReserved) { - this.reqReserved = reqReserved; - } - - public String getRiskRateInfo() { - return riskRateInfo; - } - - public void setRiskRateInfo(String riskRateInfo) { - this.riskRateInfo = riskRateInfo; - } -} diff --git a/pay-java-wx-youdian/pom.xml b/pay-java-wx-youdian/pom.xml index c4862a8..5a41758 100644 --- a/pay-java-wx-youdian/pom.xml +++ b/pay-java-wx-youdian/pom.xml @@ -5,7 +5,7 @@ pay-java-parent com.egzosn - 2.12.6-SNAPSHOT + 2.12.6 4.0.0 pay-java-wx-youdian diff --git a/pay-java-wx/pom.xml b/pay-java-wx/pom.xml index be3ef7f..f8930da 100644 --- a/pay-java-wx/pom.xml +++ b/pay-java-wx/pom.xml @@ -5,7 +5,7 @@ pay-java-parent com.egzosn - 2.12.6-SNAPSHOT + 2.12.6 4.0.0 pay-java-wx diff --git a/pom.xml b/pom.xml index 2c074f1..87e0690 100644 --- a/pom.xml +++ b/pom.xml @@ -7,7 +7,7 @@ com.egzosn pay-java-parent pom - 2.12.6-SNAPSHOT + 2.12.6 Pay Java - Parent Pay Java Parent @@ -51,12 +51,12 @@ pay-java-union pay-java-payoneer pay-java-paypal - pay-java-demo + - 2.12.6-SNAPSHOT + 2.12.6 4.5.4 1.2.17 1.2.41 @@ -126,7 +126,7 @@ utf-8 - + \ No newline at end of file -- Gitee From cce231c96152406a79e4fb62c657d6d276d5486d Mon Sep 17 00:00:00 2001 From: egan Date: Fri, 15 Feb 2019 23:19:24 +0800 Subject: [PATCH 002/299] =?UTF-8?q?=E7=89=88=E6=9C=AC=E5=8F=91=E5=B8=83?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 4 ++-- pom.xml | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index ce607a5..24b8806 100644 --- a/README.md +++ b/README.md @@ -30,7 +30,7 @@ com.egzosn pay-java-common - 2.12.5 + 2.12.6 ``` @@ -42,7 +42,7 @@ com.egzosn {module-name} - 2.12.5 + 2.12.6 ``` diff --git a/pom.xml b/pom.xml index 87e0690..1f8180f 100644 --- a/pom.xml +++ b/pom.xml @@ -51,7 +51,7 @@ pay-java-union pay-java-payoneer pay-java-paypal - + pay-java-demo @@ -126,7 +126,7 @@ utf-8 - + \ No newline at end of file -- Gitee From 55b3a6280b96638c00c0bb9d66b2e2fec1342b96 Mon Sep 17 00:00:00 2001 From: egzosn Date: Fri, 22 Feb 2019 10:17:59 +0800 Subject: [PATCH 003/299] =?UTF-8?q?=E5=BE=AE=E4=BF=A1=E8=BD=AC=E8=B4=A6?= =?UTF-8?q?=E5=88=B0=E4=BD=99=E9=A2=9D=EF=BC=8C=E5=A2=9E=E5=8A=A0=E8=AE=BE?= =?UTF-8?q?=E5=A4=87ip?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pay-java-ali/pom.xml | 2 +- pay-java-common/pom.xml | 2 +- .../com/egzosn/pay/common/bean/TransferOrder.java | 13 +++++++++++++ pay-java-demo/pom.xml | 2 +- pay-java-fuiou/pom.xml | 2 +- pay-java-payoneer/pom.xml | 2 +- pay-java-paypal/pom.xml | 2 +- pay-java-union/pom.xml | 2 +- pay-java-wx-youdian/pom.xml | 2 +- pay-java-wx/pom.xml | 2 +- .../java/com/egzosn/pay/wx/api/WxPayService.java | 2 ++ pom.xml | 4 ++-- 12 files changed, 26 insertions(+), 11 deletions(-) diff --git a/pay-java-ali/pom.xml b/pay-java-ali/pom.xml index 71ecd45..1e4d3b7 100644 --- a/pay-java-ali/pom.xml +++ b/pay-java-ali/pom.xml @@ -5,7 +5,7 @@ pay-java-parent com.egzosn - 2.12.6 + 2.12.7-SNAPSHOT 4.0.0 pay-java-ali diff --git a/pay-java-common/pom.xml b/pay-java-common/pom.xml index d5bf681..8d56620 100644 --- a/pay-java-common/pom.xml +++ b/pay-java-common/pom.xml @@ -5,7 +5,7 @@ pay-java-parent com.egzosn - 2.12.6 + 2.12.7-SNAPSHOT 4.0.0 jar diff --git a/pay-java-common/src/main/java/com/egzosn/pay/common/bean/TransferOrder.java b/pay-java-common/src/main/java/com/egzosn/pay/common/bean/TransferOrder.java index 16ee000..826f4a7 100644 --- a/pay-java-common/src/main/java/com/egzosn/pay/common/bean/TransferOrder.java +++ b/pay-java-common/src/main/java/com/egzosn/pay/common/bean/TransferOrder.java @@ -55,6 +55,11 @@ public class TransferOrder { */ private TransferType transferType; + /** + * 操作者ip,根据支付平台所需进行设置 + */ + private String ip; + public String getOutNo() { return outNo; } @@ -126,4 +131,12 @@ public class TransferOrder { public void setTransferType(TransferType transferType) { this.transferType = transferType; } + + public String getIp() { + return ip; + } + + public void setIp(String ip) { + this.ip = ip; + } } diff --git a/pay-java-demo/pom.xml b/pay-java-demo/pom.xml index dcb9ed4..28b1968 100644 --- a/pay-java-demo/pom.xml +++ b/pay-java-demo/pom.xml @@ -5,7 +5,7 @@ pay-java-parent com.egzosn - 2.12.6 + 2.12.7-SNAPSHOT 4.0.0 war diff --git a/pay-java-fuiou/pom.xml b/pay-java-fuiou/pom.xml index 6a1dd2d..47c8c94 100644 --- a/pay-java-fuiou/pom.xml +++ b/pay-java-fuiou/pom.xml @@ -5,7 +5,7 @@ pay-java-parent com.egzosn - 2.12.6 + 2.12.7-SNAPSHOT 4.0.0 pay-java-fuiou diff --git a/pay-java-payoneer/pom.xml b/pay-java-payoneer/pom.xml index 12c760a..33dbf41 100644 --- a/pay-java-payoneer/pom.xml +++ b/pay-java-payoneer/pom.xml @@ -5,7 +5,7 @@ pay-java-parent com.egzosn - 2.12.6 + 2.12.7-SNAPSHOT 4.0.0 pay-java-payoneer diff --git a/pay-java-paypal/pom.xml b/pay-java-paypal/pom.xml index c1aa1e0..94cfb71 100644 --- a/pay-java-paypal/pom.xml +++ b/pay-java-paypal/pom.xml @@ -5,7 +5,7 @@ pay-java-parent com.egzosn - 2.12.6 + 2.12.7-SNAPSHOT 4.0.0 diff --git a/pay-java-union/pom.xml b/pay-java-union/pom.xml index c8476c5..8a5fe3e 100644 --- a/pay-java-union/pom.xml +++ b/pay-java-union/pom.xml @@ -5,7 +5,7 @@ pay-java-parent com.egzosn - 2.12.6 + 2.12.7-SNAPSHOT 4.0.0 diff --git a/pay-java-wx-youdian/pom.xml b/pay-java-wx-youdian/pom.xml index 5a41758..a116b35 100644 --- a/pay-java-wx-youdian/pom.xml +++ b/pay-java-wx-youdian/pom.xml @@ -5,7 +5,7 @@ pay-java-parent com.egzosn - 2.12.6 + 2.12.7-SNAPSHOT 4.0.0 pay-java-wx-youdian diff --git a/pay-java-wx/pom.xml b/pay-java-wx/pom.xml index f8930da..e7bf24e 100644 --- a/pay-java-wx/pom.xml +++ b/pay-java-wx/pom.xml @@ -5,7 +5,7 @@ pay-java-parent com.egzosn - 2.12.6 + 2.12.7-SNAPSHOT 4.0.0 pay-java-wx 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 a571620..31bed50 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 @@ -610,6 +610,7 @@ public class WxPayService extends BasePayService { if (!StringUtils.isEmpty(order.getRemark())){ parameters.put("desc", order.getRemark()); } + parameters.put("nonce_str", SignUtils.randomStr()); if (null != order.getTransferType() && TRANSFERS == order.getTransferType()){ transfers(parameters, order); @@ -636,6 +637,7 @@ public class WxPayService extends BasePayService { //转账到余额, 申请商户号的appid或商户号绑定的appid parameters.put("mch_appid", payConfigStorage.getAppid()); parameters.put("openid", order.getPayeeAccount()); + parameters.put("spbill_create_ip", StringUtils.isEmpty(order.getIp()) ? "192.168.1.150" : order.getIp()); //默认不校验真实姓名 parameters.put("check_name", "NO_CHECK"); //当存在时候 校验收款用户真实姓名 diff --git a/pom.xml b/pom.xml index 1f8180f..643fa99 100644 --- a/pom.xml +++ b/pom.xml @@ -7,7 +7,7 @@ com.egzosn pay-java-parent pom - 2.12.6 + 2.12.7-SNAPSHOT Pay Java - Parent Pay Java Parent @@ -56,7 +56,7 @@ - 2.12.6 + 2.12.7-SNAPSHOT 4.5.4 1.2.17 1.2.41 -- Gitee From fb47dedb1ad82fd5e5e769dc76fe95ab8b75ffb0 Mon Sep 17 00:00:00 2001 From: egzosn Date: Tue, 12 Mar 2019 12:51:50 +0800 Subject: [PATCH 004/299] =?UTF-8?q?=E4=BC=81=E4=B8=9A=E4=BB=98=E6=AC=BEapi?= =?UTF-8?q?=E8=BD=AC=E8=B4=A6=E5=88=B0=E9=9B=B6=E9=92=B1=20=E5=95=86?= =?UTF-8?q?=E6=88=B7=E5=8F=B7=E5=8F=82=E6=95=B0=E5=90=8D=E5=A4=9A=E4=BA=86?= =?UTF-8?q?=5F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/main/java/com/egzosn/pay/wx/api/WxPayService.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) 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 31bed50..0449f26 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 @@ -604,17 +604,18 @@ public class WxPayService extends BasePayService { public Map transfer(TransferOrder order) { Map parameters = new TreeMap(); - parameters.put("mch_id", payConfigStorage.getPid()); + parameters.put("partner_trade_no", order.getOutNo()); parameters.put("amount", Util.conversionCentAmount(order.getAmount())); if (!StringUtils.isEmpty(order.getRemark())){ parameters.put("desc", order.getRemark()); } - parameters.put("nonce_str", SignUtils.randomStr()); if (null != order.getTransferType() && TRANSFERS == order.getTransferType()){ transfers(parameters, order); + parameters.put("mchid", payConfigStorage.getPid()); }else { + parameters.put("mch_id", payConfigStorage.getPid()); order.setTransferType(WxTransferType.PAY_BANK); payBank(parameters, order); } @@ -637,7 +638,6 @@ public class WxPayService extends BasePayService { //转账到余额, 申请商户号的appid或商户号绑定的appid parameters.put("mch_appid", payConfigStorage.getAppid()); parameters.put("openid", order.getPayeeAccount()); - parameters.put("spbill_create_ip", StringUtils.isEmpty(order.getIp()) ? "192.168.1.150" : order.getIp()); //默认不校验真实姓名 parameters.put("check_name", "NO_CHECK"); //当存在时候 校验收款用户真实姓名 -- Gitee From 017a843145c22bf9413385e18abc2b999dec28d3 Mon Sep 17 00:00:00 2001 From: egzosn Date: Tue, 12 Mar 2019 12:55:13 +0800 Subject: [PATCH 005/299] =?UTF-8?q?=E5=BE=AE=E4=BF=A1=E8=BD=AC=E8=B4=A6?= =?UTF-8?q?=E5=88=B0=E4=BD=99=E9=A2=9D=EF=BC=8C=E5=A2=9E=E5=8A=A0=E8=AE=BE?= =?UTF-8?q?=E5=A4=87ip?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/main/java/com/egzosn/pay/wx/api/WxPayService.java | 1 + 1 file changed, 1 insertion(+) 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 0449f26..abc52f0 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 @@ -638,6 +638,7 @@ public class WxPayService extends BasePayService { //转账到余额, 申请商户号的appid或商户号绑定的appid parameters.put("mch_appid", payConfigStorage.getAppid()); parameters.put("openid", order.getPayeeAccount()); + parameters.put("spbill_create_ip", StringUtils.isEmpty(order.getIp()) ? "192.168.1.150" : order.getIp()); //默认不校验真实姓名 parameters.put("check_name", "NO_CHECK"); //当存在时候 校验收款用户真实姓名 -- Gitee From 9ec058c1febde771acefc5a6de9638e9ccdca090 Mon Sep 17 00:00:00 2001 From: egzosn Date: Fri, 15 Mar 2019 17:21:46 +0800 Subject: [PATCH 006/299] =?UTF-8?q?=E6=9B=B4=E6=96=B0=E6=B5=8B=E8=AF=95?= =?UTF-8?q?=E7=B1=BB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pay-java-wx/src/test/java/PayTest.java | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/pay-java-wx/src/test/java/PayTest.java b/pay-java-wx/src/test/java/PayTest.java index b0b8dd4..d793613 100644 --- a/pay-java-wx/src/test/java/PayTest.java +++ b/pay-java-wx/src/test/java/PayTest.java @@ -23,10 +23,15 @@ public class PayTest { public static void main(String[] args) { WxPayConfigStorage wxPayConfigStorage = new WxPayConfigStorage(); + wxPayConfigStorage.setAppid("公众账号ID"); + wxPayConfigStorage.setMchId("合作者id(商户号)"); - wxPayConfigStorage.setAppid("应用id"); - wxPayConfigStorage.setKeyPublic("密钥"); - wxPayConfigStorage.setKeyPrivate("密钥"); + //以下两个参数在 服务商版模式中必填-------- +// wxPayConfigStorage.setSubAppid("子商户公众账号ID "); +// wxPayConfigStorage.setSubMchId("微信支付分配的子商户号 "); + //----------------------------------------------- + wxPayConfigStorage.setKeyPublic("转账公钥,转账时必填"); + wxPayConfigStorage.setSecretKey("密钥"); wxPayConfigStorage.setNotifyUrl("异步回调地址"); wxPayConfigStorage.setReturnUrl("同步回调地址"); wxPayConfigStorage.setSignType("签名方式"); -- Gitee From 3514c93f357bf9449415959f00c68c6df58b9138 Mon Sep 17 00:00:00 2001 From: egzosn Date: Wed, 20 Mar 2019 09:53:05 +0800 Subject: [PATCH 007/299] =?UTF-8?q?=E8=B4=9D=E5=AE=9D=E6=94=AF=E4=BB=98=20?= =?UTF-8?q?paypal=E9=80=80=E6=AC=BE=E9=81=97=E6=BC=8F=EF=BC=8Cjson?= =?UTF-8?q?=E5=8C=96=E6=95=B0=E6=8D=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/egzosn/pay/paypal/api/PayPalPayService.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/pay-java-paypal/src/main/java/com/egzosn/pay/paypal/api/PayPalPayService.java b/pay-java-paypal/src/main/java/com/egzosn/pay/paypal/api/PayPalPayService.java index 19c2332..e5114b8 100644 --- a/pay-java-paypal/src/main/java/com/egzosn/pay/paypal/api/PayPalPayService.java +++ b/pay-java-paypal/src/main/java/com/egzosn/pay/paypal/api/PayPalPayService.java @@ -261,7 +261,7 @@ public class PayPalPayService extends BasePayService{ public Map refund(RefundOrder refundOrder) { JSONObject request = new JSONObject(); - if (null != refundOrder.getRefundAmount() && BigDecimal.ZERO.compareTo( refundOrder.getRefundAmount()) > 0){ + if (null != refundOrder.getRefundAmount() && BigDecimal.ZERO.compareTo( refundOrder.getRefundAmount()) == -1){ Amount amount = new Amount(); amount.setCurrency(refundOrder.getCurType().name()); amount.setTotal(Util.conversionAmount(refundOrder.getRefundAmount()).toString()); @@ -269,7 +269,7 @@ public class PayPalPayService extends BasePayService{ request.put("description", refundOrder.getDescription()); } - HttpStringEntity httpEntity = new HttpStringEntity(request, ContentType.APPLICATION_JSON); + HttpStringEntity httpEntity = new HttpStringEntity(request.toJSONString(), ContentType.APPLICATION_JSON); httpEntity.setHeaders(authHeader()); JSONObject resp = getHttpRequestTemplate().postForObject(getReqUrl(PayPalTransactionType.REFUND), httpEntity, JSONObject.class, refundOrder.getTradeNo()); return resp; @@ -307,4 +307,6 @@ public class PayPalPayService extends BasePayService{ public Map secondaryInterface(Object tradeNoOrBillDate, String outTradeNoBillType, TransactionType transactionType) { return Collections.emptyMap(); } + + } -- Gitee From 71b18c9a0974579a18528066f29c996e77744e7a Mon Sep 17 00:00:00 2001 From: egan Date: Fri, 22 Mar 2019 10:37:35 +0800 Subject: [PATCH 008/299] =?UTF-8?q?paypal=E6=94=AF=E4=BB=98=E5=8D=95?= =?UTF-8?q?=E5=8F=B7=E9=97=AE=E9=A2=98=E5=A4=84=E7=90=86=EF=BC=8C=E6=96=B0?= =?UTF-8?q?=E5=A2=9E=E5=9B=9E=E5=AD=98=E6=96=B9=E5=BC=8F=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../egzosn/pay/demo/controller/PayController.java | 6 ++++++ .../pay/demo/controller/PayPalPayController.java | 15 +++++++++++---- .../egzosn/pay/demo/dao/ApyAccountRepository.java | 10 ++++++++++ .../egzosn/pay/paypal/api/PayPalPayService.java | 3 +++ 4 files changed, 30 insertions(+), 4 deletions(-) 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 51824b5..821ddc9 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 @@ -112,6 +112,12 @@ public class PayController { order.setBankType(bankType); } Map orderInfo = payResponse.getService().orderInfo(order); + + //某些支付下单时无法设置单号,通过下单后返回对应单号,如 paypal,友店。 + String outTradeNo = order.getOutTradeNo(); + + System.out.println("支付订单号:" + outTradeNo + " 这里可以进行回存"); + return payResponse.getService().buildRequest(orderInfo, MethodType.POST); } diff --git a/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/PayPalPayController.java b/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/PayPalPayController.java index 84611a8..79042aa 100644 --- a/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/PayPalPayController.java +++ b/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/PayPalPayController.java @@ -40,13 +40,13 @@ public class PayPalPayController { @PostConstruct public void init() { PayPalConfigStorage storage = new PayPalConfigStorage(); - storage.setClientID("商户id"); - storage.setClientSecret("商户密钥"); + storage.setClientID("1AZ7HTcvrEAxYbzYx_iDZAi06GdqbjhqqQzFgPBFLxm2VUMzwlmiNUBk_y_5QNP4zWKblTuM6ZBAmxScd"); + storage.setClientSecret("1EBMIjAag6NiRdXZxteTv0amEsmKN345xJv3bN7f_HRXSqcRJlW7PXhYXjI9sk5I4nKYOHgeqzhXCXKFo"); storage.setTest(true); //发起付款后的页面转跳地址 - storage.setReturnUrl("http://127.0.0.1:8088/pay/success"); + storage.setReturnUrl("http://www.egzosn.com/pay/success"); //取消按钮转跳地址,这里用异步通知地址的兼容的做法 - storage.setNotifyUrl("http://127.0.0.1:8088/pay/cancel"); + storage.setNotifyUrl("http://www.egzosn.com/pay/cancel"); service = new PayPalPayService(storage); //请求连接池配置 @@ -72,6 +72,13 @@ public class PayPalPayController { PayOrder order = new PayOrder("订单title", "摘要", null == price ? new BigDecimal(0.01) : price, UUID.randomUUID().toString().replace("-", ""), PayPalTransactionType.sale); Map orderInfo = service.orderInfo(order); + + //某些支付下单时无法设置单号,通过下单后返回对应单号,如 paypal,友店。 + String outTradeNo = order.getOutTradeNo(); + + System.out.println("支付订单号:" + outTradeNo + " 这里可以进行回存"); + + return service.buildRequest(orderInfo, MethodType.POST); } /** 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 d43e5a1..3bbae60 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 @@ -109,6 +109,16 @@ public class ApyAccountRepository { apyAccount5.setMsgType(MsgType.json); apyAccount5.setTest(true); apyAccounts.put(apyAccount5.getPayId(), apyAccount5); + + ApyAccount apyAccount6 = new ApyAccount(); + apyAccount6.setPayId(6); + apyAccount6.setAppid("1AZ7HTcvrEAxYbzYx_iDZAi06GdqbjhqqQzFgPBFLxm2VUMzwlmiNUBk_y_5QNP4zWKblTuM6ZBAmxScd");//Program ID + apyAccount6.setPrivateKey("1EBMIjAag6NiRdXZxteTv0amEsmKN345xJv3bN7f_HRXSqcRJlW7PXhYXjI9sk5I4nKYOHgeqzhXCXKFo");//API password + apyAccount6.setInputCharset("UTF-8"); + apyAccount6.setPayType(PayType.payPal); + apyAccount6.setMsgType(MsgType.json); + apyAccount6.setTest(true); + apyAccounts.put(apyAccount6.getPayId(), apyAccount6); } //_____________________________________________________________ diff --git a/pay-java-paypal/src/main/java/com/egzosn/pay/paypal/api/PayPalPayService.java b/pay-java-paypal/src/main/java/com/egzosn/pay/paypal/api/PayPalPayService.java index 19c2332..57987c7 100644 --- a/pay-java-paypal/src/main/java/com/egzosn/pay/paypal/api/PayPalPayService.java +++ b/pay-java-paypal/src/main/java/com/egzosn/pay/paypal/api/PayPalPayService.java @@ -181,6 +181,9 @@ public class PayPalPayService extends BasePayService{ HttpStringEntity entity = new HttpStringEntity(JSON.toJSONString(payment), ContentType.APPLICATION_JSON); entity.setHeaders(authHeader()); JSONObject resp = getHttpRequestTemplate().postForObject(getReqUrl(order.getTransactionType()), entity, JSONObject.class); + if ("created".equals(resp.getString("state")) && StringUtils.isNotEmpty(resp.getString("id"))){ + order.setOutTradeNo(resp.getString("id")); + } return resp; } -- Gitee From a1b5a694accafd499e1300d253e584da99d4df83 Mon Sep 17 00:00:00 2001 From: egzosn Date: Tue, 26 Mar 2019 10:48:04 +0800 Subject: [PATCH 009/299] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E6=97=A5=E5=BF=97?= =?UTF-8?q?=E9=85=8D=E7=BD=AE=E6=96=87=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pay-java-demo/src/main/resources/log4j.properties | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 pay-java-demo/src/main/resources/log4j.properties diff --git a/pay-java-demo/src/main/resources/log4j.properties b/pay-java-demo/src/main/resources/log4j.properties new file mode 100644 index 0000000..5eb158d --- /dev/null +++ b/pay-java-demo/src/main/resources/log4j.properties @@ -0,0 +1,10 @@ +log4j.rootLogger=debug,A1 +log4j.appender.A1=org.apache.log4j.ConsoleAppender +log4j.appender.A1.layout=org.apache.log4j.PatternLayout +log4j.appender.A1.layout.ConversionPattern=%d %5p [%t] (%F:%L) - %m%n + + +log4j.logger.com.egzosn.pay=DEBUG +log4j.logger.org.springframework=INFO +log4j.logger.edu.yale=INFO + -- Gitee From 45fb4b048931af6136f326f1f0a78ce41baf00f9 Mon Sep 17 00:00:00 2001 From: egan Date: Sun, 31 Mar 2019 23:42:04 +0800 Subject: [PATCH 010/299] =?UTF-8?q?paypal=E6=94=AF=E4=BB=98=E5=88=B7?= =?UTF-8?q?=E6=96=B0token=EF=BC=8C=E4=BB=A3=E7=A0=81=E6=95=B4=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../pay/ali/api/AliPayConfigStorage.java | 20 +++--- .../pay/common/api/BasePayConfigStorage.java | 28 ++++----- .../demo/controller/PayPalPayController.java | 4 +- .../pay/paypal/api/PayPalConfigStorage.java | 28 ++++----- .../pay/paypal/api/PayPalPayService.java | 4 +- .../pay/union/api/UnionPayConfigStorage.java | 62 ++++++++++--------- .../api/WxYouDianPayConfigStorage.java | 20 ++---- 7 files changed, 79 insertions(+), 87 deletions(-) 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 fad78e7..7286665 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,27 +4,27 @@ import com.egzosn.pay.common.api.BasePayConfigStorage; /** * 支付配置存储 - * @author egan * - * email egzosn@gmail.com - * date 2016-5-18 14:09:01 + * @author egan + *

+ * email egzosn@gmail.com + * date 2016-5-18 14:09:01 */ public class AliPayConfigStorage extends BasePayConfigStorage { /** - * 商户应用id + * 商户应用id */ - private volatile String appId ; + private String appId; /** - * 商户签约拿到的pid,partner_id的简称,合作伙伴身份等同于 partner + * 商户签约拿到的pid,partner_id的简称,合作伙伴身份等同于 partner */ - private volatile String pid ; + private String pid; /** * 商户收款账号 */ - private volatile String seller; - + private String seller; public void setAppId(String appId) { @@ -56,6 +56,4 @@ public class AliPayConfigStorage extends BasePayConfigStorage { } - - } diff --git a/pay-java-common/src/main/java/com/egzosn/pay/common/api/BasePayConfigStorage.java b/pay-java-common/src/main/java/com/egzosn/pay/common/api/BasePayConfigStorage.java index 397cd73..1d45348 100644 --- a/pay-java-common/src/main/java/com/egzosn/pay/common/api/BasePayConfigStorage.java +++ b/pay-java-common/src/main/java/com/egzosn/pay/common/api/BasePayConfigStorage.java @@ -19,61 +19,61 @@ import java.util.concurrent.locks.ReentrantLock; */ public abstract class BasePayConfigStorage implements PayConfigStorage { - private volatile Object attach; + private Object attach; /** * 证书管理器 */ - private volatile CertDescriptor certDescriptor; + private CertDescriptor certDescriptor; /** * 应用私钥,rsa_private pkcs8格式 生成签名时使用 */ - private volatile String keyPrivate; + private String keyPrivate; /** * 应用私钥,rsa_private pkcs8格式 生成签名时使用 */ - private volatile String keyPrivateCertPwd; + private String keyPrivateCertPwd; /** * 支付平台公钥(签名校验使用) */ - private volatile String keyPublic; + private String keyPublic; /** * 异步回调地址 */ - private volatile String notifyUrl; + private String notifyUrl; /** * 同步回调地址,支付完成后展示的页面 */ - private volatile String returnUrl; + private String returnUrl; /** * 签名加密类型 */ - private volatile String signType; + private String signType; /** * 字符类型 */ - private volatile String inputCharset; + private String inputCharset; /** * 支付类型 aliPay 支付宝, wxPay微信..等等,扩展支付模块定义唯一。 */ - private volatile String payType; + private String payType; /** * 消息来源类型 */ - private volatile MsgType msgType; + private MsgType msgType; /** * 访问令牌 每次请求其他方法都要传入的值 */ - private volatile String accessToken; + private String accessToken; /** * access token 到期时间时间戳 */ - private volatile long expiresTime; + private long expiresTime; /** * 授权码锁 */ @@ -91,7 +91,7 @@ public abstract class BasePayConfigStorage implements PayConfigStorage { /** * 支付回调消息 */ - protected volatile PayMessageHandler handler; + protected PayMessageHandler handler; @Override public Object getAttach() { diff --git a/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/PayPalPayController.java b/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/PayPalPayController.java index 79042aa..929299e 100644 --- a/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/PayPalPayController.java +++ b/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/PayPalPayController.java @@ -40,8 +40,8 @@ public class PayPalPayController { @PostConstruct public void init() { PayPalConfigStorage storage = new PayPalConfigStorage(); - storage.setClientID("1AZ7HTcvrEAxYbzYx_iDZAi06GdqbjhqqQzFgPBFLxm2VUMzwlmiNUBk_y_5QNP4zWKblTuM6ZBAmxScd"); - storage.setClientSecret("1EBMIjAag6NiRdXZxteTv0amEsmKN345xJv3bN7f_HRXSqcRJlW7PXhYXjI9sk5I4nKYOHgeqzhXCXKFo"); + storage.setClientID("AZ7HTcvrEAxYbzYx_iDZAi06GdqbjhqqQzFgPBFLxm2VUMzwlmiNUBk_y_5QNP4zWKblTuM6ZBAmxScd"); + storage.setClientSecret("EBMIjAag6NiRdXZxteTv0amEsmKN345xJv3bN7f_HRXSqcRJlW7PXhYXjI9sk5I4nKYOHgeqzhXCXKFo"); storage.setTest(true); //发起付款后的页面转跳地址 storage.setReturnUrl("http://www.egzosn.com/pay/success"); diff --git a/pay-java-paypal/src/main/java/com/egzosn/pay/paypal/api/PayPalConfigStorage.java b/pay-java-paypal/src/main/java/com/egzosn/pay/paypal/api/PayPalConfigStorage.java index ae836b0..8169fa7 100644 --- a/pay-java-paypal/src/main/java/com/egzosn/pay/paypal/api/PayPalConfigStorage.java +++ b/pay-java-paypal/src/main/java/com/egzosn/pay/paypal/api/PayPalConfigStorage.java @@ -4,14 +4,15 @@ import com.egzosn.pay.common.api.BasePayConfigStorage; /** * 贝宝支付配置存储 - * @author egan * - * email egzosn@gmail.com - * date 2018-4-8 22:11:42 + * @author egan + *

+ * email egzosn@gmail.com + * date 2018-4-8 22:11:42 */ public class PayPalConfigStorage extends BasePayConfigStorage { - private volatile String clientID; + private String clientID; @Override public String getAppid() { @@ -41,28 +42,25 @@ public class PayPalConfigStorage extends BasePayConfigStorage { } public void setClientSecret(String clientSecret) { - setKeyPrivate(clientSecret); + setKeyPrivate(clientSecret); } - @Override - public boolean isAccessTokenExpired() { - if (getExpiresTime() == 0){ - return true; - } - return (getExpiresTime() - System.currentTimeMillis() / 1000) <= 0; - } /** * 设置取消页面的url + * * @param cancelUrl 取消页面的url */ - public void setCancelUrl(String cancelUrl){ + public void setCancelUrl(String cancelUrl) { setNotifyUrl(cancelUrl); } + /** * 获取取消页面的url */ - public String getCancelUrl(){ - return getNotifyUrl(); + public String getCancelUrl() { + return getNotifyUrl(); } + + } diff --git a/pay-java-paypal/src/main/java/com/egzosn/pay/paypal/api/PayPalPayService.java b/pay-java-paypal/src/main/java/com/egzosn/pay/paypal/api/PayPalPayService.java index cd83c9b..2bf7714 100644 --- a/pay-java-paypal/src/main/java/com/egzosn/pay/paypal/api/PayPalPayService.java +++ b/pay-java-paypal/src/main/java/com/egzosn/pay/paypal/api/PayPalPayService.java @@ -83,7 +83,6 @@ public class PayPalPayService extends BasePayService{ } if (payConfigStorage.isAccessTokenExpired()) { - if (null == payConfigStorage.getAccessToken()){ Map header = new HashMap<>(); header.put("Authorization", "Basic " + authorizationString(getPayConfigStorage().getAppid(), getPayConfigStorage().getKeyPrivate())); header.put("Accept", "application/json"); @@ -91,12 +90,11 @@ public class PayPalPayService extends BasePayService{ try { HttpStringEntity entity = new HttpStringEntity("grant_type=client_credentials", header); JSONObject resp = getHttpRequestTemplate().postForObject(getReqUrl(PayPalTransactionType.AUTHORIZE), entity, JSONObject.class); - payConfigStorage.updateAccessToken(String.format("%s %s", resp.getString("token_type" ), resp.getString("access_token" )), resp.getLongValue("expires_in" )); + payConfigStorage.updateAccessToken(String.format("%s %s", resp.getString("token_type" ), resp.getString("access_token" )), resp.getIntValue("expires_in" )); } catch (UnsupportedEncodingException e) { throw new PayErrorException(new PayException("failure", e.getMessage())); } - } return payConfigStorage.getAccessToken(); } } finally { diff --git a/pay-java-union/src/main/java/com/egzosn/pay/union/api/UnionPayConfigStorage.java b/pay-java-union/src/main/java/com/egzosn/pay/union/api/UnionPayConfigStorage.java index 56ce420..5ae5003 100644 --- a/pay-java-union/src/main/java/com/egzosn/pay/union/api/UnionPayConfigStorage.java +++ b/pay-java-union/src/main/java/com/egzosn/pay/union/api/UnionPayConfigStorage.java @@ -5,11 +5,11 @@ import com.egzosn.pay.common.api.BasePayConfigStorage; /** * @author Actinia - * - *

- * email hayesfu@qq.com
- *   create 2017 2017/11/4 0004
- * 
+ *

+ *

+ *         email hayesfu@qq.com
+ *           create 2017 2017/11/4 0004
+ *         
*/ public class UnionPayConfigStorage extends BasePayConfigStorage { @@ -17,20 +17,20 @@ public class UnionPayConfigStorage extends BasePayConfigStorage { /** * 商户号 */ - private volatile String merId; + private String merId; /** * 商户收款账号 */ - private volatile String seller; + private String seller; - private volatile String version = "5.1.0"; + private String version = "5.1.0"; /** * 0:普通商户直连接入 * 1: 收单机构 * 2:平台类商户接入 */ - private volatile String accessType = "0"; + private String accessType = "0"; /** * 中级证书路径 @@ -43,38 +43,42 @@ public class UnionPayConfigStorage extends BasePayConfigStorage { /** * 私钥证书是否已经初始化 - * 默认没有 + * 默认没有 */ private boolean keyPrivateInit = false; /** * 公钥证书是否已经初始化 - * 默认没有 + * 默认没有 */ private boolean keyPublicInit = false; /** * 设置私钥证书 + * * @param certificatePath 私钥证书地址 - * 私钥证书密码 {@link #setKeyPrivateCertPwd(String)} + * 私钥证书密码 {@link #setKeyPrivateCertPwd(String)} */ - public void setKeyPrivateCert(String certificatePath){ + public void setKeyPrivateCert(String certificatePath) { super.setKeyPrivate(certificatePath); } /** * 设置中级证书 + * * @param certificatePath 证书地址 */ - public void setAcpMiddleCert(String certificatePath){ + public void setAcpMiddleCert(String certificatePath) { this.acpMiddleCert = certificatePath; } + /** * 设置根证书路径 + * * @param certificatePath 证书路径 */ - public void setAcpRootCert(String certificatePath){ + public void setAcpRootCert(String certificatePath) { this.acpRootCert = certificatePath; } @@ -87,20 +91,20 @@ public class UnionPayConfigStorage extends BasePayConfigStorage { } /** + * 设置私钥证书与证书密码 * - * 设置私钥证书与证书密码 * @param keyPrivate 私钥证书与证书对应的密码 格式: D:/certs/acp_test_sign.pfx;000000 - * 替代方法 - * {@link #setKeyPrivateCert(String)} - * {@link #setKeyPrivateCertPwd(String)} + * 替代方法 + * {@link #setKeyPrivateCert(String)} + * {@link #setKeyPrivateCertPwd(String)} */ @Deprecated @Override public void setKeyPrivate(String keyPrivate) { super.setKeyPrivate(keyPrivate); - if (isCertSign() && keyPrivate.length() < 1024 && keyPrivate.contains(";")){ + if (isCertSign() && keyPrivate.length() < 1024 && keyPrivate.contains(";")) { String[] split = keyPrivate.split(";"); - super.setKeyPrivateCertPwd( split[1]); + super.setKeyPrivateCertPwd(split[1]); super.setKeyPrivate(split[0]); getCertDescriptor().initPrivateSignCert(getKeyPrivate(), getKeyPrivateCertPwd(), "PKCS12"); keyPrivateInit = true; @@ -109,16 +113,17 @@ public class UnionPayConfigStorage extends BasePayConfigStorage { /** * 设置中级证书与根证书 格式:D:/certs/acp_test_middle.cer;D:/certs/acp_test_root.cer + * * @param keyPublic 中级证书与根证书 - * 替代方法 - * {@link #setAcpRootCert(String)} - * {@link #setAcpMiddleCert(String)} + * 替代方法 + * {@link #setAcpRootCert(String)} + * {@link #setAcpMiddleCert(String)} */ @Deprecated @Override public void setKeyPublic(String keyPublic) { super.setKeyPublic(keyPublic); - if (isCertSign() && keyPublic.length() < 1024 ){ + if (isCertSign() && keyPublic.length() < 1024) { String[] split = keyPublic.split(";"); getCertDescriptor().initPublicCert(split[0]); getCertDescriptor().initRootCert(split[1]); @@ -157,15 +162,16 @@ public class UnionPayConfigStorage extends BasePayConfigStorage { return merId; } - public void setPid (String pid) { + public void setPid(String pid) { this.merId = pid; } + @Override public String getSeller() { return seller; } - public void setSeller (String seller) { + public void setSeller(String seller) { this.seller = seller; } @@ -173,7 +179,7 @@ public class UnionPayConfigStorage extends BasePayConfigStorage { return merId; } - public void setMerId (String merId) { + public void setMerId(String merId) { this.merId = merId; } 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 8523223..7548278 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 @@ -4,10 +4,11 @@ import com.egzosn.pay.common.api.BasePayConfigStorage; /** * 支付客户端配置存储 - * @author egan * - * email egzosn@gmail.com - * date 2017/01/12 22:58 + * @author egan + *

+ * email egzosn@gmail.com + * date 2017/01/12 22:58 */ public class WxYouDianPayConfigStorage extends BasePayConfigStorage { @@ -15,11 +16,7 @@ public class WxYouDianPayConfigStorage extends BasePayConfigStorage { /** * 账号 */ - public volatile String seller; - - - - + public String seller; @Override @@ -43,9 +40,8 @@ public class WxYouDianPayConfigStorage extends BasePayConfigStorage { } - public void setToken(String accessToken) { - setAccessToken(accessToken); + setAccessToken(accessToken); } @Override @@ -54,8 +50,4 @@ public class WxYouDianPayConfigStorage extends BasePayConfigStorage { } - - - - } -- Gitee From 6c38f5d14955f0cec857adaba4b5ef1e4ef07442 Mon Sep 17 00:00:00 2001 From: egzosn Date: Mon, 1 Apr 2019 17:59:06 +0800 Subject: [PATCH 011/299] =?UTF-8?q?XML=E5=A4=9A=E5=B1=82=E8=A7=A3=E6=9E=90?= =?UTF-8?q?=EF=BC=8Cmap=E8=BD=ACxml=E5=AD=97=E7=AC=A6=E4=B8=B2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pay-java-ali/README.md | 2 +- .../pay/common/http/ClientHttpRequest.java | 2 +- .../java/com/egzosn/pay/common/util/XML.java | 48 ++++++++++++++++--- pay-java-demo/README.md | 2 +- pom.xml | 1 + 5 files changed, 46 insertions(+), 9 deletions(-) diff --git a/pay-java-ali/README.md b/pay-java-ali/README.md index 48bb371..a1ea919 100644 --- a/pay-java-ali/README.md +++ b/pay-java-ali/README.md @@ -10,7 +10,7 @@ AliPayConfigStorage aliPayConfigStorage = new AliPayConfigStorage(); aliPayConfigStorage.setPid("合作者id"); aliPayConfigStorage.setAppId("应用id"); - aliPayConfigStorage.setAliPublicKey("支付宝公钥"); + aliPayConfigStorage.setKeyPublic("支付宝公钥"); aliPayConfigStorage.setKeyPrivate("应用私钥"); aliPayConfigStorage.setNotifyUrl("异步回调地址"); aliPayConfigStorage.setReturnUrl("同步回调地址"); 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 db0dccf..a8f34b9 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 @@ -37,7 +37,7 @@ import static com.egzosn.pay.common.http.UriVariables.getMapToParameters; */ public class ClientHttpRequest extends HttpEntityEnclosingRequestBase implements org.apache.http.client.ResponseHandler{ protected static final Log LOG = LogFactory.getLog(ClientHttpRequest.class); - public static final ContentType APPLICATION_FORM_URLENCODED_UTF_8 = ContentType.create("application/x-www-form-urlencoded", Consts.UTF_8);; + public static final ContentType APPLICATION_FORM_URLENCODED_UTF_8 = ContentType.create("application/x-www-form-urlencoded", Consts.UTF_8); /** 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 294a457..9e89e74 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 @@ -8,6 +8,7 @@ import com.egzosn.pay.common.bean.result.PayException; import com.egzosn.pay.common.exception.PayErrorException; import com.egzosn.pay.common.util.str.StringUtils; import org.w3c.dom.Document; +import org.w3c.dom.Element; import org.w3c.dom.Node; import org.w3c.dom.NodeList; @@ -254,36 +255,49 @@ public class XML { * @return XML格式的字符串 */ public static String getMap2Xml(Map data) { + return getMap2Xml(data, "xml", "UTF-8"); + } + /** + * 将Map转换为XML格式的字符串 + * + * @param data Map类型数据 + * @param rootElementName 最外层节点名称 + * @return XML格式的字符串 + */ + public static String getMap2Xml(Map data, String rootElementName, String encoding) { Document document = null; try { document = newDocument(); } catch (ParserConfigurationException e) { throw new PayErrorException(new PayException("ParserConfigurationException", e.getLocalizedMessage())); } - org.w3c.dom.Element root = document.createElement("xml"); + org.w3c.dom.Element root = document.createElement(rootElementName); document.appendChild(root); - for (Map.Entry entry : data.entrySet()) { + /* for (Map.Entry entry : data.entrySet()) { Object value = entry.getValue(); if (value == null) { value = ""; } + value = value.toString().trim(); org.w3c.dom.Element filed = document.createElement(entry.getKey()); filed.appendChild(document.createTextNode(value.toString())); root.appendChild(filed); - } + }*/ + + map2Xml(data, document, root); try { TransformerFactory tf = TransformerFactory.newInstance(); Transformer transformer = tf.newTransformer(); DOMSource source = new DOMSource(document); - transformer.setOutputProperty(OutputKeys.ENCODING, "UTF-8"); + transformer.setOutputProperty(OutputKeys.ENCODING, encoding); transformer.setOutputProperty(OutputKeys.INDENT, "yes"); StringWriter writer = new StringWriter(); StreamResult result = new StreamResult(writer); transformer.transform(source, result); - String output = writer.getBuffer().toString(); //.replaceAll("\n|\r", ""); + String output = writer.getBuffer().toString(); return output; } catch (TransformerException e) { e.printStackTrace(); @@ -293,5 +307,27 @@ public class XML { return ""; } - + /** + * 将Map转换为XML格式的字符串 + * + * @param data Map类型数据 + * @param document 文档 + * @return XML格式的字符串 + */ + public static void map2Xml(Map data, Document document, org.w3c.dom.Element element) { + for (Map.Entry entry : data.entrySet()) { + Object value = entry.getValue(); + if (value == null) { + value = ""; + } + org.w3c.dom.Element filed = document.createElement(entry.getKey()); + if (value instanceof Map){ + map2Xml((Map)value, document, filed); + }else { + value = value.toString().trim(); + filed.appendChild(document.createTextNode(value.toString())); + } + element.appendChild(filed); + } + } } diff --git a/pay-java-demo/README.md b/pay-java-demo/README.md index fe23432..27793b7 100644 --- a/pay-java-demo/README.md +++ b/pay-java-demo/README.md @@ -23,7 +23,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()); diff --git a/pom.xml b/pom.xml index 643fa99..c4b08aa 100644 --- a/pom.xml +++ b/pom.xml @@ -51,6 +51,7 @@ pay-java-union pay-java-payoneer pay-java-paypal + pay-java-pds pay-java-demo -- Gitee From 6e5ba155c8a2a73c774801f54a92d17d361fd5e7 Mon Sep 17 00:00:00 2001 From: egzosn Date: Tue, 9 Apr 2019 21:13:01 +0800 Subject: [PATCH 012/299] md --- README.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 24b8806..ec86176 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,6 @@ ##整合支付模块 -声明: 本项目最初想法自 https://github.com/chanjarster/weixin-java-tools, 15年1月左右关注chanjarster/weixin-java-tools,并将其回调处理修改并进行使用。 - ##### 详细文档请看 [wiki](https://gitee.com/egzosn/pay-java-parent/wikis/Home)。 @@ -50,6 +48,9 @@ * 码云:https://gitee.com/egzosn/pay-java-parent * GitHub:https://github.com/egzosn/pay-java-parent +#### spring-boot-starter-pay 是一个基于spring-boot实现自动化配置的支付对接,让你可以不用理解支付怎么对接,只需要专注你的业务 +* 码云:https://gitee.com/egzosn/pay-spring-boot-starter-parent +* GitHub:https://github.com/egzosn/pay-spring-boot-starter-parent ### 使用 这里不多说直接上代码 -- Gitee From b29bd06e77c123982afc5669302b8a9797c6a87f Mon Sep 17 00:00:00 2001 From: egan Date: Mon, 15 Apr 2019 00:08:09 +0800 Subject: [PATCH 013/299] =?UTF-8?q?=E4=BC=98=E5=8C=96=E8=AF=81=E4=B9=A6?= =?UTF-8?q?=E7=9B=B8=E5=85=B3=E6=93=8D=E4=BD=9C=EF=BC=8C=E6=96=B0=E5=A2=9E?= =?UTF-8?q?=E8=AF=81=E4=B9=A6=E7=9A=84=E5=AD=98=E5=82=A8=E6=96=B9=E5=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pay-java-ali/README.md | 13 +- .../pay/ali/api/AliPayConfigStorage.java | 8 +- .../pay/common/api/BasePayConfigStorage.java | 53 +- .../pay/common/api/PayConfigStorage.java | 5 - .../egzosn/pay/common/bean/CertStoreType.java | 69 +++ .../pay/common/http/HttpConfigStorage.java | 102 +--- .../pay/common/util/sign/CertDescriptor.java | 524 +++++++++++------- pay-java-demo/README.md | 6 +- .../pay/demo/controller/AliPayController.java | 2 +- .../demo/controller/UnionPayController.java | 13 +- .../pay/demo/controller/WxPayController.java | 3 +- .../pay/demo/dao/ApyAccountRepository.java | 4 +- .../com/egzosn/pay/demo/entity/PayType.java | 23 +- pay-java-union/README.md | 27 +- .../pay/union/api/UnionPayConfigStorage.java | 138 ++--- .../egzosn/pay/union/api/UnionPayService.java | 34 +- pay-java-wx/README.md | 9 +- 17 files changed, 567 insertions(+), 466 deletions(-) create mode 100644 pay-java-common/src/main/java/com/egzosn/pay/common/bean/CertStoreType.java diff --git a/pay-java-ali/README.md b/pay-java-ali/README.md index a1ea919..d9e927b 100644 --- a/pay-java-ali/README.md +++ b/pay-java-ali/README.md @@ -9,7 +9,7 @@ AliPayConfigStorage aliPayConfigStorage = new AliPayConfigStorage(); aliPayConfigStorage.setPid("合作者id"); - aliPayConfigStorage.setAppId("应用id"); + aliPayConfigStorage.setAppid("应用id"); aliPayConfigStorage.setKeyPublic("支付宝公钥"); aliPayConfigStorage.setKeyPrivate("应用私钥"); aliPayConfigStorage.setNotifyUrl("异步回调地址"); @@ -34,16 +34,19 @@ //代理端口 httpConfigStorage.setHttpProxyPort(3308); //代理用户名 - httpConfigStorage.setHttpProxyUsername("user"); + httpConfigStorage.setAuthUsername("user"); //代理密码 - httpConfigStorage.setHttpProxyPassword("password"); + httpConfigStorage.setAuthPassword("password"); /* /网路代理配置 根据需求进行设置**/ /* 网络请求ssl证书 根据需求进行设置**/ - //设置ssl证书路径 - httpConfigStorage.setKeystorePath("证书绝对路径"); + //设置ssl证书路径 跟着setCertStoreType 进行对应 + httpConfigStorage.setKeystore("证书文件流,证书字符串信息或证书绝对地址"); //设置ssl证书对应的密码 httpConfigStorage.setStorePassword("证书对应的密码"); + //设置ssl证书对应的存储方式 + httpConfigStorage.setCertStoreType(CertStoreType.PATH); + /* /网络请求ssl证书**/ /* /网络请求连接池**/ 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 7286665..3eef8c4 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 @@ -15,7 +15,7 @@ public class AliPayConfigStorage extends BasePayConfigStorage { /** * 商户应用id */ - private String appId; + private String appid; /** * 商户签约拿到的pid,partner_id的简称,合作伙伴身份等同于 partner */ @@ -27,13 +27,13 @@ public class AliPayConfigStorage extends BasePayConfigStorage { private String seller; - public void setAppId(String appId) { - this.appId = appId; + public void setAppid(String appid) { + this.appid = appid; } @Override public String getAppid() { - return appId; + return appid; } diff --git a/pay-java-common/src/main/java/com/egzosn/pay/common/api/BasePayConfigStorage.java b/pay-java-common/src/main/java/com/egzosn/pay/common/api/BasePayConfigStorage.java index 1d45348..3964139 100644 --- a/pay-java-common/src/main/java/com/egzosn/pay/common/api/BasePayConfigStorage.java +++ b/pay-java-common/src/main/java/com/egzosn/pay/common/api/BasePayConfigStorage.java @@ -1,8 +1,7 @@ package com.egzosn.pay.common.api; +import com.egzosn.pay.common.bean.CertStoreType; import com.egzosn.pay.common.bean.MsgType; -import com.egzosn.pay.common.bean.result.PayException; -import com.egzosn.pay.common.exception.PayErrorException; import com.egzosn.pay.common.util.sign.CertDescriptor; import java.util.concurrent.locks.Lock; @@ -19,61 +18,57 @@ import java.util.concurrent.locks.ReentrantLock; */ public abstract class BasePayConfigStorage implements PayConfigStorage { - private Object attach; - /** - * 证书管理器 - */ - private CertDescriptor certDescriptor; + private Object attach; /** * 应用私钥,rsa_private pkcs8格式 生成签名时使用 */ - private String keyPrivate; + private String keyPrivate; /** - * 应用私钥,rsa_private pkcs8格式 生成签名时使用 + * 应用私钥证书,rsa_private pkcs8格式 生成签名时使用 */ - private String keyPrivateCertPwd; + private String keyPrivateCertPwd; /** * 支付平台公钥(签名校验使用) */ - private String keyPublic; + private String keyPublic; /** * 异步回调地址 */ - private String notifyUrl; + private String notifyUrl; /** * 同步回调地址,支付完成后展示的页面 */ - private String returnUrl; + private String returnUrl; /** * 签名加密类型 */ - private String signType; + private String signType; /** * 字符类型 */ - private String inputCharset; + private String inputCharset; /** * 支付类型 aliPay 支付宝, wxPay微信..等等,扩展支付模块定义唯一。 */ - private String payType; + private String payType; /** * 消息来源类型 */ - private MsgType msgType; + private MsgType msgType; /** * 访问令牌 每次请求其他方法都要传入的值 */ - private String accessToken; + private String accessToken; /** * access token 到期时间时间戳 */ - private long expiresTime; + private long expiresTime; /** * 授权码锁 */ @@ -88,10 +83,6 @@ public abstract class BasePayConfigStorage implements PayConfigStorage { */ private boolean isCertSign = false; - /** - * 支付回调消息 - */ - protected PayMessageHandler handler; @Override public Object getAttach() { @@ -102,17 +93,6 @@ public abstract class BasePayConfigStorage implements PayConfigStorage { this.attach = attach; } - @Override - public CertDescriptor getCertDescriptor() { - if (!isCertSign) { - throw new PayErrorException(new PayException("certDescriptor fail", "isCertSign is false")); - } - if (null == certDescriptor) { - certDescriptor = new CertDescriptor(); - } - return certDescriptor; - } - @Override public String getKeyPrivate() { return keyPrivate; @@ -121,6 +101,7 @@ public abstract class BasePayConfigStorage implements PayConfigStorage { public void setKeyPrivate(String keyPrivate) { this.keyPrivate = keyPrivate; } + @Override public String getKeyPrivateCertPwd() { return keyPrivateCertPwd; @@ -148,7 +129,6 @@ public abstract class BasePayConfigStorage implements PayConfigStorage { this.notifyUrl = notifyUrl; } - @Override public String getReturnUrl() { return returnUrl; @@ -264,9 +244,6 @@ public abstract class BasePayConfigStorage implements PayConfigStorage { public void setCertSign(boolean certSign) { isCertSign = certSign; - if (certSign) { - certDescriptor = new CertDescriptor(); - } } diff --git a/pay-java-common/src/main/java/com/egzosn/pay/common/api/PayConfigStorage.java b/pay-java-common/src/main/java/com/egzosn/pay/common/api/PayConfigStorage.java index 30d9329..8abdb23 100644 --- a/pay-java-common/src/main/java/com/egzosn/pay/common/api/PayConfigStorage.java +++ b/pay-java-common/src/main/java/com/egzosn/pay/common/api/PayConfigStorage.java @@ -20,11 +20,6 @@ import java.util.concurrent.locks.Lock; * @return 附加信息 */ Object getAttach(); - /** - * 获取证书解释器 - * @return 证书解释器 - */ - CertDescriptor getCertDescriptor(); /** * 获取私钥证书密码 diff --git a/pay-java-common/src/main/java/com/egzosn/pay/common/bean/CertStoreType.java b/pay-java-common/src/main/java/com/egzosn/pay/common/bean/CertStoreType.java new file mode 100644 index 0000000..196892d --- /dev/null +++ b/pay-java-common/src/main/java/com/egzosn/pay/common/bean/CertStoreType.java @@ -0,0 +1,69 @@ +package com.egzosn.pay.common.bean; + +import java.io.*; + +/** + * 证书存储类型 + * + * @author egan + * email egzosn@gmail.com + * date 2019/4/14.23:04 + */ +public enum CertStoreType { + + /** + * 路径,建议绝对路径 + */ + PATH { + /** + * 证书信息转化为对应的输入流 + * + * @param cert 证书信息 + * @return 输入流 + */ + @Override + public InputStream getInputStream(Object cert) throws IOException { + return new FileInputStream(new File((String) cert)); + } + }, + /** + * 文件流转化成字符串存储至文件或者数据库中 + */ + STR { + /** + * 证书信息转化为对应的输入流 + * + * @param cert 证书信息 + * @return 输入流 + */ + @Override + public InputStream getInputStream(Object cert) throws IOException { + return new ByteArrayInputStream(((String) cert).getBytes("ISO-8859-1")); + } + }, + + /** + * 文件流 + */ + INPUT_STREAM { + /** + * 证书信息转化为对应的输入流 + * + * @param cert 证书信息 + * @return 输入流 + */ + @Override + public InputStream getInputStream(Object cert) throws IOException { + return (InputStream) cert; + } + }; + + /** + * 证书信息转化为对应的输入流 + * + * @param cert 证书信息 + * @return 输入流 + */ + public abstract InputStream getInputStream(Object cert) throws IOException; + +} diff --git a/pay-java-common/src/main/java/com/egzosn/pay/common/http/HttpConfigStorage.java b/pay-java-common/src/main/java/com/egzosn/pay/common/http/HttpConfigStorage.java index f938035..4006a8a 100644 --- a/pay-java-common/src/main/java/com/egzosn/pay/common/http/HttpConfigStorage.java +++ b/pay-java-common/src/main/java/com/egzosn/pay/common/http/HttpConfigStorage.java @@ -1,6 +1,8 @@ package com.egzosn.pay.common.http; +import com.egzosn.pay.common.bean.CertStoreType; + import java.io.*; /** @@ -29,10 +31,12 @@ public class HttpConfigStorage { */ private String authPassword; + /** - * @see #keystore 是否为https请求所需的证书(PKCS12)的地址,默认为地址,否则为证书信息串 + * 证书存储类型 + * @see #keystore 是否为https请求所需的证书(PKCS12)的地址,默认为地址,否则为证书信息串,文件流 */ - private boolean isPath = true; + private CertStoreType certStoreType = CertStoreType.PATH; /** * https请求所需的证书(PKCS12) @@ -104,109 +108,35 @@ public class HttpConfigStorage { this.authPassword = authPassword; } - /** - * 代理用户名 - * @return 代理用户名 - * @see #getAuthUsername() - */ - @Deprecated - public String getHttpProxyUsername() { - return authUsername; - } - - /** - * 设置代理用户名 - * @param httpProxyUsername 代理用户名 - * @see #setAuthUsername(String) - */ - @Deprecated - public void setHttpProxyUsername(String httpProxyUsername) { - this.authUsername = httpProxyUsername; - } - /** - * 代理密码 - * @return 代理密码 - * @see #getAuthPassword() - */ - @Deprecated - public String getHttpProxyPassword() { - return authPassword; + public CertStoreType getCertStoreType() { + return certStoreType; } - /** - * 设置代理密码 - * @param httpProxyPassword 代理密码 - * @see #setAuthPassword(String) - */ - @Deprecated - public void setHttpProxyPassword(String httpProxyPassword) { - this.authPassword = httpProxyPassword; - } - - /** - * https请求所需的证书(PKCS12)地址,请使用绝对路径 - * @return 证书(PKCS12)地址 - * @see #getKeystore() - */ - @Deprecated - public String getKeystorePath() { - return (String) keystore; - } - - /** - * 设置https请求所需的证书(PKCS12)地址,请使用绝对路径 - * @param keystorePath 证书(PKCS12)地址 - * @see #getKeystore() - */ - @Deprecated - public void setKeystorePath(String keystorePath) { - this.keystore = keystorePath; - } - - - /** - * 获取是否为证书地址 - * @return 是否为证书地址,配合 {@link #getKeystore()}使用 - */ - public boolean isPath() { - return isPath; - } - - /** - * 设置是否为证书地址 - * @param path 是否为证书地址 - */ - public void setPath(boolean path) { - isPath = path; + public void setCertStoreType(CertStoreType certStoreType) { + this.certStoreType = certStoreType; } /** * 获取证书信息 - * @return 证书信息 根据 {@link #isPath()}进行区别地址与信息串 + * @return 证书信息 根据 {@link #getCertStoreType()}进行区别地址与信息串 */ - public InputStream getKeystoreInputStream() throws FileNotFoundException, UnsupportedEncodingException { - if (null == keystore){ + public InputStream getKeystoreInputStream() throws IOException { + if (null == keystore) { return null; } - if(isPath()){ - return new FileInputStream(new File(getKeystoreStr())); - } - if(this.keystore instanceof String){ - return new ByteArrayInputStream(getKeystoreStr().getBytes("ISO-8859-1")); - } - return (InputStream) keystore; + return certStoreType.getInputStream(keystore); } /** * 获取证书信息 - * @return 证书信息 根据 {@link #isPath()}进行区别地址与信息串 + * @return 证书信息 根据 {@link #getCertStoreType()}进行区别地址与信息串 */ public Object getKeystore() { return keystore; } /** * 获取证书信息 证书地址 - * @return 证书信息 根据 {@link #isPath()}进行区别地址与信息串 + * @return 证书信息 根据 {@link #getCertStoreType()}进行区别地址与信息串 */ public String getKeystoreStr() { return (String) keystore; diff --git a/pay-java-common/src/main/java/com/egzosn/pay/common/util/sign/CertDescriptor.java b/pay-java-common/src/main/java/com/egzosn/pay/common/util/sign/CertDescriptor.java index c98a620..f16c7d3 100644 --- a/pay-java-common/src/main/java/com/egzosn/pay/common/util/sign/CertDescriptor.java +++ b/pay-java-common/src/main/java/com/egzosn/pay/common/util/sign/CertDescriptor.java @@ -1,16 +1,15 @@ /** - * * Licensed Property to China UnionPay Co., Ltd. - * + *

* (C) Copyright of China UnionPay Co., Ltd. 2010 - * All Rights Reserved. - * - * + * All Rights Reserved. + *

+ *

* Modification History: * ============================================================================= - * Author Date Description - * ------------ ---------- --------------------------------------------------- - * xshu 2014-05-28 证书工具类. + * Author Date Description + * ------------ ---------- --------------------------------------------------- + * xshu 2014-05-28 证书工具类. * ============================================================================= */ package com.egzosn.pay.common.util.sign; @@ -19,10 +18,15 @@ import com.egzosn.pay.common.util.str.StringUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; -import java.io.*; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStream; import java.security.*; -import java.security.cert.*; -import java.util.*; +import java.security.cert.CertificateException; +import java.security.cert.CertificateFactory; +import java.security.cert.X509Certificate; +import java.util.Enumeration; /** @@ -31,227 +35,321 @@ import java.util.*; * 声明:以下代码只是为了方便接入方测试而提供的样例代码,商户可以根据自己需要,按照技术文档编写。该代码仅供参考,不提供编码,性能,规范性等方面的保障 */ public class CertDescriptor { - protected static final Log LOG = LogFactory.getLog(CertDescriptor.class); - /** 证书容器,存储对商户请求报文签名私钥证书. */ - private KeyStore keyStore = null; + protected static final Log LOG = LogFactory.getLog(CertDescriptor.class); + /** + * 证书容器,存储对商户请求报文签名私钥证书. + */ + private KeyStore keyStore = null; - /** 验签公钥/中级证书 */ - private X509Certificate publicKeyCert = null; - /** 验签根证书 */ - private X509Certificate rootKeyCert = null; + /** + * 验签公钥/中级证书 + */ + private X509Certificate publicKeyCert = null; + /** + * 验签根证书 + */ + private X509Certificate rootKeyCert = null; + /** + * 通过证书路径初始化为公钥证书 + * + * @param certIn 证书流 + * @return X509 证书 + */ + private static X509Certificate initCert(InputStream certIn) { + X509Certificate encryptCertTemp = null; + CertificateFactory cf = null; + try { + cf = CertificateFactory.getInstance("X.509"); + encryptCertTemp = (X509Certificate) cf.generateCertificate(certIn); + // 打印证书加载信息,供测试阶段调试 + if (LOG.isWarnEnabled()) { + LOG.warn("[CertId=" + encryptCertTemp.getSerialNumber().toString() + "]"); + } + } catch (CertificateException e) { + LOG.error("InitCert Error", e); + } finally { + if (null != certIn) { + try { + certIn.close(); + } catch (IOException e) { + LOG.error(e.toString()); + } + } + } + return encryptCertTemp; + } - /** - * 通过证书路径初始化为公钥证书 - * @param path 证书地址 - * @return X509 证书 - */ - private static X509Certificate initCert(String path) { - X509Certificate encryptCertTemp = null; - CertificateFactory cf = null; - FileInputStream in = null; - try { - cf = CertificateFactory.getInstance("X.509"); - in = new FileInputStream(path); - encryptCertTemp = (X509Certificate) cf.generateCertificate(in); - // 打印证书加载信息,供测试阶段调试 - if (LOG.isWarnEnabled()) { - LOG.warn("[" + path + "][CertId=" + encryptCertTemp.getSerialNumber().toString() + "]"); - } - } catch (CertificateException e) { - LOG.error("InitCert Error", e); - } catch (FileNotFoundException e) { - LOG.error("InitCert Error File Not Found", e); - }finally { - if (null != in) { - try { - in.close(); - } catch (IOException e) { - LOG.error(e.toString()); - } - } - } - return encryptCertTemp; - } + /** + * 通过证书路径初始化为公钥证书 + * + * @param path 证书地址 + * @return X509 证书 + */ + private static X509Certificate initCert(String path) { + X509Certificate encryptCertTemp = null; + CertificateFactory cf = null; + FileInputStream in = null; + try { + in = new FileInputStream(path); + encryptCertTemp = initCert(in); + } catch (FileNotFoundException e) { + LOG.error("InitCert Error File Not Found", e); + } + return encryptCertTemp; + } - /** - * 通过keyStore 获取私钥签名证书PrivateKey对象 - * - * @param pwd 证书对应密码 - * @return PrivateKey 私钥 - */ - public PrivateKey getSignCertPrivateKey(String pwd) { - try { - Enumeration aliasenum = keyStore.aliases(); - String keyAlias = null; - if (aliasenum.hasMoreElements()) { - keyAlias = aliasenum.nextElement(); - } - PrivateKey privateKey = (PrivateKey) keyStore.getKey(keyAlias, - pwd.toCharArray()); - return privateKey; - } catch (KeyStoreException e) { - LOG.error("getSignCertPrivateKey Error", e); - return null; - } catch (UnrecoverableKeyException e) { - LOG.error("getSignCertPrivateKey Error", e); - return null; - } catch (NoSuchAlgorithmException e) { - LOG.error("getSignCertPrivateKey Error", e); - return null; - } - } + /** + * 通过keyStore 获取私钥签名证书PrivateKey对象 + * + * @param pwd 证书对应密码 + * @return PrivateKey 私钥 + */ + public PrivateKey getSignCertPrivateKey(String pwd) { + try { + Enumeration aliasenum = keyStore.aliases(); + String keyAlias = null; + if (aliasenum.hasMoreElements()) { + keyAlias = aliasenum.nextElement(); + } + PrivateKey privateKey = (PrivateKey) keyStore.getKey(keyAlias, + pwd.toCharArray()); + return privateKey; + } catch (KeyStoreException e) { + LOG.error("getSignCertPrivateKey Error", e); + return null; + } catch (UnrecoverableKeyException e) { + LOG.error("getSignCertPrivateKey Error", e); + return null; + } catch (NoSuchAlgorithmException e) { + LOG.error("getSignCertPrivateKey Error", e); + return null; + } + } - - - /** - * 配置的签名私钥证书certId - * - * @return 证书的物理编号 - */ - public String getSignCertId() { - try { - Enumeration aliasenum = keyStore.aliases(); - String keyAlias = null; - if (aliasenum.hasMoreElements()) { - keyAlias = aliasenum.nextElement(); - } - X509Certificate cert = (X509Certificate) keyStore - .getCertificate(keyAlias); - return cert.getSerialNumber().toString(); - } catch (Exception e) { - LOG.error("getSignCertId Error", e); - return null; - } - } + /** + * 配置的签名私钥证书certId + * + * @return 证书的物理编号 + */ + public String getSignCertId() { + try { + Enumeration aliasenum = keyStore.aliases(); + String keyAlias = null; + if (aliasenum.hasMoreElements()) { + keyAlias = aliasenum.nextElement(); + } + X509Certificate cert = (X509Certificate) keyStore.getCertificate(keyAlias); + return cert.getSerialNumber().toString(); + } catch (Exception e) { + LOG.error("getSignCertId Error", e); + return null; + } + } + /** + * 将签名私钥证书文件读取为证书存储对象 + * + * @param signCertPath 证书文件名 + * @param signCertPwd 证书密码 + * @param signCertType 证书类型 + */ + public void initPrivateSignCert(String signCertPath, String signCertPwd, String signCertType) { + if (null != keyStore) { + keyStore = null; + } + try { + keyStore = getKeyInfo(signCertPath, signCertPwd, signCertType); + if (LOG.isInfoEnabled()) { + LOG.info("InitSignCert Successful. CertId=[" + getSignCertId() + "]"); + } + } catch (IOException e) { + LOG.error("InitSignCert Error", e); + } + } + /** + * 将签名私钥证书文件读取为证书存储对象 + * + * @param signCert 证书文件 + * @param signCertPwd 证书密码 + * @param signCertType 证书类型 + */ + public void initPrivateSignCert(InputStream signCert, String signCertPwd, String signCertType) { - /** - * 将签名私钥证书文件读取为证书存储对象 - * - * @param signCertPath 证书文件名 - * @param signCertPwd 证书密码 - * @param signCertType 证书类型 - */ - public void initPrivateSignCert(String signCertPath, String signCertPwd, String signCertType) { + if (null != keyStore) { + keyStore = null; + } + keyStore = getKeyInfo(signCert, signCertPwd, signCertType); + if (LOG.isInfoEnabled()) { + LOG.info("InitSignCert Successful. CertId=[" + getSignCertId() + "]"); + } + } - if (null != keyStore) { - keyStore = null; - } - try { - keyStore = getKeyInfo(signCertPath, signCertPwd,signCertType); - if (LOG.isInfoEnabled()) { - LOG.info("InitSignCert Successful. CertId=[" + getSignCertId() + "]"); - } - } catch (IOException e) { - LOG.error("InitSignCert Error", e); - } - } + /** + * 将签名私钥证书文件读取为证书存储对象 + * + * @param fxKeyFile 证书文件名 + * @param keyPwd 证书密码 + * @param type 证书类型 + * @return 证书对象 + * @throws IOException + */ + private KeyStore getKeyInfo(String fxKeyFile, String keyPwd, String type) throws IOException { + if (LOG.isWarnEnabled()) { + LOG.warn("加载签名证书==>" + fxKeyFile); + } + FileInputStream fis = new FileInputStream(fxKeyFile); + return getKeyInfo(fis, keyPwd, type); - /** - * 将签名私钥证书文件读取为证书存储对象 - * - * @param pfxkeyfile 证书文件名 - * @param keypwd 证书密码 - * @param type 证书类型 - * @return 证书对象 - * @throws IOException - */ - private KeyStore getKeyInfo(String pfxkeyfile, String keypwd, String type) throws IOException { - if (LOG.isWarnEnabled()) { - LOG.warn("加载签名证书==>" + pfxkeyfile); - } - try(FileInputStream fis = new FileInputStream(pfxkeyfile);) { - KeyStore ks = KeyStore.getInstance(type); - if (LOG.isWarnEnabled()) { - LOG.warn("Load RSA CertPath=[" + pfxkeyfile + "],Pwd=["+ keypwd + "],type=["+type+"]"); - } + } - char[] nPassword = null; - nPassword = null == keypwd || "".equals(keypwd.trim()) ? null: keypwd.toCharArray(); - if (null != ks) { - ks.load(fis, nPassword); - } - return ks; - } catch (Exception e) { - LOG.error("getKeyInfo Error", e); - return null; - } - } + /** + * 将签名私钥证书文件读取为证书存储对象 + * + * @param fxKeyFile 证书文件 + * @param keyPwd 证书密码 + * @param type 证书类型 + * @return 证书对象 + * @throws IOException + */ + public KeyStore getKeyInfo(InputStream fxKeyFile, String keyPwd, String type) { - - /** - * 通过keystore获取私钥证书的certId值 - * @param keyStore - * @return - */ - private String getCertIdIdByStore(KeyStore keyStore) { - Enumeration aliasenum = null; - try { - aliasenum = keyStore.aliases(); - String keyAlias = null; - if (aliasenum.hasMoreElements()) { - keyAlias = aliasenum.nextElement(); - } - X509Certificate cert = (X509Certificate) keyStore - .getCertificate(keyAlias); - return cert.getSerialNumber().toString(); - } catch (KeyStoreException e) { - LOG.error("getCertIdIdByStore Error", e); - return null; - } - } + try { + KeyStore ks = KeyStore.getInstance(type); + if (LOG.isWarnEnabled()) { + LOG.warn("Load RSA CertPath,Pwd=[" + keyPwd + "],type=[" + type + "]"); + } + char[] nPassword = null; + nPassword = null == keyPwd || "".equals(keyPwd.trim()) ? null : keyPwd.toCharArray(); + if (null != ks) { + ks.load(fxKeyFile, nPassword); + } + return ks; + } catch (Exception e) { + LOG.error("getKeyInfo Error", e); + return null; + } finally { + if (null != fxKeyFile) { + try { + fxKeyFile.close(); + } catch (IOException e) { + LOG.error("getKeyInfo Error", e); + } + } + } + } - /** - * 加载中级证书 - * @param certPath 证书地址 - */ - public void initPublicCert(String certPath) { - if (!StringUtils.isEmpty(certPath)) { - publicKeyCert = initCert(certPath); - if (LOG.isInfoEnabled()) { - LOG.info("Load PublicKeyCert Successful"); - } - } else if (LOG.isInfoEnabled()) { - LOG.info("PublicKeyCert is empty"); - } - } + /** + * 通过keystore获取私钥证书的certId值 + * + * @param keyStore + * @return + */ + private String getCertIdIdByStore(KeyStore keyStore) { + Enumeration aliasenum = null; + try { + aliasenum = keyStore.aliases(); + String keyAlias = null; + if (aliasenum.hasMoreElements()) { + keyAlias = aliasenum.nextElement(); + } + X509Certificate cert = (X509Certificate) keyStore + .getCertificate(keyAlias); + return cert.getSerialNumber().toString(); + } catch (KeyStoreException e) { + LOG.error("getCertIdIdByStore Error", e); + return null; + } + } - /** - * 加载根证书 - * @param certPath 证书地址 - */ - public void initRootCert(String certPath) { - if (!StringUtils.isEmpty(certPath)) { - rootKeyCert = initCert(certPath); - if (LOG.isInfoEnabled()) { - LOG.info("Load RootCert Successful"); - } - } else if (LOG.isInfoEnabled()) { - LOG.info("RootCert is empty"); - } - } - /** - * 获取公钥/中级证书 - * @return X509Certificate - */ - public X509Certificate getPublicCert() { - return publicKeyCert; - } + /** + * 加载中级证书 + * + * @param certPath 证书地址 + */ + public void initPublicCert(String certPath) { + if (!StringUtils.isEmpty(certPath)) { + publicKeyCert = initCert(certPath); + if (LOG.isInfoEnabled()) { + LOG.info("Load PublicKeyCert Successful"); + } + } else if (LOG.isInfoEnabled()) { + LOG.info("PublicKeyCert is empty"); + } + } + + /** + * 加载中级证书 + * + * @param cert 证书文件 + */ + public void initPublicCert(InputStream cert) { + if (null != cert) { + publicKeyCert = initCert(cert); + if (LOG.isInfoEnabled()) { + LOG.info("Load PublicKeyCert Successful"); + } + } else if (LOG.isInfoEnabled()) { + LOG.info("PublicKeyCert is empty"); + } + } + + /** + * 加载根证书 + * + * @param certPath 证书地址 + */ + public void initRootCert(String certPath) { + if (!StringUtils.isEmpty(certPath)) { + try { + initRootCert(new FileInputStream(certPath)); + } catch (FileNotFoundException e) { + LOG.info("RootCert is empty"); + } + + } else if (LOG.isInfoEnabled()) { + LOG.info("RootCert is empty"); + } + } + /** + * 加载根证书 + * + * @param cert 证书文件 + */ + public void initRootCert(InputStream cert) { + if (null != cert) { + rootKeyCert = initCert(cert); + if (LOG.isInfoEnabled()) { + LOG.info("Load RootCert Successful"); + } + } else if (LOG.isInfoEnabled()) { + LOG.info("RootCert is empty"); + } + } + + /** + * 获取公钥/中级证书 + * + * @return X509Certificate + */ + public X509Certificate getPublicCert() { + return publicKeyCert; + } + + /** + * 获取中级证书 + * + * @return X509Certificate + */ + public X509Certificate getRootCert() { + return rootKeyCert; + } - /** - * 获取中级证书 - * @return X509Certificate - */ - public X509Certificate getRootCert() { - return rootKeyCert; - } - } diff --git a/pay-java-demo/README.md b/pay-java-demo/README.md index 27793b7..b066e05 100644 --- a/pay-java-demo/README.md +++ b/pay-java-demo/README.md @@ -22,7 +22,7 @@ public enum PayType implements BasePayType { public PayService getPayService(ApyAccount apyAccount) { AliPayConfigStorage aliPayConfigStorage = new AliPayConfigStorage(); aliPayConfigStorage.setPid(apyAccount.getPartner()); - aliPayConfigStorage.setAppId(apyAccount.getAppid()); + aliPayConfigStorage.setAppid(apyAccount.getAppid()); aliPayConfigStorage.setKeyPublic(apyAccount.getPublicKey()); aliPayConfigStorage.setKeyPrivate(apyAccount.getPrivateKey()); aliPayConfigStorage.setNotifyUrl(apyAccount.getNotifyUrl()); @@ -153,9 +153,9 @@ public class PayResponse { //代理端口 httpConfigStorage.setHttpProxyPort(3308); //代理用户名 - httpConfigStorage.setHttpProxyUsername("user"); + httpConfigStorage.setAuthUsername("user"); //代理密码 - httpConfigStorage.setHttpProxyPassword("password"); + httpConfigStorage.setAuthPassword("password"); */ //设置ssl证书路径 diff --git a/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/AliPayController.java b/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/AliPayController.java index 60794cb..38a58f8 100644 --- a/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/AliPayController.java +++ b/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/AliPayController.java @@ -48,7 +48,7 @@ public class AliPayController { public void init() { AliPayConfigStorage aliPayConfigStorage = new AliPayConfigStorage(); aliPayConfigStorage.setPid("2088102169916436"); - aliPayConfigStorage.setAppId("2016080400165436"); + aliPayConfigStorage.setAppid("2016080400165436"); aliPayConfigStorage.setKeyPublic("MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDIgHnOn7LLILlKETd6BFRJ0GqgS2Y3mn1wMQmyh9zEyWlz5p1zrahRahbXAfCfSqshSNfqOmAQzSHRVjCqjsAw1jyqrXaPdKBmr90DIpIxmIyKXv4GGAkPyJ/6FTFY99uhpiq0qadD/uSzQsefWo0aTvP/65zi3eof7TcZ32oWpwIDAQAB"); aliPayConfigStorage.setKeyPrivate("MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBAKroe/8h5vC4L6T+B2WdXiVwGsMvUKgb2XsKix6VY3m2wcf6tyzpNRDCNykbIwGtaeo7FshN+qZxdXHLiIam9goYncBit/8ojfLGy2gLxO/PXfzGxYGs0KsDZ+ryVPPmE34ZZ8jiJpR0ygzCFl8pN3QJPJRGTJn5+FTT9EF/9zyZAgMBAAECgYAktngcYC35u7cQXDk+jMVyiVhWYU2ULxdSpPspgLGzrZyG1saOcTIi/XVX8Spd6+B6nmLQeF/FbU3rOeuD8U2clzul2Z2YMbJ0FYay9oVZFfp5gTEFpFRTVfzqUaZQBIjJe/xHL9kQVqc5xHlE/LVA27/Kx3dbC35Y7B4EVBDYAQJBAOhsX8ZreWLKPhXiXHTyLmNKhOHJc+0tFH7Ktise/0rNspojU7o9prOatKpNylp9v6kux7migcMRdVUWWiVe+4ECQQC8PqsuEz7B0yqirQchRg1DbHjh64bw9Kj82EN1/NzOUd53tP9tg+SO97EzsibK1F7tOcuwqsa7n2aY48mQ+y0ZAkBndA2xcRcnvOOjtAz5VO8G7R12rse181HjGfG6AeMadbKg30aeaGCyIxN1loiSfNR5xsPJwibGIBg81mUrqzqBAkB+K6rkaPXJR9XtzvdWb/N3235yPkDlw7Z4MiOVM3RzvR/VMDV7m8lXoeDde2zQyeMOMYy6ztwA6WgE1bhGOnQRAkEAouUBv1sVdSBlsexX15qphOmAevzYrpufKgJIRLFWQxroXMS7FTesj+f+FmGrpPCxIde1dqJ8lqYLTyJmbzMPYw=="); aliPayConfigStorage.setNotifyUrl("http://pay.egzosn.com/payBack.json"); diff --git a/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/UnionPayController.java b/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/UnionPayController.java index 67792e4..12508da 100644 --- a/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/UnionPayController.java +++ b/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/UnionPayController.java @@ -3,6 +3,7 @@ package com.egzosn.pay.demo.controller; import com.egzosn.pay.common.api.PayService; +import com.egzosn.pay.common.bean.CertStoreType; import com.egzosn.pay.common.bean.MethodType; import com.egzosn.pay.common.bean.PayOrder; import com.egzosn.pay.common.bean.RefundOrder; @@ -44,20 +45,22 @@ public class UnionPayController { public void init() { UnionPayConfigStorage unionPayConfigStorage = new UnionPayConfigStorage(); unionPayConfigStorage.setMerId("700000000000001"); - //设置CertSign必须在设置证书前 + //是否为证书签名 unionPayConfigStorage.setCertSign(true); - //公钥,验签证书链格式: 中级证书路径;根证书路径 -// unionPayConfigStorage.setKeyPublic("D:/certs/acp_test_middle.cer;D:/certs/acp_test_root.cer"); //中级证书路径 unionPayConfigStorage.setAcpMiddleCert("D:/certs/acp_test_middle.cer"); //根证书路径 unionPayConfigStorage.setAcpRootCert("D:/certs/acp_test_root.cer"); - //私钥, 私钥证书格式: 私钥证书路径;私钥证书对应的密码 -// unionPayConfigStorage.setKeyPrivate("D:/certs/acp_test_sign.pfx;000000"); // 私钥证书路径 unionPayConfigStorage.setKeyPrivateCert("D:/certs/acp_test_sign.pfx"); //私钥证书对应的密码 unionPayConfigStorage.setKeyPrivateCertPwd("000000"); + //设置证书对应的存储方式,这里默认为文件地址 + unionPayConfigStorage.setCertStoreType(CertStoreType.PATH); + + + + //前台通知网址 即SDKConstants.param_frontUrl unionPayConfigStorage.setReturnUrl("http://www.pay.egzosn.com/payBack.json"); //后台通知地址 即SDKConstants.param_backUrl diff --git a/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/WxPayController.java b/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/WxPayController.java index 34c90a1..0e5b377 100644 --- a/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/WxPayController.java +++ b/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/WxPayController.java @@ -79,7 +79,8 @@ public class WxPayController { // httpConfigStorage.setKeystore(WxPayController.class.getResourceAsStream("/证书文件")); httpConfigStorage.setKeystore(KEYSTORE); httpConfigStorage.setStorePassword(STORE_PASSWORD); - httpConfigStorage.setPath(true); + //设置ssl证书对应的存储方式,这里默认为文件地址 + httpConfigStorage.setCertStoreType(CertStoreType.PATH); } 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 3bbae60..ca0c1e9 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 @@ -34,9 +34,9 @@ public class ApyAccountRepository { // TODO 2017/2/9 16:20 author: egan sign_type只有单一key时public_key与private_key相等,比如sign_type=MD5的情况 apyAccount1.setPublicKey("MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDIgHnOn7LLILlKETd6BFRJ0GqgS2Y3mn1wMQmyh9zEyWlz5p1zrahRahbXAfCfSqshSNfqOmAQzSHRVjCqjsAw1jyqrXaPdKBmr90DIpIxmIyKXv4GGAkPyJ/6FTFY99uhpiq0qadD/uSzQsefWo0aTvP/65zi3eof7TcZ32oWpwIDAQAB"); apyAccount1.setPrivateKey("MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBAKroe/8h5vC4L6T+B2WdXiVwGsMvUKgb2XsKix6VY3m2wcf6tyzpNRDCNykbIwGtaeo7FshN+qZxdXHLiIam9goYncBit/8ojfLGy2gLxO/PXfzGxYGs0KsDZ+ryVPPmE34ZZ8jiJpR0ygzCFl8pN3QJPJRGTJn5+FTT9EF/9zyZAgMBAAECgYAktngcYC35u7cQXDk+jMVyiVhWYU2ULxdSpPspgLGzrZyG1saOcTIi/XVX8Spd6+B6nmLQeF/FbU3rOeuD8U2clzul2Z2YMbJ0FYay9oVZFfp5gTEFpFRTVfzqUaZQBIjJe/xHL9kQVqc5xHlE/LVA27/Kx3dbC35Y7B4EVBDYAQJBAOhsX8ZreWLKPhXiXHTyLmNKhOHJc+0tFH7Ktise/0rNspojU7o9prOatKpNylp9v6kux7migcMRdVUWWiVe+4ECQQC8PqsuEz7B0yqirQchRg1DbHjh64bw9Kj82EN1/NzOUd53tP9tg+SO97EzsibK1F7tOcuwqsa7n2aY48mQ+y0ZAkBndA2xcRcnvOOjtAz5VO8G7R12rse181HjGfG6AeMadbKg30aeaGCyIxN1loiSfNR5xsPJwibGIBg81mUrqzqBAkB+K6rkaPXJR9XtzvdWb/N3235yPkDlw7Z4MiOVM3RzvR/VMDV7m8lXoeDde2zQyeMOMYy6ztwA6WgE1bhGOnQRAkEAouUBv1sVdSBlsexX15qphOmAevzYrpufKgJIRLFWQxroXMS7FTesj+f+FmGrpPCxIde1dqJ8lqYLTyJmbzMPYw==\n"); - apyAccount1.setNotifyUrl("http://pay.egan.in/payBack1.json"); + apyAccount1.setNotifyUrl("http://pay.egzosn.com/payBack1.json"); // 无需同步回调可不填 - apyAccount1.setReturnUrl("http://pay.egan.in/payBack1.json"); + apyAccount1.setReturnUrl("http://pay.egzosn.com/payBack1.json"); apyAccount1.setInputCharset("UTF-8"); apyAccount1.setSeller("2088102169916436"); apyAccount1.setSignType(SignUtils.RSA.name()); 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 2f1e868..7525fb2 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 @@ -5,6 +5,7 @@ import com.egzosn.pay.ali.api.AliPayService; import com.egzosn.pay.ali.bean.AliTransactionType; import com.egzosn.pay.common.api.PayService; import com.egzosn.pay.common.bean.BasePayType; +import com.egzosn.pay.common.bean.CertStoreType; import com.egzosn.pay.common.bean.MsgType; import com.egzosn.pay.common.bean.TransactionType; import com.egzosn.pay.common.http.HttpConfigStorage; @@ -51,7 +52,7 @@ public enum PayType implements BasePayType { //配置的附加参数的使用 configStorage.setAttach(apyAccount.getPayId()); configStorage.setPid(apyAccount.getPartner()); - configStorage.setAppId(apyAccount.getAppid()); + configStorage.setAppid(apyAccount.getAppid()); configStorage.setKeyPublic(apyAccount.getPublicKey()); configStorage.setKeyPrivate(apyAccount.getPrivateKey()); configStorage.setNotifyUrl(apyAccount.getNotifyUrl()); @@ -103,8 +104,8 @@ public enum PayType implements BasePayType { // httpConfigStorage.setKeystore(PayType.class.getResourceAsStream("/证书文件")); httpConfigStorage.setKeystore("证书信息串"); httpConfigStorage.setStorePassword("证书密码"); - //是否为证书地址 - httpConfigStorage.setPath(false); + //设置ssl证书对应的存储方式,这里默认为文件地址 + httpConfigStorage.setCertStoreType(CertStoreType.PATH); return new WxPayService(wxPayConfigStorage, httpConfigStorage);*/ return new WxPayService(wxPayConfigStorage); } @@ -179,8 +180,20 @@ public enum PayType implements BasePayType { UnionPayConfigStorage unionPayConfigStorage = new UnionPayConfigStorage(); unionPayConfigStorage.setMerId(apyAccount.getPartner()); unionPayConfigStorage.setCertSign(true); - unionPayConfigStorage.setKeyPublic(apyAccount.getPublicKey()); - unionPayConfigStorage.setKeyPrivate(apyAccount.getPrivateKey()); +// unionPayConfigStorage.setKeyPublic(apyAccount.getPublicKey()); +// unionPayConfigStorage.setKeyPrivate(apyAccount.getPrivateKey()); + + //中级证书路径 + unionPayConfigStorage.setAcpMiddleCert("D:/certs/acp_test_middle.cer"); + //根证书路径 + unionPayConfigStorage.setAcpRootCert("D:/certs/acp_test_root.cer"); + // 私钥证书路径 + unionPayConfigStorage.setKeyPrivateCert("D:/certs/acp_test_sign.pfx"); + //私钥证书对应的密码 + unionPayConfigStorage.setKeyPrivateCertPwd("000000"); + //设置证书对应的存储方式,这里默认为文件地址 + unionPayConfigStorage.setCertStoreType(CertStoreType.PATH); + unionPayConfigStorage.setNotifyUrl(apyAccount.getNotifyUrl()); unionPayConfigStorage.setReturnUrl(apyAccount.getReturnUrl()); unionPayConfigStorage.setSignType(apyAccount.getSignType()); diff --git a/pay-java-union/README.md b/pay-java-union/README.md index 779c9b9..177c585 100644 --- a/pay-java-union/README.md +++ b/pay-java-union/README.md @@ -8,22 +8,23 @@ UnionPayConfigStorage unionPayConfigStorage = new UnionPayConfigStorage(); unionPayConfigStorage.setMerId("700000000000001"); - //设置CertSign必须在设置证书前 + //是否为证书签名 unionPayConfigStorage.setCertSign(true); - //公钥,验签证书链格式: 中级证书路径;根证书路径 -// unionPayConfigStorage.setKeyPublic("D:/certs/acp_test_middle.cer;D:/certs/acp_test_root.cer"); + //中级证书路径 - unionPayConfigStorage.setAcpMiddleCert("D:/certs/acp_test_middle.cer"); + unionPayConfigStorage.setAcpMiddleCert("证书文件流,证书字符串信息或证书绝对地址"); //根证书路径 - unionPayConfigStorage.setAcpRootCert("D:/certs/acp_test_root.cer"); - - //私钥, 私钥证书格式: 私钥证书路径;私钥证书对应的密码 -// unionPayConfigStorage.setKeyPrivate("D:/certs/acp_test_sign.pfx;000000"); + unionPayConfigStorage.setAcpRootCert("证书文件流,证书字符串信息或证书绝对地址"); // 私钥证书路径 - unionPayConfigStorage.setKeyPrivateCert("D:/certs/acp_test_sign.pfx"); + unionPayConfigStorage.setKeyPrivateCert("证书文件流,证书字符串信息或证书绝对地址"); //私钥证书对应的密码 - unionPayConfigStorage.setKeyPrivateCertPwd("000000"); - + unionPayConfigStorage.setKeyPrivateCertPwd("私钥证书对应的密码"); + //设置证书对应的存储方式,这里默认为文件地址 + httpConfigStorage.setCertStoreType(CertStoreType.PATH); + + + + unionPayConfigStorage.setNotifyUrl("http://www.pay.egzosn.com/payBack.json"); // 无需同步回调可不填 app填这个就可以 unionPayConfigStorage.setReturnUrl("http://www.pay.egzosn.com/payBack.json"); @@ -48,9 +49,9 @@ //代理端口 httpConfigStorage.setHttpProxyPort(3308); //代理用户名 - httpConfigStorage.setHttpProxyUsername("user"); + httpConfigStorage.setAuthUsername("user"); //代理密码 - httpConfigStorage.setHttpProxyPassword("password"); + httpConfigStorage.setAuthPassword("password"); /* /网路代理配置 根据需求进行设置**/ /* /网络请求连接池**/ diff --git a/pay-java-union/src/main/java/com/egzosn/pay/union/api/UnionPayConfigStorage.java b/pay-java-union/src/main/java/com/egzosn/pay/union/api/UnionPayConfigStorage.java index 5ae5003..24864d3 100644 --- a/pay-java-union/src/main/java/com/egzosn/pay/union/api/UnionPayConfigStorage.java +++ b/pay-java-union/src/main/java/com/egzosn/pay/union/api/UnionPayConfigStorage.java @@ -1,6 +1,10 @@ package com.egzosn.pay.union.api; import com.egzosn.pay.common.api.BasePayConfigStorage; +import com.egzosn.pay.common.bean.CertStoreType; + +import java.io.IOException; +import java.io.InputStream; /** @@ -32,105 +36,101 @@ public class UnionPayConfigStorage extends BasePayConfigStorage { */ private String accessType = "0"; + /** - * 中级证书路径 + * 应用私钥证书 */ - private String acpMiddleCert; + private Object keyPrivateCert; + /** - * 根证书路径 + * 中级证书 */ - private String acpRootCert; - + private Object acpMiddleCert; /** - * 私钥证书是否已经初始化 - * 默认没有 + * 根证书 */ - private boolean keyPrivateInit = false; + private Object acpRootCert; /** - * 公钥证书是否已经初始化 - * 默认没有 + * 证书存储类型 */ - private boolean keyPublicInit = false; - - + private CertStoreType certStoreType; /** * 设置私钥证书 * - * @param certificatePath 私钥证书地址 + * @param certificate 私钥证书地址 或者证书内容字符串 * 私钥证书密码 {@link #setKeyPrivateCertPwd(String)} */ - public void setKeyPrivateCert(String certificatePath) { - super.setKeyPrivate(certificatePath); + public void setKeyPrivateCert(String certificate) { + super.setKeyPrivate(certificate); + this.keyPrivateCert = certificate; + } + /** + * 设置私钥证书 + * + * @param keyPrivateCert 私钥证书信息流 + * 私钥证书密码 {@link #setKeyPrivateCertPwd(String)} + */ + public void setKeyPrivateCert(InputStream keyPrivateCert) { + this.keyPrivateCert = keyPrivateCert; + } + + public InputStream getKeyPrivateCertInputStream() throws IOException { + return certStoreType.getInputStream(keyPrivateCert); } /** * 设置中级证书 * - * @param certificatePath 证书地址 + * @param acpMiddleCert 证书信息或者证书路径 */ - public void setAcpMiddleCert(String certificatePath) { - this.acpMiddleCert = certificatePath; + public void setAcpMiddleCert(String acpMiddleCert) { + this.acpMiddleCert = acpMiddleCert; + } + /** + * 设置中级证书 + * + * @param acpMiddleCert 证书文件 + */ + public void setAcpMiddleCert(InputStream acpMiddleCert) { + this.acpMiddleCert = acpMiddleCert; } /** - * 设置根证书路径 + * 设置根证书 + * + * @param acpRootCert 证书路径或者证书信息字符串 + */ + public void setAcpRootCert(String acpRootCert) { + this.acpRootCert = acpRootCert; + } + /** + * 设置根证书 * - * @param certificatePath 证书路径 + * @param acpRootCert 证书文件流 */ - public void setAcpRootCert(String certificatePath) { - this.acpRootCert = certificatePath; + public void setAcpRootCert(InputStream acpRootCert) { + this.acpRootCert = acpRootCert; } public String getAcpMiddleCert() { - return acpMiddleCert; + return (String) acpMiddleCert; } public String getAcpRootCert() { - return acpRootCert; + return (String) acpRootCert; } - - /** - * 设置私钥证书与证书密码 - * - * @param keyPrivate 私钥证书与证书对应的密码 格式: D:/certs/acp_test_sign.pfx;000000 - * 替代方法 - * {@link #setKeyPrivateCert(String)} - * {@link #setKeyPrivateCertPwd(String)} - */ - @Deprecated - @Override - public void setKeyPrivate(String keyPrivate) { - super.setKeyPrivate(keyPrivate); - if (isCertSign() && keyPrivate.length() < 1024 && keyPrivate.contains(";")) { - String[] split = keyPrivate.split(";"); - super.setKeyPrivateCertPwd(split[1]); - super.setKeyPrivate(split[0]); - getCertDescriptor().initPrivateSignCert(getKeyPrivate(), getKeyPrivateCertPwd(), "PKCS12"); - keyPrivateInit = true; - } + public InputStream getAcpMiddleCertInputStream() throws IOException { + return certStoreType.getInputStream(acpMiddleCert); } - /** - * 设置中级证书与根证书 格式:D:/certs/acp_test_middle.cer;D:/certs/acp_test_root.cer - * - * @param keyPublic 中级证书与根证书 - * 替代方法 - * {@link #setAcpRootCert(String)} - * {@link #setAcpMiddleCert(String)} - */ - @Deprecated - @Override - public void setKeyPublic(String keyPublic) { - super.setKeyPublic(keyPublic); - if (isCertSign() && keyPublic.length() < 1024) { - String[] split = keyPublic.split(";"); - getCertDescriptor().initPublicCert(split[0]); - getCertDescriptor().initRootCert(split[1]); - keyPublicInit = true; - } + public InputStream getAcpRootCertInputStream() throws IOException { + return certStoreType.getInputStream(acpRootCert); } + + + @Override public String getAppid() { return null; @@ -199,11 +199,15 @@ public class UnionPayConfigStorage extends BasePayConfigStorage { this.accessType = accessType; } - public boolean isKeyPrivateInit() { - return keyPrivateInit; + /** + * 证书存储类型 + * @return 证书存储类型 + */ + public CertStoreType getCertStoreType() { + return certStoreType; } - public boolean isKeyPublicInit() { - return keyPublicInit; + public void setCertStoreType(CertStoreType certStoreType) { + this.certStoreType = certStoreType; } } diff --git a/pay-java-union/src/main/java/com/egzosn/pay/union/api/UnionPayService.java b/pay-java-union/src/main/java/com/egzosn/pay/union/api/UnionPayService.java index dab9c79..f44d12f 100644 --- a/pay-java-union/src/main/java/com/egzosn/pay/union/api/UnionPayService.java +++ b/pay-java-union/src/main/java/com/egzosn/pay/union/api/UnionPayService.java @@ -21,6 +21,7 @@ import com.egzosn.pay.union.bean.UnionTransactionType; import java.awt.image.BufferedImage; import java.io.ByteArrayInputStream; +import java.io.IOException; import java.io.InputStream; import java.math.BigDecimal; import java.security.cert.*; @@ -55,7 +56,10 @@ public class UnionPayService extends BasePayService { private static final String FILE_TRANS_URL = "https://filedownload.%s/"; private static final String APP_TRANS_URL = "https://gateway.%s/gateway/api/appTransReq.do"; private static final String CARD_TRANS_URL = "https://gateway.%s/gateway/api/cardTransReq.do"; - + /** + * 证书解释器 + */ + private CertDescriptor certDescriptor = new CertDescriptor(); /** * 构造函数 * @@ -81,15 +85,15 @@ public class UnionPayService extends BasePayService { if (!payConfigStorage.isCertSign()) { return this; } - CertDescriptor certDescriptor = payConfigStorage.getCertDescriptor(); - if (!payConfigStorage.isKeyPrivateInit()) { - certDescriptor.initPrivateSignCert(payConfigStorage.getKeyPrivate(), payConfigStorage.getKeyPrivateCertPwd(), "PKCS12"); - } - if (!payConfigStorage.isKeyPublicInit()) { - certDescriptor.initPublicCert(payConfigStorage.getAcpMiddleCert()); - certDescriptor.initRootCert(payConfigStorage.getAcpRootCert()); + try { + certDescriptor.initPrivateSignCert(payConfigStorage.getKeyPrivateCertInputStream(), payConfigStorage.getKeyPrivateCertPwd(), "PKCS12"); + certDescriptor.initPublicCert(payConfigStorage.getAcpMiddleCertInputStream()); + certDescriptor.initRootCert(payConfigStorage.getAcpRootCertInputStream()); + } catch (IOException e) { + LOG.error(e); } + return this; } @@ -127,7 +131,7 @@ public class UnionPayService extends BasePayService { */ private Map getCommonParam() { Map params = new TreeMap<>(); - UnionPayConfigStorage configStorage = (UnionPayConfigStorage) payConfigStorage; + UnionPayConfigStorage configStorage = payConfigStorage; //银联接口版本 params.put(SDKConstants.param_version, configStorage.getVersion()); //编码方式 @@ -291,15 +295,15 @@ public class UnionPayService extends BasePayService { switch (signUtils) { case RSA: parameters.put(SDKConstants.param_signMethod, SDKConstants.SIGNMETHOD_RSA); - parameters.put(SDKConstants.param_certId, payConfigStorage.getCertDescriptor().getSignCertId()); + parameters.put(SDKConstants.param_certId, certDescriptor.getSignCertId()); signStr = SignUtils.SHA1.createSign(SignUtils.parameterText(parameters, "&", "signature"), "", payConfigStorage.getInputCharset()); - parameters.put(SDKConstants.param_signature, RSA.sign(signStr, payConfigStorage.getCertDescriptor().getSignCertPrivateKey(payConfigStorage.getKeyPrivateCertPwd()), payConfigStorage.getInputCharset())); + parameters.put(SDKConstants.param_signature, RSA.sign(signStr, certDescriptor.getSignCertPrivateKey(payConfigStorage.getKeyPrivateCertPwd()), payConfigStorage.getInputCharset())); break; case RSA2: parameters.put(SDKConstants.param_signMethod, SDKConstants.SIGNMETHOD_RSA); - parameters.put(SDKConstants.param_certId, payConfigStorage.getCertDescriptor().getSignCertId()); + parameters.put(SDKConstants.param_certId, certDescriptor.getSignCertId()); signStr = SignUtils.SHA256.createSign(SignUtils.parameterText(parameters, "&", "signature"), "", payConfigStorage.getInputCharset()); - parameters.put(SDKConstants.param_signature, RSA2.sign(signStr, payConfigStorage.getCertDescriptor().getSignCertPrivateKey(payConfigStorage.getKeyPrivateCertPwd()), payConfigStorage.getInputCharset())); + parameters.put(SDKConstants.param_signature, RSA2.sign(signStr, certDescriptor.getSignCertPrivateKey(payConfigStorage.getKeyPrivateCertPwd()), payConfigStorage.getInputCharset())); break; case SHA1: case SHA256: @@ -326,8 +330,8 @@ public class UnionPayService extends BasePayService { private X509Certificate verifyCertificate(X509Certificate cert) { try { cert.checkValidity();//验证有效期 - X509Certificate middleCert = payConfigStorage.getCertDescriptor().getPublicCert(); - X509Certificate rootCert = payConfigStorage.getCertDescriptor().getRootCert(); + X509Certificate middleCert = certDescriptor.getPublicCert(); + X509Certificate rootCert = certDescriptor.getRootCert(); X509CertSelector selector = new X509CertSelector(); selector.setCertificate(cert); diff --git a/pay-java-wx/README.md b/pay-java-wx/README.md index 7ac3b85..3603cf1 100644 --- a/pay-java-wx/README.md +++ b/pay-java-wx/README.md @@ -29,9 +29,9 @@ //代理端口 httpConfigStorage.setHttpProxyPort(3308); //代理用户名 - httpConfigStorage.setHttpProxyUsername("user"); + httpConfigStorage.setAuthUsername("user"); //代理密码 - httpConfigStorage.setHttpProxyPassword("password"); + httpConfigStorage.setAuthPassword("password"); /* /网路代理配置 根据需求进行设置**/ //退款使用 @@ -39,9 +39,12 @@ //设置ssl证书路径 //TODO 这里也支持输入流的入参。 // httpConfigStorage.setKeystore(this.getClass()..getResourceAsStream("/证书文件")); - httpConfigStorage.setKeystorePath("证书绝对路径"); + //设置ssl证书路径 跟着setCertStoreType 进行对应 + httpConfigStorage.setKeystore("证书文件流,证书字符串信息或证书绝对地址"); //设置ssl证书对应的密码 httpConfigStorage.setStorePassword("证书对应的密码"); + //设置ssl证书对应的存储方式 + httpConfigStorage.setCertStoreType(CertStoreType.PATH); /* /网络请求ssl证书**/ /* /网络请求连接池**/ //最大连接数 -- Gitee From 14b8c5185f0d471331897b162fab6eaa65e17768 Mon Sep 17 00:00:00 2001 From: egzosn Date: Mon, 15 Apr 2019 14:12:46 +0800 Subject: [PATCH 014/299] =?UTF-8?q?appid=E7=BB=9F=E4=B8=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pay-java-ali/src/test/java/PayTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pay-java-ali/src/test/java/PayTest.java b/pay-java-ali/src/test/java/PayTest.java index 639cdbf..13d496e 100644 --- a/pay-java-ali/src/test/java/PayTest.java +++ b/pay-java-ali/src/test/java/PayTest.java @@ -24,7 +24,7 @@ public class PayTest { AliPayConfigStorage aliPayConfigStorage = new AliPayConfigStorage(); aliPayConfigStorage.setPid("合作者id"); - aliPayConfigStorage.setAppId("应用id"); + aliPayConfigStorage.setAppid("应用id"); aliPayConfigStorage.setKeyPublic("支付宝公钥"); aliPayConfigStorage.setKeyPrivate("应用私钥"); aliPayConfigStorage.setNotifyUrl("异步回调地址"); -- Gitee From 100b9aecb73da8da227fc931fc05211d43229a56 Mon Sep 17 00:00:00 2001 From: egzosn Date: Mon, 15 Apr 2019 20:25:38 +0800 Subject: [PATCH 015/299] =?UTF-8?q?=E6=96=B0=E5=A2=9E=E5=88=B7=E8=84=B8?= =?UTF-8?q?=E6=94=AF=E4=BB=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../main/java/com/egzosn/pay/wx/api/WxPayService.java | 4 +++- .../main/java/com/egzosn/pay/wx/bean/WxPayError.java | 2 +- .../com/egzosn/pay/wx/bean/WxTransactionType.java | 11 +++++++++++ 3 files changed, 15 insertions(+), 2 deletions(-) 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 abc52f0..eb55f27 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 @@ -211,7 +211,9 @@ public class WxPayService extends BasePayService { setSign(parameters); String requestXML = XML.getMap2Xml(parameters); - LOG.debug("requestXML:" + requestXML); + if (LOG.isDebugEnabled()){ + LOG.debug("requestXML:" + requestXML); + } //调起支付的参数列表 JSONObject result = requestTemplate.postForObject(getUrl(order.getTransactionType()), requestXML, JSONObject.class); diff --git a/pay-java-wx/src/main/java/com/egzosn/pay/wx/bean/WxPayError.java b/pay-java-wx/src/main/java/com/egzosn/pay/wx/bean/WxPayError.java index b3655d4..ab86676 100644 --- a/pay-java-wx/src/main/java/com/egzosn/pay/wx/bean/WxPayError.java +++ b/pay-java-wx/src/main/java/com/egzosn/pay/wx/bean/WxPayError.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2017 the original huodull or egan. + * Copyright 2002-2017 the original egan or egan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. 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 2ce5aaa..80787a9 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 @@ -47,6 +47,17 @@ public enum WxTransactionType implements TransactionType { * 移动支付 */ APP("pay/unifiedorder"), + /** + * 刷 + 脸支付 + */ + FACEPAY("pay/facepay"){ + @Override + public void setAttribute(Map parameters, PayOrder order) { + parameters.put("openid", order.getOpenid()); + parameters.put("face_code", order.getAuthCode()); + } + }, /** * H5支付 */ -- Gitee From d20ae41ab0837f5376a46cd0f8db0ec3d099316e Mon Sep 17 00:00:00 2001 From: egzosn Date: Mon, 15 Apr 2019 20:26:19 +0800 Subject: [PATCH 016/299] =?UTF-8?q?=E4=BB=A3=E7=A0=81=E6=95=B4=E7=90=86?= =?UTF-8?q?=E4=B8=8E=E4=BC=98=E5=8C=96=EF=BC=8C=E5=88=B7=E8=84=B8=E6=94=AF?= =?UTF-8?q?=E4=BB=98=E6=A1=88=E4=BE=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/egzosn/pay/common/api/Callback.java | 2 +- .../com/egzosn/pay/common/bean/MsgType.java | 2 +- .../com/egzosn/pay/common/bean/PayOrder.java | 2 +- .../pay/common/bean/result/PayException.java | 2 +- .../pay/demo/controller/AliPayController.java | 4 +-- .../pay/demo/controller/PayController.java | 2 +- .../demo/controller/UnionPayController.java | 2 +- .../pay/demo/controller/WxPayController.java | 32 ++++++++++++++++++- .../pay/demo/dao/ApyAccountRepository.java | 2 +- .../com/egzosn/pay/demo/entity/PayType.java | 5 ++- .../egzosn/pay/demo/service/PayResponse.java | 2 +- pay-java-payoneer/test/java/PayTest.java | 6 ++-- .../pay/wx/youdian/bean/YdPayError.java | 2 +- pom.xml | 1 - 14 files changed, 49 insertions(+), 17 deletions(-) diff --git a/pay-java-common/src/main/java/com/egzosn/pay/common/api/Callback.java b/pay-java-common/src/main/java/com/egzosn/pay/common/api/Callback.java index fb519ae..dbb537b 100644 --- a/pay-java-common/src/main/java/com/egzosn/pay/common/api/Callback.java +++ b/pay-java-common/src/main/java/com/egzosn/pay/common/api/Callback.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2017 the original huodull or egan. + * Copyright 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. diff --git a/pay-java-common/src/main/java/com/egzosn/pay/common/bean/MsgType.java b/pay-java-common/src/main/java/com/egzosn/pay/common/bean/MsgType.java index c003040..8806133 100644 --- a/pay-java-common/src/main/java/com/egzosn/pay/common/bean/MsgType.java +++ b/pay-java-common/src/main/java/com/egzosn/pay/common/bean/MsgType.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2017 the original huodull or egan. + * Copyright 2002-2017 the original egan or egan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. 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 753054d..8e64f60 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 @@ -46,7 +46,7 @@ public class PayOrder { */ private String spbillCreateIp; /** - * 付款条码串 与设备号类似??? + * 付款条码串,人脸凭证,有关支付代码相关的, */ private String authCode; /** diff --git a/pay-java-common/src/main/java/com/egzosn/pay/common/bean/result/PayException.java b/pay-java-common/src/main/java/com/egzosn/pay/common/bean/result/PayException.java index db26fba..e54abc6 100644 --- a/pay-java-common/src/main/java/com/egzosn/pay/common/bean/result/PayException.java +++ b/pay-java-common/src/main/java/com/egzosn/pay/common/bean/result/PayException.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2017 the original huodull or egan. + * Copyright 2002-2017 the original egan or egan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/AliPayController.java b/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/AliPayController.java index 38a58f8..60bf23d 100644 --- a/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/AliPayController.java +++ b/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/AliPayController.java @@ -136,9 +136,9 @@ public class AliPayController { public Map microPay(BigDecimal price, String authCode) throws IOException { //获取对应的支付账户操作工具(可根据账户id) //条码付 - PayOrder order = new PayOrder("huodull order", "huodull order", null == price ? new BigDecimal(0.01) : price, UUID.randomUUID().toString().replace("-", ""), AliTransactionType.BAR_CODE); + PayOrder order = new PayOrder("egan order", "egan order", null == price ? new BigDecimal(0.01) : price, UUID.randomUUID().toString().replace("-", ""), AliTransactionType.BAR_CODE); //声波付 -// PayOrder order = new PayOrder("huodull order", "huodull order", null == price ? new BigDecimal(0.01) : price, UUID.randomUUID().toString().replace("-", ""), AliTransactionType.WAVE_CODE); +// PayOrder order = new PayOrder("egan order", "egan order", null == price ? new BigDecimal(0.01) : price, UUID.randomUUID().toString().replace("-", ""), AliTransactionType.WAVE_CODE); //设置授权码,条码等 order.setAuthCode(authCode); //支付结果 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 821ddc9..e7c2afa 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 @@ -181,7 +181,7 @@ public class PayController { //获取对应的支付账户操作工具(可根据账户id) PayResponse payResponse = service.getPayResponse(payId); - PayOrder order = new PayOrder("huodull order", "huodull order", null == price ? new BigDecimal(0.01) : price, UUID.randomUUID().toString().replace("-", ""), PayType.valueOf(payResponse.getStorage().getPayType()).getTransactionType(transactionType)); + PayOrder order = new PayOrder("egan order", "egan order", null == price ? new BigDecimal(0.01) : price, UUID.randomUUID().toString().replace("-", ""), PayType.valueOf(payResponse.getStorage().getPayType()).getTransactionType(transactionType)); //设置授权码,条码等 order.setAuthCode(authCode); //支付结果 diff --git a/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/UnionPayController.java b/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/UnionPayController.java index 12508da..5ab99de 100644 --- a/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/UnionPayController.java +++ b/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/UnionPayController.java @@ -167,7 +167,7 @@ public class UnionPayController { public Map microPay(BigDecimal price, String authCode) throws IOException { //获取对应的支付账户操作工具(可根据账户id) //条码付 - PayOrder order = new PayOrder("huodull order", "huodull order", null == price ? new BigDecimal(0.01) : price, SignUtils.randomStr(), UnionTransactionType.CONSUME); + PayOrder order = new PayOrder("egan order", "egan order", null == price ? new BigDecimal(0.01) : price, SignUtils.randomStr(), UnionTransactionType.CONSUME); //设置授权码,条码等 order.setAuthCode(authCode); //支付结果 diff --git a/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/WxPayController.java b/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/WxPayController.java index 0e5b377..551bb84 100644 --- a/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/WxPayController.java +++ b/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/WxPayController.java @@ -182,7 +182,7 @@ public class WxPayController { public Map microPay( BigDecimal price, String authCode) throws IOException { //获取对应的支付账户操作工具(可根据账户id) //条码付 - PayOrder order = new PayOrder("huodull order", "huodull order", null == price ? new BigDecimal(0.01) : price, UUID.randomUUID().toString().replace("-", ""), WxTransactionType.MICROPAY); + PayOrder order = new PayOrder("egan order", "egan order", null == price ? new BigDecimal(0.01) : price, UUID.randomUUID().toString().replace("-", ""), WxTransactionType.MICROPAY); //设置授权码,条码等 order.setAuthCode(authCode); //支付结果 @@ -199,6 +199,36 @@ public class WxPayController { return params; } + /** + * 刷卡付,pos主动扫码付款(条码付) + * @param price 金额 + * @param authCode 人脸凭证 + * @param openid 用户在商户 appid下的唯一标识 + * @return 支付结果 + */ + @RequestMapping(value = "facePay") + public Map facePay(BigDecimal price, String authCode, String openid) throws IOException { + //获取对应的支付账户操作工具(可根据账户id) + //条码付 + PayOrder order = new PayOrder("egan order", "egan order", null == price ? new BigDecimal(0.01) : price, UUID.randomUUID().toString().replace("-", ""), WxTransactionType.MICROPAY); + //设置授权码,条码等 + order.setAuthCode(authCode); + order.setOpenid(openid); + + //支付结果 + Map params = service.microPay(order); + //校验 + if (service.verify(params)) { + + //支付校验通过后的处理 + //......业务逻辑处理块........ + + + } + //这里开发者自行处理 + return params; + } + /** * 支付回调地址 方式一 * 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 ca0c1e9..42bc52b 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 @@ -102,7 +102,7 @@ public class ApyAccountRepository { ApyAccount apyAccount5 = new ApyAccount(); apyAccount5.setPayId(5); apyAccount5.setPartner("100086190");//Program ID - apyAccount5.setSeller("Huodull6190");//Username + apyAccount5.setSeller("egan6190");//Username apyAccount5.setStorePassword("12BkDT8152Zj");//API password apyAccount5.setInputCharset("UTF-8"); apyAccount5.setPayType(PayType.payoneer); 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 7525fb2..519e54e 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 @@ -9,6 +9,7 @@ import com.egzosn.pay.common.bean.CertStoreType; import com.egzosn.pay.common.bean.MsgType; import com.egzosn.pay.common.bean.TransactionType; import com.egzosn.pay.common.http.HttpConfigStorage; +import com.egzosn.pay.demo.service.handler.WxPayMessageHandler; import com.egzosn.pay.fuiou.api.FuiouPayConfigStorage; import com.egzosn.pay.fuiou.api.FuiouPayService; import com.egzosn.pay.fuiou.bean.FuiouTransactionType; @@ -107,7 +108,9 @@ public enum PayType implements BasePayType { //设置ssl证书对应的存储方式,这里默认为文件地址 httpConfigStorage.setCertStoreType(CertStoreType.PATH); return new WxPayService(wxPayConfigStorage, httpConfigStorage);*/ - return new WxPayService(wxPayConfigStorage); + WxPayService wxPayService = new WxPayService(wxPayConfigStorage); + wxPayService.setPayMessageHandler(new WxPayMessageHandler(1)); + return wxPayService; } /** 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 a5e9a41..f0fc931 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 @@ -68,7 +68,7 @@ public class PayResponse { // //代理密码 // httpConfigStorage.setHttpProxyPassword("password"); //设置ssl证书路径 https证书设置 方式二 - httpConfigStorage.setKeystorePath(apyAccount.getKeystorePath()); + httpConfigStorage.setKeystore(apyAccount.getKeystorePath()); //设置ssl证书对应的密码 httpConfigStorage.setStorePassword(apyAccount.getStorePassword()); return httpConfigStorage; diff --git a/pay-java-payoneer/test/java/PayTest.java b/pay-java-payoneer/test/java/PayTest.java index e19aa42..b591876 100644 --- a/pay-java-payoneer/test/java/PayTest.java +++ b/pay-java-payoneer/test/java/PayTest.java @@ -36,7 +36,7 @@ public class PayTest { if (1==1){ - String auth = Base64.encode("Huodull6190:12BkDT8152Zj".getBytes()); + String auth = Base64.encode("egan6190:12BkDT8152Zj".getBytes()); System.out.println(auth); return; } @@ -49,7 +49,7 @@ public class PayTest { CredentialsProvider credsProvider = new BasicCredentialsProvider(); credsProvider.setCredentials( new AuthScope(uri.getHost(), uri.getPort()), - new UsernamePasswordCredentials("Huodull6190", "12BkDT8152Zj")); + new UsernamePasswordCredentials("egan6190", "12BkDT8152Zj")); CloseableHttpClient httpclient = HttpClients.custom() .setDefaultCredentialsProvider(credsProvider) @@ -73,7 +73,7 @@ public class PayTest { // PayoneerRequestBean bean = new PayoneerRequestBean("666"); String referenceId = UUID.randomUUID().toString().replace("-", ""); - PayoneerRequestBean bean = new PayoneerRequestBean("8a2950f959043699015904453b330057","1.01", referenceId, CurType.USD,"huodull order"); + PayoneerRequestBean bean = new PayoneerRequestBean("8a2950f959043699015904453b330057","1.01", referenceId, CurType.USD,"egan order"); // PayoneerRequestBean bean = JSON.parseObject("{\"amount\":\"1.00\",\"client_reference_id\":\""+ System.nanoTime()+"\",\"currency\":\"USD\",\"description\":\"aaabb\",\"payee_id\":\"asdfg13\"}", PayoneerRequestBean.class); System.out.println(JSON.toJSONString(bean)); StringEntity entity = new StringEntity(JSON.toJSONString(bean), ContentType.APPLICATION_JSON); diff --git a/pay-java-wx-youdian/src/main/java/com/egzosn/pay/wx/youdian/bean/YdPayError.java b/pay-java-wx-youdian/src/main/java/com/egzosn/pay/wx/youdian/bean/YdPayError.java index 49cd9cf..b782a50 100644 --- a/pay-java-wx-youdian/src/main/java/com/egzosn/pay/wx/youdian/bean/YdPayError.java +++ b/pay-java-wx-youdian/src/main/java/com/egzosn/pay/wx/youdian/bean/YdPayError.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2017 the original huodull or egan. + * Copyright 2002-2017 the original egan or egan. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/pom.xml b/pom.xml index c4b08aa..643fa99 100644 --- a/pom.xml +++ b/pom.xml @@ -51,7 +51,6 @@ pay-java-union pay-java-payoneer pay-java-paypal - pay-java-pds pay-java-demo -- Gitee From 0ebb5e48f683fa7acc510a6352d5fa528260f01e Mon Sep 17 00:00:00 2001 From: egan Date: Mon, 15 Apr 2019 22:04:46 +0800 Subject: [PATCH 017/299] =?UTF-8?q?=E5=88=B7=E8=84=B8=E4=BB=98=E5=AF=B9?= =?UTF-8?q?=E5=BA=94=E6=A1=88=E4=BE=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 29 ++++--------------- .../pay/demo/controller/WxPayController.java | 10 +++---- pay-java-wx/README.md | 17 +++++++++++ 3 files changed, 26 insertions(+), 30 deletions(-) diff --git a/README.md b/README.md index ec86176..fa4a1fd 100644 --- a/README.md +++ b/README.md @@ -1,42 +1,23 @@ +全能支付Java开发工具包.优雅的轻量级支付模块集成支付对接支付整合(微信支付,支付宝,银联,友店,富友,跨境支付paypal,payoneer(P卡派安盈))app,扫码,即时到帐刷卡付条码付转账服务商模式、支持多种支付类型多支付账户,支付与业务完全剥离,简单几行代码即可实现支付,简单快速完成支付模块的开发,可轻松嵌入到任何系统里 目前仅是一个开发工具包(即SDK),只提供简单Web实现,建议使用maven或gradle引用本项目即可使用本SDK提供的各种支付相关的功能 -##整合支付模块 - - -##### 详细文档请看 [wiki](https://gitee.com/egzosn/pay-java-parent/wikis/Home)。 ### 特性 - - - 1. 不依赖任何 mvc 框架,依赖极少:httpclient,fastjson,log4j,com.google.zxing,项目精简,不用担心项目迁移问题 2. 也不依赖 servlet,仅仅作为工具使用,可轻松嵌入到任何系统里(项目例子利用spring mvc的 @PathVariable进行,推荐使用类似的框架) 3. 支付请求调用支持HTTP和异步、支持http代理,连接池 - 4. 控制层统一异常处理 - 5. LogBack日志记录 - 6. 简单快速完成支付模块的开发 - 7. 支持多种支付类型多支付账户扩展 + 4. 简单快速完成支付模块的开发 + 5. 支持多种支付类型多支付账户扩展 ### 本项目包含 3 个部分: 1. pay-java-common 公共lib,支付核心与规范定义 2. pay-java-demo 具体的支付demo 3. pay-java-* 具体的支付实现库 + ### Maven配置 -支付核心模块 -```xml - - - com.egzosn - pay-java-common - 2.12.6 - - -``` - 具体支付模块 "{module-name}" 为具体的支付渠道的模块名 pay-java-ali,pay-java-wx等 ```xml - com.egzosn {module-name} @@ -56,7 +37,7 @@ 这里不多说直接上代码 -###### 单一支付教程 +###### 支付教程 * [基础模块支付宝微信讲解](https://gitee.com/egzosn/pay-java-parent/wikis/Home) * [银联](pay-java-union?dir=1&filepath=pay-java-union) diff --git a/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/WxPayController.java b/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/WxPayController.java index 551bb84..7541917 100644 --- a/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/WxPayController.java +++ b/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/WxPayController.java @@ -200,7 +200,7 @@ public class WxPayController { } /** - * 刷卡付,pos主动扫码付款(条码付) + * 刷脸付 * @param price 金额 * @param authCode 人脸凭证 * @param openid 用户在商户 appid下的唯一标识 @@ -209,17 +209,15 @@ public class WxPayController { @RequestMapping(value = "facePay") public Map facePay(BigDecimal price, String authCode, String openid) throws IOException { //获取对应的支付账户操作工具(可根据账户id) - //条码付 - PayOrder order = new PayOrder("egan order", "egan order", null == price ? new BigDecimal(0.01) : price, UUID.randomUUID().toString().replace("-", ""), WxTransactionType.MICROPAY); - //设置授权码,条码等 + PayOrder order = new PayOrder("egan order", "egan order", null == price ? new BigDecimal(0.01) : price, UUID.randomUUID().toString().replace("-", ""), WxTransactionType.FACEPAY); + //设置人脸凭证 order.setAuthCode(authCode); + // 用户在商户 appid下的唯一标识 order.setOpenid(openid); - //支付结果 Map params = service.microPay(order); //校验 if (service.verify(params)) { - //支付校验通过后的处理 //......业务逻辑处理块........ diff --git a/pay-java-wx/README.md b/pay-java-wx/README.md index 3603cf1..94eb9c0 100644 --- a/pay-java-wx/README.md +++ b/pay-java-wx/README.md @@ -138,6 +138,23 @@ /*-----------/条码付 刷卡付-------------------*/ +``` +#### 刷脸付 + +```java + + /*-----------刷脸付-------------------*/ + //获取对应的支付账户操作工具(可根据账户id) + PayOrder order = new PayOrder("egan order", "egan order", null == price ? new BigDecimal(0.01) : price, UUID.randomUUID().toString().replace("-", ""), WxTransactionType.FACEPAY); + //设置人脸凭证 + order.setAuthCode(authCode); + // 用户在商户 appid下的唯一标识 + order.setOpenid(openid); + //支付结果 + Map params = service.microPay(order); + + /*-----------/刷脸付-------------------*/ + ``` #### 回调处理 -- Gitee From c5ce9edf1fdeae6ad6e34c0643c3e982dee32fe4 Mon Sep 17 00:00:00 2001 From: egan Date: Mon, 15 Apr 2019 23:52:20 +0800 Subject: [PATCH 018/299] =?UTF-8?q?=E6=96=B0=E5=A2=9EtoPay=E6=96=B9?= =?UTF-8?q?=E6=B3=95=EF=BC=8C=E4=BB=A5=E4=BE=BF=E4=BA=8E=E7=AE=80=E5=8C=96?= =?UTF-8?q?=E8=B7=B3=E8=BD=AC=E6=94=AF=E4=BB=98=EF=BC=9B=E5=AF=B9=E6=94=AF?= =?UTF-8?q?=E4=BB=98=E5=AF=B9=E6=8E=A5=E5=9C=B0=E5=9D=80=E6=8A=BD=E8=B1=A1?= =?UTF-8?q?=E5=87=BA=E6=8E=A5=E5=8F=A3=E6=96=B9=E6=B3=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/egzosn/pay/ali/api/AliPayService.java | 14 ++- .../egzosn/pay/common/api/BasePayService.java | 12 ++ .../com/egzosn/pay/common/api/PayService.java | 15 +++ .../com/egzosn/pay/common/bean/PayOrder.java | 22 ++-- .../pay/demo/controller/AliPayController.java | 6 +- .../pay/demo/controller/PayController.java | 104 ++++++++---------- .../demo/controller/PayPalPayController.java | 9 +- .../demo/controller/UnionPayController.java | 5 +- .../pay/demo/controller/WxPayController.java | 5 +- .../egzosn/pay/fuiou/api/FuiouPayService.java | 13 ++- .../com/egzosn/pay/wx/api/WxPayService.java | 18 +-- pom.xml | 1 + 12 files changed, 124 insertions(+), 100 deletions(-) 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 214cb33..2bd1594 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 @@ -65,9 +65,17 @@ public class AliPayService extends BasePayService { * * @return 请求地址 */ - public String getReqUrl() { + public String getReqUrl(TransactionType transactionType) { return payConfigStorage.isTest() ? DEV_REQ_URL : HTTPS_REQ_URL; } + /** + * 获取对应的请求地址 + * + * @return 请求地址 + */ + public String getReqUrl() { + return getReqUrl(null); + } public AliPayService(AliPayConfigStorage payConfigStorage, HttpConfigStorage configStorage) { @@ -79,10 +87,6 @@ public class AliPayService extends BasePayService { } - public String getHttpsVerifyUrl() { - return getReqUrl() + "?service=notify_verify"; - } - /** * 回调校验 * diff --git a/pay-java-common/src/main/java/com/egzosn/pay/common/api/BasePayService.java b/pay-java-common/src/main/java/com/egzosn/pay/common/api/BasePayService.java index a455d4c..a8894eb 100644 --- a/pay-java-common/src/main/java/com/egzosn/pay/common/api/BasePayService.java +++ b/pay-java-common/src/main/java/com/egzosn/pay/common/api/BasePayService.java @@ -122,6 +122,18 @@ public abstract class BasePayService implements Pay return SignUtils.valueOf(payConfigStorage.getSignType()).sign(content, payConfigStorage.getKeyPrivate(),characterEncoding); } + /** + * 页面转跳支付, 返回对应页面重定向信息 + * + * @param order 订单信息 + * @return 对应页面重定向信息 + */ + @Override + public String toPay(PayOrder order) { + Map orderInfo = orderInfo(order); + return buildRequest(orderInfo, MethodType.POST); + } + /** * 将请求参数或者请求流转化为 Map * diff --git a/pay-java-common/src/main/java/com/egzosn/pay/common/api/PayService.java b/pay-java-common/src/main/java/com/egzosn/pay/common/api/PayService.java index 1936364..c0eb4ef 100644 --- a/pay-java-common/src/main/java/com/egzosn/pay/common/api/PayService.java +++ b/pay-java-common/src/main/java/com/egzosn/pay/common/api/PayService.java @@ -87,6 +87,12 @@ public interface PayService { */ Map orderInfo(PayOrder order); + /** + * 页面转跳支付, 返回对应页面重定向信息 + * @param order 订单信息 + * @return 对应页面重定向信息 + */ + String toPay(PayOrder order); /** * 创建签名 * @@ -141,6 +147,8 @@ public interface PayService { */ String buildRequest(Map orderInfo, MethodType method); + + /** * 获取输出二维码,用户返回给支付端, * @@ -410,4 +418,11 @@ public interface PayService { */ void addPayMessageInterceptor(PayMessageInterceptor interceptor); + /** + * 获取支付请求地址 + * @param transactionType 交易类型 + * @return 请求地址 + */ + String getReqUrl(TransactionType transactionType); + } 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 8e64f60..1dc0c46 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 @@ -29,6 +29,10 @@ public class PayOrder { * 价格 */ private BigDecimal price; + /** + * 支付平台订单号,交易号 + */ + private String tradeNo; /** * 商户订单号 */ @@ -122,24 +126,18 @@ public class PayOrder { } /** - * 获取商户订单号 - * @return 商户订单号 - * @see #getOutTradeNo() + * 支付平台订单号,交易号 + * @return 支付平台订单号,交易号 */ - @Deprecated public String getTradeNo() { - return outTradeNo; + return tradeNo; } - - /** - * - * @param tradeNo 商户订单号 - * @see #setOutTradeNo(String) + * 支付平台订单号,交易号 + * @param tradeNo 支付平台订单号,交易号 */ - @Deprecated public void setTradeNo(String tradeNo) { - this.outTradeNo = tradeNo; + this.tradeNo = tradeNo; } /** diff --git a/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/AliPayController.java b/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/AliPayController.java index 60bf23d..25ab40f 100644 --- a/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/AliPayController.java +++ b/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/AliPayController.java @@ -88,8 +88,10 @@ public class AliPayController { //WAP // PayOrder order = new PayOrder("订单title", "摘要", null == price ? new BigDecimal(0.01) : price, UUID.randomUUID().toString().replace("-", ""), AliTransactionType.WAP); - Map orderInfo = service.orderInfo(order); - return service.buildRequest(orderInfo, MethodType.POST); +// Map orderInfo = service.orderInfo(order); +// return service.buildRequest(orderInfo, MethodType.POST); + + return service.toPay(order); } 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 e7c2afa..f8ac067 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 @@ -5,7 +5,6 @@ package com.egzosn.pay.demo.controller; import com.alibaba.fastjson.JSONObject; import com.egzosn.pay.ali.api.AliPayService; import com.egzosn.pay.ali.bean.AliTransactionType; -import com.egzosn.pay.common.api.Callback; import com.egzosn.pay.common.api.PayConfigStorage; import com.egzosn.pay.common.api.PayMessageInterceptor; import com.egzosn.pay.common.api.PayService; @@ -18,16 +17,11 @@ import com.egzosn.pay.demo.entity.PayType; import com.egzosn.pay.demo.request.QueryOrder; import com.egzosn.pay.demo.service.ApyAccountService; import com.egzosn.pay.demo.service.PayResponse; -import com.egzosn.pay.payoneer.api.PayoneerPayService; import com.egzosn.pay.wx.bean.WxTransactionType; -import com.fasterxml.jackson.databind.ObjectMapper; -import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder; 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 org.springframework.web.servlet.view.RedirectView; -import org.springframework.web.servlet.view.json.MappingJackson2JsonView; import javax.annotation.Resource; import javax.imageio.ImageIO; @@ -35,9 +29,7 @@ import javax.servlet.http.HttpServletRequest; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.math.BigDecimal; -import java.net.URLEncoder; import java.util.HashMap; -import java.util.LinkedHashMap; import java.util.Map; import java.util.UUID; @@ -58,13 +50,11 @@ public class PayController { private ApyAccountService service; @RequestMapping("/") - public ModelAndView index(){ + public ModelAndView index() { return new ModelAndView("/index.html"); } - - /** * 这里模拟账户信息增加 * @@ -89,11 +79,11 @@ public class PayController { * @param payId 账户id * @param transactionType 交易类型, 这个针对于每一个 支付类型的对应的几种交易方式 * @param bankType 针对刷卡支付,卡的类型,类型值 - * @param price 金额 + * @param price 金额 * @return 跳到支付页面 */ @RequestMapping(value = "toPay.html", produces = "text/html;charset=UTF-8") - public String toPay(HttpServletRequest request,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); @@ -102,7 +92,7 @@ public class PayController { order.setSpbillCreateIp(request.getHeader("X-Real-IP")); StringBuffer requestURL = request.getRequestURL(); //设置网页地址 - order.setWapUrl(requestURL.substring(0, requestURL.indexOf("/") > 0 ? requestURL.indexOf("/") : requestURL.length() )); + order.setWapUrl(requestURL.substring(0, requestURL.indexOf("/") > 0 ? requestURL.indexOf("/") : requestURL.length())); //设置网页名称 order.setWapName("在线充值"); // ------ 微信H5使用---- @@ -124,6 +114,7 @@ public class PayController { /** * 跳到支付页面 * 针对实时支付,即时付款 + * * @return 跳到支付页面 */ @RequestMapping(value = "toWxPay.html", produces = "text/html;charset=UTF-8") @@ -131,29 +122,29 @@ public class PayController { //获取对应的支付账户操作工具(可根据账户id) PayResponse payResponse = service.getPayResponse(2); - PayOrder order = new PayOrder("订单title", "摘要", new BigDecimal(0.01) , UUID.randomUUID().toString().replace("-", ""), WxTransactionType.MWEB); + 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.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); +// Map orderInfo = payResponse.getService().orderInfo(order); +// return payResponse.getService().buildRequest(orderInfo, MethodType.POST); + return payResponse.getService().toPay(order); } /** * 公众号支付 * - * - * @param payId 账户id + * @param payId 账户id * @param openid openid - * @param price 金额 + * @param price 金额 * @return 返回jsapi所需参数 */ - @RequestMapping(value = "jsapi" ) + @RequestMapping(value = "jsapi") public Map toPay(Integer payId, String openid, BigDecimal price) { //获取对应的支付账户操作工具(可根据账户id) PayResponse payResponse = service.getPayResponse(payId); @@ -164,16 +155,17 @@ public class PayController { Map orderInfo = payResponse.getService().orderInfo(order); orderInfo.put("code", 0); - return orderInfo; + return orderInfo; } /** * 刷卡付,pos主动扫码付款(条码付) + * * @param payId 账户id * @param transactionType 交易类型, 这个针对于每一个 支付类型的对应的几种交易方式 * @param authCode 授权码,条码等 - * @param price 金额 + * @param price 金额 * @return 支付结果 */ @RequestMapping(value = "microPay") @@ -200,9 +192,10 @@ public class PayController { /** * 获取二维码图像 * 二维码支付 + * * @param payId 账户id * @param transactionType 交易类型, 这个针对于每一个 支付类型的对应的几种交易方式 - * @param price 金额 + * @param price 金额 * @return 二维码图像 */ @RequestMapping(value = "toQrPay.jpg", produces = "image/jpeg;charset=UTF-8") @@ -211,30 +204,31 @@ public class PayController { PayResponse payResponse = service.getPayResponse(payId); ByteArrayOutputStream baos = new ByteArrayOutputStream(); - ImageIO.write(payResponse.getService().genQrPay(new PayOrder("订单title", "摘要", null == price ? new BigDecimal(0.01) : price, System.currentTimeMillis()+"", PayType.valueOf(payResponse.getStorage().getPayType()).getTransactionType(transactionType))), "JPEG", baos); + ImageIO.write(payResponse.getService().genQrPay(new PayOrder("订单title", "摘要", null == price ? new BigDecimal(0.01) : price, System.currentTimeMillis() + "", PayType.valueOf(payResponse.getStorage().getPayType()).getTransactionType(transactionType))), "JPEG", baos); return baos.toByteArray(); } /** * 获取一码付二维码图像 * 二维码支付 - * @param wxPayId 微信账户id - * @param aliPayId 支付宝id - * @param price 金额 + * + * @param wxPayId 微信账户id + * @param aliPayId 支付宝id + * @param price 金额 * @return 二维码图像 */ @RequestMapping(value = "toWxAliQrPay.jpg", produces = "image/jpeg;charset=UTF-8") - public byte[] toWxAliQrPay(Integer wxPayId,Integer aliPayId, BigDecimal price, HttpServletRequest request) throws IOException { + public byte[] toWxAliQrPay(Integer wxPayId, Integer aliPayId, BigDecimal price, HttpServletRequest request) throws IOException { //获取对应的支付账户操作工具(可根据账户id) ByteArrayOutputStream baos = new ByteArrayOutputStream(); //这里为需要生成二维码的地址 StringBuffer url = request.getRequestURL(); url = new StringBuffer(url.substring(0, url.lastIndexOf(request.getRequestURI()))); - url .append("/toWxAliPay.html?"); - if (null != wxPayId){ + url.append("/toWxAliPay.html?"); + if (null != wxPayId) { url.append("wxPayId=").append(wxPayId).append("&"); } - if (null != aliPayId){ + if (null != aliPayId) { url.append("aliPayId=").append(aliPayId).append("&"); } url.append("price=").append(price); @@ -244,40 +238,39 @@ public class PayController { } /** - * * 支付宝与微信平台的判断 并进行支付的转跳 - * @param wxPayId 微信账户id - * @param aliPayId 支付宝id - * @param price 金额 + * + * @param wxPayId 微信账户id + * @param aliPayId 支付宝id + * @param price 金额 * @return 支付宝与微信平台的判断 */ @RequestMapping(value = "toWxAliPay.html", produces = "text/html;charset=UTF-8") - public String toWxAliPay(Integer wxPayId,Integer aliPayId, BigDecimal price, HttpServletRequest request) throws IOException { + public String toWxAliPay(Integer wxPayId, Integer aliPayId, BigDecimal price, HttpServletRequest request) throws IOException { StringBuilder html = new StringBuilder(); //订单 PayOrder payOrder = new PayOrder("订单title", "摘要", null == price ? new BigDecimal(0.01) : price, System.currentTimeMillis() + ""); String ua = request.getHeader("user-agent"); - if(ua.contains("MicroMessenger")){ + if (ua.contains("MicroMessenger")) { payOrder.setTransactionType(WxTransactionType.NATIVE); PayService service = this.service.getPayResponse(wxPayId).getService(); - return String.format("",(String) service.orderInfo(payOrder).get("code_url")); + return String.format("", (String) service.orderInfo(payOrder).get("code_url")); } - if(ua.contains("AlipayClient")){ + if (ua.contains("AlipayClient")) { payOrder.setTransactionType(AliTransactionType.SWEEPPAY); - AliPayService service = (AliPayService)this.service.getPayResponse(aliPayId).getService(); + AliPayService service = (AliPayService) this.service.getPayResponse(aliPayId).getService(); JSONObject result = service.getHttpRequestTemplate().postForObject(service.getReqUrl() + "?" + UriVariables.getMapToParameters(service.orderInfo(payOrder)), null, JSONObject.class); - result = result.getJSONObject("alipay_trade_precreate_response"); + result = result.getJSONObject("alipay_trade_precreate_response"); return String.format("", result.getString("qr_code")); } - return String.format("", ua); + return String.format("", ua); } - /** * 获取支付预订单信息 * @@ -299,15 +292,12 @@ public class PayController { /** * 支付回调地址 方式一 - * + *

* 方式二,{@link #payBack(HttpServletRequest, Integer)} 是属于简化方式, 试用与简单的业务场景 * - * * @param request * @param payId * @return 支付是否成功 - * - * */ @RequestMapping(value = "payBackOne{payId}.json") public String payBackOne(HttpServletRequest request, @PathVariable Integer payId) throws IOException { @@ -333,22 +323,16 @@ public class PayController { } - - - /** * 支付回调地址 * 方式二 - * @param request * - * @return - * - * 拦截器相关增加, 详情查看{@link com.egzosn.pay.common.api.PayService#addPayMessageInterceptor(PayMessageInterceptor)} + * @param request + * @return 拦截器相关增加, 详情查看{@link com.egzosn.pay.common.api.PayService#addPayMessageInterceptor(PayMessageInterceptor)} *

* 业务处理在对应的PayMessageHandler里面处理,在哪里设置PayMessageHandler,详情查看{@link com.egzosn.pay.common.api.PayService#setPayMessageHandler(com.egzosn.pay.common.api.PayMessageHandler)} - * + *

* 如果未设置 {@link com.egzosn.pay.common.api.PayMessageHandler} 那么会使用默认的 {@link com.egzosn.pay.common.api.DefaultPayMessageHandler} - * */ @RequestMapping(value = "payBack{payId}.json") public String payBack(HttpServletRequest request, @PathVariable Integer payId) throws IOException { @@ -402,7 +386,7 @@ public class PayController { * @return 返回支付方申请退款后的结果 */ @RequestMapping("refund") - public Map refund(Integer payId ,RefundOrder order) { + public Map refund(Integer payId, RefundOrder order) { PayResponse payResponse = service.getPayResponse(payId); // return payResponse.getService().refund(order.getTradeNo(), order.getOutTradeNo(), order.getRefundAmount(), order.getTotalAmount()); @@ -453,7 +437,6 @@ public class PayController { * 转账 * * @param order 转账订单 - * * @return 对应的转账结果 */ @RequestMapping("transfer") @@ -467,7 +450,6 @@ public class PayController { * * @param outNo 商户转账订单号 * @param tradeNo 支付平台转账订单号 - * * @return 对应的转账订单 */ @RequestMapping("transferQuery") diff --git a/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/PayPalPayController.java b/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/PayPalPayController.java index 929299e..0d4b366 100644 --- a/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/PayPalPayController.java +++ b/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/PayPalPayController.java @@ -71,15 +71,16 @@ public class PayPalPayController { //及时收款 PayOrder order = new PayOrder("订单title", "摘要", null == price ? new BigDecimal(0.01) : price, UUID.randomUUID().toString().replace("-", ""), PayPalTransactionType.sale); - Map orderInfo = service.orderInfo(order); +// Map orderInfo = service.orderInfo(order); +// return service.buildRequest(orderInfo, MethodType.POST); + + String toPayHtml = service.toPay(order); //某些支付下单时无法设置单号,通过下单后返回对应单号,如 paypal,友店。 String outTradeNo = order.getOutTradeNo(); - System.out.println("支付订单号:" + outTradeNo + " 这里可以进行回存"); - - return service.buildRequest(orderInfo, MethodType.POST); + return toPayHtml; } /** * 申请退款接口 diff --git a/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/UnionPayController.java b/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/UnionPayController.java index 5ab99de..e1955a8 100644 --- a/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/UnionPayController.java +++ b/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/UnionPayController.java @@ -101,8 +101,9 @@ public class UnionPayController { //企业网银支付(B2B支付) // PayOrder order = new PayOrder("订单title", "摘要", null == price ? new BigDecimal(0.01) : price, UUID.randomUUID().toString().replace("-", ""), UnionTransactionType.B2B); - Map orderInfo = service.orderInfo(order); - return service.buildRequest(orderInfo, MethodType.POST); +// Map orderInfo = service.orderInfo(order); +// return service.buildRequest(orderInfo, MethodType.POST); + return service.toPay(order); } /** diff --git a/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/WxPayController.java b/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/WxPayController.java index 7541917..c50cd70 100644 --- a/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/WxPayController.java +++ b/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/WxPayController.java @@ -115,8 +115,9 @@ public class WxPayController { //设置网页名称 order.setWapName("在线充值"); - Map orderInfo = service.orderInfo(order); - return service.buildRequest(orderInfo, MethodType.POST); +// Map orderInfo = service.orderInfo(order); +// return service.buildRequest(orderInfo, MethodType.POST); + return service.toPay(order); } /** diff --git a/pay-java-fuiou/src/main/java/com/egzosn/pay/fuiou/api/FuiouPayService.java b/pay-java-fuiou/src/main/java/com/egzosn/pay/fuiou/api/FuiouPayService.java index a04c6cd..f71f3e0 100644 --- a/pay-java-fuiou/src/main/java/com/egzosn/pay/fuiou/api/FuiouPayService.java +++ b/pay-java-fuiou/src/main/java/com/egzosn/pay/fuiou/api/FuiouPayService.java @@ -26,11 +26,11 @@ public class FuiouPayService extends BasePayService { /** * 正式域名 */ - public static final String URL_FuiouBaseDomain = "https://pay.fuiou.com/"; + public static final String URL_FUIOU_BASE_DOMAIN = "https://pay.fuiou.com/"; /** * 测试域名 */ - public static final String DEV_URL_FUIOUBASEDOMAIN = "http://www-1.fuiou.com:8888/wg1_run/"; + public static final String DEV_URL_FUIOU_BASE_DOMAIN = "http://www-1.fuiou.com:8888/wg1_run/"; /** * B2C/B2B支付 @@ -58,12 +58,19 @@ public class FuiouPayService extends BasePayService { public static final String URL_NewSmpRefundGate = "newSmpRefundGate.do"; + /** + * 获取对应的请求地址 + * @return 请求地址 + */ + public String getReqUrl(TransactionType transactionType){ + return payConfigStorage.isTest() ? DEV_URL_FUIOU_BASE_DOMAIN : URL_FUIOU_BASE_DOMAIN; + } /** * 获取对应的请求地址 * @return 请求地址 */ public String getReqUrl(){ - return payConfigStorage.isTest() ? DEV_URL_FUIOUBASEDOMAIN : URL_FuiouBaseDomain; + return getReqUrl(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 eb55f27..f98211d 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 @@ -97,7 +97,7 @@ public class WxPayService extends BasePayService { * * @return 请求url */ - private String getUrl(TransactionType transactionType) { + public String getReqUrl(TransactionType transactionType) { return URI + (payConfigStorage.isTest() ? SANDBOXNEW : "") + transactionType.getMethod(); } @@ -215,7 +215,7 @@ public class WxPayService extends BasePayService { LOG.debug("requestXML:" + requestXML); } //调起支付的参数列表 - JSONObject result = requestTemplate.postForObject(getUrl(order.getTransactionType()), requestXML, JSONObject.class); + JSONObject result = requestTemplate.postForObject(getReqUrl(order.getTransactionType()), requestXML, JSONObject.class); if (!SUCCESS.equals(result.get(RETURN_CODE))) { throw new PayErrorException(new WxPayError(result.getString(RETURN_CODE), result.getString(RETURN_MSG_CODE), result.toJSONString())); @@ -477,7 +477,7 @@ public class WxPayService extends BasePayService { //设置签名 setSign(parameters); - return requestTemplate.postForObject(getUrl(WxTransactionType.REFUND), XML.getMap2Xml(parameters), JSONObject.class); + return requestTemplate.postForObject(getReqUrl(WxTransactionType.REFUND), XML.getMap2Xml(parameters), JSONObject.class); } @@ -512,7 +512,7 @@ public class WxPayService extends BasePayService { setParameters(parameters, "out_refund_no", refundOrder.getRefundNo()); //设置签名 setSign(parameters); - return requestTemplate.postForObject(getUrl( WxTransactionType.REFUNDQUERY), XML.getMap2Xml(parameters) , JSONObject.class); + return requestTemplate.postForObject(getReqUrl( WxTransactionType.REFUNDQUERY), XML.getMap2Xml(parameters) , JSONObject.class); } @@ -536,7 +536,7 @@ public class WxPayService extends BasePayService { //设置签名 setSign(parameters); - String respStr = requestTemplate.postForObject(getUrl(WxTransactionType.DOWNLOADBILL), XML.getMap2Xml(parameters), String.class); + String respStr = requestTemplate.postForObject(getReqUrl(WxTransactionType.DOWNLOADBILL), XML.getMap2Xml(parameters), String.class); if (respStr.indexOf("<") == 0) { return XML.toJSONObject(respStr); } @@ -583,7 +583,7 @@ public class WxPayService extends BasePayService { } //设置签名 setSign(parameters); - return requestTemplate.postForObject(getUrl(transactionType), XML.getMap2Xml(parameters) , JSONObject.class); + return requestTemplate.postForObject(getReqUrl(transactionType), XML.getMap2Xml(parameters) , JSONObject.class); } /** @@ -623,7 +623,7 @@ public class WxPayService extends BasePayService { } parameters.put(SIGN, createSign(SignUtils.parameterText(parameters, "&", SIGN), payConfigStorage.getInputCharset())); - return getHttpRequestTemplate().postForObject(getUrl(order.getTransferType()), XML.getMap2Xml(parameters), JSONObject.class); + return getHttpRequestTemplate().postForObject(getReqUrl(order.getTransferType()), XML.getMap2Xml(parameters), JSONObject.class); } /** @@ -690,10 +690,10 @@ public class WxPayService extends BasePayService { } //如果类型为余额方式 if (TRANSFERS.getType().equals(wxTransferType) || GETTRANSFERINFO.getType().equals(wxTransferType)){ - return getHttpRequestTemplate().postForObject(getUrl(GETTRANSFERINFO), XML.getMap2Xml(parameters), JSONObject.class); + return getHttpRequestTemplate().postForObject(getReqUrl(GETTRANSFERINFO), XML.getMap2Xml(parameters), JSONObject.class); } //默认查询银行卡的记录 - return getHttpRequestTemplate().postForObject(getUrl(QUERY_BANK), XML.getMap2Xml(parameters), JSONObject.class); + return getHttpRequestTemplate().postForObject(getReqUrl(QUERY_BANK), XML.getMap2Xml(parameters), JSONObject.class); } diff --git a/pom.xml b/pom.xml index 643fa99..adb1c25 100644 --- a/pom.xml +++ b/pom.xml @@ -52,6 +52,7 @@ pay-java-payoneer pay-java-paypal pay-java-demo + pay-java-yiji -- Gitee From 83d949c5d05dd8c26e312335e24fb9ebe003b3c4 Mon Sep 17 00:00:00 2001 From: egan Date: Mon, 15 Apr 2019 23:53:19 +0800 Subject: [PATCH 019/299] =?UTF-8?q?=E6=98=93=E6=9E=81=E4=BB=98=E5=88=9D?= =?UTF-8?q?=E5=A7=8B=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pay-java-yiji/pom.xml | 24 + .../pay/yiji/api/YiJiPayConfigStorage.java | 78 +++ .../egzosn/pay/yiji/api/YiJiPayService.java | 452 ++++++++++++++++++ .../pay/yiji/bean/YiJiTransactionType.java | 57 +++ 4 files changed, 611 insertions(+) create mode 100644 pay-java-yiji/pom.xml create mode 100644 pay-java-yiji/src/main/java/com/egzosn/pay/yiji/api/YiJiPayConfigStorage.java create mode 100644 pay-java-yiji/src/main/java/com/egzosn/pay/yiji/api/YiJiPayService.java create mode 100644 pay-java-yiji/src/main/java/com/egzosn/pay/yiji/bean/YiJiTransactionType.java diff --git a/pay-java-yiji/pom.xml b/pay-java-yiji/pom.xml new file mode 100644 index 0000000..cb9a110 --- /dev/null +++ b/pay-java-yiji/pom.xml @@ -0,0 +1,24 @@ + + + + pay-java-parent + com.egzosn + 2.12.7-SNAPSHOT + + 4.0.0 + + com.egzosn + pay-java-yiji + + + + + + com.egzosn + pay-java-common + + + + \ No newline at end of file diff --git a/pay-java-yiji/src/main/java/com/egzosn/pay/yiji/api/YiJiPayConfigStorage.java b/pay-java-yiji/src/main/java/com/egzosn/pay/yiji/api/YiJiPayConfigStorage.java new file mode 100644 index 0000000..492ac1f --- /dev/null +++ b/pay-java-yiji/src/main/java/com/egzosn/pay/yiji/api/YiJiPayConfigStorage.java @@ -0,0 +1,78 @@ +package com.egzosn.pay.yiji.api; + +import com.egzosn.pay.common.api.BasePayConfigStorage; + +/** + * 易极付配置存储 + * @author egan + * + *

+ * email egzosn@gmail.com
+ * date 2019/04/15 22:50
+ * 
+ */ +public class YiJiPayConfigStorage extends BasePayConfigStorage { + + + /** + * 易极付分配的商户号 合作者id + */ + private String partnerId; + + /** + * 卖家id + */ + private String sellerUserId; + + public String getPartnerId() { + return partnerId; + } + + public void setPartnerId(String partnerId) { + this.partnerId = partnerId; + } + + @Override + public String getAppid() { + return null; + } + + + /** + * 合作商唯一标识 + */ + @Override + public String getPid() { + return partnerId; + } + + + + + @Override + public String getSeller() { + return sellerUserId; + } + + public String getSellerUserId() { + return sellerUserId; + } + + public void setSellerUserId(String sellerUserId) { + this.sellerUserId = sellerUserId; + } + + /** + * 为商户平台设置的密钥key + * @return 密钥 + */ + public String getSecretKey() { + return getKeyPrivate(); + } + + public void setSecretKey(String secretKey) { + setKeyPrivate(secretKey); + } + + +} diff --git a/pay-java-yiji/src/main/java/com/egzosn/pay/yiji/api/YiJiPayService.java b/pay-java-yiji/src/main/java/com/egzosn/pay/yiji/api/YiJiPayService.java new file mode 100644 index 0000000..5be93a8 --- /dev/null +++ b/pay-java-yiji/src/main/java/com/egzosn/pay/yiji/api/YiJiPayService.java @@ -0,0 +1,452 @@ +package com.egzosn.pay.yiji.api; + +import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.JSONObject; +import com.egzosn.pay.common.api.BasePayService; +import com.egzosn.pay.common.bean.*; +import com.egzosn.pay.common.bean.result.PayException; +import com.egzosn.pay.common.exception.PayErrorException; +import com.egzosn.pay.common.http.HttpConfigStorage; +import com.egzosn.pay.common.http.UriVariables; +import com.egzosn.pay.common.util.MatrixToImageWriter; +import com.egzosn.pay.common.util.Util; +import com.egzosn.pay.common.util.sign.SignUtils; +import com.egzosn.pay.common.util.str.StringUtils; +import java.awt.image.BufferedImage; +import java.math.BigDecimal; +import java.util.*; + +/** + * 易极付支付服务 + * + * @author egan + *

+ * email egzosn@gmail.com + * * date 2019/04/15 22:51 + */ +public class YiJiPayService extends BasePayService { + + /** + * 正式测试环境 + */ + private static final String HTTPS_REQ_URL = "https://api.yiji.com"; + /** + * 沙箱测试环境账号 + */ + private static final String DEV_REQ_URL = "https://openapi.yijifu.net/gateway.html"; + + public static final String SIGN = "sign"; + + public static final String SUCCESS_CODE = "10000"; + + public static final String CODE = "code"; + /** + * 附加参数 + */ + public static final String PASSBACK_PARAMS = "passback_params"; + /** + * 产品代码 + */ + public static final String PRODUCT_CODE = "product_code"; + /** + * 返回地址 + */ + public static final String RETURN_URL = "return_url"; + + /** + * 请求内容 + */ + public static final String BIZ_CONTENT = "biz_content"; + /** + * 获取对应的请求地址 + * + * @return 请求地址 + */ + public String getReqUrl(TransactionType transactionType) { + return payConfigStorage.isTest() ? DEV_REQ_URL : HTTPS_REQ_URL; + } + /** + * 获取对应的请求地址 + * + * @return 请求地址 + */ + public String getReqUrl() { + return getReqUrl(null); + } + + + public YiJiPayService(YiJiPayConfigStorage payConfigStorage, HttpConfigStorage configStorage) { + super(payConfigStorage, configStorage); + } + + public YiJiPayService(YiJiPayConfigStorage payConfigStorage) { + super(payConfigStorage); + } + + + public String getHttpsVerifyUrl() { + return getReqUrl() + "?service=notify_verify"; + } + + /** + * 回调校验 + * + * @param params 回调回来的参数集 + * @return 签名校验 true通过 + */ + @Override + public boolean verify(Map params) { + + if (params.get(SIGN) == null) { + LOG.debug("易极付支付异常:params:" + params); + return false; + } + + return signVerify(params, (String) params.get(SIGN)) && verifySource((String) params.get("notify_id")); + + } + + /** + * 根据反馈回来的信息,生成签名结果 + * + * @param params 通知返回来的参数数组 + * @param sign 比对的签名结果 + * @return 生成的签名结果 + */ + @Override + public boolean signVerify(Map params, String sign) { + + if (params instanceof JSONObject) { + for (Map.Entry entry : params.entrySet()) { + if (SIGN.equals(entry.getKey())) { + continue; + } + TreeMap response = new TreeMap((Map )entry.getValue()); + LinkedHashMap linkedHashMap = new LinkedHashMap<>(); + linkedHashMap.put(CODE, response.remove(CODE)); + linkedHashMap.put("msg", response.remove("msg")); + linkedHashMap.putAll(response); + return SignUtils.valueOf(payConfigStorage.getSignType()).verify(JSON.toJSONString(linkedHashMap), sign, payConfigStorage.getKeyPublic(), payConfigStorage.getInputCharset()); + } + } + + return SignUtils.valueOf(payConfigStorage.getSignType()).verify(params, sign, payConfigStorage.getKeyPublic(), payConfigStorage.getInputCharset()); + } + + + /** + * 校验数据来源 + * + * @param id 业务id, 数据的真实性. + * @return true通过 + */ + @Override + public boolean verifySource(String id) { + return true; + } + + + /** + * 生成并设置签名 + * + * @param parameters 请求参数 + * @return 请求参数 + */ + private Map setSign(Map parameters) { + parameters.put("sign_type", payConfigStorage.getSignType()); + String sign = createSign(SignUtils.parameterText(parameters, "&", SIGN), payConfigStorage.getInputCharset()); + + parameters.put(SIGN, sign); + return parameters; + } + + + /** + * 返回创建的订单信息 + * + * @param order 支付订单 + * @return 订单信息 + * @see PayOrder 支付订单信息 + */ + @Override + public Map orderInfo(PayOrder order) { + + return setSign(getOrder(order)); + } + + + /** + * 易极付创建订单信息 + * create the order info + * + * @param order 支付订单 + * @return 返回易极付预下单信息 + * @see PayOrder 支付订单信息 + */ + private Map getOrder(PayOrder order) { + + + Map orderInfo = getPublicParameters(order.getTransactionType()); + orderInfo.put("orderNo", order.getTradeNo()); + orderInfo.put("outOrderNo", order.getOutTradeNo()); + + if (StringUtils.isNotEmpty(payConfigStorage.getSeller())){ + orderInfo.put("sellerUserId", payConfigStorage.getSeller()); + } + orderInfo.put("tradeAmount", Util.conversionAmount(order.getPrice())); + orderInfo.put("goodsClauses", String.format("[{'name':'%s'}]", order.getBody())); + + + return orderInfo; + } + + /** + * 获取公共请求参数 + * + * @return 放回公共请求参数 + */ + private Map getPublicParameters(TransactionType transactionType) { + Map orderInfo = new TreeMap<>(); + orderInfo.put("partnerId", payConfigStorage.getPid()); + orderInfo.put("signType", payConfigStorage.getSignType()); + orderInfo.put("returnUrl", payConfigStorage.getReturnUrl()); + orderInfo.put("notifyUrl", payConfigStorage.getNotifyUrl()); + orderInfo.put("service", transactionType.getMethod()); + return orderInfo; + } + + + /** + * 获取输出消息,用户返回给支付端 + * + * @param code 状态 + * @param message 消息 + * @return 返回输出消息 + */ + @Override + public PayOutMessage getPayOutMessage(String code, String message) { + return PayOutMessage.TEXT().content(code.toLowerCase()).build(); + } + + /** + * 获取成功输出消息,用户返回给支付端 + * 主要用于拦截器中返回 + * + * @param payMessage 支付回调消息 + * @return 返回输出消息 + */ + @Override + public PayOutMessage successPayOutMessage(PayMessage payMessage) { + return PayOutMessage.TEXT().content("success").build(); + } + + /** + * @param orderInfo 发起支付的订单信息 + * @param method 请求方式 "post" "get", + * @return 获取输出消息,用户返回给支付端, 针对于web端 + */ + @Override + public String buildRequest(Map orderInfo, MethodType method) { + StringBuilder formHtml = new StringBuilder(); + formHtml.append("

"); + formHtml.append(""); + formHtml.append("
"); + formHtml.append(""); + + return formHtml.toString(); + } + + /** + * 生成二维码支付 + * + * @param order 发起支付的订单信息 + * @return 返回图片信息,支付时需要的 + */ + @Override + public BufferedImage genQrPay(PayOrder order) { + + Map orderInfo = orderInfo(order); + + + //预订单 + JSONObject result = getHttpRequestTemplate().postForObject(getReqUrl() + "?" + UriVariables.getMapToParameters(orderInfo), null, JSONObject.class); + JSONObject response = result.getJSONObject("alipay_trade_precreate_response"); + if (SUCCESS_CODE.equals(response.getString(CODE))) { + return MatrixToImageWriter.writeInfoToJpgBuff(response.getString("qr_code")); + } + throw new PayErrorException(new PayException(response.getString(CODE), response.getString("msg"), result.toJSONString())); + + } + + /** + * pos主动扫码付款(条码付) + * + * @param order 发起支付的订单信息 + * @return 支付结果 + */ + @Override + public Map microPay(PayOrder order) { + Map orderInfo = orderInfo(order); + //预订单 + JSONObject result = getHttpRequestTemplate().postForObject(getReqUrl() + "?" + UriVariables.getMapToParameters(orderInfo), null, JSONObject.class); + JSONObject response = result.getJSONObject("alipay_trade_pay_response"); + if (!SUCCESS_CODE.equals(response.getString(CODE))) { + LOG.info("收款失败"); + } + return result; + } + + /** + * 交易查询接口 + * + * @param tradeNo 支付平台订单号 + * @param outTradeNo 商户单号 + * @return 返回查询回来的结果集,支付方原值返回 + */ + @Override + public Map query(String tradeNo, String outTradeNo) { + return null; + + } + + + /** + * 交易关闭接口 + * + * @param tradeNo 支付平台订单号 + * @param outTradeNo 商户单号 + * @return 返回支付方交易关闭后的结果 + */ + @Override + public Map close(String tradeNo, String outTradeNo) { + return null; + } + + /** + * 支付交易返回失败或支付系统超时,调用该接口撤销交易。 + * 如果此订单用户支付失败,易极付系统会将此订单关闭;如果用户支付成功,易极付系统会将此订单资金退还给用户。 + * 注意:只有发生支付系统超时或者支付结果未知时可调用撤销,其他正常支付的单如需实现相同功能请调用申请退款API。 + * 提交支付交易后调用【查询订单API】,没有明确的支付结果再调用【撤销订单API】。 + * + * @param tradeNo 支付平台订单号 + * @param outTradeNo 商户单号 + * @return 返回支付方交易撤销后的结果 + */ + @Override + public Map cancel(String tradeNo, String outTradeNo) { + return null; + } + + /** + * 申请退款接口 + * 废弃 + * + * @param tradeNo 支付平台订单号 + * @param outTradeNo 商户单号 + * @param refundAmount 退款金额 + * @param totalAmount 总金额 + * @return 返回支付方申请退款后的结果 + * @see #refund(RefundOrder, com.egzosn.pay.common.api.Callback) + * @deprecated 版本替代 {@link #refund(RefundOrder, com.egzosn.pay.common.api.Callback)} + */ + @Deprecated + @Override + public Map refund(String tradeNo, String outTradeNo, BigDecimal refundAmount, BigDecimal totalAmount) { + return refund(new RefundOrder(tradeNo, outTradeNo, refundAmount, totalAmount)); + } + + + /** + * 申请退款接口 + * + * @param refundOrder 退款订单信息 + * @return 返回支付方申请退款后的结果 + */ + @Override + public Map refund(RefundOrder refundOrder) { + + return null; + } + + /** + * 查询退款 + * + * @param tradeNo 支付平台订单号 + * @param outTradeNo 商户单号 + * @return 返回支付方查询退款后的结果 + */ + @Override + public Map refundquery(String tradeNo, String outTradeNo) { + return null; + } + + /** + * 查询退款 + * + * @param refundOrder 退款订单单号信息 + * @return 返回支付方查询退款后的结果 + */ + @Override + public Map refundquery(RefundOrder refundOrder) { + + return null; + + } + + /** + * 目前只支持日账单 + * + * @param billDate 账单类型,商户通过接口或商户经开放平台授权后其所属服务商通过接口可以获取以下账单类型:trade、signcustomer;trade指商户基于易极付交易收单的业务账单;signcustomer是指基于商户易极付余额收入及支出等资金变动的帐务账单; + * @param billType 账单时间:日账单格式为yyyy-MM-dd,月账单格式为yyyy-MM。 + * @return 返回支付方下载对账单的结果 + */ + @Override + public Map downloadbill(Date billDate, String billType) { + + return null; + } + + + /** + * @param tradeNoOrBillDate 支付平台订单号或者账单类型, 具体请 + * 类型为{@link String }或者 {@link Date },类型须强制限制,类型不对应则抛出异常{@link PayErrorException} + * @param outTradeNoBillType 商户单号或者 账单类型 + * @param transactionType 交易类型 + * @return 返回支付方对应接口的结果 + */ + @Override + public Map secondaryInterface(Object tradeNoOrBillDate, String outTradeNoBillType, TransactionType transactionType) { + + + return null; + } + + /** + * 转账 + * + * @param order 转账订单 + * @return 对应的转账结果 + */ + @Override + public Map transfer(TransferOrder order) { + + return null; + } + + /** + * 转账查询 + * + * @param outNo 商户转账订单号 + * @param tradeNo 支付平台转账订单号 + * @return 对应的转账订单 + */ + @Override + public Map transferQuery(String outNo, String tradeNo) { + + return null; + } + +} diff --git a/pay-java-yiji/src/main/java/com/egzosn/pay/yiji/bean/YiJiTransactionType.java b/pay-java-yiji/src/main/java/com/egzosn/pay/yiji/bean/YiJiTransactionType.java new file mode 100644 index 0000000..144f64f --- /dev/null +++ b/pay-java-yiji/src/main/java/com/egzosn/pay/yiji/bean/YiJiTransactionType.java @@ -0,0 +1,57 @@ +package com.egzosn.pay.yiji.bean; + +import com.egzosn.pay.common.bean.TransactionType; + +/** + * 易极付交易类型 + *
+ * 说明交易类型主要用于支付接口调用参数所需
+ *
+ * 
+ * + * @author egan + * + * email egzosn@gmail.com + * date 2019/04/15 22:58 + */ +public enum YiJiTransactionType implements TransactionType { + /** + * 跳转微支付 + */ + commonWchatTradeRedirect("commonWchatTradeRedirect"), + /** + * 跳转收银台支付 + */ + commonTradePay("commonTradePay"), + /** + * 跨境订单同步 + */ + corderRemittanceSynOrder("corderRemittanceSynOrder"), + /** + * 国际转账 + */ + applyRemittranceWithSynOrder("applyRemittranceWithSynOrder") + +; + + private String method; + + YiJiTransactionType(String method) { + this.method = method; + } + + @Override + public String getType() { + return this.name(); + } + + /** + * 获取接口名称 + * @return 接口名称 + */ + @Override + public String getMethod() { + return this.method; + } + +} -- Gitee From 9d4aa5a22629660918af6436431fbdf9863a3cb7 Mon Sep 17 00:00:00 2001 From: egzosn Date: Tue, 16 Apr 2019 10:05:24 +0800 Subject: [PATCH 020/299] =?UTF-8?q?=E4=BB=A3=E7=A0=81=E6=95=B4=E7=90=86?= =?UTF-8?q?=E4=B8=8E=E4=BC=98=E5=8C=96=EF=BC=8C=E5=88=B7=E8=84=B8=E6=94=AF?= =?UTF-8?q?=E4=BB=98=E6=A1=88=E4=BE=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 2 +- .../com/egzosn/pay/demo/controller/PayPalPayController.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index fa4a1fd..61d8116 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -全能支付Java开发工具包.优雅的轻量级支付模块集成支付对接支付整合(微信支付,支付宝,银联,友店,富友,跨境支付paypal,payoneer(P卡派安盈))app,扫码,即时到帐刷卡付条码付转账服务商模式、支持多种支付类型多支付账户,支付与业务完全剥离,简单几行代码即可实现支付,简单快速完成支付模块的开发,可轻松嵌入到任何系统里 目前仅是一个开发工具包(即SDK),只提供简单Web实现,建议使用maven或gradle引用本项目即可使用本SDK提供的各种支付相关的功能 +全能支付Java开发工具包.优雅的轻量级支付模块集成支付对接支付整合(微信支付,支付宝,银联,友店,富友,跨境支付paypal,payoneer(P卡派安盈))app,扫码,即时到帐刷卡付条码付刷脸付转账服务商模式、支持多种支付类型多支付账户,支付与业务完全剥离,简单几行代码即可实现支付,简单快速完成支付模块的开发,可轻松嵌入到任何系统里 目前仅是一个开发工具包(即SDK),只提供简单Web实现,建议使用maven或gradle引用本项目即可使用本SDK提供的各种支付相关的功能 ### 特性 diff --git a/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/PayPalPayController.java b/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/PayPalPayController.java index 929299e..8e37cdd 100644 --- a/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/PayPalPayController.java +++ b/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/PayPalPayController.java @@ -44,7 +44,7 @@ public class PayPalPayController { storage.setClientSecret("EBMIjAag6NiRdXZxteTv0amEsmKN345xJv3bN7f_HRXSqcRJlW7PXhYXjI9sk5I4nKYOHgeqzhXCXKFo"); storage.setTest(true); //发起付款后的页面转跳地址 - storage.setReturnUrl("http://www.egzosn.com/pay/success"); + storage.setReturnUrl("http://www.egzosn.com/payPal/payBack.json"); //取消按钮转跳地址,这里用异步通知地址的兼容的做法 storage.setNotifyUrl("http://www.egzosn.com/pay/cancel"); service = new PayPalPayService(storage); -- Gitee From 5fa8d3881e5fc59a1477b37672a73a9dd167d4ed Mon Sep 17 00:00:00 2001 From: egzosn Date: Tue, 16 Apr 2019 14:00:16 +0800 Subject: [PATCH 021/299] =?UTF-8?q?=E5=BE=AE=E4=BF=A1=E6=B2=99=E7=AE=B1?= =?UTF-8?q?=E6=A8=A1=E5=BC=8F=E8=8E=B7=E5=8F=96=E5=AF=86=E9=92=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/egzosn/pay/wx/api/WxPayService.java | 210 +++++++++++------- .../egzosn/pay/wx/bean/WxTransactionType.java | 11 +- 2 files changed, 128 insertions(+), 93 deletions(-) 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 eb55f27..9163ad5 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 @@ -17,6 +17,7 @@ import com.egzosn.pay.wx.bean.WxPayError; import com.egzosn.pay.wx.bean.WxTransactionType; import com.egzosn.pay.common.util.XML; import com.egzosn.pay.wx.bean.WxTransferType; + import java.awt.image.BufferedImage; import java.io.IOException; import java.io.InputStream; @@ -31,9 +32,9 @@ import static com.egzosn.pay.wx.bean.WxTransferType.*; * * @author egan *
- *         email egzosn@gmail.com
- *         date 2016-5-18 14:09:01
- *         
+ * email egzosn@gmail.com + * date 2016-5-18 14:09:01 + * */ public class WxPayService extends BasePayService { @@ -57,44 +58,48 @@ public class WxPayService extends BasePayService { private static final String HMACSHA256 = "HMACSHA256"; private static final String RETURN_MSG_CODE = "return_msg"; private static final String RESULT_CODE = "result_code"; - - - + private static final String MCH_ID = "mch_id"; + private static final String NONCE_STR = "nonce_str"; /** * 创建支付服务 + * * @param payConfigStorage 微信对应的支付配置 */ public WxPayService(WxPayConfigStorage payConfigStorage) { super(payConfigStorage); } + /** * 创建支付服务 + * * @param payConfigStorage 微信对应的支付配置 - * @param configStorage 微信对应的网络配置,包含代理配置、ssl证书配置 + * @param configStorage 微信对应的网络配置,包含代理配置、ssl证书配置 */ public WxPayService(WxPayConfigStorage payConfigStorage, HttpConfigStorage configStorage) { super(payConfigStorage, configStorage); } + /** * 设置支付配置 + * * @param payConfigStorage 支付配置 */ @Override public BasePayService setPayConfigStorage(WxPayConfigStorage payConfigStorage) { String signType = payConfigStorage.getSignType(); - if (HMAC_SHA256.equals(signType)){ + if (HMAC_SHA256.equals(signType)) { payConfigStorage.setSignType(HMACSHA256); } this.payConfigStorage = payConfigStorage; return this; } + /** * 根据交易类型获取url * * @param transactionType 交易类型 - * * @return 请求url */ private String getUrl(TransactionType transactionType) { @@ -111,12 +116,12 @@ public class WxPayService extends BasePayService { @Override public boolean verify(Map params) { - if (!(SUCCESS.equals(params.get(RETURN_CODE)) && SUCCESS.equals(params.get(RESULT_CODE)))){ + if (!(SUCCESS.equals(params.get(RETURN_CODE)) && SUCCESS.equals(params.get(RESULT_CODE)))) { LOG.debug(String.format("微信支付异常:return_code=%s,参数集=%s", params.get(RETURN_CODE), params)); return false; } - if(null == params.get(SIGN)) { + if (null == params.get(SIGN)) { LOG.debug("微信支付异常:签名为空!out_trade_no=" + params.get("out_trade_no")); return false; } @@ -165,13 +170,13 @@ public class WxPayService extends BasePayService { Map parameters = new TreeMap(); parameters.put(APPID, payConfigStorage.getAppid()); - parameters.put("mch_id", payConfigStorage.getMchId()); + parameters.put(MCH_ID, payConfigStorage.getMchId()); //判断如果是服务商模式信息则加入 - if (!StringUtils.isEmpty(payConfigStorage.getSubAppid()) && !StringUtils.isEmpty(payConfigStorage.getSubMchId())){ + if (!StringUtils.isEmpty(payConfigStorage.getSubAppid()) && !StringUtils.isEmpty(payConfigStorage.getSubMchId())) { parameters.put("sub_appid", payConfigStorage.getSubAppid()); parameters.put("sub_mch_id", payConfigStorage.getSubMchId()); } - parameters.put("nonce_str", SignUtils.randomStr()); + parameters.put(NONCE_STR, SignUtils.randomStr()); return parameters; @@ -194,24 +199,24 @@ public class WxPayService extends BasePayService { // parameters.put("detail", order.getBody()); // 订单号 parameters.put("out_trade_no", order.getOutTradeNo()); - parameters.put("spbill_create_ip", StringUtils.isEmpty(order.getSpbillCreateIp()) ? "192.168.1.150" : order.getSpbillCreateIp() ); + parameters.put("spbill_create_ip", StringUtils.isEmpty(order.getSpbillCreateIp()) ? "192.168.1.150" : order.getSpbillCreateIp()); // 总金额单位为分 - parameters.put("total_fee", Util.conversionCentAmount( order.getPrice())); - if (StringUtils.isNotEmpty(order.getAddition())){ + parameters.put("total_fee", Util.conversionCentAmount(order.getPrice())); + if (StringUtils.isNotEmpty(order.getAddition())) { parameters.put("attach", order.getAddition()); } parameters.put("notify_url", payConfigStorage.getNotifyUrl()); parameters.put("trade_type", order.getTransactionType().getType()); - if (null != order.getExpirationTime()){ + if (null != order.getExpirationTime()) { parameters.put("time_start", DateUtils.formatDate(new Date(), DateUtils.YYYYMMDDHHMMSS)); - parameters.put("time_expire", DateUtils.formatDate(order.getExpirationTime(), DateUtils.YYYYMMDDHHMMSS)); + parameters.put("time_expire", DateUtils.formatDate(order.getExpirationTime(), DateUtils.YYYYMMDDHHMMSS)); } ((WxTransactionType) order.getTransactionType()).setAttribute(parameters, order); - setSign(parameters); + setSign(parameters); String requestXML = XML.getMap2Xml(parameters); - if (LOG.isDebugEnabled()){ + if (LOG.isDebugEnabled()) { LOG.debug("requestXML:" + requestXML); } //调起支付的参数列表 @@ -240,7 +245,7 @@ public class WxPayService extends BasePayService { // 对微信返回的数据进行校验 if (verify(result)) { //如果是扫码支付或者刷卡付无需处理,直接返回 - if (((WxTransactionType)order.getTransactionType()).isReturn()) { + if (((WxTransactionType) order.getTransactionType()).isReturn()) { return result; } @@ -250,14 +255,14 @@ public class WxPayService extends BasePayService { params.put("signType", payConfigStorage.getSignType()); params.put("appId", payConfigStorage.getAppid()); params.put("timeStamp", System.currentTimeMillis() / 1000); - params.put("nonceStr", result.get("nonce_str")); + params.put("nonceStr", result.get(NONCE_STR)); params.put("package", "prepay_id=" + result.get("prepay_id")); } else if (WxTransactionType.APP == order.getTransactionType()) { params.put("partnerid", payConfigStorage.getPid()); params.put(APPID, payConfigStorage.getAppid()); params.put("prepayid", result.get("prepay_id")); params.put("timestamp", System.currentTimeMillis() / 1000); - params.put("noncestr", result.get("nonce_str")); + params.put("noncestr", result.get(NONCE_STR)); params.put("package", "Sign=WXPay"); } String paySign = createSign(SignUtils.parameterText(params), payConfigStorage.getInputCharset()); @@ -276,7 +281,7 @@ public class WxPayService extends BasePayService { */ private Map setSign(Map parameters) { String signType = payConfigStorage.getSignType(); - if (HMACSHA256.equals(signType)){ + if (HMACSHA256.equals(signType)) { signType = HMAC_SHA256; } parameters.put("sign_type", signType); @@ -285,6 +290,31 @@ public class WxPayService extends BasePayService { return parameters; } + + /** + * 获取验签秘钥 + * + * @return 验签秘钥 + */ + private String getKeyPrivate() { + if (!payConfigStorage.isTest()) { + return payConfigStorage.getKeyPrivate(); + } + SortedMap parameters = new TreeMap(); + parameters.put(MCH_ID, payConfigStorage.getMchId()); + parameters.put(NONCE_STR, SignUtils.randomStr()); + + String sign = createSign(SignUtils.parameterText(parameters, "&", SIGN, "appId"), payConfigStorage.getInputCharset(), false); + parameters.put(SIGN, sign); + + JSONObject result = requestTemplate.postForObject(getUrl(WxTransactionType.GETSIGNKEY), XML.getMap2Xml(parameters), JSONObject.class); + if (SUCCESS.equals(result.get(RETURN_CODE))) { + return result.getString("sandbox_signkey"); + } + LOG.error("获取sandbox_signkey失败", new PayErrorException(new PayException(result.getString(RETURN_CODE), result.getString(RETURN_MSG_CODE), result.toJSONString()))); + return null; + } + /** * 签名 * @@ -294,8 +324,24 @@ public class WxPayService extends BasePayService { */ @Override public String createSign(String content, String characterEncoding) { + + return createSign(content, characterEncoding, payConfigStorage.isTest()); + } + /** + * 签名 + * + * @param content 需要签名的内容 不包含key + * @param characterEncoding 字符编码 + * @param test 是否为沙箱环境 + * @return 签名结果 + */ + public String createSign(String content, String characterEncoding, boolean test) { SignUtils signUtils = SignUtils.valueOf(payConfigStorage.getSignType().toUpperCase()); - return signUtils.createSign(content + "&key=" + (signUtils == SignUtils.MD5 ? "" : payConfigStorage.getKeyPrivate()) , payConfigStorage.getKeyPrivate(), characterEncoding).toUpperCase(); + String keyPrivate = payConfigStorage.getKeyPrivate(); + if (test){ + keyPrivate = getKeyPrivate(); + } + return signUtils.createSign(content + "&key=" + (signUtils == SignUtils.MD5 ? "" : keyPrivate), keyPrivate, characterEncoding).toUpperCase(); } /** @@ -356,7 +402,7 @@ public class WxPayService extends BasePayService { throw new PayErrorException(new WxPayError((String) orderInfo.get(RETURN_CODE), (String) orderInfo.get(RETURN_MSG_CODE))); } if (WxTransactionType.MWEB.name().equals(orderInfo.get("trade_type"))) { - return String.format("",orderInfo.get("mweb_url"), StringUtils.isEmpty(payConfigStorage.getReturnUrl()) ? "" : "&redirect_url=" + URLEncoder.encode(payConfigStorage.getReturnUrl())); + return String.format("", orderInfo.get("mweb_url"), StringUtils.isEmpty(payConfigStorage.getReturnUrl()) ? "" : "&redirect_url=" + URLEncoder.encode(payConfigStorage.getReturnUrl())); } throw new UnsupportedOperationException(); @@ -406,7 +452,6 @@ public class WxPayService extends BasePayService { } - /** * 交易关闭接口 * @@ -423,8 +468,8 @@ public class WxPayService extends BasePayService { /** * 交易交易撤销 * - * @param transactionId 支付平台订单号 - * @param outTradeNo 商户单号 + * @param transactionId 支付平台订单号 + * @param outTradeNo 商户单号 * @return 返回支付方交易撤销后的结果 */ @Override @@ -450,8 +495,8 @@ public class WxPayService extends BasePayService { } - private Map setParameters(Map parameters, String key, String value){ - if (!StringUtils.isEmpty(value)){ + private Map setParameters(Map parameters, String key, String value) { + if (!StringUtils.isEmpty(value)) { parameters.put(key, value); } return parameters; @@ -460,7 +505,7 @@ public class WxPayService extends BasePayService { /** * 申请退款接口 * - * @param refundOrder 退款订单信息 + * @param refundOrder 退款订单信息 * @return 返回支付方申请退款后的结果 */ @Override @@ -481,9 +526,6 @@ public class WxPayService extends BasePayService { } - - - /** * 查询退款 * @@ -512,7 +554,7 @@ public class WxPayService extends BasePayService { setParameters(parameters, "out_refund_no", refundOrder.getRefundNo()); //设置签名 setSign(parameters); - return requestTemplate.postForObject(getUrl( WxTransactionType.REFUNDQUERY), XML.getMap2Xml(parameters) , JSONObject.class); + return requestTemplate.postForObject(getUrl(WxTransactionType.REFUNDQUERY), XML.getMap2Xml(parameters), JSONObject.class); } @@ -538,10 +580,10 @@ public class WxPayService extends BasePayService { setSign(parameters); String respStr = requestTemplate.postForObject(getUrl(WxTransactionType.DOWNLOADBILL), XML.getMap2Xml(parameters), String.class); if (respStr.indexOf("<") == 0) { - return XML.toJSONObject(respStr); + return XML.toJSONObject(respStr); } - Map ret = new HashMap(); + Map ret = new HashMap(3); ret.put(RETURN_CODE, SUCCESS); ret.put(RETURN_MSG_CODE, "ok"); ret.put("data", respStr); @@ -549,7 +591,6 @@ public class WxPayService extends BasePayService { } - /** * @param transactionIdOrBillDate 支付平台订单号或者账单类型, 具体请 类型为{@link String }或者 {@link Date },类型须强制限制,类型不对应则抛出异常{@link PayErrorException} * @param outTradeNoBillType 商户单号或者 账单类型 @@ -557,49 +598,48 @@ public class WxPayService extends BasePayService { * @return 返回支付方对应接口的结果 */ @Override - public Map secondaryInterface(Object transactionIdOrBillDate, String outTradeNoBillType, TransactionType transactionType) { + public Map secondaryInterface(Object transactionIdOrBillDate, String outTradeNoBillType, TransactionType transactionType) { 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); } throw new PayErrorException(new PayException(FAILURE, "非法类型异常:" + transactionIdOrBillDate.getClass())); } - if (!(null == transactionIdOrBillDate || transactionIdOrBillDate instanceof String)){ + if (!(null == transactionIdOrBillDate || transactionIdOrBillDate instanceof String)) { throw new PayErrorException(new PayException(FAILURE, "非法类型异常:" + transactionIdOrBillDate.getClass())); } //获取公共参数 Map parameters = getPublicParameters(); - if (StringUtils.isEmpty((String)transactionIdOrBillDate)){ + if (StringUtils.isEmpty((String) transactionIdOrBillDate)) { parameters.put("out_trade_no", outTradeNoBillType); - }else { + } else { parameters.put("transaction_id", transactionIdOrBillDate); } //设置签名 setSign(parameters); - return requestTemplate.postForObject(getUrl(transactionType), XML.getMap2Xml(parameters) , JSONObject.class); + return requestTemplate.postForObject(getUrl(transactionType), XML.getMap2Xml(parameters), JSONObject.class); } /** * 转账 * * @param order 转账订单 - *
-     *
-     * 注意事项:
-     * ◆ 当返回错误码为“SYSTEMERROR”时,请不要更换商户订单号,一定要使用原商户订单号重试,否则可能造成重复支付等资金风险。
-     * ◆ XML具有可扩展性,因此返回参数可能会有新增,而且顺序可能不完全遵循此文档规范,如果在解析回包的时候发生错误,请商户务必不要换单重试,请商户联系客服确认付款情况。如果有新回包字段,会更新到此API文档中。
-     * ◆ 因为错误代码字段err_code的值后续可能会增加,所以商户如果遇到回包返回新的错误码,请商户务必不要换单重试,请商户联系客服确认付款情况。如果有新的错误码,会更新到此API文档中。
-     * ◆ 错误代码描述字段err_code_des只供人工定位问题时做参考,系统实现时请不要依赖这个字段来做自动化处理。
+     *              
      *
-     *
+ * 注意事项: + * ◆ 当返回错误码为“SYSTEMERROR”时,请不要更换商户订单号,一定要使用原商户订单号重试,否则可能造成重复支付等资金风险。 + * ◆ XML具有可扩展性,因此返回参数可能会有新增,而且顺序可能不完全遵循此文档规范,如果在解析回包的时候发生错误,请商户务必不要换单重试,请商户联系客服确认付款情况。如果有新回包字段,会更新到此API文档中。 + * ◆ 因为错误代码字段err_code的值后续可能会增加,所以商户如果遇到回包返回新的错误码,请商户务必不要换单重试,请商户联系客服确认付款情况。如果有新的错误码,会更新到此API文档中。 + * ◆ 错误代码描述字段err_code_des只供人工定位问题时做参考,系统实现时请不要依赖这个字段来做自动化处理。 * + *
* @return 对应的转账结果 */ @Override @@ -609,34 +649,35 @@ public class WxPayService extends BasePayService { parameters.put("partner_trade_no", order.getOutNo()); parameters.put("amount", Util.conversionCentAmount(order.getAmount())); - if (!StringUtils.isEmpty(order.getRemark())){ + if (!StringUtils.isEmpty(order.getRemark())) { parameters.put("desc", order.getRemark()); } - parameters.put("nonce_str", SignUtils.randomStr()); - if (null != order.getTransferType() && TRANSFERS == order.getTransferType()){ + parameters.put(NONCE_STR, SignUtils.randomStr()); + if (null != order.getTransferType() && TRANSFERS == order.getTransferType()) { transfers(parameters, order); parameters.put("mchid", payConfigStorage.getPid()); - }else { - parameters.put("mch_id", payConfigStorage.getPid()); + } else { + parameters.put(MCH_ID, payConfigStorage.getPid()); order.setTransferType(WxTransferType.PAY_BANK); payBank(parameters, order); } parameters.put(SIGN, createSign(SignUtils.parameterText(parameters, "&", SIGN), payConfigStorage.getInputCharset())); - return getHttpRequestTemplate().postForObject(getUrl(order.getTransferType()), XML.getMap2Xml(parameters), JSONObject.class); + return getHttpRequestTemplate().postForObject(getUrl(order.getTransferType()), XML.getMap2Xml(parameters), JSONObject.class); } /** * 转账到余额所需要参数 + * * @param parameters 参数信息 - * @param order 转账订单 + * @param order 转账订单 * @return 包装后参数信息 *

- * 企业付款到零钱 - * 商户企业付款到银行卡 + * 企业付款到零钱 + * 商户企业付款到银行卡 *

*/ - public Map transfers(Map parameters, TransferOrder order){ + public Map transfers(Map parameters, TransferOrder order) { //转账到余额, 申请商户号的appid或商户号绑定的appid parameters.put("mch_appid", payConfigStorage.getAppid()); parameters.put("openid", order.getPayeeAccount()); @@ -644,7 +685,7 @@ public class WxPayService extends BasePayService { //默认不校验真实姓名 parameters.put("check_name", "NO_CHECK"); //当存在时候 校验收款用户真实姓名 - if (!StringUtils.isEmpty(order.getPayeeName())){ + if (!StringUtils.isEmpty(order.getPayeeName())) { parameters.put("check_name", "FORCE_CHECK"); parameters.put("re_user_name", order.getPayeeName()); } @@ -653,11 +694,12 @@ public class WxPayService extends BasePayService { /** * 转账到银行卡所需要参数 + * * @param parameters 参数信息 - * @param order 转账订单 + * @param order 转账订单 * @return 包装后参数信息 */ - public Map payBank(Map parameters, TransferOrder order){ + public Map payBank(Map parameters, TransferOrder order) { parameters.put("enc_bank_no", keyPublic(order.getPayeeAccount())); parameters.put("enc_true_name", keyPublic(order.getPayeeName())); @@ -669,43 +711,41 @@ public class WxPayService extends BasePayService { /** * 转账查询 * - * @param outNo 商户转账订单号 + * @param outNo 商户转账订单号 * @param wxTransferType 微信转账类型,.....这里没办法了只能这样写(┬_┬),请见谅 {@link com.egzosn.pay.wx.bean.WxTransferType} - * - *

- * 企业付款到零钱 - * 商户企业付款到银行卡 - *

+ *

+ *

+ * 企业付款到零钱 + * 商户企业付款到银行卡 + *

* @return 对应的转账订单 */ @Override public Map transferQuery(String outNo, String wxTransferType) { Map parameters = new TreeMap(); - parameters.put("mch_id", payConfigStorage.getPid()); + parameters.put(MCH_ID, payConfigStorage.getPid()); parameters.put("partner_trade_no", outNo); - parameters.put("nonce_str", SignUtils.randomStr()); + parameters.put(NONCE_STR, SignUtils.randomStr()); parameters.put(SIGN, createSign(SignUtils.parameterText(parameters, "&", SIGN), payConfigStorage.getInputCharset())); - if (StringUtils.isEmpty(wxTransferType)){ + if (StringUtils.isEmpty(wxTransferType)) { throw new PayErrorException(new WxPayError(FAILURE, "微信转账类型 #transferQuery(String outNo, String wxTransferType) 必填,详情com.egzosn.pay.wx.bean.WxTransferType")); } //如果类型为余额方式 - if (TRANSFERS.getType().equals(wxTransferType) || GETTRANSFERINFO.getType().equals(wxTransferType)){ - return getHttpRequestTemplate().postForObject(getUrl(GETTRANSFERINFO), XML.getMap2Xml(parameters), JSONObject.class); + if (TRANSFERS.getType().equals(wxTransferType) || GETTRANSFERINFO.getType().equals(wxTransferType)) { + return getHttpRequestTemplate().postForObject(getUrl(GETTRANSFERINFO), XML.getMap2Xml(parameters), JSONObject.class); } //默认查询银行卡的记录 - return getHttpRequestTemplate().postForObject(getUrl(QUERY_BANK), XML.getMap2Xml(parameters), JSONObject.class); + return getHttpRequestTemplate().postForObject(getUrl(QUERY_BANK), XML.getMap2Xml(parameters), JSONObject.class); } - - public String keyPublic(String content){ + public String keyPublic(String content) { try { - return RSA2.encrypt(content, payConfigStorage.getKeyPublic(), CIPHER_ALGORITHM, payConfigStorage.getInputCharset()); + return RSA2.encrypt(content, payConfigStorage.getKeyPublic(), CIPHER_ALGORITHM, payConfigStorage.getInputCharset()); } catch (Exception e) { - throw new PayErrorException(new WxPayError(FAILURE, e.getLocalizedMessage())); + throw new PayErrorException(new WxPayError(FAILURE, e.getLocalizedMessage())); } } - } 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 80787a9..990d16c 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 @@ -131,15 +131,10 @@ public enum WxTransactionType implements TransactionType { */ DOWNLOADBILL("pay/downloadbill"), /** - * 银行卡转账 + * 获取验签秘钥,沙箱使用 */ - @Deprecated - BANK("mmpaysptrans/pay_bank"), - /** - * 转账查询 - */ - @Deprecated - QUERY_BANK("mmpaysptrans/query_bank") + GETSIGNKEY("pay/getsignkey"), + ; WxTransactionType(String method) { -- Gitee From 1b7b2f7c90aed0ac8d210cb8e070afb1798339f8 Mon Sep 17 00:00:00 2001 From: egan Date: Tue, 16 Apr 2019 23:14:28 +0800 Subject: [PATCH 022/299] =?UTF-8?q?=E6=96=B0=E5=A2=9E=E6=8A=BD=E5=87=BA?= =?UTF-8?q?=E8=B4=A7=E5=B8=81=E7=B1=BB=E5=9E=8B=E6=8E=A5=E5=8F=A3=EF=BC=8C?= =?UTF-8?q?=E5=9B=BD=E5=AE=B6=E5=9C=B0=E5=8C=BA=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/egzosn/pay/ali/api/AliPayService.java | 2 +- .../pay/ali/before/api/AliPayService.java | 11 ++++ .../egzosn/pay/common/bean/CountryCode.java | 22 ++++++++ .../com/egzosn/pay/common/bean/CurType.java | 39 ++++---------- .../pay/common/bean/DefaultCountryCode.java | 47 ++++++++++++++++ .../pay/common/bean/DefaultCurType.java | 54 +++++++++++++++++++ .../egzosn/pay/common/bean/TransferOrder.java | 53 +++++++++++++++++- .../pay/payoneer/api/PayoneerPayService.java | 2 +- .../pay/paypal/api/PayPalPayService.java | 10 ++-- .../egzosn/pay/union/api/UnionPayService.java | 15 +++++- .../wx/youdian/api/WxYouDianPayService.java | 12 ++--- .../com/egzosn/pay/wx/api/WxPayService.java | 2 +- pom.xml | 2 +- 13 files changed, 227 insertions(+), 44 deletions(-) create mode 100644 pay-java-common/src/main/java/com/egzosn/pay/common/bean/CountryCode.java create mode 100644 pay-java-common/src/main/java/com/egzosn/pay/common/bean/DefaultCountryCode.java create mode 100644 pay-java-common/src/main/java/com/egzosn/pay/common/bean/DefaultCurType.java 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 2bd1594..baac1c6 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 @@ -282,7 +282,7 @@ public class AliPayService extends BasePayService { String bizContent = (String) orderInfo.remove(BIZ_CONTENT); formHtml.append(getReqUrl()).append("?").append(UriVariables.getMapToParameters(orderInfo)) .append("\" method=\"").append(method.name().toLowerCase()).append("\">"); - formHtml.append(""); + formHtml.append(""); formHtml.append(""); formHtml.append(""); 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 dff7756..2e3956c 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 @@ -508,6 +508,17 @@ public class AliPayService extends BasePayService { return null; } + /** + * 获取支付请求地址 + * + * @param transactionType 交易类型 + * @return 请求地址 + */ + @Override + public String getReqUrl(TransactionType transactionType) { + return null; + } + /** * 获取biz_content。请求参数的集合 不包含下载账单 diff --git a/pay-java-common/src/main/java/com/egzosn/pay/common/bean/CountryCode.java b/pay-java-common/src/main/java/com/egzosn/pay/common/bean/CountryCode.java new file mode 100644 index 0000000..443a0da --- /dev/null +++ b/pay-java-common/src/main/java/com/egzosn/pay/common/bean/CountryCode.java @@ -0,0 +1,22 @@ +package com.egzosn.pay.common.bean; + +/** + * 国家代码 + * @author egan + * email egzosn@gmail.com + * date 2019/4/16.22:43 + */ +public interface CountryCode { + + /** + * 获取国家代码 + * @return 国家代码 + */ + String getCode(); + + /** + * 获取国家名称 + * @return + */ + String getName(); +} diff --git a/pay-java-common/src/main/java/com/egzosn/pay/common/bean/CurType.java b/pay-java-common/src/main/java/com/egzosn/pay/common/bean/CurType.java index 4917abe..7ec8e34 100644 --- a/pay-java-common/src/main/java/com/egzosn/pay/common/bean/CurType.java +++ b/pay-java-common/src/main/java/com/egzosn/pay/common/bean/CurType.java @@ -1,39 +1,22 @@ package com.egzosn.pay.common.bean; /** - * 货币类型 - * @author Actinia - *
- * email hayesfu@qq.com
- * create 2017 2017/1/16
- * 
+ * 基础货币类型 + * @author egan + * email egzosn@gmail.com + * date 2019/4/16.20:55 */ -public enum CurType { - - CNY("人民币"), - USD("美元"), - HKD("港币"), - MOP("澳门元"), - EUR("欧元"), - TWD("新台币"), - KRW("韩元"), - JPY("日元"), - SGD("新加坡元"), - AUD("澳大利亚元"); +public interface CurType { /** - * 币种名称 + * 获取货币类型 + * @return 货币类型 */ - private String name; - //索引 - private int index; + String getType(); /** - * 构造函数 - * @param name + * 货币名称 + * @return 货币名称 */ - CurType(String name) { - this.name = name; - } - + String getName(); } diff --git a/pay-java-common/src/main/java/com/egzosn/pay/common/bean/DefaultCountryCode.java b/pay-java-common/src/main/java/com/egzosn/pay/common/bean/DefaultCountryCode.java new file mode 100644 index 0000000..39e5184 --- /dev/null +++ b/pay-java-common/src/main/java/com/egzosn/pay/common/bean/DefaultCountryCode.java @@ -0,0 +1,47 @@ +package com.egzosn.pay.common.bean; + +/** + * 默认的国家地区代码 + * @author egan + * email egzosn@gmail.com + * date 2019/4/16.22:43 + */ +public enum DefaultCountryCode implements CountryCode{ + + CHN("中国"), + USA("美国"), + JPN("日本"), + HKG("香港"), + GBR("英国"), + MAC("澳门"), + TWN("中国台湾"), + ; + /** + * 国家名称 + */ + private String name; + + DefaultCountryCode(String name) { + this.name = name; + } + + /** + * 获取国家代码 + * + * @return 国家代码 + */ + @Override + public String getCode() { + return this.name(); + } + + /** + * 获取国家名称 + * + * @return + */ + @Override + public String getName() { + return name; + } +} diff --git a/pay-java-common/src/main/java/com/egzosn/pay/common/bean/DefaultCurType.java b/pay-java-common/src/main/java/com/egzosn/pay/common/bean/DefaultCurType.java new file mode 100644 index 0000000..51f509e --- /dev/null +++ b/pay-java-common/src/main/java/com/egzosn/pay/common/bean/DefaultCurType.java @@ -0,0 +1,54 @@ +package com.egzosn.pay.common.bean; + +/** + * 基础货币类型 + * @author Actinia + *
+ * email hayesfu@qq.com
+ * create 2017 2017/1/16
+ * 
+ */ +public enum DefaultCurType implements CurType{ + + CNY("人民币"), + USD("美元"), + HKD("港币"), + MOP("澳门元"), + EUR("欧元"), + TWD("新台币"), + KRW("韩元"), + JPY("日元"), + SGD("新加坡元"), + AUD("澳大利亚元"); + /** + * 币种名称 + */ + private String name; + + /** + * 构造函数 + * @param name + */ + DefaultCurType(String name) { + this.name = name; + } + + /** + * 获取货币类型 + * + * @return 货币类型 + */ + @Override + public String getType() { + return this.name(); + } + /** + * 货币名称 + * + * @return 货币名称 + */ + @Override + public String getName() { + return name; + } +} diff --git a/pay-java-common/src/main/java/com/egzosn/pay/common/bean/TransferOrder.java b/pay-java-common/src/main/java/com/egzosn/pay/common/bean/TransferOrder.java index 826f4a7..2b89117 100644 --- a/pay-java-common/src/main/java/com/egzosn/pay/common/bean/TransferOrder.java +++ b/pay-java-common/src/main/java/com/egzosn/pay/common/bean/TransferOrder.java @@ -12,13 +12,18 @@ import java.math.BigDecimal; */ public class TransferOrder { + /** + * 转账批次订单单号 + */ + private String batchNo; + /** * 转账订单单号 */ private String outNo; /** - * 收款方账户, 用户openid + * 收款方账户, 用户openid,卡号等等 */ private String payeeAccount ; @@ -36,6 +41,11 @@ public class TransferOrder { * 收款人名称 */ private String payeeName; + /** + * 收款人地址 + */ + private String payeeAddress; + /** * 备注 */ @@ -46,10 +56,19 @@ public class TransferOrder { */ private Bank bank; + /** + * 收款开户行地址 + */ + private String payeeBankAddress; + /** * 币种 */ private CurType curType; + /** + * 国家代码 + */ + private CountryCode countryCode; /** * 转账类型,收款方账户类型,比如支付宝账户或者银行卡 */ @@ -60,6 +79,14 @@ public class TransferOrder { */ private String ip; + public String getBatchNo() { + return batchNo; + } + + public void setBatchNo(String batchNo) { + this.batchNo = batchNo; + } + public String getOutNo() { return outNo; } @@ -100,6 +127,14 @@ public class TransferOrder { this.payeeName = payeeName; } + public String getPayeeAddress() { + return payeeAddress; + } + + public void setPayeeAddress(String payeeAddress) { + this.payeeAddress = payeeAddress; + } + public String getRemark() { return remark; } @@ -116,6 +151,22 @@ public class TransferOrder { this.bank = bank; } + public String getPayeeBankAddress() { + return payeeBankAddress; + } + + public void setPayeeBankAddress(String payeeBankAddress) { + this.payeeBankAddress = payeeBankAddress; + } + + public CountryCode getCountryCode() { + return countryCode; + } + + public void setCountryCode(CountryCode countryCode) { + this.countryCode = countryCode; + } + public CurType getCurType() { return curType; } diff --git a/pay-java-payoneer/src/main/java/com/egzosn/pay/payoneer/api/PayoneerPayService.java b/pay-java-payoneer/src/main/java/com/egzosn/pay/payoneer/api/PayoneerPayService.java index 49e5b71..1b655d2 100644 --- a/pay-java-payoneer/src/main/java/com/egzosn/pay/payoneer/api/PayoneerPayService.java +++ b/pay-java-payoneer/src/main/java/com/egzosn/pay/payoneer/api/PayoneerPayService.java @@ -180,7 +180,7 @@ public class PayoneerPayService extends BasePayService im params.put("amount", Util.conversionAmount(order.getPrice())); params.put("client_reference_id", order.getOutTradeNo()); if (null == order.getCurType()) { - order.setCurType(CurType.USD); + order.setCurType(DefaultCurType.USD); } params.put("currency", order.getCurType()); params.put("description", order.getSubject()); diff --git a/pay-java-paypal/src/main/java/com/egzosn/pay/paypal/api/PayPalPayService.java b/pay-java-paypal/src/main/java/com/egzosn/pay/paypal/api/PayPalPayService.java index 2bf7714..be82995 100644 --- a/pay-java-paypal/src/main/java/com/egzosn/pay/paypal/api/PayPalPayService.java +++ b/pay-java-paypal/src/main/java/com/egzosn/pay/paypal/api/PayPalPayService.java @@ -147,9 +147,9 @@ public class PayPalPayService extends BasePayService{ public Map orderInfo(PayOrder order) { Amount amount = new Amount(); if (null == order.getCurType()){ - order.setCurType(CurType.USD); + order.setCurType(DefaultCurType.USD); } - amount.setCurrency(order.getCurType().name()); + amount.setCurrency(order.getCurType().getType()); amount.setTotal(Util.conversionAmount(order.getPrice()).toString()); Transaction transaction = new Transaction(); @@ -264,7 +264,11 @@ public class PayPalPayService extends BasePayService{ if (null != refundOrder.getRefundAmount() && BigDecimal.ZERO.compareTo( refundOrder.getRefundAmount()) == -1){ Amount amount = new Amount(); - amount.setCurrency(refundOrder.getCurType().name()); + if(null == refundOrder.getCurType()){ + refundOrder.setCurType(DefaultCurType.USD); + } + + amount.setCurrency(refundOrder.getCurType().getType()); amount.setTotal(Util.conversionAmount(refundOrder.getRefundAmount()).toString()); request.put("amount", amount); request.put("description", refundOrder.getDescription()); diff --git a/pay-java-union/src/main/java/com/egzosn/pay/union/api/UnionPayService.java b/pay-java-union/src/main/java/com/egzosn/pay/union/api/UnionPayService.java index f44d12f..0a844d1 100644 --- a/pay-java-union/src/main/java/com/egzosn/pay/union/api/UnionPayService.java +++ b/pay-java-union/src/main/java/com/egzosn/pay/union/api/UnionPayService.java @@ -96,14 +96,23 @@ public class UnionPayService extends BasePayService { return this; } - + /** + * 获取支付请求地址 + * + * @param transactionType 交易类型 + * @return 请求地址 + */ + @Override + public String getReqUrl(TransactionType transactionType) { + return (payConfigStorage.isTest() ? TEST_BASE_DOMAIN : RELEASE_BASE_DOMAIN); + } /** * 根据是否为沙箱环境进行获取请求地址 * * @return 请求地址 */ public String getReqUrl() { - return (payConfigStorage.isTest() ? TEST_BASE_DOMAIN : RELEASE_BASE_DOMAIN); + return getReqUrl(null); } public String getFrontTransUrl() { @@ -677,4 +686,6 @@ public class UnionPayService extends BasePayService { } + + } 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 701b734..56d92d1 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 @@ -67,7 +67,7 @@ public class WxYouDianPayService extends BasePayService { String sign = createSign(SignUtils.parameterText(parameters, "&", SIGN, "appId"), payConfigStorage.getInputCharset(), false); parameters.put(SIGN, sign); - JSONObject result = requestTemplate.postForObject(getUrl(WxTransactionType.GETSIGNKEY), XML.getMap2Xml(parameters), JSONObject.class); + JSONObject result = requestTemplate.postForObject(getReqUrl(WxTransactionType.GETSIGNKEY), XML.getMap2Xml(parameters), JSONObject.class); if (SUCCESS.equals(result.get(RETURN_CODE))) { return result.getString("sandbox_signkey"); } diff --git a/pom.xml b/pom.xml index adb1c25..24786e4 100644 --- a/pom.xml +++ b/pom.xml @@ -51,8 +51,8 @@ pay-java-union pay-java-payoneer pay-java-paypal - pay-java-demo pay-java-yiji + pay-java-demo -- Gitee From b41106c44bda058fbef5a00bb6451e4fdc13f59a Mon Sep 17 00:00:00 2001 From: egan Date: Tue, 16 Apr 2019 23:14:51 +0800 Subject: [PATCH 023/299] =?UTF-8?q?=E6=98=93=E6=9E=81=E4=BB=98=E5=8A=9F?= =?UTF-8?q?=E8=83=BD=E5=AE=8C=E6=88=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../egzosn/pay/yiji/api/YiJiPayService.java | 188 ++++++++---------- .../com/egzosn/pay/yiji/bean/CurType.java | 94 +++++++++ .../com/egzosn/pay/yiji/bean/YiJiBank.java | 63 ++++++ .../pay/yiji/bean/YiJiTransactionType.java | 50 ++++- 4 files changed, 281 insertions(+), 114 deletions(-) create mode 100644 pay-java-yiji/src/main/java/com/egzosn/pay/yiji/bean/CurType.java create mode 100644 pay-java-yiji/src/main/java/com/egzosn/pay/yiji/bean/YiJiBank.java diff --git a/pay-java-yiji/src/main/java/com/egzosn/pay/yiji/api/YiJiPayService.java b/pay-java-yiji/src/main/java/com/egzosn/pay/yiji/api/YiJiPayService.java index 5be93a8..374790e 100644 --- a/pay-java-yiji/src/main/java/com/egzosn/pay/yiji/api/YiJiPayService.java +++ b/pay-java-yiji/src/main/java/com/egzosn/pay/yiji/api/YiJiPayService.java @@ -1,20 +1,23 @@ package com.egzosn.pay.yiji.api; -import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONObject; import com.egzosn.pay.common.api.BasePayService; import com.egzosn.pay.common.bean.*; -import com.egzosn.pay.common.bean.result.PayException; import com.egzosn.pay.common.exception.PayErrorException; import com.egzosn.pay.common.http.HttpConfigStorage; -import com.egzosn.pay.common.http.UriVariables; -import com.egzosn.pay.common.util.MatrixToImageWriter; +import com.egzosn.pay.common.util.DateUtils; import com.egzosn.pay.common.util.Util; import com.egzosn.pay.common.util.sign.SignUtils; import com.egzosn.pay.common.util.str.StringUtils; +import com.egzosn.pay.yiji.bean.YiJiTransactionType; + import java.awt.image.BufferedImage; import java.math.BigDecimal; -import java.util.*; +import java.util.Collections; +import java.util.Date; +import java.util.Map; +import java.util.TreeMap; + /** * 易极付支付服务 @@ -30,6 +33,10 @@ public class YiJiPayService extends BasePayService { * 正式测试环境 */ private static final String HTTPS_REQ_URL = "https://api.yiji.com"; + /** + * 全球正式测试环境 + */ + private static final String HTTPS_GLOBAL_REQ_URL = "https://openapiglobal.yiji.com/gateway.html"; /** * 沙箱测试环境账号 */ @@ -40,41 +47,22 @@ public class YiJiPayService extends BasePayService { public static final String SUCCESS_CODE = "10000"; public static final String CODE = "code"; - /** - * 附加参数 - */ - public static final String PASSBACK_PARAMS = "passback_params"; - /** - * 产品代码 - */ - public static final String PRODUCT_CODE = "product_code"; - /** - * 返回地址 - */ - public static final String RETURN_URL = "return_url"; - /** - * 请求内容 - */ - public static final String BIZ_CONTENT = "biz_content"; /** * 获取对应的请求地址 * * @return 请求地址 */ public String getReqUrl(TransactionType transactionType) { - return payConfigStorage.isTest() ? DEV_REQ_URL : HTTPS_REQ_URL; - } - /** - * 获取对应的请求地址 - * - * @return 请求地址 - */ - public String getReqUrl() { - return getReqUrl(null); + if (payConfigStorage.isTest()){ + return DEV_REQ_URL; + }else if (/*YiJiTransactionType.corderRemittanceSynOrder == transactionType ||*/ YiJiTransactionType.applyRemittranceWithSynOrder == transactionType){ + return HTTPS_GLOBAL_REQ_URL; + }else { + return HTTPS_REQ_URL; + } } - public YiJiPayService(YiJiPayConfigStorage payConfigStorage, HttpConfigStorage configStorage) { super(payConfigStorage, configStorage); } @@ -84,9 +72,6 @@ public class YiJiPayService extends BasePayService { } - public String getHttpsVerifyUrl() { - return getReqUrl() + "?service=notify_verify"; - } /** * 回调校验 @@ -102,7 +87,7 @@ public class YiJiPayService extends BasePayService { return false; } - return signVerify(params, (String) params.get(SIGN)) && verifySource((String) params.get("notify_id")); + return signVerify(params, (String) params.get(SIGN)); } @@ -116,20 +101,6 @@ public class YiJiPayService extends BasePayService { @Override public boolean signVerify(Map params, String sign) { - if (params instanceof JSONObject) { - for (Map.Entry entry : params.entrySet()) { - if (SIGN.equals(entry.getKey())) { - continue; - } - TreeMap response = new TreeMap((Map )entry.getValue()); - LinkedHashMap linkedHashMap = new LinkedHashMap<>(); - linkedHashMap.put(CODE, response.remove(CODE)); - linkedHashMap.put("msg", response.remove("msg")); - linkedHashMap.putAll(response); - return SignUtils.valueOf(payConfigStorage.getSignType()).verify(JSON.toJSONString(linkedHashMap), sign, payConfigStorage.getKeyPublic(), payConfigStorage.getInputCharset()); - } - } - return SignUtils.valueOf(payConfigStorage.getSignType()).verify(params, sign, payConfigStorage.getKeyPublic(), payConfigStorage.getInputCharset()); } @@ -153,7 +124,7 @@ public class YiJiPayService extends BasePayService { * @return 请求参数 */ private Map setSign(Map parameters) { - parameters.put("sign_type", payConfigStorage.getSignType()); + parameters.put("signType", payConfigStorage.getSignType()); String sign = createSign(SignUtils.parameterText(parameters, "&", SIGN), payConfigStorage.getInputCharset()); parameters.put(SIGN, sign); @@ -185,17 +156,24 @@ public class YiJiPayService extends BasePayService { */ private Map getOrder(PayOrder order) { - Map orderInfo = getPublicParameters(order.getTransactionType()); - orderInfo.put("orderNo", order.getTradeNo()); + orderInfo.put("orderNo", order.getOutTradeNo()); orderInfo.put("outOrderNo", order.getOutTradeNo()); if (StringUtils.isNotEmpty(payConfigStorage.getSeller())){ orderInfo.put("sellerUserId", payConfigStorage.getSeller()); } + + ((YiJiTransactionType)order.getTransactionType()).setAttribute(orderInfo, order); + orderInfo.put("tradeAmount", Util.conversionAmount(order.getPrice())); + //商品条款信息 商品名称 orderInfo.put("goodsClauses", String.format("[{'name':'%s'}]", order.getBody())); - + //交易名称 + orderInfo.put("tradeName", order.getSubject()); + if (null != order.getCurType()){ + orderInfo.put("currency", order.getCurType()); + } return orderInfo; } @@ -208,7 +186,6 @@ public class YiJiPayService extends BasePayService { private Map getPublicParameters(TransactionType transactionType) { Map orderInfo = new TreeMap<>(); orderInfo.put("partnerId", payConfigStorage.getPid()); - orderInfo.put("signType", payConfigStorage.getSignType()); orderInfo.put("returnUrl", payConfigStorage.getReturnUrl()); orderInfo.put("notifyUrl", payConfigStorage.getNotifyUrl()); orderInfo.put("service", transactionType.getMethod()); @@ -248,13 +225,19 @@ public class YiJiPayService extends BasePayService { @Override public String buildRequest(Map orderInfo, MethodType method) { StringBuilder formHtml = new StringBuilder(); - formHtml.append("
\n"); + formHtml.append(""); - formHtml.append(""); - formHtml.append("
"); - formHtml.append(""); + for (Map.Entry entry : orderInfo.entrySet()) { + formHtml.append("\n"); + } + formHtml.append("\n"); + formHtml.append("\n"); + return formHtml.toString(); } @@ -268,17 +251,7 @@ public class YiJiPayService extends BasePayService { @Override public BufferedImage genQrPay(PayOrder order) { - Map orderInfo = orderInfo(order); - - - //预订单 - JSONObject result = getHttpRequestTemplate().postForObject(getReqUrl() + "?" + UriVariables.getMapToParameters(orderInfo), null, JSONObject.class); - JSONObject response = result.getJSONObject("alipay_trade_precreate_response"); - if (SUCCESS_CODE.equals(response.getString(CODE))) { - return MatrixToImageWriter.writeInfoToJpgBuff(response.getString("qr_code")); - } - throw new PayErrorException(new PayException(response.getString(CODE), response.getString("msg"), result.toJSONString())); - + return null; } /** @@ -289,14 +262,8 @@ public class YiJiPayService extends BasePayService { */ @Override public Map microPay(PayOrder order) { - Map orderInfo = orderInfo(order); - //预订单 - JSONObject result = getHttpRequestTemplate().postForObject(getReqUrl() + "?" + UriVariables.getMapToParameters(orderInfo), null, JSONObject.class); - JSONObject response = result.getJSONObject("alipay_trade_pay_response"); - if (!SUCCESS_CODE.equals(response.getString(CODE))) { - LOG.info("收款失败"); - } - return result; + + return Collections.emptyMap(); } /** @@ -308,8 +275,7 @@ public class YiJiPayService extends BasePayService { */ @Override public Map query(String tradeNo, String outTradeNo) { - return null; - + return Collections.emptyMap(); } @@ -322,23 +288,10 @@ public class YiJiPayService extends BasePayService { */ @Override public Map close(String tradeNo, String outTradeNo) { - return null; + return Collections.emptyMap(); } - /** - * 支付交易返回失败或支付系统超时,调用该接口撤销交易。 - * 如果此订单用户支付失败,易极付系统会将此订单关闭;如果用户支付成功,易极付系统会将此订单资金退还给用户。 - * 注意:只有发生支付系统超时或者支付结果未知时可调用撤销,其他正常支付的单如需实现相同功能请调用申请退款API。 - * 提交支付交易后调用【查询订单API】,没有明确的支付结果再调用【撤销订单API】。 - * - * @param tradeNo 支付平台订单号 - * @param outTradeNo 商户单号 - * @return 返回支付方交易撤销后的结果 - */ - @Override - public Map cancel(String tradeNo, String outTradeNo) { - return null; - } + /** * 申请退款接口 @@ -367,8 +320,14 @@ public class YiJiPayService extends BasePayService { */ @Override public Map refund(RefundOrder refundOrder) { - - return null; + Map orderInfo = getPublicParameters(YiJiTransactionType.tradeRefund); + orderInfo.put("orderNo", refundOrder.getOutTradeNo()); + orderInfo.put("outOrderNo", refundOrder.getOutTradeNo()); + orderInfo.put("refundAmount", refundOrder.getRefundAmount()); + orderInfo.put("refundTime", DateUtils.formatDay(refundOrder.getOrderDate())); + orderInfo.put("refundReason", refundOrder.getDescription()); + setSign(orderInfo); + return getHttpRequestTemplate().postForObject(getReqUrl(YiJiTransactionType.tradeRefund), orderInfo, JSONObject.class); } /** @@ -380,7 +339,7 @@ public class YiJiPayService extends BasePayService { */ @Override public Map refundquery(String tradeNo, String outTradeNo) { - return null; + return Collections.emptyMap(); } /** @@ -392,7 +351,7 @@ public class YiJiPayService extends BasePayService { @Override public Map refundquery(RefundOrder refundOrder) { - return null; + return Collections.emptyMap(); } @@ -406,7 +365,7 @@ public class YiJiPayService extends BasePayService { @Override public Map downloadbill(Date billDate, String billType) { - return null; + return Collections.emptyMap(); } @@ -421,19 +380,36 @@ public class YiJiPayService extends BasePayService { public Map secondaryInterface(Object tradeNoOrBillDate, String outTradeNoBillType, TransactionType transactionType) { - return null; + return Collections.emptyMap(); } /** - * 转账 + * 转账 这里外部进行调用{@link #buildRequest(Map, MethodType)} * * @param order 转账订单 * @return 对应的转账结果 */ @Override public Map transfer(TransferOrder order) { - - return null; + Map data = getPublicParameters(YiJiTransactionType.applyRemittranceWithSynOrder); + data.put("remittranceBatchNo", order.getBatchNo()); + data.put("outOrderNo", order.getOutNo()); + data.put("payAmount", Util.conversionAmount(order.getAmount()) ); + data.put("payCurrency", order.getCurType().getType()); + data.put("withdrawCurrency", DefaultCurType.CNY.getType()); + data.put("payMemo",order.getRemark()); + data.put("toCountryCode", order.getCountryCode().getCode()); + data.put("tradeUseCode", "326"); + data.put("payeeName", order.getPayeeName()); + data.put("payeeAddress", order.getPayeeAddress()); + data.put("payeeBankName", order.getBank().getCode()); + data.put("payeeBankAddress", order.getPayeeBankAddress()); + data.put("payeeBankSwiftCode", "CNAPS CODE"); + data.put("payeeBankNo", order.getPayeeAccount()); + setSign(data); + + + return data; } /** @@ -446,7 +422,7 @@ public class YiJiPayService extends BasePayService { @Override public Map transferQuery(String outNo, String tradeNo) { - return null; + return Collections.emptyMap(); } } diff --git a/pay-java-yiji/src/main/java/com/egzosn/pay/yiji/bean/CurType.java b/pay-java-yiji/src/main/java/com/egzosn/pay/yiji/bean/CurType.java new file mode 100644 index 0000000..22f2461 --- /dev/null +++ b/pay-java-yiji/src/main/java/com/egzosn/pay/yiji/bean/CurType.java @@ -0,0 +1,94 @@ +package com.egzosn.pay.yiji.bean; + +/** + * 币种 + * @author egan + * email egzosn@gmail.com + * date 2019/4/16.22:48 + */ +public enum CurType implements com.egzosn.pay.common.bean.CurType { + CNY(156, "人民币"), + USD(840, "美元"), + JPY(392, "日元"), + HKD(344, "港币"), + GBP(826, "英镑"), + EUR(978, "欧元"), + AUD(30, "澳元"), + CAD(124, "加元"), + SGD(702, "坡币"), + NZD(554, "新西"), + TWD(901, "台币"), + KRW(410, "韩元"), + DKK(208, "丹朗"), + TRY(949, "土拉"), + MYR(458, "马来"), + THB(764, "泰铢"), + INR(356, "印卢"), + PHP(608, "菲比"), + CHF(756, "瑞士"), + SEK(752, "瑞典"), + ILS(376, "以谢"), + ZAR(710, "南非"), + RUB(643, "俄卢"), + NOK(578, "挪威克朗"), + AED(784, "阿联酋"), + BRL(986, "巴西雷亚尔"), + IDR(360, "印尼卢比"), + SAR(682, "沙特里亚尔"), + MXN(484, "墨西哥比索"), + PLN(985, "波兰兹罗提"), + VND(704, "越南盾"), + CLP(152, "智利比索"), + KZT(398, "哈萨克腾格"), + CZK(203, "捷克克朗"), + EGP(818, "埃及镑"), + VEF(937, "委玻利瓦尔"), + ARS(26, "阿根廷比索"), + MOP(446, "澳门元"), + UAH(980, "乌格里夫纳"), + LBP(422, "黎巴嫩镑"), + JOD(400, "黎巴嫩镑"), + PEN(604, "秘鲁新索尔"), + PKR(586, "巴基斯坦卢比"), + RON(946, "罗马尼亚列伊"), + QAR(634, "卡塔尔里亚尔"), + KWD(414, "科威特第纳尔"), + NGN(566, "尼日利亚奈拉"), + COP(170, "哥伦比亚比索"), + HUF(348, "匈牙利福林"); + + /** + * 币种名称 + */ + private String name; + private int code; + + CurType(String name, int code) { + this.name = name; + this.code = code; + } + + /** + * 获取货币类型 + * + * @return 货币类型 + */ + @Override + public String getType() { + return this.name(); + } + + /** + * 货币名称 + * + * @return 货币名称 + */ + @Override + public String getName() { + return name; + } + + public int getCode() { + return code; + } +} diff --git a/pay-java-yiji/src/main/java/com/egzosn/pay/yiji/bean/YiJiBank.java b/pay-java-yiji/src/main/java/com/egzosn/pay/yiji/bean/YiJiBank.java new file mode 100644 index 0000000..9c443ad --- /dev/null +++ b/pay-java-yiji/src/main/java/com/egzosn/pay/yiji/bean/YiJiBank.java @@ -0,0 +1,63 @@ +package com.egzosn.pay.yiji.bean; + +import com.egzosn.pay.common.bean.Bank; + +/** + * 对应的银行列表 + * + * @author egan + *
+ *         email egzosn@gmail.com
+ *         date 2018/1/31
+ *         
+ */ +public enum YiJiBank implements Bank { + ABC("中国农业银行"), + BOC("中国银行"), + COMM("交通银行"), + CCB("中国建设银行"), + CEB("中国光大银行"), + CIB("兴业银行"), + CMB("招商银行"), + CMBC("民生银行"), + CITIC("中信银行"), + CQRCB("重庆农村商业银行"), + ICBC("中国工商银行"), + PSBC("中国邮政储蓄银行"), + SPDB("浦发银行"), + UNION("中国银联"), + CQCB("重庆银行"), + GDB("广东发展银行"), + SDB("深圳发展银行"), + HXB("华夏银行"), + CQTGB("重庆三峡银行"), + PINGANBANK("平安银行"), + BANKSH("上海银行"),; + + private String name; + + + YiJiBank(String name) { + this.name = name; + } + + /** + * 获取银行的代码 + * + * @return 银行的代码 + */ + @Override + public String getCode() { + return this.name(); + } + + /** + * 获取银行的名称 + * + * @return 银行的名称 + */ + @Override + public String getName() { + return name; + } +} diff --git a/pay-java-yiji/src/main/java/com/egzosn/pay/yiji/bean/YiJiTransactionType.java b/pay-java-yiji/src/main/java/com/egzosn/pay/yiji/bean/YiJiTransactionType.java index 144f64f..21af7d6 100644 --- a/pay-java-yiji/src/main/java/com/egzosn/pay/yiji/bean/YiJiTransactionType.java +++ b/pay-java-yiji/src/main/java/com/egzosn/pay/yiji/bean/YiJiTransactionType.java @@ -1,7 +1,11 @@ package com.egzosn.pay.yiji.bean; +import com.egzosn.pay.common.bean.PayOrder; import com.egzosn.pay.common.bean.TransactionType; +import java.util.HashMap; +import java.util.Map; + /** * 易极付交易类型 *
@@ -10,11 +14,11 @@ import com.egzosn.pay.common.bean.TransactionType;
  * 
* * @author egan - * - * email egzosn@gmail.com - * date 2019/04/15 22:58 + *

+ * email egzosn@gmail.com + * date 2019/04/15 22:58 */ -public enum YiJiTransactionType implements TransactionType { +public enum YiJiTransactionType implements TransactionType { /** * 跳转微支付 */ @@ -22,19 +26,35 @@ public enum YiJiTransactionType implements TransactionType { /** * 跳转收银台支付 */ - commonTradePay("commonTradePay"), + commonTradePay("commonTradePay"){ + public String getVersion() { + return "2.0"; + } + }, + tradeRefund("tradeRefund"), /** * 跨境订单同步 - */ + *//* corderRemittanceSynOrder("corderRemittanceSynOrder"), + */ /** * 国际转账 */ applyRemittranceWithSynOrder("applyRemittranceWithSynOrder") - -; + ; private String method; + /** + * 版本 + */ + private String version = "1.0"; + + private static final Map transactiontypes = new HashMap(); + static { + for (TransactionType type : YiJiTransactionType.values()){ + transactiontypes.put(type.getMethod(), type); + } + } YiJiTransactionType(String method) { this.method = method; @@ -45,8 +65,13 @@ public enum YiJiTransactionType implements TransactionType { return this.name(); } + public String getVersion() { + return version; + } + /** * 获取接口名称 + * * @return 接口名称 */ @Override @@ -54,4 +79,13 @@ public enum YiJiTransactionType implements TransactionType { return this.method; } + + public void setAttribute(Map parameters, PayOrder order) { + parameters.put("version", getVersion()); + } + + public static TransactionType getTransactionType(String method) { + return transactiontypes.get(method); + } + } -- Gitee From 66add7308d704c0c9cae7cc5b66833788edca364 Mon Sep 17 00:00:00 2001 From: egzosn Date: Wed, 17 Apr 2019 14:27:10 +0800 Subject: [PATCH 024/299] =?UTF-8?q?=E8=B4=A7=E5=B8=81=E7=B1=BB=E5=9E=8B?= =?UTF-8?q?=E7=9B=B8=E5=85=B3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../egzosn/pay/demo/controller/PayPalPayController.java | 8 ++------ .../egzosn/pay/demo/controller/PayoneerPayController.java | 4 ++-- .../src/main/java/com/egzosn/pay/yiji/bean/CurType.java | 4 ++-- .../com/egzosn/pay/yiji/bean/YiJiTransactionType.java | 1 + 4 files changed, 7 insertions(+), 10 deletions(-) diff --git a/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/PayPalPayController.java b/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/PayPalPayController.java index 28242cb..15735ee 100644 --- a/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/PayPalPayController.java +++ b/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/PayPalPayController.java @@ -1,12 +1,8 @@ package com.egzosn.pay.demo.controller; -import com.egzosn.pay.ali.bean.AliTransactionType; import com.egzosn.pay.common.api.PayService; -import com.egzosn.pay.common.bean.CurType; -import com.egzosn.pay.common.bean.MethodType; -import com.egzosn.pay.common.bean.PayOrder; -import com.egzosn.pay.common.bean.RefundOrder; +import com.egzosn.pay.common.bean.*; import com.egzosn.pay.common.http.HttpConfigStorage; import com.egzosn.pay.paypal.api.PayPalConfigStorage; import com.egzosn.pay.paypal.api.PayPalPayService; @@ -91,7 +87,7 @@ public class PayPalPayController { public Map refund() { // TODO 这里需要 refundAmount, curType, description, tradeNo RefundOrder order = new RefundOrder(); - order.setCurType(CurType.USD); + order.setCurType(DefaultCurType.USD); order.setDescription(" description "); order.setTradeNo("paypal 平台的单号"); order.setRefundAmount(new BigDecimal(0.01)); diff --git a/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/PayoneerPayController.java b/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/PayoneerPayController.java index f1b34be..b0afb01 100644 --- a/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/PayoneerPayController.java +++ b/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/PayoneerPayController.java @@ -102,7 +102,7 @@ public class PayoneerPayController { PayOrder order = new PayOrder("Order_payment:", "Order payment", price, UUID.randomUUID().toString().replace("-", ""), PayoneerTransactionType.CHARGE); //币种 - order.setCurType(CurType.USD); + order.setCurType(DefaultCurType.USD); //设置授权码,条码等 order.setAuthCode( userId); //支付结果 @@ -200,7 +200,7 @@ public class PayoneerPayController { @RequestMapping("transfer") public Map transfer(TransferOrder order) { order.setOutNo("商户转账订单号"); - order.setCurType(CurType.USD); + order.setCurType(DefaultCurType.USD); order.setPayeeAccount("收款方账户,用户授权所使用的userId"); order.setAmount(new BigDecimal(10)); order.setRemark("转账备注, 非必填"); diff --git a/pay-java-yiji/src/main/java/com/egzosn/pay/yiji/bean/CurType.java b/pay-java-yiji/src/main/java/com/egzosn/pay/yiji/bean/CurType.java index 22f2461..09e6771 100644 --- a/pay-java-yiji/src/main/java/com/egzosn/pay/yiji/bean/CurType.java +++ b/pay-java-yiji/src/main/java/com/egzosn/pay/yiji/bean/CurType.java @@ -57,13 +57,13 @@ public enum CurType implements com.egzosn.pay.common.bean.CurType { COP(170, "哥伦比亚比索"), HUF(348, "匈牙利福林"); + private int code; /** * 币种名称 */ private String name; - private int code; - CurType(String name, int code) { + CurType(int code, String name) { this.name = name; this.code = code; } diff --git a/pay-java-yiji/src/main/java/com/egzosn/pay/yiji/bean/YiJiTransactionType.java b/pay-java-yiji/src/main/java/com/egzosn/pay/yiji/bean/YiJiTransactionType.java index 21af7d6..365356f 100644 --- a/pay-java-yiji/src/main/java/com/egzosn/pay/yiji/bean/YiJiTransactionType.java +++ b/pay-java-yiji/src/main/java/com/egzosn/pay/yiji/bean/YiJiTransactionType.java @@ -27,6 +27,7 @@ public enum YiJiTransactionType implements TransactionType { * 跳转收银台支付 */ commonTradePay("commonTradePay"){ + @Override public String getVersion() { return "2.0"; } -- Gitee From 5e8c480c3455aa8dc0abcc5333afc6f8ee2b9e1a Mon Sep 17 00:00:00 2001 From: egzosn Date: Wed, 17 Apr 2019 14:55:58 +0800 Subject: [PATCH 025/299] =?UTF-8?q?=E8=AF=81=E4=B9=A6=E8=AE=BE=E7=BD=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pay-java-demo/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pay-java-demo/README.md b/pay-java-demo/README.md index b066e05..6cf5816 100644 --- a/pay-java-demo/README.md +++ b/pay-java-demo/README.md @@ -159,7 +159,7 @@ public class PayResponse { */ //设置ssl证书路径 - httpConfigStorage.setKeystorePath(apyAccount.getKeystorePath()); + httpConfigStorage.setKeystore(apyAccount.getKeystorePath()); //设置ssl证书对应的密码 httpConfigStorage.setStorePassword(apyAccount.getStorePassword()); return httpConfigStorage; -- Gitee From b9c6a82a373e06dd0b9f0def65a4efa7a72194ed Mon Sep 17 00:00:00 2001 From: egan Date: Wed, 17 Apr 2019 20:24:58 +0800 Subject: [PATCH 026/299] =?UTF-8?q?=E7=B1=BB=E6=B3=A8=E9=87=8A=E6=95=B4?= =?UTF-8?q?=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../egzosn/pay/common/bean/CountryCode.java | 2 +- .../pay/common/bean/DefaultCountryCode.java | 2 +- .../pay/common/http/HttpConfigStorage.java | 1 + .../java/com/egzosn/pay/common/util/XML.java | 4 +- .../pay/common/util/sign/CertDescriptor.java | 1 - .../pay/demo/controller/AliPayController.java | 10 +++-- .../pay/demo/controller/PayController.java | 40 ++++++++++++------- .../demo/controller/PayPalPayController.java | 13 +++--- .../controller/PayoneerPayController.java | 17 ++++---- .../demo/controller/UnionPayController.java | 17 ++++---- .../pay/demo/controller/WxPayController.java | 25 +++++++----- .../pay/demo/dao/ApyAccountRepository.java | 6 +-- .../egzosn/pay/demo/entity/ApyAccount.java | 4 +- .../egzosn/pay/demo/request/QueryOrder.java | 4 +- .../pay/demo/service/ApyAccountService.java | 6 +-- .../egzosn/pay/demo/service/PayResponse.java | 4 +- .../handler/FuiouPayMessageHandler.java | 2 +- .../handler/PayoneerMessageHandler.java | 6 +-- .../handler/UnionPayMessageHandler.java | 2 +- .../handler/YouDianPayMessageHandler.java | 2 +- .../interceptor/AliPayMessageInterceptor.java | 3 +- .../YoudianPayMessageInterceptor.java | 3 +- pom.xml | 4 +- 23 files changed, 102 insertions(+), 76 deletions(-) diff --git a/pay-java-common/src/main/java/com/egzosn/pay/common/bean/CountryCode.java b/pay-java-common/src/main/java/com/egzosn/pay/common/bean/CountryCode.java index 443a0da..6806e7a 100644 --- a/pay-java-common/src/main/java/com/egzosn/pay/common/bean/CountryCode.java +++ b/pay-java-common/src/main/java/com/egzosn/pay/common/bean/CountryCode.java @@ -16,7 +16,7 @@ public interface CountryCode { /** * 获取国家名称 - * @return + * @return 国家名称 */ String getName(); } diff --git a/pay-java-common/src/main/java/com/egzosn/pay/common/bean/DefaultCountryCode.java b/pay-java-common/src/main/java/com/egzosn/pay/common/bean/DefaultCountryCode.java index 39e5184..63abfc2 100644 --- a/pay-java-common/src/main/java/com/egzosn/pay/common/bean/DefaultCountryCode.java +++ b/pay-java-common/src/main/java/com/egzosn/pay/common/bean/DefaultCountryCode.java @@ -38,7 +38,7 @@ public enum DefaultCountryCode implements CountryCode{ /** * 获取国家名称 * - * @return + * @return 国家名称 */ @Override public String getName() { diff --git a/pay-java-common/src/main/java/com/egzosn/pay/common/http/HttpConfigStorage.java b/pay-java-common/src/main/java/com/egzosn/pay/common/http/HttpConfigStorage.java index 4006a8a..5f6e39f 100644 --- a/pay-java-common/src/main/java/com/egzosn/pay/common/http/HttpConfigStorage.java +++ b/pay-java-common/src/main/java/com/egzosn/pay/common/http/HttpConfigStorage.java @@ -120,6 +120,7 @@ public class HttpConfigStorage { /** * 获取证书信息 * @return 证书信息 根据 {@link #getCertStoreType()}进行区别地址与信息串 + * @throws IOException 找不到文件异常 */ public InputStream getKeystoreInputStream() throws IOException { if (null == keystore) { 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 9e89e74..2410653 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 @@ -79,6 +79,7 @@ public class XML { * 解析xml并转化为Json值 * * @param content json字符串 + * @param charset 字符编码 * @return Json值 */ public static JSONObject toJSONObject(String content, Charset charset) { @@ -264,6 +265,7 @@ public class XML { * * @param data Map类型数据 * @param rootElementName 最外层节点名称 + * @param encoding 字符编码 * @return XML格式的字符串 */ public static String getMap2Xml(Map data, String rootElementName, String encoding) { @@ -312,7 +314,7 @@ public class XML { * * @param data Map类型数据 * @param document 文档 - * @return XML格式的字符串 + * @param element 节点 */ public static void map2Xml(Map data, Document document, org.w3c.dom.Element element) { for (Map.Entry entry : data.entrySet()) { diff --git a/pay-java-common/src/main/java/com/egzosn/pay/common/util/sign/CertDescriptor.java b/pay-java-common/src/main/java/com/egzosn/pay/common/util/sign/CertDescriptor.java index f16c7d3..9d1e75d 100644 --- a/pay-java-common/src/main/java/com/egzosn/pay/common/util/sign/CertDescriptor.java +++ b/pay-java-common/src/main/java/com/egzosn/pay/common/util/sign/CertDescriptor.java @@ -214,7 +214,6 @@ public class CertDescriptor { * @param keyPwd 证书密码 * @param type 证书类型 * @return 证书对象 - * @throws IOException */ public KeyStore getKeyInfo(InputStream fxKeyFile, String keyPwd, String type) { diff --git a/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/AliPayController.java b/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/AliPayController.java index 25ab40f..625afa4 100644 --- a/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/AliPayController.java +++ b/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/AliPayController.java @@ -33,8 +33,8 @@ import java.util.UUID; * 发起支付入口 * * @author: egan - * @email egzosn@gmail.com - * @date 2016/11/18 0:25 + * email egzosn@gmail.com + * date 2016/11/18 0:25 */ @RestController @RequestMapping("ali") @@ -118,6 +118,7 @@ public class AliPayController { * 二维码支付 * @param price 金额 * @return 二维码图像 + * @throws IOException IOException */ @RequestMapping(value = "toQrPay.jpg", produces = "image/jpeg;charset=UTF-8") public byte[] toWxQrPay( BigDecimal price) throws IOException { @@ -135,7 +136,7 @@ public class AliPayController { * @return 支付结果 */ @RequestMapping(value = "microPay") - public Map microPay(BigDecimal price, String authCode) throws IOException { + public Map microPay(BigDecimal price, String authCode) { //获取对应的支付账户操作工具(可根据账户id) //条码付 PayOrder order = new PayOrder("egan order", "egan order", null == price ? new BigDecimal(0.01) : price, UUID.randomUUID().toString().replace("-", ""), AliTransactionType.BAR_CODE); @@ -167,6 +168,7 @@ public class AliPayController { * * @return 返回对应的响应码 * @see #payBack(HttpServletRequest) + * @throws IOException IOException */ @Deprecated @RequestMapping(value = "payBackBefore.json") @@ -197,7 +199,7 @@ public class AliPayController { * 业务处理在对应的PayMessageHandler里面处理,在哪里设置PayMessageHandler,详情查看{@link com.egzosn.pay.common.api.PayService#setPayMessageHandler(com.egzosn.pay.common.api.PayMessageHandler)} * * 如果未设置 {@link com.egzosn.pay.common.api.PayMessageHandler} 那么会使用默认的 {@link com.egzosn.pay.common.api.DefaultPayMessageHandler} - * + * @throws IOException IOException */ @RequestMapping(value = "payBack.json") public String payBack(HttpServletRequest request) throws IOException { 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 f8ac067..95fa828 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 @@ -39,8 +39,8 @@ import static com.egzosn.pay.demo.dao.ApyAccountRepository.apyAccounts; * 发起支付入口 * * @author: egan - * @email egzosn@gmail.com - * @date 2016/11/18 0:25 + * email egzosn@gmail.com + * date 2016/11/18 0:25 */ @RestController @RequestMapping @@ -58,7 +58,7 @@ public class PayController { /** * 这里模拟账户信息增加 * - * @param account + * @param account 支付账户信息 * @return 支付账户信息 */ @RequestMapping("add") @@ -76,6 +76,7 @@ public class PayController { * 跳到支付页面 * 针对实时支付,即时付款 * + * @param request 请求 * @param payId 账户id * @param transactionType 交易类型, 这个针对于每一个 支付类型的对应的几种交易方式 * @param bankType 针对刷卡支付,卡的类型,类型值 @@ -114,7 +115,7 @@ public class PayController { /** * 跳到支付页面 * 针对实时支付,即时付款 - * + * @param request 请求 * @return 跳到支付页面 */ @RequestMapping(value = "toWxPay.html", produces = "text/html;charset=UTF-8") @@ -169,7 +170,7 @@ public class PayController { * @return 支付结果 */ @RequestMapping(value = "microPay") - public Map microPay(Integer payId, String transactionType, BigDecimal price, String authCode) throws IOException { + public Map microPay(Integer payId, String transactionType, BigDecimal price, String authCode) { //获取对应的支付账户操作工具(可根据账户id) PayResponse payResponse = service.getPayResponse(payId); @@ -196,7 +197,9 @@ public class PayController { * @param payId 账户id * @param transactionType 交易类型, 这个针对于每一个 支付类型的对应的几种交易方式 * @param price 金额 + * * @return 二维码图像 + * @throws IOException IOException */ @RequestMapping(value = "toQrPay.jpg", produces = "image/jpeg;charset=UTF-8") public byte[] toWxQrPay(Integer payId, String transactionType, BigDecimal price) throws IOException { @@ -215,7 +218,9 @@ public class PayController { * @param wxPayId 微信账户id * @param aliPayId 支付宝id * @param price 金额 + * @param request 请求 * @return 二维码图像 + * @throws IOException IOException */ @RequestMapping(value = "toWxAliQrPay.jpg", produces = "image/jpeg;charset=UTF-8") public byte[] toWxAliQrPay(Integer wxPayId, Integer aliPayId, BigDecimal price, HttpServletRequest request) throws IOException { @@ -243,7 +248,9 @@ public class PayController { * @param wxPayId 微信账户id * @param aliPayId 支付宝id * @param price 金额 + * @param request 请求 * @return 支付宝与微信平台的判断 + * @throws IOException IOException */ @RequestMapping(value = "toWxAliPay.html", produces = "text/html;charset=UTF-8") public String toWxAliPay(Integer wxPayId, Integer aliPayId, BigDecimal price, HttpServletRequest request) throws IOException { @@ -276,6 +283,7 @@ public class PayController { * * @param payId 支付账户id * @param transactionType 交易类型 + * @param price 金额 * @return 支付预订单信息 */ @RequestMapping("getOrderInfo") @@ -295,9 +303,10 @@ public class PayController { *

* 方式二,{@link #payBack(HttpServletRequest, Integer)} 是属于简化方式, 试用与简单的业务场景 * - * @param request - * @param payId + * @param request 请求 + * @param payId 账户id * @return 支付是否成功 + * @throws IOException IOException */ @RequestMapping(value = "payBackOne{payId}.json") public String payBackOne(HttpServletRequest request, @PathVariable Integer payId) throws IOException { @@ -327,11 +336,14 @@ public class PayController { * 支付回调地址 * 方式二 * - * @param request - * @return 拦截器相关增加, 详情查看{@link com.egzosn.pay.common.api.PayService#addPayMessageInterceptor(PayMessageInterceptor)} - *

- * 业务处理在对应的PayMessageHandler里面处理,在哪里设置PayMessageHandler,详情查看{@link com.egzosn.pay.common.api.PayService#setPayMessageHandler(com.egzosn.pay.common.api.PayMessageHandler)} + * @param request 请求 + * @param payId 账户id + * @return 支付是否成功 + * @throws IOException IOException + * 拦截器相关增加, 详情查看{@link com.egzosn.pay.common.api.PayService#addPayMessageInterceptor(PayMessageInterceptor)} *

+ * 业务处理在对应的PayMessageHandler里面处理,在哪里设置PayMessageHandler,详情查看{@link com.egzosn.pay.common.api.PayService#setPayMessageHandler(com.egzosn.pay.common.api.PayMessageHandler)} + *

* 如果未设置 {@link com.egzosn.pay.common.api.PayMessageHandler} 那么会使用默认的 {@link com.egzosn.pay.common.api.DefaultPayMessageHandler} */ @RequestMapping(value = "payBack{payId}.json") @@ -381,7 +393,7 @@ public class PayController { /** * 申请退款接口 - * + * @param payId 账户id * @param order 订单的请求体 * @return 返回支付方申请退款后的结果 */ @@ -435,7 +447,7 @@ public class PayController { /** * 转账 - * + * @param payId 账户id * @param order 转账订单 * @return 对应的转账结果 */ @@ -447,7 +459,7 @@ public class PayController { /** * 转账查询 - * + * @param payId 账户id * @param outNo 商户转账订单号 * @param tradeNo 支付平台转账订单号 * @return 对应的转账订单 diff --git a/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/PayPalPayController.java b/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/PayPalPayController.java index 15735ee..c2bee1a 100644 --- a/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/PayPalPayController.java +++ b/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/PayPalPayController.java @@ -23,8 +23,8 @@ import java.util.UUID; * 发起支付入口 * * @author: egan - * @email egzosn@gmail.com - * @date 2018/05/06 10:30 + * email egzosn@gmail.com + * date 2018/05/06 10:30 */ @RestController @RequestMapping("payPal") @@ -99,8 +99,9 @@ public class PayPalPayController { * return url * PayPal确认付款调用的接口 * 用户确认付款后,paypal调用的这个方法执行付款 - * + * @param request 请求 * @return 付款成功信息 + * @throws IOException IOException */ @GetMapping(value = "payBackBefore.json") public String payBackBefore(HttpServletRequest request) throws IOException { @@ -117,10 +118,10 @@ public class PayPalPayController { /** * 支付回调地址 * - * @param request - * - * @return + * @param request 请求 * + * @return 结果 + * @throws IOException IOException * 业务处理在对应的PayMessageHandler里面处理,在哪里设置PayMessageHandler,详情查看{@link com.egzosn.pay.common.api.PayService#setPayMessageHandler(com.egzosn.pay.common.api.PayMessageHandler)} * * 如果未设置 {@link com.egzosn.pay.common.api.PayMessageHandler} 那么会使用默认的 {@link com.egzosn.pay.common.api.DefaultPayMessageHandler} diff --git a/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/PayoneerPayController.java b/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/PayoneerPayController.java index b0afb01..0fca69d 100644 --- a/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/PayoneerPayController.java +++ b/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/PayoneerPayController.java @@ -20,8 +20,8 @@ import java.util.UUID; /** * @author egan - * @email egzosn@gmail.com - * @date 2018/2/5 + * email egzosn@gmail.com + * date 2018/2/5 */ @RestController @RequestMapping("payoneer") @@ -63,8 +63,8 @@ public class PayoneerPayController { /** * 获取授权页面 - * @param payeeId - * @return + * @param payeeId 用户id + * @return 获取授权页面 */ @RequestMapping("getAuthorizationPage.json") public Map getAuthorizationPage( String payeeId ){ @@ -78,7 +78,7 @@ public class PayoneerPayController { /** * 获取授权用户信息,包含用户状态,注册时间,联系人信息,地址信息等等 * @param payeeId 用户id - * @return + * @return 获取授权用户信息 */ @RequestMapping("getAuthorizationUser.json") public Map getAuthorizationUser( String payeeId ){ @@ -98,7 +98,7 @@ public class PayoneerPayController { */ @ResponseBody @RequestMapping(value = "microPay.json") - public Map microPay(BigDecimal price, String userId) throws IOException { + public Map microPay(BigDecimal price, String userId){ PayOrder order = new PayOrder("Order_payment:", "Order payment", price, UUID.randomUUID().toString().replace("-", ""), PayoneerTransactionType.CHARGE); //币种 @@ -120,9 +120,10 @@ public class PayoneerPayController { /** * 用户授权回调地址 * - * @param request + * @param request 请求 * - * @return + * @return 是否成功 + * @throws IOException IOException */ @RequestMapping(value = "payBack.json") public String payBack(HttpServletRequest request) throws IOException { diff --git a/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/UnionPayController.java b/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/UnionPayController.java index e1955a8..77d1eb2 100644 --- a/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/UnionPayController.java +++ b/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/UnionPayController.java @@ -32,8 +32,8 @@ import static com.egzosn.pay.union.bean.UnionTransactionType.WEB; * 银联相关 * * @author: egan - * @email egzosn@gmail.com - * @date 2016/11/18 0:25 + * email egzosn@gmail.com + * date 2016/11/18 0:25 */ @RestController @RequestMapping("union") @@ -148,6 +148,7 @@ public class UnionPayController { * 二维码支付 * @param price 金额 * @return 二维码图像 + * @throws IOException IOException */ @RequestMapping(value = "toQrPay.jpg", produces = "image/jpeg;charset=UTF-8") public byte[] toWxQrPay( BigDecimal price) throws IOException { @@ -165,7 +166,7 @@ public class UnionPayController { * @return 支付结果 */ @RequestMapping(value = "microPay") - public Map microPay(BigDecimal price, String authCode) throws IOException { + public Map microPay(BigDecimal price, String authCode) { //获取对应的支付账户操作工具(可根据账户id) //条码付 PayOrder order = new PayOrder("egan order", "egan order", null == price ? new BigDecimal(0.01) : price, SignUtils.randomStr(), UnionTransactionType.CONSUME); @@ -190,10 +191,11 @@ public class UnionPayController { * * 方式二,{@link #payBack(HttpServletRequest)} 是属于简化方式, 试用与简单的业务场景 * - * @param request + * @param request 请求 * - * @return + * @return 是否成功 * @see #payBack(HttpServletRequest) + * @throws IOException IOException */ @Deprecated @RequestMapping(value = "payBackBefore.json") @@ -217,13 +219,14 @@ public class UnionPayController { /** * 支付回调地址 * - * @param request + * @param request 请求 * - * @return + * @return 是否成功 * * 业务处理在对应的PayMessageHandler里面处理,在哪里设置PayMessageHandler,详情查看{@link PayService#setPayMessageHandler(com.egzosn.pay.common.api.PayMessageHandler)} * * 如果未设置 {@link com.egzosn.pay.common.api.PayMessageHandler} 那么会使用默认的 {@link com.egzosn.pay.common.api.DefaultPayMessageHandler} + * @throws IOException IOException * */ @RequestMapping(value = "payBack.json") diff --git a/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/WxPayController.java b/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/WxPayController.java index c50cd70..abb6ea6 100644 --- a/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/WxPayController.java +++ b/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/WxPayController.java @@ -36,8 +36,8 @@ import java.util.UUID; * 发起支付入口 * * @author: egan - * @email egzosn@gmail.com - * @date 2016/11/18 0:25 + * email egzosn@gmail.com + * date 2016/11/18 0:25 */ @RestController @RequestMapping("wx") @@ -102,6 +102,7 @@ public class WxPayController { * 跳到支付页面 * 针对实时支付 * + * @param request 请求 * @param price 金额 * @return 跳到支付页面 */ @@ -163,6 +164,7 @@ public class WxPayController { * 二维码支付 * @param price 金额 * @return 二维码图像 + * @throws IOException IOException */ @RequestMapping(value = "toQrPay.jpg", produces = "image/jpeg;charset=UTF-8") public byte[] toWxQrPay( BigDecimal price) throws IOException { @@ -180,7 +182,7 @@ public class WxPayController { * @return 支付结果 */ @RequestMapping(value = "microPay") - public Map microPay( BigDecimal price, String authCode) throws IOException { + public Map microPay( BigDecimal price, String authCode) { //获取对应的支付账户操作工具(可根据账户id) //条码付 PayOrder order = new PayOrder("egan order", "egan order", null == price ? new BigDecimal(0.01) : price, UUID.randomUUID().toString().replace("-", ""), WxTransactionType.MICROPAY); @@ -208,7 +210,7 @@ public class WxPayController { * @return 支付结果 */ @RequestMapping(value = "facePay") - public Map facePay(BigDecimal price, String authCode, String openid) throws IOException { + public Map facePay(BigDecimal price, String authCode, String openid) { //获取对应的支付账户操作工具(可根据账户id) PayOrder order = new PayOrder("egan order", "egan order", null == price ? new BigDecimal(0.01) : price, UUID.randomUUID().toString().replace("-", ""), WxTransactionType.FACEPAY); //设置人脸凭证 @@ -233,9 +235,10 @@ public class WxPayController { * * 方式二,{@link #payBack(HttpServletRequest)} 是属于简化方式, 试用与简单的业务场景 * - * @param request + * @param request 请求 * - * @return + * @return 是否成功 + * @throws IOException IOException * @see #payBack(HttpServletRequest) */ @Deprecated @@ -260,14 +263,14 @@ public class WxPayController { /** * 支付回调地址 * - * @param request + * @param request 请求 * - * @return + * @return 是否成功 * * 业务处理在对应的PayMessageHandler里面处理,在哪里设置PayMessageHandler,详情查看{@link com.egzosn.pay.common.api.PayService#setPayMessageHandler(com.egzosn.pay.common.api.PayMessageHandler)} * * 如果未设置 {@link com.egzosn.pay.common.api.PayMessageHandler} 那么会使用默认的 {@link com.egzosn.pay.common.api.DefaultPayMessageHandler} - * + * @throws IOException IOException */ @RequestMapping(value = "payBack.json") public String payBack(HttpServletRequest request) throws IOException { @@ -403,10 +406,10 @@ public class WxPayController { * {@link com.egzosn.pay.wx.bean.WxTransferType#QUERY_BANK} * {@link com.egzosn.pay.wx.bean.WxTransferType#GETTRANSFERINFO} * - *
+ *

* 企业付款到零钱 * 商户企业付款到银行卡 - *
+ *

* @return 对应的转账订单 */ @RequestMapping("transferQuery") 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 42bc52b..e2176b5 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 @@ -14,8 +14,8 @@ import java.util.Map; /** * 账户 * @author: egan - * @email egzosn@gmail.com - * @date 2016/11/18 1:21 + * email egzosn@gmail.com + * date 2016/11/18 1:21 */ //@Repository public class ApyAccountRepository { @@ -126,7 +126,7 @@ public class ApyAccountRepository { /** * 根据id获取对应的账户信息 * @param payId 账户id - * @return + * @return 账户信息 */ public ApyAccount findByPayId(Integer payId){ // TODO 2016/11/18 1:23 author: egan 这里简单模拟 具体实现 略。。 diff --git a/pay-java-demo/src/main/java/com/egzosn/pay/demo/entity/ApyAccount.java b/pay-java-demo/src/main/java/com/egzosn/pay/demo/entity/ApyAccount.java index 71e4794..2ac2dd6 100644 --- a/pay-java-demo/src/main/java/com/egzosn/pay/demo/entity/ApyAccount.java +++ b/pay-java-demo/src/main/java/com/egzosn/pay/demo/entity/ApyAccount.java @@ -9,8 +9,8 @@ import com.egzosn.pay.common.bean.MsgType; /** * 支付账户 * @author: egan - * @email egzosn@gmail.com - * @date 2016/11/18 0:36 + * email egzosn@gmail.com + * date 2016/11/18 0:36 */ //@Table(name = "apy_account") //@Entity diff --git a/pay-java-demo/src/main/java/com/egzosn/pay/demo/request/QueryOrder.java b/pay-java-demo/src/main/java/com/egzosn/pay/demo/request/QueryOrder.java index e9e7631..38d814d 100644 --- a/pay-java-demo/src/main/java/com/egzosn/pay/demo/request/QueryOrder.java +++ b/pay-java-demo/src/main/java/com/egzosn/pay/demo/request/QueryOrder.java @@ -6,8 +6,8 @@ import java.util.Date; /** * 订单辅助接口 * @author: egan - * @email egzosn@gmail.com - * @date 2017/3/12 14:50 + * email egzosn@gmail.com + * date 2017/3/12 14:50 */ public class QueryOrder { 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 496d52c..a55bde1 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 @@ -11,8 +11,8 @@ import java.util.Map; /** * @author: egan - * @email egzosn@gmail.com - * @date 2016/11/18 1:11 + * email egzosn@gmail.com + * date 2016/11/18 1:11 */ @Service public class ApyAccountService { @@ -40,7 +40,7 @@ public class ApyAccountService { /** * 获取支付响应 * @param id 账户id - * @return + * @return 支付响应 */ public PayResponse getPayResponse(Integer id) { 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 f0fc931..1ebfbaf 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 @@ -18,8 +18,8 @@ import javax.annotation.Resource; /** * 支付响应对象 * @author: egan - * @email egzosn@gmail.com - * @date 2016/11/18 0:34 + * email egzosn@gmail.com + * date 2016/11/18 0:34 */ public class PayResponse { diff --git a/pay-java-demo/src/main/java/com/egzosn/pay/demo/service/handler/FuiouPayMessageHandler.java b/pay-java-demo/src/main/java/com/egzosn/pay/demo/service/handler/FuiouPayMessageHandler.java index a027c07..3513c41 100644 --- a/pay-java-demo/src/main/java/com/egzosn/pay/demo/service/handler/FuiouPayMessageHandler.java +++ b/pay-java-demo/src/main/java/com/egzosn/pay/demo/service/handler/FuiouPayMessageHandler.java @@ -9,7 +9,7 @@ import java.util.Map; /** * @author Fuzx - * @create 2017 2017/1/24 0024 + * create 2017 2017/1/24 0024 */ public class FuiouPayMessageHandler extends BasePayMessageHandler { diff --git a/pay-java-demo/src/main/java/com/egzosn/pay/demo/service/handler/PayoneerMessageHandler.java b/pay-java-demo/src/main/java/com/egzosn/pay/demo/service/handler/PayoneerMessageHandler.java index d1ee66c..f788387 100644 --- a/pay-java-demo/src/main/java/com/egzosn/pay/demo/service/handler/PayoneerMessageHandler.java +++ b/pay-java-demo/src/main/java/com/egzosn/pay/demo/service/handler/PayoneerMessageHandler.java @@ -9,10 +9,10 @@ import com.egzosn.pay.payoneer.api.PayoneerPayService; import java.util.Map; /** - * @descrption * @author Actinia - * @email hayesfu@qq.com - * @date 2018-01-19 + * email hayesfu@qq.com + * date 2018-01-19 + * */ public class PayoneerMessageHandler extends BasePayMessageHandler { diff --git a/pay-java-demo/src/main/java/com/egzosn/pay/demo/service/handler/UnionPayMessageHandler.java b/pay-java-demo/src/main/java/com/egzosn/pay/demo/service/handler/UnionPayMessageHandler.java index 00a25b4..e088054 100644 --- a/pay-java-demo/src/main/java/com/egzosn/pay/demo/service/handler/UnionPayMessageHandler.java +++ b/pay-java-demo/src/main/java/com/egzosn/pay/demo/service/handler/UnionPayMessageHandler.java @@ -10,7 +10,7 @@ import java.util.Map; /** * @author Actinia - * @email hayesfu@qq.com + * email hayesfu@qq.com *
  * create 2017 2017/11/4 0004
  * 
diff --git a/pay-java-demo/src/main/java/com/egzosn/pay/demo/service/handler/YouDianPayMessageHandler.java b/pay-java-demo/src/main/java/com/egzosn/pay/demo/service/handler/YouDianPayMessageHandler.java index 3270f0d..578d3b8 100644 --- a/pay-java-demo/src/main/java/com/egzosn/pay/demo/service/handler/YouDianPayMessageHandler.java +++ b/pay-java-demo/src/main/java/com/egzosn/pay/demo/service/handler/YouDianPayMessageHandler.java @@ -10,7 +10,7 @@ import java.util.Map; /** * @author Fuzx - * @create 2017 2017/1/24 0024 + * create 2017 2017/1/24 0024 */ public class YouDianPayMessageHandler extends BasePayMessageHandler { diff --git a/pay-java-demo/src/main/java/com/egzosn/pay/demo/service/interceptor/AliPayMessageInterceptor.java b/pay-java-demo/src/main/java/com/egzosn/pay/demo/service/interceptor/AliPayMessageInterceptor.java index 3308ad6..b8e9635 100644 --- a/pay-java-demo/src/main/java/com/egzosn/pay/demo/service/interceptor/AliPayMessageInterceptor.java +++ b/pay-java-demo/src/main/java/com/egzosn/pay/demo/service/interceptor/AliPayMessageInterceptor.java @@ -22,9 +22,10 @@ public class AliPayMessageInterceptor implements PayMessageInterceptor { * * @param payMessage 支付回调消息 * @param context 上下文,如果handler或interceptor之间有信息要传递,可以用这个 - * @param payService + * @param payService 支付服务 * @return true代表OK,false代表不OK并直接中断对应的支付处理器 * @see PayMessageHandler 支付处理器 + * @throws PayErrorException PayErrorException* */ @Override public boolean intercept(PayMessage payMessage, Map context, PayService payService) throws PayErrorException { diff --git a/pay-java-demo/src/main/java/com/egzosn/pay/demo/service/interceptor/YoudianPayMessageInterceptor.java b/pay-java-demo/src/main/java/com/egzosn/pay/demo/service/interceptor/YoudianPayMessageInterceptor.java index 0387ecf..58f9317 100644 --- a/pay-java-demo/src/main/java/com/egzosn/pay/demo/service/interceptor/YoudianPayMessageInterceptor.java +++ b/pay-java-demo/src/main/java/com/egzosn/pay/demo/service/interceptor/YoudianPayMessageInterceptor.java @@ -25,9 +25,10 @@ public class YoudianPayMessageInterceptor implements PayMessageInterceptor { * * @param payMessage 支付回调消息 * @param context 上下文,如果handler或interceptor之间有信息要传递,可以用这个 - * @param payService + * @param payService 支付服务 * @return true代表OK,false代表不OK并直接中断对应的支付处理器 * @see PayMessageHandler 支付处理器 + * @throws PayErrorException PayErrorException */ @Override public boolean intercept(PayMessage payMessage, Map context, PayService payService) throws PayErrorException { diff --git a/pom.xml b/pom.xml index 24786e4..7193d7d 100644 --- a/pom.xml +++ b/pom.xml @@ -127,7 +127,7 @@ utf-8
- + \ No newline at end of file -- Gitee From 05a4b367f9f7f9837f0d2cfb029d24e27fe8234e Mon Sep 17 00:00:00 2001 From: egzosn Date: Mon, 6 May 2019 11:32:38 +0800 Subject: [PATCH 031/299] =?UTF-8?q?=E5=AD=97=E6=AE=B5=E4=BD=9C=E7=94=A8?= =?UTF-8?q?=E5=9F=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pay-java-ali/pom.xml | 2 +- pay-java-common/pom.xml | 2 +- pay-java-demo/pom.xml | 2 +- pay-java-fuiou/pom.xml | 2 +- .../com/egzosn/pay/fuiou/api/FuiouPayConfigStorage.java | 6 ++++-- pay-java-payoneer/pom.xml | 2 +- .../com/egzosn/pay/payoneer/api/PayoneerConfigStorage.java | 4 ++-- pay-java-paypal/pom.xml | 2 +- pay-java-union/pom.xml | 2 +- pay-java-wx-youdian/pom.xml | 2 +- pay-java-wx/pom.xml | 2 +- pay-java-yiji/pom.xml | 2 +- pom.xml | 4 ++-- 13 files changed, 18 insertions(+), 16 deletions(-) diff --git a/pay-java-ali/pom.xml b/pay-java-ali/pom.xml index dadeede..968ba5b 100644 --- a/pay-java-ali/pom.xml +++ b/pay-java-ali/pom.xml @@ -5,7 +5,7 @@ pay-java-parent com.egzosn - 2.12.7 + 2.12.8-SNAPSHOT 4.0.0 pay-java-ali diff --git a/pay-java-common/pom.xml b/pay-java-common/pom.xml index 75b6b2a..20e98c2 100644 --- a/pay-java-common/pom.xml +++ b/pay-java-common/pom.xml @@ -5,7 +5,7 @@ pay-java-parent com.egzosn - 2.12.7 + 2.12.8-SNAPSHOT 4.0.0 jar diff --git a/pay-java-demo/pom.xml b/pay-java-demo/pom.xml index 6044444..560a894 100644 --- a/pay-java-demo/pom.xml +++ b/pay-java-demo/pom.xml @@ -5,7 +5,7 @@ pay-java-parent com.egzosn - 2.12.7 + 2.12.8-SNAPSHOT 4.0.0 war diff --git a/pay-java-fuiou/pom.xml b/pay-java-fuiou/pom.xml index a8db6ca..0408e39 100644 --- a/pay-java-fuiou/pom.xml +++ b/pay-java-fuiou/pom.xml @@ -5,7 +5,7 @@ pay-java-parent com.egzosn - 2.12.7 + 2.12.8-SNAPSHOT 4.0.0 pay-java-fuiou 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 480ee7d..2dd49a8 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 @@ -9,8 +9,10 @@ import com.egzosn.pay.common.api.BasePayConfigStorage; * */ public class FuiouPayConfigStorage extends BasePayConfigStorage { - - public String mchntCd;//商户代码 + /** + * 商户代码 + */ + private String mchntCd; /** * 应用id diff --git a/pay-java-payoneer/pom.xml b/pay-java-payoneer/pom.xml index 9b56aed..3debe3b 100644 --- a/pay-java-payoneer/pom.xml +++ b/pay-java-payoneer/pom.xml @@ -5,7 +5,7 @@ pay-java-parent com.egzosn - 2.12.7 + 2.12.8-SNAPSHOT 4.0.0 pay-java-payoneer diff --git a/pay-java-payoneer/src/main/java/com/egzosn/pay/payoneer/api/PayoneerConfigStorage.java b/pay-java-payoneer/src/main/java/com/egzosn/pay/payoneer/api/PayoneerConfigStorage.java index 20324de..a598c74 100644 --- a/pay-java-payoneer/src/main/java/com/egzosn/pay/payoneer/api/PayoneerConfigStorage.java +++ b/pay-java-payoneer/src/main/java/com/egzosn/pay/payoneer/api/PayoneerConfigStorage.java @@ -14,11 +14,11 @@ public class PayoneerConfigStorage extends BasePayConfigStorage { /** * 商户Id */ - public String programId; + private String programId; /** * PayoneerPay 用户名 */ - public String userName; + private String userName; /** * 应用id diff --git a/pay-java-paypal/pom.xml b/pay-java-paypal/pom.xml index 7e7c7ed..1a74ff7 100644 --- a/pay-java-paypal/pom.xml +++ b/pay-java-paypal/pom.xml @@ -5,7 +5,7 @@ pay-java-parent com.egzosn - 2.12.7 + 2.12.8-SNAPSHOT 4.0.0 diff --git a/pay-java-union/pom.xml b/pay-java-union/pom.xml index bb6a27a..f5a4bbe 100644 --- a/pay-java-union/pom.xml +++ b/pay-java-union/pom.xml @@ -5,7 +5,7 @@ pay-java-parent com.egzosn - 2.12.7 + 2.12.8-SNAPSHOT 4.0.0 diff --git a/pay-java-wx-youdian/pom.xml b/pay-java-wx-youdian/pom.xml index d1b0d70..0e1cac4 100644 --- a/pay-java-wx-youdian/pom.xml +++ b/pay-java-wx-youdian/pom.xml @@ -5,7 +5,7 @@ pay-java-parent com.egzosn - 2.12.7 + 2.12.8-SNAPSHOT 4.0.0 pay-java-wx-youdian diff --git a/pay-java-wx/pom.xml b/pay-java-wx/pom.xml index d5d10aa..466491e 100644 --- a/pay-java-wx/pom.xml +++ b/pay-java-wx/pom.xml @@ -5,7 +5,7 @@ pay-java-parent com.egzosn - 2.12.7 + 2.12.8-SNAPSHOT 4.0.0 pay-java-wx diff --git a/pay-java-yiji/pom.xml b/pay-java-yiji/pom.xml index 9a26a34..40b78ce 100644 --- a/pay-java-yiji/pom.xml +++ b/pay-java-yiji/pom.xml @@ -5,7 +5,7 @@ pay-java-parent com.egzosn - 2.12.7 + 2.12.8-SNAPSHOT 4.0.0 diff --git a/pom.xml b/pom.xml index 61dfeba..1214132 100644 --- a/pom.xml +++ b/pom.xml @@ -7,7 +7,7 @@ com.egzosn pay-java-parent pom - 2.12.7 + 2.12.8-SNAPSHOT Pay Java - Parent Pay Java Parent @@ -58,7 +58,7 @@ - 2.12.7 + 2.12.8-SNAPSHOT 4.5.4 1.2.17 1.2.41 -- Gitee From 8a31e659dd318ecb372f0e9fee36913d3671d1ff Mon Sep 17 00:00:00 2001 From: egzosn Date: Wed, 15 May 2019 12:59:28 +0800 Subject: [PATCH 032/299] =?UTF-8?q?=E7=A7=BB=E9=99=A4JDK=E7=89=88=E6=9D=83?= =?UTF-8?q?Base64=EF=BC=8C=E6=94=B9=E7=94=A8=E4=BB=A3=E7=90=86Base64?= =?UTF-8?q?=E5=AF=B9=E8=B1=A1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../pay/common/util/sign/encrypt/Base64.java | 1050 +---------------- pom.xml | 4 +- 2 files changed, 13 insertions(+), 1041 deletions(-) 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 0d40b16..243b915 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,1053 +1,25 @@ -/* - * 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 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; +package com.egzosn.pay.common.util.sign.encrypt; /** - * 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.

  • + * Base64 + * @author egan + *
    + * email egzosn@gmail.com
      *
    - * 
  • 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 + * create 2019/05/15 12:50 + * */ - public class Base64 { private Base64() {} - /** - * Encodes hex octects into Base64 - * - * @param binaryData Array containing binaryData - * @return Encoded Base64 array - */ - public static String encode(byte[] binaryData) { - return Base64.getEncoder().encodeToString(binaryData); - } - - /** - * Decodes Base64 data into octects - * - * @param encoded string containing Base64 data - * @return Array containind decoded data. - */ - public static byte[] decode(String encoded) { - 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; + public static byte[] decode(String str) { + return org.apache.commons.codec.binary.Base64.decodeBase64(str); } - /** - * Returns a {@link Encoder} that encodes using the - * MIME type base64 encoding scheme. - * - * @return A Base64 encoder. - */ - public static Encoder getMimeEncoder() { - return Encoder.RFC2045; + public static String encode(byte[] bytes) { + return org.apache.commons.codec.binary.Base64.encodeBase64String(bytes); } - /** - * 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) { - if (null == src){ - return null; - } - src = src.replaceAll("[\r\n]", ""); - 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/pom.xml b/pom.xml index 1214132..73e89fd 100644 --- a/pom.xml +++ b/pom.xml @@ -175,7 +175,7 @@ - + \ No newline at end of file -- Gitee From c1fefa6a02086ad5a35e5cfba92051e6c31cd2d8 Mon Sep 17 00:00:00 2001 From: egzosn Date: Wed, 15 May 2019 12:59:58 +0800 Subject: [PATCH 033/299] =?UTF-8?q?=E4=BB=A3=E7=A0=81=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/egzosn/pay/common/util/DateUtils.java | 56 ++++++++++--------- .../pay/common/util/sign/CertDescriptor.java | 2 + 2 files changed, 33 insertions(+), 25 deletions(-) diff --git a/pay-java-common/src/main/java/com/egzosn/pay/common/util/DateUtils.java b/pay-java-common/src/main/java/com/egzosn/pay/common/util/DateUtils.java index 530741e..39081ea 100644 --- a/pay-java-common/src/main/java/com/egzosn/pay/common/util/DateUtils.java +++ b/pay-java-common/src/main/java/com/egzosn/pay/common/util/DateUtils.java @@ -8,34 +8,35 @@ import java.util.*; /** * 日期转换运算工具 - * @author egan - *

- * email egzosn@gmail.com
- * date 2018-11-21 16:43:20
- * 
+ * + * @author egan + *
+ *         email egzosn@gmail.com
+ *         date 2018-11-21 16:43:20
+ *         
*/ public final class DateUtils { static final class DateFormatHolder { - private static final ThreadLocal>> THREADLOCAL_FORMATS = new ThreadLocal(); + private static final ThreadLocal>> THREADLOCAL_FORMATS = new ThreadLocal>>(); DateFormatHolder() { } public static SimpleDateFormat formatFor(String pattern) { - SoftReference ref = (SoftReference)THREADLOCAL_FORMATS.get(); - Object formats = ref == null?null:(Map)ref.get(); - if(formats == null) { - formats = new HashMap(); + SoftReference> ref = THREADLOCAL_FORMATS.get(); + Map formats = ref == null ? null : ref.get(); + if (formats == null) { + formats = new HashMap(); THREADLOCAL_FORMATS.set(new SoftReference(formats)); } - SimpleDateFormat format = (SimpleDateFormat)((Map)formats).get(pattern); + SimpleDateFormat format = formats.get(pattern); - if(format == null) { + if (format == null) { format = new SimpleDateFormat(pattern); format.setTimeZone(TimeZone.getTimeZone("GMT+8")); - ((Map)formats).put(pattern, format); + ((Map) formats).put(pattern, format); } return format; @@ -46,8 +47,8 @@ public final class DateUtils { } } - public static final String YYYY_MM_DD_HH_MM_SS = "yyyy-MM-dd HH:mm:ss"; - public static final String YYYY_MM_DD = "yyyy-MM-dd"; + public static final String YYYY_MM_DD_HH_MM_SS = "yyyy-MM-dd HH:mm:ss"; + public static final String YYYY_MM_DD = "yyyy-MM-dd"; public static final String YYYYMMDD = "yyyyMMdd"; public static final String YYYYMMDDHHMMSS = "yyyyMMddHHmmss"; public static final String MMDD = "MMdd"; @@ -56,41 +57,46 @@ public final class DateUtils { public static String formatDate(Date date, String pattern) { Args.notNull(date, "Date"); Args.notNull(pattern, "Pattern"); - SimpleDateFormat formatFor = DateFormatHolder.formatFor(pattern); + SimpleDateFormat formatFor = DateFormatHolder.formatFor(pattern); return formatFor.format(date); } - public static final String format(Date date){ + + public static final String format(Date date) { return formatDate(date, YYYY_MM_DD_HH_MM_SS); } - public static final String formatDay(Date date){ - return formatDate(date, YYYY_MM_DD); + public static final String formatDay(Date date) { + return formatDate(date, YYYY_MM_DD); } /** * 剩余分钟数 + * * @param date 结束点日期 * @return 分钟数 */ - public static final long minutesRemaining(Date date){ - return (date.getTime() - System.currentTimeMillis()) / 1000 / 60 ; + public static final long minutesRemaining(Date date) { + return (date.getTime() - System.currentTimeMillis()) / 1000 / 60; } /** * 剩余小时 + * * @param date 结束点日期 * @return 小时数 */ - public static final long remainingHours(Date date){ - return minutesRemaining(date) / 60 ; + public static final long remainingHours(Date date) { + return minutesRemaining(date) / 60; } + /** * 剩余天数 + * * @param date 结束点日期 * @return 天数 */ - public static final long remainingDays(Date date){ - return remainingHours(date) / 24 ; + public static final long remainingDays(Date date) { + return remainingHours(date) / 24; } } diff --git a/pay-java-common/src/main/java/com/egzosn/pay/common/util/sign/CertDescriptor.java b/pay-java-common/src/main/java/com/egzosn/pay/common/util/sign/CertDescriptor.java index 9d1e75d..3577a11 100644 --- a/pay-java-common/src/main/java/com/egzosn/pay/common/util/sign/CertDescriptor.java +++ b/pay-java-common/src/main/java/com/egzosn/pay/common/util/sign/CertDescriptor.java @@ -50,6 +50,8 @@ public class CertDescriptor { */ private X509Certificate rootKeyCert = null; + public CertDescriptor() { + } /** * 通过证书路径初始化为公钥证书 -- Gitee From 0f010c7e7e79e9da5ed08ce8cec06190129f1e96 Mon Sep 17 00:00:00 2001 From: egzosn Date: Wed, 15 May 2019 13:00:33 +0800 Subject: [PATCH 034/299] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E5=AF=8C=E5=8F=8B?= =?UTF-8?q?=E6=B5=8B=E8=AF=95=E7=B1=BB(=E4=BB=8E=E6=8E=A5=E5=85=A5?= =?UTF-8?q?=E6=95=99=E7=A8=8B=E6=8B=B7=E8=B4=9D=E8=BF=87=E6=9D=A5)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../demo/controller/FuiouPayController.java | 126 ++++++++++++++++++ .../pay/demo/controller/PayController.java | 36 ++--- 2 files changed, 144 insertions(+), 18 deletions(-) create mode 100644 pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/FuiouPayController.java diff --git a/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/FuiouPayController.java b/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/FuiouPayController.java new file mode 100644 index 0000000..06d1114 --- /dev/null +++ b/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/FuiouPayController.java @@ -0,0 +1,126 @@ + +package com.egzosn.pay.demo.controller; + + +import com.egzosn.pay.common.api.PayService; +import com.egzosn.pay.common.bean.PayOrder; +import com.egzosn.pay.fuiou.api.FuiouPayConfigStorage; +import com.egzosn.pay.fuiou.api.FuiouPayService; +import com.egzosn.pay.fuiou.bean.FuiouTransactionType; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import javax.annotation.PostConstruct; +import javax.servlet.http.HttpServletRequest; +import java.io.IOException; +import java.math.BigDecimal; +import java.util.Map; +import java.util.UUID; + +/** + * 发起支付入口 + * + * @author: egan + * email egzosn@gmail.com + * date 2016/11/18 0:25 + */ +@RestController +@RequestMapping("fuiou") +public class FuiouPayController { + + private PayService service = null; + + + @PostConstruct + public void init() { + FuiouPayConfigStorage fuiouPayConfigStorage = new FuiouPayConfigStorage(); + fuiouPayConfigStorage.setMchntCd("合作者id"); + fuiouPayConfigStorage.setKeyPublic("支付密钥"); + fuiouPayConfigStorage.setKeyPrivate("支付密钥"); + fuiouPayConfigStorage.setNotifyUrl("异步回调地址"); + fuiouPayConfigStorage.setReturnUrl("同步回调地址"); + fuiouPayConfigStorage.setSignType("MD5"); + fuiouPayConfigStorage.setInputCharset("utf-8"); + //是否为测试账号,沙箱环境 + fuiouPayConfigStorage.setTest(true); + + + service = new FuiouPayService(fuiouPayConfigStorage); + + + //设置回调消息处理 + //TODO {@link com.egzosn.pay.demo.controller.FuiouPayController#payBack} +// service.setPayMessageHandler(new FuiouPayMessageHandler(null)); + } + + + + /** + * 跳到支付页面 + * 针对实时支付 + * + * @param price 金额 + * @return 跳到支付页面 + */ + @RequestMapping(value = "toPay.html", produces = "text/html;charset=UTF-8") + public String toPay( BigDecimal price) { + //支付订单基础信息 + PayOrder order = new PayOrder("订单title", "摘要", new BigDecimal(0.01) , UUID.randomUUID().toString().replace("-", "").substring(2)); + order.setTransactionType(FuiouTransactionType.B2C); + //获取支付所需的信息 +// Map directOrderInfo = service.orderInfo(order); + //获取表单提交对应的字符串,将其序列化到页面即可, +// return service.buildRequest(directOrderInfo, MethodType.POST); + return service.toPay(order); + } + + + /** + * 支付回调地址 方式一 + * + * 方式二,{@link #payBack(HttpServletRequest)} 是属于简化方式, 试用与简单的业务场景 + * + * @param request 请求 + * + * @return 是否成功 + * @throws IOException IOException + * @see #payBack(HttpServletRequest) + */ + @Deprecated + @RequestMapping(value = "payBackBefore.json") + public String payBackBefore(HttpServletRequest request) throws IOException { + + //获取支付方返回的对应参数 + Map params = service.getParameter2Map(request.getParameterMap(), request.getInputStream()); + if (null == params) { + return service.getPayOutMessage("fail", "失败").toMessage(); + } + + //校验 + if (service.verify(params)) { + //这里处理业务逻辑 + //......业务逻辑处理块........ + return service.successPayOutMessage(null).toMessage(); + } + + return service.getPayOutMessage("fail", "失败").toMessage(); + } + /** + * 支付回调地址 + * + * @param request 请求 + * + * @return 是否成功 + * + * 业务处理在对应的PayMessageHandler里面处理,在哪里设置PayMessageHandler,详情查看{@link PayService#setPayMessageHandler(com.egzosn.pay.common.api.PayMessageHandler)} + * + * 如果未设置 {@link com.egzosn.pay.common.api.PayMessageHandler} 那么会使用默认的 {@link com.egzosn.pay.common.api.DefaultPayMessageHandler} + * @throws IOException IOException + */ + @RequestMapping(value = "payBack.json") + public String payBack(HttpServletRequest request) throws IOException { + //业务处理在对应的PayMessageHandler里面处理,在哪里设置PayMessageHandler,详情查看com.egzosn.pay.common.api.PayService.setPayMessageHandler() + return service.payBack(request.getParameterMap(), request.getInputStream()).toMessage(); + } + +} 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 95fa828..f3d7d44 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 @@ -159,6 +159,24 @@ public class PayController { return orderInfo; } + /** + * 获取支付预订单信息 + * + * @param payId 支付账户id + * @param transactionType 交易类型 + * @param price 金额 + * @return 支付预订单信息 + */ + @RequestMapping("getOrderInfo") + public Map getOrderInfo(Integer payId, String transactionType, BigDecimal price) { + //获取对应的支付账户操作工具(可根据账户id) + PayResponse payResponse = service.getPayResponse(payId); + Map data = new HashMap<>(); + data.put("code", 0); + PayOrder order = new PayOrder("订单title", "摘要", null == price ? new BigDecimal(0.01) : price, UUID.randomUUID().toString().replace("-", ""), PayType.valueOf(payResponse.getStorage().getPayType()).getTransactionType(transactionType)); + data.put("orderInfo", payResponse.getService().orderInfo(order)); + return data; + } /** * 刷卡付,pos主动扫码付款(条码付) @@ -278,24 +296,6 @@ public class PayController { } - /** - * 获取支付预订单信息 - * - * @param payId 支付账户id - * @param transactionType 交易类型 - * @param price 金额 - * @return 支付预订单信息 - */ - @RequestMapping("getOrderInfo") - public Map getOrderInfo(Integer payId, String transactionType, BigDecimal price) { - //获取对应的支付账户操作工具(可根据账户id) - PayResponse payResponse = service.getPayResponse(payId); - Map data = new HashMap<>(); - data.put("code", 0); - PayOrder order = new PayOrder("订单title", "摘要", null == price ? new BigDecimal(0.01) : price, UUID.randomUUID().toString().replace("-", ""), PayType.valueOf(payResponse.getStorage().getPayType()).getTransactionType(transactionType)); - data.put("orderInfo", payResponse.getService().orderInfo(order)); - return data; - } /** -- Gitee From abdae8ab48e6d94ee414998b18ebd889136e3d12 Mon Sep 17 00:00:00 2001 From: egzosn Date: Fri, 17 May 2019 11:29:04 +0800 Subject: [PATCH 035/299] =?UTF-8?q?=E9=93=B6=E8=81=94=E8=AF=81=E4=B9=A6?= =?UTF-8?q?=E7=AE=A1=E7=90=86=E5=99=A8=E5=88=9D=E5=A7=8B=E5=8C=96=E9=97=AE?= =?UTF-8?q?=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../main/java/com/egzosn/pay/union/api/UnionPayService.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/pay-java-union/src/main/java/com/egzosn/pay/union/api/UnionPayService.java b/pay-java-union/src/main/java/com/egzosn/pay/union/api/UnionPayService.java index 0a844d1..cb867ab 100644 --- a/pay-java-union/src/main/java/com/egzosn/pay/union/api/UnionPayService.java +++ b/pay-java-union/src/main/java/com/egzosn/pay/union/api/UnionPayService.java @@ -59,7 +59,7 @@ public class UnionPayService extends BasePayService { /** * 证书解释器 */ - private CertDescriptor certDescriptor = new CertDescriptor(); + private CertDescriptor certDescriptor = null; /** * 构造函数 * @@ -82,9 +82,11 @@ public class UnionPayService extends BasePayService { @Override public UnionPayService setPayConfigStorage(UnionPayConfigStorage payConfigStorage) { super.setPayConfigStorage(payConfigStorage); - if (!payConfigStorage.isCertSign()) { + if (!payConfigStorage.isCertSign() || null != certDescriptor) { return this; } + + certDescriptor = new CertDescriptor(); try { certDescriptor.initPrivateSignCert(payConfigStorage.getKeyPrivateCertInputStream(), payConfigStorage.getKeyPrivateCertPwd(), "PKCS12"); certDescriptor.initPublicCert(payConfigStorage.getAcpMiddleCertInputStream()); -- Gitee From 96a8e6d8b9a4673bd767b80eea67635d3ba227c8 Mon Sep 17 00:00:00 2001 From: egzosn Date: Wed, 29 May 2019 16:00:23 +0800 Subject: [PATCH 036/299] json --- pay-java-demo/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pay-java-demo/pom.xml b/pay-java-demo/pom.xml index 560a894..1dc62bc 100644 --- a/pay-java-demo/pom.xml +++ b/pay-java-demo/pom.xml @@ -92,7 +92,7 @@ com.fasterxml.jackson.core jackson-databind - 2.8.4 + [2.9.9,) -- Gitee From d892723f4654cc1c085ef83e98ee5cb92477915d Mon Sep 17 00:00:00 2001 From: yjj Date: Wed, 5 Jun 2019 17:57:20 +0800 Subject: [PATCH 037/299] =?UTF-8?q?bugfix:=E5=BE=AE=E4=BF=A1=E6=B2=99?= =?UTF-8?q?=E7=AE=B1=E6=A8=A1=E5=BC=8F,=E9=AA=8C=E7=AD=BE,=E6=B2=A1?= =?UTF-8?q?=E6=9C=89=E4=BD=BF=E7=94=A8=E6=B2=99=E7=AE=B1=E5=AF=86=E9=92=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/egzosn/pay/wx/api/WxPayService.java | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) 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 16c5073..ec8f5a6 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 @@ -154,9 +154,17 @@ public class WxPayService extends BasePayService { */ @Override public boolean signVerify(Map params, String sign) { + return signVerify(params, sign, payConfigStorage.isTest()); + } + + private boolean signVerify(Map params, String sign, boolean isTest) { SignUtils signUtils = SignUtils.valueOf(payConfigStorage.getSignType()); - String content = SignUtils.parameterText(params, "&", SIGN, "appId") + "&key=" + (signUtils == SignUtils.MD5 ? "" : payConfigStorage.getKeyPrivate()); - return signUtils.verify(content, sign, payConfigStorage.getKeyPrivate(), payConfigStorage.getInputCharset()); + String keyPrivate = payConfigStorage.getKeyPrivate(); + if (isTest) { + keyPrivate = getKeyPrivate(); + } + String content = SignUtils.parameterText(params, "&", SIGN, "appId") + "&key=" + (signUtils == SignUtils.MD5 ? "" : keyPrivate); + return signUtils.verify(content, sign, keyPrivate, payConfigStorage.getInputCharset()); } /** -- Gitee From cc874c1fdc294cd32841d09934922ff1de52548b Mon Sep 17 00:00:00 2001 From: egzosn Date: Sun, 9 Jun 2019 13:01:06 +0800 Subject: [PATCH 038/299] =?UTF-8?q?=E9=93=B6=E8=81=94=E8=AF=81=E4=B9=A6?= =?UTF-8?q?=E8=A6=86=E7=9B=96=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/main/java/com/egzosn/pay/union/api/UnionPayService.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pay-java-union/src/main/java/com/egzosn/pay/union/api/UnionPayService.java b/pay-java-union/src/main/java/com/egzosn/pay/union/api/UnionPayService.java index cb867ab..3abd21d 100644 --- a/pay-java-union/src/main/java/com/egzosn/pay/union/api/UnionPayService.java +++ b/pay-java-union/src/main/java/com/egzosn/pay/union/api/UnionPayService.java @@ -59,7 +59,7 @@ public class UnionPayService extends BasePayService { /** * 证书解释器 */ - private CertDescriptor certDescriptor = null; + private CertDescriptor certDescriptor; /** * 构造函数 * -- Gitee From bf6b6d9628971320d82ae460b02f287987af76d5 Mon Sep 17 00:00:00 2001 From: egan Date: Wed, 3 Jul 2019 22:40:49 +0800 Subject: [PATCH 039/299] =?UTF-8?q?=E6=94=AF=E4=BB=98=E5=9B=9E=E8=B0=83?= =?UTF-8?q?=E6=B6=88=E6=81=AF=E7=9A=84=E5=AE=9A=E4=B9=89=E4=B8=8E=E9=80=BB?= =?UTF-8?q?=E8=BE=91=E5=AE=9E=E7=8E=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../egzosn/pay/common/api/BasePayService.java | 131 +++++---- .../pay/common/api/PayMessageHandler.java | 6 +- .../pay/common/api/PayMessageInterceptor.java | 6 +- .../pay/common/api/PayMessageRouter.java | 278 ++++++++++-------- .../com/egzosn/pay/common/api/PayService.java | 175 ++++++----- .../egzosn/pay/common/bean/PayMessage.java | 10 +- .../com/egzosn/pay/common/util/DateUtils.java | 25 +- 7 files changed, 358 insertions(+), 273 deletions(-) diff --git a/pay-java-common/src/main/java/com/egzosn/pay/common/api/BasePayService.java b/pay-java-common/src/main/java/com/egzosn/pay/common/api/BasePayService.java index a8894eb..ed8e109 100644 --- a/pay-java-common/src/main/java/com/egzosn/pay/common/api/BasePayService.java +++ b/pay-java-common/src/main/java/com/egzosn/pay/common/api/BasePayService.java @@ -17,13 +17,14 @@ import java.util.*; /** * 支付基础服务 + * * @author: egan - *
+ * 
  *      email egzosn@gmail.com
  *      date 2017/3/5 20:36
  *   
*/ -public abstract class BasePayService implements PayService { +public abstract class BasePayService implements PayService { protected final Log LOG = LogFactory.getLog(getClass()); protected PC payConfigStorage; @@ -38,10 +39,12 @@ public abstract class BasePayService implements Pay /** * 支付消息拦截器 */ - protected List interceptors = new ArrayList();; + protected List interceptors = new ArrayList(); + ; /** * 设置支付配置 + * * @param payConfigStorage 支付配置 */ @Override @@ -54,6 +57,7 @@ public abstract class BasePayService implements Pay public PC getPayConfigStorage() { return payConfigStorage; } + @Override public HttpRequestTemplate getHttpRequestTemplate() { return requestTemplate; @@ -61,6 +65,7 @@ public abstract class BasePayService implements Pay /** * 设置并创建请求模版, 代理请求配置这里是否合理??, + * * @param configStorage http请求配置 * @return 支付服务 */ @@ -82,17 +87,18 @@ public abstract class BasePayService implements Pay /** - * Generate a Base64 encoded String from user , password - * @param user 用户名 + * Generate a Base64 encoded String from user , password + * + * @param user 用户名 * @param password 密码 * @return authorizationString */ protected String authorizationString(String user, String password) { String base64ClientID = null; try { - base64ClientID = com.egzosn.pay.common.util.sign.encrypt.Base64.encode(String.format("%s:%s", user , password).getBytes("UTF-8")); + base64ClientID = com.egzosn.pay.common.util.sign.encrypt.Base64.encode(String.format("%s:%s", user, password).getBytes("UTF-8")); } catch (UnsupportedEncodingException e) { - LOG.error(e); + LOG.error(e); } return base64ClientID; @@ -108,8 +114,9 @@ public abstract class BasePayService implements Pay @Override public String createSign(String content, String characterEncoding) { - return SignUtils.valueOf(payConfigStorage.getSignType()).createSign(content, payConfigStorage.getKeyPrivate(),characterEncoding); + return SignUtils.valueOf(payConfigStorage.getSignType()).createSign(content, payConfigStorage.getKeyPrivate(), characterEncoding); } + /** * 创建签名 * @@ -119,7 +126,7 @@ public abstract class BasePayService implements Pay */ @Override public String createSign(Map content, String characterEncoding) { - return SignUtils.valueOf(payConfigStorage.getSignType()).sign(content, payConfigStorage.getKeyPrivate(),characterEncoding); + return SignUtils.valueOf(payConfigStorage.getSignType()).sign(content, payConfigStorage.getKeyPrivate(), characterEncoding); } /** @@ -142,20 +149,20 @@ public abstract class BasePayService implements Pay * @return 获得回调的请求参数 */ @Override - public Map getParameter2Map (Map parameterMap, InputStream is) { + public Map getParameter2Map(Map parameterMap, InputStream is) { - Map params = new TreeMap(); - for (Map.Entry entry : parameterMap.entrySet()) { + Map params = new TreeMap(); + for (Map.Entry entry : parameterMap.entrySet()) { String name = entry.getKey(); String[] values = entry.getValue(); String valueStr = ""; - for (int i = 0,len = values.length; i < len; i++) { - valueStr += (i == len - 1) ? values[i] : values[i] + ","; + for (int i = 0, len = values.length; i < len; i++) { + valueStr += (i == len - 1) ? values[i] : values[i] + ","; } - if (StringUtils.isNotEmpty(payConfigStorage.getInputCharset()) && !valueStr.matches("\\w+")){ + if (StringUtils.isNotEmpty(payConfigStorage.getInputCharset()) && !valueStr.matches("\\w+")) { try { - if(valueStr.equals(new String(valueStr.getBytes("iso8859-1"), "iso8859-1"))){ - valueStr=new String(valueStr.getBytes("iso8859-1"), payConfigStorage.getInputCharset()); + if (valueStr.equals(new String(valueStr.getBytes("iso8859-1"), "iso8859-1"))) { + valueStr = new String(valueStr.getBytes("iso8859-1"), payConfigStorage.getInputCharset()); } } catch (UnsupportedEncodingException e) { LOG.error(e); @@ -169,11 +176,12 @@ public abstract class BasePayService implements Pay /** * 交易查询接口,带处理器 + * * @param tradeNo 支付平台订单号 * @param outTradeNo 商户单号 - * @param callback 处理器 - * @param 返回类型 - * @return 返回查询回来的结果集 + * @param callback 处理器 + * @param 返回类型 + * @return 返回查询回来的结果集 */ @Override public T query(String tradeNo, String outTradeNo, Callback callback) { @@ -186,26 +194,27 @@ public abstract class BasePayService implements Pay * * @param tradeNo 支付平台订单号 * @param outTradeNo 商户单号 - * @param callback 处理器 - * @param 返回类型 + * @param callback 处理器 + * @param 返回类型 * @return 返回支付方交易关闭后的结果 */ @Override public T close(String tradeNo, String outTradeNo, Callback callback) { - return callback.perform(close(tradeNo, outTradeNo)); + return callback.perform(close(tradeNo, outTradeNo)); } + /** * 交易撤销 * * @param tradeNo 支付平台订单号 * @param outTradeNo 商户单号 - * @param callback 处理器 - * @param 返回类型 + * @param callback 处理器 + * @param 返回类型 * @return 返回支付方交易撤销后的结果 */ @Override public T cancel(String tradeNo, String outTradeNo, Callback callback) { - return callback.perform(close(tradeNo, outTradeNo)); + return callback.perform(close(tradeNo, outTradeNo)); } /** @@ -229,7 +238,6 @@ public abstract class BasePayService implements Pay * @param totalAmount 总金额 * @param callback 处理器 * @param 返回类型 - * * @return 处理过后的类型对象, 返回支付方申请退款后的结果 * @see #refund(RefundOrder, Callback) */ @@ -243,16 +251,15 @@ public abstract class BasePayService implements Pay /** * 申请退款接口 * - * @param refundOrder 退款订单信息 - * @return 返回支付方申请退款后的结果 - * @param callback 处理器 - * @param 返回类型 + * @param refundOrder 退款订单信息 + * @param callback 处理器 + * @param 返回类型 * @return 返回支付方申请退款后的结果 */ @Override public T refund(RefundOrder refundOrder, Callback callback) { - return callback.perform(refund(refundOrder)); + return callback.perform(refund(refundOrder)); } @@ -263,7 +270,6 @@ public abstract class BasePayService implements Pay * @param outTradeNo 商户单号 * @param callback 处理器 * @param 返回类型 - * * @return 处理过后的类型对象,返回支付方查询退款后的结果 */ @Override @@ -274,13 +280,13 @@ public abstract class BasePayService implements Pay /** * 查询退款 * - * @param refundOrder 退款订单信息 - * @param callback 处理器 - * @param 返回类型 + * @param refundOrder 退款订单信息 + * @param callback 处理器 + * @param 返回类型 * @return 返回支付方查询退款后的结果 */ @Override - public T refundquery(RefundOrder refundOrder, Callback callback){ + public T refundquery(RefundOrder refundOrder, Callback callback) { return callback.perform(refundquery(refundOrder)); } @@ -291,7 +297,6 @@ public abstract class BasePayService implements Pay * @param billType 账单类型,具体请查看对应支付平台 * @param callback 处理器 * @param 返回类型 - * * @return 返回支付方下载对账单的结果 */ @Override @@ -300,15 +305,15 @@ public abstract class BasePayService implements Pay } /** - * @param tradeNoOrBillDate 支付平台订单号或者账单类型, 具体请 类型为{@link String }或者 {@link Date },类型须强制限制,类型不对应则抛出异常{@link PayErrorException} - * @param outTradeNoBillType 商户单号或者 账单类型 - * @param transactionType 交易类型 - * @param callback 处理器 - * @param 返回类型 + * @param tradeNoOrBillDate 支付平台订单号或者账单类型, 具体请 类型为{@link String }或者 {@link Date },类型须强制限制,类型不对应则抛出异常{@link PayErrorException} + * @param outTradeNoBillType 商户单号或者 账单类型 + * @param transactionType 交易类型 + * @param callback 处理器 + * @param 返回类型 * @return 返回支付方对应接口的结果 */ @Override - public T secondaryInterface(Object tradeNoOrBillDate, String outTradeNoBillType, TransactionType transactionType, Callback callback){ + public T secondaryInterface(Object tradeNoOrBillDate, String outTradeNoBillType, TransactionType transactionType, Callback callback) { return callback.perform(secondaryInterface(tradeNoOrBillDate, outTradeNoBillType, transactionType)); } @@ -317,7 +322,6 @@ public abstract class BasePayService implements Pay * * @param order 转账订单 * @param callback 处理器 - * * @return 对应的转账结果 */ @Override @@ -330,7 +334,6 @@ public abstract class BasePayService implements Pay * 转账 * * @param order 转账订单 - * * @return 对应的转账结果 */ @Override @@ -341,27 +344,26 @@ public abstract class BasePayService implements Pay /** * 转账查询 * - * @param outNo 商户转账订单号 + * @param outNo 商户转账订单号 * @param tradeNo 支付平台转账订单号 - * * @return 对应的转账订单 */ @Override - public Map transferQuery(String outNo, String tradeNo){ + public Map transferQuery(String outNo, String tradeNo) { return new HashMap<>(0); } /** * 转账查询 * - * @param outNo 商户转账订单号 - * @param tradeNo 支付平台转账订单号 + * @param outNo 商户转账订单号 + * @param tradeNo 支付平台转账订单号 * @param callback 处理器 - * @param 返回类型 + * @param 返回类型 * @return 对应的转账订单 */ @Override - public T transferQuery(String outNo, String tradeNo, Callback callback){ + public T transferQuery(String outNo, String tradeNo, Callback callback) { return callback.perform(transferQuery(outNo, tradeNo)); } @@ -382,11 +384,11 @@ public abstract class BasePayService implements Pay * 获取支付消息处理器,这里用于处理具体的支付业务 * 配合{@link PayService#payBack(Map, InputStream)}进行使用 *

- * @return 默认使用{@link DefaultPayMessageHandler }进行实现 * + * @return 默认使用{@link DefaultPayMessageHandler }进行实现 */ public PayMessageHandler getPayMessageHandler() { - if (null == handler){ + if (null == handler) { setPayMessageHandler(new DefaultPayMessageHandler()); } return handler; @@ -416,16 +418,27 @@ public abstract class BasePayService implements Pay if (LOG.isDebugEnabled()) { LOG.debug("回调响应:" + JSON.toJSONString(data)); } - if (!verify(data)){ + if (!verify(data)) { return getPayOutMessage("fail", "失败"); } - PayMessage payMessage = new PayMessage(data); + PayMessage payMessage = this.createMessage(data); Map context = new HashMap(); - for (PayMessageInterceptor interceptor : interceptors){ - if (!interceptor.intercept(payMessage, context, this)){ + for (PayMessageInterceptor interceptor : interceptors) { + if (!interceptor.intercept(payMessage, context, this)) { return successPayOutMessage(payMessage); } } return getPayMessageHandler().handle(payMessage, context, this); } + + /** + * 创建消息 + * + * @param message 支付平台返回的消息 + * @return 支付消息对象 + */ + @Override + public PayMessage createMessage(Map message) { + return new PayMessage(message); + } } diff --git a/pay-java-common/src/main/java/com/egzosn/pay/common/api/PayMessageHandler.java b/pay-java-common/src/main/java/com/egzosn/pay/common/api/PayMessageHandler.java index 5485d84..53485a7 100644 --- a/pay-java-common/src/main/java/com/egzosn/pay/common/api/PayMessageHandler.java +++ b/pay-java-common/src/main/java/com/egzosn/pay/common/api/PayMessageHandler.java @@ -19,7 +19,7 @@ import java.util.Map; * source Daniel Qian *

*/ -public interface PayMessageHandler { +public interface PayMessageHandler { /** * 处理支付回调消息的处理器接口 @@ -29,9 +29,9 @@ public interface PayMessageHandler { * @return xml,text格式的消息,如果在异步规则里处理的话,可以返回null * @throws PayErrorException 支付错误异常 */ - PayOutMessage handle(PayMessage payMessage, + PayOutMessage handle(M payMessage, Map context, - PayService payService + S payService ) throws PayErrorException; } diff --git a/pay-java-common/src/main/java/com/egzosn/pay/common/api/PayMessageInterceptor.java b/pay-java-common/src/main/java/com/egzosn/pay/common/api/PayMessageInterceptor.java index bcec731..69b7f4d 100644 --- a/pay-java-common/src/main/java/com/egzosn/pay/common/api/PayMessageInterceptor.java +++ b/pay-java-common/src/main/java/com/egzosn/pay/common/api/PayMessageInterceptor.java @@ -18,7 +18,7 @@ import java.util.Map; * source Daniel Qian * */ -public interface PayMessageInterceptor { +public interface PayMessageInterceptor { /** * 拦截微信消息 @@ -28,9 +28,9 @@ public interface PayMessageInterceptor { * @param payService 支付服务 * @return true代表OK,false代表不OK */ - boolean intercept(PayMessage payMessage, + boolean intercept(M payMessage, Map context, - PayService payService + S payService ) throws PayErrorException; } diff --git a/pay-java-common/src/main/java/com/egzosn/pay/common/api/PayMessageRouter.java b/pay-java-common/src/main/java/com/egzosn/pay/common/api/PayMessageRouter.java index 4675b71..3a54246 100644 --- a/pay-java-common/src/main/java/com/egzosn/pay/common/api/PayMessageRouter.java +++ b/pay-java-common/src/main/java/com/egzosn/pay/common/api/PayMessageRouter.java @@ -2,13 +2,13 @@ package com.egzosn.pay.common.api; import com.egzosn.pay.common.bean.PayMessage; import com.egzosn.pay.common.bean.PayOutMessage; - import com.egzosn.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.Map; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; @@ -17,7 +17,7 @@ import java.util.concurrent.Future; /** *
  * 支付消息路由器,通过代码化的配置,把来自支付的消息交给handler处理
- * 
+ *
  * 说明:
  * 1. 配置路由规则时要按照从细到粗的原则,否则可能消息可能会被提前处理
  * 2. 默认情况下消息只会被处理一次,除非使用 {@link PayMessageRouterRule#next()}
@@ -39,147 +39,167 @@ import java.util.concurrent.Future;
  * router.route(message);
  *  source chanjarster/weixin-java-tools  Daniel Qian
  * 
- * @author egan * + * @author egan */ 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 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); - 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; - } - - /** - * 获取所有的规则 - * @return 规则 - */ - List getRules() { - return this.rules; - } - - /** - * 开始一个新的Route规则 - * @return 新的Route规则 - */ - public PayMessageRouterRule rule() { - return new PayMessageRouterRule(this); - } - - /** - * 处理支付消息 - * @param payMessage 支付消息 - * @return 支付输出结果 - */ - 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; - } - } + 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 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); + this.exceptionHandler = new LogExceptionHandler(); } - if (matchRules.isEmpty()) { - return null; + /** + *
+     * 设置自定义的 {@link ExecutorService}
+     * 如果不调用用该方法,默认使 Executors.newFixedThreadPool(100)
+     * 
+ * + * @param executorService 异步线程处理器 + */ + public void setExecutorService(ExecutorService executorService) { + this.executorService = executorService; } - PayOutMessage res = null; - final List futures = new ArrayList(); - for (final PayMessageRouterRule rule : matchRules) { - // 返回最后一个非异步的rule的执行结果 - if(rule.isAsync()) { - futures.add( - executorService.submit(new Runnable() { - @Override - public void run() { - rule.service(payMessage, payService, exceptionHandler); - } - }) - ); - } else { - res = rule.service(payMessage, payService, exceptionHandler); - // 在同步操作结束,session访问结束 - if (LOG.isDebugEnabled()) { - LOG.debug("End session access: async=false, fromPay=" + payMessage.getFromPay()); + + /** + *
+     * 设置自定义的{@link PayErrorExceptionHandler}
+     * 如果不调用该方法,默认使用 {@link LogExceptionHandler}
+     * 
+ * + * @param exceptionHandler 异常处理器 + */ + public void setExceptionHandler(PayErrorExceptionHandler exceptionHandler) { + this.exceptionHandler = exceptionHandler; + } + + /** + * 获取所有的规则 + * + * @return 规则 + */ + List getRules() { + return this.rules; + } + + /** + * 开始一个新的Route规则 + * + * @return 新的Route规则 + */ + public PayMessageRouterRule rule() { + return new PayMessageRouterRule(this); + } + + /** + * 处理支付消息 + * + * @param payMessage 支付消息 + * @return 支付输出结果 + */ + public PayOutMessage route(Map payMessage, PayConfigStorage storage) { + PayMessage message = payService.createMessage(payMessage); + message.setPayType(storage.getPayType()); + if (null == storage.getMsgType()){ + message.setMsgType(storage.getMsgType().name()); } - } + return route(message); } - 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); + /** + * 处理支付消息 + * + * @param payMessage 支付消息 + * @return 支付输出结果 + */ + 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.isEmpty()) { + 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() { + @Override + public void run() { + rule.service(payMessage, payService, exceptionHandler); + } + }) + ); + } else { + res = rule.service(payMessage, payService, exceptionHandler); + // 在同步操作结束,session访问结束 + if (LOG.isDebugEnabled()) { + 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; } - return res; - } } diff --git a/pay-java-common/src/main/java/com/egzosn/pay/common/api/PayService.java b/pay-java-common/src/main/java/com/egzosn/pay/common/api/PayService.java index c0eb4ef..0c7f73d 100644 --- a/pay-java-common/src/main/java/com/egzosn/pay/common/api/PayService.java +++ b/pay-java-common/src/main/java/com/egzosn/pay/common/api/PayService.java @@ -15,17 +15,17 @@ import java.util.Map; * 支付服务 * * @author egan - *
- * email egzosn@gmail.com
- * date 2016-5-18 14:09:01
- *
+ *
+ *         email egzosn@gmail.com
+ *         date 2016-5-18 14:09:01
+ *         
*/ public interface PayService { - /** * 设置支付配置 + * * @param payConfigStorage 支付配置 * @return 支付服务 */ @@ -37,6 +37,7 @@ public interface PayService { * @return 支付配置 */ PC getPayConfigStorage(); + /** * 获取http请求工具 * @@ -46,8 +47,9 @@ public interface PayService { /** * 设置 请求工具配置 设置并创建请求模版, 代理请求配置这里是否合理??, + * * @param configStorage http请求配置 - * @return 支付服务 + * @return 支付服务 */ PayService setRequestTemplateConfigStorage(HttpConfigStorage configStorage); @@ -72,6 +74,7 @@ public interface PayService { /** * 支付宝需要,微信是否也需要再次校验来源,进行订单查询 * 校验数据来源 + * * @param id 业务id, 数据的真实性. * @return true通过 */ @@ -85,14 +88,16 @@ public interface PayService { * @return 订单信息 * @see PayOrder 支付订单信息 */ - Map orderInfo(PayOrder order); + Map orderInfo(PayOrder order); /** * 页面转跳支付, 返回对应页面重定向信息 + * * @param order 订单信息 * @return 对应页面重定向信息 */ String toPay(PayOrder order); + /** * 创建签名 * @@ -123,7 +128,7 @@ public interface PayService { /** * 获取输出消息,用户返回给支付端 * - * @param code 状态 + * @param code 状态 * @param message 消息 * @return 返回输出消息 */ @@ -132,6 +137,7 @@ public interface PayService { /** * 获取成功输出消息,用户返回给支付端 * 主要用于拦截器中返回 + * * @param payMessage 支付回调消息 * @return 返回输出消息 */ @@ -148,7 +154,6 @@ public interface PayService { String buildRequest(Map orderInfo, MethodType method); - /** * 获取输出二维码,用户返回给支付端, * @@ -163,7 +168,7 @@ public interface PayService { * @param order 发起支付的订单信息 * @return 返回支付结果 */ - Map microPay(PayOrder order); + Map microPay(PayOrder order); /** * 交易查询接口 @@ -176,13 +181,14 @@ public interface PayService { /** * 交易查询接口,带处理器 + * * @param tradeNo 支付平台订单号 * @param outTradeNo 商户单号 - * @param callback 处理器 - * @param 返回类型 - * @return 返回查询回来的结果集 + * @param callback 处理器 + * @param 返回类型 + * @return 返回查询回来的结果集 */ - T query(String tradeNo, String outTradeNo, Callback callback); + T query(String tradeNo, String outTradeNo, Callback callback); /** * 交易关闭接口 @@ -199,11 +205,11 @@ public interface PayService { * * @param tradeNo 支付平台订单号 * @param outTradeNo 商户单号 - * @param callback 处理器 - * @param 返回类型 + * @param callback 处理器 + * @param 返回类型 * @return 返回支付方交易关闭后的结果 */ - T close(String tradeNo, String outTradeNo, Callback callback); + T close(String tradeNo, String outTradeNo, Callback callback); /** * 交易交易撤销 @@ -219,55 +225,59 @@ public interface PayService { * * @param tradeNo 支付平台订单号 * @param outTradeNo 商户单号 - * @param callback 处理器 - * @param 返回类型 + * @param callback 处理器 + * @param 返回类型 * @return 返回支付方交易撤销后的结果 */ - T cancel(String tradeNo, String outTradeNo, Callback callback); + T cancel(String tradeNo, String outTradeNo, Callback callback); /** * 申请退款接口 * 废弃 - * @param tradeNo 支付平台订单号 - * @param outTradeNo 商户单号 + * + * @param tradeNo 支付平台订单号 + * @param outTradeNo 商户单号 * @param refundAmount 退款金额 - * @param totalAmount 总金额 + * @param totalAmount 总金额 * @return 返回支付方申请退款后的结果 * @see #refund(RefundOrder) */ @Deprecated Map refund(String tradeNo, String outTradeNo, BigDecimal refundAmount, BigDecimal totalAmount); + /** * 申请退款接口 * 废弃 - * @param tradeNo 支付平台订单号 - * @param outTradeNo 商户单号 + * + * @param tradeNo 支付平台订单号 + * @param outTradeNo 商户单号 * @param refundAmount 退款金额 - * @param totalAmount 总金额 - * @param callback 处理器 - * @param 返回类型 + * @param totalAmount 总金额 + * @param callback 处理器 + * @param 返回类型 * @return 返回支付方申请退款后的结果 * @see #refund(RefundOrder, Callback) */ @Deprecated - T refund(String tradeNo, String outTradeNo, BigDecimal refundAmount, BigDecimal totalAmount, Callback callback); + T refund(String tradeNo, String outTradeNo, BigDecimal refundAmount, BigDecimal totalAmount, Callback callback); /** * 申请退款接口 * - * @param refundOrder 退款订单信息 + * @param refundOrder 退款订单信息 * @return 返回支付方申请退款后的结果 */ Map refund(RefundOrder refundOrder); + /** * 申请退款接口 * - * @param refundOrder 退款订单信息 - * @param callback 处理器 - * @param 返回类型 + * @param refundOrder 退款订单信息 + * @param callback 处理器 + * @param 返回类型 * @return 返回支付方申请退款后的结果 */ - T refund(RefundOrder refundOrder, Callback callback); + T refund(RefundOrder refundOrder, Callback callback); /** * 查询退款 @@ -278,33 +288,36 @@ public interface PayService { */ @Deprecated Map refundquery(String tradeNo, String outTradeNo); + /** * 查询退款 * * @param tradeNo 支付平台订单号 * @param outTradeNo 商户单号 - * @param callback 处理器 - * @param 返回类型 + * @param callback 处理器 + * @param 返回类型 * @return 返回支付方查询退款后的结果 */ @Deprecated - T refundquery(String tradeNo, String outTradeNo, Callback callback); + T refundquery(String tradeNo, String outTradeNo, Callback callback); + /** * 查询退款 * - * @param refundOrder 退款订单单号信息 + * @param refundOrder 退款订单单号信息 * @return 返回支付方查询退款后的结果 */ Map refundquery(RefundOrder refundOrder); + /** * 查询退款 * - * @param refundOrder 退款订单信息 - * @param callback 处理器 - * @param 返回类型 + * @param refundOrder 退款订单信息 + * @param callback 处理器 + * @param 返回类型 * @return 返回支付方查询退款后的结果 */ - T refundquery(RefundOrder refundOrder, Callback callback); + T refundquery(RefundOrder refundOrder, Callback callback); /** * 下载对账单 @@ -321,18 +334,19 @@ public interface PayService { * @param billDate 账单时间:具体请查看对应支付平台 * @param billType 账单类型,具体请查看对应支付平台 * @param callback 处理器 - * @param 返回类型 + * @param 返回类型 * @return 返回支付方下载对账单的结果 */ - T downloadbill(Date billDate, String billType, Callback callback); + T downloadbill(Date billDate, String billType, Callback callback); /** * 通用查询接口 - * @param tradeNoOrBillDate 支付平台订单号或者账单类型, 具体请 - * 类型为{@link String }或者 {@link Date },类型须强制限制,类型不对应则抛出异常{@link PayErrorException} - * @param outTradeNoBillType 商户单号或者 账单类型 - * @param transactionType 交易类型 + * + * @param tradeNoOrBillDate 支付平台订单号或者账单类型, 具体请 + * 类型为{@link String }或者 {@link Date },类型须强制限制,类型不对应则抛出异常{@link PayErrorException} + * @param outTradeNoBillType 商户单号或者 账单类型 + * @param transactionType 交易类型 * @return 返回支付方对应接口的结果 */ Map secondaryInterface(Object tradeNoOrBillDate, String outTradeNoBillType, TransactionType transactionType); @@ -340,56 +354,56 @@ public interface PayService { /** * 通用查询接口 * - * @param tradeNoOrBillDate 支付平台订单号或者账单日期, 具体请 类型为{@link String }或者 {@link Date },类型须强制限制,类型不对应则抛出异常{@link PayErrorException} - * - * @param outTradeNoBillType 商户单号或者 账单类型 - * @param transactionType 交易类型 - * @param callback 处理器 - * @param 返回类型 + * @param tradeNoOrBillDate 支付平台订单号或者账单日期, 具体请 类型为{@link String }或者 {@link Date },类型须强制限制,类型不对应则抛出异常{@link PayErrorException} + * @param outTradeNoBillType 商户单号或者 账单类型 + * @param transactionType 交易类型 + * @param callback 处理器 + * @param 返回类型 * @return 返回支付方对应接口的结果 */ - T secondaryInterface(Object tradeNoOrBillDate, String outTradeNoBillType, TransactionType transactionType, Callback callback); + T secondaryInterface(Object tradeNoOrBillDate, String outTradeNoBillType, TransactionType transactionType, Callback callback); /** * 转账 + * * @param order 转账订单 * @return 对应的转账结果 */ Map transfer(TransferOrder order); + /** * 转账 - * @param order 转账订单 + * + * @param order 转账订单 * @param callback 处理器 - * @param 返回类型 + * @param 返回类型 * @return 对应的转账结果 */ - T transfer(TransferOrder order, Callback callback); - + T transfer(TransferOrder order, Callback callback); /** * 转账查询 * - * @param outNo 商户转账订单号 + * @param outNo 商户转账订单号 * @param tradeNo 支付平台转账订单号 - * * @return 对应的转账订单 */ - Map transferQuery(String outNo, String tradeNo); + Map transferQuery(String outNo, String tradeNo); /** * 转账查询 * - * @param outNo 商户转账订单号 - * @param tradeNo 支付平台转账订单号 + * @param outNo 商户转账订单号 + * @param tradeNo 支付平台转账订单号 * @param callback 处理器 - * @param 返回类型 + * @param 返回类型 * @return 对应的转账订单 */ - T transferQuery(String outNo, String tradeNo, Callback callback); + T transferQuery(String outNo, String tradeNo, Callback callback); - /** + /** * 将请求参数或者请求流转化为 Map * * @param parameterMap 请求参数 @@ -400,29 +414,36 @@ public interface PayService { /** * 设置支付消息处理器,这里用于处理具体的支付业务 - * @param handler 消息处理器 - * 配合{@link com.egzosn.pay.common.api.PayService#payBack(java.util.Map, java.io.InputStream)}进行使用 - * - * 默认使用{@link com.egzosn.pay.common.api.DefaultPayMessageHandler }进行实现 * + * @param handler 消息处理器 + * 配合{@link com.egzosn.pay.common.api.PayService#payBack(java.util.Map, java.io.InputStream)}进行使用 + *

+ * 默认使用{@link com.egzosn.pay.common.api.DefaultPayMessageHandler }进行实现 */ void setPayMessageHandler(PayMessageHandler handler); /** * 设置支付消息处理器,这里用于处理具体的支付业务 - * @param interceptor 消息拦截器 - * 配合{@link com.egzosn.pay.common.api.PayService#payBack(java.util.Map, java.io.InputStream)}进行使用 - * - * 默认使用{@link com.egzosn.pay.common.api.DefaultPayMessageHandler }进行实现 * + * @param interceptor 消息拦截器 + * 配合{@link com.egzosn.pay.common.api.PayService#payBack(java.util.Map, java.io.InputStream)}进行使用 + *

+ * 默认使用{@link com.egzosn.pay.common.api.DefaultPayMessageHandler }进行实现 */ void addPayMessageInterceptor(PayMessageInterceptor interceptor); /** - * 获取支付请求地址 + * 获取支付请求地址 + * * @param transactionType 交易类型 * @return 请求地址 */ - String getReqUrl(TransactionType transactionType); + String getReqUrl(TransactionType transactionType); + /** + * 创建消息 + * @param message 支付平台返回的消息 + * @return 支付消息对象 + */ + PayMessage createMessage(Map message); } diff --git a/pay-java-common/src/main/java/com/egzosn/pay/common/bean/PayMessage.java b/pay-java-common/src/main/java/com/egzosn/pay/common/bean/PayMessage.java index cff3dc2..1836758 100644 --- a/pay-java-common/src/main/java/com/egzosn/pay/common/bean/PayMessage.java +++ b/pay-java-common/src/main/java/com/egzosn/pay/common/bean/PayMessage.java @@ -24,12 +24,16 @@ public class PayMessage implements Serializable { private String fromPay; private String describe; + + public PayMessage() { + } + public PayMessage(Map payMessage) { this.payMessage = payMessage; } public PayMessage(Map payMessage, String payType, String msgType) { - this(payMessage); + this.payMessage = payMessage; this.payType = payType; this.msgType = msgType; } @@ -42,6 +46,10 @@ public class PayMessage implements Serializable { this.transactionType = transactionType; } + protected void setPayMessage(Map payMessage) { + this.payMessage = payMessage; + } + public String getMsgType() { return msgType; } diff --git a/pay-java-common/src/main/java/com/egzosn/pay/common/util/DateUtils.java b/pay-java-common/src/main/java/com/egzosn/pay/common/util/DateUtils.java index 39081ea..b101b44 100644 --- a/pay-java-common/src/main/java/com/egzosn/pay/common/util/DateUtils.java +++ b/pay-java-common/src/main/java/com/egzosn/pay/common/util/DateUtils.java @@ -1,8 +1,11 @@ package com.egzosn.pay.common.util; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; import org.apache.http.util.Args; import java.lang.ref.SoftReference; +import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.*; @@ -16,7 +19,10 @@ import java.util.*; * */ public final class DateUtils { + private DateUtils() { + } + private static final Log LOG = LogFactory.getLog(DateUtils.class); static final class DateFormatHolder { private static final ThreadLocal>> THREADLOCAL_FORMATS = new ThreadLocal>>(); @@ -60,11 +66,28 @@ public final class DateUtils { SimpleDateFormat formatFor = DateFormatHolder.formatFor(pattern); return formatFor.format(date); } - + public static Date parseDate(String date, String pattern) { + Args.notNull(date, "Date"); + Args.notNull(pattern, "Pattern"); + SimpleDateFormat formatFor = DateFormatHolder.formatFor(pattern); + try { + return formatFor.parse(date); + } catch (ParseException e) { + LOG.error(e); + } + return null; + } + public static Date parse(String date) { + return parseDate(date, YYYY_MM_DD_HH_MM_SS); + } public static final String format(Date date) { return formatDate(date, YYYY_MM_DD_HH_MM_SS); } + public static final Date parseDay(String date) { + return parseDate(date, YYYY_MM_DD); + } + public static final String formatDay(Date date) { return formatDate(date, YYYY_MM_DD); } -- Gitee From 91f2e3de13e2256120a3da12a908f77705d80b1b Mon Sep 17 00:00:00 2001 From: egan Date: Wed, 3 Jul 2019 22:41:52 +0800 Subject: [PATCH 040/299] =?UTF-8?q?=E5=90=84=E4=B8=AA=E6=94=AF=E4=BB=98?= =?UTF-8?q?=E6=B8=A0=E9=81=93=E7=9A=84=E6=94=AF=E4=BB=98=E5=9B=9E=E8=B0=83?= =?UTF-8?q?=E6=B6=88=E6=81=AF=E5=AE=9E=E7=8E=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/egzosn/pay/ali/api/AliPayService.java | 11 + .../egzosn/pay/ali/bean/AliPayMessage.java | 263 +++++++++++++ .../egzosn/pay/union/api/UnionPayService.java | 13 +- .../pay/union/bean/UnionPayMessage.java | 354 ++++++++++++++++++ .../wx/youdian/api/WxYouDianPayService.java | 12 + .../wx/youdian/bean/WxYoudianPayMessage.java | 327 ++++++++++++++++ .../com/egzosn/pay/wx/api/WxPayService.java | 12 +- .../com/egzosn/pay/wx/bean/WxPayMessage.java | 327 ++++++++++++++++ 8 files changed, 1316 insertions(+), 3 deletions(-) create mode 100644 pay-java-ali/src/main/java/com/egzosn/pay/ali/bean/AliPayMessage.java create mode 100644 pay-java-union/src/main/java/com/egzosn/pay/union/bean/UnionPayMessage.java create mode 100644 pay-java-wx-youdian/src/main/java/com/egzosn/pay/wx/youdian/bean/WxYoudianPayMessage.java create mode 100644 pay-java-wx/src/main/java/com/egzosn/pay/wx/bean/WxPayMessage.java 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 922264d..fa21cfb 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 @@ -2,6 +2,7 @@ package com.egzosn.pay.ali.api; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONObject; +import com.egzosn.pay.ali.bean.AliPayMessage; import com.egzosn.pay.ali.bean.AliTransactionType; import com.egzosn.pay.ali.bean.OrderSettle; import com.egzosn.pay.common.api.BasePayService; @@ -608,4 +609,14 @@ public class AliPayService extends BasePayService { return JSON.toJSONString(getBizContent(tradeNo, outTradeNo, null)); } + /** + * 创建消息 + * + * @param message 支付平台返回的消息 + * @return 支付消息对象 + */ + @Override + public PayMessage createMessage(Map message) { + return AliPayMessage.create(message); + } } diff --git a/pay-java-ali/src/main/java/com/egzosn/pay/ali/bean/AliPayMessage.java b/pay-java-ali/src/main/java/com/egzosn/pay/ali/bean/AliPayMessage.java new file mode 100644 index 0000000..f27cd6d --- /dev/null +++ b/pay-java-ali/src/main/java/com/egzosn/pay/ali/bean/AliPayMessage.java @@ -0,0 +1,263 @@ +package com.egzosn.pay.ali.bean; + +import com.alibaba.fastjson.JSONObject; +import com.alibaba.fastjson.annotation.JSONField; +import com.egzosn.pay.common.bean.PayMessage; + +import java.math.BigDecimal; +import java.util.Date; +import java.util.Map; + +/** + * 支付宝回调信息 + * + * @author egan + * email egzosn@gmail.com + * date 2019/6/30.19:19 + */ + +public class AliPayMessage extends PayMessage { + //notify_time 通知时间 Date 是 通知的发送时间。格式为yyyy-MM-dd HH:mm:ss 2015-14-27 15:45:58 + @JSONField(name = "notify_time") + private Date notifyTime; + //notify_type 通知类型 String(64) 是 通知的类型 trade_status_sync + @JSONField(name = "notify_type") + private String notifyType; + // notify_id 通知校验ID String(128) 是 通知校验ID ac05099524730693a8b330c5ecf72da9786 + @JSONField(name = "notify_id") + private String notifyId; + // app_id 支付宝分配给开发者的应用Id String(32) 是 支付宝分配给开发者的应用Id 2014072300007148 + @JSONField(name = "app_id") + private String appId; + // charset 编码格式 String(10) 是 编码格式,如utf-8、gbk、gb2312等 utf-8 + private String charset; + // version 接口版本 String(3) 是 调用的接口版本,固定为:1.0 1.0 + private String version; + // sign_type 签名类型 String(10) 是 商户生成签名字符串所使用的签名算法类型,目前支持RSA2和RSA,推荐使用RSA2 RSA2 + @JSONField(name = "sign_type") + private String signType; + // sign 签名 String(256) 是 请参考文末 异步返回结果的验签 601510b7970e52cc63db0f44997cf70e + private String sign; + // trade_no 支付宝交易号 String(64) 是 支付宝交易凭证号 2013112011001004330000121536 + @JSONField(name = "trade_no") + private String tradeNo; + // out_trade_no 商户订单号 String(64) 是 原支付请求的商户订单号 6823789339978248 + @JSONField(name = "out_trade_no") + private String outTradeNo; + // out_biz_no 商户业务号 String(64) 否 商户业务ID,主要是退款通知中返回退款申请的流水号 HZRF001 + @JSONField(name = "out_biz_no") + private String outBizNo; + // buyer_id 买家支付宝用户号 String(16) 否 买家支付宝账号对应的支付宝唯一用户号。以2088开头的纯16位数字 2088102122524333 + @JSONField(name = "buyer_id") + private String buyerId; + // buyer_logon_id 买家支付宝账号 String(100) 否 买家支付宝账号 15901825620 + @JSONField(name = "buyer_logon_id") + private String buyerLogonId; + // seller_id 卖家支付宝用户号 String(30) 否 卖家支付宝用户号 2088101106499364 + @JSONField(name = "seller_id") + private String sellerId; + // seller_email 卖家支付宝账号 String(100) 否 卖家支付宝账号 zhuzhanghu@alitest.com + @JSONField(name = "seller_email") + private String sellerEmail; + // trade_status 交易状态 String(32) 否 交易目前所处的状态,见下张表 交易状态说明 TRADE_CLOSED + @JSONField(name = "trade_status") + private String tradeStatus; + // total_amount 订单金额 Number(9,2) 否 本次交易支付的订单金额,单位为人民币(元) 20 + @JSONField(name = "total_amount") + private BigDecimal totalAmount; + // receipt_amount 实收金额 Number(9,2) 否 商家在交易中实际收到的款项,单位为元 15 + @JSONField(name = "receipt_amount") + private BigDecimal receiptAmount; + // invoice_amount 开票金额 Number(9,2) 否 用户在交易中支付的可开发票的金额 10.00 + @JSONField(name = "invoice_amount") + private BigDecimal invoiceAmount; + // buyer_pay_amount 付款金额 Number(9,2) 否 用户在交易中支付的金额 13.88 + @JSONField(name = "buyer_pay_amount") + private BigDecimal buyerPayAmount; + // point_amount 集分宝金额 Number(9,2) 否 使用集分宝支付的金额 12.00 + @JSONField(name = "point_amount") + private BigDecimal pointAmount; + // refund_fee 总退款金额 Number(9,2) 否 退款通知中,返回总退款金额,单位为元,支持两位小数 2.58 + @JSONField(name = "refund_fee") + private BigDecimal refundFee; + // subject 订单标题 String(256) 否 商品的标题/交易标题/订单标题/订单关键字等,是请求时对应的参数,原样通知回来 当面付交易 + @JSONField(name = "subject") + private String subject; + // body 商品描述 String(400) 否 该订单的备注、描述、明细等。对应请求时的body参数,原样通知回来 当面付交易内容 + @JSONField(name = "body") + private String body; + // gmt_create 交易创建时间 Date 否 该笔交易创建的时间。格式为yyyy-MM-dd HH:mm:ss 2015-04-27 15:45:57 + @JSONField(name = "gmt_create") + private Date gmtCreate; + // gmt_payment 交易付款时间 Date 否 该笔交易的买家付款时间。格式为yyyy-MM-dd HH:mm:ss 2015-04-27 15:45:57 + @JSONField(name = "gmt_payment") + private Date gmtPayment; + // gmt_refund 交易退款时间 Date 否 该笔交易的退款时间。格式为yyyy-MM-dd HH:mm:ss.S 2015-04-28 15:45:57.320 + @JSONField(name = "gmt_refund") + private Date gmtRefund; + // gmt_close 交易结束时间 Date 否 该笔交易结束时间。格式为yyyy-MM-dd HH:mm:ss 2015-04-29 15:45:57 + @JSONField(name = "gmt_close") + private Date gmtClose; + // fund_bill_list 支付金额信息 String(512) 否 支付成功的各个渠道金额信息,详见下表 资金明细信息说明 [{“amount”:“15.00”,“fundChannel”:“ALIPAYACCOUNT”}] + @JSONField(name = "fund_bill_list") + private String fundBillList; + // passback_params 回传参数 String(512) 否 公共回传参数,如果请求时传递了该参数,则返回给商户时会在异步通知时将该参数原样返回。本参数必须进行UrlEncode之后才可以发送给支付宝 merchantBizType%3d3C%26merchantBizNo%3d2016010101111 + @JSONField(name = "passback_params") + private String passbackParams; + // voucher_detail_list 优惠券信息 String 否 本交易支付时所使用的所有优惠券信息,详见下表 优惠券信息说明 [{“amount”:“0.20”,“merchantContribute”:“0.00”,“name”:“一键创建券模板的券名称”,“otherContribute”:“0.20”,“type”:“ALIPAY_DISCOUNT_VOUCHER”,“memo”:“学生卡8折优惠”] + @JSONField(name = "voucher_detail_list") + private String voucherDetailList; + + public Date getNotifyTime() { + return notifyTime; + } + + public void setNotifyTime(Date notifyTime) { + this.notifyTime = notifyTime; + } + + public String getNotifyType() { + return notifyType; + } + + public void setNotifyType(String notifyType) { + this.notifyType = notifyType; + } + + public String getNotifyId() { + return notifyId; + } + + public void setNotifyId(String notifyId) { + this.notifyId = notifyId; + } + + public String getAppId() { + return appId; + } + + public void setAppId(String appId) { + this.appId = appId; + } + + public String getCharset() { + return charset; + } + + public void setCharset(String charset) { + this.charset = charset; + } + + public String getVersion() { + return version; + } + + public void setVersion(String version) { + this.version = version; + } + + public String getSignType() { + return signType; + } + + public void setSignType(String signType) { + this.signType = signType; + } + + @Override + public String getSign() { + return sign; + } + + public void setSign(String sign) { + this.sign = sign; + } + + public String getTradeNo() { + return tradeNo; + } + + public void setTradeNo(String tradeNo) { + this.tradeNo = tradeNo; + } + + @Override + public String getOutTradeNo() { + return outTradeNo; + } + + public void setOutTradeNo(String outTradeNo) { + this.outTradeNo = outTradeNo; + } + + public String getOutBizNo() { + return outBizNo; + } + + public void setOutBizNo(String outBizNo) { + this.outBizNo = outBizNo; + } + + public String getBuyerId() { + return buyerId; + } + + public void setBuyerId(String buyerId) { + this.buyerId = buyerId; + } + + public String getBuyerLogonId() { + return buyerLogonId; + } + + public void setBuyerLogonId(String buyerLogonId) { + this.buyerLogonId = buyerLogonId; + } + + public String getSellerId() { + return sellerId; + } + + public void setSellerId(String sellerId) { + this.sellerId = sellerId; + } + + public String getSellerEmail() { + return sellerEmail; + } + + public void setSellerEmail(String sellerEmail) { + this.sellerEmail = sellerEmail; + } + + public String getTradeStatus() { + return tradeStatus; + } + + public void setTradeStatus(String tradeStatus) { + this.tradeStatus = tradeStatus; + } + + public BigDecimal getTotalAmount() { + return totalAmount; + } + + public void setTotalAmount(BigDecimal totalAmount) { + this.totalAmount = totalAmount; + } + + public BigDecimal getReceiptAmount() { + return receiptAmount; + } + + public void setReceiptAmount(BigDecimal receiptAmount) { + this.receiptAmount = receiptAmount; + } + + public static final AliPayMessage create(Map message){ + AliPayMessage payMessage = new JSONObject(message).toJavaObject(AliPayMessage.class); + payMessage.setPayMessage(message); + return payMessage; + } + +} diff --git a/pay-java-union/src/main/java/com/egzosn/pay/union/api/UnionPayService.java b/pay-java-union/src/main/java/com/egzosn/pay/union/api/UnionPayService.java index cb867ab..1494be0 100644 --- a/pay-java-union/src/main/java/com/egzosn/pay/union/api/UnionPayService.java +++ b/pay-java-union/src/main/java/com/egzosn/pay/union/api/UnionPayService.java @@ -17,6 +17,7 @@ import com.egzosn.pay.common.util.sign.encrypt.RSA; import com.egzosn.pay.common.util.sign.encrypt.RSA2; import com.egzosn.pay.common.util.str.StringUtils; import com.egzosn.pay.union.bean.SDKConstants; +import com.egzosn.pay.union.bean.UnionPayMessage; import com.egzosn.pay.union.bean.UnionTransactionType; import java.awt.image.BufferedImage; @@ -688,6 +689,14 @@ public class UnionPayService extends BasePayService { } - - + /** + * 创建消息 + * + * @param message 支付平台返回的消息 + * @return 支付消息对象 + */ + @Override + public PayMessage createMessage(Map message) { + return UnionPayMessage.create(message); + } } diff --git a/pay-java-union/src/main/java/com/egzosn/pay/union/bean/UnionPayMessage.java b/pay-java-union/src/main/java/com/egzosn/pay/union/bean/UnionPayMessage.java new file mode 100644 index 0000000..0ef0d8d --- /dev/null +++ b/pay-java-union/src/main/java/com/egzosn/pay/union/bean/UnionPayMessage.java @@ -0,0 +1,354 @@ +package com.egzosn.pay.union.bean; + +import com.alibaba.fastjson.JSONObject; +import com.alibaba.fastjson.annotation.JSONField; +import com.egzosn.pay.common.bean.PayMessage; + +import java.math.BigDecimal; +import java.util.Map; + +/** + * 银联回调信息 + * + * @author egan + *

+ *                         email egzosn@gmail.com
+ *                         date 2019/7/3.22:07
+ *                 
+ */ + +public class UnionPayMessage extends PayMessage { +// 查询流水号 queryId AN20..21 M-必填 消费交易的流水号,供后续查询用 + private String queryId; + // 交易币种 currencyCode AN3 M-必填 默认为156 + private String currencyCode; + // 交易传输时间 traceTime MMDDhhmmss M-必填 + @JSONField(name = "traceTime", format = "MMDDhhmmss") + private String traceTime; + // 签名 signature ANS1..1024 M-必填 + private String signature; + // 签名方法 signMethod N2 M-必填 + private String signMethod; + // 清算币种 settleCurrencyCode AN3 M-必填 + private String settleCurrencyCode; + // 清算金额 settleAmt N1..12 M-必填 + private BigDecimal settleAmt; + // 清算日期 settleDate MMDD M-必填 + @JSONField(name = "settleDate", format = "MMDD") + private String settleDate; + // 系统跟踪号 traceNo N6 M-必填 + private String traceNo; + // 应答码 respCode AN2 M-必填 + private String respCode; + // 应答信息 respMsg ANS1..256 M-必填 + private String respMsg; + // 兑换日期 exchangeDate MMDD C-按条件必填 交易成功,交易币种和清算币种不一致的时候返回 + @JSONField(name = "exchangeDate", format = "MMDD") + private String exchangeDate; + // 签名公钥证书 signPubKeyCert AN1..2048 C-按条件必填 使用RSA签名方式时必选,此域填写银联签名公钥证书。 + private String signPubKeyCert; + // 清算汇率 exchangeRate N8 C-按条件必填 交易成功,交易币种和清算币种不一致的时候返回 + private String exchangeRate; + // 账号 accNo AN1..1024 C-按条件必填 根据商户配置返回 + private String accNo; + // 支付方式 payType N4 C-按条件必填 根据商户配置返回 + private String payType; + // 支付卡标识 payCardNo ANS1..19 C-按条件必填 移动支付交易时,根据商户配置返回 + private String payCardNo; + // 支付卡类型 payCardType N2 C-按条件必填 根据商户配置返回 + private String payCardType; + // 支付卡名称 payCardIssueName ANS1..64 C-按条件必填 移动支付交易时,根据商户配置返回 + private String payCardIssueName; + // 版本号 version NS5 R-需要返回 + private String version; + // 绑定标识号 bindId ANS1..128 R-需要返回 绑定支付时,根据商户配置返回 + private String bindId; + // 编码方式 encoding ANS1..20 R-需要返回 + private String encoding; + // 产品类型 bizType N6 R-需要返回 + private String bizType; + // 订单发送时间 txnTime YYYYMMDDhhmmss R-需要返回 + @JSONField(name = "txnTime", format = "YYYYMMDDhhmmss") + private String txnTime; + // 交易金额 txnAmt N1..12 R-需要返回 + private BigDecimal txnAmt; + // 交易类型 txnType N2 R-需要返回 + private String txnType; + // 交易子类 txnSubType N2 R-需要返回 + private String txnSubType; + // 接入类型 accessType N1 R-需要返回 0:商户直连接入 1:收单机构接入2:平台商户接入 + private String accessType; + // 请求方保留域 reqReserved ANS1..1024 R-需要返回 + private String reqReserved; + // 商户代码 merId AN15 R-需要返回 + private String merId; + // 商户订单号 orderId AN8..40 R-需要返回 商户订单号,不能含“-”或“_”; 商户自定义,同一交易日期内不可重复; 商户代码merId、商户订单号orderId、订单发送时间txnTime三要素唯一确定一笔交易。 保留域 reserved ANS1..2048O- 选填 查看详情 + private String orderId; + // 分账域 accSplitData ANS1..512O- 选填 查看详情 + private String accSplitData; + + public String getQueryId() { + return queryId; + } + + public void setQueryId(String queryId) { + this.queryId = queryId; + } + + public String getCurrencyCode() { + return currencyCode; + } + + public void setCurrencyCode(String currencyCode) { + this.currencyCode = currencyCode; + } + + public String getTraceTime() { + return traceTime; + } + + public void setTraceTime(String traceTime) { + this.traceTime = traceTime; + } + + public String getSignature() { + return signature; + } + + public void setSignature(String signature) { + this.signature = signature; + } + + public String getSignMethod() { + return signMethod; + } + + public void setSignMethod(String signMethod) { + this.signMethod = signMethod; + } + + public String getSettleCurrencyCode() { + return settleCurrencyCode; + } + + public void setSettleCurrencyCode(String settleCurrencyCode) { + this.settleCurrencyCode = settleCurrencyCode; + } + + public BigDecimal getSettleAmt() { + return settleAmt; + } + + public void setSettleAmt(BigDecimal settleAmt) { + this.settleAmt = settleAmt; + } + + public String getSettleDate() { + return settleDate; + } + + public void setSettleDate(String settleDate) { + this.settleDate = settleDate; + } + + public String getTraceNo() { + return traceNo; + } + + public void setTraceNo(String traceNo) { + this.traceNo = traceNo; + } + + public String getRespCode() { + return respCode; + } + + public void setRespCode(String respCode) { + this.respCode = respCode; + } + + public String getRespMsg() { + return respMsg; + } + + public void setRespMsg(String respMsg) { + this.respMsg = respMsg; + } + + public String getExchangeDate() { + return exchangeDate; + } + + public void setExchangeDate(String exchangeDate) { + this.exchangeDate = exchangeDate; + } + + public String getSignPubKeyCert() { + return signPubKeyCert; + } + + public void setSignPubKeyCert(String signPubKeyCert) { + this.signPubKeyCert = signPubKeyCert; + } + + public String getExchangeRate() { + return exchangeRate; + } + + public void setExchangeRate(String exchangeRate) { + this.exchangeRate = exchangeRate; + } + + public String getAccNo() { + return accNo; + } + + public void setAccNo(String accNo) { + this.accNo = accNo; + } + + @Override + public String getPayType() { + return payType; + } + + @Override + public void setPayType(String payType) { + this.payType = payType; + } + + public String getPayCardNo() { + return payCardNo; + } + + public void setPayCardNo(String payCardNo) { + this.payCardNo = payCardNo; + } + + public String getPayCardType() { + return payCardType; + } + + public void setPayCardType(String payCardType) { + this.payCardType = payCardType; + } + + public String getPayCardIssueName() { + return payCardIssueName; + } + + public void setPayCardIssueName(String payCardIssueName) { + this.payCardIssueName = payCardIssueName; + } + + public String getVersion() { + return version; + } + + public void setVersion(String version) { + this.version = version; + } + + public String getBindId() { + return bindId; + } + + public void setBindId(String bindId) { + this.bindId = bindId; + } + + public String getEncoding() { + return encoding; + } + + public void setEncoding(String encoding) { + this.encoding = encoding; + } + + public String getBizType() { + return bizType; + } + + public void setBizType(String bizType) { + this.bizType = bizType; + } + + public String getTxnTime() { + return txnTime; + } + + public void setTxnTime(String txnTime) { + this.txnTime = txnTime; + } + + public BigDecimal getTxnAmt() { + return txnAmt; + } + + public void setTxnAmt(BigDecimal txnAmt) { + this.txnAmt = txnAmt; + } + + public String getTxnType() { + return txnType; + } + + public void setTxnType(String txnType) { + this.txnType = txnType; + } + + public String getTxnSubType() { + return txnSubType; + } + + public void setTxnSubType(String txnSubType) { + this.txnSubType = txnSubType; + } + + public String getAccessType() { + return accessType; + } + + public void setAccessType(String accessType) { + this.accessType = accessType; + } + + public String getReqReserved() { + return reqReserved; + } + + public void setReqReserved(String reqReserved) { + this.reqReserved = reqReserved; + } + + public String getMerId() { + return merId; + } + + public void setMerId(String merId) { + this.merId = merId; + } + + public String getOrderId() { + return orderId; + } + + public void setOrderId(String orderId) { + this.orderId = orderId; + } + + public String getAccSplitData() { + return accSplitData; + } + + public void setAccSplitData(String accSplitData) { + this.accSplitData = accSplitData; + } + + public static final UnionPayMessage create(Map message) { + UnionPayMessage payMessage = new JSONObject(message).toJavaObject(UnionPayMessage.class); + payMessage.setPayMessage(message); + return payMessage; + } + +} 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 56d92d1..76898c3 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 @@ -11,6 +11,7 @@ import com.egzosn.pay.common.util.MatrixToImageWriter; import com.egzosn.pay.common.util.Util; import com.egzosn.pay.common.util.sign.SignUtils; import com.egzosn.pay.common.util.str.StringUtils; +import com.egzosn.pay.wx.youdian.bean.WxYoudianPayMessage; import com.egzosn.pay.wx.youdian.bean.YdPayError; import com.egzosn.pay.wx.youdian.bean.YoudianTransactionType; import java.awt.image.BufferedImage; @@ -463,4 +464,15 @@ public class WxYouDianPayService extends BasePayService message) { + return WxYoudianPayMessage.create(message); + } } diff --git a/pay-java-wx-youdian/src/main/java/com/egzosn/pay/wx/youdian/bean/WxYoudianPayMessage.java b/pay-java-wx-youdian/src/main/java/com/egzosn/pay/wx/youdian/bean/WxYoudianPayMessage.java new file mode 100644 index 0000000..c909f4d --- /dev/null +++ b/pay-java-wx-youdian/src/main/java/com/egzosn/pay/wx/youdian/bean/WxYoudianPayMessage.java @@ -0,0 +1,327 @@ +package com.egzosn.pay.wx.youdian.bean; + +import com.alibaba.fastjson.JSONObject; +import com.alibaba.fastjson.annotation.JSONField; +import com.egzosn.pay.common.bean.PayMessage; + +import java.math.BigDecimal; +import java.util.Date; +import java.util.Map; + +/** + * 友店回调信息 + * + * @author egan + * email egzosn@gmail.com + * date 2019/7/3.20:25 + */ + +public class WxYoudianPayMessage extends PayMessage { + // 公众账号ID appid 是 String(32) wx8888888888888888 微信分配的公众账号ID(企业号corpid即为此appId) + @JSONField(name = "appid") + private String appid; + // 商户号 mch_id 是 String(32) 1900000109 微信支付分配的商户号 + @JSONField(name = "mch_id") + private String mchId; + // 设备号 device_info 否 String(32) 013467007045764 微信支付分配的终端设备号, + @JSONField(name = "device_info") + private String deviceInfo; + // 随机字符串 nonce_str 是 String(32) 5K8264ILTKCH16CQ2502SI8ZNMTM67VS 随机字符串,不长于32位 + @JSONField(name = "nonce_str") + private String nonceStr; + // 签名 sign 是 String(32) C380BEC2BFD727A4B6845133519F3AD6 签名,详见签名算法 + @JSONField(name = "sign") + private String sign; + // 签名类型 sign_type 否 String(32) HMAC-SHA256 签名类型,目前支持HMAC-SHA256和MD5,默认为MD5 + @JSONField(name = "sign_type") + private String signType; + // 业务结果 result_code 是 String(16) SUCCESS SUCCESS/FAIL + @JSONField(name = "result_code") + private String resultCode; + // 错误代码 err_code 否 String(32) SYSTEMERROR 错误返回的信息描述 + @JSONField(name = "err_code") + private String errCode; + // 错误代码描述 err_code_des 否 String(128) 系统错误 错误返回的信息描述 + @JSONField(name = "err_code_des") + private String errCodeDes; + // 用户标识 openid 是 String(128) wxd930ea5d5a258f4f 用户在商户appid下的唯一标识 + @JSONField(name = "openid") + private String openid; + // 是否关注公众账号 is_subscribe 是 String(1) Y 用户是否关注公众账号,Y-关注,N-未关注 + @JSONField(name = "is_subscribe") + private String isSubscribe; + // 交易类型 trade_type 是 String(16) JSAPI JSAPI、NATIVE、APP + @JSONField(name = "trade_type") + private String tradeType; + // 付款银行 bank_type 是 String(16) CMC 银行类型,采用字符串类型的银行标识,银行类型见银行列表 + @JSONField(name = "bank_type") + private String bankType; + // 订单金额 total_fee 是 Int 100 订单总金额,单位为分 + @JSONField(name = "total_fee") + private BigDecimal totalFee; + // 应结订单金额 settlement_total_fee 否 Int 100 应结订单金额=订单金额-非充值代金券金额,应结订单金额<=订单金额。 + @JSONField(name = "settlement_total_fee") + private BigDecimal settlementTotalFee; + // 货币种类 fee_type 否 String(8) CNY 货币类型,符合ISO4217标准的三位字母代码,默认人民币:CNY,其他值列表详见货币类型 + @JSONField(name = "fee_type") + private String feeType; + // 现金支付金额 cash_fee 是 Int 100 现金支付金额订单现金支付金额,详见支付金额 + @JSONField(name = "cash_fee") + private BigDecimal cashFee; + // 现金支付货币类型 cash_fee_type 否 String(16) CNY 货币类型,符合ISO4217标准的三位字母代码,默认人民币:CNY,其他值列表详见货币类型 + @JSONField(name = "cash_fee_type") + private String cashFeeType; + // 总代金券金额 coupon_fee 否 Int 10 代金券金额<=订单金额,订单金额-代金券金额=现金支付金额,详见支付金额 + @JSONField(name = "coupon_fee") + private BigDecimal couponFee; + // 代金券使用数量 coupon_count 否 Int 1 代金券使用数量 + @JSONField(name = "coupon_count") + private Integer couponCount; + // 代金券类型 coupon_type_$n 否 String CASH CASH--充值代金券 NO_CASH---非充值代金券 并且订单使用了免充值券后有返回(取值:CASH、NO_CASH)。$n为下标,从0开始编号,举例:coupon_type_0 + @JSONField(name = "coupon_type_$0") + private String couponType0; + // 代金券ID coupon_id_$n 否 String(20) 10000 代金券ID,$n为下标,从0开始编号 + @JSONField(name = "coupon_id_$0") + private String couponId0; + // 单个代金券支付金额 coupon_fee_$n 否 Int 100 单个代金券支付金额,$n为下标,从0开始编号 + @JSONField(name = "coupon_fee_$0") + private Integer couponFee0; + // 微信支付订单号 transaction_id 是 String(32) 1217752501201407033233368018 微信支付订单号 + @JSONField(name = "transaction_id") + private String transactionId; + // 商户订单号 out_trade_no 是 String(32) 1212321211201407033568112322 商户系统内部订单号,要求32个字符内,只能是数字、大小写字母_-|*@ ,且在同一个商户号下唯一。 + @JSONField(name = "out_trade_no") + private String outTradeNo; + // 商家数据包 attach 否 String(128) 123456 商家数据包,原样返回 + @JSONField(name = "attach") + private String attach; + // 支付完成时间 time_end 是 String(14) 20141030133525 支付完成时间,格式为yyyyMMddHHmmss,如2009年12月25日9点10分10秒表示为20091225091010。其他详见时间规则 + @JSONField(name = "time_end", format="yyyyMMddHHmmss") + private Date timeEnd; + + public String getAppid() { + return appid; + } + + public void setAppid(String appid) { + this.appid = appid; + } + + public String getMchId() { + return mchId; + } + + public void setMchId(String mchId) { + this.mchId = mchId; + } + + public String getDeviceInfo() { + return deviceInfo; + } + + public void setDeviceInfo(String deviceInfo) { + this.deviceInfo = deviceInfo; + } + + public String getNonceStr() { + return nonceStr; + } + + public void setNonceStr(String nonceStr) { + this.nonceStr = nonceStr; + } + + @Override + public String getSign() { + return sign; + } + + public void setSign(String sign) { + this.sign = sign; + } + + public String getSignType() { + return signType; + } + + public void setSignType(String signType) { + this.signType = signType; + } + + public String getResultCode() { + return resultCode; + } + + public void setResultCode(String resultCode) { + this.resultCode = resultCode; + } + + public String getErrCode() { + return errCode; + } + + public void setErrCode(String errCode) { + this.errCode = errCode; + } + + public String getErrCodeDes() { + return errCodeDes; + } + + public void setErrCodeDes(String errCodeDes) { + this.errCodeDes = errCodeDes; + } + + public String getOpenid() { + return openid; + } + + public void setOpenid(String openid) { + this.openid = openid; + } + + public String getIsSubscribe() { + return isSubscribe; + } + + public void setIsSubscribe(String isSubscribe) { + this.isSubscribe = isSubscribe; + } + + public String getTradeType() { + return tradeType; + } + + public void setTradeType(String tradeType) { + this.tradeType = tradeType; + } + + public String getBankType() { + return bankType; + } + + public void setBankType(String bankType) { + this.bankType = bankType; + } + + @Override + public BigDecimal getTotalFee() { + return totalFee; + } + + public void setTotalFee(BigDecimal totalFee) { + this.totalFee = totalFee; + } + + public BigDecimal getSettlementTotalFee() { + return settlementTotalFee; + } + + public void setSettlementTotalFee(BigDecimal settlementTotalFee) { + this.settlementTotalFee = settlementTotalFee; + } + + public String getFeeType() { + return feeType; + } + + public void setFeeType(String feeType) { + this.feeType = feeType; + } + + public BigDecimal getCashFee() { + return cashFee; + } + + public void setCashFee(BigDecimal cashFee) { + this.cashFee = cashFee; + } + + public String getCashFeeType() { + return cashFeeType; + } + + public void setCashFeeType(String cashFeeType) { + this.cashFeeType = cashFeeType; + } + + public BigDecimal getCouponFee() { + return couponFee; + } + + public void setCouponFee(BigDecimal couponFee) { + this.couponFee = couponFee; + } + + public Integer getCouponCount() { + return couponCount; + } + + public void setCouponCount(Integer couponCount) { + this.couponCount = couponCount; + } + + public String getCouponType0() { + return couponType0; + } + + public void setCouponType0(String couponType0) { + this.couponType0 = couponType0; + } + + public String getCouponId0() { + return couponId0; + } + + public void setCouponId0(String couponId0) { + this.couponId0 = couponId0; + } + + public Integer getCouponFee0() { + return couponFee0; + } + + public void setCouponFee0(Integer couponFee0) { + this.couponFee0 = couponFee0; + } + + public String getTransactionId() { + return transactionId; + } + + public void setTransactionId(String transactionId) { + this.transactionId = transactionId; + } + + @Override + public String getOutTradeNo() { + return outTradeNo; + } + + public void setOutTradeNo(String outTradeNo) { + this.outTradeNo = outTradeNo; + } + + public String getAttach() { + return attach; + } + + public void setAttach(String attach) { + this.attach = attach; + } + + public Date getTimeEnd() { + return timeEnd; + } + + public void setTimeEnd(Date timeEnd) { + this.timeEnd = timeEnd; + } + + public static final WxYoudianPayMessage create(Map message) { + WxYoudianPayMessage payMessage = new JSONObject(message).toJavaObject(WxYoudianPayMessage.class); + payMessage.setPayMessage(message); + return payMessage; + } + +} 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 ec8f5a6..87d5445 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 @@ -14,6 +14,7 @@ import com.egzosn.pay.common.util.sign.SignUtils; import com.egzosn.pay.common.util.sign.encrypt.RSA2; import com.egzosn.pay.common.util.str.StringUtils; import com.egzosn.pay.wx.bean.WxPayError; +import com.egzosn.pay.wx.bean.WxPayMessage; import com.egzosn.pay.wx.bean.WxTransactionType; import com.egzosn.pay.common.util.XML; import com.egzosn.pay.wx.bean.WxTransferType; @@ -753,5 +754,14 @@ public class WxPayService extends BasePayService { } } - + /** + * 创建消息 + * + * @param message 支付平台返回的消息 + * @return 支付消息对象 + */ + @Override + public PayMessage createMessage(Map message) { + return WxPayMessage.create(message); + } } diff --git a/pay-java-wx/src/main/java/com/egzosn/pay/wx/bean/WxPayMessage.java b/pay-java-wx/src/main/java/com/egzosn/pay/wx/bean/WxPayMessage.java new file mode 100644 index 0000000..3c7e5b6 --- /dev/null +++ b/pay-java-wx/src/main/java/com/egzosn/pay/wx/bean/WxPayMessage.java @@ -0,0 +1,327 @@ +package com.egzosn.pay.wx.bean; + +import com.alibaba.fastjson.JSONObject; +import com.alibaba.fastjson.annotation.JSONField; +import com.egzosn.pay.common.bean.PayMessage; + +import java.math.BigDecimal; +import java.util.Date; +import java.util.Map; + +/** + * 微信回调信息 + * + * @author egan + * email egzosn@gmail.com + * date 2019/7/3.20:25 + */ + +public class WxPayMessage extends PayMessage { + // 公众账号ID appid 是 String(32) wx8888888888888888 微信分配的公众账号ID(企业号corpid即为此appId) + @JSONField(name = "appid") + private String appid; + // 商户号 mch_id 是 String(32) 1900000109 微信支付分配的商户号 + @JSONField(name = "mch_id") + private String mchId; + // 设备号 device_info 否 String(32) 013467007045764 微信支付分配的终端设备号, + @JSONField(name = "device_info") + private String deviceInfo; + // 随机字符串 nonce_str 是 String(32) 5K8264ILTKCH16CQ2502SI8ZNMTM67VS 随机字符串,不长于32位 + @JSONField(name = "nonce_str") + private String nonceStr; + // 签名 sign 是 String(32) C380BEC2BFD727A4B6845133519F3AD6 签名,详见签名算法 + @JSONField(name = "sign") + private String sign; + // 签名类型 sign_type 否 String(32) HMAC-SHA256 签名类型,目前支持HMAC-SHA256和MD5,默认为MD5 + @JSONField(name = "sign_type") + private String signType; + // 业务结果 result_code 是 String(16) SUCCESS SUCCESS/FAIL + @JSONField(name = "result_code") + private String resultCode; + // 错误代码 err_code 否 String(32) SYSTEMERROR 错误返回的信息描述 + @JSONField(name = "err_code") + private String errCode; + // 错误代码描述 err_code_des 否 String(128) 系统错误 错误返回的信息描述 + @JSONField(name = "err_code_des") + private String errCodeDes; + // 用户标识 openid 是 String(128) wxd930ea5d5a258f4f 用户在商户appid下的唯一标识 + @JSONField(name = "openid") + private String openid; + // 是否关注公众账号 is_subscribe 是 String(1) Y 用户是否关注公众账号,Y-关注,N-未关注 + @JSONField(name = "is_subscribe") + private String isSubscribe; + // 交易类型 trade_type 是 String(16) JSAPI JSAPI、NATIVE、APP + @JSONField(name = "trade_type") + private String tradeType; + // 付款银行 bank_type 是 String(16) CMC 银行类型,采用字符串类型的银行标识,银行类型见银行列表 + @JSONField(name = "bank_type") + private String bankType; + // 订单金额 total_fee 是 Int 100 订单总金额,单位为分 + @JSONField(name = "total_fee") + private BigDecimal totalFee; + // 应结订单金额 settlement_total_fee 否 Int 100 应结订单金额=订单金额-非充值代金券金额,应结订单金额<=订单金额。 + @JSONField(name = "settlement_total_fee") + private BigDecimal settlementTotalFee; + // 货币种类 fee_type 否 String(8) CNY 货币类型,符合ISO4217标准的三位字母代码,默认人民币:CNY,其他值列表详见货币类型 + @JSONField(name = "fee_type") + private String feeType; + // 现金支付金额 cash_fee 是 Int 100 现金支付金额订单现金支付金额,详见支付金额 + @JSONField(name = "cash_fee") + private BigDecimal cashFee; + // 现金支付货币类型 cash_fee_type 否 String(16) CNY 货币类型,符合ISO4217标准的三位字母代码,默认人民币:CNY,其他值列表详见货币类型 + @JSONField(name = "cash_fee_type") + private String cashFeeType; + // 总代金券金额 coupon_fee 否 Int 10 代金券金额<=订单金额,订单金额-代金券金额=现金支付金额,详见支付金额 + @JSONField(name = "coupon_fee") + private BigDecimal couponFee; + // 代金券使用数量 coupon_count 否 Int 1 代金券使用数量 + @JSONField(name = "coupon_count") + private Integer couponCount; + // 代金券类型 coupon_type_$n 否 String CASH CASH--充值代金券 NO_CASH---非充值代金券 并且订单使用了免充值券后有返回(取值:CASH、NO_CASH)。$n为下标,从0开始编号,举例:coupon_type_0 + @JSONField(name = "coupon_type_$0") + private String couponType0; + // 代金券ID coupon_id_$n 否 String(20) 10000 代金券ID,$n为下标,从0开始编号 + @JSONField(name = "coupon_id_$0") + private String couponId0; + // 单个代金券支付金额 coupon_fee_$n 否 Int 100 单个代金券支付金额,$n为下标,从0开始编号 + @JSONField(name = "coupon_fee_$0") + private Integer couponFee0; + // 微信支付订单号 transaction_id 是 String(32) 1217752501201407033233368018 微信支付订单号 + @JSONField(name = "transaction_id") + private String transactionId; + // 商户订单号 out_trade_no 是 String(32) 1212321211201407033568112322 商户系统内部订单号,要求32个字符内,只能是数字、大小写字母_-|*@ ,且在同一个商户号下唯一。 + @JSONField(name = "out_trade_no") + private String outTradeNo; + // 商家数据包 attach 否 String(128) 123456 商家数据包,原样返回 + @JSONField(name = "attach") + private String attach; + // 支付完成时间 time_end 是 String(14) 20141030133525 支付完成时间,格式为yyyyMMddHHmmss,如2009年12月25日9点10分10秒表示为20091225091010。其他详见时间规则 + @JSONField(name = "time_end", format="yyyyMMddHHmmss") + private Date timeEnd; + + public String getAppid() { + return appid; + } + + public void setAppid(String appid) { + this.appid = appid; + } + + public String getMchId() { + return mchId; + } + + public void setMchId(String mchId) { + this.mchId = mchId; + } + + public String getDeviceInfo() { + return deviceInfo; + } + + public void setDeviceInfo(String deviceInfo) { + this.deviceInfo = deviceInfo; + } + + public String getNonceStr() { + return nonceStr; + } + + public void setNonceStr(String nonceStr) { + this.nonceStr = nonceStr; + } + + @Override + public String getSign() { + return sign; + } + + public void setSign(String sign) { + this.sign = sign; + } + + public String getSignType() { + return signType; + } + + public void setSignType(String signType) { + this.signType = signType; + } + + public String getResultCode() { + return resultCode; + } + + public void setResultCode(String resultCode) { + this.resultCode = resultCode; + } + + public String getErrCode() { + return errCode; + } + + public void setErrCode(String errCode) { + this.errCode = errCode; + } + + public String getErrCodeDes() { + return errCodeDes; + } + + public void setErrCodeDes(String errCodeDes) { + this.errCodeDes = errCodeDes; + } + + public String getOpenid() { + return openid; + } + + public void setOpenid(String openid) { + this.openid = openid; + } + + public String getIsSubscribe() { + return isSubscribe; + } + + public void setIsSubscribe(String isSubscribe) { + this.isSubscribe = isSubscribe; + } + + public String getTradeType() { + return tradeType; + } + + public void setTradeType(String tradeType) { + this.tradeType = tradeType; + } + + public String getBankType() { + return bankType; + } + + public void setBankType(String bankType) { + this.bankType = bankType; + } + + @Override + public BigDecimal getTotalFee() { + return totalFee; + } + + public void setTotalFee(BigDecimal totalFee) { + this.totalFee = totalFee; + } + + public BigDecimal getSettlementTotalFee() { + return settlementTotalFee; + } + + public void setSettlementTotalFee(BigDecimal settlementTotalFee) { + this.settlementTotalFee = settlementTotalFee; + } + + public String getFeeType() { + return feeType; + } + + public void setFeeType(String feeType) { + this.feeType = feeType; + } + + public BigDecimal getCashFee() { + return cashFee; + } + + public void setCashFee(BigDecimal cashFee) { + this.cashFee = cashFee; + } + + public String getCashFeeType() { + return cashFeeType; + } + + public void setCashFeeType(String cashFeeType) { + this.cashFeeType = cashFeeType; + } + + public BigDecimal getCouponFee() { + return couponFee; + } + + public void setCouponFee(BigDecimal couponFee) { + this.couponFee = couponFee; + } + + public Integer getCouponCount() { + return couponCount; + } + + public void setCouponCount(Integer couponCount) { + this.couponCount = couponCount; + } + + public String getCouponType0() { + return couponType0; + } + + public void setCouponType0(String couponType0) { + this.couponType0 = couponType0; + } + + public String getCouponId0() { + return couponId0; + } + + public void setCouponId0(String couponId0) { + this.couponId0 = couponId0; + } + + public Integer getCouponFee0() { + return couponFee0; + } + + public void setCouponFee0(Integer couponFee0) { + this.couponFee0 = couponFee0; + } + + public String getTransactionId() { + return transactionId; + } + + public void setTransactionId(String transactionId) { + this.transactionId = transactionId; + } + + @Override + public String getOutTradeNo() { + return outTradeNo; + } + + public void setOutTradeNo(String outTradeNo) { + this.outTradeNo = outTradeNo; + } + + public String getAttach() { + return attach; + } + + public void setAttach(String attach) { + this.attach = attach; + } + + public Date getTimeEnd() { + return timeEnd; + } + + public void setTimeEnd(Date timeEnd) { + this.timeEnd = timeEnd; + } + + public static final WxPayMessage create(Map message) { + WxPayMessage payMessage = new JSONObject(message).toJavaObject(WxPayMessage.class); + payMessage.setPayMessage(message); + return payMessage; + } + +} -- Gitee From 1c99de3e06ad522978e260f7311e6e16459ce0c7 Mon Sep 17 00:00:00 2001 From: egan Date: Wed, 3 Jul 2019 22:42:39 +0800 Subject: [PATCH 041/299] =?UTF-8?q?=E6=94=AF=E4=BB=98=E5=9B=9E=E8=B0=83?= =?UTF-8?q?=E6=B6=88=E6=81=AF=E7=9B=B8=E5=85=B3=E6=A1=88=E4=BE=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pay-java-demo/pom.xml | 8 +------ .../pay/demo/controller/PayController.java | 15 +++++++++++-- .../pay/demo/dao/ApyAccountRepository.java | 2 +- .../service/handler/AliPayMessageHandler.java | 22 +++++++++++++------ .../handler/BasePayMessageHandler.java | 4 +++- .../handler/FuiouPayMessageHandler.java | 2 +- .../handler/PayPalPayMessageHandler.java | 5 +++-- .../handler/PayoneerMessageHandler.java | 2 +- .../handler/UnionPayMessageHandler.java | 8 +++---- .../service/handler/WxPayMessageHandler.java | 6 ++--- .../handler/YouDianPayMessageHandler.java | 6 ++--- .../interceptor/AliPayMessageInterceptor.java | 6 +++-- .../YoudianPayMessageInterceptor.java | 5 +++-- 13 files changed, 55 insertions(+), 36 deletions(-) diff --git a/pay-java-demo/pom.xml b/pay-java-demo/pom.xml index 1dc62bc..23c7ad4 100644 --- a/pay-java-demo/pom.xml +++ b/pay-java-demo/pom.xml @@ -80,13 +80,7 @@ jar compile - - org.springframework - spring-web - ${spring.version} - jar - compile - + 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 f3d7d44..54974a3 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 @@ -323,8 +323,19 @@ public class PayController { //校验 if (payResponse.getService().verify(params)) { - PayMessage message = new PayMessage(params, storage.getPayType(), storage.getMsgType().name()); - PayOutMessage outMessage = payResponse.getRouter().route(message); + //方式一 或者创建PayMessage的子类,AliPayMessage,WxPayMessage等等 + /* PayMessage message = new PayMessage(params, storage.getPayType(), storage.getMsgType().name()); + PayOutMessage outMessage = payResponse.getRouter().route(message);*/ + + //方式二 + /*PayMessage message = payResponse.getService().createMessage(params); + message.setPayType(storage.getPayType()); + message.setMsgType(storage.getMsgType().name()); + PayOutMessage outMessage = payResponse.getRouter().route(message); + */ + //方式三 + PayOutMessage outMessage = payResponse.getRouter().route(params, storage); + return outMessage.toMessage(); } 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 e2176b5..04aef65 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 @@ -33,7 +33,7 @@ public class ApyAccountRepository { apyAccount1.setAppid("2016080400165436"); // TODO 2017/2/9 16:20 author: egan sign_type只有单一key时public_key与private_key相等,比如sign_type=MD5的情况 apyAccount1.setPublicKey("MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDIgHnOn7LLILlKETd6BFRJ0GqgS2Y3mn1wMQmyh9zEyWlz5p1zrahRahbXAfCfSqshSNfqOmAQzSHRVjCqjsAw1jyqrXaPdKBmr90DIpIxmIyKXv4GGAkPyJ/6FTFY99uhpiq0qadD/uSzQsefWo0aTvP/65zi3eof7TcZ32oWpwIDAQAB"); - apyAccount1.setPrivateKey("MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBAKroe/8h5vC4L6T+B2WdXiVwGsMvUKgb2XsKix6VY3m2wcf6tyzpNRDCNykbIwGtaeo7FshN+qZxdXHLiIam9goYncBit/8ojfLGy2gLxO/PXfzGxYGs0KsDZ+ryVPPmE34ZZ8jiJpR0ygzCFl8pN3QJPJRGTJn5+FTT9EF/9zyZAgMBAAECgYAktngcYC35u7cQXDk+jMVyiVhWYU2ULxdSpPspgLGzrZyG1saOcTIi/XVX8Spd6+B6nmLQeF/FbU3rOeuD8U2clzul2Z2YMbJ0FYay9oVZFfp5gTEFpFRTVfzqUaZQBIjJe/xHL9kQVqc5xHlE/LVA27/Kx3dbC35Y7B4EVBDYAQJBAOhsX8ZreWLKPhXiXHTyLmNKhOHJc+0tFH7Ktise/0rNspojU7o9prOatKpNylp9v6kux7migcMRdVUWWiVe+4ECQQC8PqsuEz7B0yqirQchRg1DbHjh64bw9Kj82EN1/NzOUd53tP9tg+SO97EzsibK1F7tOcuwqsa7n2aY48mQ+y0ZAkBndA2xcRcnvOOjtAz5VO8G7R12rse181HjGfG6AeMadbKg30aeaGCyIxN1loiSfNR5xsPJwibGIBg81mUrqzqBAkB+K6rkaPXJR9XtzvdWb/N3235yPkDlw7Z4MiOVM3RzvR/VMDV7m8lXoeDde2zQyeMOMYy6ztwA6WgE1bhGOnQRAkEAouUBv1sVdSBlsexX15qphOmAevzYrpufKgJIRLFWQxroXMS7FTesj+f+FmGrpPCxIde1dqJ8lqYLTyJmbzMPYw==\n"); + apyAccount1.setPrivateKey("MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBAKroe/8h5vC4L6T+B2WdXiVwGsMvUKgb2XsKix6VY3m2wcf6tyzpNRDCNykbIwGtaeo7FshN+qZxdXHLiIam9goYncBit/8ojfLGy2gLxO/PXfzGxYGs0KsDZ+ryVPPmE34ZZ8jiJpR0ygzCFl8pN3QJPJRGTJn5+FTT9EF/9zyZAgMBAAECgYAktngcYC35u7cQXDk+jMVyiVhWYU2ULxdSpPspgLGzrZyG1saOcTIi/XVX8Spd6+B6nmLQeF/FbU3rOeuD8U2clzul2Z2YMbJ0FYay9oVZFfp5gTEFpFRTVfzqUaZQBIjJe/xHL9kQVqc5xHlE/LVA27/Kx3dbC35Y7B4EVBDYAQJBAOhsX8ZreWLKPhXiXHTyLmNKhOHJc+0tFH7Ktise/0rNspojU7o9prOatKpNylp9v6kux7migcMRdVUWWiVe+4ECQQC8PqsuEz7B0yqirQchRg1DbHjh64bw9Kj82EN1/NzOUd53tP9tg+SO97EzsibK1F7tOcuwqsa7n2aY48mQ+y0ZAkBndA2xcRcnvOOjtAz5VO8G7R12rse181HjGfG6AeMadbKg30aeaGCyIxN1loiSfNR5xsPJwibGIBg81mUrqzqBAkB+K6rkaPXJR9XtzvdWb/N3235yPkDlw7Z4MiOVM3RzvR/VMDV7m8lXoeDde2zQyeMOMYy6ztwA6WgE1bhGOnQRAkEAouUBv1sVdSBlsexX15qphOmAevzYrpufKgJIRLFWQxroXMS7FTesj+f+FmGrpPCxIde1dqJ8lqYLTyJmbzMPYw=="); apyAccount1.setNotifyUrl("http://pay.egzosn.com/payBack1.json"); // 无需同步回调可不填 apyAccount1.setReturnUrl("http://pay.egzosn.com/payBack1.json"); 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 8ad88ad..b994349 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 @@ -1,5 +1,7 @@ package com.egzosn.pay.demo.service.handler; +import com.egzosn.pay.ali.api.AliPayService; +import com.egzosn.pay.ali.bean.AliPayMessage; import com.egzosn.pay.common.api.PayMessageHandler; import com.egzosn.pay.common.api.PayService; import com.egzosn.pay.common.bean.PayMessage; @@ -17,14 +19,20 @@ import java.util.Map; * */ @Component -public class AliPayMessageHandler implements PayMessageHandler { - - - - - +public class AliPayMessageHandler implements PayMessageHandler { + + + /** + * 处理支付回调消息的处理器接口 + * + * @param payMessage 支付消息 + * @param context 上下文,如果handler或interceptor之间有信息要传递,可以用这个 + * @param payService 支付服务 + * @return xml, text格式的消息,如果在异步规则里处理的话,可以返回null + * @throws PayErrorException 支付错误异常 + */ @Override - public PayOutMessage handle(PayMessage payMessage, Map context, PayService payService) throws PayErrorException { + public PayOutMessage handle(AliPayMessage payMessage, Map context, AliPayService payService) throws PayErrorException { //com.egzosn.pay.demo.entity.PayType.getPayService()#48 Object payId = payService.getPayConfigStorage().getAttach(); diff --git a/pay-java-demo/src/main/java/com/egzosn/pay/demo/service/handler/BasePayMessageHandler.java b/pay-java-demo/src/main/java/com/egzosn/pay/demo/service/handler/BasePayMessageHandler.java index a55947e..bcaad7b 100644 --- a/pay-java-demo/src/main/java/com/egzosn/pay/demo/service/handler/BasePayMessageHandler.java +++ b/pay-java-demo/src/main/java/com/egzosn/pay/demo/service/handler/BasePayMessageHandler.java @@ -1,12 +1,14 @@ package com.egzosn.pay.demo.service.handler; import com.egzosn.pay.common.api.PayMessageHandler; +import com.egzosn.pay.common.api.PayService; +import com.egzosn.pay.common.bean.PayMessage; /** * * Created by ZaoSheng on 2016/6/1. */ -public abstract class BasePayMessageHandler implements PayMessageHandler { +public abstract class BasePayMessageHandler implements PayMessageHandler { //支付账户id private Integer payId; diff --git a/pay-java-demo/src/main/java/com/egzosn/pay/demo/service/handler/FuiouPayMessageHandler.java b/pay-java-demo/src/main/java/com/egzosn/pay/demo/service/handler/FuiouPayMessageHandler.java index 3513c41..eae307f 100644 --- a/pay-java-demo/src/main/java/com/egzosn/pay/demo/service/handler/FuiouPayMessageHandler.java +++ b/pay-java-demo/src/main/java/com/egzosn/pay/demo/service/handler/FuiouPayMessageHandler.java @@ -21,7 +21,7 @@ public class FuiouPayMessageHandler extends BasePayMessageHandler { } @Override - public PayOutMessage handle(PayMessage payMessage, Map context, PayService payService) throws PayErrorException { + public PayOutMessage handle(PayMessage payMessage, Map context, PayService payService) throws PayErrorException { //交易状态 if ("0000".equals(payMessage.getPayMessage().get("order_pay_code"))){ /////这里进行成功的处理 diff --git a/pay-java-demo/src/main/java/com/egzosn/pay/demo/service/handler/PayPalPayMessageHandler.java b/pay-java-demo/src/main/java/com/egzosn/pay/demo/service/handler/PayPalPayMessageHandler.java index 9067bc5..912df2e 100644 --- a/pay-java-demo/src/main/java/com/egzosn/pay/demo/service/handler/PayPalPayMessageHandler.java +++ b/pay-java-demo/src/main/java/com/egzosn/pay/demo/service/handler/PayPalPayMessageHandler.java @@ -5,6 +5,7 @@ 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.exception.PayErrorException; +import com.egzosn.pay.paypal.api.PayPalPayService; import org.springframework.stereotype.Component; import java.math.BigDecimal; @@ -16,14 +17,14 @@ import java.util.Map; * */ @Component -public class PayPalPayMessageHandler implements PayMessageHandler { +public class PayPalPayMessageHandler implements PayMessageHandler { @Override - public PayOutMessage handle(PayMessage payMessage, Map context, PayService payService) throws PayErrorException { + public PayOutMessage handle(PayMessage payMessage, Map context, PayPalPayService payService) throws PayErrorException { return payService.getPayOutMessage("fail", "失败"); diff --git a/pay-java-demo/src/main/java/com/egzosn/pay/demo/service/handler/PayoneerMessageHandler.java b/pay-java-demo/src/main/java/com/egzosn/pay/demo/service/handler/PayoneerMessageHandler.java index f788387..49a7afb 100644 --- a/pay-java-demo/src/main/java/com/egzosn/pay/demo/service/handler/PayoneerMessageHandler.java +++ b/pay-java-demo/src/main/java/com/egzosn/pay/demo/service/handler/PayoneerMessageHandler.java @@ -22,7 +22,7 @@ public class PayoneerMessageHandler extends BasePayMessageHandler { } @Override - public PayOutMessage handle(PayMessage payMessage, Map context, PayService payService) throws PayErrorException { + public PayOutMessage handle(PayMessage payMessage, Map context, PayService payService) throws PayErrorException { //交易状态 if ("0".equals(payMessage.getPayMessage().get(PayoneerPayService.CODE))) { /////这里进行成功的处理 diff --git a/pay-java-demo/src/main/java/com/egzosn/pay/demo/service/handler/UnionPayMessageHandler.java b/pay-java-demo/src/main/java/com/egzosn/pay/demo/service/handler/UnionPayMessageHandler.java index e088054..6770d23 100644 --- a/pay-java-demo/src/main/java/com/egzosn/pay/demo/service/handler/UnionPayMessageHandler.java +++ b/pay-java-demo/src/main/java/com/egzosn/pay/demo/service/handler/UnionPayMessageHandler.java @@ -1,10 +1,10 @@ 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.exception.PayErrorException; +import com.egzosn.pay.union.api.UnionPayService; import com.egzosn.pay.union.bean.SDKConstants; +import com.egzosn.pay.union.bean.UnionPayMessage; import java.util.Map; @@ -15,7 +15,7 @@ import java.util.Map; * create 2017 2017/11/4 0004 * */ -public class UnionPayMessageHandler extends BasePayMessageHandler { +public class UnionPayMessageHandler extends BasePayMessageHandler { public UnionPayMessageHandler(Integer payId) { @@ -23,7 +23,7 @@ public class UnionPayMessageHandler extends BasePayMessageHandler { } @Override - public PayOutMessage handle(PayMessage payMessage, Map context, PayService payService) throws PayErrorException { + public PayOutMessage handle(UnionPayMessage payMessage, Map context, UnionPayService payService) throws PayErrorException { //交易状态 if (SDKConstants.OK_RESP_CODE.equals(payMessage.getPayMessage().get(SDKConstants.param_respCode))) { /////这里进行成功的处理 diff --git a/pay-java-demo/src/main/java/com/egzosn/pay/demo/service/handler/WxPayMessageHandler.java b/pay-java-demo/src/main/java/com/egzosn/pay/demo/service/handler/WxPayMessageHandler.java index 0bf21ad..b44b020 100644 --- a/pay-java-demo/src/main/java/com/egzosn/pay/demo/service/handler/WxPayMessageHandler.java +++ b/pay-java-demo/src/main/java/com/egzosn/pay/demo/service/handler/WxPayMessageHandler.java @@ -1,9 +1,9 @@ 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.exception.PayErrorException; +import com.egzosn.pay.wx.bean.WxPayMessage; import java.util.Map; @@ -11,7 +11,7 @@ import java.util.Map; * 微信支付回调处理器 * Created by ZaoSheng on 2016/6/1. */ -public class WxPayMessageHandler extends BasePayMessageHandler { +public class WxPayMessageHandler extends BasePayMessageHandler { @@ -21,7 +21,7 @@ public class WxPayMessageHandler extends BasePayMessageHandler { } @Override - public PayOutMessage handle(PayMessage payMessage, Map context, PayService payService) throws PayErrorException { + public PayOutMessage handle(WxPayMessage payMessage, Map context, PayService payService) throws PayErrorException { //交易状态 if ("SUCCESS".equals(payMessage.getPayMessage().get("result_code"))){ /////这里进行成功的处理 diff --git a/pay-java-demo/src/main/java/com/egzosn/pay/demo/service/handler/YouDianPayMessageHandler.java b/pay-java-demo/src/main/java/com/egzosn/pay/demo/service/handler/YouDianPayMessageHandler.java index 578d3b8..4737df9 100644 --- a/pay-java-demo/src/main/java/com/egzosn/pay/demo/service/handler/YouDianPayMessageHandler.java +++ b/pay-java-demo/src/main/java/com/egzosn/pay/demo/service/handler/YouDianPayMessageHandler.java @@ -2,9 +2,9 @@ package com.egzosn.pay.demo.service.handler; import com.alibaba.fastjson.JSON; 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.exception.PayErrorException; +import com.egzosn.pay.wx.youdian.bean.WxYoudianPayMessage; import java.util.Map; @@ -12,7 +12,7 @@ import java.util.Map; * @author Fuzx * create 2017 2017/1/24 0024 */ -public class YouDianPayMessageHandler extends BasePayMessageHandler { +public class YouDianPayMessageHandler extends BasePayMessageHandler { @@ -22,7 +22,7 @@ public class YouDianPayMessageHandler extends BasePayMessageHandler { } @Override - public PayOutMessage handle(PayMessage payMessage, Map context, PayService payService) throws PayErrorException { + public PayOutMessage handle(WxYoudianPayMessage payMessage, Map context, PayService payService) throws PayErrorException { //交易状态 Map message = payMessage.getPayMessage(); //上下文对象中获取账单 diff --git a/pay-java-demo/src/main/java/com/egzosn/pay/demo/service/interceptor/AliPayMessageInterceptor.java b/pay-java-demo/src/main/java/com/egzosn/pay/demo/service/interceptor/AliPayMessageInterceptor.java index b8e9635..3d35c88 100644 --- a/pay-java-demo/src/main/java/com/egzosn/pay/demo/service/interceptor/AliPayMessageInterceptor.java +++ b/pay-java-demo/src/main/java/com/egzosn/pay/demo/service/interceptor/AliPayMessageInterceptor.java @@ -1,6 +1,8 @@ package com.egzosn.pay.demo.service.interceptor; +import com.egzosn.pay.ali.api.AliPayService; +import com.egzosn.pay.ali.bean.AliPayMessage; import com.egzosn.pay.common.api.PayMessageHandler; import com.egzosn.pay.common.api.PayMessageInterceptor; import com.egzosn.pay.common.api.PayService; @@ -15,7 +17,7 @@ import java.util.Map; * email egzosn@gmail.com * date 2017/1/18 19:28 */ -public class AliPayMessageInterceptor implements PayMessageInterceptor { +public class AliPayMessageInterceptor implements PayMessageInterceptor { /** * 拦截支付消息 @@ -28,7 +30,7 @@ public class AliPayMessageInterceptor implements PayMessageInterceptor { * @throws PayErrorException PayErrorException* */ @Override - public boolean intercept(PayMessage payMessage, Map context, PayService payService) throws PayErrorException { + public boolean intercept(AliPayMessage payMessage, Map context, AliPayService payService) throws PayErrorException { //这里进行拦截器处理,自行实现 String outTradeNo = payMessage.getOutTradeNo(); diff --git a/pay-java-demo/src/main/java/com/egzosn/pay/demo/service/interceptor/YoudianPayMessageInterceptor.java b/pay-java-demo/src/main/java/com/egzosn/pay/demo/service/interceptor/YoudianPayMessageInterceptor.java index 58f9317..efb8a07 100644 --- a/pay-java-demo/src/main/java/com/egzosn/pay/demo/service/interceptor/YoudianPayMessageInterceptor.java +++ b/pay-java-demo/src/main/java/com/egzosn/pay/demo/service/interceptor/YoudianPayMessageInterceptor.java @@ -6,6 +6,7 @@ import com.egzosn.pay.common.api.PayMessageInterceptor; import com.egzosn.pay.common.api.PayService; import com.egzosn.pay.common.bean.PayMessage; import com.egzosn.pay.common.exception.PayErrorException; +import com.egzosn.pay.wx.youdian.bean.WxYoudianPayMessage; import java.util.Map; @@ -15,7 +16,7 @@ import java.util.Map; * email egzosn@gmail.com * date 2017/1/18 19:28 */ -public class YoudianPayMessageInterceptor implements PayMessageInterceptor { +public class YoudianPayMessageInterceptor implements PayMessageInterceptor { // @Autowired // private AmtApplyService amtApplyService; @@ -31,7 +32,7 @@ public class YoudianPayMessageInterceptor implements PayMessageInterceptor { * @throws PayErrorException PayErrorException */ @Override - public boolean intercept(PayMessage payMessage, Map context, PayService payService) throws PayErrorException { + public boolean intercept(WxYoudianPayMessage payMessage, Map context, PayService payService) throws PayErrorException { //这里进行拦截器处理,自行实现 -- Gitee From c00f13473f54a203bd76fa718777517ff1b2118f Mon Sep 17 00:00:00 2001 From: egan Date: Wed, 3 Jul 2019 22:43:27 +0800 Subject: [PATCH 042/299] =?UTF-8?q?fastjson=20=E7=89=88=E6=9C=AC=E5=85=BC?= =?UTF-8?q?=E5=AE=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../pay/common/http/ClientHttpRequest.java | 139 +++++++++++------- 1 file changed, 84 insertions(+), 55 deletions(-) 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 a8f34b9..a9ffdec 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 @@ -2,6 +2,7 @@ package com.egzosn.pay.common.http; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONException; +import com.alibaba.fastjson.JSONObject; import com.egzosn.pay.common.bean.MethodType; import com.egzosn.pay.common.bean.result.PayException; import com.egzosn.pay.common.exception.PayErrorException; @@ -29,13 +30,14 @@ import static com.egzosn.pay.common.http.UriVariables.getMapToParameters; /** * 一个HTTP请求的客户端 + * * @author: egan - *
+ * 
  * email egzosn@gmail.com
  * date 2017/3/4 17:56
  *  
*/ -public class ClientHttpRequest extends HttpEntityEnclosingRequestBase implements org.apache.http.client.ResponseHandler{ +public class ClientHttpRequest extends HttpEntityEnclosingRequestBase implements org.apache.http.client.ResponseHandler { protected static final Log LOG = LogFactory.getLog(ClientHttpRequest.class); public static final ContentType APPLICATION_FORM_URLENCODED_UTF_8 = ContentType.create("application/x-www-form-urlencoded", Consts.UTF_8); @@ -49,7 +51,7 @@ public class ClientHttpRequest extends HttpEntityEnclosingRequestBase impleme */ private Charset defaultCharset; /** - * 响应类型 + * 响应类型 */ private Class responseType; @@ -66,25 +68,28 @@ public class ClientHttpRequest extends HttpEntityEnclosingRequestBase impleme } /** - * 根据请求地址 请求方法,请求内容对象 - * @param uri 请求地址 - * @param method 请求方法 - * @param request 请求内容 + * 根据请求地址 请求方法,请求内容对象 + * + * @param uri 请求地址 + * @param method 请求方法 + * @param request 请求内容 * @param defaultCharset 默认使用的响应编码 */ public ClientHttpRequest(URI uri, MethodType method, Object request, String defaultCharset) { - this(uri, method); + this(uri, method); setParameters(request); - if (StringUtils.isNotEmpty(defaultCharset)){ - setDefaultCharset( Charset.forName(defaultCharset)); + if (StringUtils.isNotEmpty(defaultCharset)) { + setDefaultCharset(Charset.forName(defaultCharset)); } } + /** - * 根据请求地址 请求方法,请求内容对象 - * @param uri 请求地址 - * @param method 请求方法 - * @param request 请求内容 + * 根据请求地址 请求方法,请求内容对象 + * + * @param uri 请求地址 + * @param method 请求方法 + * @param request 请求内容 * @param defaultCharset 默认使用的响应编码 */ public ClientHttpRequest(URI uri, MethodType method, Object request, Charset defaultCharset) { @@ -92,20 +97,24 @@ public class ClientHttpRequest extends HttpEntityEnclosingRequestBase impleme setParameters(request); setDefaultCharset(defaultCharset); } + /** - * 根据请求地址 请求方法,请求内容对象 - * @param uri 请求地址 + * 根据请求地址 请求方法,请求内容对象 + * + * @param uri 请求地址 * @param method 请求方法 * @param request 请求内容 */ public ClientHttpRequest(URI uri, MethodType method, Object request) { - this(uri, method); + this(uri, method); setParameters(request); } + /** * 根据请求地址 请求方法 - * @param uri 请求地址 - * @param method 请求方法 + * + * @param uri 请求地址 + * @param method 请求方法 */ public ClientHttpRequest(URI uri, MethodType method) { this.setURI(uri); @@ -114,30 +123,37 @@ public class ClientHttpRequest extends HttpEntityEnclosingRequestBase impleme /** * 根据请求地址 - * @param uri 请求地址 + * + * @param uri 请求地址 */ public ClientHttpRequest(URI uri) { this.setURI(uri); } + /** * 根据请求地址 - * @param uri 请求地址 + * + * @param uri 请求地址 */ public ClientHttpRequest(String uri) { this.setURI(URI.create(uri)); } + /** * 根据请求地址 请求方法 - * @param uri 请求地址 - * @param method 请求方法 + * + * @param uri 请求地址 + * @param method 请求方法 */ public ClientHttpRequest(String uri, MethodType method) { this.setURI(URI.create(uri)); this.method = method; } + /** - * 根据请求地址 请求方法,请求内容对象 - * @param uri 请求地址 + * 根据请求地址 请求方法,请求内容对象 + * + * @param uri 请求地址 * @param method 请求方法 * @param request 请求内容 */ @@ -150,7 +166,7 @@ public class ClientHttpRequest extends HttpEntityEnclosingRequestBase impleme * 设置请求方式 * * @param method 请求方式 - * {@link com.egzosn.pay.common.bean.MethodType} 请求方式 + * {@link com.egzosn.pay.common.bean.MethodType} 请求方式 */ public void setMethod(MethodType method) { this.method = method; @@ -158,6 +174,7 @@ public class ClientHttpRequest extends HttpEntityEnclosingRequestBase impleme /** * 获取请求方式 + * * @return 请求方式 */ @Override @@ -178,10 +195,11 @@ public class ClientHttpRequest extends HttpEntityEnclosingRequestBase impleme /** * 设置代理 + * * @param httpProxy http代理配置信息 * @return 当前HTTP请求的客户端 */ - public ClientHttpRequest setProxy(HttpHost httpProxy){ + public ClientHttpRequest setProxy(HttpHost httpProxy) { if (httpProxy != null) { RequestConfig config = RequestConfig.custom().setProxy(httpProxy).build(); setConfig(config); @@ -197,34 +215,34 @@ public class ClientHttpRequest extends HttpEntityEnclosingRequestBase impleme * @return 当前HTTP请求的客户端 */ public ClientHttpRequest setParameters(Object request) { - if (null == request){ + if (null == request) { return this; } - if (request instanceof HttpHeader){ - HttpHeader entity = (HttpHeader)request; - if (null != entity.getHeaders() ){ + if (request instanceof HttpHeader) { + HttpHeader entity = (HttpHeader) request; + if (null != entity.getHeaders()) { if (LOG.isDebugEnabled()) { LOG.debug("header : " + JSON.toJSONString(entity.getHeaders())); } - for (Header header : entity.getHeaders()){ + for (Header header : entity.getHeaders()) { addHeader(header); } } - }else if (request instanceof HttpStringEntity){ - HttpStringEntity entity = (HttpStringEntity)request; - if (!entity.isEmpty()){ + } else if (request instanceof HttpStringEntity) { + HttpStringEntity entity = (HttpStringEntity) request; + if (!entity.isEmpty()) { setEntity(entity); } - if (null != entity.getHeaders() ){ + if (null != entity.getHeaders()) { if (LOG.isDebugEnabled()) { LOG.debug("header : " + JSON.toJSONString(entity.getHeaders())); } - for (Header header : entity.getHeaders()){ + for (Header header : entity.getHeaders()) { addHeader(header); } } - } else if (request instanceof HttpEntity){ - setEntity((HttpEntity)request); + } else if (request instanceof HttpEntity) { + setEntity((HttpEntity) request); } else if (request instanceof Map) { String parameters = getMapToParameters((Map) request); if (LOG.isDebugEnabled()) { @@ -236,7 +254,7 @@ public class ClientHttpRequest extends HttpEntityEnclosingRequestBase impleme if (LOG.isDebugEnabled()) { LOG.debug("Parameter : " + request); } - StringEntity entity = new StringEntity((String) request, APPLICATION_FORM_URLENCODED_UTF_8); + StringEntity entity = new StringEntity((String) request, APPLICATION_FORM_URLENCODED_UTF_8); setEntity(entity); } else { String body = JSON.toJSONString(request); @@ -258,21 +276,21 @@ public class ClientHttpRequest extends HttpEntityEnclosingRequestBase impleme final HttpEntity entity = response.getEntity(); String[] value = null; - if (null == entity.getContentType()){ + if (null == entity.getContentType()) { value = new String[]{"application/x-www-form-urlencoded"}; - }else { + } else { value = entity.getContentType().getValue().split(";"); } //这里进行特殊处理,如果状态码非正常状态,但内容类型匹配至对应的结果也进行对应的响应类型转换 if (statusLine.getStatusCode() >= 300 && statusLine.getStatusCode() != 304) { - if (isJson(value[0], "") || isXml(value[0], "") ){ + if (isJson(value[0], "") || isXml(value[0], "")) { return toBean(entity, value); } EntityUtils.consume(entity); throw new HttpResponseException(statusLine.getStatusCode(), statusLine.getReasonPhrase()); } - if (null == responseType){ + if (null == responseType) { responseType = (Class) String.class; } @@ -283,7 +301,8 @@ public class ClientHttpRequest extends HttpEntityEnclosingRequestBase impleme /** * 对请求进行转化至对应的可转化类型 - * @param entity 响应实体 + * + * @param entity 响应实体 * @param contentType 内容类型编码数组,第一个值为内容类型,第二个值为编码类型 * @return 对应的响应对象 * @throws IOException 响应类型文本转换时抛出异常 @@ -298,7 +317,7 @@ public class ClientHttpRequest extends HttpEntityEnclosingRequestBase impleme //获取响应的文本内容 String result = EntityUtils.toString(entity, getDefaultCharset()); - if (LOG.isDebugEnabled()){ + if (LOG.isDebugEnabled()) { LOG.debug("请求响应内容:\r\n" + result); } if (responseType.isAssignableFrom(String.class)) { @@ -309,6 +328,9 @@ public class ClientHttpRequest extends HttpEntityEnclosingRequestBase impleme //json类型 if (isJson(contentType[0], first)) { try { + if (responseType.isAssignableFrom(JSONObject.class)) { + return (T)JSON.parseObject(result); + } return JSON.parseObject(result, responseType); } catch (JSONException e) { throw new PayErrorException(new PayException("failure", String.format("类型转化异常,contentType: %s\n%s", entity.getContentType().getValue(), e.getMessage()), result)); @@ -317,8 +339,11 @@ public class ClientHttpRequest extends HttpEntityEnclosingRequestBase impleme //xml类型 if (isXml(contentType[0], first)) { try { + if (responseType.isAssignableFrom(JSONObject.class)) { + return (T) XML.toJSONObject(result, getDefaultCharset()); + } return XML.toJSONObject(result, getDefaultCharset()).toJavaObject(responseType); - }catch (Exception e){ + } catch (Exception e) { ; } } @@ -346,30 +371,34 @@ public class ClientHttpRequest extends HttpEntityEnclosingRequestBase impleme /** * 检测响应类型是否为json + * * @param contentType 内容类型 - * @param textFirst 文本第一个字符 + * @param textFirst 文本第一个字符 * @return 布尔型, true为json内容类型 */ - private boolean isJson(String contentType, String textFirst){ - return( ContentType.APPLICATION_JSON.getMimeType().equals(contentType) || "{[".indexOf(textFirst) >= 0 ); + private boolean isJson(String contentType, String textFirst) { + return (ContentType.APPLICATION_JSON.getMimeType().equals(contentType) || "{[".indexOf(textFirst) >= 0); } + /** * 检测响应类型是否为文本类型 + * * @param contentType 内容类型 * @return 布尔型, true为文本内容类型 */ - private boolean isText(String contentType){ - return contentType.contains("xml") || contentType.contains("json") || contentType.contains("text") || contentType.contains("form-data")|| contentType.contains("x-www-form-urlencoded"); + private boolean isText(String contentType) { + return contentType.contains("xml") || contentType.contains("json") || contentType.contains("text") || contentType.contains("form-data") || contentType.contains("x-www-form-urlencoded"); } /** * 检测响应类型是否为xml + * * @param contentType 内容类型 - * @param textFirst 文本第一个字符 + * @param textFirst 文本第一个字符 * @return 布尔型, true为xml内容类型 */ - private boolean isXml(String contentType, String textFirst){ - return( ContentType.APPLICATION_XML.getMimeType().equals(contentType) || "<".indexOf(textFirst) >= 0 ); + private boolean isXml(String contentType, String textFirst) { + return (ContentType.APPLICATION_XML.getMimeType().equals(contentType) || "<".indexOf(textFirst) >= 0); } -- Gitee From 7dae9dcca2e86cafea65c54f46fa98203e00c53b Mon Sep 17 00:00:00 2001 From: egzosn Date: Thu, 4 Jul 2019 15:51:17 +0800 Subject: [PATCH 043/299] =?UTF-8?q?=E9=81=97=E6=BC=8F=E6=B3=9B=E5=9E=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/egzosn/pay/common/api/DefaultPayMessageHandler.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pay-java-common/src/main/java/com/egzosn/pay/common/api/DefaultPayMessageHandler.java b/pay-java-common/src/main/java/com/egzosn/pay/common/api/DefaultPayMessageHandler.java index a963720..496b7a3 100644 --- a/pay-java-common/src/main/java/com/egzosn/pay/common/api/DefaultPayMessageHandler.java +++ b/pay-java-common/src/main/java/com/egzosn/pay/common/api/DefaultPayMessageHandler.java @@ -19,7 +19,7 @@ import java.util.Map; * date 2018-10-29 17:31:05 *
*/ -public class DefaultPayMessageHandler implements PayMessageHandler { +public class DefaultPayMessageHandler implements PayMessageHandler { protected final Log LOG = LogFactory.getLog(DefaultPayMessageHandler.class); /** -- Gitee From 8e0f14351a4a4a3e8ae9541ad458fc3af46e5508 Mon Sep 17 00:00:00 2001 From: egzosn Date: Fri, 5 Jul 2019 10:34:08 +0800 Subject: [PATCH 044/299] =?UTF-8?q?=E6=B6=88=E6=81=AF=E7=B1=BB=E5=9E=8B?= =?UTF-8?q?=E5=88=A4=E6=96=AD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../main/java/com/egzosn/pay/common/api/PayMessageRouter.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pay-java-common/src/main/java/com/egzosn/pay/common/api/PayMessageRouter.java b/pay-java-common/src/main/java/com/egzosn/pay/common/api/PayMessageRouter.java index 3a54246..990e24e 100644 --- a/pay-java-common/src/main/java/com/egzosn/pay/common/api/PayMessageRouter.java +++ b/pay-java-common/src/main/java/com/egzosn/pay/common/api/PayMessageRouter.java @@ -129,7 +129,7 @@ public class PayMessageRouter { public PayOutMessage route(Map payMessage, PayConfigStorage storage) { PayMessage message = payService.createMessage(payMessage); message.setPayType(storage.getPayType()); - if (null == storage.getMsgType()){ + if (null != storage.getMsgType()){ message.setMsgType(storage.getMsgType().name()); } return route(message); -- Gitee From ad3df4fa7903da190bf32a3eee0556ee58ddbbcc Mon Sep 17 00:00:00 2001 From: egzosn Date: Thu, 11 Jul 2019 10:37:25 +0800 Subject: [PATCH 045/299] =?UTF-8?q?=E7=9B=AE=E5=89=8D=20Fastjson=20?= =?UTF-8?q?=E8=A2=AB=E5=B9=BF=E6=B3=9B=E4=BD=BF=E7=94=A8=EF=BC=8C=E4=B8=BA?= =?UTF-8?q?=E4=BA=86=E9=81=BF=E5=85=8D=20EXP=20=E6=B5=81=E5=87=BA=E9=81=AD?= =?UTF-8?q?=E9=BB=91=E5=AE=A2=E6=94=BB=E5=87=BB=EF=BC=8C=E6=85=A2=E9=9B=BE?= =?UTF-8?q?=E5=AE=89=E5=85=A8=E5=9B=A2=E9=98=9F=E5=BB=BA=E8=AE=AE=E7=AB=8B?= =?UTF-8?q?=E5=8D=B3=E6=9B=B4=E6=96=B0=20Fastjson=20=E5=88=B0=E5=BD=93?= =?UTF-8?q?=E5=89=8D=E6=9C=80=E6=96=B0=E7=89=88=E6=9C=AC=EF=BC=9A1.2.58?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 73e89fd..905521c 100644 --- a/pom.xml +++ b/pom.xml @@ -61,7 +61,7 @@ 2.12.8-SNAPSHOT 4.5.4 1.2.17 - 1.2.41 + 1.2.58 3.3.1
-- Gitee From f3310aab91e1e0fd0eff35909661868a34b89425 Mon Sep 17 00:00:00 2001 From: egzosn Date: Mon, 15 Jul 2019 11:31:24 +0800 Subject: [PATCH 046/299] =?UTF-8?q?=E8=AF=B7=E6=B1=82=E4=BB=A3=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/egzosn/pay/common/http/HttpRequestTemplate.java | 2 +- .../src/main/java/com/egzosn/pay/wx/api/WxPayService.java | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/pay-java-common/src/main/java/com/egzosn/pay/common/http/HttpRequestTemplate.java b/pay-java-common/src/main/java/com/egzosn/pay/common/http/HttpRequestTemplate.java index 2c5b071..6a63b97 100644 --- a/pay-java-common/src/main/java/com/egzosn/pay/common/http/HttpRequestTemplate.java +++ b/pay-java-common/src/main/java/com/egzosn/pay/common/http/HttpRequestTemplate.java @@ -332,7 +332,7 @@ public class HttpRequestTemplate { } ClientHttpRequest httpRequest = new ClientHttpRequest(uri ,method, request, null == configStorage ? null : configStorage.getCharset()); //判断是否有代理设置 - if (null == httpProxy){ + if (null != httpProxy){ httpRequest.setProxy(httpProxy); } httpRequest.setResponseType(responseType); 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 87d5445..e377b99 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 @@ -526,6 +526,7 @@ public class WxPayService extends BasePayService { parameters.put("total_fee", Util.conversionCentAmount(refundOrder.getTotalAmount())); parameters.put("refund_fee", Util.conversionCentAmount(refundOrder.getRefundAmount())); parameters.put("op_user_id", payConfigStorage.getPid()); + setParameters(parameters, "notify_url", payConfigStorage.getNotifyUrl()); //设置签名 setSign(parameters); -- Gitee From 66055980f6432c099268854b57aa5aef04ba8ada Mon Sep 17 00:00:00 2001 From: egan Date: Sun, 4 Aug 2019 17:16:41 +0800 Subject: [PATCH 047/299] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E4=BA=8C=E7=BB=B4?= =?UTF-8?q?=E7=A0=81=E4=BF=A1=E6=81=AF=E8=BF=94=E5=9B=9E=E6=8E=A5=E5=8F=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/egzosn/pay/ali/api/AliPayService.java | 13 ++++++------- .../com/egzosn/pay/common/api/BasePayService.java | 13 +++++++++++++ .../java/com/egzosn/pay/common/api/PayService.java | 7 +++++++ .../com/egzosn/pay/fuiou/api/FuiouPayService.java | 2 +- .../egzosn/pay/payoneer/api/PayoneerPayService.java | 2 +- .../com/egzosn/pay/paypal/api/PayPalPayService.java | 2 +- .../com/egzosn/pay/union/api/UnionPayService.java | 4 ++-- .../pay/wx/youdian/api/WxYouDianPayService.java | 4 ++-- .../com/egzosn/pay/yiji/api/YiJiPayService.java | 2 +- 9 files changed, 34 insertions(+), 15 deletions(-) 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 fa21cfb..6c23d92 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 @@ -291,23 +291,22 @@ public class AliPayService extends BasePayService { return formHtml.toString(); } + + /** - * 生成二维码支付 + * 获取输出二维码信息, * * @param order 发起支付的订单信息 - * @return 返回图片信息,支付时需要的 + * @return 返回二维码信息,,支付时需要的 */ @Override - public BufferedImage genQrPay(PayOrder order) { - + public String getQrPay(PayOrder order){ Map orderInfo = orderInfo(order); - - //预订单 JSONObject result = getHttpRequestTemplate().postForObject(getReqUrl() + "?" + UriVariables.getMapToParameters(orderInfo), null, JSONObject.class); JSONObject response = result.getJSONObject("alipay_trade_precreate_response"); if (SUCCESS_CODE.equals(response.getString(CODE))) { - return MatrixToImageWriter.writeInfoToJpgBuff(response.getString("qr_code")); + return response.getString("qr_code"); } throw new PayErrorException(new PayException(response.getString(CODE), response.getString("msg"), result.toJSONString())); diff --git a/pay-java-common/src/main/java/com/egzosn/pay/common/api/BasePayService.java b/pay-java-common/src/main/java/com/egzosn/pay/common/api/BasePayService.java index ed8e109..509a2f3 100644 --- a/pay-java-common/src/main/java/com/egzosn/pay/common/api/BasePayService.java +++ b/pay-java-common/src/main/java/com/egzosn/pay/common/api/BasePayService.java @@ -5,11 +5,13 @@ import com.egzosn.pay.common.bean.*; import com.egzosn.pay.common.exception.PayErrorException; import com.egzosn.pay.common.http.HttpConfigStorage; import com.egzosn.pay.common.http.HttpRequestTemplate; +import com.egzosn.pay.common.util.MatrixToImageWriter; import com.egzosn.pay.common.util.sign.SignUtils; import com.egzosn.pay.common.util.str.StringUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +import java.awt.image.BufferedImage; import java.io.InputStream; import java.io.UnsupportedEncodingException; import java.math.BigDecimal; @@ -141,6 +143,17 @@ public abstract class BasePayService implements Pay return buildRequest(orderInfo, MethodType.POST); } + /** + * 生成二维码支付 + * + * @param order 发起支付的订单信息 + * @return 返回图片信息,支付时需要的 + */ + @Override + public BufferedImage genQrPay(PayOrder order) { + return MatrixToImageWriter.writeInfoToJpgBuff(getQrPay(order)); + } + /** * 将请求参数或者请求流转化为 Map * diff --git a/pay-java-common/src/main/java/com/egzosn/pay/common/api/PayService.java b/pay-java-common/src/main/java/com/egzosn/pay/common/api/PayService.java index 0c7f73d..eececb9 100644 --- a/pay-java-common/src/main/java/com/egzosn/pay/common/api/PayService.java +++ b/pay-java-common/src/main/java/com/egzosn/pay/common/api/PayService.java @@ -161,6 +161,13 @@ public interface PayService { * @return 返回图片信息,支付时需要的 */ BufferedImage genQrPay(PayOrder order); + /** + * 获取输出二维码信息, + * + * @param order 发起支付的订单信息 + * @return 返回二维码信息,,支付时需要的 + */ + String getQrPay(PayOrder order); /** * 刷卡付,pos主动扫码付款(条码付) diff --git a/pay-java-fuiou/src/main/java/com/egzosn/pay/fuiou/api/FuiouPayService.java b/pay-java-fuiou/src/main/java/com/egzosn/pay/fuiou/api/FuiouPayService.java index f71f3e0..b5109dd 100644 --- a/pay-java-fuiou/src/main/java/com/egzosn/pay/fuiou/api/FuiouPayService.java +++ b/pay-java-fuiou/src/main/java/com/egzosn/pay/fuiou/api/FuiouPayService.java @@ -288,7 +288,7 @@ public class FuiouPayService extends BasePayService { * @return 空 */ @Override - public BufferedImage genQrPay (PayOrder order) { + public String getQrPay (PayOrder order) { throw new UnsupportedOperationException(); } diff --git a/pay-java-payoneer/src/main/java/com/egzosn/pay/payoneer/api/PayoneerPayService.java b/pay-java-payoneer/src/main/java/com/egzosn/pay/payoneer/api/PayoneerPayService.java index 1b655d2..c83766f 100644 --- a/pay-java-payoneer/src/main/java/com/egzosn/pay/payoneer/api/PayoneerPayService.java +++ b/pay-java-payoneer/src/main/java/com/egzosn/pay/payoneer/api/PayoneerPayService.java @@ -250,7 +250,7 @@ public class PayoneerPayService extends BasePayService im * @return 返回图片信息,支付时需要的 */ @Override - public BufferedImage genQrPay(PayOrder order) { + public String getQrPay(PayOrder order) { throw new UnsupportedOperationException(); } diff --git a/pay-java-paypal/src/main/java/com/egzosn/pay/paypal/api/PayPalPayService.java b/pay-java-paypal/src/main/java/com/egzosn/pay/paypal/api/PayPalPayService.java index be82995..eeec14f 100644 --- a/pay-java-paypal/src/main/java/com/egzosn/pay/paypal/api/PayPalPayService.java +++ b/pay-java-paypal/src/main/java/com/egzosn/pay/paypal/api/PayPalPayService.java @@ -209,7 +209,7 @@ public class PayPalPayService extends BasePayService{ } @Override - public BufferedImage genQrPay(PayOrder order) { + public String getQrPay(PayOrder order) { return null; } diff --git a/pay-java-union/src/main/java/com/egzosn/pay/union/api/UnionPayService.java b/pay-java-union/src/main/java/com/egzosn/pay/union/api/UnionPayService.java index 1e0b4a4..c7e0f2e 100644 --- a/pay-java-union/src/main/java/com/egzosn/pay/union/api/UnionPayService.java +++ b/pay-java-union/src/main/java/com/egzosn/pay/union/api/UnionPayService.java @@ -386,7 +386,7 @@ public class UnionPayService extends BasePayService { * @return 返回图片信息,支付时需要的 */ @Override - public BufferedImage genQrPay(PayOrder order) { + public String getQrPay(PayOrder order) { Map params = orderInfo(order); String responseStr = getHttpRequestTemplate().postForObject(this.getBackTransUrl(), params, String.class); Map response = UriVariables.getParametersToMap(responseStr); @@ -396,7 +396,7 @@ public class UnionPayService extends BasePayService { if (this.verify(response)) { if (SDKConstants.OK_RESP_CODE.equals(response.get(SDKConstants.param_respCode))) { //成功 - return MatrixToImageWriter.writeInfoToJpgBuff((String) response.get(SDKConstants.param_qrCode)); + return (String) response.get(SDKConstants.param_qrCode); } throw new PayErrorException(new PayException((String) response.get(SDKConstants.param_respCode), (String) response.get(SDKConstants.param_respMsg), responseStr)); } 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 76898c3..c77dd23 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 @@ -334,9 +334,9 @@ public class WxYouDianPayService extends BasePayService { * @return 返回图片信息,支付时需要的 */ @Override - public BufferedImage genQrPay(PayOrder order) { + public String getQrPay(PayOrder order) { return null; } -- Gitee From b9244395b399b8554e13b2320a0b4916cc70915f Mon Sep 17 00:00:00 2001 From: egan Date: Sun, 4 Aug 2019 17:17:02 +0800 Subject: [PATCH 048/299] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E4=BA=8C=E7=BB=B4?= =?UTF-8?q?=E7=A0=81=E4=BF=A1=E6=81=AF=E8=BF=94=E5=9B=9E=E6=8E=A5=E5=8F=A3?= =?UTF-8?q?=20=E7=A7=BB=E9=99=A4=E5=BE=AE=E4=BF=A1sub=5Fappid=E5=BF=85?= =?UTF-8?q?=E5=A1=AB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/egzosn/pay/wx/api/WxPayService.java | 23 +++++++++---------- 1 file changed, 11 insertions(+), 12 deletions(-) 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 87d5445..2132288 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 @@ -179,10 +179,12 @@ public class WxPayService extends BasePayService { parameters.put(APPID, payConfigStorage.getAppid()); parameters.put(MCH_ID, payConfigStorage.getMchId()); //判断如果是服务商模式信息则加入 - if (!StringUtils.isEmpty(payConfigStorage.getSubAppid()) && !StringUtils.isEmpty(payConfigStorage.getSubMchId())) { - parameters.put("sub_appid", payConfigStorage.getSubAppid()); + if (!StringUtils.isEmpty(payConfigStorage.getSubMchId())) { parameters.put("sub_mch_id", payConfigStorage.getSubMchId()); } + if (!StringUtils.isEmpty(payConfigStorage.getSubAppid())) { + parameters.put("sub_appid", payConfigStorage.getSubAppid()); + } parameters.put(NONCE_STR, SignUtils.randomStr()); return parameters; @@ -229,8 +231,8 @@ public class WxPayService extends BasePayService { //调起支付的参数列表 JSONObject result = requestTemplate.postForObject(getReqUrl(order.getTransactionType()), requestXML, JSONObject.class); - if (!SUCCESS.equals(result.get(RETURN_CODE))) { - throw new PayErrorException(new WxPayError(result.getString(RETURN_CODE), result.getString(RETURN_MSG_CODE), result.toJSONString())); + if (!SUCCESS.equals(result.get(RETURN_CODE)) || !SUCCESS.equals(result.get(RESULT_CODE))) { + throw new PayErrorException(new WxPayError(result.getString(RESULT_CODE), result.getString(RETURN_MSG_CODE), result.toJSONString())); } return result; } @@ -416,23 +418,20 @@ public class WxPayService extends BasePayService { } /** - * 获取输出二维码,用户返回给支付端, + * 获取输出二维码信息, * * @param order 发起支付的订单信息 - * @return 返回图片信息,支付时需要的 + * @return 返回二维码信息,,支付时需要的 */ @Override - public BufferedImage genQrPay(PayOrder order) { + public String getQrPay(PayOrder order){ Map orderInfo = orderInfo(order); //获取对应的支付账户操作工具(可根据账户id) if (!SUCCESS.equals(orderInfo.get(RESULT_CODE))) { - throw new PayErrorException(new WxPayError("-1", (String) orderInfo.get("err_code"))); + throw new PayErrorException(new WxPayError((String)orderInfo.get("err_code"), orderInfo.toString())); } - - - return MatrixToImageWriter.writeInfoToJpgBuff((String) orderInfo.get("code_url")); + return (String) orderInfo.get("code_url"); } - /** * 刷卡付,pos主动扫码付款 * -- Gitee From b214c576353fae229c3ab0c3546ff2c54e56d37e Mon Sep 17 00:00:00 2001 From: egan Date: Sun, 4 Aug 2019 17:17:43 +0800 Subject: [PATCH 049/299] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E8=AF=B7=E6=B1=82?= =?UTF-8?q?=E5=B7=A5=E5=85=B7=E8=B6=85=E6=97=B6=E6=97=B6=E9=97=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../pay/common/http/HttpConfigStorage.java | 20 +++++++++++++++++++ .../pay/common/http/HttpRequestTemplate.java | 13 +++++++++++- 2 files changed, 32 insertions(+), 1 deletion(-) diff --git a/pay-java-common/src/main/java/com/egzosn/pay/common/http/HttpConfigStorage.java b/pay-java-common/src/main/java/com/egzosn/pay/common/http/HttpConfigStorage.java index 5f6e39f..646a0ec 100644 --- a/pay-java-common/src/main/java/com/egzosn/pay/common/http/HttpConfigStorage.java +++ b/pay-java-common/src/main/java/com/egzosn/pay/common/http/HttpConfigStorage.java @@ -60,6 +60,10 @@ public class HttpConfigStorage { */ private String charset; + private int socketTimeout; + + private int connectTimeout; + /** * http代理地址 * @return http代理地址 @@ -193,4 +197,20 @@ public class HttpConfigStorage { public void setCharset(String charset) { this.charset = charset; } + + public int getSocketTimeout() { + return socketTimeout; + } + + public void setSocketTimeout(int socketTimeout) { + this.socketTimeout = socketTimeout; + } + + public int getConnectTimeout() { + return connectTimeout; + } + + public void setConnectTimeout(int connectTimeout) { + this.connectTimeout = connectTimeout; + } } diff --git a/pay-java-common/src/main/java/com/egzosn/pay/common/http/HttpRequestTemplate.java b/pay-java-common/src/main/java/com/egzosn/pay/common/http/HttpRequestTemplate.java index 2c5b071..6453209 100644 --- a/pay-java-common/src/main/java/com/egzosn/pay/common/http/HttpRequestTemplate.java +++ b/pay-java-common/src/main/java/com/egzosn/pay/common/http/HttpRequestTemplate.java @@ -10,6 +10,7 @@ import org.apache.http.HttpHost; import org.apache.http.auth.AuthScope; import org.apache.http.auth.UsernamePasswordCredentials; import org.apache.http.client.CredentialsProvider; +import org.apache.http.client.config.RequestConfig; import org.apache.http.client.methods.CloseableHttpResponse; import org.apache.http.config.Registry; import org.apache.http.config.RegistryBuilder; @@ -43,7 +44,7 @@ import java.util.Map; */ public class HttpRequestTemplate { - protected final Log LOG = LogFactory.getLog(HttpRequestTemplate.class); + protected static final Log LOG = LogFactory.getLog(HttpRequestTemplate.class); protected CloseableHttpClient httpClient; @@ -75,6 +76,7 @@ public class HttpRequestTemplate { //设置httpclient的SSLSocketFactory .setSSLSocketFactory(createSSL(configStorage)) .setConnectionManager(connectionManager(configStorage)) + .setDefaultRequestConfig(createRequestConfig(configStorage)) .build(); if (null == connectionManager) { return this.httpClient = httpClient; @@ -84,6 +86,15 @@ public class HttpRequestTemplate { } + private RequestConfig createRequestConfig(HttpConfigStorage configStorage) { + RequestConfig requestConfig = RequestConfig.custom() + .setSocketTimeout(configStorage.getSocketTimeout()) + .setConnectTimeout(configStorage.getConnectTimeout()) +// .setConnectionRequestTimeout(1000) + .build(); + return requestConfig; + } + /** * 初始化 * @param configStorage 请求配置 -- Gitee From c8004a42387e23fde977b40f8eec663542d75613 Mon Sep 17 00:00:00 2001 From: egan Date: Sun, 4 Aug 2019 17:27:07 +0800 Subject: [PATCH 050/299] . --- .../main/java/com/egzosn/pay/ali/before/api/AliPayService.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 2e3956c..d1f4b37 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 @@ -307,7 +307,7 @@ public class AliPayService extends BasePayService { * @return 返回图片信息,支付时需要的 */ @Override - public BufferedImage genQrPay(PayOrder orderInfo) { + public String getQrPay(PayOrder orderInfo) { throw new UnsupportedOperationException(); } -- Gitee From bbedf582a3ae32c42ac97071454e0e38b575f3f6 Mon Sep 17 00:00:00 2001 From: egzosn Date: Mon, 12 Aug 2019 10:13:23 +0800 Subject: [PATCH 051/299] =?UTF-8?q?2.12.8=20=E6=A1=88=E4=BE=8B=E7=BC=96?= =?UTF-8?q?=E5=86=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 2 +- pay-java-ali/README.md | 1 + pay-java-ali/pom.xml | 2 +- pay-java-common/pom.xml | 2 +- .../egzosn/pay/common/http/HttpConfigStorage.java | 4 ++-- pay-java-demo/pom.xml | 2 +- .../pay/demo/controller/AliPayController.java | 15 +++++++++++++-- .../egzosn/pay/demo/controller/PayController.java | 15 ++++++++++++++- .../pay/demo/controller/UnionPayController.java | 13 ++++++++++++- .../pay/demo/controller/WxPayController.java | 13 ++++++++++++- pay-java-fuiou/pom.xml | 2 +- pay-java-payoneer/pom.xml | 2 +- pay-java-paypal/pom.xml | 2 +- pay-java-union/README.md | 1 + pay-java-union/pom.xml | 2 +- pay-java-wx-youdian/README.md | 1 + pay-java-wx-youdian/pom.xml | 2 +- pay-java-wx/README.md | 1 + pay-java-wx/pom.xml | 2 +- pay-java-yiji/pom.xml | 2 +- pom.xml | 4 ++-- 21 files changed, 70 insertions(+), 20 deletions(-) diff --git a/README.md b/README.md index a6583fd..caa87bf 100644 --- a/README.md +++ b/README.md @@ -21,7 +21,7 @@ com.egzosn {module-name} - 2.12.7 + 2.12.8 ``` diff --git a/pay-java-ali/README.md b/pay-java-ali/README.md index 68a10ee..99bdc99 100644 --- a/pay-java-ali/README.md +++ b/pay-java-ali/README.md @@ -97,6 +97,7 @@ /*-----------扫码付-------------------*/ payOrder.setTransactionType(AliTransactionType.SWEEPPAY); //获取扫码付的二维码 +// String image = service.getQrPay(payOrder); BufferedImage image = service.genQrPay(payOrder); /*-----------/扫码付-------------------*/ diff --git a/pay-java-ali/pom.xml b/pay-java-ali/pom.xml index 968ba5b..30ae700 100644 --- a/pay-java-ali/pom.xml +++ b/pay-java-ali/pom.xml @@ -5,7 +5,7 @@ pay-java-parent com.egzosn - 2.12.8-SNAPSHOT + 2.12.8 4.0.0 pay-java-ali diff --git a/pay-java-common/pom.xml b/pay-java-common/pom.xml index 20e98c2..fad81d5 100644 --- a/pay-java-common/pom.xml +++ b/pay-java-common/pom.xml @@ -5,7 +5,7 @@ pay-java-parent com.egzosn - 2.12.8-SNAPSHOT + 2.12.8 4.0.0 jar diff --git a/pay-java-common/src/main/java/com/egzosn/pay/common/http/HttpConfigStorage.java b/pay-java-common/src/main/java/com/egzosn/pay/common/http/HttpConfigStorage.java index 646a0ec..274b9d9 100644 --- a/pay-java-common/src/main/java/com/egzosn/pay/common/http/HttpConfigStorage.java +++ b/pay-java-common/src/main/java/com/egzosn/pay/common/http/HttpConfigStorage.java @@ -60,9 +60,9 @@ public class HttpConfigStorage { */ private String charset; - private int socketTimeout; + private int socketTimeout = -1; - private int connectTimeout; + private int connectTimeout = -1; /** * http代理地址 diff --git a/pay-java-demo/pom.xml b/pay-java-demo/pom.xml index 23c7ad4..d21f59e 100644 --- a/pay-java-demo/pom.xml +++ b/pay-java-demo/pom.xml @@ -5,7 +5,7 @@ pay-java-parent com.egzosn - 2.12.8-SNAPSHOT + 2.12.8 4.0.0 war diff --git a/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/AliPayController.java b/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/AliPayController.java index 52bd299..ccfb3ae 100644 --- a/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/AliPayController.java +++ b/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/AliPayController.java @@ -7,7 +7,6 @@ import com.egzosn.pay.ali.api.AliPayService; import com.egzosn.pay.ali.bean.AliTransactionType; import com.egzosn.pay.ali.bean.AliTransferType; import com.egzosn.pay.ali.bean.OrderSettle; -import com.egzosn.pay.common.api.PayService; import com.egzosn.pay.common.bean.*; import com.egzosn.pay.common.http.HttpConfigStorage; import com.egzosn.pay.common.http.UriVariables; @@ -122,12 +121,24 @@ public class AliPayController { * @throws IOException IOException */ @RequestMapping(value = "toQrPay.jpg", produces = "image/jpeg;charset=UTF-8") - public byte[] toWxQrPay( BigDecimal price) throws IOException { + public byte[] toQrPay( BigDecimal price) throws IOException { //获取对应的支付账户操作工具(可根据账户id) ByteArrayOutputStream baos = new ByteArrayOutputStream(); ImageIO.write(service.genQrPay( new PayOrder("订单title", "摘要", null == price ? new BigDecimal(0.01) : price, System.currentTimeMillis()+"", AliTransactionType.SWEEPPAY)), "JPEG", baos); return baos.toByteArray(); } + /** + * 获取二维码地址 + * 二维码支付 + * @param price 金额 + * @return 二维码图像 + * @throws IOException IOException + */ + @RequestMapping(value = "getQrPay.json") + public String getQrPay(BigDecimal price) throws IOException { + //获取对应的支付账户操作工具(可根据账户id) + return service.getQrPay( new PayOrder("订单title", "摘要", null == price ? new BigDecimal(0.01) : price, System.currentTimeMillis()+"", AliTransactionType.SWEEPPAY)); + } /** 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 54974a3..0705246 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 @@ -228,7 +228,20 @@ public class PayController { ImageIO.write(payResponse.getService().genQrPay(new PayOrder("订单title", "摘要", null == price ? new BigDecimal(0.01) : price, System.currentTimeMillis() + "", PayType.valueOf(payResponse.getStorage().getPayType()).getTransactionType(transactionType))), "JPEG", baos); return baos.toByteArray(); } - + /** + * 获取二维码地址 + * 二维码支付 + * @param price 金额 + * @return 二维码图像 + * @throws IOException IOException + */ + @RequestMapping(value = "getQrPay.json") + public String getQrPay(Integer payId, String transactionType, BigDecimal price) throws IOException { + //获取对应的支付账户操作工具(可根据账户id) + //获取对应的支付账户操作工具(可根据账户id) + PayResponse payResponse = service.getPayResponse(payId); + return payResponse.getService().getQrPay( new PayOrder("订单title", "摘要", null == price ? new BigDecimal(0.01) : price, System.currentTimeMillis() + "", PayType.valueOf(payResponse.getStorage().getPayType()).getTransactionType(transactionType))); + } /** * 获取一码付二维码图像 * 二维码支付 diff --git a/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/UnionPayController.java b/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/UnionPayController.java index 77d1eb2..d48f0ed 100644 --- a/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/UnionPayController.java +++ b/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/UnionPayController.java @@ -157,7 +157,18 @@ public class UnionPayController { ImageIO.write(service.genQrPay( new PayOrder("订单title", "摘要", null == price ? new BigDecimal(0.01) : price, System.currentTimeMillis()+"", UnionTransactionType.APPLY_QR_CODE)), "JPEG", baos); return baos.toByteArray(); } - + /** + * 获取二维码地址 + * 二维码支付 + * @param price 金额 + * @return 二维码图像 + * @throws IOException IOException + */ + @RequestMapping(value = "getQrPay.json") + public String getQrPay(BigDecimal price) throws IOException { + //获取对应的支付账户操作工具(可根据账户id) + return service.getQrPay( new PayOrder("订单title", "摘要", null == price ? new BigDecimal(0.01) : price, System.currentTimeMillis()+"", UnionTransactionType.APPLY_QR_CODE)); + } /** * 刷卡付,pos主动扫码付款(条码付) CONSUME diff --git a/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/WxPayController.java b/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/WxPayController.java index abb6ea6..4f72fb3 100644 --- a/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/WxPayController.java +++ b/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/WxPayController.java @@ -174,7 +174,18 @@ public class WxPayController { return baos.toByteArray(); } - + /** + * 获取二维码地址 + * 二维码支付 + * @param price 金额 + * @return 二维码图像 + * @throws IOException IOException + */ + @RequestMapping(value = "getQrPay.json") + public String getQrPay(BigDecimal price) throws IOException { + //获取对应的支付账户操作工具(可根据账户id) + return service.getQrPay( new PayOrder("订单title", "摘要", null == price ? new BigDecimal(0.01) : price, System.currentTimeMillis()+"", WxTransactionType.NATIVE)); + } /** * 刷卡付,pos主动扫码付款(条码付) * @param authCode 授权码,条码等 diff --git a/pay-java-fuiou/pom.xml b/pay-java-fuiou/pom.xml index 0408e39..a51c075 100644 --- a/pay-java-fuiou/pom.xml +++ b/pay-java-fuiou/pom.xml @@ -5,7 +5,7 @@ pay-java-parent com.egzosn - 2.12.8-SNAPSHOT + 2.12.8 4.0.0 pay-java-fuiou diff --git a/pay-java-payoneer/pom.xml b/pay-java-payoneer/pom.xml index 3debe3b..c9c4936 100644 --- a/pay-java-payoneer/pom.xml +++ b/pay-java-payoneer/pom.xml @@ -5,7 +5,7 @@ pay-java-parent com.egzosn - 2.12.8-SNAPSHOT + 2.12.8 4.0.0 pay-java-payoneer diff --git a/pay-java-paypal/pom.xml b/pay-java-paypal/pom.xml index 1a74ff7..bd05591 100644 --- a/pay-java-paypal/pom.xml +++ b/pay-java-paypal/pom.xml @@ -5,7 +5,7 @@ pay-java-parent com.egzosn - 2.12.8-SNAPSHOT + 2.12.8 4.0.0 diff --git a/pay-java-union/README.md b/pay-java-union/README.md index 177c585..dad8d53 100644 --- a/pay-java-union/README.md +++ b/pay-java-union/README.md @@ -93,6 +93,7 @@ /*-----------扫码付-------------------*/ payOrder.setTransactionType(UnionTransactionType.APPLY_QR_CODE); //获取扫码付的二维码 +// String image = service.getQrPay(payOrder); BufferedImage image = service.genQrPay(payOrder); /*-----------/扫码付-------------------*/ diff --git a/pay-java-union/pom.xml b/pay-java-union/pom.xml index f5a4bbe..a477d65 100644 --- a/pay-java-union/pom.xml +++ b/pay-java-union/pom.xml @@ -5,7 +5,7 @@ pay-java-parent com.egzosn - 2.12.8-SNAPSHOT + 2.12.8 4.0.0 diff --git a/pay-java-wx-youdian/README.md b/pay-java-wx-youdian/README.md index b5afadf..89445d1 100644 --- a/pay-java-wx-youdian/README.md +++ b/pay-java-wx-youdian/README.md @@ -62,6 +62,7 @@ /*-----------扫码付-------------------*/ payOrder.setTransactionType(YoudianTransactionType.NATIVE); //获取扫码付的二维码 +// String image = service.getQrPay(payOrder); BufferedImage image = service.genQrPay(payOrder); /*-----------/扫码付-------------------*/ diff --git a/pay-java-wx-youdian/pom.xml b/pay-java-wx-youdian/pom.xml index 0e1cac4..5d95127 100644 --- a/pay-java-wx-youdian/pom.xml +++ b/pay-java-wx-youdian/pom.xml @@ -5,7 +5,7 @@ pay-java-parent com.egzosn - 2.12.8-SNAPSHOT + 2.12.8 4.0.0 pay-java-wx-youdian diff --git a/pay-java-wx/README.md b/pay-java-wx/README.md index 94eb9c0..d98f264 100644 --- a/pay-java-wx/README.md +++ b/pay-java-wx/README.md @@ -93,6 +93,7 @@ /*-----------扫码付-------------------*/ payOrder.setTransactionType(WxTransactionType.NATIVE); //获取扫码付的二维码 +// String image = service.getQrPay(payOrder); BufferedImage image = service.genQrPay(payOrder); /*-----------/扫码付-------------------*/ diff --git a/pay-java-wx/pom.xml b/pay-java-wx/pom.xml index 466491e..4d52037 100644 --- a/pay-java-wx/pom.xml +++ b/pay-java-wx/pom.xml @@ -5,7 +5,7 @@ pay-java-parent com.egzosn - 2.12.8-SNAPSHOT + 2.12.8 4.0.0 pay-java-wx diff --git a/pay-java-yiji/pom.xml b/pay-java-yiji/pom.xml index 40b78ce..fb5987b 100644 --- a/pay-java-yiji/pom.xml +++ b/pay-java-yiji/pom.xml @@ -5,7 +5,7 @@ pay-java-parent com.egzosn - 2.12.8-SNAPSHOT + 2.12.8 4.0.0 diff --git a/pom.xml b/pom.xml index 905521c..50c0ce6 100644 --- a/pom.xml +++ b/pom.xml @@ -7,7 +7,7 @@ com.egzosn pay-java-parent pom - 2.12.8-SNAPSHOT + 2.12.8 Pay Java - Parent Pay Java Parent @@ -58,7 +58,7 @@ - 2.12.8-SNAPSHOT + 2.12.8 4.5.4 1.2.17 1.2.58 -- Gitee From b28dc3a5065afe144aaec186141c059231df7d67 Mon Sep 17 00:00:00 2001 From: egzosn Date: Sun, 18 Aug 2019 21:49:07 +0800 Subject: [PATCH 052/299] =?UTF-8?q?=E5=B8=AE=E5=8A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index caa87bf..40cdb3f 100644 --- a/README.md +++ b/README.md @@ -29,7 +29,7 @@ * 码云:https://gitee.com/egzosn/pay-java-parent * GitHub:https://github.com/egzosn/pay-java-parent -#### spring-boot-starter-pay 是一个基于spring-boot实现自动化配置的支付对接,让你可以不用理解支付怎么对接,只需要专注你的业务 +#### 基于spring-boot实现自动化配置的支付对接,让你真正做到一行代码实现支付聚合,让你可以不用理解支付怎么对接,只需要专注你的业务 全能第三方支付对接spring-boot-starter-pay开发工具包 * 码云:https://gitee.com/egzosn/pay-spring-boot-starter-parent * GitHub:https://github.com/egzosn/pay-spring-boot-starter-parent -- Gitee From b7527183a81d477e799b989667b0ec092122aeed Mon Sep 17 00:00:00 2001 From: egzosn Date: Thu, 22 Aug 2019 18:35:24 +0800 Subject: [PATCH 053/299] =?UTF-8?q?=E5=BC=80=E6=BA=90=E4=B8=AD=E5=9B=BD?= =?UTF-8?q?=E9=A1=B9=E7=9B=AE=E8=BF=9E=E6=8E=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 40cdb3f..9c4c785 100644 --- a/README.md +++ b/README.md @@ -33,9 +33,10 @@ * 码云:https://gitee.com/egzosn/pay-spring-boot-starter-parent * GitHub:https://github.com/egzosn/pay-spring-boot-starter-parent -### 使用 -这里不多说直接上代码 - +##### 开源中国项目地址 +如果你觉得项目对你有帮助,也点击下进入后点击收藏呗 +* 基础支付聚合组件[pay-java-parent](https://www.oschina.net/p/pay-java-parent) +* spring-boot-starter自动化配置支付聚合组件 [pay-spring-boot-starter](https://www.oschina.net/p/spring-boot-starter-pay) ###### 支付教程 -- Gitee From fdf5df5d5f86c908bb7e3ce7b6fb2659a0b20935 Mon Sep 17 00:00:00 2001 From: egzosn Date: Thu, 19 Sep 2019 19:19:49 +0800 Subject: [PATCH 054/299] =?UTF-8?q?=E5=BC=80=E6=BA=90=E4=B8=AD=E5=9B=BD?= =?UTF-8?q?=E9=A1=B9=E7=9B=AE=E8=BF=9E=E6=8E=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pom.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/pom.xml b/pom.xml index 50c0ce6..0e84e67 100644 --- a/pom.xml +++ b/pom.xml @@ -166,6 +166,7 @@ org.apache.maven.plugins maven-source-plugin + 3.1.0 attach-sources -- Gitee From ad7efdce9d8de749481b4852d324df80c0fe2a29 Mon Sep 17 00:00:00 2001 From: egzosn Date: Tue, 8 Oct 2019 22:05:53 +0800 Subject: [PATCH 055/299] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E9=A2=84=E8=AE=A2?= =?UTF-8?q?=E5=8D=95=E5=9B=9E=E8=B0=83=E5=A4=84=E7=90=86=EF=BC=8C=E7=94=A8?= =?UTF-8?q?=E4=BA=8E=E6=89=A9=E5=B1=95=E4=BF=A1=E6=81=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../egzosn/pay/ali/before/api/AliPayService.java | 4 ++-- .../com/egzosn/pay/common/api/BasePayService.java | 13 +++++++++++++ .../com/egzosn/pay/fuiou/api/FuiouPayService.java | 7 ++++--- .../egzosn/pay/payoneer/api/PayoneerPayService.java | 2 +- .../com/egzosn/pay/paypal/api/PayPalPayService.java | 3 ++- .../com/egzosn/pay/union/api/UnionPayService.java | 2 +- .../pay/wx/youdian/api/WxYouDianPayService.java | 4 +++- .../java/com/egzosn/pay/wx/api/WxPayService.java | 10 ++++++---- .../com/egzosn/pay/yiji/api/YiJiPayService.java | 3 ++- 9 files changed, 34 insertions(+), 14 deletions(-) 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 d1f4b37..ae9c93b 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 @@ -153,7 +153,7 @@ public class AliPayService extends BasePayService { @Override public Map orderInfo(PayOrder order) { - Map orderInfo = getOrder(order); + Map orderInfo = getOrder(order); String sign = null; if (AliTransactionType.APP == order.getTransactionType() ){ @@ -233,7 +233,7 @@ public class AliPayService extends BasePayService { // 支付宝处理完请求后,当前页面跳转到商户指定页面的路径,可空 orderInfo.put("return_url", payConfigStorage.getReturnUrl()); - return orderInfo; + return preOrderHandler(orderInfo, order); } diff --git a/pay-java-common/src/main/java/com/egzosn/pay/common/api/BasePayService.java b/pay-java-common/src/main/java/com/egzosn/pay/common/api/BasePayService.java index 509a2f3..53d5c90 100644 --- a/pay-java-common/src/main/java/com/egzosn/pay/common/api/BasePayService.java +++ b/pay-java-common/src/main/java/com/egzosn/pay/common/api/BasePayService.java @@ -454,4 +454,17 @@ public abstract class BasePayService implements Pay public PayMessage createMessage(Map message) { return new PayMessage(message); } + + /** + * 预订单回调处理器,用于订单信息的扩展 + * 签名之前使用 + * 如果需要进行扩展请重写该方法即可 + * @param orderInfo 预订单信息 + * @param orderInfo 订单信息 + * @return 处理后订单信息 + */ + public Map preOrderHandler(Map orderInfo, PayOrder payOrder){ + return orderInfo; + } + } diff --git a/pay-java-fuiou/src/main/java/com/egzosn/pay/fuiou/api/FuiouPayService.java b/pay-java-fuiou/src/main/java/com/egzosn/pay/fuiou/api/FuiouPayService.java index b5109dd..d297c9d 100644 --- a/pay-java-fuiou/src/main/java/com/egzosn/pay/fuiou/api/FuiouPayService.java +++ b/pay-java-fuiou/src/main/java/com/egzosn/pay/fuiou/api/FuiouPayService.java @@ -62,6 +62,7 @@ public class FuiouPayService extends BasePayService { * 获取对应的请求地址 * @return 请求地址 */ + @Override public String getReqUrl(TransactionType transactionType){ return payConfigStorage.isTest() ? DEV_URL_FUIOU_BASE_DOMAIN : URL_FUIOU_BASE_DOMAIN; } @@ -152,7 +153,7 @@ public class FuiouPayService extends BasePayService { */ @Override public Map orderInfo(PayOrder order) { - LinkedHashMap parameters = getOrderInfo(order); + Map parameters = getOrderInfo(order); String sign = createSign(SignUtils.parameters2MD5Str(parameters, "|"), payConfigStorage.getInputCharset()); parameters.put("md5", sign); return parameters; @@ -163,7 +164,7 @@ public class FuiouPayService extends BasePayService { * @param order 支付订单 * @return 返回支付请求参数集合 */ - private LinkedHashMap getOrderInfo(PayOrder order) { + private Map getOrderInfo(PayOrder order) { LinkedHashMap parameters = new LinkedHashMap(); //商户代码 parameters.put("mchnt_cd", payConfigStorage.getPid()); @@ -195,7 +196,7 @@ public class FuiouPayService extends BasePayService { parameters.put("rem", ""); //版本号 parameters.put("ver", "1.0.1"); - return parameters; + return preOrderHandler(parameters, order); } /** diff --git a/pay-java-payoneer/src/main/java/com/egzosn/pay/payoneer/api/PayoneerPayService.java b/pay-java-payoneer/src/main/java/com/egzosn/pay/payoneer/api/PayoneerPayService.java index c83766f..4d61b73 100644 --- a/pay-java-payoneer/src/main/java/com/egzosn/pay/payoneer/api/PayoneerPayService.java +++ b/pay-java-payoneer/src/main/java/com/egzosn/pay/payoneer/api/PayoneerPayService.java @@ -185,7 +185,7 @@ public class PayoneerPayService extends BasePayService im params.put("currency", order.getCurType()); params.put("description", order.getSubject()); - return params; + return preOrderHandler(params, order); } /** diff --git a/pay-java-paypal/src/main/java/com/egzosn/pay/paypal/api/PayPalPayService.java b/pay-java-paypal/src/main/java/com/egzosn/pay/paypal/api/PayPalPayService.java index eeec14f..b94f887 100644 --- a/pay-java-paypal/src/main/java/com/egzosn/pay/paypal/api/PayPalPayService.java +++ b/pay-java-paypal/src/main/java/com/egzosn/pay/paypal/api/PayPalPayService.java @@ -44,6 +44,7 @@ public class PayPalPayService extends BasePayService{ * 获取对应的请求地址 * @return 请求地址 */ + @Override public String getReqUrl(TransactionType transactionType){ return (payConfigStorage.isTest() ? SANDBOX_REQ_URL : REQ_URL) + transactionType.getMethod(); } @@ -182,7 +183,7 @@ public class PayPalPayService extends BasePayService{ if ("created".equals(resp.getString("state")) && StringUtils.isNotEmpty(resp.getString("id"))){ order.setOutTradeNo(resp.getString("id")); } - return resp; + return preOrderHandler(resp, order); } @Override diff --git a/pay-java-union/src/main/java/com/egzosn/pay/union/api/UnionPayService.java b/pay-java-union/src/main/java/com/egzosn/pay/union/api/UnionPayService.java index c7e0f2e..90aaad6 100644 --- a/pay-java-union/src/main/java/com/egzosn/pay/union/api/UnionPayService.java +++ b/pay-java-union/src/main/java/com/egzosn/pay/union/api/UnionPayService.java @@ -288,7 +288,7 @@ public class UnionPayService extends BasePayService { params.put(SDKConstants.param_payTimeout, getPayTimeout(order.getExpirationTime())); params.put("orderDesc", order.getSubject()); } - + params = preOrderHandler(params, order); return setSign(params); } 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 c77dd23..2d7775f 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 @@ -224,9 +224,10 @@ public class WxYouDianPayService extends BasePayService data = new TreeMap<>(); + Map data = new TreeMap<>(); data.put("access_token", getAccessToken()); data.put("paymoney", Util.conversionAmount(order.getPrice()).toString()); + data = preOrderHandler(data, order); String apbNonce = SignUtils.randomStr(); String sign = createSign(SignUtils.parameterText(data, "") + apbNonce, payConfigStorage.getInputCharset()); data.put("PayMoney", data.remove("paymoney")); @@ -460,6 +461,7 @@ public class WxYouDianPayService extends BasePayService { * @param transactionType 交易类型 * @return 请求url */ + @Override public String getReqUrl(TransactionType transactionType) { return URI + (payConfigStorage.isTest() ? SANDBOXNEW : "") + transactionType.getMethod(); @@ -221,13 +222,14 @@ public class WxPayService extends BasePayService { parameters.put("time_expire", DateUtils.formatDate(order.getExpirationTime(), DateUtils.YYYYMMDDHHMMSS)); } ((WxTransactionType) order.getTransactionType()).setAttribute(parameters, order); - + parameters = preOrderHandler(parameters, order); setSign(parameters); String requestXML = XML.getMap2Xml(parameters); if (LOG.isDebugEnabled()) { LOG.debug("requestXML:" + requestXML); } + //调起支付的参数列表 JSONObject result = requestTemplate.postForObject(getReqUrl(order.getTransactionType()), requestXML, JSONObject.class); @@ -250,15 +252,14 @@ public class WxPayService extends BasePayService { ////统一下单 JSONObject result = unifiedOrder(order); - // 对微信返回的数据进行校验 - if (verify(result)) { + if (verify(preOrderHandler(result, order))) { //如果是扫码支付或者刷卡付无需处理,直接返回 if (((WxTransactionType) order.getTransactionType()).isReturn()) { return result; } - SortedMap params = new TreeMap(); + Map params = new TreeMap(); if (WxTransactionType.JSAPI == order.getTransactionType()) { params.put("signType", payConfigStorage.getSignType()); @@ -274,6 +275,7 @@ public class WxPayService extends BasePayService { params.put("noncestr", result.get(NONCE_STR)); params.put("package", "Sign=WXPay"); } + params = preOrderHandler(params, order); String paySign = createSign(SignUtils.parameterText(params), payConfigStorage.getInputCharset()); params.put(SIGN, paySign); return params; diff --git a/pay-java-yiji/src/main/java/com/egzosn/pay/yiji/api/YiJiPayService.java b/pay-java-yiji/src/main/java/com/egzosn/pay/yiji/api/YiJiPayService.java index 4ea2fa4..cece47f 100644 --- a/pay-java-yiji/src/main/java/com/egzosn/pay/yiji/api/YiJiPayService.java +++ b/pay-java-yiji/src/main/java/com/egzosn/pay/yiji/api/YiJiPayService.java @@ -53,6 +53,7 @@ public class YiJiPayService extends BasePayService { * * @return 请求地址 */ + @Override public String getReqUrl(TransactionType transactionType) { if (payConfigStorage.isTest()){ return DEV_REQ_URL; @@ -175,7 +176,7 @@ public class YiJiPayService extends BasePayService { orderInfo.put("currency", order.getCurType()); } - return orderInfo; + return preOrderHandler(orderInfo, order); } /** -- Gitee From afc328c182b4fa1fc1976a392e6b5f6f8da110f4 Mon Sep 17 00:00:00 2001 From: egzosn Date: Tue, 8 Oct 2019 22:08:08 +0800 Subject: [PATCH 056/299] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E9=A2=84=E8=AE=A2?= =?UTF-8?q?=E5=8D=95=E5=9B=9E=E8=B0=83=E5=A4=84=E7=90=86=EF=BC=8C=E7=94=A8?= =?UTF-8?q?=E4=BA=8E=E6=89=A9=E5=B1=95=E4=BF=A1=E6=81=AF=EF=BC=8C=E6=94=AF?= =?UTF-8?q?=E4=BB=98=E5=AE=9DAPP=E7=A7=BB=E9=99=A4=E5=90=8C=E6=AD=A5?= =?UTF-8?q?=E5=9B=9E=E8=B0=83?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/main/java/com/egzosn/pay/ali/api/AliPayService.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) 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 6c23d92..9af574d 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 @@ -67,6 +67,7 @@ public class AliPayService extends BasePayService { * * @return 请求地址 */ + @Override public String getReqUrl(TransactionType transactionType) { return payConfigStorage.isTest() ? DEV_REQ_URL : HTTPS_REQ_URL; } @@ -214,7 +215,6 @@ public class AliPayService extends BasePayService { case APP: bizContent.put(PASSBACK_PARAMS, order.getAddition()); bizContent.put(PRODUCT_CODE, "QUICK_MSECURITY_PAY"); - orderInfo.put(RETURN_URL, payConfigStorage.getReturnUrl()); break; case BAR_CODE: case WAVE_CODE: @@ -228,7 +228,8 @@ public class AliPayService extends BasePayService { bizContent.put("timeout_express", DateUtils.minutesRemaining(order.getExpirationTime()) + "m"); } orderInfo.put(BIZ_CONTENT, JSON.toJSONString(bizContent)); - return orderInfo; + + return preOrderHandler(orderInfo, order); } /** -- Gitee From 7171f4d003727741a5954e96ca0f309aacb67124 Mon Sep 17 00:00:00 2001 From: egzosn Date: Tue, 8 Oct 2019 22:15:19 +0800 Subject: [PATCH 057/299] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E9=A2=84=E8=AE=A2?= =?UTF-8?q?=E5=8D=95=E5=9B=9E=E8=B0=83=E5=A4=84=E7=90=86=EF=BC=8C=E7=94=A8?= =?UTF-8?q?=E4=BA=8E=E6=89=A9=E5=B1=95=E4=BF=A1=E6=81=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pay-java-ali/pom.xml | 2 +- pay-java-common/pom.xml | 2 +- pay-java-demo/pom.xml | 2 +- pay-java-fuiou/pom.xml | 2 +- pay-java-payoneer/pom.xml | 2 +- pay-java-paypal/pom.xml | 2 +- pay-java-union/pom.xml | 2 +- pay-java-wx-youdian/pom.xml | 2 +- pay-java-wx/pom.xml | 2 +- pay-java-yiji/pom.xml | 2 +- pom.xml | 4 ++-- 11 files changed, 12 insertions(+), 12 deletions(-) diff --git a/pay-java-ali/pom.xml b/pay-java-ali/pom.xml index 30ae700..2683373 100644 --- a/pay-java-ali/pom.xml +++ b/pay-java-ali/pom.xml @@ -5,7 +5,7 @@ pay-java-parent com.egzosn - 2.12.8 + 2.12.9-SNAPSHOT 4.0.0 pay-java-ali diff --git a/pay-java-common/pom.xml b/pay-java-common/pom.xml index fad81d5..ea25260 100644 --- a/pay-java-common/pom.xml +++ b/pay-java-common/pom.xml @@ -5,7 +5,7 @@ pay-java-parent com.egzosn - 2.12.8 + 2.12.9-SNAPSHOT 4.0.0 jar diff --git a/pay-java-demo/pom.xml b/pay-java-demo/pom.xml index d21f59e..1f47a60 100644 --- a/pay-java-demo/pom.xml +++ b/pay-java-demo/pom.xml @@ -5,7 +5,7 @@ pay-java-parent com.egzosn - 2.12.8 + 2.12.9-SNAPSHOT 4.0.0 war diff --git a/pay-java-fuiou/pom.xml b/pay-java-fuiou/pom.xml index a51c075..15c9e85 100644 --- a/pay-java-fuiou/pom.xml +++ b/pay-java-fuiou/pom.xml @@ -5,7 +5,7 @@ pay-java-parent com.egzosn - 2.12.8 + 2.12.9-SNAPSHOT 4.0.0 pay-java-fuiou diff --git a/pay-java-payoneer/pom.xml b/pay-java-payoneer/pom.xml index c9c4936..e8a7e2e 100644 --- a/pay-java-payoneer/pom.xml +++ b/pay-java-payoneer/pom.xml @@ -5,7 +5,7 @@ pay-java-parent com.egzosn - 2.12.8 + 2.12.9-SNAPSHOT 4.0.0 pay-java-payoneer diff --git a/pay-java-paypal/pom.xml b/pay-java-paypal/pom.xml index bd05591..3cdb33f 100644 --- a/pay-java-paypal/pom.xml +++ b/pay-java-paypal/pom.xml @@ -5,7 +5,7 @@ pay-java-parent com.egzosn - 2.12.8 + 2.12.9-SNAPSHOT 4.0.0 diff --git a/pay-java-union/pom.xml b/pay-java-union/pom.xml index a477d65..6310983 100644 --- a/pay-java-union/pom.xml +++ b/pay-java-union/pom.xml @@ -5,7 +5,7 @@ pay-java-parent com.egzosn - 2.12.8 + 2.12.9-SNAPSHOT 4.0.0 diff --git a/pay-java-wx-youdian/pom.xml b/pay-java-wx-youdian/pom.xml index 5d95127..610977d 100644 --- a/pay-java-wx-youdian/pom.xml +++ b/pay-java-wx-youdian/pom.xml @@ -5,7 +5,7 @@ pay-java-parent com.egzosn - 2.12.8 + 2.12.9-SNAPSHOT 4.0.0 pay-java-wx-youdian diff --git a/pay-java-wx/pom.xml b/pay-java-wx/pom.xml index 4d52037..bee89ca 100644 --- a/pay-java-wx/pom.xml +++ b/pay-java-wx/pom.xml @@ -5,7 +5,7 @@ pay-java-parent com.egzosn - 2.12.8 + 2.12.9-SNAPSHOT 4.0.0 pay-java-wx diff --git a/pay-java-yiji/pom.xml b/pay-java-yiji/pom.xml index fb5987b..7ecd98b 100644 --- a/pay-java-yiji/pom.xml +++ b/pay-java-yiji/pom.xml @@ -5,7 +5,7 @@ pay-java-parent com.egzosn - 2.12.8 + 2.12.9-SNAPSHOT 4.0.0 diff --git a/pom.xml b/pom.xml index 0e84e67..689af9a 100644 --- a/pom.xml +++ b/pom.xml @@ -7,7 +7,7 @@ com.egzosn pay-java-parent pom - 2.12.8 + 2.12.9-SNAPSHOT Pay Java - Parent Pay Java Parent @@ -58,7 +58,7 @@ - 2.12.8 + 2.12.9-SNAPSHOT 4.5.4 1.2.17 1.2.58 -- Gitee From 409ed1df61cdd6afcbeb31e8da8dcf753cf102c0 Mon Sep 17 00:00:00 2001 From: egzosn Date: Sun, 13 Oct 2019 23:20:57 +0800 Subject: [PATCH 058/299] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E4=B8=A4=E7=A7=8D?= =?UTF-8?q?=E8=AF=81=E4=B9=A6=E8=8E=B7=E5=8F=96=E6=96=B9=E5=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/egzosn/pay/common/api/CertStore.java | 22 +++++++ .../egzosn/pay/common/bean/CertStoreType.java | 57 ++++++++++++++++--- 2 files changed, 71 insertions(+), 8 deletions(-) create mode 100644 pay-java-common/src/main/java/com/egzosn/pay/common/api/CertStore.java diff --git a/pay-java-common/src/main/java/com/egzosn/pay/common/api/CertStore.java b/pay-java-common/src/main/java/com/egzosn/pay/common/api/CertStore.java new file mode 100644 index 0000000..cb119a4 --- /dev/null +++ b/pay-java-common/src/main/java/com/egzosn/pay/common/api/CertStore.java @@ -0,0 +1,22 @@ +package com.egzosn.pay.common.api; + +import java.io.IOException; +import java.io.InputStream; + +/** + * 证书存储方式 + * @author egan + * email egzosn@gmail.com + * date 2019/10/13.23:09 + */ +public interface CertStore { + + /** + * 证书信息转化为对应的输入流 + * + * @param cert 证书信息 + * @return 输入流 + * @throws IOException 找不到文件异常 + */ + public abstract InputStream getInputStream(Object cert) throws IOException; +} diff --git a/pay-java-common/src/main/java/com/egzosn/pay/common/bean/CertStoreType.java b/pay-java-common/src/main/java/com/egzosn/pay/common/bean/CertStoreType.java index 0b7ebba..d7b6895 100644 --- a/pay-java-common/src/main/java/com/egzosn/pay/common/bean/CertStoreType.java +++ b/pay-java-common/src/main/java/com/egzosn/pay/common/bean/CertStoreType.java @@ -1,5 +1,8 @@ package com.egzosn.pay.common.bean; +import com.egzosn.pay.common.api.CertStore; +import com.egzosn.pay.common.http.HttpRequestTemplate; + import java.io.*; /** @@ -9,7 +12,7 @@ import java.io.*; * email egzosn@gmail.com * date 2019/4/14.23:04 */ -public enum CertStoreType { +public enum CertStoreType implements CertStore { /** * 路径,建议绝对路径 @@ -59,15 +62,53 @@ public enum CertStoreType { public InputStream getInputStream(Object cert) throws IOException { return (InputStream) cert; } - }; + }, + + /** + * URL获取的方式 + */ + URL { + /** + * 证书信息转化为对应的输入流 + * + * @param url 获取证书信息的URL + * @return 输入流 + * @throws IOException 找不到文件异常 + */ + @Override + public InputStream getInputStream(Object url) throws IOException { + return new HttpRequestTemplate().getForObject((String) url, InputStream.class); + } + }, /** - * 证书信息转化为对应的输入流 - * - * @param cert 证书信息 - * @return 输入流 - * @throws IOException 找不到文件异常 + * URL获取的方式 */ - public abstract InputStream getInputStream(Object cert) throws IOException; + BEAN { + /** + * 证书信息转化为对应的输入流 + * + * @param beanClazz 获取证书信息的类路径(字符串),该类必须实现{@link CertStore} + * @return 输入流 + * @throws IOException 找不到文件异常 + */ + @Override + public InputStream getInputStream(Object beanClazz) throws IOException { + try { + Class clazz = Class.forName((String) beanClazz); + CertStore certStore = (CertStore)clazz.newInstance(); + return certStore.getInputStream(beanClazz); + } catch (ClassNotFoundException e) { + e.printStackTrace(); + } catch (IllegalAccessException e) { + e.printStackTrace(); + } catch (InstantiationException e) { + e.printStackTrace(); + } + return null; + } + }; + + } -- Gitee From 8628da1f822be79e3b49752040b405707c532e44 Mon Sep 17 00:00:00 2001 From: egzosn Date: Sat, 19 Oct 2019 17:33:30 +0800 Subject: [PATCH 059/299] =?UTF-8?q?=E8=AF=B7=E6=B1=82=E5=B7=A5=E5=85=B7?= =?UTF-8?q?=E7=B1=BB=E5=9E=8B=E8=BD=AC=E6=8D=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pay-java-ali/pom.xml | 10 ++++++++ .../pay/common/http/ClientHttpRequest.java | 25 +++++++++++++------ pay-java-demo/pom.xml | 2 +- .../demo/controller/UnionPayController.java | 8 +++--- pom.xml | 6 ++--- 5 files changed, 36 insertions(+), 15 deletions(-) diff --git a/pay-java-ali/pom.xml b/pay-java-ali/pom.xml index 2683373..dae4fff 100644 --- a/pay-java-ali/pom.xml +++ b/pay-java-ali/pom.xml @@ -12,6 +12,11 @@ + + com.alipay.sdk + alipay-sdk-java + 4.8.10.ALL + @@ -19,6 +24,11 @@ pay-java-common + + org.bouncycastle + bcprov-jdk15on + 1.54 + 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 a9ffdec..bfc799b 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 @@ -19,9 +19,7 @@ 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.*; import java.net.URI; import java.nio.charset.Charset; import java.util.Map; @@ -352,14 +350,27 @@ public class ClientHttpRequest extends HttpEntityEnclosingRequestBase impleme //是否为 输入流 if (InputStream.class.isAssignableFrom(responseType)) { - return (T) entity.getContent(); + ByteArrayOutputStream os = new ByteArrayOutputStream(); + entity.writeTo(os); + return (T) new ByteArrayInputStream(os.toByteArray()); + } + //是否为 字节数数组 + if (byte[].class.isAssignableFrom(responseType)) { + ByteArrayOutputStream os = new ByteArrayOutputStream(); + entity.writeTo(os); + return (T) os.toByteArray(); } //输出流 if (OutputStream.class.isAssignableFrom(responseType)) { try { - T t = responseType.newInstance(); - entity.writeTo((OutputStream) t); - return t; + OutputStream t; + if (responseType == OutputStream.class){ + t= new ByteArrayOutputStream(); + }else { + t = (OutputStream) responseType.newInstance(); + } + entity.writeTo( t); + return (T) t; } catch (InstantiationException e) { throw new PayErrorException(new PayException("InstantiationException", e.getMessage())); } catch (IllegalAccessException e) { diff --git a/pay-java-demo/pom.xml b/pay-java-demo/pom.xml index 1f47a60..144cc7f 100644 --- a/pay-java-demo/pom.xml +++ b/pay-java-demo/pom.xml @@ -86,7 +86,7 @@ com.fasterxml.jackson.core jackson-databind - [2.9.9,) + 2.9.10 diff --git a/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/UnionPayController.java b/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/UnionPayController.java index d48f0ed..f751cbe 100644 --- a/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/UnionPayController.java +++ b/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/UnionPayController.java @@ -48,15 +48,15 @@ public class UnionPayController { //是否为证书签名 unionPayConfigStorage.setCertSign(true); //中级证书路径 - unionPayConfigStorage.setAcpMiddleCert("D:/certs/acp_test_middle.cer"); + unionPayConfigStorage.setAcpMiddleCert("http://www.egzosn.com/certs/acp_test_middle.cer"); //根证书路径 - unionPayConfigStorage.setAcpRootCert("D:/certs/acp_test_root.cer"); + unionPayConfigStorage.setAcpRootCert("http://www.egzosn.com/certs/acp_test_root.cer"); // 私钥证书路径 - unionPayConfigStorage.setKeyPrivateCert("D:/certs/acp_test_sign.pfx"); + unionPayConfigStorage.setKeyPrivateCert("http://www.egzosn.com/certs/acp_test_sign.pfx"); //私钥证书对应的密码 unionPayConfigStorage.setKeyPrivateCertPwd("000000"); //设置证书对应的存储方式,这里默认为文件地址 - unionPayConfigStorage.setCertStoreType(CertStoreType.PATH); + unionPayConfigStorage.setCertStoreType(CertStoreType.URL); diff --git a/pom.xml b/pom.xml index 689af9a..775e767 100644 --- a/pom.xml +++ b/pom.xml @@ -176,20 +176,20 @@ - + \ No newline at end of file -- Gitee From 891d128b0d903c02f5389f7cea41bdf045449fa8 Mon Sep 17 00:00:00 2001 From: egzosn Date: Tue, 22 Oct 2019 13:56:06 +0800 Subject: [PATCH 060/299] =?UTF-8?q?=E7=A7=BB=E9=99=A4=E4=B8=8D=E5=BF=85?= =?UTF-8?q?=E8=A6=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pay-java-ali/pom.xml | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/pay-java-ali/pom.xml b/pay-java-ali/pom.xml index dae4fff..942190e 100644 --- a/pay-java-ali/pom.xml +++ b/pay-java-ali/pom.xml @@ -12,11 +12,6 @@ - - com.alipay.sdk - alipay-sdk-java - 4.8.10.ALL - @@ -24,11 +19,12 @@ pay-java-common + -- Gitee From 2101706c7a50c3bc5ca2d8e0a788e40e02003136 Mon Sep 17 00:00:00 2001 From: egzosn Date: Thu, 7 Nov 2019 11:32:21 +0800 Subject: [PATCH 061/299] =?UTF-8?q?=E7=A7=BB=E9=99=A4=E4=B8=8D=E5=BF=85?= =?UTF-8?q?=E8=A6=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 2 ++ wx.jpg | Bin 0 -> 78746 bytes 2 files changed, 2 insertions(+) create mode 100644 wx.jpg diff --git a/README.md b/README.md index 9c4c785..5eeef11 100644 --- a/README.md +++ b/README.md @@ -67,3 +67,5 @@ E-Mail:egzosn@gmail.com QQ群:542193977 +微信群: ![image](https://gitee.com/egzosn/pay-java-parent/blob/master/wx.jpg?raw=true) + diff --git a/wx.jpg b/wx.jpg new file mode 100644 index 0000000000000000000000000000000000000000..4332207c6d81f5ed0c161c6e87e630d5831bf33b GIT binary patch literal 78746 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 Date: Thu, 7 Nov 2019 11:34:10 +0800 Subject: [PATCH 062/299] wx --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 5eeef11..3207a68 100644 --- a/README.md +++ b/README.md @@ -67,5 +67,5 @@ E-Mail:egzosn@gmail.com QQ群:542193977 -微信群: ![image](https://gitee.com/egzosn/pay-java-parent/blob/master/wx.jpg?raw=true) +微信群: ![wx](https://images.gitee.com/uploads/images/2019/1107/113350_43cf64ef_1221178.jpeg "wx.jpg") -- Gitee From 40b25e3e2a7e78f45bd27428d22a4c7209fed63e Mon Sep 17 00:00:00 2001 From: egan Date: Thu, 7 Nov 2019 12:15:58 +0800 Subject: [PATCH 063/299] wx --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 3207a68..c3c5545 100644 --- a/README.md +++ b/README.md @@ -67,5 +67,5 @@ E-Mail:egzosn@gmail.com QQ群:542193977 -微信群: ![wx](https://images.gitee.com/uploads/images/2019/1107/113350_43cf64ef_1221178.jpeg "wx.jpg") +微信群: ![微信群](https://images.gitee.com/uploads/images/2019/1107/121546_6dbab7f7_1221178.jpeg "wx.jpg") -- Gitee From d5cac5dc01fc2c08c71e81641b40439bd3071d00 Mon Sep 17 00:00:00 2001 From: egan Date: Sun, 17 Nov 2019 15:04:42 +0800 Subject: [PATCH 064/299] =?UTF-8?q?=E5=BE=AE=E4=BF=A1=E7=BE=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/README.md b/README.md index c3c5545..a7a7ec9 100644 --- a/README.md +++ b/README.md @@ -67,5 +67,4 @@ E-Mail:egzosn@gmail.com QQ群:542193977 -微信群: ![微信群](https://images.gitee.com/uploads/images/2019/1107/121546_6dbab7f7_1221178.jpeg "wx.jpg") - +微信群: ![微信群](https://images.gitee.com/uploads/images/2019/1117/150348_7dcb83cd_1221178.jpeg "wx.jpg") -- Gitee From 3ca4ceb639e168db8f7b125abdd1df1bd7ed58a4 Mon Sep 17 00:00:00 2001 From: egan Date: Sun, 17 Nov 2019 15:20:54 +0800 Subject: [PATCH 065/299] =?UTF-8?q?=E5=BE=AE=E4=BF=A1=E7=BE=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index a7a7ec9..ef5cfc9 100644 --- a/README.md +++ b/README.md @@ -67,4 +67,4 @@ E-Mail:egzosn@gmail.com QQ群:542193977 -微信群: ![微信群](https://images.gitee.com/uploads/images/2019/1117/150348_7dcb83cd_1221178.jpeg "wx.jpg") +微信群: ![微信群](https://images.gitee.com/uploads/images/2019/1117/151422_5085eaae_1221178.jpeg "wx.jpg") -- Gitee From 2c998e9cf59876e5649a34d197c16848eaa7f5db Mon Sep 17 00:00:00 2001 From: egzosn Date: Sun, 17 Nov 2019 16:57:14 +0800 Subject: [PATCH 066/299] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E6=94=AF=E4=BB=98?= =?UTF-8?q?=E8=AE=A2=E5=8D=95=E6=B3=9B=E5=9E=8B=E7=94=A8=E4=BA=8E=E5=BC=80?= =?UTF-8?q?=E5=8F=91=E8=80=85=E6=89=A9=E5=B1=95=E8=AE=A2=E5=8D=95=E5=8F=82?= =?UTF-8?q?=E6=95=B0=E4=BF=A1=E6=81=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/egzosn/pay/ali/api/AliPayService.java | 4 +-- .../com/egzosn/pay/ali/bean/OrderSettle.java | 2 +- .../pay/ali/before/api/AliPayService.java | 2 +- .../egzosn/pay/common/api/BasePayService.java | 8 +++--- .../com/egzosn/pay/common/api/PayService.java | 23 +++++++++++++----- .../egzosn/pay/fuiou/api/FuiouPayService.java | 8 +++--- .../pay/payoneer/api/AdvancedPayService.java | 3 ++- .../pay/payoneer/api/PayoneerPayService.java | 2 +- .../pay/paypal/api/PayPalPayService.java | 2 +- .../egzosn/pay/union/api/UnionPayService.java | 2 +- .../wx/youdian/api/WxYouDianPayService.java | 2 +- .../com/egzosn/pay/wx/api/WxPayService.java | 2 +- .../egzosn/pay/yiji/api/YiJiPayService.java | 2 +- wx.jpg | Bin 78746 -> 0 bytes 14 files changed, 36 insertions(+), 26 deletions(-) delete mode 100644 wx.jpg 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 9af574d..a0e76c4 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 @@ -12,11 +12,9 @@ import com.egzosn.pay.common.exception.PayErrorException; import com.egzosn.pay.common.http.HttpConfigStorage; import com.egzosn.pay.common.http.UriVariables; import com.egzosn.pay.common.util.DateUtils; -import com.egzosn.pay.common.util.MatrixToImageWriter; import com.egzosn.pay.common.util.Util; import com.egzosn.pay.common.util.sign.SignUtils; import com.egzosn.pay.common.util.str.StringUtils; -import java.awt.image.BufferedImage; import java.math.BigDecimal; import java.util.*; @@ -28,7 +26,7 @@ import java.util.*; * email egzosn@gmail.com * date 2017-2-22 20:09 */ -public class AliPayService extends BasePayService { +public class AliPayService extends BasePayService { /** * 正式测试环境 diff --git a/pay-java-ali/src/main/java/com/egzosn/pay/ali/bean/OrderSettle.java b/pay-java-ali/src/main/java/com/egzosn/pay/ali/bean/OrderSettle.java index 0f52df3..f4227b9 100644 --- a/pay-java-ali/src/main/java/com/egzosn/pay/ali/bean/OrderSettle.java +++ b/pay-java-ali/src/main/java/com/egzosn/pay/ali/bean/OrderSettle.java @@ -4,11 +4,11 @@ import com.egzosn.pay.common.util.Util; import com.egzosn.pay.common.util.str.StringUtils; import java.math.BigDecimal; -import java.util.HashMap; import java.util.Map; import java.util.TreeMap; /** + * 交易结算信息 * @author egan * email egzosn@gmail.com * date 2019/4/28.20:29 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 ae9c93b..5ad9318 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 @@ -35,7 +35,7 @@ import static com.egzosn.pay.ali.api.AliPayService.SIGN; * @see com.egzosn.pay.ali.api.AliPayService */ @Deprecated -public class AliPayService extends BasePayService { +public class AliPayService extends BasePayService { diff --git a/pay-java-common/src/main/java/com/egzosn/pay/common/api/BasePayService.java b/pay-java-common/src/main/java/com/egzosn/pay/common/api/BasePayService.java index 53d5c90..ef39af7 100644 --- a/pay-java-common/src/main/java/com/egzosn/pay/common/api/BasePayService.java +++ b/pay-java-common/src/main/java/com/egzosn/pay/common/api/BasePayService.java @@ -26,7 +26,7 @@ import java.util.*; * date 2017/3/5 20:36 * */ -public abstract class BasePayService implements PayService { +public abstract class BasePayService implements PayService { protected final Log LOG = LogFactory.getLog(getClass()); protected PC payConfigStorage; @@ -138,7 +138,7 @@ public abstract class BasePayService implements Pay * @return 对应页面重定向信息 */ @Override - public String toPay(PayOrder order) { + public String toPay(O order) { Map orderInfo = orderInfo(order); return buildRequest(orderInfo, MethodType.POST); } @@ -150,7 +150,7 @@ public abstract class BasePayService implements Pay * @return 返回图片信息,支付时需要的 */ @Override - public BufferedImage genQrPay(PayOrder order) { + public BufferedImage genQrPay(O order) { return MatrixToImageWriter.writeInfoToJpgBuff(getQrPay(order)); } @@ -463,7 +463,7 @@ public abstract class BasePayService implements Pay * @param orderInfo 订单信息 * @return 处理后订单信息 */ - public Map preOrderHandler(Map orderInfo, PayOrder payOrder){ + public Map preOrderHandler(Map orderInfo, O payOrder){ return orderInfo; } diff --git a/pay-java-common/src/main/java/com/egzosn/pay/common/api/PayService.java b/pay-java-common/src/main/java/com/egzosn/pay/common/api/PayService.java index eececb9..7066266 100644 --- a/pay-java-common/src/main/java/com/egzosn/pay/common/api/PayService.java +++ b/pay-java-common/src/main/java/com/egzosn/pay/common/api/PayService.java @@ -20,7 +20,7 @@ import java.util.Map; * date 2016-5-18 14:09:01 * */ -public interface PayService { +public interface PayService { /** @@ -88,7 +88,7 @@ public interface PayService { * @return 订单信息 * @see PayOrder 支付订单信息 */ - Map orderInfo(PayOrder order); + Map orderInfo(O order); /** * 页面转跳支付, 返回对应页面重定向信息 @@ -96,7 +96,7 @@ public interface PayService { * @param order 订单信息 * @return 对应页面重定向信息 */ - String toPay(PayOrder order); + String toPay(O order); /** * 创建签名 @@ -160,14 +160,14 @@ public interface PayService { * @param order 发起支付的订单信息 * @return 返回图片信息,支付时需要的 */ - BufferedImage genQrPay(PayOrder order); + BufferedImage genQrPay(O order); /** * 获取输出二维码信息, * * @param order 发起支付的订单信息 * @return 返回二维码信息,,支付时需要的 */ - String getQrPay(PayOrder order); + String getQrPay(O order); /** * 刷卡付,pos主动扫码付款(条码付) @@ -175,7 +175,7 @@ public interface PayService { * @param order 发起支付的订单信息 * @return 返回支付结果 */ - Map microPay(PayOrder order); + Map microPay(O order); /** * 交易查询接口 @@ -453,4 +453,15 @@ public interface PayService { * @return 支付消息对象 */ PayMessage createMessage(Map message); + + /** + * 预订单回调处理器,用于订单信息的扩展 + * 签名之前使用 + * 如果需要进行扩展请重写该方法即可 + * @param orderInfo 商户平台预订单信息 + * @param payOrder 订单信息 + * @return 处理后订单信息 + */ + Map preOrderHandler(Map orderInfo, O payOrder); + } diff --git a/pay-java-fuiou/src/main/java/com/egzosn/pay/fuiou/api/FuiouPayService.java b/pay-java-fuiou/src/main/java/com/egzosn/pay/fuiou/api/FuiouPayService.java index d297c9d..2c67b2c 100644 --- a/pay-java-fuiou/src/main/java/com/egzosn/pay/fuiou/api/FuiouPayService.java +++ b/pay-java-fuiou/src/main/java/com/egzosn/pay/fuiou/api/FuiouPayService.java @@ -21,7 +21,7 @@ import java.util.*; * create 2017 2017/1/16 0016 * */ -public class FuiouPayService extends BasePayService { +public class FuiouPayService extends BasePayService { /** * 正式域名 @@ -129,14 +129,14 @@ public class FuiouPayService extends BasePayService { /** * 校验回调数据来源是否合法 * - * @param order_id 业务id, 数据的真实性. + * @param orderId 业务id, 数据的真实性. * @return 返回校验结果 */ @Override - public boolean verifySource(String order_id) { + public boolean verifySource(String orderId) { LinkedHashMap params = new LinkedHashMap<>(); params.put("mchnt_cd", payConfigStorage.getPid()); - params.put("order_id", order_id); + params.put("order_id", orderId); params.put("md5", createSign(SignUtils.parameters2MD5Str(params, "|"), payConfigStorage.getInputCharset())); JSONObject resultJson = getHttpRequestTemplate().postForObject(getReqUrl() + URL_FuiouSmpAQueryGate + "?" + UriVariables.getMapToParameters(params), null, JSONObject.class); if (null == resultJson){ diff --git a/pay-java-payoneer/src/main/java/com/egzosn/pay/payoneer/api/AdvancedPayService.java b/pay-java-payoneer/src/main/java/com/egzosn/pay/payoneer/api/AdvancedPayService.java index 9ef4017..76c3a0e 100644 --- a/pay-java-payoneer/src/main/java/com/egzosn/pay/payoneer/api/AdvancedPayService.java +++ b/pay-java-payoneer/src/main/java/com/egzosn/pay/payoneer/api/AdvancedPayService.java @@ -1,6 +1,7 @@ package com.egzosn.pay.payoneer.api; import com.egzosn.pay.common.api.PayService; +import com.egzosn.pay.common.bean.PayOrder; import java.util.Map; @@ -15,7 +16,7 @@ import java.util.Map; * */ -public interface AdvancedPayService extends PayService { +public interface AdvancedPayService extends PayService { /** * 获取授权页面 * @param payeeId 用户id diff --git a/pay-java-payoneer/src/main/java/com/egzosn/pay/payoneer/api/PayoneerPayService.java b/pay-java-payoneer/src/main/java/com/egzosn/pay/payoneer/api/PayoneerPayService.java index 4d61b73..f0f2de8 100644 --- a/pay-java-payoneer/src/main/java/com/egzosn/pay/payoneer/api/PayoneerPayService.java +++ b/pay-java-payoneer/src/main/java/com/egzosn/pay/payoneer/api/PayoneerPayService.java @@ -33,7 +33,7 @@ import java.util.*; * create 2018-01-19 * */ -public class PayoneerPayService extends BasePayService implements AdvancedPayService { +public class PayoneerPayService extends BasePayService implements AdvancedPayService { /** * 测试地址 */ diff --git a/pay-java-paypal/src/main/java/com/egzosn/pay/paypal/api/PayPalPayService.java b/pay-java-paypal/src/main/java/com/egzosn/pay/paypal/api/PayPalPayService.java index b94f887..6519c9d 100644 --- a/pay-java-paypal/src/main/java/com/egzosn/pay/paypal/api/PayPalPayService.java +++ b/pay-java-paypal/src/main/java/com/egzosn/pay/paypal/api/PayPalPayService.java @@ -29,7 +29,7 @@ import java.util.concurrent.locks.Lock; * email egzosn@gmail.com * date 2018-4-8 ‏‎22:15:09 */ -public class PayPalPayService extends BasePayService{ +public class PayPalPayService extends BasePayService{ /** * 沙箱环境 diff --git a/pay-java-union/src/main/java/com/egzosn/pay/union/api/UnionPayService.java b/pay-java-union/src/main/java/com/egzosn/pay/union/api/UnionPayService.java index 90aaad6..0637aa1 100644 --- a/pay-java-union/src/main/java/com/egzosn/pay/union/api/UnionPayService.java +++ b/pay-java-union/src/main/java/com/egzosn/pay/union/api/UnionPayService.java @@ -38,7 +38,7 @@ import java.util.*; * create 2017 2017/11/5 * */ -public class UnionPayService extends BasePayService { +public class UnionPayService extends BasePayService { /** * 测试域名 */ 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 2d7775f..7134cfb 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 @@ -27,7 +27,7 @@ import java.util.concurrent.locks.Lock; * email egzosn@gmail.com * date 2017/01/12 22:58 */ -public class WxYouDianPayService extends BasePayService { +public class WxYouDianPayService extends BasePayService { private final static String URL = "http://life.51youdian.com/Api/CheckoutCounter/"; 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 5027d82..9368a2f 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 @@ -36,7 +36,7 @@ import static com.egzosn.pay.wx.bean.WxTransferType.*; * date 2016-5-18 14:09:01 * */ -public class WxPayService extends BasePayService { +public class WxPayService extends BasePayService { /** diff --git a/pay-java-yiji/src/main/java/com/egzosn/pay/yiji/api/YiJiPayService.java b/pay-java-yiji/src/main/java/com/egzosn/pay/yiji/api/YiJiPayService.java index cece47f..911c9a2 100644 --- a/pay-java-yiji/src/main/java/com/egzosn/pay/yiji/api/YiJiPayService.java +++ b/pay-java-yiji/src/main/java/com/egzosn/pay/yiji/api/YiJiPayService.java @@ -27,7 +27,7 @@ import java.util.TreeMap; * email egzosn@gmail.com * * date 2019/04/15 22:51 */ -public class YiJiPayService extends BasePayService { +public class YiJiPayService extends BasePayService { /** * 正式测试环境 diff --git a/wx.jpg b/wx.jpg deleted file mode 100644 index 4332207c6d81f5ed0c161c6e87e630d5831bf33b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 78746 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 Date: Sun, 17 Nov 2019 23:27:12 +0800 Subject: [PATCH 067/299] =?UTF-8?q?#=20=E7=99=BE=E5=BA=A6=E6=94=AF?= =?UTF-8?q?=E4=BB=98=E5=B9=B3=E5=8F=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 4 + pay-java-baidu/pom.xml | 23 + .../pay/baidu/api/BaiduPayConfigStorage.java | 52 +++ .../egzosn/pay/baidu/api/BaiduPayService.java | 400 ++++++++++++++++++ .../egzosn/pay/baidu/bean/BaiduPayOrder.java | 71 ++++ .../pay/baidu/bean/BaiduRefundOrder.java | 65 +++ .../pay/baidu/bean/BaiduTransactionType.java | 37 ++ .../pay/baidu/bean/type/AuditStatus.java | 22 + .../com/egzosn/pay/baidu/package-info.java | 1 + .../com/egzosn/pay/baidu/util/Asserts.java | 16 + .../com/egzosn/pay/baidu/util/NoNullMap.java | 14 + pom.xml | 1 + 12 files changed, 706 insertions(+) create mode 100644 .gitignore create mode 100644 pay-java-baidu/pom.xml create mode 100644 pay-java-baidu/src/com/egzosn/pay/baidu/api/BaiduPayConfigStorage.java create mode 100644 pay-java-baidu/src/com/egzosn/pay/baidu/api/BaiduPayService.java create mode 100644 pay-java-baidu/src/com/egzosn/pay/baidu/bean/BaiduPayOrder.java create mode 100644 pay-java-baidu/src/com/egzosn/pay/baidu/bean/BaiduRefundOrder.java create mode 100644 pay-java-baidu/src/com/egzosn/pay/baidu/bean/BaiduTransactionType.java create mode 100644 pay-java-baidu/src/com/egzosn/pay/baidu/bean/type/AuditStatus.java create mode 100644 pay-java-baidu/src/com/egzosn/pay/baidu/package-info.java create mode 100644 pay-java-baidu/src/com/egzosn/pay/baidu/util/Asserts.java create mode 100644 pay-java-baidu/src/com/egzosn/pay/baidu/util/NoNullMap.java diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ec5cc02 --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +# Created by .ignore support plugin (hsz.mobi) +target/ +.idea/ +*.iml \ No newline at end of file diff --git a/pay-java-baidu/pom.xml b/pay-java-baidu/pom.xml new file mode 100644 index 0000000..2aa2bda --- /dev/null +++ b/pay-java-baidu/pom.xml @@ -0,0 +1,23 @@ + + + + pay-java-parent + com.egzosn + 2.12.9-SNAPSHOT + + 4.0.0 + pay-java-baidu + + + + + com.egzosn + pay-java-common + + + + + + \ No newline at end of file diff --git a/pay-java-baidu/src/com/egzosn/pay/baidu/api/BaiduPayConfigStorage.java b/pay-java-baidu/src/com/egzosn/pay/baidu/api/BaiduPayConfigStorage.java new file mode 100644 index 0000000..df84f5e --- /dev/null +++ b/pay-java-baidu/src/com/egzosn/pay/baidu/api/BaiduPayConfigStorage.java @@ -0,0 +1,52 @@ +package com.egzosn.pay.baidu.api; + +import com.egzosn.pay.common.api.BasePayConfigStorage; + +public class BaiduPayConfigStorage extends BasePayConfigStorage { + private String appid; + private String dealId; + + @Override + public String getAppid() { + return this.appid; + } + + @Override + public String getPid() { + return getDealId(); + } + + @Override + public String getSeller() { + throw new UnsupportedOperationException("不支持"); + } + + public String getDealId() { + return dealId; + } + + public void setDealId(String dealId) { + this.dealId = dealId; + } + + public String getAppKey() { + return this.getKeyPrivate(); + } + + public void setAppKey(String appKey) { + setKeyPrivate(appKey); + } + + @Override + public String getKeyPublic() { + return super.getKeyPrivate(); + } + + public void setKeyPublic(String keyPublic) { + super.setKeyPublic(keyPublic); + } + + public void setAppid(String appid) { + this.appid = appid; + } +} diff --git a/pay-java-baidu/src/com/egzosn/pay/baidu/api/BaiduPayService.java b/pay-java-baidu/src/com/egzosn/pay/baidu/api/BaiduPayService.java new file mode 100644 index 0000000..a7a1ad8 --- /dev/null +++ b/pay-java-baidu/src/com/egzosn/pay/baidu/api/BaiduPayService.java @@ -0,0 +1,400 @@ +package com.egzosn.pay.baidu.api; + +import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.JSONObject; +import com.egzosn.pay.baidu.bean.BaiduPayOrder; +import com.egzosn.pay.baidu.bean.BaiduRefundOrder; +import com.egzosn.pay.baidu.bean.BaiduTransactionType; +import com.egzosn.pay.baidu.bean.type.AuditStatus; +import com.egzosn.pay.baidu.util.Asserts; +import com.egzosn.pay.baidu.util.NoNullMap; +import com.egzosn.pay.common.api.BasePayService; +import com.egzosn.pay.common.bean.*; +import com.egzosn.pay.common.http.HttpConfigStorage; +import com.egzosn.pay.common.http.UriVariables; +import com.egzosn.pay.common.util.DateUtils; +import com.egzosn.pay.common.util.sign.SignUtils; +import com.egzosn.pay.common.util.str.StringUtils; + +import java.math.BigDecimal; +import java.util.Date; +import java.util.HashMap; +import java.util.Map; + + +public class BaiduPayService extends BasePayService { + public static final String APP_KEY = "appKey"; + public static final String APP_ID = "appId"; + public static final String DEAL_ID = "dealId"; + public static final String TP_ORDER_ID = "tpOrderId"; + public static final String DEAL_TITLE = "dealTitle"; + public static final String TOTAL_AMOUNT = "totalAmount"; + public static final String SIGN_FIELDS_RANGE = "signFieldsRange"; + public static final String BIZ_INFO = "bizInfo"; + public static final String RSA_SIGN = "rsaSign"; + public static final String ORDER_ID = "orderId"; + public static final String USER_ID = "userId"; + public static final String SITE_ID = "siteId"; + public static final String METHOD = "method"; + public static final String TYPE = "type"; + + public static final Integer RESPONSE_SUCCESS = 2; + public static final String RESPONSE_STATUS = "status"; + + + public BaiduPayService(BaiduPayConfigStorage payConfigStorage) { + super(payConfigStorage); + } + + public BaiduPayService(BaiduPayConfigStorage payConfigStorage, + HttpConfigStorage configStorage) { + super(payConfigStorage, configStorage); + } + + @Override + public boolean verify(Map params) { + if (!RESPONSE_SUCCESS.equals(params.get(RESPONSE_STATUS))) { + return false; + } + return signVerify(params, String.valueOf(params.get(RSA_SIGN))) && verifySource(String.valueOf(params.get(TP_ORDER_ID))); + } + + @Override + public boolean signVerify(Map params, String sign) { + String keyPrivate = payConfigStorage.getKeyPrivate(); + String rsaSign = String.valueOf(params.get(RSA_SIGN)); + String targetRsaSign = getRsaSign(params, keyPrivate, RSA_SIGN); + LOG.debug("百度返回的签名: " + rsaSign + " 本地产生的签名: " + targetRsaSign); + return StringUtils.equals(rsaSign, targetRsaSign); + } + + @Override + public boolean verifySource(String id) { + return true; + } + + @Override + public Map orderInfo(PayOrder order) { + if (!(order instanceof BaiduPayOrder)) { + throw new UnsupportedOperationException("请使用 " + BaiduPayOrder.class.getName()); + } + NoNullMap params = getUseOrderInfoParams(order); + String rsaSign = getRsaSignUserOrderInfo(params, payConfigStorage.getKeyPrivate()); + params.putIfNoNull(RSA_SIGN, rsaSign); + return params; + } + + /** + * 获取"查询支付状态"所需参数 + * + * @return + */ + public NoNullMap getUseQueryPay() { + String appKey = payConfigStorage.getAppKey(); + NoNullMap result = new NoNullMap<>(); + result.putIfNoNull(APP_KEY, appKey) + .putIfNoNull(APP_ID, payConfigStorage.getAppid()); + return result; + } + + /** + * 获取"创建订单"所需参数 + * + * @param order + * @return + */ + private NoNullMap getUseOrderInfoParams(PayOrder order) { + BaiduPayOrder payOrder = (BaiduPayOrder) order; + NoNullMap result = new NoNullMap<>(); + String appKey = payConfigStorage.getAppKey(); + String dealId = payConfigStorage.getDealId(); + result.putIfNoNull(APP_KEY, appKey) + .putIfNoNull(TP_ORDER_ID, payOrder.getTradeNo()) + .putIfNoNull(DEAL_ID, dealId) + .putIfNoNull(DEAL_TITLE, payOrder.getSubject()) + .putIfNoNull(SIGN_FIELDS_RANGE, payOrder.getSignFieldsRange()) + .putIfNoNull(BIZ_INFO, JSON.toJSONString(payOrder.getBizInfo())) + .putIfNoNull(TOTAL_AMOUNT, String.valueOf(order.getPrice())); + return result; + } + + @Override + @Deprecated + public PayOutMessage getPayOutMessage(String code, String message) { + throw new UnsupportedOperationException("请使用 " + getClass().getName() + "#getPayOutMessageUseBaidu"); + } + + /** + * 请求业务方退款审核/响应处理 + * http://smartprogram.baidu.com/docs/develop/function/tune_up_examine/ + * + * @param errno + * @param message + * @param auditStatus + * @param refundPayMoney + * @return + */ + public PayOutMessage getApplyRefundOutMessageUseBaidu(Integer errno, + String message, + AuditStatus auditStatus, + BigDecimal refundPayMoney) { + JSONObject data = new JSONObject(); + data.put("auditStatus", auditStatus.getCode()); + JSONObject calculateRes = new JSONObject(); + calculateRes.put("refundPayMoney", refundPayMoney); + data.put("calculateRes", calculateRes); + return PayOutMessage.JSON() + .content("errno", errno) + .content("message", message) + .content("data", data) + .build(); + + } + + /** + * 通知退款状态/响应处理 + * http://smartprogram.baidu.com/docs/develop/function/tune_up_drawback/ + * + * @param errno + * @param message + * @return + */ + public PayOutMessage getRefundOutMessageUseBaidu(Integer errno, + String message) { + return PayOutMessage.JSON() + .content("errno", errno) + .content("message", message) + .content("data", "{}") + .build(); + + } + + /** + * 支付通知/响应处理 + * + * @param errno + * @param message + * @param isConsumed + * @param isErrorOrder + * @return + */ + public PayOutMessage getPayOutMessageUseBaidu(Integer errno, + String message, + Integer isConsumed, + Integer isErrorOrder) { + Asserts.isNoNull(errno, "errno 是必填的"); + Asserts.isNoNull(message, "message 是必填的"); + Asserts.isNoNull(isConsumed, "isConsumed 是必填的"); + JSONObject data = new JSONObject(); + data.put("isConsumed", isConsumed); + if (isErrorOrder != null) { + data.put("isErrorOrder", isErrorOrder); + } + return PayOutMessage.JSON() + .content("errno", errno) + .content("message", message) + .content("data", data) + .build(); + } + + /** + * 支付通知/响应处理 + * http://smartprogram.baidu.com/docs/develop/function/tune_up_notice/ + * + * @param code + * @param message + * @param isConsumed + * @return + */ + public PayOutMessage getPayOutMessageUseBaidu(Integer code, + String message, + Integer isConsumed) { + return getPayOutMessageUseBaidu(code, message, isConsumed, null); + } + + /** + * 支付通知/响应处理 + * http://smartprogram.baidu.com/docs/develop/function/tune_up_notice/ + * + * @param payMessage 支付回调消息 + * @return + */ + @Override + public PayOutMessage successPayOutMessage(PayMessage payMessage) { + return getPayOutMessageUseBaidu(0, "success", 2); + } + + @Override + public String buildRequest(Map orderInfo, + MethodType method) { + throw new UnsupportedOperationException("百度不支持PC支付"); + } + + @Override + public String getQrPay(PayOrder order) { + throw new UnsupportedOperationException("百度不支持扫码付"); + } + + @Override + public Map microPay(PayOrder order) { + throw new UnsupportedOperationException("百度不支持刷卡付"); + } + + @Override + public Map query(String tradeNo, String outTradeNo) { + return secondaryInterface(tradeNo, outTradeNo, BaiduTransactionType.PAY_QUERY); + } + + @Override + public Map close(String tradeNo, String outTradeNo) { + throw new UnsupportedOperationException("不支持该操作"); + } + + @Override + @Deprecated + public Map refund(String orderId, + String userId, + BigDecimal refundAmount, + BigDecimal totalAmount) { + throw new UnsupportedOperationException("请使用 " + getClass().getName() + "#refundUseBaidu"); + } + + public Map refundUseBaidu(Long orderId, + Long userId, + Integer refundType, + String tpOrderId, + String refundReason) { + return refundUseBaidu(new BaiduRefundOrder(orderId, userId, refundType, refundReason, tpOrderId)); + } + + @Override + public Map refund(RefundOrder refundOrder) { + throw new UnsupportedOperationException("请使用 " + getClass().getName() + "#refundUseBaidu"); + } + + public Map refundUseBaidu(BaiduRefundOrder refundOrder) { + NoNullMap parameters = getUseQueryPay(); + BaiduTransactionType transactionType = BaiduTransactionType.APPLY_REFUND; + parameters.putIfNoNull(METHOD, transactionType.getMethod()) + .putIfNoNull(ORDER_ID, refundOrder.getTradeNo()) + .putIfNoNull(USER_ID, refundOrder.getUserId()) + .putIfNoNull("refundType", refundOrder.getRefundType()) + .putIfNoNull("refundReason", String.valueOf(refundOrder.getRefundReason())) + .putIfNoNull(TP_ORDER_ID, refundOrder.getTpOrderId()) + .putIfNoNull("applyRefundMoney", refundOrder.getApplyRefundMoney()) + .putIfNoNull("bizRefundBatchId", refundOrder.getBizRefundBatchId()) + .putIfNoNull(APP_KEY, payConfigStorage.getAppKey()) + .putIfNoNull(RSA_SIGN, getRsaSign(parameters, payConfigStorage.getKeyPrivate())); + return requestTemplate.getForObject(String.format("%s?%s", getReqUrl(transactionType), UriVariables.getMapToParameters(parameters)), JSONObject.class); + } + + /** + * @param orderId 百度平台订单ID + * @param userId 百度用户ID + * @return + */ + @Override + public Map refundquery(String orderId, + String userId) { + NoNullMap parameters = getUseQueryPay(); + BaiduTransactionType transactionType = BaiduTransactionType.REFUND_QUERY; + parameters.putIfNoNull(METHOD, transactionType.getMethod()) + .putIfNoNull(TYPE, 3) + .putIfNoNull(ORDER_ID, orderId) + .putIfNoNull(USER_ID, userId) + .putIfNoNull(APP_KEY, payConfigStorage.getAppKey()) + .putIfNoNull(RSA_SIGN, getRsaSign(parameters, payConfigStorage.getKeyPrivate())); + return requestTemplate.getForObject(String.format("%s?%s", getReqUrl(transactionType), UriVariables.getMapToParameters(parameters)), JSONObject.class); + } + + @Override + public Map refundquery(RefundOrder refundOrder) { + return refundquery(refundOrder.getTradeNo(), refundOrder.getOutTradeNo()); + } + + @Override + public Map downloadbill(Date billDate, String access_token) { + Map parameters = new HashMap<>(); + parameters.put("access_token", access_token); + parameters.put("billTime", DateUtils.formatDay(billDate)); + return requestTemplate.getForObject(String.format("%s?%s", getReqUrl(BaiduTransactionType.DOWNLOAD_BILL), + UriVariables.getMapToParameters(parameters)), JSONObject.class); + } + + public Map downloadOrderBill(Date billDate, String access_token) { + Map parameters = new HashMap<>(); + parameters.put("access_token", access_token); + parameters.put("billTime", DateUtils.formatDay(billDate)); + return requestTemplate.getForObject(String.format("%s?%s", getReqUrl(BaiduTransactionType.DOWNLOAD_ORDER_BILL), + UriVariables.getMapToParameters(parameters)), JSONObject.class); + } + + @Override + public Map secondaryInterface(Object orderId, + String siteId, + TransactionType transactionType) { + if (!BaiduTransactionType.PAY_QUERY.equals(transactionType)) { + throw new UnsupportedOperationException("不支持该操作"); + } + + NoNullMap parameters = getUseQueryPay(); + parameters.putIfNoNull(ORDER_ID, orderId) + .putIfNoNull(SITE_ID, siteId) + .putIfNoNull("sign", getRsaSignUsePayQuery(parameters, payConfigStorage.getKeyPrivate())); + return requestTemplate.getForObject(String.format("%s?%s", getReqUrl(transactionType), UriVariables.getMapToParameters(parameters)), JSONObject.class); + } + + @Override + public String getReqUrl(TransactionType transactionType) { + return ((BaiduTransactionType) transactionType).getUrl(); + } + + private String getRsaSignUserOrderInfo(Map params, String privateKey) { + Map signParams = new HashMap<>(); + signParams.put(APP_KEY, String.valueOf(params.get(APP_KEY))); + signParams.put(DEAL_ID, String.valueOf(params.get(DEAL_ID))); + signParams.put(TP_ORDER_ID, String.valueOf(params.get(TP_ORDER_ID))); + signParams.put(TOTAL_AMOUNT, String.valueOf(params.get(TOTAL_AMOUNT))); + if (signParams.containsValue(null)) { + throw new IllegalArgumentException("参数 " + signParams.keySet().toString() + " 均为必填"); + } + + return SignUtils.RSA.sign(params, privateKey, "UTF-8"); + } + + /** + * "支付状态查询" 使用的签名 + * + * @param params + * @param privateKey + * @return + */ + private String getRsaSignUsePayQuery(Map params, String privateKey) { + Map signParams = new HashMap<>(); + signParams.put(APP_KEY, String.valueOf(params.get(APP_KEY))); + signParams.put(APP_ID, String.valueOf(params.get(APP_ID))); + signParams.put(ORDER_ID, String.valueOf(params.get(ORDER_ID))); + signParams.put(SITE_ID, String.valueOf(params.get(SITE_ID))); + if (signParams.containsValue(null)) { + throw new IllegalArgumentException("参数 " + signParams.keySet().toString() + " 均为必填"); + } + + return SignUtils.RSA.sign(params, privateKey, "UTF-8"); + } + + private String getRsaSign(Map params, String privateKey) { + Map signParams = new HashMap<>(); + signParams.put(APP_KEY, String.valueOf(params.get(APP_KEY))); + signParams.put(USER_ID, String.valueOf(params.get(USER_ID))); + signParams.put(ORDER_ID, String.valueOf(params.get(ORDER_ID))); + if (signParams.containsValue(null)) { + throw new IllegalArgumentException("参数 " + signParams.keySet().toString() + " 均为必填"); + } + + return SignUtils.RSA.sign(params, privateKey, "UTF-8"); + } + + private String getRsaSign(Map params, String... ignoreKeys) { + return SignUtils.RSA.createSign(SignUtils.parameterText(params, "&", ignoreKeys), payConfigStorage.getKeyPrivate(), "UTF-8"); + } +} diff --git a/pay-java-baidu/src/com/egzosn/pay/baidu/bean/BaiduPayOrder.java b/pay-java-baidu/src/com/egzosn/pay/baidu/bean/BaiduPayOrder.java new file mode 100644 index 0000000..58e5417 --- /dev/null +++ b/pay-java-baidu/src/com/egzosn/pay/baidu/bean/BaiduPayOrder.java @@ -0,0 +1,71 @@ +package com.egzosn.pay.baidu.bean; + +import com.alibaba.fastjson.JSONObject; +import com.egzosn.pay.common.bean.PayOrder; +import com.egzosn.pay.common.bean.TransactionType; + +import java.math.BigDecimal; +import java.util.Collections; +import java.util.List; + +public class BaiduPayOrder extends PayOrder { + + /** + * 需要隐藏的支付方式 + */ + private List bannedChannels = Collections.emptyList(); + + /** + * 固定值 + */ + private String signFieldsRange; + + /** + * 附加信息 + */ + private JSONObject bizInfo = new JSONObject(); + + public BaiduPayOrder(String dealTitle, + BigDecimal totalAmount, + String tpOrderId, + String signFieldsRange) { + this(dealTitle, totalAmount, tpOrderId, signFieldsRange, Collections.emptyList()); + } + + public BaiduPayOrder(String dealTitle, + BigDecimal totalAmount, + String tpOrderId, + String signFieldsRange, + List bannedChannels) { + setPrice(totalAmount); + setOutTradeNo(tpOrderId); + setSubject(dealTitle); + setSignFieldsRange(signFieldsRange); + setBannedChannels(bannedChannels); + } + + public JSONObject getBizInfo() { + return bizInfo; + } + + public void setBizInfo(JSONObject bizInfo) { + this.bizInfo = bizInfo; + } + + public List getBannedChannels() { + return bannedChannels; + } + + public void setBannedChannels(List bannedChannels) { + this.bannedChannels = bannedChannels; + } + + public String getSignFieldsRange() { + return signFieldsRange; + } + + public void setSignFieldsRange(String signFieldsRange) { + this.signFieldsRange = signFieldsRange; + } + +} diff --git a/pay-java-baidu/src/com/egzosn/pay/baidu/bean/BaiduRefundOrder.java b/pay-java-baidu/src/com/egzosn/pay/baidu/bean/BaiduRefundOrder.java new file mode 100644 index 0000000..ee68440 --- /dev/null +++ b/pay-java-baidu/src/com/egzosn/pay/baidu/bean/BaiduRefundOrder.java @@ -0,0 +1,65 @@ +package com.egzosn.pay.baidu.bean; + +import com.egzosn.pay.common.bean.RefundOrder; + +import java.math.BigDecimal; + +public class BaiduRefundOrder extends RefundOrder { + private Long userId; + private Integer refundType; + private String refundReason; + private String tpOrderId; + /** + * 退款金额,单位:分,发起部分退款时必传 + */ + private BigDecimal applyRefundMoney; + /** + * 业务方退款批次id,退款业务流水唯一编号,发起部分退款时必传 + */ + private String bizRefundBatchId; + + public BaiduRefundOrder(Long orderId, + Long userId, + Integer refundType, + String refundReason, + String tpOrderId) { + super(); + setTradeNo(String.valueOf(orderId)); + this.userId = userId; + this.refundType = refundType; + this.refundReason = refundReason; + this.tpOrderId = tpOrderId; + } + + public BigDecimal getApplyRefundMoney() { + return applyRefundMoney; + } + + public void setApplyRefundMoney(BigDecimal applyRefundMoney) { + setRefundAmount(applyRefundMoney); + } + + public String getBizRefundBatchId() { + return bizRefundBatchId; + } + + public void setBizRefundBatchId(String bizRefundBatchId) { + this.bizRefundBatchId = bizRefundBatchId; + } + + public Long getUserId() { + return userId; + } + + public Integer getRefundType() { + return refundType; + } + + public String getRefundReason() { + return refundReason; + } + + public String getTpOrderId() { + return tpOrderId; + } +} diff --git a/pay-java-baidu/src/com/egzosn/pay/baidu/bean/BaiduTransactionType.java b/pay-java-baidu/src/com/egzosn/pay/baidu/bean/BaiduTransactionType.java new file mode 100644 index 0000000..3e23333 --- /dev/null +++ b/pay-java-baidu/src/com/egzosn/pay/baidu/bean/BaiduTransactionType.java @@ -0,0 +1,37 @@ +package com.egzosn.pay.baidu.bean; + +import com.egzosn.pay.common.bean.TransactionType; + +public enum BaiduTransactionType implements TransactionType { + // 查询支付状态 + PAY_QUERY("https://dianshang.baidu.com/platform/entity/openapi/queryorderdetail", "PAY_QUERY"), + // 取消核销 + REFUND_QUERY("https://nop.nuomi.com/nop/server/rest", "nuomi.cashier.syncorderstatus"), + // 下载资金账单 + DOWNLOAD_BILL("https://openapi.baidu.com/rest/2.0/smartapp/pay/paymentservice/capitaBill", "DOWNLOAD_BILL"), + // 下载订单对账单 + DOWNLOAD_ORDER_BILL("https://openapi.baidu.com/rest/2.0/smartapp/pay/paymentservice/orderBill", "DOWNLOAD_ORDER_BILL"), + // 申请退款 + APPLY_REFUND("https://nop.nuomi.com/nop/server/rest", "nuomi.cashier.applyorderrefund"); + private final String method; + private final String url; + + BaiduTransactionType( String url, String method) { + this.url = url; + this.method = method; + } + + @Override + public String getType() { + return this.name(); + } + + @Override + public String getMethod() { + return this.method; + } + + public String getUrl() { + return url; + } +} diff --git a/pay-java-baidu/src/com/egzosn/pay/baidu/bean/type/AuditStatus.java b/pay-java-baidu/src/com/egzosn/pay/baidu/bean/type/AuditStatus.java new file mode 100644 index 0000000..22960bf --- /dev/null +++ b/pay-java-baidu/src/com/egzosn/pay/baidu/bean/type/AuditStatus.java @@ -0,0 +1,22 @@ +package com.egzosn.pay.baidu.bean.type; + +public enum AuditStatus { + SUCCESS(1, "审核通过可退款"), + FAIL(2, "审核不通过,不能退款"), + UNKNOWN(3, "审核结果不确定,待重试"); + private final int code; + private final String desc; + + AuditStatus(int code, String desc) { + this.code = code; + this.desc = desc; + } + + public int getCode() { + return code; + } + + public String getDesc() { + return desc; + } +} diff --git a/pay-java-baidu/src/com/egzosn/pay/baidu/package-info.java b/pay-java-baidu/src/com/egzosn/pay/baidu/package-info.java new file mode 100644 index 0000000..e9370e2 --- /dev/null +++ b/pay-java-baidu/src/com/egzosn/pay/baidu/package-info.java @@ -0,0 +1 @@ +package com.egzosn.pay.baidu; \ No newline at end of file diff --git a/pay-java-baidu/src/com/egzosn/pay/baidu/util/Asserts.java b/pay-java-baidu/src/com/egzosn/pay/baidu/util/Asserts.java new file mode 100644 index 0000000..7c94996 --- /dev/null +++ b/pay-java-baidu/src/com/egzosn/pay/baidu/util/Asserts.java @@ -0,0 +1,16 @@ +package com.egzosn.pay.baidu.util; + +public class Asserts { + + public static void isNoNull(Object object, String message) { + if (object == null) { + throw new IllegalArgumentException(message); + } + } + + public static void isTrue(boolean bool, String message) { + if (!bool) { + throw new IllegalArgumentException(message); + } + } +} diff --git a/pay-java-baidu/src/com/egzosn/pay/baidu/util/NoNullMap.java b/pay-java-baidu/src/com/egzosn/pay/baidu/util/NoNullMap.java new file mode 100644 index 0000000..7e24a13 --- /dev/null +++ b/pay-java-baidu/src/com/egzosn/pay/baidu/util/NoNullMap.java @@ -0,0 +1,14 @@ +package com.egzosn.pay.baidu.util; + +import java.util.HashMap; + +public class NoNullMap extends HashMap { + + public NoNullMap putIfNoNull(K key, V value) { + if (value != null) { + put(key, value); + } + return this; + } + +} diff --git a/pom.xml b/pom.xml index 775e767..5ae16b1 100644 --- a/pom.xml +++ b/pom.xml @@ -52,6 +52,7 @@ pay-java-payoneer pay-java-paypal pay-java-yiji + pay-java-baidu pay-java-demo -- Gitee From 251646d43513e2bfd8416ba45e42886808eb0a83 Mon Sep 17 00:00:00 2001 From: hocgin Date: Sun, 24 Nov 2019 16:27:01 +0800 Subject: [PATCH 068/299] =?UTF-8?q?#=20=E7=AD=BE=E5=90=8D=E5=8F=82?= =?UTF-8?q?=E6=95=B0=E8=B0=83=E6=95=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../pay/baidu/api/BaiduPayConfigStorage.java | 1 + .../egzosn/pay/baidu/api/BaiduPayService.java | 90 +++++++------------ .../pay/common/util/sign/SignUtils.java | 20 ++++- 3 files changed, 48 insertions(+), 63 deletions(-) diff --git a/pay-java-baidu/src/com/egzosn/pay/baidu/api/BaiduPayConfigStorage.java b/pay-java-baidu/src/com/egzosn/pay/baidu/api/BaiduPayConfigStorage.java index df84f5e..f1496e7 100644 --- a/pay-java-baidu/src/com/egzosn/pay/baidu/api/BaiduPayConfigStorage.java +++ b/pay-java-baidu/src/com/egzosn/pay/baidu/api/BaiduPayConfigStorage.java @@ -42,6 +42,7 @@ public class BaiduPayConfigStorage extends BasePayConfigStorage { return super.getKeyPrivate(); } + @Override public void setKeyPublic(String keyPublic) { super.setKeyPublic(keyPublic); } diff --git a/pay-java-baidu/src/com/egzosn/pay/baidu/api/BaiduPayService.java b/pay-java-baidu/src/com/egzosn/pay/baidu/api/BaiduPayService.java index a7a1ad8..3826166 100644 --- a/pay-java-baidu/src/com/egzosn/pay/baidu/api/BaiduPayService.java +++ b/pay-java-baidu/src/com/egzosn/pay/baidu/api/BaiduPayService.java @@ -35,6 +35,7 @@ public class BaiduPayService extends BasePayService { public static final String ORDER_ID = "orderId"; public static final String USER_ID = "userId"; public static final String SITE_ID = "siteId"; + public static final String SIGN = "sign"; public static final String METHOD = "method"; public static final String TYPE = "type"; @@ -63,7 +64,7 @@ public class BaiduPayService extends BasePayService { public boolean signVerify(Map params, String sign) { String keyPrivate = payConfigStorage.getKeyPrivate(); String rsaSign = String.valueOf(params.get(RSA_SIGN)); - String targetRsaSign = getRsaSign(params, keyPrivate, RSA_SIGN); + String targetRsaSign = getRsaSign(params, RSA_SIGN); LOG.debug("百度返回的签名: " + rsaSign + " 本地产生的签名: " + targetRsaSign); return StringUtils.equals(rsaSign, targetRsaSign); } @@ -89,11 +90,11 @@ public class BaiduPayService extends BasePayService { * * @return */ - public NoNullMap getUseQueryPay() { + public Map getUseQueryPay() { String appKey = payConfigStorage.getAppKey(); - NoNullMap result = new NoNullMap<>(); - result.putIfNoNull(APP_KEY, appKey) - .putIfNoNull(APP_ID, payConfigStorage.getAppid()); + Map result = new HashMap<>(); + result.put(APP_KEY, appKey); + result.put(APP_ID, payConfigStorage.getAppid()); return result; } @@ -273,18 +274,18 @@ public class BaiduPayService extends BasePayService { } public Map refundUseBaidu(BaiduRefundOrder refundOrder) { - NoNullMap parameters = getUseQueryPay(); + Map parameters = getUseQueryPay(); BaiduTransactionType transactionType = BaiduTransactionType.APPLY_REFUND; - parameters.putIfNoNull(METHOD, transactionType.getMethod()) - .putIfNoNull(ORDER_ID, refundOrder.getTradeNo()) - .putIfNoNull(USER_ID, refundOrder.getUserId()) - .putIfNoNull("refundType", refundOrder.getRefundType()) - .putIfNoNull("refundReason", String.valueOf(refundOrder.getRefundReason())) - .putIfNoNull(TP_ORDER_ID, refundOrder.getTpOrderId()) - .putIfNoNull("applyRefundMoney", refundOrder.getApplyRefundMoney()) - .putIfNoNull("bizRefundBatchId", refundOrder.getBizRefundBatchId()) - .putIfNoNull(APP_KEY, payConfigStorage.getAppKey()) - .putIfNoNull(RSA_SIGN, getRsaSign(parameters, payConfigStorage.getKeyPrivate())); + parameters.put(METHOD, transactionType.getMethod()); + parameters.put(ORDER_ID, refundOrder.getTradeNo()); + parameters.put(USER_ID, refundOrder.getUserId()); + parameters.put("refundType", refundOrder.getRefundType()); + parameters.put("refundReason", String.valueOf(refundOrder.getRefundReason())); + parameters.put(TP_ORDER_ID, refundOrder.getTpOrderId()); + parameters.put("applyRefundMoney", refundOrder.getApplyRefundMoney()); + parameters.put("bizRefundBatchId", refundOrder.getBizRefundBatchId()); + parameters.put(APP_KEY, payConfigStorage.getAppKey()); + parameters.put(RSA_SIGN, getRsaSign(parameters, RSA_SIGN)); return requestTemplate.getForObject(String.format("%s?%s", getReqUrl(transactionType), UriVariables.getMapToParameters(parameters)), JSONObject.class); } @@ -296,14 +297,14 @@ public class BaiduPayService extends BasePayService { @Override public Map refundquery(String orderId, String userId) { - NoNullMap parameters = getUseQueryPay(); + Map parameters = getUseQueryPay(); BaiduTransactionType transactionType = BaiduTransactionType.REFUND_QUERY; - parameters.putIfNoNull(METHOD, transactionType.getMethod()) - .putIfNoNull(TYPE, 3) - .putIfNoNull(ORDER_ID, orderId) - .putIfNoNull(USER_ID, userId) - .putIfNoNull(APP_KEY, payConfigStorage.getAppKey()) - .putIfNoNull(RSA_SIGN, getRsaSign(parameters, payConfigStorage.getKeyPrivate())); + parameters.put(METHOD, transactionType.getMethod()); + parameters.put(TYPE, 3); + parameters.put(ORDER_ID, orderId); + parameters.put(USER_ID, userId); + parameters.put(APP_KEY, payConfigStorage.getAppKey()); + parameters.put(RSA_SIGN, getRsaSign(parameters, RSA_SIGN)); return requestTemplate.getForObject(String.format("%s?%s", getReqUrl(transactionType), UriVariables.getMapToParameters(parameters)), JSONObject.class); } @@ -337,10 +338,10 @@ public class BaiduPayService extends BasePayService { throw new UnsupportedOperationException("不支持该操作"); } - NoNullMap parameters = getUseQueryPay(); - parameters.putIfNoNull(ORDER_ID, orderId) - .putIfNoNull(SITE_ID, siteId) - .putIfNoNull("sign", getRsaSignUsePayQuery(parameters, payConfigStorage.getKeyPrivate())); + Map parameters = getUseQueryPay(); + parameters.put(ORDER_ID, orderId); + parameters.put(SITE_ID, siteId); + parameters.put(SIGN, getRsaSign(parameters, SIGN)); return requestTemplate.getForObject(String.format("%s?%s", getReqUrl(transactionType), UriVariables.getMapToParameters(parameters)), JSONObject.class); } @@ -362,39 +363,8 @@ public class BaiduPayService extends BasePayService { return SignUtils.RSA.sign(params, privateKey, "UTF-8"); } - /** - * "支付状态查询" 使用的签名 - * - * @param params - * @param privateKey - * @return - */ - private String getRsaSignUsePayQuery(Map params, String privateKey) { - Map signParams = new HashMap<>(); - signParams.put(APP_KEY, String.valueOf(params.get(APP_KEY))); - signParams.put(APP_ID, String.valueOf(params.get(APP_ID))); - signParams.put(ORDER_ID, String.valueOf(params.get(ORDER_ID))); - signParams.put(SITE_ID, String.valueOf(params.get(SITE_ID))); - if (signParams.containsValue(null)) { - throw new IllegalArgumentException("参数 " + signParams.keySet().toString() + " 均为必填"); - } - - return SignUtils.RSA.sign(params, privateKey, "UTF-8"); - } - - private String getRsaSign(Map params, String privateKey) { - Map signParams = new HashMap<>(); - signParams.put(APP_KEY, String.valueOf(params.get(APP_KEY))); - signParams.put(USER_ID, String.valueOf(params.get(USER_ID))); - signParams.put(ORDER_ID, String.valueOf(params.get(ORDER_ID))); - if (signParams.containsValue(null)) { - throw new IllegalArgumentException("参数 " + signParams.keySet().toString() + " 均为必填"); - } - - return SignUtils.RSA.sign(params, privateKey, "UTF-8"); - } - private String getRsaSign(Map params, String... ignoreKeys) { - return SignUtils.RSA.createSign(SignUtils.parameterText(params, "&", ignoreKeys), payConfigStorage.getKeyPrivate(), "UTF-8"); + String waitSignVal = SignUtils.parameterText(params, "&", false, ignoreKeys); + return SignUtils.RSA.createSign(waitSignVal, payConfigStorage.getKeyPrivate(), "UTF-8"); } } 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 3d13bf5..a971b05 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 @@ -178,16 +178,30 @@ public enum SignUtils { public static String parameterText(Map parameters, String separator) { return parameterText(parameters, separator, "signature", "sign", "key", "sign_type"); } - + + + /** + * + * 把数组所有元素排序,并按照“参数=参数值”的模式用“@param separator”字符拼接成字符串 + * @param parameters 参数 + * @param separator 分隔符 + * @param ignoreKey 需要忽略添加的key + * @return 去掉空值与签名参数后的新签名,拼接后字符串 + */ + public static String parameterText(Map parameters, String separator, String... ignoreKey) { + return parameterText(parameters, separator, true, ignoreKey); + } + /** * * 把数组所有元素排序,并按照“参数=参数值”的模式用“@param separator”字符拼接成字符串 * @param parameters 参数 * @param separator 分隔符 + * @param ignoreNullValue 需要忽略NULL值 * @param ignoreKey 需要忽略添加的key * @return 去掉空值与签名参数后的新签名,拼接后字符串 */ - public static String parameterText(Map parameters, String separator, String... ignoreKey ) { + public static String parameterText(Map parameters, String separator, boolean ignoreNullValue, String... ignoreKey ) { if(parameters == null){ return ""; } @@ -219,7 +233,7 @@ public enum SignUtils { for (String k : keys) { String valueStr = ""; Object o = parameters.get(k); - if (null == o) { + if (ignoreNullValue && null == o) { continue; } if (o instanceof String[]) { -- Gitee From cc6380635377f2667aae33061a4357c39a6bdddc Mon Sep 17 00:00:00 2001 From: hocgin Date: Sun, 24 Nov 2019 16:42:13 +0800 Subject: [PATCH 069/299] =?UTF-8?q?#=20=E7=AD=BE=E5=90=8D=E5=8F=82?= =?UTF-8?q?=E6=95=B0=E8=B0=83=E6=95=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pay-java-baidu/pom.xml | 5 ++++ .../pay/baidu/api/BaiduPayConfigStorage.java | 0 .../egzosn/pay/baidu/api/BaiduPayService.java | 23 +++++++++++------- .../egzosn/pay/baidu/bean/BaiduPayOrder.java | 0 .../pay/baidu/bean/BaiduRefundOrder.java | 0 .../pay/baidu/bean/BaiduTransactionType.java | 0 .../pay/baidu/bean/type/AuditStatus.java | 0 .../com/egzosn/pay/baidu/package-info.java | 0 .../com/egzosn/pay/baidu/util/Asserts.java | 0 .../com/egzosn/pay/baidu/util/NoNullMap.java | 0 .../pay/baidu/api/BaiduPayServiceTest.java | 24 +++++++++++++++++++ pom.xml | 7 ++++++ 12 files changed, 50 insertions(+), 9 deletions(-) rename pay-java-baidu/src/{ => main/java}/com/egzosn/pay/baidu/api/BaiduPayConfigStorage.java (100%) rename pay-java-baidu/src/{ => main/java}/com/egzosn/pay/baidu/api/BaiduPayService.java (97%) rename pay-java-baidu/src/{ => main/java}/com/egzosn/pay/baidu/bean/BaiduPayOrder.java (100%) rename pay-java-baidu/src/{ => main/java}/com/egzosn/pay/baidu/bean/BaiduRefundOrder.java (100%) rename pay-java-baidu/src/{ => main/java}/com/egzosn/pay/baidu/bean/BaiduTransactionType.java (100%) rename pay-java-baidu/src/{ => main/java}/com/egzosn/pay/baidu/bean/type/AuditStatus.java (100%) rename pay-java-baidu/src/{ => main/java}/com/egzosn/pay/baidu/package-info.java (100%) rename pay-java-baidu/src/{ => main/java}/com/egzosn/pay/baidu/util/Asserts.java (100%) rename pay-java-baidu/src/{ => main/java}/com/egzosn/pay/baidu/util/NoNullMap.java (100%) create mode 100644 pay-java-baidu/src/test/java/com/egzosn/pay/baidu/api/BaiduPayServiceTest.java diff --git a/pay-java-baidu/pom.xml b/pay-java-baidu/pom.xml index 2aa2bda..ea4e52a 100644 --- a/pay-java-baidu/pom.xml +++ b/pay-java-baidu/pom.xml @@ -17,6 +17,11 @@ pay-java-common + + org.junit.jupiter + junit-jupiter + test + diff --git a/pay-java-baidu/src/com/egzosn/pay/baidu/api/BaiduPayConfigStorage.java b/pay-java-baidu/src/main/java/com/egzosn/pay/baidu/api/BaiduPayConfigStorage.java similarity index 100% rename from pay-java-baidu/src/com/egzosn/pay/baidu/api/BaiduPayConfigStorage.java rename to pay-java-baidu/src/main/java/com/egzosn/pay/baidu/api/BaiduPayConfigStorage.java diff --git a/pay-java-baidu/src/com/egzosn/pay/baidu/api/BaiduPayService.java b/pay-java-baidu/src/main/java/com/egzosn/pay/baidu/api/BaiduPayService.java similarity index 97% rename from pay-java-baidu/src/com/egzosn/pay/baidu/api/BaiduPayService.java rename to pay-java-baidu/src/main/java/com/egzosn/pay/baidu/api/BaiduPayService.java index 3826166..47a7c8b 100644 --- a/pay-java-baidu/src/com/egzosn/pay/baidu/api/BaiduPayService.java +++ b/pay-java-baidu/src/main/java/com/egzosn/pay/baidu/api/BaiduPayService.java @@ -2,12 +2,6 @@ package com.egzosn.pay.baidu.api; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONObject; -import com.egzosn.pay.baidu.bean.BaiduPayOrder; -import com.egzosn.pay.baidu.bean.BaiduRefundOrder; -import com.egzosn.pay.baidu.bean.BaiduTransactionType; -import com.egzosn.pay.baidu.bean.type.AuditStatus; -import com.egzosn.pay.baidu.util.Asserts; -import com.egzosn.pay.baidu.util.NoNullMap; import com.egzosn.pay.common.api.BasePayService; import com.egzosn.pay.common.bean.*; import com.egzosn.pay.common.http.HttpConfigStorage; @@ -15,6 +9,12 @@ import com.egzosn.pay.common.http.UriVariables; import com.egzosn.pay.common.util.DateUtils; import com.egzosn.pay.common.util.sign.SignUtils; import com.egzosn.pay.common.util.str.StringUtils; +import com.egzosn.pay.baidu.bean.BaiduPayOrder; +import com.egzosn.pay.baidu.bean.BaiduRefundOrder; +import com.egzosn.pay.baidu.bean.BaiduTransactionType; +import com.egzosn.pay.baidu.bean.type.AuditStatus; +import com.egzosn.pay.baidu.util.Asserts; +import com.egzosn.pay.baidu.util.NoNullMap; import java.math.BigDecimal; import java.util.Date; @@ -22,7 +22,7 @@ import java.util.HashMap; import java.util.Map; -public class BaiduPayService extends BasePayService { +public class BaiduPayService extends BasePayService { public static final String APP_KEY = "appKey"; public static final String APP_ID = "appId"; public static final String DEAL_ID = "dealId"; @@ -43,11 +43,11 @@ public class BaiduPayService extends BasePayService { public static final String RESPONSE_STATUS = "status"; - public BaiduPayService(BaiduPayConfigStorage payConfigStorage) { + public BaiduPayService(com.egzosn.pay.baidu.api.BaiduPayConfigStorage payConfigStorage) { super(payConfigStorage); } - public BaiduPayService(BaiduPayConfigStorage payConfigStorage, + public BaiduPayService(com.egzosn.pay.baidu.api.BaiduPayConfigStorage payConfigStorage, HttpConfigStorage configStorage) { super(payConfigStorage, configStorage); } @@ -226,17 +226,20 @@ public class BaiduPayService extends BasePayService { } @Override + @Deprecated public String buildRequest(Map orderInfo, MethodType method) { throw new UnsupportedOperationException("百度不支持PC支付"); } @Override + @Deprecated public String getQrPay(PayOrder order) { throw new UnsupportedOperationException("百度不支持扫码付"); } @Override + @Deprecated public Map microPay(PayOrder order) { throw new UnsupportedOperationException("百度不支持刷卡付"); } @@ -247,6 +250,7 @@ public class BaiduPayService extends BasePayService { } @Override + @Deprecated public Map close(String tradeNo, String outTradeNo) { throw new UnsupportedOperationException("不支持该操作"); } @@ -269,6 +273,7 @@ public class BaiduPayService extends BasePayService { } @Override + @Deprecated public Map refund(RefundOrder refundOrder) { throw new UnsupportedOperationException("请使用 " + getClass().getName() + "#refundUseBaidu"); } diff --git a/pay-java-baidu/src/com/egzosn/pay/baidu/bean/BaiduPayOrder.java b/pay-java-baidu/src/main/java/com/egzosn/pay/baidu/bean/BaiduPayOrder.java similarity index 100% rename from pay-java-baidu/src/com/egzosn/pay/baidu/bean/BaiduPayOrder.java rename to pay-java-baidu/src/main/java/com/egzosn/pay/baidu/bean/BaiduPayOrder.java diff --git a/pay-java-baidu/src/com/egzosn/pay/baidu/bean/BaiduRefundOrder.java b/pay-java-baidu/src/main/java/com/egzosn/pay/baidu/bean/BaiduRefundOrder.java similarity index 100% rename from pay-java-baidu/src/com/egzosn/pay/baidu/bean/BaiduRefundOrder.java rename to pay-java-baidu/src/main/java/com/egzosn/pay/baidu/bean/BaiduRefundOrder.java diff --git a/pay-java-baidu/src/com/egzosn/pay/baidu/bean/BaiduTransactionType.java b/pay-java-baidu/src/main/java/com/egzosn/pay/baidu/bean/BaiduTransactionType.java similarity index 100% rename from pay-java-baidu/src/com/egzosn/pay/baidu/bean/BaiduTransactionType.java rename to pay-java-baidu/src/main/java/com/egzosn/pay/baidu/bean/BaiduTransactionType.java diff --git a/pay-java-baidu/src/com/egzosn/pay/baidu/bean/type/AuditStatus.java b/pay-java-baidu/src/main/java/com/egzosn/pay/baidu/bean/type/AuditStatus.java similarity index 100% rename from pay-java-baidu/src/com/egzosn/pay/baidu/bean/type/AuditStatus.java rename to pay-java-baidu/src/main/java/com/egzosn/pay/baidu/bean/type/AuditStatus.java diff --git a/pay-java-baidu/src/com/egzosn/pay/baidu/package-info.java b/pay-java-baidu/src/main/java/com/egzosn/pay/baidu/package-info.java similarity index 100% rename from pay-java-baidu/src/com/egzosn/pay/baidu/package-info.java rename to pay-java-baidu/src/main/java/com/egzosn/pay/baidu/package-info.java diff --git a/pay-java-baidu/src/com/egzosn/pay/baidu/util/Asserts.java b/pay-java-baidu/src/main/java/com/egzosn/pay/baidu/util/Asserts.java similarity index 100% rename from pay-java-baidu/src/com/egzosn/pay/baidu/util/Asserts.java rename to pay-java-baidu/src/main/java/com/egzosn/pay/baidu/util/Asserts.java diff --git a/pay-java-baidu/src/com/egzosn/pay/baidu/util/NoNullMap.java b/pay-java-baidu/src/main/java/com/egzosn/pay/baidu/util/NoNullMap.java similarity index 100% rename from pay-java-baidu/src/com/egzosn/pay/baidu/util/NoNullMap.java rename to pay-java-baidu/src/main/java/com/egzosn/pay/baidu/util/NoNullMap.java diff --git a/pay-java-baidu/src/test/java/com/egzosn/pay/baidu/api/BaiduPayServiceTest.java b/pay-java-baidu/src/test/java/com/egzosn/pay/baidu/api/BaiduPayServiceTest.java new file mode 100644 index 0000000..6f9b5c2 --- /dev/null +++ b/pay-java-baidu/src/test/java/com/egzosn/pay/baidu/api/BaiduPayServiceTest.java @@ -0,0 +1,24 @@ +package com.egzosn.pay.baidu.api; + +import org.junit.jupiter.api.Test; + +/** + * Created by hocgin on 2019/11/24. + * email: hocgin@gmail.com + * + * @author hocgin + */ +public class BaiduPayServiceTest { + + @Test + public void orderInfo() { + BaiduPayConfigStorage configStorage = new BaiduPayConfigStorage(); + configStorage.setAppid("APP ID"); + configStorage.setAppKey("APP KEY"); + configStorage.setDealId("DEAL ID"); + configStorage.setKeyPublic("KEY PUBLIC"); + + BaiduPayService payService = new BaiduPayService(configStorage); + // payService.refund() + } +} diff --git a/pom.xml b/pom.xml index 5ae16b1..76479a2 100644 --- a/pom.xml +++ b/pom.xml @@ -64,6 +64,7 @@ 1.2.17 1.2.58 3.3.1 + 5.5.1 @@ -105,6 +106,12 @@ ${zxing.version} + + org.junit.jupiter + junit-jupiter + ${junit.version} + + -- Gitee From f0a080fcb34605bbecc8c174b0136bfa42fb0ae3 Mon Sep 17 00:00:00 2001 From: hocgin Date: Sun, 24 Nov 2019 17:02:17 +0800 Subject: [PATCH 070/299] =?UTF-8?q?#=20=E6=B3=A8=E9=87=8A=E8=A1=A5?= =?UTF-8?q?=E5=85=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../egzosn/pay/baidu/api/BaiduPayService.java | 161 +++++++++++++++--- .../pay/baidu/bean/BaiduRefundOrder.java | 12 ++ .../pay/baidu/bean/BaiduTransactionType.java | 20 ++- 3 files changed, 167 insertions(+), 26 deletions(-) diff --git a/pay-java-baidu/src/main/java/com/egzosn/pay/baidu/api/BaiduPayService.java b/pay-java-baidu/src/main/java/com/egzosn/pay/baidu/api/BaiduPayService.java index 47a7c8b..fa05920 100644 --- a/pay-java-baidu/src/main/java/com/egzosn/pay/baidu/api/BaiduPayService.java +++ b/pay-java-baidu/src/main/java/com/egzosn/pay/baidu/api/BaiduPayService.java @@ -2,6 +2,12 @@ package com.egzosn.pay.baidu.api; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONObject; +import com.egzosn.pay.baidu.bean.BaiduPayOrder; +import com.egzosn.pay.baidu.bean.BaiduRefundOrder; +import com.egzosn.pay.baidu.bean.BaiduTransactionType; +import com.egzosn.pay.baidu.bean.type.AuditStatus; +import com.egzosn.pay.baidu.util.Asserts; +import com.egzosn.pay.baidu.util.NoNullMap; import com.egzosn.pay.common.api.BasePayService; import com.egzosn.pay.common.bean.*; import com.egzosn.pay.common.http.HttpConfigStorage; @@ -9,12 +15,6 @@ import com.egzosn.pay.common.http.UriVariables; import com.egzosn.pay.common.util.DateUtils; import com.egzosn.pay.common.util.sign.SignUtils; import com.egzosn.pay.common.util.str.StringUtils; -import com.egzosn.pay.baidu.bean.BaiduPayOrder; -import com.egzosn.pay.baidu.bean.BaiduRefundOrder; -import com.egzosn.pay.baidu.bean.BaiduTransactionType; -import com.egzosn.pay.baidu.bean.type.AuditStatus; -import com.egzosn.pay.baidu.util.Asserts; -import com.egzosn.pay.baidu.util.NoNullMap; import java.math.BigDecimal; import java.util.Date; @@ -52,6 +52,12 @@ public class BaiduPayService extends BasePayService params) { if (!RESPONSE_SUCCESS.equals(params.get(RESPONSE_STATUS))) { @@ -60,9 +66,15 @@ public class BaiduPayService extends BasePayService params, String sign) { - String keyPrivate = payConfigStorage.getKeyPrivate(); String rsaSign = String.valueOf(params.get(RSA_SIGN)); String targetRsaSign = getRsaSign(params, RSA_SIGN); LOG.debug("百度返回的签名: " + rsaSign + " 本地产生的签名: " + targetRsaSign); @@ -74,13 +86,19 @@ public class BaiduPayService extends BasePayService orderInfo(PayOrder order) { if (!(order instanceof BaiduPayOrder)) { throw new UnsupportedOperationException("请使用 " + BaiduPayOrder.class.getName()); } NoNullMap params = getUseOrderInfoParams(order); - String rsaSign = getRsaSignUserOrderInfo(params, payConfigStorage.getKeyPrivate()); + String rsaSign = getRsaSign(params, RSA_SIGN); params.putIfNoNull(RSA_SIGN, rsaSign); return params; } @@ -119,6 +137,13 @@ public class BaiduPayService extends BasePayService orderInfo, @@ -232,29 +264,64 @@ public class BaiduPayService extends BasePayService microPay(PayOrder order) { throw new UnsupportedOperationException("百度不支持刷卡付"); } + /** + * 查询订单 + * + * @param tradeNo 支付平台订单号 + * @param outTradeNo 商户单号 + * @return + */ @Override public Map query(String tradeNo, String outTradeNo) { return secondaryInterface(tradeNo, outTradeNo, BaiduTransactionType.PAY_QUERY); } + /** + * 百度不支持该操作 + * + * @param tradeNo 支付平台订单号 + * @param outTradeNo 商户单号 + * @return + */ @Override @Deprecated public Map close(String tradeNo, String outTradeNo) { throw new UnsupportedOperationException("不支持该操作"); } + /** + * 退款, 请使用 {@link com.egzosn.pay.baidu.api.BaiduPayService#refundUseBaidu} + * + * @param orderId + * @param userId + * @param refundAmount 退款金额 + * @param totalAmount 总金额 + * @return + */ @Override @Deprecated public Map refund(String orderId, @@ -264,6 +331,16 @@ public class BaiduPayService extends BasePayService refundUseBaidu(Long orderId, Long userId, Integer refundType, @@ -272,12 +349,24 @@ public class BaiduPayService extends BasePayService refund(RefundOrder refundOrder) { throw new UnsupportedOperationException("请使用 " + getClass().getName() + "#refundUseBaidu"); } + /** + * 退款, 请使用 {@link com.egzosn.pay.baidu.api.BaiduPayService#refundUseBaidu} + * + * @param refundOrder + * @return + */ public Map refundUseBaidu(BaiduRefundOrder refundOrder) { Map parameters = getUseQueryPay(); BaiduTransactionType transactionType = BaiduTransactionType.APPLY_REFUND; @@ -295,6 +384,8 @@ public class BaiduPayService extends BasePayService refundquery(RefundOrder refundOrder) { return refundquery(refundOrder.getTradeNo(), refundOrder.getOutTradeNo()); } + /** + * 下载资金账单 + * + * @param billDate 账单时间:日账单格式为yyyy-MM-dd,月账单格式为yyyy-MM。 + * @param access_token + * @return + */ @Override public Map downloadbill(Date billDate, String access_token) { Map parameters = new HashMap<>(); @@ -327,6 +431,13 @@ public class BaiduPayService extends BasePayService downloadOrderBill(Date billDate, String access_token) { Map parameters = new HashMap<>(); parameters.put("access_token", access_token); @@ -335,6 +446,14 @@ public class BaiduPayService extends BasePayService secondaryInterface(Object orderId, String siteId, @@ -350,24 +469,24 @@ public class BaiduPayService extends BasePayService params, String privateKey) { - Map signParams = new HashMap<>(); - signParams.put(APP_KEY, String.valueOf(params.get(APP_KEY))); - signParams.put(DEAL_ID, String.valueOf(params.get(DEAL_ID))); - signParams.put(TP_ORDER_ID, String.valueOf(params.get(TP_ORDER_ID))); - signParams.put(TOTAL_AMOUNT, String.valueOf(params.get(TOTAL_AMOUNT))); - if (signParams.containsValue(null)) { - throw new IllegalArgumentException("参数 " + signParams.keySet().toString() + " 均为必填"); - } - - return SignUtils.RSA.sign(params, privateKey, "UTF-8"); - } - + /** + * 签名 + * + * @param params + * @param ignoreKeys + * @return + */ private String getRsaSign(Map params, String... ignoreKeys) { String waitSignVal = SignUtils.parameterText(params, "&", false, ignoreKeys); return SignUtils.RSA.createSign(waitSignVal, payConfigStorage.getKeyPrivate(), "UTF-8"); diff --git a/pay-java-baidu/src/main/java/com/egzosn/pay/baidu/bean/BaiduRefundOrder.java b/pay-java-baidu/src/main/java/com/egzosn/pay/baidu/bean/BaiduRefundOrder.java index ee68440..6386be8 100644 --- a/pay-java-baidu/src/main/java/com/egzosn/pay/baidu/bean/BaiduRefundOrder.java +++ b/pay-java-baidu/src/main/java/com/egzosn/pay/baidu/bean/BaiduRefundOrder.java @@ -5,9 +5,21 @@ import com.egzosn.pay.common.bean.RefundOrder; import java.math.BigDecimal; public class BaiduRefundOrder extends RefundOrder { + /** + * 平台用户ID + */ private Long userId; + /** + * 退款类型 + */ private Integer refundType; + /** + * 退款原因 + */ private String refundReason; + /** + * 平台订单ID + */ private String tpOrderId; /** * 退款金额,单位:分,发起部分退款时必传 diff --git a/pay-java-baidu/src/main/java/com/egzosn/pay/baidu/bean/BaiduTransactionType.java b/pay-java-baidu/src/main/java/com/egzosn/pay/baidu/bean/BaiduTransactionType.java index 3e23333..3c3838f 100644 --- a/pay-java-baidu/src/main/java/com/egzosn/pay/baidu/bean/BaiduTransactionType.java +++ b/pay-java-baidu/src/main/java/com/egzosn/pay/baidu/bean/BaiduTransactionType.java @@ -3,15 +3,25 @@ package com.egzosn.pay.baidu.bean; import com.egzosn.pay.common.bean.TransactionType; public enum BaiduTransactionType implements TransactionType { - // 查询支付状态 + /** + * 查询支付状态 + */ PAY_QUERY("https://dianshang.baidu.com/platform/entity/openapi/queryorderdetail", "PAY_QUERY"), - // 取消核销 + /** + * 取消核销 + */ REFUND_QUERY("https://nop.nuomi.com/nop/server/rest", "nuomi.cashier.syncorderstatus"), - // 下载资金账单 + /** + * 下载资金账单 + */ DOWNLOAD_BILL("https://openapi.baidu.com/rest/2.0/smartapp/pay/paymentservice/capitaBill", "DOWNLOAD_BILL"), - // 下载订单对账单 + /** + * 下载订单对账单 + */ DOWNLOAD_ORDER_BILL("https://openapi.baidu.com/rest/2.0/smartapp/pay/paymentservice/orderBill", "DOWNLOAD_ORDER_BILL"), - // 申请退款 + /** + * 申请退款 + */ APPLY_REFUND("https://nop.nuomi.com/nop/server/rest", "nuomi.cashier.applyorderrefund"); private final String method; private final String url; -- Gitee From b8298c31004c6e373e8be694b49d8efd228284c3 Mon Sep 17 00:00:00 2001 From: hocgin Date: Sun, 24 Nov 2019 17:02:50 +0800 Subject: [PATCH 071/299] =?UTF-8?q?#=20=E6=B3=A8=E9=87=8A=E8=A1=A5?= =?UTF-8?q?=E5=85=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/egzosn/pay/baidu/bean/BaiduTransactionType.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pay-java-baidu/src/main/java/com/egzosn/pay/baidu/bean/BaiduTransactionType.java b/pay-java-baidu/src/main/java/com/egzosn/pay/baidu/bean/BaiduTransactionType.java index 3c3838f..7a0cefe 100644 --- a/pay-java-baidu/src/main/java/com/egzosn/pay/baidu/bean/BaiduTransactionType.java +++ b/pay-java-baidu/src/main/java/com/egzosn/pay/baidu/bean/BaiduTransactionType.java @@ -6,7 +6,7 @@ public enum BaiduTransactionType implements TransactionType { /** * 查询支付状态 */ - PAY_QUERY("https://dianshang.baidu.com/platform/entity/openapi/queryorderdetail", "PAY_QUERY"), + PAY_QUERY("https://dianshang.baidu.com/platform/entity/openapi/queryorderdetail", null), /** * 取消核销 */ @@ -14,11 +14,11 @@ public enum BaiduTransactionType implements TransactionType { /** * 下载资金账单 */ - DOWNLOAD_BILL("https://openapi.baidu.com/rest/2.0/smartapp/pay/paymentservice/capitaBill", "DOWNLOAD_BILL"), + DOWNLOAD_BILL("https://openapi.baidu.com/rest/2.0/smartapp/pay/paymentservice/capitaBill", null), /** * 下载订单对账单 */ - DOWNLOAD_ORDER_BILL("https://openapi.baidu.com/rest/2.0/smartapp/pay/paymentservice/orderBill", "DOWNLOAD_ORDER_BILL"), + DOWNLOAD_ORDER_BILL("https://openapi.baidu.com/rest/2.0/smartapp/pay/paymentservice/orderBill", null), /** * 申请退款 */ -- Gitee From ecc0c5e3d3a697090b25ef4700b716586e985053 Mon Sep 17 00:00:00 2001 From: hocgin Date: Sun, 24 Nov 2019 17:12:54 +0800 Subject: [PATCH 072/299] =?UTF-8?q?#=20=E6=B3=A8=E9=87=8A=E8=A1=A5?= =?UTF-8?q?=E5=85=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../egzosn/pay/baidu/api/BaiduPayService.java | 39 +++++++++---------- .../com/egzosn/pay/baidu/util/NoNullMap.java | 14 ------- 2 files changed, 18 insertions(+), 35 deletions(-) delete mode 100644 pay-java-baidu/src/main/java/com/egzosn/pay/baidu/util/NoNullMap.java diff --git a/pay-java-baidu/src/main/java/com/egzosn/pay/baidu/api/BaiduPayService.java b/pay-java-baidu/src/main/java/com/egzosn/pay/baidu/api/BaiduPayService.java index fa05920..2d66ff7 100644 --- a/pay-java-baidu/src/main/java/com/egzosn/pay/baidu/api/BaiduPayService.java +++ b/pay-java-baidu/src/main/java/com/egzosn/pay/baidu/api/BaiduPayService.java @@ -7,7 +7,6 @@ import com.egzosn.pay.baidu.bean.BaiduRefundOrder; import com.egzosn.pay.baidu.bean.BaiduTransactionType; import com.egzosn.pay.baidu.bean.type.AuditStatus; import com.egzosn.pay.baidu.util.Asserts; -import com.egzosn.pay.baidu.util.NoNullMap; import com.egzosn.pay.common.api.BasePayService; import com.egzosn.pay.common.bean.*; import com.egzosn.pay.common.http.HttpConfigStorage; @@ -22,7 +21,7 @@ import java.util.HashMap; import java.util.Map; -public class BaiduPayService extends BasePayService { +public class BaiduPayService extends BasePayService { public static final String APP_KEY = "appKey"; public static final String APP_ID = "appId"; public static final String DEAL_ID = "dealId"; @@ -43,11 +42,11 @@ public class BaiduPayService extends BasePayService orderInfo(PayOrder order) { - if (!(order instanceof BaiduPayOrder)) { - throw new UnsupportedOperationException("请使用 " + BaiduPayOrder.class.getName()); - } - NoNullMap params = getUseOrderInfoParams(order); + public Map orderInfo(BaiduPayOrder order) { + Map params = getUseOrderInfoParams(order); String rsaSign = getRsaSign(params, RSA_SIGN); - params.putIfNoNull(RSA_SIGN, rsaSign); + params.put(RSA_SIGN, rsaSign); return params; } @@ -122,18 +118,19 @@ public class BaiduPayService extends BasePayService getUseOrderInfoParams(PayOrder order) { + private Map getUseOrderInfoParams(PayOrder order) { BaiduPayOrder payOrder = (BaiduPayOrder) order; - NoNullMap result = new NoNullMap<>(); + Map result = new HashMap<>(); String appKey = payConfigStorage.getAppKey(); String dealId = payConfigStorage.getDealId(); - result.putIfNoNull(APP_KEY, appKey) - .putIfNoNull(TP_ORDER_ID, payOrder.getTradeNo()) - .putIfNoNull(DEAL_ID, dealId) - .putIfNoNull(DEAL_TITLE, payOrder.getSubject()) - .putIfNoNull(SIGN_FIELDS_RANGE, payOrder.getSignFieldsRange()) - .putIfNoNull(BIZ_INFO, JSON.toJSONString(payOrder.getBizInfo())) - .putIfNoNull(TOTAL_AMOUNT, String.valueOf(order.getPrice())); + result.put(APP_KEY, appKey); + result.put(TP_ORDER_ID, payOrder.getTradeNo()); + result.put(DEAL_ID, dealId); + result.put(DEAL_TITLE, payOrder.getSubject()); + result.put(SIGN_FIELDS_RANGE, payOrder.getSignFieldsRange()); + result.put(BIZ_INFO, JSON.toJSONString(payOrder.getBizInfo())); + result.put(TOTAL_AMOUNT, String.valueOf(order.getPrice())); + return result; } @@ -272,7 +269,7 @@ public class BaiduPayService extends BasePayService microPay(PayOrder order) { + public Map microPay(BaiduPayOrder order) { throw new UnsupportedOperationException("百度不支持刷卡付"); } diff --git a/pay-java-baidu/src/main/java/com/egzosn/pay/baidu/util/NoNullMap.java b/pay-java-baidu/src/main/java/com/egzosn/pay/baidu/util/NoNullMap.java deleted file mode 100644 index 7e24a13..0000000 --- a/pay-java-baidu/src/main/java/com/egzosn/pay/baidu/util/NoNullMap.java +++ /dev/null @@ -1,14 +0,0 @@ -package com.egzosn.pay.baidu.util; - -import java.util.HashMap; - -public class NoNullMap extends HashMap { - - public NoNullMap putIfNoNull(K key, V value) { - if (value != null) { - put(key, value); - } - return this; - } - -} -- Gitee From b9a6173ab8cf490365bff5a06f86c4279f4f2e55 Mon Sep 17 00:00:00 2001 From: egzosn Date: Sun, 24 Nov 2019 22:03:28 +0800 Subject: [PATCH 073/299] =?UTF-8?q?=E6=94=AF=E4=BB=98=E5=AE=9D=E5=88=B7?= =?UTF-8?q?=E8=84=B8=E5=8F=91=E8=B5=B7=E6=94=AF=E4=BB=98=E6=8E=A5=E5=8F=A3?= =?UTF-8?q?=EF=BC=8C=E5=94=A4=E8=B5=B7=E6=94=AF=E4=BB=98=E6=9A=82=E6=9C=AA?= =?UTF-8?q?=E7=BC=96=E5=86=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/egzosn/pay/ali/api/AliPayService.java | 1 + .../egzosn/pay/ali/bean/AliTransactionType.java | 17 ++++++++++++++++- 2 files changed, 17 insertions(+), 1 deletion(-) 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 a0e76c4..1dcdeeb 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 @@ -216,6 +216,7 @@ public class AliPayService extends BasePayService break; case BAR_CODE: case WAVE_CODE: + case SECURITY_CODE: bizContent.put("scene", order.getTransactionType().toString().toLowerCase()); bizContent.put(PRODUCT_CODE, "FACE_TO_FACE_PAYMENT"); bizContent.put("auth_code", order.getAuthCode()); 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 cf4d912..b97e5ef 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 @@ -49,6 +49,16 @@ public enum AliTransactionType implements TransactionType { * 声波付 */ WAVE_CODE("alipay.trade.pay"), + /** + * 刷脸付 + */ + SECURITY_CODE("alipay.trade.pay"), + /** + * 人脸初始化刷脸付 + * 暂时未接入 + * + */ + SMILEPAY("zoloz.authentication.customer.smilepay.initialize"), //交易辅助接口 /** @@ -86,7 +96,12 @@ public enum AliTransactionType implements TransactionType { /** * 转账查询 */ - TRANS_QUERY("alipay.fund.trans.order.query") + TRANS_QUERY("alipay.fund.trans.order.query"), + /** + * 查询刷脸结果信息 + * 暂时未接入 + */ + FTOKEN_QUERY("zoloz.authentication.customer.ftoken.query") ; -- Gitee From 5e7e9a95ad90e28688275a9522abac40dfcdc4d3 Mon Sep 17 00:00:00 2001 From: egzosn Date: Sun, 24 Nov 2019 22:05:16 +0800 Subject: [PATCH 074/299] =?UTF-8?q?=E5=88=B7=E8=84=B8=E4=BB=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/main/java/com/egzosn/pay/common/api/PayService.java | 2 +- .../main/java/com/egzosn/pay/wx/bean/WxTransactionType.java | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/pay-java-common/src/main/java/com/egzosn/pay/common/api/PayService.java b/pay-java-common/src/main/java/com/egzosn/pay/common/api/PayService.java index 7066266..70fe5e5 100644 --- a/pay-java-common/src/main/java/com/egzosn/pay/common/api/PayService.java +++ b/pay-java-common/src/main/java/com/egzosn/pay/common/api/PayService.java @@ -171,7 +171,7 @@ public interface PayService { /** * 刷卡付,pos主动扫码付款(条码付) - * + * 刷脸付 * @param order 发起支付的订单信息 * @return 返回支付结果 */ 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 990d16c..740f9b5 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 @@ -48,8 +48,7 @@ public enum WxTransactionType implements TransactionType { */ APP("pay/unifiedorder"), /** - * 刷 - 脸支付 + * 刷脸支付 */ FACEPAY("pay/facepay"){ @Override -- Gitee From ee83d5c3bbe243fafd3dcf06e9a758a89beb802e Mon Sep 17 00:00:00 2001 From: egzosn Date: Mon, 25 Nov 2019 10:54:26 +0800 Subject: [PATCH 075/299] =?UTF-8?q?=E8=BF=9E=E6=8E=A5=E6=B1=A0=E4=B8=ADssl?= =?UTF-8?q?=E8=AF=81=E4=B9=A6=E5=A4=9A=E6=AC=A1=E5=8A=A0=E8=BD=BD=E9=97=AE?= =?UTF-8?q?=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../pay/common/http/HttpRequestTemplate.java | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/pay-java-common/src/main/java/com/egzosn/pay/common/http/HttpRequestTemplate.java b/pay-java-common/src/main/java/com/egzosn/pay/common/http/HttpRequestTemplate.java index c816e32..025b03f 100644 --- a/pay-java-common/src/main/java/com/egzosn/pay/common/http/HttpRequestTemplate.java +++ b/pay-java-common/src/main/java/com/egzosn/pay/common/http/HttpRequestTemplate.java @@ -52,7 +52,9 @@ public class HttpRequestTemplate { protected HttpHost httpProxy; - HttpConfigStorage configStorage; + protected HttpConfigStorage configStorage; + + private SSLConnectionSocketFactory sslsf; /** * 获取代理带代理地址的 HttpHost * @return 获取代理带代理地址的 HttpHost @@ -73,9 +75,9 @@ public class HttpRequestTemplate { .custom() //网络提供者 .setDefaultCredentialsProvider(createCredentialsProvider(configStorage)) + .setConnectionManager(connectionManager(configStorage)) //设置httpclient的SSLSocketFactory .setSSLSocketFactory(createSSL(configStorage)) - .setConnectionManager(connectionManager(configStorage)) .setDefaultRequestConfig(createRequestConfig(configStorage)) .build(); if (null == connectionManager) { @@ -114,10 +116,12 @@ public class HttpRequestTemplate { * @return SSLConnectionSocketFactory Layered socket factory for TLS/SSL connections. */ public SSLConnectionSocketFactory createSSL( HttpConfigStorage configStorage){ - + if (null != sslsf){ + return sslsf; + } if (null == configStorage.getKeystore()){ try { - return new SSLConnectionSocketFactory(SSLContext.getDefault()); + return sslsf = new SSLConnectionSocketFactory(SSLContext.getDefault()); } catch (NoSuchAlgorithmException e) { LOG.error(e); } @@ -139,7 +143,7 @@ public class HttpRequestTemplate { .loadKeyMaterial(keyStore, password).build(); //指定TLS版本 - SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory( + sslsf = new SSLConnectionSocketFactory( sslcontext, new String[]{"TLSv1"}, null, new DefaultHostnameVerifier()); -- Gitee From b38c2b7f2a3bbe3c012d490aa553ebfc22551602 Mon Sep 17 00:00:00 2001 From: egzosn Date: Mon, 25 Nov 2019 20:10:03 +0800 Subject: [PATCH 076/299] =?UTF-8?q?=E7=B1=BB=E9=A2=84=E8=AE=A2=E5=8D=95?= =?UTF-8?q?=E6=B3=9B=E5=9E=8B=E7=BA=A6=E6=9D=9F=E6=9C=AC=E7=89=88=E6=9C=AC?= =?UTF-8?q?=E6=9A=82=E6=97=B6=E7=A7=BB=E9=99=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/egzosn/pay/ali/api/AliPayService.java | 2 +- .../pay/ali/before/api/AliPayService.java | 2 +- .../egzosn/pay/common/api/BasePayService.java | 8 +-- .../com/egzosn/pay/common/api/PayService.java | 14 ++--- .../pay/common/http/HttpRequestTemplate.java | 60 ++++++++++--------- .../egzosn/pay/fuiou/api/FuiouPayService.java | 2 +- .../pay/payoneer/api/AdvancedPayService.java | 2 +- .../pay/payoneer/api/PayoneerPayService.java | 2 +- .../pay/paypal/api/PayPalPayService.java | 2 +- .../egzosn/pay/union/api/UnionPayService.java | 2 +- .../wx/youdian/api/WxYouDianPayService.java | 2 +- .../com/egzosn/pay/wx/api/WxPayService.java | 2 +- .../egzosn/pay/yiji/api/YiJiPayService.java | 2 +- 13 files changed, 53 insertions(+), 49 deletions(-) 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 1dcdeeb..ff51eb2 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 @@ -26,7 +26,7 @@ import java.util.*; * email egzosn@gmail.com * date 2017-2-22 20:09 */ -public class AliPayService extends BasePayService { +public class AliPayService extends BasePayService { /** * 正式测试环境 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 5ad9318..ae9c93b 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 @@ -35,7 +35,7 @@ import static com.egzosn.pay.ali.api.AliPayService.SIGN; * @see com.egzosn.pay.ali.api.AliPayService */ @Deprecated -public class AliPayService extends BasePayService { +public class AliPayService extends BasePayService { diff --git a/pay-java-common/src/main/java/com/egzosn/pay/common/api/BasePayService.java b/pay-java-common/src/main/java/com/egzosn/pay/common/api/BasePayService.java index ef39af7..c955e04 100644 --- a/pay-java-common/src/main/java/com/egzosn/pay/common/api/BasePayService.java +++ b/pay-java-common/src/main/java/com/egzosn/pay/common/api/BasePayService.java @@ -26,7 +26,7 @@ import java.util.*; * date 2017/3/5 20:36 * */ -public abstract class BasePayService implements PayService { +public abstract class BasePayService implements PayService { protected final Log LOG = LogFactory.getLog(getClass()); protected PC payConfigStorage; @@ -138,7 +138,7 @@ public abstract class BasePayService String toPay(O order) { Map orderInfo = orderInfo(order); return buildRequest(orderInfo, MethodType.POST); } @@ -150,7 +150,7 @@ public abstract class BasePayService BufferedImage genQrPay(O order) { return MatrixToImageWriter.writeInfoToJpgBuff(getQrPay(order)); } @@ -463,7 +463,7 @@ public abstract class BasePayService preOrderHandler(Map orderInfo, O payOrder){ + public Map preOrderHandler(Map orderInfo, O payOrder){ return orderInfo; } diff --git a/pay-java-common/src/main/java/com/egzosn/pay/common/api/PayService.java b/pay-java-common/src/main/java/com/egzosn/pay/common/api/PayService.java index 70fe5e5..346b3f5 100644 --- a/pay-java-common/src/main/java/com/egzosn/pay/common/api/PayService.java +++ b/pay-java-common/src/main/java/com/egzosn/pay/common/api/PayService.java @@ -20,7 +20,7 @@ import java.util.Map; * date 2016-5-18 14:09:01 * */ -public interface PayService { +public interface PayService { /** @@ -88,7 +88,7 @@ public interface PayService { * @return 订单信息 * @see PayOrder 支付订单信息 */ - Map orderInfo(O order); + Map orderInfo(O order); /** * 页面转跳支付, 返回对应页面重定向信息 @@ -96,7 +96,7 @@ public interface PayService { * @param order 订单信息 * @return 对应页面重定向信息 */ - String toPay(O order); + String toPay(O order); /** * 创建签名 @@ -160,14 +160,14 @@ public interface PayService { * @param order 发起支付的订单信息 * @return 返回图片信息,支付时需要的 */ - BufferedImage genQrPay(O order); + BufferedImage genQrPay(O order); /** * 获取输出二维码信息, * * @param order 发起支付的订单信息 * @return 返回二维码信息,,支付时需要的 */ - String getQrPay(O order); + String getQrPay(O order); /** * 刷卡付,pos主动扫码付款(条码付) @@ -175,7 +175,7 @@ public interface PayService { * @param order 发起支付的订单信息 * @return 返回支付结果 */ - Map microPay(O order); + Map microPay(O order); /** * 交易查询接口 @@ -462,6 +462,6 @@ public interface PayService { * @param payOrder 订单信息 * @return 处理后订单信息 */ - Map preOrderHandler(Map orderInfo, O payOrder); + Map preOrderHandler(Map orderInfo, O payOrder); } diff --git a/pay-java-common/src/main/java/com/egzosn/pay/common/http/HttpRequestTemplate.java b/pay-java-common/src/main/java/com/egzosn/pay/common/http/HttpRequestTemplate.java index c816e32..ceaf45b 100644 --- a/pay-java-common/src/main/java/com/egzosn/pay/common/http/HttpRequestTemplate.java +++ b/pay-java-common/src/main/java/com/egzosn/pay/common/http/HttpRequestTemplate.java @@ -52,7 +52,9 @@ public class HttpRequestTemplate { protected HttpHost httpProxy; - HttpConfigStorage configStorage; + protected HttpConfigStorage configStorage; + + private SSLConnectionSocketFactory sslsf; /** * 获取代理带代理地址的 HttpHost * @return 获取代理带代理地址的 HttpHost @@ -73,9 +75,9 @@ public class HttpRequestTemplate { .custom() //网络提供者 .setDefaultCredentialsProvider(createCredentialsProvider(configStorage)) + .setConnectionManager(connectionManager(configStorage)) //设置httpclient的SSLSocketFactory .setSSLSocketFactory(createSSL(configStorage)) - .setConnectionManager(connectionManager(configStorage)) .setDefaultRequestConfig(createRequestConfig(configStorage)) .build(); if (null == connectionManager) { @@ -114,36 +116,38 @@ public class HttpRequestTemplate { * @return SSLConnectionSocketFactory Layered socket factory for TLS/SSL connections. */ public SSLConnectionSocketFactory createSSL( HttpConfigStorage configStorage){ - + if (null != sslsf){ + return sslsf; + } if (null == configStorage.getKeystore()){ try { - return new SSLConnectionSocketFactory(SSLContext.getDefault()); + return sslsf = new SSLConnectionSocketFactory(SSLContext.getDefault()); } catch (NoSuchAlgorithmException e) { LOG.error(e); } } - //读取本机存放的PKCS12证书文件 + //读取本机存放的PKCS12证书文件 try(InputStream instream = configStorage.getKeystoreInputStream()){ - //指定读取证书格式为PKCS12 - KeyStore keyStore = KeyStore.getInstance("PKCS12"); - - char[] password = configStorage.getStorePassword().toCharArray(); - //指定PKCS12的密码 - keyStore.load(instream, password); - // 实例化密钥库 & 初始化密钥工厂 - KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm()); - kmf.init(keyStore, password); - // 创建 SSLContext - SSLContext sslcontext = SSLContexts.custom() - .loadKeyMaterial(keyStore, password).build(); - - //指定TLS版本 - SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory( - sslcontext, new String[]{"TLSv1"}, null, - new DefaultHostnameVerifier()); - - return sslsf; + //指定读取证书格式为PKCS12 + KeyStore keyStore = KeyStore.getInstance("PKCS12"); + + char[] password = configStorage.getStorePassword().toCharArray(); + //指定PKCS12的密码 + keyStore.load(instream, password); + // 实例化密钥库 & 初始化密钥工厂 + KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm()); + kmf.init(keyStore, password); + // 创建 SSLContext + SSLContext sslcontext = SSLContexts.custom() + .loadKeyMaterial(keyStore, password).build(); + + //指定TLS版本 + sslsf = new SSLConnectionSocketFactory( + sslcontext, new String[]{"TLSv1"}, null, + new DefaultHostnameVerifier()); + + return sslsf; } catch (IOException e) { LOG.error(e); } catch (GeneralSecurityException e) { @@ -168,7 +172,7 @@ public class HttpRequestTemplate { // 需要用户认证的代理服务器 CredentialsProvider credsProvider = new BasicCredentialsProvider(); credsProvider.setCredentials( - AuthScope.ANY, + AuthScope.ANY, new UsernamePasswordCredentials(configStorage.getAuthUsername(), configStorage.getAuthPassword())); @@ -348,9 +352,9 @@ public class HttpRequestTemplate { } httpRequest.setResponseType(responseType); try (CloseableHttpResponse response = getHttpClient().execute(httpRequest)) { - return httpRequest.handleResponse(response); + return httpRequest.handleResponse(response); }catch (IOException e){ - throw new PayErrorException(new PayException("IOException", e.getLocalizedMessage())); + throw new PayErrorException(new PayException("IOException", e.getLocalizedMessage())); }finally { httpRequest.releaseConnection(); } @@ -368,6 +372,6 @@ public class HttpRequestTemplate { * @return 类型对象 */ public T doExecute(String uri, Object request, Class responseType, MethodType method){ - return doExecute(URI.create(uri), request, responseType, method); + return doExecute(URI.create(uri), request, responseType, method); } } diff --git a/pay-java-fuiou/src/main/java/com/egzosn/pay/fuiou/api/FuiouPayService.java b/pay-java-fuiou/src/main/java/com/egzosn/pay/fuiou/api/FuiouPayService.java index 2c67b2c..fdbfde6 100644 --- a/pay-java-fuiou/src/main/java/com/egzosn/pay/fuiou/api/FuiouPayService.java +++ b/pay-java-fuiou/src/main/java/com/egzosn/pay/fuiou/api/FuiouPayService.java @@ -21,7 +21,7 @@ import java.util.*; * create 2017 2017/1/16 0016 * */ -public class FuiouPayService extends BasePayService { +public class FuiouPayService extends BasePayService { /** * 正式域名 diff --git a/pay-java-payoneer/src/main/java/com/egzosn/pay/payoneer/api/AdvancedPayService.java b/pay-java-payoneer/src/main/java/com/egzosn/pay/payoneer/api/AdvancedPayService.java index 76c3a0e..bd04eff 100644 --- a/pay-java-payoneer/src/main/java/com/egzosn/pay/payoneer/api/AdvancedPayService.java +++ b/pay-java-payoneer/src/main/java/com/egzosn/pay/payoneer/api/AdvancedPayService.java @@ -16,7 +16,7 @@ import java.util.Map; * */ -public interface AdvancedPayService extends PayService { +public interface AdvancedPayService extends PayService { /** * 获取授权页面 * @param payeeId 用户id diff --git a/pay-java-payoneer/src/main/java/com/egzosn/pay/payoneer/api/PayoneerPayService.java b/pay-java-payoneer/src/main/java/com/egzosn/pay/payoneer/api/PayoneerPayService.java index f0f2de8..4d61b73 100644 --- a/pay-java-payoneer/src/main/java/com/egzosn/pay/payoneer/api/PayoneerPayService.java +++ b/pay-java-payoneer/src/main/java/com/egzosn/pay/payoneer/api/PayoneerPayService.java @@ -33,7 +33,7 @@ import java.util.*; * create 2018-01-19 * */ -public class PayoneerPayService extends BasePayService implements AdvancedPayService { +public class PayoneerPayService extends BasePayService implements AdvancedPayService { /** * 测试地址 */ diff --git a/pay-java-paypal/src/main/java/com/egzosn/pay/paypal/api/PayPalPayService.java b/pay-java-paypal/src/main/java/com/egzosn/pay/paypal/api/PayPalPayService.java index 6519c9d..b94f887 100644 --- a/pay-java-paypal/src/main/java/com/egzosn/pay/paypal/api/PayPalPayService.java +++ b/pay-java-paypal/src/main/java/com/egzosn/pay/paypal/api/PayPalPayService.java @@ -29,7 +29,7 @@ import java.util.concurrent.locks.Lock; * email egzosn@gmail.com * date 2018-4-8 ‏‎22:15:09 */ -public class PayPalPayService extends BasePayService{ +public class PayPalPayService extends BasePayService{ /** * 沙箱环境 diff --git a/pay-java-union/src/main/java/com/egzosn/pay/union/api/UnionPayService.java b/pay-java-union/src/main/java/com/egzosn/pay/union/api/UnionPayService.java index 0637aa1..90aaad6 100644 --- a/pay-java-union/src/main/java/com/egzosn/pay/union/api/UnionPayService.java +++ b/pay-java-union/src/main/java/com/egzosn/pay/union/api/UnionPayService.java @@ -38,7 +38,7 @@ import java.util.*; * create 2017 2017/11/5 * */ -public class UnionPayService extends BasePayService { +public class UnionPayService extends BasePayService { /** * 测试域名 */ 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 7134cfb..2d7775f 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 @@ -27,7 +27,7 @@ import java.util.concurrent.locks.Lock; * email egzosn@gmail.com * date 2017/01/12 22:58 */ -public class WxYouDianPayService extends BasePayService { +public class WxYouDianPayService extends BasePayService { private final static String URL = "http://life.51youdian.com/Api/CheckoutCounter/"; 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 9368a2f..5027d82 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 @@ -36,7 +36,7 @@ import static com.egzosn.pay.wx.bean.WxTransferType.*; * date 2016-5-18 14:09:01 * */ -public class WxPayService extends BasePayService { +public class WxPayService extends BasePayService { /** diff --git a/pay-java-yiji/src/main/java/com/egzosn/pay/yiji/api/YiJiPayService.java b/pay-java-yiji/src/main/java/com/egzosn/pay/yiji/api/YiJiPayService.java index 911c9a2..cece47f 100644 --- a/pay-java-yiji/src/main/java/com/egzosn/pay/yiji/api/YiJiPayService.java +++ b/pay-java-yiji/src/main/java/com/egzosn/pay/yiji/api/YiJiPayService.java @@ -27,7 +27,7 @@ import java.util.TreeMap; * email egzosn@gmail.com * * date 2019/04/15 22:51 */ -public class YiJiPayService extends BasePayService { +public class YiJiPayService extends BasePayService { /** * 正式测试环境 -- Gitee From 0be5bfa2c5c9cc151d368a44983d117a1522c036 Mon Sep 17 00:00:00 2001 From: egzosn Date: Mon, 25 Nov 2019 20:28:59 +0800 Subject: [PATCH 077/299] =?UTF-8?q?2.12.9=E5=8F=91=E5=B8=83?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pay-java-ali/pom.xml | 8 +------- pay-java-common/pom.xml | 2 +- .../java/com/egzosn/pay/common/api/PayMessageRouter.java | 1 + .../main/java/com/egzosn/pay/common/api/PayService.java | 6 ++++++ pay-java-demo/pom.xml | 2 +- pay-java-fuiou/pom.xml | 2 +- pay-java-payoneer/pom.xml | 2 +- pay-java-paypal/pom.xml | 2 +- pay-java-union/pom.xml | 2 +- pay-java-wx-youdian/pom.xml | 2 +- pay-java-wx/pom.xml | 2 +- pay-java-yiji/pom.xml | 2 +- pom.xml | 8 ++++---- 13 files changed, 21 insertions(+), 20 deletions(-) diff --git a/pay-java-ali/pom.xml b/pay-java-ali/pom.xml index 942190e..9b17eb2 100644 --- a/pay-java-ali/pom.xml +++ b/pay-java-ali/pom.xml @@ -5,7 +5,7 @@ pay-java-parent com.egzosn - 2.12.9-SNAPSHOT + 2.12.9 4.0.0 pay-java-ali @@ -19,12 +19,6 @@ pay-java-common - diff --git a/pay-java-common/pom.xml b/pay-java-common/pom.xml index ea25260..1dfa318 100644 --- a/pay-java-common/pom.xml +++ b/pay-java-common/pom.xml @@ -5,7 +5,7 @@ pay-java-parent com.egzosn - 2.12.9-SNAPSHOT + 2.12.9 4.0.0 jar diff --git a/pay-java-common/src/main/java/com/egzosn/pay/common/api/PayMessageRouter.java b/pay-java-common/src/main/java/com/egzosn/pay/common/api/PayMessageRouter.java index 990e24e..24e849b 100644 --- a/pay-java-common/src/main/java/com/egzosn/pay/common/api/PayMessageRouter.java +++ b/pay-java-common/src/main/java/com/egzosn/pay/common/api/PayMessageRouter.java @@ -124,6 +124,7 @@ public class PayMessageRouter { * 处理支付消息 * * @param payMessage 支付消息 + * @param storage 支付配置 * @return 支付输出结果 */ public PayOutMessage route(Map payMessage, PayConfigStorage storage) { diff --git a/pay-java-common/src/main/java/com/egzosn/pay/common/api/PayService.java b/pay-java-common/src/main/java/com/egzosn/pay/common/api/PayService.java index 346b3f5..ce065ea 100644 --- a/pay-java-common/src/main/java/com/egzosn/pay/common/api/PayService.java +++ b/pay-java-common/src/main/java/com/egzosn/pay/common/api/PayService.java @@ -86,6 +86,7 @@ public interface PayService { * * @param order 支付订单 * @return 订单信息 + * @param 预订单类型 * @see PayOrder 支付订单信息 */ Map orderInfo(O order); @@ -94,6 +95,7 @@ public interface PayService { * 页面转跳支付, 返回对应页面重定向信息 * * @param order 订单信息 + * @param 预订单类型 * @return 对应页面重定向信息 */ String toPay(O order); @@ -158,6 +160,7 @@ public interface PayService { * 获取输出二维码,用户返回给支付端, * * @param order 发起支付的订单信息 + * @param 预订单类型 * @return 返回图片信息,支付时需要的 */ BufferedImage genQrPay(O order); @@ -165,6 +168,7 @@ public interface PayService { * 获取输出二维码信息, * * @param order 发起支付的订单信息 + * @param 预订单类型 * @return 返回二维码信息,,支付时需要的 */ String getQrPay(O order); @@ -173,6 +177,7 @@ public interface PayService { * 刷卡付,pos主动扫码付款(条码付) * 刷脸付 * @param order 发起支付的订单信息 + * @param 预订单类型 * @return 返回支付结果 */ Map microPay(O order); @@ -460,6 +465,7 @@ public interface PayService { * 如果需要进行扩展请重写该方法即可 * @param orderInfo 商户平台预订单信息 * @param payOrder 订单信息 + * @param 预订单类型 * @return 处理后订单信息 */ Map preOrderHandler(Map orderInfo, O payOrder); diff --git a/pay-java-demo/pom.xml b/pay-java-demo/pom.xml index 144cc7f..17c0e16 100644 --- a/pay-java-demo/pom.xml +++ b/pay-java-demo/pom.xml @@ -5,7 +5,7 @@ pay-java-parent com.egzosn - 2.12.9-SNAPSHOT + 2.12.9 4.0.0 war diff --git a/pay-java-fuiou/pom.xml b/pay-java-fuiou/pom.xml index 15c9e85..63e5797 100644 --- a/pay-java-fuiou/pom.xml +++ b/pay-java-fuiou/pom.xml @@ -5,7 +5,7 @@ pay-java-parent com.egzosn - 2.12.9-SNAPSHOT + 2.12.9 4.0.0 pay-java-fuiou diff --git a/pay-java-payoneer/pom.xml b/pay-java-payoneer/pom.xml index e8a7e2e..dd049db 100644 --- a/pay-java-payoneer/pom.xml +++ b/pay-java-payoneer/pom.xml @@ -5,7 +5,7 @@ pay-java-parent com.egzosn - 2.12.9-SNAPSHOT + 2.12.9 4.0.0 pay-java-payoneer diff --git a/pay-java-paypal/pom.xml b/pay-java-paypal/pom.xml index 3cdb33f..6dbe403 100644 --- a/pay-java-paypal/pom.xml +++ b/pay-java-paypal/pom.xml @@ -5,7 +5,7 @@ pay-java-parent com.egzosn - 2.12.9-SNAPSHOT + 2.12.9 4.0.0 diff --git a/pay-java-union/pom.xml b/pay-java-union/pom.xml index 6310983..cadae0c 100644 --- a/pay-java-union/pom.xml +++ b/pay-java-union/pom.xml @@ -5,7 +5,7 @@ pay-java-parent com.egzosn - 2.12.9-SNAPSHOT + 2.12.9 4.0.0 diff --git a/pay-java-wx-youdian/pom.xml b/pay-java-wx-youdian/pom.xml index 610977d..cdd7f5d 100644 --- a/pay-java-wx-youdian/pom.xml +++ b/pay-java-wx-youdian/pom.xml @@ -5,7 +5,7 @@ pay-java-parent com.egzosn - 2.12.9-SNAPSHOT + 2.12.9 4.0.0 pay-java-wx-youdian diff --git a/pay-java-wx/pom.xml b/pay-java-wx/pom.xml index bee89ca..0842391 100644 --- a/pay-java-wx/pom.xml +++ b/pay-java-wx/pom.xml @@ -5,7 +5,7 @@ pay-java-parent com.egzosn - 2.12.9-SNAPSHOT + 2.12.9 4.0.0 pay-java-wx diff --git a/pay-java-yiji/pom.xml b/pay-java-yiji/pom.xml index 7ecd98b..c1d5845 100644 --- a/pay-java-yiji/pom.xml +++ b/pay-java-yiji/pom.xml @@ -5,7 +5,7 @@ pay-java-parent com.egzosn - 2.12.9-SNAPSHOT + 2.12.9 4.0.0 diff --git a/pom.xml b/pom.xml index 775e767..a2e4081 100644 --- a/pom.xml +++ b/pom.xml @@ -7,7 +7,7 @@ com.egzosn pay-java-parent pom - 2.12.9-SNAPSHOT + 2.12.9 Pay Java - Parent Pay Java Parent @@ -52,13 +52,13 @@ pay-java-payoneer pay-java-paypal pay-java-yiji - pay-java-demo + - 2.12.9-SNAPSHOT + 2.12.9 4.5.4 1.2.17 1.2.58 @@ -183,7 +183,7 @@ sign-artifacts - none + install sign -- Gitee From fb619526f1b6396d2c044617be1ecf658273fda3 Mon Sep 17 00:00:00 2001 From: egzosn Date: Mon, 25 Nov 2019 20:41:40 +0800 Subject: [PATCH 078/299] =?UTF-8?q?2.12.9=E5=8F=91=E5=B8=83?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pom.xml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/pom.xml b/pom.xml index a2e4081..8db4562 100644 --- a/pom.xml +++ b/pom.xml @@ -52,7 +52,7 @@ pay-java-payoneer pay-java-paypal pay-java-yiji - + pay-java-demo @@ -183,10 +183,10 @@ sign-artifacts - install - + none + -- Gitee From a4bd0f0dbfb4f878510628d74bf8da57ad718cd4 Mon Sep 17 00:00:00 2001 From: egzosn Date: Mon, 25 Nov 2019 20:42:40 +0800 Subject: [PATCH 079/299] =?UTF-8?q?2.12.9=E5=8F=91=E5=B8=83?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index ef5cfc9..bcb3242 100644 --- a/README.md +++ b/README.md @@ -21,7 +21,7 @@ com.egzosn {module-name} - 2.12.8 + 2.12.9 ``` -- Gitee From 607a4fd9c5df323d127a4fa40d1c1f3aacb59f45 Mon Sep 17 00:00:00 2001 From: egzosn Date: Tue, 26 Nov 2019 22:50:21 +0800 Subject: [PATCH 080/299] =?UTF-8?q?2.12.9=E5=8F=91=E5=B8=83?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pay-java-payoneer/README.md | 2 +- pay-java-union/README.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pay-java-payoneer/README.md b/pay-java-payoneer/README.md index 40143db..06e14f3 100644 --- a/pay-java-payoneer/README.md +++ b/pay-java-payoneer/README.md @@ -102,7 +102,7 @@ ```java - Map result = service..query(null, "我方系统单号"); + Map result = service.query(null, "我方系统单号"); ``` diff --git a/pay-java-union/README.md b/pay-java-union/README.md index dad8d53..40951df 100644 --- a/pay-java-union/README.md +++ b/pay-java-union/README.md @@ -180,7 +180,7 @@ ```java - Map result = service..query(null, "我方系统单号"); + Map result = service.query(null, "我方系统单号"); ``` -- Gitee From 31e1b2ea612d58859a592e3dc55594e640926a7b Mon Sep 17 00:00:00 2001 From: egzosn Date: Tue, 26 Nov 2019 22:53:52 +0800 Subject: [PATCH 081/299] =?UTF-8?q?2.12.9=E5=8F=91=E5=B8=83?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pay-java-ali/README.md | 2 +- pay-java-wx/README.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pay-java-ali/README.md b/pay-java-ali/README.md index 99bdc99..e50a842 100644 --- a/pay-java-ali/README.md +++ b/pay-java-ali/README.md @@ -197,7 +197,7 @@ ```java - Map result = service..query("支付宝单号", "我方系统单号"); + Map result = service.query("支付宝单号", "我方系统单号"); ``` diff --git a/pay-java-wx/README.md b/pay-java-wx/README.md index d98f264..fa69395 100644 --- a/pay-java-wx/README.md +++ b/pay-java-wx/README.md @@ -194,7 +194,7 @@ ```java - Map result = service..query("微信单号", "我方系统单号"); + Map result = service.query("微信单号", "我方系统单号"); ``` -- Gitee From f4cec5623352975fa0015285f2f7c6ad8d40b913 Mon Sep 17 00:00:00 2001 From: egan Date: Wed, 27 Nov 2019 16:45:44 +0800 Subject: [PATCH 082/299] wx --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index bcb3242..c65e3be 100644 --- a/README.md +++ b/README.md @@ -67,4 +67,4 @@ E-Mail:egzosn@gmail.com QQ群:542193977 -微信群: ![微信群](https://images.gitee.com/uploads/images/2019/1117/151422_5085eaae_1221178.jpeg "wx.jpg") +微信群: ![微信群](https://www.egzosn.com/images/wx.jpeg "wx.jpg") -- Gitee From 307009dc59910d57a57206a76f13bf68fe2583d1 Mon Sep 17 00:00:00 2001 From: egan Date: Wed, 27 Nov 2019 16:46:36 +0800 Subject: [PATCH 083/299] wx --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index c65e3be..cc7525c 100644 --- a/README.md +++ b/README.md @@ -67,4 +67,4 @@ E-Mail:egzosn@gmail.com QQ群:542193977 -微信群: ![微信群](https://www.egzosn.com/images/wx.jpeg "wx.jpg") +微信群: ![微信群](https://www.egzosn.com/images/wx.jpg "wx.jpg") -- Gitee From 16b26d4a2f62bfac5a3b38a2e9ba93b34e363795 Mon Sep 17 00:00:00 2001 From: egzosn Date: Mon, 2 Dec 2019 23:20:43 +0800 Subject: [PATCH 084/299] =?UTF-8?q?xml=E6=B7=BB=E5=8A=A0=E5=A4=9A=E5=B1=82?= =?UTF-8?q?=E8=A7=A3=E6=9E=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 24 ++++++++++ pay-java-ali/pom.xml | 2 +- pay-java-common/pom.xml | 2 +- .../java/com/egzosn/pay/common/util/XML.java | 46 +++++++++++++++++-- pay-java-demo/pom.xml | 2 +- pay-java-fuiou/pom.xml | 2 +- pay-java-payoneer/pom.xml | 2 +- pay-java-paypal/pom.xml | 2 +- pay-java-union/pom.xml | 2 +- pay-java-wx-youdian/pom.xml | 2 +- pay-java-wx/pom.xml | 2 +- pay-java-yiji/pom.xml | 2 +- pom.xml | 4 +- 13 files changed, 77 insertions(+), 17 deletions(-) create mode 100644 .gitignore diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..b40fb55 --- /dev/null +++ b/.gitignore @@ -0,0 +1,24 @@ +# Compiled class file +*.class + +# Log file +*.log + +# BlueJ files +*.ctxt + +# Mobile Tools for Java (J2ME) +.mtj.tmp/ + +# Package Files # +*.jar +*.war +*.nar +*.ear +*.zip +*.tar.gz +*.rar + +.idea +# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml +hs_err_pid* diff --git a/pay-java-ali/pom.xml b/pay-java-ali/pom.xml index 9b17eb2..35e8c8c 100644 --- a/pay-java-ali/pom.xml +++ b/pay-java-ali/pom.xml @@ -5,7 +5,7 @@ pay-java-parent com.egzosn - 2.12.9 + 2.13.1-SNAPSHOT 4.0.0 pay-java-ali diff --git a/pay-java-common/pom.xml b/pay-java-common/pom.xml index 1dfa318..0a0a62d 100644 --- a/pay-java-common/pom.xml +++ b/pay-java-common/pom.xml @@ -5,7 +5,7 @@ pay-java-parent com.egzosn - 2.12.9 + 2.13.1-SNAPSHOT 4.0.0 jar 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 2410653..b61898b 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 @@ -27,6 +27,7 @@ import java.io.IOException; import java.io.InputStream; import java.io.StringWriter; import java.nio.charset.Charset; +import java.util.List; import java.util.Map; @@ -232,10 +233,11 @@ public class XML { for (int idx = 0; idx < children.getLength(); ++idx) { Node node = children.item(idx); NodeList nodeList = node.getChildNodes(); - if (node.getNodeType() == Node.ELEMENT_NODE && nodeList.getLength() <= 1) { - m.put(node.getNodeName(), node.getTextContent()); - } else if (node.getNodeType() == Node.ELEMENT_NODE && nodeList.getLength() > 1) { + int length = nodeList.getLength(); + if (node.getNodeType() == Node.ELEMENT_NODE && length >= 1 && nodeList.item(0).hasChildNodes()) { m.put(node.getNodeName(), getChildren(nodeList)); + } else if (node.getNodeType() == Node.ELEMENT_NODE ) { + m.put(node.getNodeName(), node.getTextContent()); } } } catch (Exception e) { @@ -323,13 +325,47 @@ public class XML { value = ""; } org.w3c.dom.Element filed = document.createElement(entry.getKey()); - if (value instanceof Map){ + /* if (value instanceof Map){ + map2Xml((Map)value, document, filed); + }else if (value instanceof List){ + List vs = (List)value; + for (Object v : vs ){ + if (value instanceof Map){ + map2Xml((Map)value, document, filed); + } + } map2Xml((Map)value, document, filed); }else { value = value.toString().trim(); filed.appendChild(document.createTextNode(value.toString())); - } + }*/ + object2Xml(value, document, filed); element.appendChild(filed); } } + + private static void object2Xml(Object value, Document document, org.w3c.dom.Element element){ + + if (value instanceof Map){ + map2Xml((Map)value, document, element); + }else if (value instanceof List){ + List vs = (List)value; + for (Object v : vs ){ + object2Xml(v, document, element); + } +// map2Xml((Map)value, document, element); + }else { + value = value.toString().trim(); + element.appendChild(document.createTextNode(value.toString())); + } + + + } + + + public static void main(String[] args) { + String text = "01张三2张4"; + System.out.println( getMap2Xml(toJSONObject(text), "data", "utf-8")); + + } } diff --git a/pay-java-demo/pom.xml b/pay-java-demo/pom.xml index 17c0e16..b196ab5 100644 --- a/pay-java-demo/pom.xml +++ b/pay-java-demo/pom.xml @@ -5,7 +5,7 @@ pay-java-parent com.egzosn - 2.12.9 + 2.13.1-SNAPSHOT 4.0.0 war diff --git a/pay-java-fuiou/pom.xml b/pay-java-fuiou/pom.xml index 63e5797..a77d590 100644 --- a/pay-java-fuiou/pom.xml +++ b/pay-java-fuiou/pom.xml @@ -5,7 +5,7 @@ pay-java-parent com.egzosn - 2.12.9 + 2.13.1-SNAPSHOT 4.0.0 pay-java-fuiou diff --git a/pay-java-payoneer/pom.xml b/pay-java-payoneer/pom.xml index dd049db..6020570 100644 --- a/pay-java-payoneer/pom.xml +++ b/pay-java-payoneer/pom.xml @@ -5,7 +5,7 @@ pay-java-parent com.egzosn - 2.12.9 + 2.13.1-SNAPSHOT 4.0.0 pay-java-payoneer diff --git a/pay-java-paypal/pom.xml b/pay-java-paypal/pom.xml index 6dbe403..969573e 100644 --- a/pay-java-paypal/pom.xml +++ b/pay-java-paypal/pom.xml @@ -5,7 +5,7 @@ pay-java-parent com.egzosn - 2.12.9 + 2.13.1-SNAPSHOT 4.0.0 diff --git a/pay-java-union/pom.xml b/pay-java-union/pom.xml index cadae0c..b9ebdc1 100644 --- a/pay-java-union/pom.xml +++ b/pay-java-union/pom.xml @@ -5,7 +5,7 @@ pay-java-parent com.egzosn - 2.12.9 + 2.13.1-SNAPSHOT 4.0.0 diff --git a/pay-java-wx-youdian/pom.xml b/pay-java-wx-youdian/pom.xml index cdd7f5d..c424d22 100644 --- a/pay-java-wx-youdian/pom.xml +++ b/pay-java-wx-youdian/pom.xml @@ -5,7 +5,7 @@ pay-java-parent com.egzosn - 2.12.9 + 2.13.1-SNAPSHOT 4.0.0 pay-java-wx-youdian diff --git a/pay-java-wx/pom.xml b/pay-java-wx/pom.xml index 0842391..484a61e 100644 --- a/pay-java-wx/pom.xml +++ b/pay-java-wx/pom.xml @@ -5,7 +5,7 @@ pay-java-parent com.egzosn - 2.12.9 + 2.13.1-SNAPSHOT 4.0.0 pay-java-wx diff --git a/pay-java-yiji/pom.xml b/pay-java-yiji/pom.xml index c1d5845..63fb58e 100644 --- a/pay-java-yiji/pom.xml +++ b/pay-java-yiji/pom.xml @@ -5,7 +5,7 @@ pay-java-parent com.egzosn - 2.12.9 + 2.13.1-SNAPSHOT 4.0.0 diff --git a/pom.xml b/pom.xml index 8db4562..979bbd9 100644 --- a/pom.xml +++ b/pom.xml @@ -7,7 +7,7 @@ com.egzosn pay-java-parent pom - 2.12.9 + 2.13.1-SNAPSHOT Pay Java - Parent Pay Java Parent @@ -58,7 +58,7 @@ - 2.12.9 + 2.13.1-SNAPSHOT 4.5.4 1.2.17 1.2.58 -- Gitee From 096332bfc98a56cca7c38a9bde0b6f363e5c8c58 Mon Sep 17 00:00:00 2001 From: "fuhai999@gmail.com" Date: Fri, 6 Dec 2019 12:33:59 +0800 Subject: [PATCH 085/299] optimize payorder constructor --- .../com/egzosn/pay/common/bean/PayOrder.java | 58 +++++++++++-------- 1 file changed, 35 insertions(+), 23 deletions(-) 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 1dc0c46..d4c52fa 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 @@ -7,7 +7,7 @@ import java.util.Date; * 支付订单信息 * * @author egan - *

+ * 
  *      email egzosn@gmail.com
  *      date 2016/10/19 22:34
  *  
@@ -66,7 +66,7 @@ public class PayOrder { private String wapName; /** * 用户唯一标识 - * 微信含 sub_openid 字段 + * 微信含 sub_openid 字段 */ private String openid; /** @@ -83,6 +83,21 @@ public class PayOrder { private Date expirationTime; + public PayOrder() { + } + + + public PayOrder(String subject, String body, BigDecimal price, String outTradeNo) { + this(subject, body, price, outTradeNo, null); + } + + public PayOrder(String subject, String body, BigDecimal price, String outTradeNo, TransactionType transactionType) { + this.subject = tryTrim(subject); + this.body = tryTrim(body); + this.price = price; + this.outTradeNo = tryTrim(outTradeNo); + this.transactionType = transactionType; + } public CurType getCurType() { @@ -127,13 +142,16 @@ public class PayOrder { /** * 支付平台订单号,交易号 - * @return 支付平台订单号,交易号 + * + * @return 支付平台订单号, 交易号 */ public String getTradeNo() { return tradeNo; } + /** * 支付平台订单号,交易号 + * * @param tradeNo 支付平台订单号,交易号 */ public void setTradeNo(String tradeNo) { @@ -141,7 +159,8 @@ public class PayOrder { } /** - * 获取商户订单号 + * 获取商户订单号 + * * @return 商户订单号 */ public String getOutTradeNo() { @@ -150,7 +169,8 @@ public class PayOrder { /** * 设置商户订单号 - * @param outTradeNo 商户订单号 + * + * @param outTradeNo 商户订单号 */ public void setOutTradeNo(String outTradeNo) { this.outTradeNo = outTradeNo; @@ -196,24 +216,6 @@ public class PayOrder { this.deviceInfo = deviceInfo; } - public PayOrder() { - } - - - public PayOrder(String subject, String body, BigDecimal price, String outTradeNo, TransactionType transactionType) { - this.subject = subject; - this.body = body; - this.price = price; - this.outTradeNo = outTradeNo; - this.transactionType = transactionType; - } - public PayOrder(String subject, String body, BigDecimal price, String outTradeNo) { - this.subject = subject; - this.body = body; - this.price = price; - this.outTradeNo = outTradeNo; - } - public String getWapUrl() { return wapUrl; } @@ -246,6 +248,16 @@ public class PayOrder { this.expirationTime = expirationTime; } + /** + * 对 subject body 进行 trim 运算, + * 以防止在签名是可能造成的签名错误问题 + * @param str + * @return + */ + private static String tryTrim(String str) { + return str == null ? null : str.trim(); + } + @Override public String toString() { return "PayOrder{" + -- Gitee From 9c447db949eb75884dc2293b613f4b1c403f79e2 Mon Sep 17 00:00:00 2001 From: egzosn Date: Sun, 8 Dec 2019 21:01:23 +0800 Subject: [PATCH 086/299] =?UTF-8?q?=E5=8F=91=E8=B5=B7=E6=94=AF=E4=BB=98?= =?UTF-8?q?=E7=AE=80=E5=8C=96=E4=BA=A4=E6=98=93=E7=B1=BB=E5=9E=8B=EF=BC=8C?= =?UTF-8?q?=E6=96=B9=E6=B3=95=E8=83=BD=E5=A4=9F=E7=A1=AE=E5=AE=9A=E4=BA=A4?= =?UTF-8?q?=E6=98=93=E7=B1=BB=E5=9E=8B=E7=9A=84=E5=85=A8=E9=83=A8=E4=BB=A5?= =?UTF-8?q?=E6=96=B9=E6=B3=95=E4=B8=BA=E5=9F=BA=E5=87=86=EF=BC=8C=E5=A6=82?= =?UTF-8?q?=E6=9E=9C=E8=87=AA=E8=A1=8C=E8=AE=BE=E7=BD=AE=E4=BA=86=E4=BD=86?= =?UTF-8?q?=E5=8F=91=E7=8E=B0=E4=B8=8D=E4=B8=80=E6=A0=B7=E5=B0=B1=E6=8A=A5?= =?UTF-8?q?=E9=94=99=E6=8F=90=E7=A4=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/egzosn/pay/ali/api/AliPayService.java | 18 +++++- .../pay/ali/bean/AliTransactionType.java | 7 +-- .../egzosn/pay/fuiou/api/FuiouPayService.java | 9 ++- .../pay/payoneer/api/PayoneerPayService.java | 2 + .../pay/paypal/api/PayPalPayService.java | 4 ++ .../egzosn/pay/union/api/UnionPayService.java | 14 +++++ .../wx/youdian/api/WxYouDianPayService.java | 2 + .../com/egzosn/pay/wx/api/WxPayService.java | 62 +++++++++++-------- 8 files changed, 84 insertions(+), 34 deletions(-) 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 ff51eb2..41ae9a2 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 @@ -200,7 +200,6 @@ public class AliPayService extends BasePayService { bizContent.put("total_amount", Util.conversionAmount(order.getPrice()).toString()); switch ((AliTransactionType) order.getTransactionType()) { case PAGE: - case DIRECT: bizContent.put(PASSBACK_PARAMS, order.getAddition()); bizContent.put(PRODUCT_CODE, "FAST_INSTANT_TRADE_PAY"); orderInfo.put(RETURN_URL, payConfigStorage.getReturnUrl()); @@ -272,6 +271,16 @@ public class AliPayService extends BasePayService { return PayOutMessage.TEXT().content("success").build(); } + @Override + public String toPay(PayOrder order) { + if (null == order.getTransactionType()) { + order.setTransactionType(AliTransactionType.PAGE); + } else if (order.getTransactionType() != AliTransactionType.PAGE && order.getTransactionType() != AliTransactionType.WAP) { + throw new PayErrorException(new PayException("-1", "错误的交易类型:" + order.getTransactionType())); + } + return super.toPay(order); + } + /** * @param orderInfo 发起支付的订单信息 * @param method 请求方式 "post" "get", @@ -301,6 +310,7 @@ public class AliPayService extends BasePayService { */ @Override public String getQrPay(PayOrder order){ + order.setTransactionType(AliTransactionType.SWEEPPAY); Map orderInfo = orderInfo(order); //预订单 JSONObject result = getHttpRequestTemplate().postForObject(getReqUrl() + "?" + UriVariables.getMapToParameters(orderInfo), null, JSONObject.class); @@ -320,6 +330,12 @@ public class AliPayService extends BasePayService { */ @Override public Map microPay(PayOrder order) { + if (null == order.getTransactionType()){ + order.setTransactionType(AliTransactionType.BAR_CODE); + }else if (order.getTransactionType() != AliTransactionType.BAR_CODE && order.getTransactionType() != AliTransactionType.WAVE_CODE && order.getTransactionType() != AliTransactionType.SECURITY_CODE){ + throw new PayErrorException(new PayException("-1", "错误的交易类型:" + order.getTransactionType())); + } + Map orderInfo = orderInfo(order); //预订单 JSONObject result = getHttpRequestTemplate().postForObject(getReqUrl() + "?" + UriVariables.getMapToParameters(orderInfo), null, JSONObject.class); 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 b97e5ef..bb722de 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 @@ -18,12 +18,7 @@ import com.egzosn.pay.common.bean.TransactionType; * date 2016/10/19 22:58 */ public enum AliTransactionType implements TransactionType { - /** - * 即时到帐 - * 过时的名称,请换至 {@link #PAGE} - */ - @Deprecated - DIRECT("alipay.trade.page.pay"), + /** * 网页支付 */ diff --git a/pay-java-fuiou/src/main/java/com/egzosn/pay/fuiou/api/FuiouPayService.java b/pay-java-fuiou/src/main/java/com/egzosn/pay/fuiou/api/FuiouPayService.java index fdbfde6..3b06ce2 100644 --- a/pay-java-fuiou/src/main/java/com/egzosn/pay/fuiou/api/FuiouPayService.java +++ b/pay-java-fuiou/src/main/java/com/egzosn/pay/fuiou/api/FuiouPayService.java @@ -9,6 +9,8 @@ import com.egzosn.pay.common.util.DateUtils; import com.egzosn.pay.common.util.Util; import com.egzosn.pay.common.util.sign.SignUtils; import com.egzosn.pay.common.util.str.StringUtils; +import com.egzosn.pay.fuiou.bean.FuiouTransactionType; + import java.awt.image.BufferedImage; import java.io.InputStream; import java.math.BigDecimal; @@ -134,7 +136,7 @@ public class FuiouPayService extends BasePayService { */ @Override public boolean verifySource(String orderId) { - LinkedHashMap params = new LinkedHashMap<>(); + LinkedHashMap params = new LinkedHashMap(3); params.put("mchnt_cd", payConfigStorage.getPid()); params.put("order_id", orderId); params.put("md5", createSign(SignUtils.parameters2MD5Str(params, "|"), payConfigStorage.getInputCharset())); @@ -153,6 +155,10 @@ public class FuiouPayService extends BasePayService { */ @Override public Map orderInfo(PayOrder order) { + if (null == order.getTransactionType()){ + order.setTransactionType(FuiouTransactionType.B2C); + } + Map parameters = getOrderInfo(order); String sign = createSign(SignUtils.parameters2MD5Str(parameters, "|"), payConfigStorage.getInputCharset()); parameters.put("md5", sign); @@ -165,6 +171,7 @@ public class FuiouPayService extends BasePayService { * @return 返回支付请求参数集合 */ private Map getOrderInfo(PayOrder order) { + LinkedHashMap parameters = new LinkedHashMap(); //商户代码 parameters.put("mchnt_cd", payConfigStorage.getPid()); diff --git a/pay-java-payoneer/src/main/java/com/egzosn/pay/payoneer/api/PayoneerPayService.java b/pay-java-payoneer/src/main/java/com/egzosn/pay/payoneer/api/PayoneerPayService.java index 4d61b73..76afaba 100644 --- a/pay-java-payoneer/src/main/java/com/egzosn/pay/payoneer/api/PayoneerPayService.java +++ b/pay-java-payoneer/src/main/java/com/egzosn/pay/payoneer/api/PayoneerPayService.java @@ -263,6 +263,8 @@ public class PayoneerPayService extends BasePayService im */ @Override public Map microPay(PayOrder order) { + order.setTransactionType(PayoneerTransactionType.CHARGE); + HttpStringEntity entity = new HttpStringEntity(JSON.toJSONString(orderInfo(order)), ContentType.APPLICATION_JSON); //设置 base atuh entity.setHeaders(authHeader()); diff --git a/pay-java-paypal/src/main/java/com/egzosn/pay/paypal/api/PayPalPayService.java b/pay-java-paypal/src/main/java/com/egzosn/pay/paypal/api/PayPalPayService.java index b94f887..e522814 100644 --- a/pay-java-paypal/src/main/java/com/egzosn/pay/paypal/api/PayPalPayService.java +++ b/pay-java-paypal/src/main/java/com/egzosn/pay/paypal/api/PayPalPayService.java @@ -146,6 +146,10 @@ public class PayPalPayService extends BasePayService{ */ @Override public Map orderInfo(PayOrder order) { + if (null == order.getTransactionType()){ + order.setTransactionType(PayPalTransactionType.sale); + } + Amount amount = new Amount(); if (null == order.getCurType()){ order.setCurType(DefaultCurType.USD); diff --git a/pay-java-union/src/main/java/com/egzosn/pay/union/api/UnionPayService.java b/pay-java-union/src/main/java/com/egzosn/pay/union/api/UnionPayService.java index 90aaad6..93792d3 100644 --- a/pay-java-union/src/main/java/com/egzosn/pay/union/api/UnionPayService.java +++ b/pay-java-union/src/main/java/com/egzosn/pay/union/api/UnionPayService.java @@ -379,6 +379,18 @@ public class UnionPayService extends BasePayService { return null; } + @Override + public String toPay(PayOrder order) { + + if (null == order.getTransactionType()){ + order.setTransactionType(UnionTransactionType.WEB); + }else if (UnionTransactionType.WEB != order.getTransactionType() && UnionTransactionType.WAP != order.getTransactionType() && UnionTransactionType.B2B != order.getTransactionType()){ + throw new PayErrorException(new PayException("-1", "错误的交易类型:" + order.getTransactionType())); + } + + return super.toPay(order); + } + /** * 获取输出二维码,用户返回给支付端, * @@ -387,6 +399,7 @@ public class UnionPayService extends BasePayService { */ @Override public String getQrPay(PayOrder order) { + order.setTransactionType(UnionTransactionType.APPLY_QR_CODE); Map params = orderInfo(order); String responseStr = getHttpRequestTemplate().postForObject(this.getBackTransUrl(), params, String.class); Map response = UriVariables.getParametersToMap(responseStr); @@ -411,6 +424,7 @@ public class UnionPayService extends BasePayService { */ @Override public Map microPay(PayOrder order) { + order.setTransactionType(UnionTransactionType.CONSUME); Map params = orderInfo(order); String responseStr = getHttpRequestTemplate().postForObject(this.getBackTransUrl(), params, String.class); return UriVariables.getParametersToMap(responseStr); 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 2d7775f..b108c5b 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 @@ -336,6 +336,7 @@ public class WxYouDianPayService extends BasePayService microPay(PayOrder order) { + order.setTransactionType(YoudianTransactionType.MICROPAY); JSONObject orderInfo = orderInfo(order); return orderInfo; } 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 5027d82..0940488 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 @@ -31,7 +31,7 @@ import static com.egzosn.pay.wx.bean.WxTransferType.*; * 微信支付服务 * * @author egan - *
+ * 
  *                 email egzosn@gmail.com
  *                 date 2016-5-18 14:09:01
  *                 
@@ -95,6 +95,7 @@ public class WxPayService extends BasePayService { this.payConfigStorage = payConfigStorage; return this; } + /** * 根据交易类型获取url * @@ -222,7 +223,7 @@ public class WxPayService extends BasePayService { parameters.put("time_expire", DateUtils.formatDate(order.getExpirationTime(), DateUtils.YYYYMMDDHHMMSS)); } ((WxTransactionType) order.getTransactionType()).setAttribute(parameters, order); - parameters = preOrderHandler(parameters, order); + parameters = preOrderHandler(parameters, order); setSign(parameters); String requestXML = XML.getMap2Xml(parameters); @@ -275,7 +276,7 @@ public class WxPayService extends BasePayService { params.put("noncestr", result.get(NONCE_STR)); params.put("package", "Sign=WXPay"); } - params = preOrderHandler(params, order); + params = preOrderHandler(params, order); String paySign = createSign(SignUtils.parameterText(params), payConfigStorage.getInputCharset()); params.put(SIGN, paySign); return params; @@ -291,11 +292,12 @@ public class WxPayService extends BasePayService { * @return 请求参数 */ private Map setSign(Map parameters) { - String signType = payConfigStorage.getSignType(); - if (HMACSHA256.equals(signType)) { - signType = HMAC_SHA256; + + String signTypeStr = payConfigStorage.getSignType(); + if (HMACSHA256.equals(signTypeStr)) { + signTypeStr = SignUtils.HMACSHA256.getName(); } - parameters.put("sign_type", signType); + parameters.put("sign_type", signTypeStr); String sign = createSign(SignUtils.parameterText(parameters, "&", SIGN, "appId"), payConfigStorage.getInputCharset()); parameters.put(SIGN, sign); return parameters; @@ -338,21 +340,22 @@ public class WxPayService extends BasePayService { return createSign(content, characterEncoding, payConfigStorage.isTest()); } + /** * 签名 * * @param content 需要签名的内容 不包含key * @param characterEncoding 字符编码 - * @param test 是否为沙箱环境 + * @param test 是否为沙箱环境 * @return 签名结果 */ public String createSign(String content, String characterEncoding, boolean test) { - SignUtils signUtils = SignUtils.valueOf(payConfigStorage.getSignType().toUpperCase()); + SignType signType = SignUtils.valueOf(payConfigStorage.getSignType().toUpperCase()); String keyPrivate = payConfigStorage.getKeyPrivate(); - if (test){ + if (test) { keyPrivate = getKeyPrivate(); } - return signUtils.createSign(content + "&key=" + (signUtils == SignUtils.MD5 ? "" : keyPrivate), keyPrivate, characterEncoding).toUpperCase(); + return signType.createSign(content + "&key=" + (signType == SignUtils.MD5 ? "" : keyPrivate), keyPrivate, characterEncoding).toUpperCase(); } /** @@ -426,14 +429,16 @@ public class WxPayService extends BasePayService { * @return 返回二维码信息,,支付时需要的 */ @Override - public String getQrPay(PayOrder order){ + public String getQrPay(PayOrder order) { + order.setTransactionType(WxTransactionType.NATIVE); Map orderInfo = orderInfo(order); //获取对应的支付账户操作工具(可根据账户id) if (!SUCCESS.equals(orderInfo.get(RESULT_CODE))) { - throw new PayErrorException(new WxPayError((String)orderInfo.get("err_code"), orderInfo.toString())); + throw new PayErrorException(new WxPayError((String) orderInfo.get("err_code"), orderInfo.toString())); } return (String) orderInfo.get("code_url"); } + /** * 刷卡付,pos主动扫码付款 * @@ -442,7 +447,12 @@ public class WxPayService extends BasePayService { */ @Override public Map microPay(PayOrder order) { + if (null == order.getTransactionType()) { + order.setTransactionType(WxTransactionType.MICROPAY); + } else if (WxTransactionType.MICROPAY != order.getTransactionType() && WxTransactionType.FACEPAY != order.getTransactionType()) { + throw new PayErrorException(new PayException("-1", "错误的交易类型:" + order.getTransactionType())); + } return orderInfo(order); } @@ -527,7 +537,7 @@ public class WxPayService extends BasePayService { parameters.put("total_fee", Util.conversionCentAmount(refundOrder.getTotalAmount())); parameters.put("refund_fee", Util.conversionCentAmount(refundOrder.getRefundAmount())); parameters.put("op_user_id", payConfigStorage.getPid()); - setParameters(parameters, "notify_url", payConfigStorage.getNotifyUrl()); + setParameters(parameters, "notify_url", payConfigStorage.getNotifyUrl()); //设置签名 setSign(parameters); @@ -563,7 +573,7 @@ public class WxPayService extends BasePayService { setParameters(parameters, "out_refund_no", refundOrder.getRefundNo()); //设置签名 setSign(parameters); - return requestTemplate.postForObject(getReqUrl( WxTransactionType.REFUNDQUERY), XML.getMap2Xml(parameters) , JSONObject.class); + return requestTemplate.postForObject(getReqUrl(WxTransactionType.REFUNDQUERY), XML.getMap2Xml(parameters), JSONObject.class); } @@ -633,7 +643,7 @@ public class WxPayService extends BasePayService { } //设置签名 setSign(parameters); - return requestTemplate.postForObject(getReqUrl(transactionType), XML.getMap2Xml(parameters) , JSONObject.class); + return requestTemplate.postForObject(getReqUrl(transactionType), XML.getMap2Xml(parameters), JSONObject.class); } /** @@ -642,13 +652,13 @@ public class WxPayService extends BasePayService { * @param order 转账订单 *
      *
-     *              注意事项:
-     *              ◆ 当返回错误码为“SYSTEMERROR”时,请不要更换商户订单号,一定要使用原商户订单号重试,否则可能造成重复支付等资金风险。
-     *              ◆ XML具有可扩展性,因此返回参数可能会有新增,而且顺序可能不完全遵循此文档规范,如果在解析回包的时候发生错误,请商户务必不要换单重试,请商户联系客服确认付款情况。如果有新回包字段,会更新到此API文档中。
-     *              ◆ 因为错误代码字段err_code的值后续可能会增加,所以商户如果遇到回包返回新的错误码,请商户务必不要换单重试,请商户联系客服确认付款情况。如果有新的错误码,会更新到此API文档中。
-     *              ◆ 错误代码描述字段err_code_des只供人工定位问题时做参考,系统实现时请不要依赖这个字段来做自动化处理。
+     *                           注意事项:
+     *                           ◆ 当返回错误码为“SYSTEMERROR”时,请不要更换商户订单号,一定要使用原商户订单号重试,否则可能造成重复支付等资金风险。
+     *                           ◆ XML具有可扩展性,因此返回参数可能会有新增,而且顺序可能不完全遵循此文档规范,如果在解析回包的时候发生错误,请商户务必不要换单重试,请商户联系客服确认付款情况。如果有新回包字段,会更新到此API文档中。
+     *                           ◆ 因为错误代码字段err_code的值后续可能会增加,所以商户如果遇到回包返回新的错误码,请商户务必不要换单重试,请商户联系客服确认付款情况。如果有新的错误码,会更新到此API文档中。
+     *                           ◆ 错误代码描述字段err_code_des只供人工定位问题时做参考,系统实现时请不要依赖这个字段来做自动化处理。
      *
-     *              
+ *
* @return 对应的转账结果 */ @Override @@ -672,7 +682,7 @@ public class WxPayService extends BasePayService { } parameters.put(SIGN, createSign(SignUtils.parameterText(parameters, "&", SIGN), payConfigStorage.getInputCharset())); - return getHttpRequestTemplate().postForObject(getReqUrl(order.getTransferType()), XML.getMap2Xml(parameters), JSONObject.class); + return getHttpRequestTemplate().postForObject(getReqUrl(order.getTransferType()), XML.getMap2Xml(parameters), JSONObject.class); } /** @@ -740,11 +750,11 @@ public class WxPayService extends BasePayService { throw new PayErrorException(new WxPayError(FAILURE, "微信转账类型 #transferQuery(String outNo, String wxTransferType) 必填,详情com.egzosn.pay.wx.bean.WxTransferType")); } //如果类型为余额方式 - if (TRANSFERS.getType().equals(wxTransferType) || GETTRANSFERINFO.getType().equals(wxTransferType)){ - return getHttpRequestTemplate().postForObject(getReqUrl(GETTRANSFERINFO), XML.getMap2Xml(parameters), JSONObject.class); + if (TRANSFERS.getType().equals(wxTransferType) || GETTRANSFERINFO.getType().equals(wxTransferType)) { + return getHttpRequestTemplate().postForObject(getReqUrl(GETTRANSFERINFO), XML.getMap2Xml(parameters), JSONObject.class); } //默认查询银行卡的记录 - return getHttpRequestTemplate().postForObject(getReqUrl(QUERY_BANK), XML.getMap2Xml(parameters), JSONObject.class); + return getHttpRequestTemplate().postForObject(getReqUrl(QUERY_BANK), XML.getMap2Xml(parameters), JSONObject.class); } -- Gitee From 36b590ca2070216a11451c555463a884b779df93 Mon Sep 17 00:00:00 2001 From: egzosn Date: Sun, 8 Dec 2019 21:04:26 +0800 Subject: [PATCH 087/299] =?UTF-8?q?=E7=AD=BE=E5=90=8D=E7=B1=BB=E5=9E=8B?= =?UTF-8?q?=E6=8E=A5=E5=8F=A3=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../pay/common/util/sign/SignUtils.java | 33 +++++++------------ 1 file changed, 12 insertions(+), 21 deletions(-) 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 3d13bf5..ae31172 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 @@ -1,6 +1,7 @@ package com.egzosn.pay.common.util.sign; +import com.egzosn.pay.common.bean.SignType; import com.egzosn.pay.common.bean.result.PayException; import com.egzosn.pay.common.exception.PayErrorException; import com.egzosn.pay.common.util.str.StringUtils; @@ -22,7 +23,7 @@ import java.util.*; * date 2016/11/9 17:45 *
*/ -public enum SignUtils { +public enum SignUtils implements SignType { MD5 { /** @@ -51,6 +52,11 @@ public enum SignUtils { return com.egzosn.pay.common.util.sign.encrypt.MD5.verify(text, sign, key, characterEncoding); } },HMACSHA256{ + @Override + public String getName() { + return "HMAC-SHA256"; + } + /** * 签名 * @@ -279,6 +285,11 @@ public enum SignUtils { return UUID.randomUUID().toString().replace("-", ""); } + @Override + public String getName() { + return this.name(); + } + /** * 签名 * @@ -305,15 +316,6 @@ public enum SignUtils { } - /** - * 签名 - * - * @param content 需要签名的内容 - * @param key 密钥 - * @param characterEncoding 字符编码 - * @return 签名值 - */ - public abstract String createSign(String content, String key, String characterEncoding); /** * 签名字符串 @@ -330,16 +332,5 @@ public enum SignUtils { } - /** - * 签名字符串 - * - * @param text 需要签名的字符串 - * @param sign 签名结果 - * @param key 密钥 - * @param characterEncoding 编码格式 - * @return 签名结果 - */ - public abstract boolean verify(String text, String sign, String key, String characterEncoding); - } -- Gitee From 543466b0dd6cccc58f8a307e4a4676f64fed3b7e Mon Sep 17 00:00:00 2001 From: egzosn Date: Mon, 9 Dec 2019 15:37:42 +0800 Subject: [PATCH 088/299] =?UTF-8?q?ali=20=E5=9B=9E=E8=B0=83=E6=B6=88?= =?UTF-8?q?=E6=81=AF=E6=89=A9=E5=B1=95=E5=AD=97=E6=AE=B5=E9=81=97=E6=BC=8F?= =?UTF-8?q?gett?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../egzosn/pay/ali/bean/AliPayMessage.java | 105 ++++++++++++++++++ 1 file changed, 105 insertions(+) diff --git a/pay-java-ali/src/main/java/com/egzosn/pay/ali/bean/AliPayMessage.java b/pay-java-ali/src/main/java/com/egzosn/pay/ali/bean/AliPayMessage.java index f27cd6d..82261e8 100644 --- a/pay-java-ali/src/main/java/com/egzosn/pay/ali/bean/AliPayMessage.java +++ b/pay-java-ali/src/main/java/com/egzosn/pay/ali/bean/AliPayMessage.java @@ -254,6 +254,111 @@ public class AliPayMessage extends PayMessage { this.receiptAmount = receiptAmount; } + public BigDecimal getInvoiceAmount() { + return invoiceAmount; + } + + public void setInvoiceAmount(BigDecimal invoiceAmount) { + this.invoiceAmount = invoiceAmount; + } + + public BigDecimal getBuyerPayAmount() { + return buyerPayAmount; + } + + public void setBuyerPayAmount(BigDecimal buyerPayAmount) { + this.buyerPayAmount = buyerPayAmount; + } + + public BigDecimal getPointAmount() { + return pointAmount; + } + + public void setPointAmount(BigDecimal pointAmount) { + this.pointAmount = pointAmount; + } + + public BigDecimal getRefundFee() { + return refundFee; + } + + public void setRefundFee(BigDecimal refundFee) { + this.refundFee = refundFee; + } + + @Override + public String getSubject() { + return subject; + } + + public void setSubject(String subject) { + this.subject = subject; + } + + public String getBody() { + return body; + } + + public void setBody(String body) { + this.body = body; + } + + public Date getGmtCreate() { + return gmtCreate; + } + + public void setGmtCreate(Date gmtCreate) { + this.gmtCreate = gmtCreate; + } + + public Date getGmtPayment() { + return gmtPayment; + } + + public void setGmtPayment(Date gmtPayment) { + this.gmtPayment = gmtPayment; + } + + public Date getGmtRefund() { + return gmtRefund; + } + + public void setGmtRefund(Date gmtRefund) { + this.gmtRefund = gmtRefund; + } + + public Date getGmtClose() { + return gmtClose; + } + + public void setGmtClose(Date gmtClose) { + this.gmtClose = gmtClose; + } + + public String getFundBillList() { + return fundBillList; + } + + public void setFundBillList(String fundBillList) { + this.fundBillList = fundBillList; + } + + public String getPassbackParams() { + return passbackParams; + } + + public void setPassbackParams(String passbackParams) { + this.passbackParams = passbackParams; + } + + public String getVoucherDetailList() { + return voucherDetailList; + } + + public void setVoucherDetailList(String voucherDetailList) { + this.voucherDetailList = voucherDetailList; + } + public static final AliPayMessage create(Map message){ AliPayMessage payMessage = new JSONObject(message).toJavaObject(AliPayMessage.class); payMessage.setPayMessage(message); -- Gitee From c3cd115e329d6defecf2a3ce39f9cbd4bb3275f7 Mon Sep 17 00:00:00 2001 From: egzosn Date: Mon, 9 Dec 2019 21:32:05 +0800 Subject: [PATCH 089/299] =?UTF-8?q?=E6=96=87=E6=A1=A3demo=E6=8F=90?= =?UTF-8?q?=E4=BA=A4=EF=BC=8C=E6=94=AF=E4=BB=98=E5=AE=9D=E7=A7=BB=E9=99=A4?= =?UTF-8?q?AliTransactionType.DIRECT?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pay-java-ali/README.md | 2 +- .../java/com/egzosn/pay/demo/controller/AliPayController.java | 2 +- pay-java-demo/src/main/webapp/index.html | 3 +-- 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/pay-java-ali/README.md b/pay-java-ali/README.md index e50a842..bebaa86 100644 --- a/pay-java-ali/README.md +++ b/pay-java-ali/README.md @@ -122,7 +122,7 @@ /*-----------即时到帐 WAP 网页支付-------------------*/ // payOrder.setTransactionType(AliTransactionType.WAP); //WAP支付 - payOrder.setTransactionType(AliTransactionType.DIRECT); // 即时到帐 PC网页支付 + payOrder.setTransactionType(AliTransactionType.PAGE); // 即时到帐 PC网页支付 //获取支付所需的信息 Map directOrderInfo = service.orderInfo(payOrder); //获取表单提交对应的字符串,将其序列化到页面即可, diff --git a/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/AliPayController.java b/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/AliPayController.java index ccfb3ae..64e235f 100644 --- a/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/AliPayController.java +++ b/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/AliPayController.java @@ -84,7 +84,7 @@ public class AliPayController { @RequestMapping(value = "toPay.html", produces = "text/html;charset=UTF-8") public String toPay( BigDecimal price) { //及时收款 - PayOrder order = new PayOrder("订单title", "摘要", null == price ? new BigDecimal(0.01) : price, UUID.randomUUID().toString().replace("-", ""), AliTransactionType.DIRECT); + PayOrder order = new PayOrder("订单title", "摘要", null == price ? new BigDecimal(0.01) : price, UUID.randomUUID().toString().replace("-", ""), AliTransactionType.PAGE); //WAP // PayOrder order = new PayOrder("订单title", "摘要", null == price ? new BigDecimal(0.01) : price, UUID.randomUUID().toString().replace("-", ""), AliTransactionType.WAP); diff --git a/pay-java-demo/src/main/webapp/index.html b/pay-java-demo/src/main/webapp/index.html index 09dc853..03b359c 100644 --- a/pay-java-demo/src/main/webapp/index.html +++ b/pay-java-demo/src/main/webapp/index.html @@ -70,8 +70,7 @@
各个支付对应的交易类型可自行查看对应的官方文档,本项目已实现几种交易类型,对应各个支付类型的com.egzosn.pay.common.bean.TransactionType具体实现
-
旧版支付宝(com.egzosn.pay.ali.before.bean.AliTransactionType): 即时付款=DIRECT , 移动支付=APP , 手机网站支付=WAP
-
新版支付宝(com.egzosn.pay.ali.bean.AliTransactionType): 即时付款=DIRECT , app支付=APP , 手机网站支付=WAP , 扫码付=SWEEPPAY, 条码付=BAR_CODE, 声波付=WAVE_CODE
+
新版支付宝(com.egzosn.pay.ali.bean.AliTransactionType): 即时付款=PAGE , app支付=APP , 手机网站支付=WAP , 扫码付=SWEEPPAY, 条码付=BAR_CODE, 声波付=WAVE_CODE
微信(com.egzosn.pay.wx.bean.WxTransactionType): 公众号支付=JSAPI , 移动支付=APP , 扫码付=NATIVE
银联(com.egzosn.pay.union.bean.UnionTransactionType):苹果支付=APPLE,手机控件=APP,WAP支付=WAP,网关支付=WEB,无跳转支付=NO_JUMP,B2B支付=B2B,申码(主扫场景)=APPLY_QR_CODE,消费(被扫场景)=CONSUME
友店微信(com.egzosn.pay.wx.youdian.bean.YoudianTransactionType): 扫码付=NATIVE
-- Gitee From 51dd8d2c0cef1ec635202795d62c7adf51e52f7b Mon Sep 17 00:00:00 2001 From: egzosn Date: Mon, 9 Dec 2019 21:42:32 +0800 Subject: [PATCH 090/299] =?UTF-8?q?=E7=AD=BE=E5=90=8D=E7=B1=BB=E5=9E=8B?= =?UTF-8?q?=E6=8E=A5=E5=8F=A3=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/egzosn/pay/common/bean/SignType.java | 78 +++++++++++++++++++ 1 file changed, 78 insertions(+) create mode 100644 pay-java-common/src/main/java/com/egzosn/pay/common/bean/SignType.java diff --git a/pay-java-common/src/main/java/com/egzosn/pay/common/bean/SignType.java b/pay-java-common/src/main/java/com/egzosn/pay/common/bean/SignType.java new file mode 100644 index 0000000..11230e8 --- /dev/null +++ b/pay-java-common/src/main/java/com/egzosn/pay/common/bean/SignType.java @@ -0,0 +1,78 @@ +package com.egzosn.pay.common.bean; + + +import java.util.*; + +/** + * 签名类型 + * + * @author: egan + *
+ * email egzosn@gmail.com
+ * date 2019/12/08 13:30
+ * 
+ */ +public interface SignType { + + + /** + * 获取签名类型名称 + * @return 类型名称 + */ + String getName(); + + /** + * 签名 + * + * @param parameters 需要进行排序签名的参数 + * @param key 密钥 + * @param characterEncoding 编码格式 + * @return 签名值 + */ + String sign(Map parameters, String key, String characterEncoding); + /** + * 签名 + * @param parameters 需要进行排序签名的参数 + * @param key 密钥 + * @param separator 分隔符 默认 & + * @param characterEncoding 编码格式 + * @return 签名值 + */ + String sign(Map parameters, String key, String separator, String characterEncoding); + + /** + * 签名 + * + * @param content 需要签名的内容 + * @param key 密钥 + * @param characterEncoding 字符编码 + * @return 签名值 + */ + String createSign(String content, String key, String characterEncoding); + + /** + * 签名字符串 + * + * @param params 需要签名的字符串 + * @param sign 签名结果 + * @param key 密钥 + * @param characterEncoding 编码格式 + * @return 签名结果 + */ + boolean verify(Map params, String sign, String key, String characterEncoding); + + + /** + * 签名字符串 + * + * @param text 需要签名的字符串 + * @param sign 签名结果 + * @param key 密钥 + * @param characterEncoding 编码格式 + * @return 签名结果 + */ + boolean verify(String text, String sign, String key, String characterEncoding); + + + +} -- Gitee From 9d5a3aedea6afc4713448a8e5b564641a33a7536 Mon Sep 17 00:00:00 2001 From: egzosn Date: Tue, 24 Dec 2019 22:26:19 +0800 Subject: [PATCH 091/299] =?UTF-8?q?xml=20=E5=A4=9A=E5=B1=82=E8=8A=82?= =?UTF-8?q?=E7=82=B9=E4=BF=AE=E5=A4=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/egzosn/pay/common/util/XML.java | 37 +++++++------------ 1 file changed, 13 insertions(+), 24 deletions(-) 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 b61898b..a323e8a 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 @@ -142,12 +142,9 @@ public class XML { for (int idx = 0; idx < children.getLength(); ++idx) { Node node = children.item(idx); NodeList nodeList = node.getChildNodes(); - if (node.getNodeType() == Node.ELEMENT_NODE && nodeList.getLength() <= 1) { - if (null == json) { - json = new JSONObject(); - } - ((JSONObject) json).put(node.getNodeName(), node.getTextContent()); - } else if (node.getNodeType() == Node.ELEMENT_NODE && nodeList.getLength() > 1) { + int length = nodeList.getLength(); + + if (node.getNodeType() == Node.ELEMENT_NODE && length >= 1 && nodeList.item(0).hasChildNodes()) { if (null == json) { json = new JSONObject(); } @@ -167,6 +164,11 @@ public class XML { c.put(node.getNodeName(), getChildren(nodeList)); ((JSONArray) json).add(c); } + } else if (node.getNodeType() == Node.ELEMENT_NODE ) { + if (null == json) { + json = new JSONObject(); + } + ((JSONObject) json).put(node.getNodeName(), node.getTextContent()); } } @@ -199,20 +201,8 @@ public class XML { * @throws IOException xml io转化异常 */ public static T inputStream2Bean(InputStream in, Class clazz) throws IOException { - try { - - DocumentBuilder documentBuilder = newDocumentBuilder(); - org.w3c.dom.Document doc = documentBuilder.parse(in); - doc.getDocumentElement().normalize(); - NodeList children = doc.getDocumentElement().getChildNodes(); - JSON json = getChildren(children); - return json.toJavaObject(clazz); - } catch (Exception e) { - throw new PayErrorException(new PayException("XML failure", "XML解析失败\n" + e.getMessage())); - } finally { - in.close(); - } - + JSON json = toJSONObject(in); + return json.toJavaObject(clazz); } /** @@ -234,14 +224,13 @@ public class XML { Node node = children.item(idx); NodeList nodeList = node.getChildNodes(); int length = nodeList.getLength(); - if (node.getNodeType() == Node.ELEMENT_NODE && length >= 1 && nodeList.item(0).hasChildNodes()) { + if (node.getNodeType() == Node.ELEMENT_NODE && (length >1 || length==1 && nodeList.item(0).hasChildNodes())) { m.put(node.getNodeName(), getChildren(nodeList)); } else if (node.getNodeType() == Node.ELEMENT_NODE ) { m.put(node.getNodeName(), node.getTextContent()); } } } catch (Exception e) { -// e.printStackTrace(); throw new PayErrorException(new PayException("XML failure", "XML解析失败\n" + e.getMessage())); } finally { in.close(); @@ -364,8 +353,8 @@ public class XML { public static void main(String[] args) { - String text = "01张三2张4"; - System.out.println( getMap2Xml(toJSONObject(text), "data", "utf-8")); + String text = "01张三2张4"; + System.out.println( getMap2Xml(toJSONObject(text), "datas", "utf-8")); } } -- Gitee From 2f64a27a5cac7e451fb6c043d1a0cb82e10efde0 Mon Sep 17 00:00:00 2001 From: egzosn Date: Tue, 24 Dec 2019 22:33:03 +0800 Subject: [PATCH 092/299] =?UTF-8?q?=E5=AE=98=E6=96=B9=E9=80=80=E6=AC=BE?= =?UTF-8?q?=E6=8E=A5=E5=8F=A3=E6=9B=B4=E6=96=B0=E9=9A=8F=E7=9D=80=E6=9B=B4?= =?UTF-8?q?=E6=96=B0=EF=BC=8C=E5=B9=B6=E5=8A=A0=E5=85=A5=E9=80=80=E6=AC=BE?= =?UTF-8?q?=E8=B4=A7=E5=B8=81=E7=B1=BB=E5=9E=8B=E4=B8=8E=E8=AF=B4=E6=98=8E?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/main/java/com/egzosn/pay/wx/api/WxPayService.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) 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 0940488..bf74515 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 @@ -536,9 +536,11 @@ public class WxPayService extends BasePayService { setParameters(parameters, "out_refund_no", refundOrder.getRefundNo()); parameters.put("total_fee", Util.conversionCentAmount(refundOrder.getTotalAmount())); parameters.put("refund_fee", Util.conversionCentAmount(refundOrder.getRefundAmount())); - parameters.put("op_user_id", payConfigStorage.getPid()); setParameters(parameters, "notify_url", payConfigStorage.getNotifyUrl()); - + if (null != refundOrder.getCurType()){ + parameters.put("refund_fee_type", refundOrder.getCurType().getType()); + } + setParameters(parameters, "refund_desc", refundOrder.getDescription()); //设置签名 setSign(parameters); return requestTemplate.postForObject(getReqUrl(WxTransactionType.REFUND), XML.getMap2Xml(parameters), JSONObject.class); -- Gitee From 972a8f62947ef280c0fc43f4489f4175de9a8d6b Mon Sep 17 00:00:00 2001 From: egzosn Date: Thu, 26 Dec 2019 20:30:59 +0800 Subject: [PATCH 093/299] =?UTF-8?q?=E5=8A=A0=E5=85=A5=E8=B4=A7=E5=B8=81?= =?UTF-8?q?=E7=B1=BB=E5=9E=8B,=E4=BB=A3=E7=A0=81=E6=95=B4=E7=90=86?= =?UTF-8?q?=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/egzosn/pay/wx/api/WxPayService.java | 42 +++++++++---------- 1 file changed, 19 insertions(+), 23 deletions(-) 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 bf74515..b2a412e 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 @@ -181,12 +181,8 @@ public class WxPayService extends BasePayService { parameters.put(APPID, payConfigStorage.getAppid()); parameters.put(MCH_ID, payConfigStorage.getMchId()); //判断如果是服务商模式信息则加入 - if (!StringUtils.isEmpty(payConfigStorage.getSubMchId())) { - parameters.put("sub_mch_id", payConfigStorage.getSubMchId()); - } - if (!StringUtils.isEmpty(payConfigStorage.getSubAppid())) { - parameters.put("sub_appid", payConfigStorage.getSubAppid()); - } + setParameters(parameters, "sub_mch_id", payConfigStorage.getSubMchId()); + setParameters(parameters, "sub_appid", payConfigStorage.getSubAppid()); parameters.put(NONCE_STR, SignUtils.randomStr()); return parameters; @@ -213,15 +209,18 @@ public class WxPayService extends BasePayService { parameters.put("spbill_create_ip", StringUtils.isEmpty(order.getSpbillCreateIp()) ? "192.168.1.150" : order.getSpbillCreateIp()); // 总金额单位为分 parameters.put("total_fee", Util.conversionCentAmount(order.getPrice())); - if (StringUtils.isNotEmpty(order.getAddition())) { - parameters.put("attach", order.getAddition()); - } + setParameters(parameters, "attach", order.getAddition()); parameters.put("notify_url", payConfigStorage.getNotifyUrl()); parameters.put("trade_type", order.getTransactionType().getType()); if (null != order.getExpirationTime()) { parameters.put("time_start", DateUtils.formatDate(new Date(), DateUtils.YYYYMMDDHHMMSS)); parameters.put("time_expire", DateUtils.formatDate(order.getExpirationTime(), DateUtils.YYYYMMDDHHMMSS)); } + + if (null != order.getCurType()) { + parameters.put("fee_type", order.getCurType().getType()); + } + ((WxTransactionType) order.getTransactionType()).setAttribute(parameters, order); parameters = preOrderHandler(parameters, order); setSign(parameters); @@ -514,7 +513,7 @@ public class WxPayService extends BasePayService { private Map setParameters(Map parameters, String key, String value) { - if (!StringUtils.isEmpty(value)) { + if (StringUtils.isNotEmpty(value)) { parameters.put(key, value); } return parameters; @@ -537,7 +536,7 @@ public class WxPayService extends BasePayService { parameters.put("total_fee", Util.conversionCentAmount(refundOrder.getTotalAmount())); parameters.put("refund_fee", Util.conversionCentAmount(refundOrder.getRefundAmount())); setParameters(parameters, "notify_url", payConfigStorage.getNotifyUrl()); - if (null != refundOrder.getCurType()){ + if (null != refundOrder.getCurType()) { parameters.put("refund_fee_type", refundOrder.getCurType().getType()); } setParameters(parameters, "refund_desc", refundOrder.getDescription()); @@ -613,7 +612,7 @@ public class WxPayService extends BasePayService { /** - * @param transactionIdOrBillDate 支付平台订单号或者账单类型, 具体请 类型为{@link String }或者 {@link Date },类型须强制限制,类型不对应则抛出异常{@link PayErrorException} + * @param transactionIdOrBillDate 支付平台订单号或者账单日期, 具体请 类型为{@link String }或者 {@link Date },类型须强制限制,类型不对应则抛出异常{@link PayErrorException} * @param outTradeNoBillType 商户单号或者 账单类型 * @param transactionType 交易类型 * @return 返回支付方对应接口的结果 @@ -638,11 +637,8 @@ public class WxPayService extends BasePayService { //获取公共参数 Map parameters = getPublicParameters(); - if (StringUtils.isEmpty((String) transactionIdOrBillDate)) { - parameters.put("out_trade_no", outTradeNoBillType); - } else { - parameters.put("transaction_id", transactionIdOrBillDate); - } + setParameters(parameters, "out_trade_no", outTradeNoBillType); + setParameters(parameters, "transaction_id", (String) transactionIdOrBillDate); //设置签名 setSign(parameters); return requestTemplate.postForObject(getReqUrl(transactionType), XML.getMap2Xml(parameters), JSONObject.class); @@ -654,13 +650,13 @@ public class WxPayService extends BasePayService { * @param order 转账订单 *
      *
-     *                           注意事项:
-     *                           ◆ 当返回错误码为“SYSTEMERROR”时,请不要更换商户订单号,一定要使用原商户订单号重试,否则可能造成重复支付等资金风险。
-     *                           ◆ XML具有可扩展性,因此返回参数可能会有新增,而且顺序可能不完全遵循此文档规范,如果在解析回包的时候发生错误,请商户务必不要换单重试,请商户联系客服确认付款情况。如果有新回包字段,会更新到此API文档中。
-     *                           ◆ 因为错误代码字段err_code的值后续可能会增加,所以商户如果遇到回包返回新的错误码,请商户务必不要换单重试,请商户联系客服确认付款情况。如果有新的错误码,会更新到此API文档中。
-     *                           ◆ 错误代码描述字段err_code_des只供人工定位问题时做参考,系统实现时请不要依赖这个字段来做自动化处理。
+     *                                        注意事项:
+     *                                        ◆ 当返回错误码为“SYSTEMERROR”时,请不要更换商户订单号,一定要使用原商户订单号重试,否则可能造成重复支付等资金风险。
+     *                                        ◆ XML具有可扩展性,因此返回参数可能会有新增,而且顺序可能不完全遵循此文档规范,如果在解析回包的时候发生错误,请商户务必不要换单重试,请商户联系客服确认付款情况。如果有新回包字段,会更新到此API文档中。
+     *                                        ◆ 因为错误代码字段err_code的值后续可能会增加,所以商户如果遇到回包返回新的错误码,请商户务必不要换单重试,请商户联系客服确认付款情况。如果有新的错误码,会更新到此API文档中。
+     *                                        ◆ 错误代码描述字段err_code_des只供人工定位问题时做参考,系统实现时请不要依赖这个字段来做自动化处理。
      *
-     *                           
+ * * @return 对应的转账结果 */ @Override -- Gitee From f5accfbf6039c19a995fcdbb763bea8e1911ae38 Mon Sep 17 00:00:00 2001 From: egzosn Date: Thu, 26 Dec 2019 20:31:36 +0800 Subject: [PATCH 094/299] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E5=8F=AF=E8=83=BD?= =?UTF-8?q?=E7=9A=84=E7=A9=BA=E6=8C=87=E9=92=88=E9=81=97=E4=BA=A7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/main/java/com/egzosn/pay/ali/api/AliPayService.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 41ae9a2..97043dd 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 @@ -529,7 +529,7 @@ public class AliPayService extends BasePayService { //获取公共参数 Map parameters = getPublicParameters(transactionType); //设置请求参数的集合 - parameters.put(BIZ_CONTENT, getContentToJson(tradeNoOrBillDate.toString(), outTradeNoBillType)); + parameters.put(BIZ_CONTENT, getContentToJson((String) tradeNoOrBillDate, outTradeNoBillType)); //设置签名 setSign(parameters); return requestTemplate.getForObject(getReqUrl() + "?" + UriVariables.getMapToParameters(parameters), JSONObject.class); -- Gitee From afd3797f03371801913558a1ba8fba4ed91c045d Mon Sep 17 00:00:00 2001 From: egzosn Date: Sun, 5 Jan 2020 22:38:50 +0800 Subject: [PATCH 095/299] =?UTF-8?q?=E8=AE=A2=E5=8D=95=E5=B1=9E=E6=80=A7?= =?UTF-8?q?=E6=8E=A5=E5=8F=A3=E5=8C=96=EF=BC=8C=E6=9A=82=E6=97=B6=E7=94=A8?= =?UTF-8?q?=E5=81=9A=E8=87=AA=E5=AE=9A=E4=B9=89=E5=B1=9E=E6=80=A7=E4=BD=BF?= =?UTF-8?q?=E7=94=A8=EF=BC=8C=E4=B8=8B=E4=B8=80=E6=AD=A5=E5=85=B7=E4=BD=93?= =?UTF-8?q?=E5=8C=96=E6=94=AF=E4=BB=98=E8=AE=A2=E5=8D=95=E5=AF=B9=E8=B1=A1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/egzosn/pay/ali/api/AliPayService.java | 2 +- .../pay/ali/before/api/AliPayService.java | 554 ------------------ .../ali/before/bean/AliTransactionType.java | 84 --- .../com/egzosn/pay/common/bean/Order.java | 22 + .../com/egzosn/pay/common/bean/PayOrder.java | 27 +- .../egzosn/pay/fuiou/api/FuiouPayService.java | 1 + .../pay/payoneer/api/PayoneerPayService.java | 2 +- .../egzosn/pay/union/api/UnionPayService.java | 7 +- .../wx/youdian/api/WxYouDianPayService.java | 1 + .../com/egzosn/pay/wx/api/WxPayService.java | 1 + .../egzosn/pay/yiji/api/YiJiPayService.java | 2 +- 11 files changed, 56 insertions(+), 647 deletions(-) delete mode 100644 pay-java-ali/src/main/java/com/egzosn/pay/ali/before/api/AliPayService.java delete mode 100644 pay-java-ali/src/main/java/com/egzosn/pay/ali/before/bean/AliTransactionType.java create mode 100644 pay-java-common/src/main/java/com/egzosn/pay/common/bean/Order.java 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 97043dd..b9685c8 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 @@ -226,7 +226,7 @@ public class AliPayService extends BasePayService { bizContent.put("timeout_express", DateUtils.minutesRemaining(order.getExpirationTime()) + "m"); } orderInfo.put(BIZ_CONTENT, JSON.toJSONString(bizContent)); - + orderInfo.putAll(order.getAttr()); return preOrderHandler(orderInfo, order); } 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 deleted file mode 100644 index ae9c93b..0000000 --- a/pay-java-ali/src/main/java/com/egzosn/pay/ali/before/api/AliPayService.java +++ /dev/null @@ -1,554 +0,0 @@ -package com.egzosn.pay.ali.before.api; - -import com.alibaba.fastjson.JSON; -import com.alibaba.fastjson.JSONObject; -import com.egzosn.pay.ali.api.AliPayConfigStorage; -import com.egzosn.pay.ali.before.bean.AliTransactionType; -import com.egzosn.pay.common.api.BasePayService; -import com.egzosn.pay.common.api.Callback; -import com.egzosn.pay.common.bean.*; -import com.egzosn.pay.common.bean.result.PayException; -import com.egzosn.pay.common.exception.PayErrorException; -import com.egzosn.pay.common.http.HttpConfigStorage; -import com.egzosn.pay.common.http.UriVariables; -import com.egzosn.pay.common.util.DateUtils; -import com.egzosn.pay.common.util.Util; -import com.egzosn.pay.common.util.sign.SignUtils; -import com.egzosn.pay.common.util.str.StringUtils; -import java.awt.image.BufferedImage; -import java.io.UnsupportedEncodingException; -import java.math.BigDecimal; -import java.net.URLEncoder; -import java.text.DateFormat; -import java.text.SimpleDateFormat; -import java.util.*; - -import static com.egzosn.pay.ali.api.AliPayService.SIGN; - -/** - * 支付宝支付服务 - * @author egan - * - * email egzosn@gmail.com - * date 2016-5-18 14:09:01 - * 旧版本支付服务,2015年之前的支付方式,之后版本请看新类 - * @see com.egzosn.pay.ali.api.AliPayService - */ -@Deprecated -public class AliPayService extends BasePayService { - - - - 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 static final String NOTIFY_ID = "notify_id"; - - public static final DateFormat df = new SimpleDateFormat("yyyy-MM-dd"); - - static { - df.setTimeZone(TimeZone.getTimeZone("GMT+8")); - } - public AliPayService(AliPayConfigStorage payConfigStorage) { - super(payConfigStorage); - } - - public AliPayService(AliPayConfigStorage payConfigStorage, HttpConfigStorage configStorage) { - super(payConfigStorage, configStorage); - } - - - public String getHttpsVerifyUrl() { - return HTTPS_REQ_URL + "?service=notify_verify"; - } - - /** - * 回调校验 - * - * @param params 回调回来的参数集 - * @return 签名校验 true通过 - */ - @Override - public boolean verify(Map params) { - - if (params.get(SIGN) == null || params.get(NOTIFY_ID) == null) { - LOG.debug("支付宝支付异常:params:" + params); - return false; - } - - try { - return signVerify(params, (String) params.get(SIGN)) && verifySource((String) params.get(NOTIFY_ID)); - } catch (PayErrorException e) { - LOG.error(e); - } - - return false; - } - /** - * 校验数据来源 - * - * @param id 业务id, 数据的真实性. - * @return true通过 - */ - @Override - public boolean verifySource(String id) { - return "true".equals(requestTemplate.getForObject( getHttpsVerifyUrl() + "&partner=" + payConfigStorage.getPid() + "¬ify_id=" + id, String.class)); - } - - /** - * 根据反馈回来的信息,生成签名结果 - * @param params 通知返回来的参数数组 - * @param sign 比对的签名结果 - * @return 生成的签名结果 - */ - @Override - public boolean signVerify(Map params, String sign) { - - return SignUtils.valueOf(payConfigStorage.getSignType()).verify(params, sign, payConfigStorage.getKeyPublic(), payConfigStorage.getInputCharset()); - } - - - /** - * 生成并设置签名 - * @param parameters 请求参数 - * @return 订单信息 - */ - private Map setSign(Map parameters){ - parameters.put("sign_type", payConfigStorage.getSignType()); - String sign = createSign(SignUtils.parameterText(parameters, "&", SIGN, "appId"), payConfigStorage.getInputCharset()); - parameters.put(SIGN, sign); - - return parameters; - } - - /** - * 获取公共请求参数 - * @param transactionType 交易类型 - * @return 公共请求参数 - */ - private Map getPublicParameters(TransactionType transactionType ){ - Map orderInfo = new TreeMap<>(); - orderInfo.put("app_id", payConfigStorage.getAppid()); - orderInfo.put("method", transactionType.getMethod()); - orderInfo.put("format", "json"); - orderInfo.put("charset", payConfigStorage.getInputCharset()); - - DateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); - df.setTimeZone(TimeZone.getTimeZone("GMT+8")); -// DateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); - orderInfo.put("timestamp", df.format(new Date())); - orderInfo.put("version", "1.0"); - return orderInfo; - } - - - - - /** - * 返回创建的订单信息 - * - * @param order 支付订单 - * @return 订单信息 - * @see PayOrder 支付订单信息 - */ - @Override - public Map orderInfo(PayOrder order) { - - Map orderInfo = getOrder(order); - - String sign = null; - if (AliTransactionType.APP == order.getTransactionType() ){ - sign = createSign(getOrderInfo(order), payConfigStorage.getInputCharset()); - }else { - sign = createSign(orderInfo, payConfigStorage.getInputCharset()); - } - - try { - sign = URLEncoder.encode(sign, "UTF-8"); - } catch (UnsupportedEncodingException e) { - e.printStackTrace(); - } - orderInfo.put(SIGN, sign); - orderInfo.put("sign_type", payConfigStorage.getSignType()); - return orderInfo; - } - - private String getOrderInfo(PayOrder order) { - 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() + "\""; - orderInfo = orderInfo + "&body=\"" + order.getBody() + "\""; - orderInfo = orderInfo + "&total_fee=\"" + Util.conversionAmount(order.getPrice()).toString() + "\""; - orderInfo = orderInfo + "¬ify_url=\"" + this.payConfigStorage.getNotifyUrl() + "\""; - orderInfo = orderInfo + "&service=\"mobile.securitypay.pay\""; - orderInfo = orderInfo + "&payment_type=\"1\""; - orderInfo = orderInfo + "&_input_charset=\""+ payConfigStorage.getInputCharset()+"\""; - orderInfo = orderInfo + "&it_b_pay=\"30m\""; - orderInfo = orderInfo + "&return_url=\""+payConfigStorage.getReturnUrl()+"\""; - return orderInfo; - } - - /** - * 支付宝创建订单信息 - * create the order info - * - * @param order 支付订单 - * @return 订单信息 - * @see PayOrder - */ - private Map getOrder(PayOrder order) { - Map orderInfo = new TreeMap<>(); - // 签约合作者身份ID - orderInfo.put("partner", payConfigStorage.getPid()); - // 签约卖家支付宝账号 - orderInfo.put("seller_id", payConfigStorage.getSeller()); - // 商户网站唯一订单号 - orderInfo.put("out_trade_no", order.getOutTradeNo()); - // 商品名称 - orderInfo.put("subject", order.getSubject()); - // 商品详情 - orderInfo.put("body", order.getBody()); - // 商品金额 - orderInfo.put("total_fee", Util.conversionAmount(order.getPrice()).toString() ); - // 服务器异步通知页面路径 - orderInfo.put("notify_url", payConfigStorage.getNotifyUrl() ); - // 服务接口名称, 固定值 - orderInfo.put("service", order.getTransactionType().getMethod() ); - // 支付类型, 固定值 - orderInfo.put("payment_type", "1" ); - // 参数编码, 固定值 - orderInfo.put("_input_charset", payConfigStorage.getInputCharset()); - // 设置未付款交易的超时时间 - // 默认30分钟,一旦超时,该笔交易就会自动被关闭。 - // 取值范围:1m~15d。 - // m-分钟,h-小时,d-天,1c-当天(无论交易何时创建,都在0点关闭)。 - // 该参数数值不接受小数点,如1.5h,可转换为90m。 - // TODO 2017/2/6 11:05 author: egan 目前写死,这一块建议配置 - - if (null != order.getExpirationTime()) { - orderInfo.put("timeout_express", DateUtils.minutesRemaining(order.getExpirationTime()) + "m"); - }else { - orderInfo.put("it_b_pay", "30m"); - } - // 支付宝处理完请求后,当前页面跳转到商户指定页面的路径,可空 - orderInfo.put("return_url", payConfigStorage.getReturnUrl()); - - return preOrderHandler(orderInfo, order); - } - - - - /** - * 获取输出消息,用户返回给支付端 - * - * @param code 状态 - * @param message 消息 - * @return 返回输出消息 - */ - @Override - public PayOutMessage getPayOutMessage(String code, String message) { - return PayOutMessage.TEXT().content(code.toLowerCase()).build(); - } - - /** - * 获取成功输出消息,用户返回给支付端 - * 主要用于拦截器中返回 - * @param payMessage 支付回调消息 - * @return 返回输出消息 - */ - @Override - public PayOutMessage successPayOutMessage(PayMessage payMessage) { - return PayOutMessage.TEXT().content("success").build(); - } - - - /** - * 获取输出消息,用户返回给支付端, 针对于web端 - * - * @param orderInfo 发起支付的订单信息 - * @param method 请求方式 "post" "get", - * @return 获取输出消息,用户返回给支付端, 针对于web端 - * @see MethodType 请求类型 - */ - @Override - public String buildRequest(Map orderInfo, MethodType method) { - - StringBuffer formHtml = new StringBuffer(); - - formHtml.append("
"); - - for (Map.Entry entry : orderInfo.entrySet()) { - Object o = entry.getValue(); - if (StringUtils.isEmpty((String)o) || "null".equals(o) ) { - continue; - } - formHtml.append(""); - } - - - //submit按钮控件请不要含有name属性 - formHtml.append(""); - formHtml.append(""); - - return formHtml.toString(); - } - - - - /** - * 生成二维码支付 - * 暂未实现或无此功能 - * @param orderInfo 发起支付的订单信息 - * @return 返回图片信息,支付时需要的 - */ - @Override - public String getQrPay(PayOrder orderInfo) { - throw new UnsupportedOperationException(); - } - - /** - * 刷卡付,pos主动扫码付款(条码付) - * @param order 发起支付的订单信息 - * @return 支付结果 - */ - @Override - public Map microPay(PayOrder order) { - throw new UnsupportedOperationException(); - } - - - /** - * 交易查询接口 - * @param tradeNo 支付平台订单号 - * @param outTradeNo 商户单号 - * @return 返回查询回来的结果集,支付方原值返回 - */ - @Override - public Map query(String tradeNo, String outTradeNo) { - - return secondaryInterface(tradeNo, outTradeNo, AliTransactionType.QUERY); - } - - - - /** - * 交易关闭接口 - * - * @param tradeNo 支付平台订单号 - * @param outTradeNo 商户单号 - * @return 返回支付方交易关闭后的结果 - */ - @Override - public Map close(String tradeNo, String outTradeNo) { - - return secondaryInterface(tradeNo, outTradeNo, AliTransactionType.CLOSE); - } - - - /** - * 支付交易返回失败或支付系统超时,调用该接口撤销交易。 - * 如果此订单用户支付失败,支付宝系统会将此订单关闭;如果用户支付成功,支付宝系统会将此订单资金退还给用户。 - * 注意:只有发生支付系统超时或者支付结果未知时可调用撤销,其他正常支付的单如需实现相同功能请调用申请退款API。 - * 提交支付交易后调用【查询订单API】,没有明确的支付结果再调用【撤销订单API】。 - * - * @param tradeNo 支付平台订单号 - * @param outTradeNo 商户单号 - * @return 返回支付方交易撤销后的结果 - */ - @Override - public Map cancel(String tradeNo, String outTradeNo) { - return secondaryInterface(tradeNo, outTradeNo, AliTransactionType.CANCEL); - } - - /** - * 申请退款接口 - * 废弃 - * @param tradeNo 支付平台订单号 - * @param outTradeNo 商户单号 - * @param refundAmount 退款金额 - * @param totalAmount 总金额 - * @return 返回支付方申请退款后的结果 - * @see #refund(RefundOrder, Callback) - */ - @Deprecated - @Override - public Map refund(String tradeNo, String outTradeNo, BigDecimal refundAmount, BigDecimal totalAmount) { - - return refund(new RefundOrder(tradeNo, outTradeNo, refundAmount, totalAmount)); - } - - /** - * 申请退款接口 - * - * @param refundOrder 退款订单信息 - * @return 返回支付方申请退款后的结果 - */ - @Override - public Map refund(RefundOrder refundOrder) { - Map parameters = getPublicParameters(AliTransactionType.REFUND); - - Map bizContent = getBizContent(refundOrder.getTradeNo(), refundOrder.getOutTradeNo(), null); - if (!StringUtils.isEmpty(refundOrder.getRefundNo())){ - bizContent.put("out_request_no", refundOrder.getRefundNo()); - } - bizContent.put("refund_amount", refundOrder.getRefundAmount()); - //设置请求参数的集合 - parameters.put("biz_content", JSON.toJSONString(bizContent)); - //设置签名 - setSign(parameters); - return requestTemplate.getForObject(QUERY_REQ_URL + "?" + UriVariables.getMapToParameters(parameters), JSONObject.class); - } - - /** - * 查询退款 - * - * @param tradeNo 支付平台订单号 - * @param outTradeNo 商户单号 - * @return 返回支付方查询退款后的结果 - */ - @Override - public Map refundquery(String tradeNo, String outTradeNo) { - return secondaryInterface(tradeNo, outTradeNo, AliTransactionType.REFUNDQUERY); - } - /** - * 查询退款 - * - * @param refundOrder 退款订单单号信息 - * @return 返回支付方查询退款后的结果 - */ - @Override - public Map refundquery(RefundOrder refundOrder){ - - //获取公共参数 - Map parameters = getPublicParameters(AliTransactionType.REFUNDQUERY); - - Map bizContent = getBizContent(refundOrder.getTradeNo(), refundOrder.getOutTradeNo(), null); - if (!StringUtils.isEmpty(refundOrder.getRefundNo())){ - bizContent.put("out_request_no", refundOrder.getRefundNo()); - } - //设置请求参数的集合 - parameters.put("biz_content", JSON.toJSONString(bizContent)); - - //设置签名 - setSign(parameters); - return requestTemplate.getForObject(QUERY_REQ_URL + "?" + UriVariables.getMapToParameters(parameters), JSONObject.class); - - } - - - /** - * 目前只支持日账单 - * @param billDate 账单类型,商户通过接口或商户经开放平台授权后其所属服务商通过接口可以获取以下账单类型:trade、signcustomer;trade指商户基于支付宝交易收单的业务账单;signcustomer是指基于商户支付宝余额收入及支出等资金变动的帐务账单; - * @param billType 账单时间:日账单格式为yyyy-MM-dd,月账单格式为yyyy-MM。 - * @return 返回支付方下载对账单的结果 - */ - @Override - public Map downloadbill(Date billDate, String billType) { - //获取公共参数 - Map parameters = getPublicParameters(AliTransactionType.DOWNLOADBILL); - - Map bizContent = new TreeMap<>(); - bizContent.put("bill_type", billType); - //目前只支持日账单 - - bizContent.put("bill_date", df.format(billDate)); - //设置请求参数的集合 - parameters.put("biz_content", JSON.toJSONString(bizContent)); - //设置签名 - setSign(parameters); - return requestTemplate.getForObject(QUERY_REQ_URL + "?" + UriVariables.getMapToParameters(parameters), JSONObject.class); - } - - - /** - * - * @param tradeNoOrBillDate 支付平台订单号或者账单类型, 具体请 - * 类型为{@link String }或者 {@link Date },类型须强制限制,类型不对应则抛出异常{@link PayErrorException} - * @param outTradeNoBillType 商户单号或者 账单类型 - * @param transactionType 交易类型 - * @return 返回支付方对应接口的结果 - */ - @Override - public Map secondaryInterface(Object tradeNoOrBillDate, String outTradeNoBillType, TransactionType transactionType) { - if (transactionType == AliTransactionType.DOWNLOADBILL){ - if (tradeNoOrBillDate instanceof Date){ - return downloadbill((Date) tradeNoOrBillDate, outTradeNoBillType); - } - throw new PayErrorException(new PayException("failure", "非法类型异常:" + tradeNoOrBillDate.getClass())); - } - - if (!(tradeNoOrBillDate instanceof String)){ - throw new PayErrorException(new PayException("failure", "非法类型异常:" + tradeNoOrBillDate.getClass())); - } - - //获取公共参数 - Map parameters = getPublicParameters(transactionType); - //设置请求参数的集合 - parameters.put("biz_content", getContentToJson(tradeNoOrBillDate.toString(), outTradeNoBillType)); - //设置签名 - setSign(parameters); - return requestTemplate.getForObject(QUERY_REQ_URL + "?" + UriVariables.getMapToParameters(parameters), JSONObject.class); - - } - - /** - * 转账 - * - * @param order 转账订单 - * - * @return 对应的转账结果 - */ - @Override - public Map transfer(TransferOrder order) { - return null; - } - - /** - * 获取支付请求地址 - * - * @param transactionType 交易类型 - * @return 请求地址 - */ - @Override - public String getReqUrl(TransactionType transactionType) { - return null; - } - - - /** - * 获取biz_content。请求参数的集合 不包含下载账单 - * @param tradeNo 支付平台订单号 - * @param outTradeNo 商户单号 - * @param bizContent 请求参数的集合 - * @return 请求参数的集合 不包含下载账单 - */ - private Map getBizContent(String tradeNo, String outTradeNo, Map bizContent){ - if (null == bizContent){ - bizContent = new TreeMap<>(); - } - if (null != outTradeNo){ - bizContent.put("out_trade_no", outTradeNo); - } - if (null != tradeNo){ - bizContent.put("trade_no", tradeNo); - } - return bizContent; - } - - /** - * 获取biz_content。不包含下载账单 - * @param tradeNo 支付平台订单号 - * @param outTradeNo 商户单号 - * @return 请求参数的集合 不包含下载账单 - */ - private String getContentToJson(String tradeNo, String outTradeNo){ - - return JSON.toJSONString(getBizContent(tradeNo, outTradeNo, null)); - } - -} diff --git a/pay-java-ali/src/main/java/com/egzosn/pay/ali/before/bean/AliTransactionType.java b/pay-java-ali/src/main/java/com/egzosn/pay/ali/before/bean/AliTransactionType.java deleted file mode 100644 index 6dcf1d1..0000000 --- a/pay-java-ali/src/main/java/com/egzosn/pay/ali/before/bean/AliTransactionType.java +++ /dev/null @@ -1,84 +0,0 @@ -package com.egzosn.pay.ali.before.bean; - -import com.egzosn.pay.common.bean.TransactionType; - -/** - * 阿里交易类型 - *
- * 说明交易类型主要用于支付接口调用参数所需
- * {@link #APP 新版app支付}
- *
- *
- *
- * 
- * - * @author egan - * - * email egzosn@gmail.com - * date 2016/10/19 22:58 - * - * @see com.egzosn.pay.ali.bean.AliTransactionType - */ -@Deprecated -public enum AliTransactionType implements TransactionType { - /** - * 即时到帐 - */ - DIRECT("create_direct_pay_by_user"), - /** - * 移动支付 - */ - APP("mobile.securitypay.pay"), - /** - * 手机网站支付 - */ - WAP("alipay.wap.create.direct.pay.by.user"), - - //交易辅助接口,以下属于新版接口 - - /** - * 交易订单查询 - */ - QUERY("alipay.trade.query"), - /** - * 交易订单关闭 - */ - CLOSE("alipay.trade.close"), - /** - * 交易订单撤销 - */ - CANCEL("alipay.trade.cancel "), - /** - * 退款 - */ - REFUND("alipay.trade.refund"), - /** - * 退款查询 - */ - REFUNDQUERY("alipay.trade.fastpay.refund.query"), - /** - * 下载对账单 - */ - DOWNLOADBILL("alipay.data.dataservice.bill.downloadurl.query") - ; - - private String method; - - private AliTransactionType(String method) { - this.method = method; - } - - @Override - public String getType() { - return this.name(); - } - - /** - * 获取接口名称 - * @return 接口名称 - */ - @Override - public String getMethod() { - return this.method; - } -} diff --git a/pay-java-common/src/main/java/com/egzosn/pay/common/bean/Order.java b/pay-java-common/src/main/java/com/egzosn/pay/common/bean/Order.java new file mode 100644 index 0000000..57046df --- /dev/null +++ b/pay-java-common/src/main/java/com/egzosn/pay/common/bean/Order.java @@ -0,0 +1,22 @@ +package com.egzosn.pay.common.bean; + +import java.util.Map; + +/** + * 支付订单信息 + * + * @author egan + *
+ *      email egzosn@gmail.com
+ *      date 2020/01/05 13:34
+ *  
+ */ +public interface Order { + + /** + * 获取订单属性 这里可用做覆盖已设置的订单信息属性,订单信息在签名前进行覆盖。 + * @return 属性 + */ + Map getAttr(); + +} 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 d4c52fa..434d0e1 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 @@ -2,6 +2,8 @@ package com.egzosn.pay.common.bean; import java.math.BigDecimal; import java.util.Date; +import java.util.HashMap; +import java.util.Map; /** * 支付订单信息 @@ -12,7 +14,7 @@ import java.util.Date; * date 2016/10/19 22:34 * */ -public class PayOrder { +public class PayOrder implements Order { /** * 商品名称 */ @@ -82,6 +84,11 @@ public class PayOrder { */ private Date expirationTime; + /** + * 订单附加信息,可用于预设未提供的参数,这里会覆盖以上所有的订单信息, + */ + private Map attr; + public PayOrder() { } @@ -248,6 +255,24 @@ public class PayOrder { this.expirationTime = expirationTime; } + @Override + public Map getAttr() { + if (null == attr){ + attr = new HashMap<>(); + } + return attr; + } + + /** + * 添加订单信息 + * @param key key + * @param value 值 + */ + public void addAttr(String key, Object value) { + getAttr().put(key, value); + } + + /** * 对 subject body 进行 trim 运算, * 以防止在签名是可能造成的签名错误问题 diff --git a/pay-java-fuiou/src/main/java/com/egzosn/pay/fuiou/api/FuiouPayService.java b/pay-java-fuiou/src/main/java/com/egzosn/pay/fuiou/api/FuiouPayService.java index 3b06ce2..8a3b4b0 100644 --- a/pay-java-fuiou/src/main/java/com/egzosn/pay/fuiou/api/FuiouPayService.java +++ b/pay-java-fuiou/src/main/java/com/egzosn/pay/fuiou/api/FuiouPayService.java @@ -203,6 +203,7 @@ public class FuiouPayService extends BasePayService { parameters.put("rem", ""); //版本号 parameters.put("ver", "1.0.1"); + parameters.putAll(order.getAttr()); return preOrderHandler(parameters, order); } diff --git a/pay-java-payoneer/src/main/java/com/egzosn/pay/payoneer/api/PayoneerPayService.java b/pay-java-payoneer/src/main/java/com/egzosn/pay/payoneer/api/PayoneerPayService.java index 76afaba..29cdb28 100644 --- a/pay-java-payoneer/src/main/java/com/egzosn/pay/payoneer/api/PayoneerPayService.java +++ b/pay-java-payoneer/src/main/java/com/egzosn/pay/payoneer/api/PayoneerPayService.java @@ -184,7 +184,7 @@ public class PayoneerPayService extends BasePayService im } params.put("currency", order.getCurType()); params.put("description", order.getSubject()); - + params.putAll(order.getAttr()); return preOrderHandler(params, order); } diff --git a/pay-java-union/src/main/java/com/egzosn/pay/union/api/UnionPayService.java b/pay-java-union/src/main/java/com/egzosn/pay/union/api/UnionPayService.java index 93792d3..2d33850 100644 --- a/pay-java-union/src/main/java/com/egzosn/pay/union/api/UnionPayService.java +++ b/pay-java-union/src/main/java/com/egzosn/pay/union/api/UnionPayService.java @@ -246,11 +246,7 @@ public class UnionPayService extends BasePayService { @Override public Map orderInfo(PayOrder order) { Map params = this.getCommonParam(); -// if(order instanceof UnionPayOrder){ -// UnionPayOrder unionPayOrder = (UnionPayOrder)order; -// //todo 其他参数 -//// params.put(); -// } + UnionTransactionType type = (UnionTransactionType) order.getTransactionType(); @@ -288,6 +284,7 @@ public class UnionPayService extends BasePayService { params.put(SDKConstants.param_payTimeout, getPayTimeout(order.getExpirationTime())); params.put("orderDesc", order.getSubject()); } + params.putAll(order.getAttr()); params = preOrderHandler(params, order); return setSign(params); } 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 b108c5b..6fefb92 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 @@ -227,6 +227,7 @@ public class WxYouDianPayService extends BasePayService data = new TreeMap<>(); data.put("access_token", getAccessToken()); data.put("paymoney", Util.conversionAmount(order.getPrice()).toString()); + data.putAll(order.getAttr()); data = preOrderHandler(data, order); String apbNonce = SignUtils.randomStr(); String sign = createSign(SignUtils.parameterText(data, "") + apbNonce, payConfigStorage.getInputCharset()); 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 b2a412e..f4b67de 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 @@ -222,6 +222,7 @@ public class WxPayService extends BasePayService { } ((WxTransactionType) order.getTransactionType()).setAttribute(parameters, order); + parameters.putAll(order.getAttr()); parameters = preOrderHandler(parameters, order); setSign(parameters); diff --git a/pay-java-yiji/src/main/java/com/egzosn/pay/yiji/api/YiJiPayService.java b/pay-java-yiji/src/main/java/com/egzosn/pay/yiji/api/YiJiPayService.java index cece47f..1428b15 100644 --- a/pay-java-yiji/src/main/java/com/egzosn/pay/yiji/api/YiJiPayService.java +++ b/pay-java-yiji/src/main/java/com/egzosn/pay/yiji/api/YiJiPayService.java @@ -175,7 +175,7 @@ public class YiJiPayService extends BasePayService { if (null != order.getCurType()){ orderInfo.put("currency", order.getCurType()); } - + orderInfo.putAll(order.getAttr()); return preOrderHandler(orderInfo, order); } -- Gitee From 7f642fc0e28b7d4ec748ce7019a2f5f8ca041730 Mon Sep 17 00:00:00 2001 From: egzosn Date: Mon, 6 Jan 2020 11:29:59 +0800 Subject: [PATCH 096/299] =?UTF-8?q?=E5=AD=97=E7=AC=A6=E5=A4=84=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pay-java-ali/src/test/java/PayTest.java | 2 +- .../com/egzosn/pay/common/bean/PayOrder.java | 17 +++++------------ .../egzosn/pay/common/util/str/StringUtils.java | 9 +++++++++ pay-java-demo/README.md | 5 +---- .../com/egzosn/pay/demo/entity/PayType.java | 2 +- 5 files changed, 17 insertions(+), 18 deletions(-) diff --git a/pay-java-ali/src/test/java/PayTest.java b/pay-java-ali/src/test/java/PayTest.java index 13d496e..b266376 100644 --- a/pay-java-ali/src/test/java/PayTest.java +++ b/pay-java-ali/src/test/java/PayTest.java @@ -53,7 +53,7 @@ public class PayTest { /*-----------即时到帐 WAP 网页支付-------------------*/ // payOrder.setTransactionType(AliTransactionType.WAP); //WAP支付 - payOrder.setTransactionType(AliTransactionType.DIRECT); // 即时到帐 PC网页支付 + payOrder.setTransactionType(AliTransactionType.PAGE); // 即时到帐 PC网页支付 //获取支付所需的信息 Map directOrderInfo = service.orderInfo(payOrder); //获取表单提交对应的字符串,将其序列化到页面即可, 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 434d0e1..57a6842 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 @@ -5,6 +5,8 @@ import java.util.Date; import java.util.HashMap; import java.util.Map; +import com.egzosn.pay.common.util.str.StringUtils; + /** * 支付订单信息 * @@ -99,10 +101,10 @@ public class PayOrder implements Order { } public PayOrder(String subject, String body, BigDecimal price, String outTradeNo, TransactionType transactionType) { - this.subject = tryTrim(subject); - this.body = tryTrim(body); + this.subject = StringUtils.tryTrim(subject); + this.body = StringUtils.tryTrim(body); this.price = price; - this.outTradeNo = tryTrim(outTradeNo); + this.outTradeNo = StringUtils.tryTrim(outTradeNo); this.transactionType = transactionType; } @@ -273,15 +275,6 @@ public class PayOrder implements Order { } - /** - * 对 subject body 进行 trim 运算, - * 以防止在签名是可能造成的签名错误问题 - * @param str - * @return - */ - private static String tryTrim(String str) { - return str == null ? null : str.trim(); - } @Override public String toString() { diff --git a/pay-java-common/src/main/java/com/egzosn/pay/common/util/str/StringUtils.java b/pay-java-common/src/main/java/com/egzosn/pay/common/util/str/StringUtils.java index 8b451a2..79a0636 100644 --- a/pay-java-common/src/main/java/com/egzosn/pay/common/util/str/StringUtils.java +++ b/pay-java-common/src/main/java/com/egzosn/pay/common/util/str/StringUtils.java @@ -125,5 +125,14 @@ public class StringUtils { throw new RuntimeException("转码过程中出现错误,指定的编码集不对,您目前指定的编码集是:" + charset); } } + /** + * 对 subject body 进行 trim 运算, + * 以防止在签名是可能造成的签名错误问题 + * @param str 字符 + * @return 去除空格之后的字符 + */ + public static String tryTrim(String str) { + return str == null ? null : str.trim(); + } } diff --git a/pay-java-demo/README.md b/pay-java-demo/README.md index 6cf5816..0f00886 100644 --- a/pay-java-demo/README.md +++ b/pay-java-demo/README.md @@ -14,7 +14,7 @@ public enum PayType implements BasePayType { aliPay{ /** - * @see com.egzosn.pay.ali.api.AliPayService 17年更新的版本,旧版本请自行切换{@link com.egzosn.pay.ali.before.api.AliPayService } + * @see com.egzosn.pay.ali.api.AliPayService * @param apyAccount * @return */ @@ -176,7 +176,6 @@ public class PayResponse { router = new PayMessageRouter(this.service); router .rule() - .async(false) .msgType(MsgType.text.name()) //消息类型 .payType(PayType.aliPay.name()) //支付账户事件类型 .transactionType(AliTransactionType.UNAWARE.name())//交易类型,有关回调的可在这处理 @@ -184,13 +183,11 @@ public class PayResponse { .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()) .handler(autowire(new YouDianPayMessageHandler(payId))) 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 519e54e..85b0b91 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 @@ -43,7 +43,7 @@ public enum PayType implements BasePayType { aliPay{ /** - * @see com.egzosn.pay.ali.api.AliPayService 17年更新的版本,旧版本请自行切换 + * @see com.egzosn.pay.ali.api.AliPayService * @param apyAccount * @return */ -- Gitee From 563e4d10801b43736dfbb3fb06ccde12bd0ac843 Mon Sep 17 00:00:00 2001 From: egzosn Date: Tue, 7 Jan 2020 09:29:28 +0800 Subject: [PATCH 097/299] 2.13.1 --- README.md | 2 +- pay-java-ali/pom.xml | 2 +- pay-java-common/pom.xml | 2 +- pay-java-demo/pom.xml | 2 +- pay-java-fuiou/pom.xml | 2 +- pay-java-payoneer/pom.xml | 2 +- pay-java-paypal/pom.xml | 2 +- pay-java-union/pom.xml | 2 +- pay-java-wx-youdian/pom.xml | 2 +- pay-java-wx/pom.xml | 2 +- pay-java-yiji/pom.xml | 2 +- pom.xml | 12 ++++++------ 12 files changed, 17 insertions(+), 17 deletions(-) diff --git a/README.md b/README.md index cc7525c..c5d602a 100644 --- a/README.md +++ b/README.md @@ -21,7 +21,7 @@ com.egzosn {module-name} - 2.12.9 + 2.13.1 ``` diff --git a/pay-java-ali/pom.xml b/pay-java-ali/pom.xml index 35e8c8c..229ac85 100644 --- a/pay-java-ali/pom.xml +++ b/pay-java-ali/pom.xml @@ -5,7 +5,7 @@ pay-java-parent com.egzosn - 2.13.1-SNAPSHOT + 2.13.1 4.0.0 pay-java-ali diff --git a/pay-java-common/pom.xml b/pay-java-common/pom.xml index 0a0a62d..0c6cdfc 100644 --- a/pay-java-common/pom.xml +++ b/pay-java-common/pom.xml @@ -5,7 +5,7 @@ pay-java-parent com.egzosn - 2.13.1-SNAPSHOT + 2.13.1 4.0.0 jar diff --git a/pay-java-demo/pom.xml b/pay-java-demo/pom.xml index b196ab5..896b184 100644 --- a/pay-java-demo/pom.xml +++ b/pay-java-demo/pom.xml @@ -5,7 +5,7 @@ pay-java-parent com.egzosn - 2.13.1-SNAPSHOT + 2.13.1 4.0.0 war diff --git a/pay-java-fuiou/pom.xml b/pay-java-fuiou/pom.xml index a77d590..524b783 100644 --- a/pay-java-fuiou/pom.xml +++ b/pay-java-fuiou/pom.xml @@ -5,7 +5,7 @@ pay-java-parent com.egzosn - 2.13.1-SNAPSHOT + 2.13.1 4.0.0 pay-java-fuiou diff --git a/pay-java-payoneer/pom.xml b/pay-java-payoneer/pom.xml index 6020570..19f68f3 100644 --- a/pay-java-payoneer/pom.xml +++ b/pay-java-payoneer/pom.xml @@ -5,7 +5,7 @@ pay-java-parent com.egzosn - 2.13.1-SNAPSHOT + 2.13.1 4.0.0 pay-java-payoneer diff --git a/pay-java-paypal/pom.xml b/pay-java-paypal/pom.xml index 969573e..f0d8047 100644 --- a/pay-java-paypal/pom.xml +++ b/pay-java-paypal/pom.xml @@ -5,7 +5,7 @@ pay-java-parent com.egzosn - 2.13.1-SNAPSHOT + 2.13.1 4.0.0 diff --git a/pay-java-union/pom.xml b/pay-java-union/pom.xml index b9ebdc1..d22f2fd 100644 --- a/pay-java-union/pom.xml +++ b/pay-java-union/pom.xml @@ -5,7 +5,7 @@ pay-java-parent com.egzosn - 2.13.1-SNAPSHOT + 2.13.1 4.0.0 diff --git a/pay-java-wx-youdian/pom.xml b/pay-java-wx-youdian/pom.xml index c424d22..0b77a06 100644 --- a/pay-java-wx-youdian/pom.xml +++ b/pay-java-wx-youdian/pom.xml @@ -5,7 +5,7 @@ pay-java-parent com.egzosn - 2.13.1-SNAPSHOT + 2.13.1 4.0.0 pay-java-wx-youdian diff --git a/pay-java-wx/pom.xml b/pay-java-wx/pom.xml index 484a61e..9e124f0 100644 --- a/pay-java-wx/pom.xml +++ b/pay-java-wx/pom.xml @@ -5,7 +5,7 @@ pay-java-parent com.egzosn - 2.13.1-SNAPSHOT + 2.13.1 4.0.0 pay-java-wx diff --git a/pay-java-yiji/pom.xml b/pay-java-yiji/pom.xml index 63fb58e..bb8b23e 100644 --- a/pay-java-yiji/pom.xml +++ b/pay-java-yiji/pom.xml @@ -5,7 +5,7 @@ pay-java-parent com.egzosn - 2.13.1-SNAPSHOT + 2.13.1 4.0.0 diff --git a/pom.xml b/pom.xml index 979bbd9..f756558 100644 --- a/pom.xml +++ b/pom.xml @@ -7,7 +7,7 @@ com.egzosn pay-java-parent pom - 2.13.1-SNAPSHOT + 2.13.1 Pay Java - Parent Pay Java Parent @@ -52,13 +52,13 @@ pay-java-payoneer pay-java-paypal pay-java-yiji - pay-java-demo + - 2.13.1-SNAPSHOT + 2.13.1 4.5.4 1.2.17 1.2.58 @@ -183,10 +183,10 @@ sign-artifacts - none - + -- Gitee From 290502139ee227f9c08322f502d1a0e9cf5aa3a5 Mon Sep 17 00:00:00 2001 From: egzosn Date: Tue, 7 Jan 2020 11:06:54 +0800 Subject: [PATCH 098/299] =?UTF-8?q?=E5=AD=97=E7=AC=A6=E5=A4=84=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index f756558..cb8f1ba 100644 --- a/pom.xml +++ b/pom.xml @@ -52,7 +52,7 @@ pay-java-payoneer pay-java-paypal pay-java-yiji - + pay-java-demo -- Gitee From bf424148d76e7e8a71ba360410565f6bb464d432 Mon Sep 17 00:00:00 2001 From: egzosn Date: Thu, 9 Jan 2020 18:29:13 +0800 Subject: [PATCH 099/299] =?UTF-8?q?2.13.2-SNAPSHOT=E5=BC=80=E5=A7=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pay-java-ali/pom.xml | 2 +- pay-java-fuiou/pom.xml | 2 +- pay-java-payoneer/pom.xml | 2 +- pay-java-paypal/pom.xml | 2 +- pay-java-union/pom.xml | 2 +- pay-java-wx-youdian/pom.xml | 2 +- pay-java-wx/pom.xml | 2 +- pay-java-yiji/pom.xml | 2 +- pom.xml | 16 ++++++++++------ 9 files changed, 18 insertions(+), 14 deletions(-) diff --git a/pay-java-ali/pom.xml b/pay-java-ali/pom.xml index 229ac85..d909904 100644 --- a/pay-java-ali/pom.xml +++ b/pay-java-ali/pom.xml @@ -5,7 +5,7 @@ pay-java-parent com.egzosn - 2.13.1 + 2.13.2-SNAPSHOT 4.0.0 pay-java-ali diff --git a/pay-java-fuiou/pom.xml b/pay-java-fuiou/pom.xml index 524b783..b3c1383 100644 --- a/pay-java-fuiou/pom.xml +++ b/pay-java-fuiou/pom.xml @@ -5,7 +5,7 @@ pay-java-parent com.egzosn - 2.13.1 + 2.13.2-SNAPSHOT 4.0.0 pay-java-fuiou diff --git a/pay-java-payoneer/pom.xml b/pay-java-payoneer/pom.xml index 19f68f3..22940d6 100644 --- a/pay-java-payoneer/pom.xml +++ b/pay-java-payoneer/pom.xml @@ -5,7 +5,7 @@ pay-java-parent com.egzosn - 2.13.1 + 2.13.2-SNAPSHOT 4.0.0 pay-java-payoneer diff --git a/pay-java-paypal/pom.xml b/pay-java-paypal/pom.xml index f0d8047..7f56a2e 100644 --- a/pay-java-paypal/pom.xml +++ b/pay-java-paypal/pom.xml @@ -5,7 +5,7 @@ pay-java-parent com.egzosn - 2.13.1 + 2.13.2-SNAPSHOT 4.0.0 diff --git a/pay-java-union/pom.xml b/pay-java-union/pom.xml index d22f2fd..61cee3e 100644 --- a/pay-java-union/pom.xml +++ b/pay-java-union/pom.xml @@ -5,7 +5,7 @@ pay-java-parent com.egzosn - 2.13.1 + 2.13.2-SNAPSHOT 4.0.0 diff --git a/pay-java-wx-youdian/pom.xml b/pay-java-wx-youdian/pom.xml index 0b77a06..7ce65c9 100644 --- a/pay-java-wx-youdian/pom.xml +++ b/pay-java-wx-youdian/pom.xml @@ -5,7 +5,7 @@ pay-java-parent com.egzosn - 2.13.1 + 2.13.2-SNAPSHOT 4.0.0 pay-java-wx-youdian diff --git a/pay-java-wx/pom.xml b/pay-java-wx/pom.xml index 9e124f0..a305a8c 100644 --- a/pay-java-wx/pom.xml +++ b/pay-java-wx/pom.xml @@ -5,7 +5,7 @@ pay-java-parent com.egzosn - 2.13.1 + 2.13.2-SNAPSHOT 4.0.0 pay-java-wx diff --git a/pay-java-yiji/pom.xml b/pay-java-yiji/pom.xml index bb8b23e..44ba156 100644 --- a/pay-java-yiji/pom.xml +++ b/pay-java-yiji/pom.xml @@ -5,7 +5,7 @@ pay-java-parent com.egzosn - 2.13.1 + 2.13.2-SNAPSHOT 4.0.0 diff --git a/pom.xml b/pom.xml index b426986..4f03d79 100644 --- a/pom.xml +++ b/pom.xml @@ -7,7 +7,7 @@ com.egzosn pay-java-parent pom - 2.13.1 + 2.13.2-SNAPSHOT Pay Java - Parent Pay Java Parent @@ -34,6 +34,11 @@ zhangchenghui.dev@gmail.com https://github.com/objcoding + + hocgin + hocgin@gmail.com + https://github.com/hocgin + scm:git:https://github.com/egzosn/pay-java-parent.git @@ -59,7 +64,7 @@ - 2.13.1 + 2.13.2-SNAPSHOT 4.5.4 1.2.17 1.2.58 @@ -105,7 +110,6 @@ core ${zxing.version} - org.junit.jupiter junit-jupiter @@ -191,10 +195,10 @@ sign-artifacts - install - + none + -- Gitee From f77c1edd96ecf64312c8d5e7998ebab17421643c Mon Sep 17 00:00:00 2001 From: egzosn Date: Tue, 10 Mar 2020 22:04:12 +0800 Subject: [PATCH 100/299] =?UTF-8?q?=E8=87=AA=E5=AE=9A=E4=B9=89=E5=8F=82?= =?UTF-8?q?=E6=95=B0=E8=AE=BE=E7=BD=AE=E6=96=B9=E5=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/egzosn/pay/ali/api/AliPayService.java | 2 +- .../egzosn/pay/common/api/BasePayService.java | 16 +++++ .../com/egzosn/pay/common/bean/Order.java | 8 ++- .../com/egzosn/pay/common/bean/PayOrder.java | 10 +++- .../egzosn/pay/common/bean/TransferOrder.java | 34 ++++++++++- .../egzosn/pay/fuiou/api/FuiouPayService.java | 2 +- .../pay/payoneer/api/PayoneerPayService.java | 2 +- .../egzosn/pay/union/api/UnionPayService.java | 2 +- .../wx/youdian/api/WxYouDianPayService.java | 2 +- .../java/com/egzosn/pay/wx/api/WxConst.java | 42 +++++++++++++ .../com/egzosn/pay/wx/api/WxPayService.java | 59 +++++++------------ .../egzosn/pay/yiji/api/YiJiPayService.java | 2 +- 12 files changed, 132 insertions(+), 49 deletions(-) create mode 100644 pay-java-wx/src/main/java/com/egzosn/pay/wx/api/WxConst.java 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 b9685c8..06cd7b5 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 @@ -226,7 +226,7 @@ public class AliPayService extends BasePayService { bizContent.put("timeout_express", DateUtils.minutesRemaining(order.getExpirationTime()) + "m"); } orderInfo.put(BIZ_CONTENT, JSON.toJSONString(bizContent)); - orderInfo.putAll(order.getAttr()); + orderInfo.putAll(order.getAttrs()); return preOrderHandler(orderInfo, order); } diff --git a/pay-java-common/src/main/java/com/egzosn/pay/common/api/BasePayService.java b/pay-java-common/src/main/java/com/egzosn/pay/common/api/BasePayService.java index c955e04..d2be6c3 100644 --- a/pay-java-common/src/main/java/com/egzosn/pay/common/api/BasePayService.java +++ b/pay-java-common/src/main/java/com/egzosn/pay/common/api/BasePayService.java @@ -467,4 +467,20 @@ public abstract class BasePayService implements Pay return orderInfo; } + protected Map setParameters(Map parameters, String key, String value) { + if (StringUtils.isNotEmpty(value)) { + parameters.put(key, value); + } + return parameters; + } + + protected Map setParameters(Map parameters, String key, Order order) { + Object attr = order.getAttr(key); + if (null != attr && !"".equals(attr)) { + parameters.put(key, attr); + } + return parameters; + } + + } diff --git a/pay-java-common/src/main/java/com/egzosn/pay/common/bean/Order.java b/pay-java-common/src/main/java/com/egzosn/pay/common/bean/Order.java index 57046df..5f18b00 100644 --- a/pay-java-common/src/main/java/com/egzosn/pay/common/bean/Order.java +++ b/pay-java-common/src/main/java/com/egzosn/pay/common/bean/Order.java @@ -17,6 +17,12 @@ public interface Order { * 获取订单属性 这里可用做覆盖已设置的订单信息属性,订单信息在签名前进行覆盖。 * @return 属性 */ - Map getAttr(); + Map getAttrs(); + /** + * 获取订单属性 这里可用做覆盖已设置的订单信息属性,订单信息在签名前进行覆盖。 + * @param key 属性名 + * @return 属性 + */ + Object getAttr(String key); } 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 57a6842..dd550be 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 @@ -258,20 +258,26 @@ public class PayOrder implements Order { } @Override - public Map getAttr() { + public Map getAttrs() { if (null == attr){ attr = new HashMap<>(); } return attr; } + @Override + public Object getAttr(String key) { + return getAttrs().get(key); + } + + /** * 添加订单信息 * @param key key * @param value 值 */ public void addAttr(String key, Object value) { - getAttr().put(key, value); + getAttrs().put(key, value); } diff --git a/pay-java-common/src/main/java/com/egzosn/pay/common/bean/TransferOrder.java b/pay-java-common/src/main/java/com/egzosn/pay/common/bean/TransferOrder.java index 2b89117..83f05b8 100644 --- a/pay-java-common/src/main/java/com/egzosn/pay/common/bean/TransferOrder.java +++ b/pay-java-common/src/main/java/com/egzosn/pay/common/bean/TransferOrder.java @@ -1,6 +1,8 @@ package com.egzosn.pay.common.bean; import java.math.BigDecimal; +import java.util.HashMap; +import java.util.Map; /** * 转账订单 @@ -10,7 +12,7 @@ import java.math.BigDecimal; * date 2018/1/31 * */ -public class TransferOrder { +public class TransferOrder implements Order { /** * 转账批次订单单号 @@ -79,6 +81,12 @@ public class TransferOrder { */ private String ip; + + /** + * 订单附加信息,可用于预设未提供的参数,这里会覆盖以上所有的订单信息, + */ + private Map attr; + public String getBatchNo() { return batchNo; } @@ -190,4 +198,28 @@ public class TransferOrder { public void setIp(String ip) { this.ip = ip; } + @Override + public Map getAttrs() { + if (null == attr){ + attr = new HashMap<>(); + } + return attr; + } + + @Override + public Object getAttr(String key) { + return getAttrs().get(key); + } + + + /** + * 添加订单信息 + * @param key key + * @param value 值 + */ + public void addAttr(String key, Object value) { + getAttrs().put(key, value); + } + + } diff --git a/pay-java-fuiou/src/main/java/com/egzosn/pay/fuiou/api/FuiouPayService.java b/pay-java-fuiou/src/main/java/com/egzosn/pay/fuiou/api/FuiouPayService.java index 8a3b4b0..69a28c0 100644 --- a/pay-java-fuiou/src/main/java/com/egzosn/pay/fuiou/api/FuiouPayService.java +++ b/pay-java-fuiou/src/main/java/com/egzosn/pay/fuiou/api/FuiouPayService.java @@ -203,7 +203,7 @@ public class FuiouPayService extends BasePayService { parameters.put("rem", ""); //版本号 parameters.put("ver", "1.0.1"); - parameters.putAll(order.getAttr()); + parameters.putAll(order.getAttrs()); return preOrderHandler(parameters, order); } diff --git a/pay-java-payoneer/src/main/java/com/egzosn/pay/payoneer/api/PayoneerPayService.java b/pay-java-payoneer/src/main/java/com/egzosn/pay/payoneer/api/PayoneerPayService.java index 29cdb28..c12e396 100644 --- a/pay-java-payoneer/src/main/java/com/egzosn/pay/payoneer/api/PayoneerPayService.java +++ b/pay-java-payoneer/src/main/java/com/egzosn/pay/payoneer/api/PayoneerPayService.java @@ -184,7 +184,7 @@ public class PayoneerPayService extends BasePayService im } params.put("currency", order.getCurType()); params.put("description", order.getSubject()); - params.putAll(order.getAttr()); + params.putAll(order.getAttrs()); return preOrderHandler(params, order); } diff --git a/pay-java-union/src/main/java/com/egzosn/pay/union/api/UnionPayService.java b/pay-java-union/src/main/java/com/egzosn/pay/union/api/UnionPayService.java index 2d33850..1f4770d 100644 --- a/pay-java-union/src/main/java/com/egzosn/pay/union/api/UnionPayService.java +++ b/pay-java-union/src/main/java/com/egzosn/pay/union/api/UnionPayService.java @@ -284,7 +284,7 @@ public class UnionPayService extends BasePayService { params.put(SDKConstants.param_payTimeout, getPayTimeout(order.getExpirationTime())); params.put("orderDesc", order.getSubject()); } - params.putAll(order.getAttr()); + params.putAll(order.getAttrs()); params = preOrderHandler(params, order); return setSign(params); } 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 6fefb92..8d4e387 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 @@ -227,7 +227,7 @@ public class WxYouDianPayService extends BasePayService data = new TreeMap<>(); data.put("access_token", getAccessToken()); data.put("paymoney", Util.conversionAmount(order.getPrice()).toString()); - data.putAll(order.getAttr()); + data.putAll(order.getAttrs()); data = preOrderHandler(data, order); String apbNonce = SignUtils.randomStr(); String sign = createSign(SignUtils.parameterText(data, "") + apbNonce, payConfigStorage.getInputCharset()); diff --git a/pay-java-wx/src/main/java/com/egzosn/pay/wx/api/WxConst.java b/pay-java-wx/src/main/java/com/egzosn/pay/wx/api/WxConst.java new file mode 100644 index 0000000..08994f3 --- /dev/null +++ b/pay-java-wx/src/main/java/com/egzosn/pay/wx/api/WxConst.java @@ -0,0 +1,42 @@ +package com.egzosn.pay.wx.api; + +import com.egzosn.pay.common.bean.Order; +import com.egzosn.pay.common.bean.PayOrder; + +import java.math.BigDecimal; +import java.util.HashMap; +import java.util.Map; + +/** + * @author egan + * @date 2020/3/10 21:22 + * 郑灶生 + *
+ * email: zheng.zaosheng@iwhalecloud.com + */ +public interface WxConst { + /** + * 微信请求地址 + */ + String URI = "https://api.mch.weixin.qq.com/"; + /** + * 沙箱 + */ + String SANDBOXNEW = "sandboxnew/"; + + String SUCCESS = "SUCCESS"; + String RETURN_CODE = "return_code"; + String SIGN = "sign"; + String CIPHER_ALGORITHM = "RSA/ECB/OAEPWITHSHA-1ANDMGF1PADDING"; + String FAILURE = "failure"; + String APPID = "appid"; + String HMAC_SHA256 = "HMAC-SHA256"; + String HMACSHA256 = "HMACSHA256"; + String RETURN_MSG_CODE = "return_msg"; + String RESULT_CODE = "result_code"; + String MCH_ID = "mch_id"; + String NONCE_STR = "nonce_str"; + String OUT_TRADE_NO = "out_trade_no"; + + +} 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 f4b67de..229a2b5 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 @@ -8,7 +8,6 @@ import com.egzosn.pay.common.bean.result.PayException; import com.egzosn.pay.common.exception.PayErrorException; import com.egzosn.pay.common.http.HttpConfigStorage; import com.egzosn.pay.common.util.DateUtils; -import com.egzosn.pay.common.util.MatrixToImageWriter; import com.egzosn.pay.common.util.Util; import com.egzosn.pay.common.util.sign.SignUtils; import com.egzosn.pay.common.util.sign.encrypt.RSA2; @@ -18,13 +17,13 @@ import com.egzosn.pay.wx.bean.WxPayMessage; import com.egzosn.pay.wx.bean.WxTransactionType; import com.egzosn.pay.common.util.XML; import com.egzosn.pay.wx.bean.WxTransferType; -import java.awt.image.BufferedImage; import java.io.IOException; import java.io.InputStream; import java.math.BigDecimal; import java.net.URLEncoder; import java.util.*; +import static com.egzosn.pay.wx.api.WxConst.*; import static com.egzosn.pay.wx.bean.WxTransferType.*; /** @@ -39,27 +38,6 @@ import static com.egzosn.pay.wx.bean.WxTransferType.*; public class WxPayService extends BasePayService { - /** - * 微信请求地址 - */ - public static final String URI = "https://api.mch.weixin.qq.com/"; - /** - * 沙箱 - */ - public static final String SANDBOXNEW = "sandboxnew/"; - - public static final String SUCCESS = "SUCCESS"; - public static final String RETURN_CODE = "return_code"; - public static final String SIGN = "sign"; - public static final String CIPHER_ALGORITHM = "RSA/ECB/OAEPWITHSHA-1ANDMGF1PADDING"; - public static final String FAILURE = "failure"; - public static final String APPID = "appid"; - private static final String HMAC_SHA256 = "HMAC-SHA256"; - private static final String HMACSHA256 = "HMACSHA256"; - private static final String RETURN_MSG_CODE = "return_msg"; - private static final String RESULT_CODE = "result_code"; - private static final String MCH_ID = "mch_id"; - private static final String NONCE_STR = "nonce_str"; /** @@ -118,17 +96,21 @@ public class WxPayService extends BasePayService { public boolean verify(Map params) { if (!(SUCCESS.equals(params.get(RETURN_CODE)) && SUCCESS.equals(params.get(RESULT_CODE)))) { - LOG.debug(String.format("微信支付异常:return_code=%s,参数集=%s", params.get(RETURN_CODE), params)); + if (LOG.isErrorEnabled()){ + LOG.error(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 (LOG.isDebugEnabled()){ + LOG.debug(String.format("微信支付异常:签名为空!%s=%s", 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")); + return signVerify(params, (String) params.get(SIGN)) && verifySource((String) params.get(OUT_TRADE_NO)); } catch (PayErrorException e) { LOG.error(e); } @@ -203,9 +185,9 @@ public class WxPayService extends BasePayService { // 购买支付信息 parameters.put("body", order.getSubject()); // 购买支付信息 -// parameters.put("detail", order.getBody()); + setParameters(parameters, "detail", order); // 订单号 - parameters.put("out_trade_no", order.getOutTradeNo()); + parameters.put(OUT_TRADE_NO, order.getOutTradeNo()); parameters.put("spbill_create_ip", StringUtils.isEmpty(order.getSpbillCreateIp()) ? "192.168.1.150" : order.getSpbillCreateIp()); // 总金额单位为分 parameters.put("total_fee", Util.conversionCentAmount(order.getPrice())); @@ -222,7 +204,12 @@ public class WxPayService extends BasePayService { } ((WxTransactionType) order.getTransactionType()).setAttribute(parameters, order); - parameters.putAll(order.getAttr()); + //可覆盖参数 + setParameters(parameters, "notify_url", order); + setParameters(parameters, "goods_tag", order); + setParameters(parameters, "limit_pay", order); + setParameters(parameters, "receipt", order); + setParameters(parameters, "product_id", order); parameters = preOrderHandler(parameters, order); setSign(parameters); @@ -276,7 +263,6 @@ public class WxPayService extends BasePayService { params.put("noncestr", result.get(NONCE_STR)); params.put("package", "Sign=WXPay"); } - params = preOrderHandler(params, order); String paySign = createSign(SignUtils.parameterText(params), payConfigStorage.getInputCharset()); params.put(SIGN, paySign); return params; @@ -513,12 +499,7 @@ public class WxPayService extends BasePayService { } - private Map setParameters(Map parameters, String key, String value) { - if (StringUtils.isNotEmpty(value)) { - parameters.put(key, value); - } - return parameters; - } + /** * 申请退款接口 @@ -532,7 +513,7 @@ public class WxPayService extends BasePayService { Map parameters = getPublicParameters(); setParameters(parameters, "transaction_id", refundOrder.getTradeNo()); - setParameters(parameters, "out_trade_no", refundOrder.getOutTradeNo()); + setParameters(parameters, OUT_TRADE_NO, refundOrder.getOutTradeNo()); setParameters(parameters, "out_refund_no", refundOrder.getRefundNo()); parameters.put("total_fee", Util.conversionCentAmount(refundOrder.getTotalAmount())); parameters.put("refund_fee", Util.conversionCentAmount(refundOrder.getRefundAmount())); @@ -571,7 +552,7 @@ public class WxPayService extends BasePayService { //获取公共参数 Map parameters = getPublicParameters(); setParameters(parameters, "transaction_id", refundOrder.getTradeNo()); - setParameters(parameters, "out_trade_no", refundOrder.getOutTradeNo()); + setParameters(parameters, OUT_TRADE_NO, refundOrder.getOutTradeNo()); setParameters(parameters, "out_refund_no", refundOrder.getRefundNo()); //设置签名 setSign(parameters); @@ -638,7 +619,7 @@ public class WxPayService extends BasePayService { //获取公共参数 Map parameters = getPublicParameters(); - setParameters(parameters, "out_trade_no", outTradeNoBillType); + setParameters(parameters, OUT_TRADE_NO, outTradeNoBillType); setParameters(parameters, "transaction_id", (String) transactionIdOrBillDate); //设置签名 setSign(parameters); diff --git a/pay-java-yiji/src/main/java/com/egzosn/pay/yiji/api/YiJiPayService.java b/pay-java-yiji/src/main/java/com/egzosn/pay/yiji/api/YiJiPayService.java index 1428b15..7bdfc2f 100644 --- a/pay-java-yiji/src/main/java/com/egzosn/pay/yiji/api/YiJiPayService.java +++ b/pay-java-yiji/src/main/java/com/egzosn/pay/yiji/api/YiJiPayService.java @@ -175,7 +175,7 @@ public class YiJiPayService extends BasePayService { if (null != order.getCurType()){ orderInfo.put("currency", order.getCurType()); } - orderInfo.putAll(order.getAttr()); + orderInfo.putAll(order.getAttrs()); return preOrderHandler(orderInfo, order); } -- Gitee From 45f6942a0c4c7671c790c2587614e45a2bb5b2c7 Mon Sep 17 00:00:00 2001 From: egzosn Date: Tue, 10 Mar 2020 22:08:15 +0800 Subject: [PATCH 101/299] =?UTF-8?q?=E4=BB=A3=E7=A0=81=E5=9B=9E=E6=BB=9A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../egzosn/pay/common/api/BasePayService.java | 16 ----- .../com/egzosn/pay/common/bean/Order.java | 8 +-- .../egzosn/pay/fuiou/api/FuiouPayService.java | 2 +- .../pay/payoneer/api/PayoneerPayService.java | 2 +- .../egzosn/pay/union/api/UnionPayService.java | 2 +- .../wx/youdian/api/WxYouDianPayService.java | 2 +- .../com/egzosn/pay/wx/api/WxPayService.java | 59 ++++++++++++------- .../egzosn/pay/yiji/api/YiJiPayService.java | 2 +- 8 files changed, 45 insertions(+), 48 deletions(-) diff --git a/pay-java-common/src/main/java/com/egzosn/pay/common/api/BasePayService.java b/pay-java-common/src/main/java/com/egzosn/pay/common/api/BasePayService.java index d2be6c3..c955e04 100644 --- a/pay-java-common/src/main/java/com/egzosn/pay/common/api/BasePayService.java +++ b/pay-java-common/src/main/java/com/egzosn/pay/common/api/BasePayService.java @@ -467,20 +467,4 @@ public abstract class BasePayService implements Pay return orderInfo; } - protected Map setParameters(Map parameters, String key, String value) { - if (StringUtils.isNotEmpty(value)) { - parameters.put(key, value); - } - return parameters; - } - - protected Map setParameters(Map parameters, String key, Order order) { - Object attr = order.getAttr(key); - if (null != attr && !"".equals(attr)) { - parameters.put(key, attr); - } - return parameters; - } - - } diff --git a/pay-java-common/src/main/java/com/egzosn/pay/common/bean/Order.java b/pay-java-common/src/main/java/com/egzosn/pay/common/bean/Order.java index 5f18b00..57046df 100644 --- a/pay-java-common/src/main/java/com/egzosn/pay/common/bean/Order.java +++ b/pay-java-common/src/main/java/com/egzosn/pay/common/bean/Order.java @@ -17,12 +17,6 @@ public interface Order { * 获取订单属性 这里可用做覆盖已设置的订单信息属性,订单信息在签名前进行覆盖。 * @return 属性 */ - Map getAttrs(); - /** - * 获取订单属性 这里可用做覆盖已设置的订单信息属性,订单信息在签名前进行覆盖。 - * @param key 属性名 - * @return 属性 - */ - Object getAttr(String key); + Map getAttr(); } diff --git a/pay-java-fuiou/src/main/java/com/egzosn/pay/fuiou/api/FuiouPayService.java b/pay-java-fuiou/src/main/java/com/egzosn/pay/fuiou/api/FuiouPayService.java index 69a28c0..8a3b4b0 100644 --- a/pay-java-fuiou/src/main/java/com/egzosn/pay/fuiou/api/FuiouPayService.java +++ b/pay-java-fuiou/src/main/java/com/egzosn/pay/fuiou/api/FuiouPayService.java @@ -203,7 +203,7 @@ public class FuiouPayService extends BasePayService { parameters.put("rem", ""); //版本号 parameters.put("ver", "1.0.1"); - parameters.putAll(order.getAttrs()); + parameters.putAll(order.getAttr()); return preOrderHandler(parameters, order); } diff --git a/pay-java-payoneer/src/main/java/com/egzosn/pay/payoneer/api/PayoneerPayService.java b/pay-java-payoneer/src/main/java/com/egzosn/pay/payoneer/api/PayoneerPayService.java index c12e396..29cdb28 100644 --- a/pay-java-payoneer/src/main/java/com/egzosn/pay/payoneer/api/PayoneerPayService.java +++ b/pay-java-payoneer/src/main/java/com/egzosn/pay/payoneer/api/PayoneerPayService.java @@ -184,7 +184,7 @@ public class PayoneerPayService extends BasePayService im } params.put("currency", order.getCurType()); params.put("description", order.getSubject()); - params.putAll(order.getAttrs()); + params.putAll(order.getAttr()); return preOrderHandler(params, order); } diff --git a/pay-java-union/src/main/java/com/egzosn/pay/union/api/UnionPayService.java b/pay-java-union/src/main/java/com/egzosn/pay/union/api/UnionPayService.java index 1f4770d..2d33850 100644 --- a/pay-java-union/src/main/java/com/egzosn/pay/union/api/UnionPayService.java +++ b/pay-java-union/src/main/java/com/egzosn/pay/union/api/UnionPayService.java @@ -284,7 +284,7 @@ public class UnionPayService extends BasePayService { params.put(SDKConstants.param_payTimeout, getPayTimeout(order.getExpirationTime())); params.put("orderDesc", order.getSubject()); } - params.putAll(order.getAttrs()); + params.putAll(order.getAttr()); params = preOrderHandler(params, order); return setSign(params); } 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 8d4e387..6fefb92 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 @@ -227,7 +227,7 @@ public class WxYouDianPayService extends BasePayService data = new TreeMap<>(); data.put("access_token", getAccessToken()); data.put("paymoney", Util.conversionAmount(order.getPrice()).toString()); - data.putAll(order.getAttrs()); + data.putAll(order.getAttr()); data = preOrderHandler(data, order); String apbNonce = SignUtils.randomStr(); String sign = createSign(SignUtils.parameterText(data, "") + apbNonce, payConfigStorage.getInputCharset()); 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 229a2b5..f4b67de 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 @@ -8,6 +8,7 @@ import com.egzosn.pay.common.bean.result.PayException; import com.egzosn.pay.common.exception.PayErrorException; import com.egzosn.pay.common.http.HttpConfigStorage; import com.egzosn.pay.common.util.DateUtils; +import com.egzosn.pay.common.util.MatrixToImageWriter; import com.egzosn.pay.common.util.Util; import com.egzosn.pay.common.util.sign.SignUtils; import com.egzosn.pay.common.util.sign.encrypt.RSA2; @@ -17,13 +18,13 @@ import com.egzosn.pay.wx.bean.WxPayMessage; import com.egzosn.pay.wx.bean.WxTransactionType; import com.egzosn.pay.common.util.XML; import com.egzosn.pay.wx.bean.WxTransferType; +import java.awt.image.BufferedImage; import java.io.IOException; import java.io.InputStream; import java.math.BigDecimal; import java.net.URLEncoder; import java.util.*; -import static com.egzosn.pay.wx.api.WxConst.*; import static com.egzosn.pay.wx.bean.WxTransferType.*; /** @@ -38,6 +39,27 @@ import static com.egzosn.pay.wx.bean.WxTransferType.*; public class WxPayService extends BasePayService { + /** + * 微信请求地址 + */ + public static final String URI = "https://api.mch.weixin.qq.com/"; + /** + * 沙箱 + */ + public static final String SANDBOXNEW = "sandboxnew/"; + + public static final String SUCCESS = "SUCCESS"; + public static final String RETURN_CODE = "return_code"; + public static final String SIGN = "sign"; + public static final String CIPHER_ALGORITHM = "RSA/ECB/OAEPWITHSHA-1ANDMGF1PADDING"; + public static final String FAILURE = "failure"; + public static final String APPID = "appid"; + private static final String HMAC_SHA256 = "HMAC-SHA256"; + private static final String HMACSHA256 = "HMACSHA256"; + private static final String RETURN_MSG_CODE = "return_msg"; + private static final String RESULT_CODE = "result_code"; + private static final String MCH_ID = "mch_id"; + private static final String NONCE_STR = "nonce_str"; /** @@ -96,21 +118,17 @@ public class WxPayService extends BasePayService { public boolean verify(Map params) { if (!(SUCCESS.equals(params.get(RETURN_CODE)) && SUCCESS.equals(params.get(RESULT_CODE)))) { - if (LOG.isErrorEnabled()){ - LOG.error(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 (null == params.get(SIGN)) { - if (LOG.isDebugEnabled()){ - LOG.debug(String.format("微信支付异常:签名为空!%s=%s", OUT_TRADE_NO, params.get(OUT_TRADE_NO))); - } + 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)); + return signVerify(params, (String) params.get(SIGN)) && verifySource((String) params.get("out_trade_no")); } catch (PayErrorException e) { LOG.error(e); } @@ -185,9 +203,9 @@ public class WxPayService extends BasePayService { // 购买支付信息 parameters.put("body", order.getSubject()); // 购买支付信息 - setParameters(parameters, "detail", order); +// parameters.put("detail", order.getBody()); // 订单号 - parameters.put(OUT_TRADE_NO, order.getOutTradeNo()); + parameters.put("out_trade_no", order.getOutTradeNo()); parameters.put("spbill_create_ip", StringUtils.isEmpty(order.getSpbillCreateIp()) ? "192.168.1.150" : order.getSpbillCreateIp()); // 总金额单位为分 parameters.put("total_fee", Util.conversionCentAmount(order.getPrice())); @@ -204,12 +222,7 @@ public class WxPayService extends BasePayService { } ((WxTransactionType) order.getTransactionType()).setAttribute(parameters, order); - //可覆盖参数 - setParameters(parameters, "notify_url", order); - setParameters(parameters, "goods_tag", order); - setParameters(parameters, "limit_pay", order); - setParameters(parameters, "receipt", order); - setParameters(parameters, "product_id", order); + parameters.putAll(order.getAttr()); parameters = preOrderHandler(parameters, order); setSign(parameters); @@ -263,6 +276,7 @@ public class WxPayService extends BasePayService { params.put("noncestr", result.get(NONCE_STR)); params.put("package", "Sign=WXPay"); } + params = preOrderHandler(params, order); String paySign = createSign(SignUtils.parameterText(params), payConfigStorage.getInputCharset()); params.put(SIGN, paySign); return params; @@ -499,7 +513,12 @@ public class WxPayService extends BasePayService { } - + private Map setParameters(Map parameters, String key, String value) { + if (StringUtils.isNotEmpty(value)) { + parameters.put(key, value); + } + return parameters; + } /** * 申请退款接口 @@ -513,7 +532,7 @@ public class WxPayService extends BasePayService { Map parameters = getPublicParameters(); setParameters(parameters, "transaction_id", refundOrder.getTradeNo()); - setParameters(parameters, OUT_TRADE_NO, refundOrder.getOutTradeNo()); + setParameters(parameters, "out_trade_no", refundOrder.getOutTradeNo()); setParameters(parameters, "out_refund_no", refundOrder.getRefundNo()); parameters.put("total_fee", Util.conversionCentAmount(refundOrder.getTotalAmount())); parameters.put("refund_fee", Util.conversionCentAmount(refundOrder.getRefundAmount())); @@ -552,7 +571,7 @@ public class WxPayService extends BasePayService { //获取公共参数 Map parameters = getPublicParameters(); setParameters(parameters, "transaction_id", refundOrder.getTradeNo()); - setParameters(parameters, OUT_TRADE_NO, refundOrder.getOutTradeNo()); + setParameters(parameters, "out_trade_no", refundOrder.getOutTradeNo()); setParameters(parameters, "out_refund_no", refundOrder.getRefundNo()); //设置签名 setSign(parameters); @@ -619,7 +638,7 @@ public class WxPayService extends BasePayService { //获取公共参数 Map parameters = getPublicParameters(); - setParameters(parameters, OUT_TRADE_NO, outTradeNoBillType); + setParameters(parameters, "out_trade_no", outTradeNoBillType); setParameters(parameters, "transaction_id", (String) transactionIdOrBillDate); //设置签名 setSign(parameters); diff --git a/pay-java-yiji/src/main/java/com/egzosn/pay/yiji/api/YiJiPayService.java b/pay-java-yiji/src/main/java/com/egzosn/pay/yiji/api/YiJiPayService.java index 7bdfc2f..1428b15 100644 --- a/pay-java-yiji/src/main/java/com/egzosn/pay/yiji/api/YiJiPayService.java +++ b/pay-java-yiji/src/main/java/com/egzosn/pay/yiji/api/YiJiPayService.java @@ -175,7 +175,7 @@ public class YiJiPayService extends BasePayService { if (null != order.getCurType()){ orderInfo.put("currency", order.getCurType()); } - orderInfo.putAll(order.getAttrs()); + orderInfo.putAll(order.getAttr()); return preOrderHandler(orderInfo, order); } -- Gitee From 0f1255fe06a846384decbb9563637aa1b552e2f1 Mon Sep 17 00:00:00 2001 From: egzosn Date: Tue, 10 Mar 2020 22:41:02 +0800 Subject: [PATCH 102/299] =?UTF-8?q?=E8=87=AA=E5=AE=9A=E4=B9=89=E5=8F=82?= =?UTF-8?q?=E6=95=B0=E4=BD=BF=E7=94=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/egzosn/pay/ali/api/AliPayService.java | 2 +- .../egzosn/pay/baidu/api/BaiduPayService.java | 19 +-- .../egzosn/pay/baidu/bean/BaiduPayOrder.java | 6 + .../egzosn/pay/common/api/BasePayService.java | 16 ++ .../com/egzosn/pay/common/bean/Order.java | 8 +- .../com/egzosn/pay/common/bean/PayOrder.java | 10 +- .../egzosn/pay/common/bean/TransferOrder.java | 34 ++++- pay-java-demo/pom.xml | 34 +---- .../egzosn/pay/fuiou/api/FuiouPayService.java | 2 +- .../pay/payoneer/api/PayoneerPayService.java | 3 +- .../egzosn/pay/union/api/UnionPayService.java | 3 +- .../wx/youdian/api/WxYouDianPayService.java | 2 +- .../java/com/egzosn/pay/wx/api/WxConst.java | 42 ++++++ .../com/egzosn/pay/wx/api/WxPayService.java | 59 +++----- .../egzosn/pay/yiji/api/YiJiPayService.java | 4 +- pom.xml | 141 ++++++++++-------- 16 files changed, 228 insertions(+), 157 deletions(-) create mode 100644 pay-java-wx/src/main/java/com/egzosn/pay/wx/api/WxConst.java 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 b9685c8..06cd7b5 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 @@ -226,7 +226,7 @@ public class AliPayService extends BasePayService { bizContent.put("timeout_express", DateUtils.minutesRemaining(order.getExpirationTime()) + "m"); } orderInfo.put(BIZ_CONTENT, JSON.toJSONString(bizContent)); - orderInfo.putAll(order.getAttr()); + orderInfo.putAll(order.getAttrs()); return preOrderHandler(orderInfo, order); } diff --git a/pay-java-baidu/src/main/java/com/egzosn/pay/baidu/api/BaiduPayService.java b/pay-java-baidu/src/main/java/com/egzosn/pay/baidu/api/BaiduPayService.java index 2d66ff7..d2ee285 100644 --- a/pay-java-baidu/src/main/java/com/egzosn/pay/baidu/api/BaiduPayService.java +++ b/pay-java-baidu/src/main/java/com/egzosn/pay/baidu/api/BaiduPayService.java @@ -12,6 +12,7 @@ import com.egzosn.pay.common.bean.*; import com.egzosn.pay.common.http.HttpConfigStorage; import com.egzosn.pay.common.http.UriVariables; import com.egzosn.pay.common.util.DateUtils; +import com.egzosn.pay.common.util.Util; import com.egzosn.pay.common.util.sign.SignUtils; import com.egzosn.pay.common.util.str.StringUtils; @@ -21,7 +22,7 @@ import java.util.HashMap; import java.util.Map; -public class BaiduPayService extends BasePayService { +public class BaiduPayService extends BasePayService { public static final String APP_KEY = "appKey"; public static final String APP_ID = "appId"; public static final String DEAL_ID = "dealId"; @@ -84,7 +85,7 @@ public class BaiduPayService extends BasePayService orderInfo(BaiduPayOrder order) { + public Map orderInfo(PayOrder order) { Map params = getUseOrderInfoParams(order); String rsaSign = getRsaSign(params, RSA_SIGN); params.put(RSA_SIGN, rsaSign); @@ -129,7 +130,7 @@ public class BaiduPayService extends BasePayService microPay(BaiduPayOrder order) { + public Map microPay(PayOrder order) { throw new UnsupportedOperationException("百度不支持刷卡付"); } diff --git a/pay-java-baidu/src/main/java/com/egzosn/pay/baidu/bean/BaiduPayOrder.java b/pay-java-baidu/src/main/java/com/egzosn/pay/baidu/bean/BaiduPayOrder.java index 58e5417..a0b5ffd 100644 --- a/pay-java-baidu/src/main/java/com/egzosn/pay/baidu/bean/BaiduPayOrder.java +++ b/pay-java-baidu/src/main/java/com/egzosn/pay/baidu/bean/BaiduPayOrder.java @@ -8,6 +8,9 @@ import java.math.BigDecimal; import java.util.Collections; import java.util.List; +import static com.egzosn.pay.baidu.api.BaiduPayService.BIZ_INFO; +import static com.egzosn.pay.baidu.api.BaiduPayService.SIGN_FIELDS_RANGE; + public class BaiduPayOrder extends PayOrder { /** @@ -50,6 +53,7 @@ public class BaiduPayOrder extends PayOrder { public void setBizInfo(JSONObject bizInfo) { this.bizInfo = bizInfo; + addAttr(BIZ_INFO, bizInfo); } public List getBannedChannels() { @@ -58,6 +62,7 @@ public class BaiduPayOrder extends PayOrder { public void setBannedChannels(List bannedChannels) { this.bannedChannels = bannedChannels; + addAttr("bannedChannels", bannedChannels); } public String getSignFieldsRange() { @@ -66,6 +71,7 @@ public class BaiduPayOrder extends PayOrder { public void setSignFieldsRange(String signFieldsRange) { this.signFieldsRange = signFieldsRange; + addAttr(SIGN_FIELDS_RANGE, signFieldsRange); } } diff --git a/pay-java-common/src/main/java/com/egzosn/pay/common/api/BasePayService.java b/pay-java-common/src/main/java/com/egzosn/pay/common/api/BasePayService.java index c955e04..d2be6c3 100644 --- a/pay-java-common/src/main/java/com/egzosn/pay/common/api/BasePayService.java +++ b/pay-java-common/src/main/java/com/egzosn/pay/common/api/BasePayService.java @@ -467,4 +467,20 @@ public abstract class BasePayService implements Pay return orderInfo; } + protected Map setParameters(Map parameters, String key, String value) { + if (StringUtils.isNotEmpty(value)) { + parameters.put(key, value); + } + return parameters; + } + + protected Map setParameters(Map parameters, String key, Order order) { + Object attr = order.getAttr(key); + if (null != attr && !"".equals(attr)) { + parameters.put(key, attr); + } + return parameters; + } + + } diff --git a/pay-java-common/src/main/java/com/egzosn/pay/common/bean/Order.java b/pay-java-common/src/main/java/com/egzosn/pay/common/bean/Order.java index 57046df..5f18b00 100644 --- a/pay-java-common/src/main/java/com/egzosn/pay/common/bean/Order.java +++ b/pay-java-common/src/main/java/com/egzosn/pay/common/bean/Order.java @@ -17,6 +17,12 @@ public interface Order { * 获取订单属性 这里可用做覆盖已设置的订单信息属性,订单信息在签名前进行覆盖。 * @return 属性 */ - Map getAttr(); + Map getAttrs(); + /** + * 获取订单属性 这里可用做覆盖已设置的订单信息属性,订单信息在签名前进行覆盖。 + * @param key 属性名 + * @return 属性 + */ + Object getAttr(String key); } 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 57a6842..dd550be 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 @@ -258,20 +258,26 @@ public class PayOrder implements Order { } @Override - public Map getAttr() { + public Map getAttrs() { if (null == attr){ attr = new HashMap<>(); } return attr; } + @Override + public Object getAttr(String key) { + return getAttrs().get(key); + } + + /** * 添加订单信息 * @param key key * @param value 值 */ public void addAttr(String key, Object value) { - getAttr().put(key, value); + getAttrs().put(key, value); } diff --git a/pay-java-common/src/main/java/com/egzosn/pay/common/bean/TransferOrder.java b/pay-java-common/src/main/java/com/egzosn/pay/common/bean/TransferOrder.java index 2b89117..83f05b8 100644 --- a/pay-java-common/src/main/java/com/egzosn/pay/common/bean/TransferOrder.java +++ b/pay-java-common/src/main/java/com/egzosn/pay/common/bean/TransferOrder.java @@ -1,6 +1,8 @@ package com.egzosn.pay.common.bean; import java.math.BigDecimal; +import java.util.HashMap; +import java.util.Map; /** * 转账订单 @@ -10,7 +12,7 @@ import java.math.BigDecimal; * date 2018/1/31 * */ -public class TransferOrder { +public class TransferOrder implements Order { /** * 转账批次订单单号 @@ -79,6 +81,12 @@ public class TransferOrder { */ private String ip; + + /** + * 订单附加信息,可用于预设未提供的参数,这里会覆盖以上所有的订单信息, + */ + private Map attr; + public String getBatchNo() { return batchNo; } @@ -190,4 +198,28 @@ public class TransferOrder { public void setIp(String ip) { this.ip = ip; } + @Override + public Map getAttrs() { + if (null == attr){ + attr = new HashMap<>(); + } + return attr; + } + + @Override + public Object getAttr(String key) { + return getAttrs().get(key); + } + + + /** + * 添加订单信息 + * @param key key + * @param value 值 + */ + public void addAttr(String key, Object value) { + getAttrs().put(key, value); + } + + } diff --git a/pay-java-demo/pom.xml b/pay-java-demo/pom.xml index 896b184..e7599d0 100644 --- a/pay-java-demo/pom.xml +++ b/pay-java-demo/pom.xml @@ -90,39 +90,7 @@
- - - port8080 - - 8080 - - - true - - - - port9096 - - 9096 - - - - local - - local - - - true - - - - proc - - proc - - - pay-java-demo @@ -140,7 +108,7 @@ tomcat7-maven-plugin 2.0 - ${port} + 8080 / UTF-8 diff --git a/pay-java-fuiou/src/main/java/com/egzosn/pay/fuiou/api/FuiouPayService.java b/pay-java-fuiou/src/main/java/com/egzosn/pay/fuiou/api/FuiouPayService.java index 8a3b4b0..69a28c0 100644 --- a/pay-java-fuiou/src/main/java/com/egzosn/pay/fuiou/api/FuiouPayService.java +++ b/pay-java-fuiou/src/main/java/com/egzosn/pay/fuiou/api/FuiouPayService.java @@ -203,7 +203,7 @@ public class FuiouPayService extends BasePayService { parameters.put("rem", ""); //版本号 parameters.put("ver", "1.0.1"); - parameters.putAll(order.getAttr()); + parameters.putAll(order.getAttrs()); return preOrderHandler(parameters, order); } diff --git a/pay-java-payoneer/src/main/java/com/egzosn/pay/payoneer/api/PayoneerPayService.java b/pay-java-payoneer/src/main/java/com/egzosn/pay/payoneer/api/PayoneerPayService.java index 29cdb28..80360b8 100644 --- a/pay-java-payoneer/src/main/java/com/egzosn/pay/payoneer/api/PayoneerPayService.java +++ b/pay-java-payoneer/src/main/java/com/egzosn/pay/payoneer/api/PayoneerPayService.java @@ -18,7 +18,6 @@ import org.apache.http.Header; import org.apache.http.entity.ContentType; import org.apache.http.message.BasicHeader; -import java.awt.image.BufferedImage; import java.math.BigDecimal; import java.util.*; @@ -184,7 +183,7 @@ public class PayoneerPayService extends BasePayService im } params.put("currency", order.getCurType()); params.put("description", order.getSubject()); - params.putAll(order.getAttr()); + params.putAll(order.getAttrs()); return preOrderHandler(params, order); } diff --git a/pay-java-union/src/main/java/com/egzosn/pay/union/api/UnionPayService.java b/pay-java-union/src/main/java/com/egzosn/pay/union/api/UnionPayService.java index 2d33850..50dbcf8 100644 --- a/pay-java-union/src/main/java/com/egzosn/pay/union/api/UnionPayService.java +++ b/pay-java-union/src/main/java/com/egzosn/pay/union/api/UnionPayService.java @@ -20,7 +20,6 @@ import com.egzosn.pay.union.bean.SDKConstants; import com.egzosn.pay.union.bean.UnionPayMessage; import com.egzosn.pay.union.bean.UnionTransactionType; -import java.awt.image.BufferedImage; import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; @@ -284,7 +283,7 @@ public class UnionPayService extends BasePayService { params.put(SDKConstants.param_payTimeout, getPayTimeout(order.getExpirationTime())); params.put("orderDesc", order.getSubject()); } - params.putAll(order.getAttr()); + params.putAll(order.getAttrs()); params = preOrderHandler(params, order); return setSign(params); } 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 6fefb92..8d4e387 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 @@ -227,7 +227,7 @@ public class WxYouDianPayService extends BasePayService data = new TreeMap<>(); data.put("access_token", getAccessToken()); data.put("paymoney", Util.conversionAmount(order.getPrice()).toString()); - data.putAll(order.getAttr()); + data.putAll(order.getAttrs()); data = preOrderHandler(data, order); String apbNonce = SignUtils.randomStr(); String sign = createSign(SignUtils.parameterText(data, "") + apbNonce, payConfigStorage.getInputCharset()); diff --git a/pay-java-wx/src/main/java/com/egzosn/pay/wx/api/WxConst.java b/pay-java-wx/src/main/java/com/egzosn/pay/wx/api/WxConst.java new file mode 100644 index 0000000..08994f3 --- /dev/null +++ b/pay-java-wx/src/main/java/com/egzosn/pay/wx/api/WxConst.java @@ -0,0 +1,42 @@ +package com.egzosn.pay.wx.api; + +import com.egzosn.pay.common.bean.Order; +import com.egzosn.pay.common.bean.PayOrder; + +import java.math.BigDecimal; +import java.util.HashMap; +import java.util.Map; + +/** + * @author egan + * @date 2020/3/10 21:22 + * 郑灶生 + *
+ * email: zheng.zaosheng@iwhalecloud.com + */ +public interface WxConst { + /** + * 微信请求地址 + */ + String URI = "https://api.mch.weixin.qq.com/"; + /** + * 沙箱 + */ + String SANDBOXNEW = "sandboxnew/"; + + String SUCCESS = "SUCCESS"; + String RETURN_CODE = "return_code"; + String SIGN = "sign"; + String CIPHER_ALGORITHM = "RSA/ECB/OAEPWITHSHA-1ANDMGF1PADDING"; + String FAILURE = "failure"; + String APPID = "appid"; + String HMAC_SHA256 = "HMAC-SHA256"; + String HMACSHA256 = "HMACSHA256"; + String RETURN_MSG_CODE = "return_msg"; + String RESULT_CODE = "result_code"; + String MCH_ID = "mch_id"; + String NONCE_STR = "nonce_str"; + String OUT_TRADE_NO = "out_trade_no"; + + +} 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 f4b67de..229a2b5 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 @@ -8,7 +8,6 @@ import com.egzosn.pay.common.bean.result.PayException; import com.egzosn.pay.common.exception.PayErrorException; import com.egzosn.pay.common.http.HttpConfigStorage; import com.egzosn.pay.common.util.DateUtils; -import com.egzosn.pay.common.util.MatrixToImageWriter; import com.egzosn.pay.common.util.Util; import com.egzosn.pay.common.util.sign.SignUtils; import com.egzosn.pay.common.util.sign.encrypt.RSA2; @@ -18,13 +17,13 @@ import com.egzosn.pay.wx.bean.WxPayMessage; import com.egzosn.pay.wx.bean.WxTransactionType; import com.egzosn.pay.common.util.XML; import com.egzosn.pay.wx.bean.WxTransferType; -import java.awt.image.BufferedImage; import java.io.IOException; import java.io.InputStream; import java.math.BigDecimal; import java.net.URLEncoder; import java.util.*; +import static com.egzosn.pay.wx.api.WxConst.*; import static com.egzosn.pay.wx.bean.WxTransferType.*; /** @@ -39,27 +38,6 @@ import static com.egzosn.pay.wx.bean.WxTransferType.*; public class WxPayService extends BasePayService { - /** - * 微信请求地址 - */ - public static final String URI = "https://api.mch.weixin.qq.com/"; - /** - * 沙箱 - */ - public static final String SANDBOXNEW = "sandboxnew/"; - - public static final String SUCCESS = "SUCCESS"; - public static final String RETURN_CODE = "return_code"; - public static final String SIGN = "sign"; - public static final String CIPHER_ALGORITHM = "RSA/ECB/OAEPWITHSHA-1ANDMGF1PADDING"; - public static final String FAILURE = "failure"; - public static final String APPID = "appid"; - private static final String HMAC_SHA256 = "HMAC-SHA256"; - private static final String HMACSHA256 = "HMACSHA256"; - private static final String RETURN_MSG_CODE = "return_msg"; - private static final String RESULT_CODE = "result_code"; - private static final String MCH_ID = "mch_id"; - private static final String NONCE_STR = "nonce_str"; /** @@ -118,17 +96,21 @@ public class WxPayService extends BasePayService { public boolean verify(Map params) { if (!(SUCCESS.equals(params.get(RETURN_CODE)) && SUCCESS.equals(params.get(RESULT_CODE)))) { - LOG.debug(String.format("微信支付异常:return_code=%s,参数集=%s", params.get(RETURN_CODE), params)); + if (LOG.isErrorEnabled()){ + LOG.error(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 (LOG.isDebugEnabled()){ + LOG.debug(String.format("微信支付异常:签名为空!%s=%s", 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")); + return signVerify(params, (String) params.get(SIGN)) && verifySource((String) params.get(OUT_TRADE_NO)); } catch (PayErrorException e) { LOG.error(e); } @@ -203,9 +185,9 @@ public class WxPayService extends BasePayService { // 购买支付信息 parameters.put("body", order.getSubject()); // 购买支付信息 -// parameters.put("detail", order.getBody()); + setParameters(parameters, "detail", order); // 订单号 - parameters.put("out_trade_no", order.getOutTradeNo()); + parameters.put(OUT_TRADE_NO, order.getOutTradeNo()); parameters.put("spbill_create_ip", StringUtils.isEmpty(order.getSpbillCreateIp()) ? "192.168.1.150" : order.getSpbillCreateIp()); // 总金额单位为分 parameters.put("total_fee", Util.conversionCentAmount(order.getPrice())); @@ -222,7 +204,12 @@ public class WxPayService extends BasePayService { } ((WxTransactionType) order.getTransactionType()).setAttribute(parameters, order); - parameters.putAll(order.getAttr()); + //可覆盖参数 + setParameters(parameters, "notify_url", order); + setParameters(parameters, "goods_tag", order); + setParameters(parameters, "limit_pay", order); + setParameters(parameters, "receipt", order); + setParameters(parameters, "product_id", order); parameters = preOrderHandler(parameters, order); setSign(parameters); @@ -276,7 +263,6 @@ public class WxPayService extends BasePayService { params.put("noncestr", result.get(NONCE_STR)); params.put("package", "Sign=WXPay"); } - params = preOrderHandler(params, order); String paySign = createSign(SignUtils.parameterText(params), payConfigStorage.getInputCharset()); params.put(SIGN, paySign); return params; @@ -513,12 +499,7 @@ public class WxPayService extends BasePayService { } - private Map setParameters(Map parameters, String key, String value) { - if (StringUtils.isNotEmpty(value)) { - parameters.put(key, value); - } - return parameters; - } + /** * 申请退款接口 @@ -532,7 +513,7 @@ public class WxPayService extends BasePayService { Map parameters = getPublicParameters(); setParameters(parameters, "transaction_id", refundOrder.getTradeNo()); - setParameters(parameters, "out_trade_no", refundOrder.getOutTradeNo()); + setParameters(parameters, OUT_TRADE_NO, refundOrder.getOutTradeNo()); setParameters(parameters, "out_refund_no", refundOrder.getRefundNo()); parameters.put("total_fee", Util.conversionCentAmount(refundOrder.getTotalAmount())); parameters.put("refund_fee", Util.conversionCentAmount(refundOrder.getRefundAmount())); @@ -571,7 +552,7 @@ public class WxPayService extends BasePayService { //获取公共参数 Map parameters = getPublicParameters(); setParameters(parameters, "transaction_id", refundOrder.getTradeNo()); - setParameters(parameters, "out_trade_no", refundOrder.getOutTradeNo()); + setParameters(parameters, OUT_TRADE_NO, refundOrder.getOutTradeNo()); setParameters(parameters, "out_refund_no", refundOrder.getRefundNo()); //设置签名 setSign(parameters); @@ -638,7 +619,7 @@ public class WxPayService extends BasePayService { //获取公共参数 Map parameters = getPublicParameters(); - setParameters(parameters, "out_trade_no", outTradeNoBillType); + setParameters(parameters, OUT_TRADE_NO, outTradeNoBillType); setParameters(parameters, "transaction_id", (String) transactionIdOrBillDate); //设置签名 setSign(parameters); diff --git a/pay-java-yiji/src/main/java/com/egzosn/pay/yiji/api/YiJiPayService.java b/pay-java-yiji/src/main/java/com/egzosn/pay/yiji/api/YiJiPayService.java index 1428b15..afd5d43 100644 --- a/pay-java-yiji/src/main/java/com/egzosn/pay/yiji/api/YiJiPayService.java +++ b/pay-java-yiji/src/main/java/com/egzosn/pay/yiji/api/YiJiPayService.java @@ -10,8 +10,6 @@ import com.egzosn.pay.common.util.Util; import com.egzosn.pay.common.util.sign.SignUtils; import com.egzosn.pay.common.util.str.StringUtils; import com.egzosn.pay.yiji.bean.YiJiTransactionType; - -import java.awt.image.BufferedImage; import java.math.BigDecimal; import java.util.Collections; import java.util.Date; @@ -175,7 +173,7 @@ public class YiJiPayService extends BasePayService { if (null != order.getCurType()){ orderInfo.put("currency", order.getCurType()); } - orderInfo.putAll(order.getAttr()); + orderInfo.putAll(order.getAttrs()); return preOrderHandler(orderInfo, order); } diff --git a/pom.xml b/pom.xml index 4f03d79..f2ecd1f 100644 --- a/pom.xml +++ b/pom.xml @@ -140,68 +140,85 @@ utf-8 - - org.sonatype.plugins - nexus-staging-maven-plugin - 1.6.3 - true - - ossrh - https://oss.sonatype.org/ - false - - - - org.apache.maven.plugins - maven-release-plugin - 2.5.1 - - false - false - release - deploy - - - - org.apache.maven.plugins - maven-javadoc-plugin - 2.10.3 - - - attach-javadocs - - jar - - - - - - org.apache.maven.plugins - maven-source-plugin - 3.1.0 - - - attach-sources - - jar-no-fork - - - - - - org.apache.maven.plugins - maven-gpg-plugin - 1.6 - - - sign-artifacts - none - - - - +
+ + + + local + + true + + + + proc + + + + org.sonatype.plugins + nexus-staging-maven-plugin + 1.6.3 + true + + ossrh + https://oss.sonatype.org/ + false + + + + org.apache.maven.plugins + maven-release-plugin + 2.5.1 + + false + false + release + deploy + + + + org.apache.maven.plugins + maven-javadoc-plugin + 2.10.3 + + + attach-javadocs + + jar + + + + + + org.apache.maven.plugins + maven-source-plugin + 3.1.0 + + + attach-sources + + jar-no-fork + + + + + + org.apache.maven.plugins + maven-gpg-plugin + 1.6 + + + sign-artifacts + install + + sign + + + + + + + + \ No newline at end of file -- Gitee From f05f09c3ea01be8c402276414f2829c14daead42 Mon Sep 17 00:00:00 2001 From: egzosn Date: Tue, 10 Mar 2020 22:42:20 +0800 Subject: [PATCH 103/299] =?UTF-8?q?=E8=87=AA=E5=AE=9A=E4=B9=89=E5=8F=82?= =?UTF-8?q?=E6=95=B0=E4=BD=BF=E7=94=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/main/java/com/egzosn/pay/baidu/bean/BaiduPayOrder.java | 1 - 1 file changed, 1 deletion(-) diff --git a/pay-java-baidu/src/main/java/com/egzosn/pay/baidu/bean/BaiduPayOrder.java b/pay-java-baidu/src/main/java/com/egzosn/pay/baidu/bean/BaiduPayOrder.java index a0b5ffd..2f82452 100644 --- a/pay-java-baidu/src/main/java/com/egzosn/pay/baidu/bean/BaiduPayOrder.java +++ b/pay-java-baidu/src/main/java/com/egzosn/pay/baidu/bean/BaiduPayOrder.java @@ -62,7 +62,6 @@ public class BaiduPayOrder extends PayOrder { public void setBannedChannels(List bannedChannels) { this.bannedChannels = bannedChannels; - addAttr("bannedChannels", bannedChannels); } public String getSignFieldsRange() { -- Gitee From 7468e29c3cb7f32eae2d47c7ca451a78fbb7acd6 Mon Sep 17 00:00:00 2001 From: egzosn Date: Tue, 10 Mar 2020 22:47:34 +0800 Subject: [PATCH 104/299] =?UTF-8?q?=E8=87=AA=E5=AE=9A=E4=B9=89=E8=AE=A2?= =?UTF-8?q?=E5=8D=95=E5=8F=82=E6=95=B0=E4=BD=BF=E7=94=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pay-java-demo/src/main/webapp/index.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pay-java-demo/src/main/webapp/index.html b/pay-java-demo/src/main/webapp/index.html index 03b359c..6aed160 100644 --- a/pay-java-demo/src/main/webapp/index.html +++ b/pay-java-demo/src/main/webapp/index.html @@ -69,12 +69,12 @@
-
各个支付对应的交易类型可自行查看对应的官方文档,本项目已实现几种交易类型,对应各个支付类型的com.egzosn.pay.common.bean.TransactionType具体实现
新版支付宝(com.egzosn.pay.ali.bean.AliTransactionType): 即时付款=PAGE , app支付=APP , 手机网站支付=WAP , 扫码付=SWEEPPAY, 条码付=BAR_CODE, 声波付=WAVE_CODE
微信(com.egzosn.pay.wx.bean.WxTransactionType): 公众号支付=JSAPI , 移动支付=APP , 扫码付=NATIVE
银联(com.egzosn.pay.union.bean.UnionTransactionType):苹果支付=APPLE,手机控件=APP,WAP支付=WAP,网关支付=WEB,无跳转支付=NO_JUMP,B2B支付=B2B,申码(主扫场景)=APPLY_QR_CODE,消费(被扫场景)=CONSUME
友店微信(com.egzosn.pay.wx.youdian.bean.YoudianTransactionType): 扫码付=NATIVE
富友(com.egzosn.pay.fuiou.bean.FuiouTransactionType): B2B,B2C
+
详情请查看 com.egzosn.pay.common.bean.TransactionType对应的子类


-- Gitee From cfe4d72e50aae1392a79054f6c04348d2a3f1001 Mon Sep 17 00:00:00 2001 From: egzosn Date: Sat, 25 Apr 2020 16:08:14 +0800 Subject: [PATCH 105/299] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E6=94=AF=E4=BB=98?= =?UTF-8?q?=E8=AE=A2=E5=8D=95=E8=BF=87=E6=9C=9F=E6=97=B6=E9=97=B4=E4=B8=8D?= =?UTF-8?q?=E7=B2=BE=E5=87=86=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/egzosn/pay/common/util/DateUtils.java | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/pay-java-common/src/main/java/com/egzosn/pay/common/util/DateUtils.java b/pay-java-common/src/main/java/com/egzosn/pay/common/util/DateUtils.java index b101b44..f8141d9 100644 --- a/pay-java-common/src/main/java/com/egzosn/pay/common/util/DateUtils.java +++ b/pay-java-common/src/main/java/com/egzosn/pay/common/util/DateUtils.java @@ -13,7 +13,7 @@ import java.util.*; * 日期转换运算工具 * * @author egan - *
+ * 
  *         email egzosn@gmail.com
  *         date 2018-11-21 16:43:20
  *         
@@ -23,6 +23,7 @@ public final class DateUtils { } private static final Log LOG = LogFactory.getLog(DateUtils.class); + static final class DateFormatHolder { private static final ThreadLocal>> THREADLOCAL_FORMATS = new ThreadLocal>>(); @@ -37,7 +38,7 @@ public final class DateUtils { THREADLOCAL_FORMATS.set(new SoftReference(formats)); } - SimpleDateFormat format = formats.get(pattern); + SimpleDateFormat format = formats.get(pattern); if (format == null) { format = new SimpleDateFormat(pattern); @@ -66,6 +67,7 @@ public final class DateUtils { SimpleDateFormat formatFor = DateFormatHolder.formatFor(pattern); return formatFor.format(date); } + public static Date parseDate(String date, String pattern) { Args.notNull(date, "Date"); Args.notNull(pattern, "Pattern"); @@ -77,9 +79,11 @@ public final class DateUtils { } return null; } + public static Date parse(String date) { return parseDate(date, YYYY_MM_DD_HH_MM_SS); } + public static final String format(Date date) { return formatDate(date, YYYY_MM_DD_HH_MM_SS); } @@ -99,7 +103,7 @@ public final class DateUtils { * @return 分钟数 */ public static final long minutesRemaining(Date date) { - return (date.getTime() - System.currentTimeMillis()) / 1000 / 60; + return (date.getTime() / 1000 / 60 - System.currentTimeMillis() / 1000 / 60); } /** -- Gitee From 12a6efb176af301673f473568234349530793558 Mon Sep 17 00:00:00 2001 From: egzosn Date: Sat, 25 Apr 2020 16:09:19 +0800 Subject: [PATCH 106/299] =?UTF-8?q?1.=E6=94=AF=E4=BB=98=E5=AE=9D=E5=B0=8F?= =?UTF-8?q?=E7=A8=8B=E5=BA=8F=E6=94=AF=E4=BB=98=202.=E4=BF=AE=E5=A4=8D?= =?UTF-8?q?=E4=BA=8C=E7=BB=B4=E7=A0=81=E6=94=AF=E4=BB=98=E8=BF=87=E6=9C=9F?= =?UTF-8?q?=E6=97=B6=E9=97=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pay-java-ali/README.md | 12 ++++++++++++ .../java/com/egzosn/pay/ali/api/AliPayService.java | 7 ++++++- .../com/egzosn/pay/ali/bean/AliTransactionType.java | 4 ++++ .../java/com/egzosn/pay/common/bean/PayOrder.java | 1 + 4 files changed, 23 insertions(+), 1 deletion(-) diff --git a/pay-java-ali/README.md b/pay-java-ali/README.md index bebaa86..e7a2f5e 100644 --- a/pay-java-ali/README.md +++ b/pay-java-ali/README.md @@ -113,6 +113,18 @@ Map appOrderInfo = service.orderInfo(payOrder); /*-----------/APP-------------------*/ +``` +#### 小程序支付 + +```java + + /*-----------APP-------------------*/ + payOrder.setTransactionType(AliTransactionType.MINAPP); + payOrder.setOpenid("支付宝小程序授权登录成功后获取到的支付宝 user_id") + //获取小程序支付所需的信息组,直接给小程序网页端就可使用 + Map appOrderInfo = service.orderInfo(payOrder); + /*-----------/APP-------------------*/ + ``` #### 即时到帐 WAP 网页支付 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 06cd7b5..7cc5075 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 @@ -213,6 +213,11 @@ public class AliPayService extends BasePayService { bizContent.put(PASSBACK_PARAMS, order.getAddition()); bizContent.put(PRODUCT_CODE, "QUICK_MSECURITY_PAY"); break; + case MINAPP: + bizContent.put("extend_params", order.getAddition()); + bizContent.put("buyer_id", order.getOpenid()); + bizContent.put(PRODUCT_CODE, "FACE_TO_FACE_PAYMENT"); + break; case BAR_CODE: case WAVE_CODE: case SECURITY_CODE: @@ -223,7 +228,7 @@ public class AliPayService extends BasePayService { } if (null != order.getExpirationTime()) { - bizContent.put("timeout_express", DateUtils.minutesRemaining(order.getExpirationTime()) + "m"); + bizContent.put(order.getTransactionType() == AliTransactionType.SWEEPPAY ? "qr_code_timeout_express" : "timeout_express", DateUtils.minutesRemaining(order.getExpirationTime()) + "m"); } orderInfo.put(BIZ_CONTENT, JSON.toJSONString(bizContent)); orderInfo.putAll(order.getAttrs()); 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 bb722de..2073ba3 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 @@ -44,6 +44,10 @@ public enum AliTransactionType implements TransactionType { * 声波付 */ WAVE_CODE("alipay.trade.pay"), + /** + * 小程序 + */ + MINAPP("alipay.trade.create"), /** * 刷脸付 */ 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 dd550be..1982763 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 @@ -71,6 +71,7 @@ public class PayOrder implements Order { /** * 用户唯一标识 * 微信含 sub_openid 字段 + * 支付宝 buyer_id */ private String openid; /** -- Gitee From d8d9cd6a88a95c06a2dc9b8146ffc0d24d26819b Mon Sep 17 00:00:00 2001 From: egzosn Date: Sat, 25 Apr 2020 16:09:41 +0800 Subject: [PATCH 107/299] =?UTF-8?q?=E8=87=AA=E5=AE=9A=E4=B9=89=E8=AE=A2?= =?UTF-8?q?=E5=8D=95=E5=8F=82=E6=95=B0=E4=BD=BF=E7=94=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/main/java/com/egzosn/pay/wx/api/WxPayService.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) 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 229a2b5..2814e1f 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 @@ -205,11 +205,12 @@ public class WxPayService extends BasePayService { ((WxTransactionType) order.getTransactionType()).setAttribute(parameters, order); //可覆盖参数 - setParameters(parameters, "notify_url", order); +/* setParameters(parameters, "notify_url", order); setParameters(parameters, "goods_tag", order); setParameters(parameters, "limit_pay", order); setParameters(parameters, "receipt", order); - setParameters(parameters, "product_id", order); + setParameters(parameters, "product_id", order);*/ + parameters.putAll(order.getAttrs()); parameters = preOrderHandler(parameters, order); setSign(parameters); -- Gitee From 871a9838337c783ad4431c2e4c7a959dc6f39ff7 Mon Sep 17 00:00:00 2001 From: faymanwang <1057438332@qq.com> Date: Fri, 15 May 2020 14:35:23 +0800 Subject: [PATCH 108/299] =?UTF-8?q?=E5=BE=AE=E4=BF=A1=E6=94=AF=E4=BB=98-?= =?UTF-8?q?=E5=8F=91=E7=BA=A2=E5=8C=85=E6=A8=A1=E5=9D=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/egzosn/pay/wx/api/WxPayService.java | 60 ++++++- .../com/egzosn/pay/wx/bean/RedpackOrder.java | 152 ++++++++++++++++++ .../egzosn/pay/wx/bean/WxSendredpackType.java | 43 +++++ 3 files changed, 251 insertions(+), 4 deletions(-) create mode 100644 pay-java-wx/src/main/java/com/egzosn/pay/wx/bean/RedpackOrder.java create mode 100644 pay-java-wx/src/main/java/com/egzosn/pay/wx/bean/WxSendredpackType.java 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 2814e1f..845f1b9 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 @@ -12,11 +12,9 @@ import com.egzosn.pay.common.util.Util; import com.egzosn.pay.common.util.sign.SignUtils; import com.egzosn.pay.common.util.sign.encrypt.RSA2; import com.egzosn.pay.common.util.str.StringUtils; -import com.egzosn.pay.wx.bean.WxPayError; -import com.egzosn.pay.wx.bean.WxPayMessage; -import com.egzosn.pay.wx.bean.WxTransactionType; +import com.egzosn.pay.wx.bean.*; import com.egzosn.pay.common.util.XML; -import com.egzosn.pay.wx.bean.WxTransferType; + import java.io.IOException; import java.io.InputStream; import java.math.BigDecimal; @@ -757,4 +755,58 @@ public class WxPayService extends BasePayService { public PayMessage createMessage(Map message) { return WxPayMessage.create(message); } + + /** + * 发放裂变红包 + * 裂变红包:一次可以发放一组红包。首先领取的用户为种子用户,种子用户领取一组红包当中的一个,并可以通过社交分享将剩下的红包给其他用户。裂变红包充分利用了人际传播的优势。 + * + * @author: faymanwang 1057438332@qq.com + * @param redpackOrder 红包实体 + * @return 返回发红包实体后的结果 + */ + public Map sendgroupredpack(RedpackOrder redpackOrder) { + Map parameters = new TreeMap(); + redpackParam(redpackOrder, parameters); + parameters.put("amt_type", "ALL_RAND"); + parameters.put(SIGN, createSign(SignUtils.parameterText(parameters, "&", SIGN), payConfigStorage.getInputCharset())); + return requestTemplate.postForObject(getReqUrl( WxSendredpackType.SENDGROUPREDPACK), XML.getMap2Xml(parameters) , JSONObject.class); + } + + /** + * 查询红包记录 + * 用于商户对已发放的红包进行查询红包的具体信息,可支持普通红包和裂变包 + * 查询红包记录API只支持查询30天内的红包订单,30天之前的红包订单请登录商户平台查询。 + * + * @author: faymanwang 1057438332@qq.com + * @param mchBillno 商户发放红包的商户订单号 + * @return 返回查询结果 + */ + public Map gethbinfo (String mchBillno) { + Map parameters = this.getPublicParameters(); + parameters.put("mch_billno", mchBillno); + parameters.put("bill_type", "MCHT"); + parameters.put(SIGN, createSign(SignUtils.parameterText(parameters, "&", SIGN), payConfigStorage.getInputCharset())); + return requestTemplate.postForObject(getReqUrl( WxSendredpackType.GETHBINFO), XML.getMap2Xml(parameters) , JSONObject.class); + } + + /** + * 微信红包构造参数方法 + * @param redpackOrder 红包实体 + * @param parameters + */ + private void redpackParam(RedpackOrder redpackOrder, Map parameters) { + parameters.put(NONCE_STR, SignUtils.randomStr()); + parameters.put(MCH_ID, payConfigStorage.getPid()); + parameters.put("wxappid", payConfigStorage.getAppid()); + parameters.put("send_name", redpackOrder.getSendName()); + parameters.put("re_openid", redpackOrder.getReOpenid()); + parameters.put("mch_billno", redpackOrder.getMchBillno()); + parameters.put("total_amount", Util.conversionCentAmount(redpackOrder.getTotalAmount())); + parameters.put("total_num", redpackOrder.getTotalNum() > 0 ? redpackOrder.getTotalNum() : 1); + parameters.put("wishing", redpackOrder.getWishing()); + parameters.put("client_ip", StringUtils.isNotEmpty(redpackOrder.getIp()) ? redpackOrder.getIp() : "192.168.0.1"); + parameters.put("act_name", redpackOrder.getActName()); + parameters.put("remark", redpackOrder.getRemark()); + parameters.put("scene_id", redpackOrder.getSceneId()); + } } diff --git a/pay-java-wx/src/main/java/com/egzosn/pay/wx/bean/RedpackOrder.java b/pay-java-wx/src/main/java/com/egzosn/pay/wx/bean/RedpackOrder.java new file mode 100644 index 0000000..801f94f --- /dev/null +++ b/pay-java-wx/src/main/java/com/egzosn/pay/wx/bean/RedpackOrder.java @@ -0,0 +1,152 @@ +package com.egzosn.pay.wx.bean; + +import java.math.BigDecimal; + +/** + * @description: + * @author: 保网 faymanwang 1057438332@qq.com + * @time: 2020/5/15 12:40 + */ +public class RedpackOrder { + /** + * 商户订单号(每个订单号必须唯一。取值范围:0~9,a~z,A~Z) + * 接口根据商户订单号支持重入,如出现超时可再调用 + */ + private String mchBillno; + + /** + * 商户名称:红包发送者名称 + */ + private String sendName; + + /** + * 用户openid + */ + private String reOpenid; + + /** + * 付款金额 每个红包金额必须在默认额度内(默认大于1元,小于200元,可在产品设置中自行申请调高额度) + */ + private BigDecimal totalAmount; + + /** + * 红包发放总人数 + * 普通红包:1 + * 裂变:必须介于(包括)3到20之间 + */ + private int totalNum; + + /** + * 红包祝福语 + */ + private String wishing; + + /** + * 操作者ip,根据支付平台所需进行设置 + */ + private String ip; + + /** + * 活动名称 + */ + private String actName; + + /** + * 备注 + */ + private String remark; + + /** + * 发放红包使用场景,红包金额大于200或者小于1元时必传 + * PRODUCT_1:商品促销 + * PRODUCT_2:抽奖 + * PRODUCT_3:虚拟物品兑奖 + * PRODUCT_4:企业内部福利 + * PRODUCT_5:渠道分润 + * PRODUCT_6:保险回馈 + * PRODUCT_7:彩票派奖 + * PRODUCT_8:税务刮奖 + */ + private String sceneId; + + + public String getMchBillno() { + return mchBillno; + } + + public void setMchBillno(String mchBillno) { + this.mchBillno = mchBillno; + } + + public String getSendName() { + return sendName; + } + + public void setSendName(String sendName) { + this.sendName = sendName; + } + + public String getReOpenid() { + return reOpenid; + } + + public void setReOpenid(String reOpenid) { + this.reOpenid = reOpenid; + } + + public BigDecimal getTotalAmount() { + return totalAmount; + } + + public void setTotalAmount(BigDecimal totalAmount) { + this.totalAmount = totalAmount; + } + + public int getTotalNum() { + return totalNum; + } + + public void setTotalNum(int totalNum) { + this.totalNum = totalNum; + } + + public String getWishing() { + return wishing; + } + + public void setWishing(String wishing) { + this.wishing = wishing; + } + + public String getIp() { + return ip; + } + + public void setIp(String ip) { + this.ip = ip; + } + + public String getActName() { + return actName; + } + + public void setActName(String actName) { + this.actName = actName; + } + + public String getRemark() { + return remark; + } + + public void setRemark(String remark) { + this.remark = remark; + } + + public String getSceneId() { + return sceneId; + } + + public void setSceneId(String sceneId) { + this.sceneId = sceneId; + } +} diff --git a/pay-java-wx/src/main/java/com/egzosn/pay/wx/bean/WxSendredpackType.java b/pay-java-wx/src/main/java/com/egzosn/pay/wx/bean/WxSendredpackType.java new file mode 100644 index 0000000..6e114b2 --- /dev/null +++ b/pay-java-wx/src/main/java/com/egzosn/pay/wx/bean/WxSendredpackType.java @@ -0,0 +1,43 @@ +package com.egzosn.pay.wx.bean; + +import com.egzosn.pay.common.bean.TransferType; + +/** + * @description: + * @author: faymanwang + * @time: 2020/5/14 20:11 + */ +public enum WxSendredpackType implements TransferType { + /** + * 现金红包-发放红包接口 + */ + SENDREDPACK("mmpaymkttransfers/sendredpack"), + /** + * 现金红包-发放裂变红包 + */ + SENDGROUPREDPACK("mmpaymkttransfers/sendgroupredpack"), + /** + * 现金红包-查询红包记录 + */ + GETHBINFO ("mmpaymkttransfers/gethbinfo"), + /** + * 小程序 + */ + SENDMINIPROGRAMHB ("mmpaymkttransfers/sendminiprogramhb") + + ; + + WxSendredpackType(String method) { + this.method = method; + } + private String method; + + @Override + public String getType() { + return this.name(); + } + @Override + public String getMethod() { + return this.method; + } +} -- Gitee From e297bbd155e6ee172e55c24950681bc7251fa09e Mon Sep 17 00:00:00 2001 From: faymanwang <1057438332@qq.com> Date: Fri, 15 May 2020 15:16:38 +0800 Subject: [PATCH 109/299] =?UTF-8?q?=E5=BE=AE=E4=BF=A1=E6=94=AF=E4=BB=98-?= =?UTF-8?q?=E5=8F=91=E7=BA=A2=E5=8C=85=E6=A8=A1=E5=9D=97-=E5=B7=B2?= =?UTF-8?q?=E6=B5=8B=E8=AF=95=E7=8E=B0=E9=87=91=E5=8F=91=E7=BA=A2=E5=8C=85?= =?UTF-8?q?=EF=BC=8C=E5=B0=8F=E7=A8=8B=E5=BA=8F=E5=8F=91=E7=BA=A2=E5=8C=85?= =?UTF-8?q?=E6=B5=8B=E8=AF=95=E8=BF=94=E5=9B=9E=E5=95=86=E6=88=B7=E7=BC=BA?= =?UTF-8?q?=E5=A4=B1=E6=9D=83=E9=99=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/egzosn/pay/wx/api/WxPayService.java | 33 +++++++++++++++++-- 1 file changed, 31 insertions(+), 2 deletions(-) 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 845f1b9..a69bff2 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 @@ -756,7 +756,20 @@ public class WxPayService extends BasePayService { return WxPayMessage.create(message); } - /** + /** + * 微信发红包 + * @author: faymanwang 1057438332@qq.com + * @param redpackOrder 红包实体 + * @return 返回发红包实体后的结果 + */ + public Map sendredpack(RedpackOrder redpackOrder) { + Map parameters = new TreeMap(); + redpackParam(redpackOrder, parameters); + parameters.put(SIGN, createSign(SignUtils.parameterText(parameters, "&", SIGN), payConfigStorage.getInputCharset())); + return requestTemplate.postForObject(getReqUrl( WxSendredpackType.SENDREDPACK), XML.getMap2Xml(parameters) , JSONObject.class); + } + + /** * 发放裂变红包 * 裂变红包:一次可以发放一组红包。首先领取的用户为种子用户,种子用户领取一组红包当中的一个,并可以通过社交分享将剩下的红包给其他用户。裂变红包充分利用了人际传播的优势。 * @@ -772,6 +785,20 @@ public class WxPayService extends BasePayService { return requestTemplate.postForObject(getReqUrl( WxSendredpackType.SENDGROUPREDPACK), XML.getMap2Xml(parameters) , JSONObject.class); } + /** + * 小程序发红包 + * @author: faymanwang 1057438332@qq.com + * @param redpackOrder 红包实体 + * @return 返回发红包实体后的结果 + */ + public Map sendminiprogramhb(RedpackOrder redpackOrder) { + Map parameters = new TreeMap(); + redpackParam(redpackOrder, parameters); + parameters.put("notify_way", "MINI_PROGRAM_JSAPI"); + parameters.put(SIGN, createSign(SignUtils.parameterText(parameters, "&", SIGN), payConfigStorage.getInputCharset())); + return requestTemplate.postForObject(getReqUrl( WxSendredpackType.SENDMINIPROGRAMHB), XML.getMap2Xml(parameters) , JSONObject.class); + } + /** * 查询红包记录 * 用于商户对已发放的红包进行查询红包的具体信息,可支持普通红包和裂变包 @@ -807,6 +834,8 @@ public class WxPayService extends BasePayService { parameters.put("client_ip", StringUtils.isNotEmpty(redpackOrder.getIp()) ? redpackOrder.getIp() : "192.168.0.1"); parameters.put("act_name", redpackOrder.getActName()); parameters.put("remark", redpackOrder.getRemark()); - parameters.put("scene_id", redpackOrder.getSceneId()); + if(StringUtils.isNotEmpty(redpackOrder.getSceneId())){ + parameters.put("scene_id", redpackOrder.getSceneId()); + } } } -- Gitee From 9adaae20aa9e1118912e0bb6f54cd5fe2c2ffb1a Mon Sep 17 00:00:00 2001 From: egzosn Date: Sun, 17 May 2020 10:46:29 +0800 Subject: [PATCH 110/299] =?UTF-8?q?=E8=BF=98=E5=8E=9F=E8=87=B3=E5=8F=91?= =?UTF-8?q?=E5=B8=83=E6=97=B6=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/egzosn/pay/common/bean/PayOrder.java | 10 ++---- .../egzosn/pay/common/bean/TransferOrder.java | 34 +------------------ pay-java-demo/pom.xml | 2 +- 3 files changed, 4 insertions(+), 42 deletions(-) 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 dd550be..57a6842 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 @@ -258,26 +258,20 @@ public class PayOrder implements Order { } @Override - public Map getAttrs() { + public Map getAttr() { if (null == attr){ attr = new HashMap<>(); } return attr; } - @Override - public Object getAttr(String key) { - return getAttrs().get(key); - } - - /** * 添加订单信息 * @param key key * @param value 值 */ public void addAttr(String key, Object value) { - getAttrs().put(key, value); + getAttr().put(key, value); } diff --git a/pay-java-common/src/main/java/com/egzosn/pay/common/bean/TransferOrder.java b/pay-java-common/src/main/java/com/egzosn/pay/common/bean/TransferOrder.java index 83f05b8..2b89117 100644 --- a/pay-java-common/src/main/java/com/egzosn/pay/common/bean/TransferOrder.java +++ b/pay-java-common/src/main/java/com/egzosn/pay/common/bean/TransferOrder.java @@ -1,8 +1,6 @@ package com.egzosn.pay.common.bean; import java.math.BigDecimal; -import java.util.HashMap; -import java.util.Map; /** * 转账订单 @@ -12,7 +10,7 @@ import java.util.Map; * date 2018/1/31 *
*/ -public class TransferOrder implements Order { +public class TransferOrder { /** * 转账批次订单单号 @@ -81,12 +79,6 @@ public class TransferOrder implements Order { */ private String ip; - - /** - * 订单附加信息,可用于预设未提供的参数,这里会覆盖以上所有的订单信息, - */ - private Map attr; - public String getBatchNo() { return batchNo; } @@ -198,28 +190,4 @@ public class TransferOrder implements Order { public void setIp(String ip) { this.ip = ip; } - @Override - public Map getAttrs() { - if (null == attr){ - attr = new HashMap<>(); - } - return attr; - } - - @Override - public Object getAttr(String key) { - return getAttrs().get(key); - } - - - /** - * 添加订单信息 - * @param key key - * @param value 值 - */ - public void addAttr(String key, Object value) { - getAttrs().put(key, value); - } - - } diff --git a/pay-java-demo/pom.xml b/pay-java-demo/pom.xml index 896b184..d2a5c0d 100644 --- a/pay-java-demo/pom.xml +++ b/pay-java-demo/pom.xml @@ -86,7 +86,7 @@ com.fasterxml.jackson.core jackson-databind - 2.9.10 + 2.9.10.1 -- Gitee From cfc25ffe5c5164ecbdc192bfebed457808676c92 Mon Sep 17 00:00:00 2001 From: egzosn Date: Sun, 17 May 2020 11:08:53 +0800 Subject: [PATCH 111/299] =?UTF-8?q?=E8=BF=98=E5=8E=9F=E8=87=B3=E5=8F=91?= =?UTF-8?q?=E5=B8=83=E6=97=B6=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/main/java/com/egzosn/pay/ali/api/AliPayService.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 06cd7b5..b9685c8 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 @@ -226,7 +226,7 @@ public class AliPayService extends BasePayService { bizContent.put("timeout_express", DateUtils.minutesRemaining(order.getExpirationTime()) + "m"); } orderInfo.put(BIZ_CONTENT, JSON.toJSONString(bizContent)); - orderInfo.putAll(order.getAttrs()); + orderInfo.putAll(order.getAttr()); return preOrderHandler(orderInfo, order); } -- Gitee From eef3617fc5ccee46bacd123ba48a544467f639f6 Mon Sep 17 00:00:00 2001 From: faymanwang <1057438332@qq.com> Date: Sun, 17 May 2020 14:27:19 +0800 Subject: [PATCH 112/299] =?UTF-8?q?=E5=BE=AE=E4=BF=A1=E6=94=AF=E4=BB=98-?= =?UTF-8?q?=E5=8F=91=E7=BA=A2=E5=8C=85=E6=A8=A1=E5=9D=97-=E7=8E=B0?= =?UTF-8?q?=E9=87=91=E7=BA=A2=E5=8C=85=EF=BC=8C=E5=B0=8F=E7=A8=8B=E5=BA=8F?= =?UTF-8?q?=E4=BA=BA=E6=95=B0=E5=9B=BA=E5=AE=9A=E4=BC=A01=EF=BC=8C?= =?UTF-8?q?=E6=8A=BD=E5=8F=96=E5=85=AC=E5=85=B1=E7=B1=BB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/egzosn/pay/wx/api/WxPayService.java | 26 ++++++++++++++----- .../com/egzosn/pay/wx/bean/RedpackOrder.java | 4 ++- .../egzosn/pay/wx/bean/WxSendredpackType.java | 4 +-- 3 files changed, 24 insertions(+), 10 deletions(-) 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 a69bff2..068cf22 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 @@ -7,6 +7,7 @@ import com.egzosn.pay.common.bean.*; import com.egzosn.pay.common.bean.result.PayException; import com.egzosn.pay.common.exception.PayErrorException; import com.egzosn.pay.common.http.HttpConfigStorage; +import com.egzosn.pay.common.http.HttpRequestTemplate; import com.egzosn.pay.common.util.DateUtils; import com.egzosn.pay.common.util.Util; import com.egzosn.pay.common.util.sign.SignUtils; @@ -765,8 +766,8 @@ public class WxPayService extends BasePayService { public Map sendredpack(RedpackOrder redpackOrder) { Map parameters = new TreeMap(); redpackParam(redpackOrder, parameters); - parameters.put(SIGN, createSign(SignUtils.parameterText(parameters, "&", SIGN), payConfigStorage.getInputCharset())); - return requestTemplate.postForObject(getReqUrl( WxSendredpackType.SENDREDPACK), XML.getMap2Xml(parameters) , JSONObject.class); + parameters.put("total_num", 1); + return routRedpack(parameters, WxSendredpackType.SENDREDPACK); } /** @@ -781,8 +782,7 @@ public class WxPayService extends BasePayService { Map parameters = new TreeMap(); redpackParam(redpackOrder, parameters); parameters.put("amt_type", "ALL_RAND"); - parameters.put(SIGN, createSign(SignUtils.parameterText(parameters, "&", SIGN), payConfigStorage.getInputCharset())); - return requestTemplate.postForObject(getReqUrl( WxSendredpackType.SENDGROUPREDPACK), XML.getMap2Xml(parameters) , JSONObject.class); + return routRedpack(parameters, WxSendredpackType.SENDGROUPREDPACK); } /** @@ -794,9 +794,9 @@ public class WxPayService extends BasePayService { public Map sendminiprogramhb(RedpackOrder redpackOrder) { Map parameters = new TreeMap(); redpackParam(redpackOrder, parameters); + parameters.put("total_num", 1); parameters.put("notify_way", "MINI_PROGRAM_JSAPI"); - parameters.put(SIGN, createSign(SignUtils.parameterText(parameters, "&", SIGN), payConfigStorage.getInputCharset())); - return requestTemplate.postForObject(getReqUrl( WxSendredpackType.SENDMINIPROGRAMHB), XML.getMap2Xml(parameters) , JSONObject.class); + return routRedpack(parameters, WxSendredpackType.SENDMINIPROGRAMHB); } /** @@ -812,12 +812,24 @@ public class WxPayService extends BasePayService { Map parameters = this.getPublicParameters(); parameters.put("mch_billno", mchBillno); parameters.put("bill_type", "MCHT"); + return routRedpack(parameters, WxSendredpackType.GETHBINFO); + } + + /** + * + * @author: faymanwang 1057438332@qq.com + * @param parameters + * @param sendredpackType + * @return + */ + private Map routRedpack(Map parameters, TransferType sendredpackType) { parameters.put(SIGN, createSign(SignUtils.parameterText(parameters, "&", SIGN), payConfigStorage.getInputCharset())); - return requestTemplate.postForObject(getReqUrl( WxSendredpackType.GETHBINFO), XML.getMap2Xml(parameters) , JSONObject.class); + return requestTemplate.postForObject(getReqUrl(sendredpackType), XML.getMap2Xml(parameters), JSONObject.class); } /** * 微信红包构造参数方法 + * @author: faymanwang 1057438332@qq.com * @param redpackOrder 红包实体 * @param parameters */ diff --git a/pay-java-wx/src/main/java/com/egzosn/pay/wx/bean/RedpackOrder.java b/pay-java-wx/src/main/java/com/egzosn/pay/wx/bean/RedpackOrder.java index 801f94f..cc9d80f 100644 --- a/pay-java-wx/src/main/java/com/egzosn/pay/wx/bean/RedpackOrder.java +++ b/pay-java-wx/src/main/java/com/egzosn/pay/wx/bean/RedpackOrder.java @@ -25,7 +25,9 @@ public class RedpackOrder { private String reOpenid; /** - * 付款金额 每个红包金额必须在默认额度内(默认大于1元,小于200元,可在产品设置中自行申请调高额度) + * 付款金额 + * 每个红包金额必须在默认额度内(默认大于1元,小于200元,可在产品设置中自行申请调高额度) + * 可以设置最低每个0.3 */ private BigDecimal totalAmount; diff --git a/pay-java-wx/src/main/java/com/egzosn/pay/wx/bean/WxSendredpackType.java b/pay-java-wx/src/main/java/com/egzosn/pay/wx/bean/WxSendredpackType.java index 6e114b2..ab5b742 100644 --- a/pay-java-wx/src/main/java/com/egzosn/pay/wx/bean/WxSendredpackType.java +++ b/pay-java-wx/src/main/java/com/egzosn/pay/wx/bean/WxSendredpackType.java @@ -4,7 +4,7 @@ import com.egzosn.pay.common.bean.TransferType; /** * @description: - * @author: faymanwang + * @author: 保网 faymanwang 1057438332@qq.com * @time: 2020/5/14 20:11 */ public enum WxSendredpackType implements TransferType { @@ -17,7 +17,7 @@ public enum WxSendredpackType implements TransferType { */ SENDGROUPREDPACK("mmpaymkttransfers/sendgroupredpack"), /** - * 现金红包-查询红包记录 + * 现金红包,小程序-查询红包记录 */ GETHBINFO ("mmpaymkttransfers/gethbinfo"), /** -- Gitee From c153f736249a50d78a61dd2cc055ae986abee89d Mon Sep 17 00:00:00 2001 From: egzosn Date: Sun, 17 May 2020 22:39:11 +0800 Subject: [PATCH 113/299] =?UTF-8?q?=E7=A7=BB=E9=99=A4=E8=BF=87=E6=97=B6?= =?UTF-8?q?=E7=9A=84=E9=80=80=E6=AC=BE=E6=96=B9=E6=B3=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/egzosn/pay/ali/api/AliPayService.java | 35 ++----------- .../egzosn/pay/baidu/api/BaiduPayService.java | 43 +++------------- .../egzosn/pay/common/api/BasePayService.java | 30 ------------ .../com/egzosn/pay/common/api/PayService.java | 49 ------------------- .../egzosn/pay/fuiou/api/FuiouPayService.java | 30 ------------ .../pay/payoneer/api/PayoneerPayService.java | 30 +----------- .../pay/paypal/api/PayPalPayService.java | 34 ++----------- .../egzosn/pay/union/api/UnionPayService.java | 29 ----------- .../wx/youdian/api/WxYouDianPayService.java | 16 +++--- .../egzosn/pay/yiji/api/YiJiPayService.java | 32 +----------- 10 files changed, 24 insertions(+), 304 deletions(-) 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 7cc5075..af3ceec 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 @@ -15,8 +15,10 @@ import com.egzosn.pay.common.util.DateUtils; import com.egzosn.pay.common.util.Util; import com.egzosn.pay.common.util.sign.SignUtils; import com.egzosn.pay.common.util.str.StringUtils; -import java.math.BigDecimal; -import java.util.*; +import java.util.Date; +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.TreeMap; /** * 支付宝支付服务 @@ -407,24 +409,6 @@ public class AliPayService extends BasePayService { return secondaryInterface(tradeNo, outTradeNo, AliTransactionType.CANCEL); } - /** - * 申请退款接口 - * 废弃 - * - * @param tradeNo 支付平台订单号 - * @param outTradeNo 商户单号 - * @param refundAmount 退款金额 - * @param totalAmount 总金额 - * @return 返回支付方申请退款后的结果 - * @see #refund(RefundOrder, com.egzosn.pay.common.api.Callback) - * @deprecated 版本替代 {@link #refund(RefundOrder, com.egzosn.pay.common.api.Callback)} - */ - @Deprecated - @Override - public Map refund(String tradeNo, String outTradeNo, BigDecimal refundAmount, BigDecimal totalAmount) { - return refund(new RefundOrder(tradeNo, outTradeNo, refundAmount, totalAmount)); - } - /** * 申请退款接口 @@ -449,17 +433,6 @@ public class AliPayService extends BasePayService { return requestTemplate.getForObject(getReqUrl() + "?" + UriVariables.getMapToParameters(parameters), JSONObject.class); } - /** - * 查询退款 - * - * @param tradeNo 支付平台订单号 - * @param outTradeNo 商户单号 - * @return 返回支付方查询退款后的结果 - */ - @Override - public Map refundquery(String tradeNo, String outTradeNo) { - return secondaryInterface(tradeNo, outTradeNo, AliTransactionType.REFUNDQUERY); - } /** * 查询退款 diff --git a/pay-java-baidu/src/main/java/com/egzosn/pay/baidu/api/BaiduPayService.java b/pay-java-baidu/src/main/java/com/egzosn/pay/baidu/api/BaiduPayService.java index d2ee285..ef1b1cb 100644 --- a/pay-java-baidu/src/main/java/com/egzosn/pay/baidu/api/BaiduPayService.java +++ b/pay-java-baidu/src/main/java/com/egzosn/pay/baidu/api/BaiduPayService.java @@ -310,24 +310,7 @@ public class BaiduPayService extends BasePayService { public Map close(String tradeNo, String outTradeNo) { throw new UnsupportedOperationException("不支持该操作"); } - - /** - * 退款, 请使用 {@link com.egzosn.pay.baidu.api.BaiduPayService#refundUseBaidu} - * - * @param orderId - * @param userId - * @param refundAmount 退款金额 - * @param totalAmount 总金额 - * @return - */ - @Override - @Deprecated - public Map refund(String orderId, - String userId, - BigDecimal refundAmount, - BigDecimal totalAmount) { - throw new UnsupportedOperationException("请使用 " + getClass().getName() + "#refundUseBaidu"); - } + /** * 退款 @@ -381,38 +364,28 @@ public class BaiduPayService extends BasePayService { return requestTemplate.getForObject(String.format("%s?%s", getReqUrl(transactionType), UriVariables.getMapToParameters(parameters)), JSONObject.class); } + + /** * 退费查询 * - * @param orderId 百度平台订单ID - * @param userId 百度用户ID + * @param refundOrder 退款订单单号信息 * @return */ @Override - public Map refundquery(String orderId, - String userId) { + public Map refundquery(RefundOrder refundOrder) { + Map parameters = getUseQueryPay(); BaiduTransactionType transactionType = BaiduTransactionType.REFUND_QUERY; parameters.put(METHOD, transactionType.getMethod()); parameters.put(TYPE, 3); - parameters.put(ORDER_ID, orderId); - parameters.put(USER_ID, userId); + parameters.put(ORDER_ID, refundOrder.getTradeNo()); + parameters.put(USER_ID, refundOrder.getUserId()); parameters.put(APP_KEY, payConfigStorage.getAppKey()); parameters.put(RSA_SIGN, getRsaSign(parameters, RSA_SIGN)); return requestTemplate.getForObject(String.format("%s?%s", getReqUrl(transactionType), UriVariables.getMapToParameters(parameters)), JSONObject.class); } - /** - * 退费查询 - * - * @param refundOrder 退款订单单号信息 - * @return - */ - @Override - public Map refundquery(RefundOrder refundOrder) { - return refundquery(refundOrder.getTradeNo(), refundOrder.getOutTradeNo()); - } - /** * 下载资金账单 * diff --git a/pay-java-common/src/main/java/com/egzosn/pay/common/api/BasePayService.java b/pay-java-common/src/main/java/com/egzosn/pay/common/api/BasePayService.java index d2be6c3..04e6141 100644 --- a/pay-java-common/src/main/java/com/egzosn/pay/common/api/BasePayService.java +++ b/pay-java-common/src/main/java/com/egzosn/pay/common/api/BasePayService.java @@ -242,24 +242,7 @@ public abstract class BasePayService implements Pay return Collections.EMPTY_MAP; } - /** - * 退款 - * - * @param tradeNo 支付平台订单号 - * @param outTradeNo 商户单号 - * @param refundAmount 退款金额 - * @param totalAmount 总金额 - * @param callback 处理器 - * @param 返回类型 - * @return 处理过后的类型对象, 返回支付方申请退款后的结果 - * @see #refund(RefundOrder, Callback) - */ - @Deprecated - @Override - public T refund(String tradeNo, String outTradeNo, BigDecimal refundAmount, BigDecimal totalAmount, Callback callback) { - return callback.perform(refund(new RefundOrder(tradeNo, outTradeNo, refundAmount, totalAmount))); - } /** * 申请退款接口 @@ -276,19 +259,6 @@ public abstract class BasePayService implements Pay } - /** - * 查询退款 - * - * @param tradeNo 支付平台订单号 - * @param outTradeNo 商户单号 - * @param callback 处理器 - * @param 返回类型 - * @return 处理过后的类型对象,返回支付方查询退款后的结果 - */ - @Override - public T refundquery(String tradeNo, String outTradeNo, Callback callback) { - return callback.perform(refundquery(tradeNo, outTradeNo)); - } /** * 查询退款 diff --git a/pay-java-common/src/main/java/com/egzosn/pay/common/api/PayService.java b/pay-java-common/src/main/java/com/egzosn/pay/common/api/PayService.java index ce065ea..9397046 100644 --- a/pay-java-common/src/main/java/com/egzosn/pay/common/api/PayService.java +++ b/pay-java-common/src/main/java/com/egzosn/pay/common/api/PayService.java @@ -7,7 +7,6 @@ import com.egzosn.pay.common.http.HttpRequestTemplate; import java.awt.image.BufferedImage; import java.io.InputStream; -import java.math.BigDecimal; import java.util.Date; import java.util.Map; @@ -243,35 +242,7 @@ public interface PayService { */ T cancel(String tradeNo, String outTradeNo, Callback callback); - /** - * 申请退款接口 - * 废弃 - * - * @param tradeNo 支付平台订单号 - * @param outTradeNo 商户单号 - * @param refundAmount 退款金额 - * @param totalAmount 总金额 - * @return 返回支付方申请退款后的结果 - * @see #refund(RefundOrder) - */ - @Deprecated - Map refund(String tradeNo, String outTradeNo, BigDecimal refundAmount, BigDecimal totalAmount); - /** - * 申请退款接口 - * 废弃 - * - * @param tradeNo 支付平台订单号 - * @param outTradeNo 商户单号 - * @param refundAmount 退款金额 - * @param totalAmount 总金额 - * @param callback 处理器 - * @param 返回类型 - * @return 返回支付方申请退款后的结果 - * @see #refund(RefundOrder, Callback) - */ - @Deprecated - T refund(String tradeNo, String outTradeNo, BigDecimal refundAmount, BigDecimal totalAmount, Callback callback); /** * 申请退款接口 @@ -291,27 +262,7 @@ public interface PayService { */ T refund(RefundOrder refundOrder, Callback callback); - /** - * 查询退款 - * - * @param tradeNo 支付平台订单号 - * @param outTradeNo 商户单号 - * @return 返回支付方查询退款后的结果 - */ - @Deprecated - Map refundquery(String tradeNo, String outTradeNo); - /** - * 查询退款 - * - * @param tradeNo 支付平台订单号 - * @param outTradeNo 商户单号 - * @param callback 处理器 - * @param 返回类型 - * @return 返回支付方查询退款后的结果 - */ - @Deprecated - T refundquery(String tradeNo, String outTradeNo, Callback callback); /** * 查询退款 diff --git a/pay-java-fuiou/src/main/java/com/egzosn/pay/fuiou/api/FuiouPayService.java b/pay-java-fuiou/src/main/java/com/egzosn/pay/fuiou/api/FuiouPayService.java index 69a28c0..1446463 100644 --- a/pay-java-fuiou/src/main/java/com/egzosn/pay/fuiou/api/FuiouPayService.java +++ b/pay-java-fuiou/src/main/java/com/egzosn/pay/fuiou/api/FuiouPayService.java @@ -371,22 +371,6 @@ public class FuiouPayService extends BasePayService { - /** - * 申请退款接口 - * - * @param tradeNo 支付平台订单号 - * @param outTradeNo 商户单号 - * @param refundAmount 退款金额 - * @param totalAmount 总金额 - * @return 退款返回结果集 - */ - @Override - public Map refund (String tradeNo, String outTradeNo, BigDecimal refundAmount, BigDecimal totalAmount) { - return refund(new RefundOrder(tradeNo, outTradeNo, refundAmount, totalAmount)); - } - - - /** * 申请退款接口 @@ -413,20 +397,6 @@ public class FuiouPayService extends BasePayService { } - - /** - * 查询退款 - * @param tradeNo 支付平台订单号 - * @param outTradeNo 商户单号 - * @return 空 - * - */ - - @Override - public Map refundquery(String tradeNo, String outTradeNo) { - return Collections.emptyMap(); - } - /** * 查询退款 * diff --git a/pay-java-payoneer/src/main/java/com/egzosn/pay/payoneer/api/PayoneerPayService.java b/pay-java-payoneer/src/main/java/com/egzosn/pay/payoneer/api/PayoneerPayService.java index 80360b8..8596b7b 100644 --- a/pay-java-payoneer/src/main/java/com/egzosn/pay/payoneer/api/PayoneerPayService.java +++ b/pay-java-payoneer/src/main/java/com/egzosn/pay/payoneer/api/PayoneerPayService.java @@ -18,7 +18,6 @@ import org.apache.http.Header; import org.apache.http.entity.ContentType; import org.apache.http.message.BasicHeader; -import java.math.BigDecimal; import java.util.*; /** @@ -313,22 +312,7 @@ public class PayoneerPayService extends BasePayService im return secondaryInterface(tradeNo, outTradeNo, PayoneerTransactionType.CHARGE_CANCEL); } - /** - * 申请退款接口 - * 废弃 - * - * @param tradeNo 支付平台订单号 - * @param outTradeNo 商户单号 - * @param refundAmount 退款金额 - * @param totalAmount 总金额 - * - * @return 返回支付方申请退款后的结果 - * @see #refund(RefundOrder) - */ - @Override - public Map refund(String tradeNo, String outTradeNo, BigDecimal refundAmount, BigDecimal totalAmount) { - return close(tradeNo, outTradeNo); - } + /** @@ -343,18 +327,6 @@ public class PayoneerPayService extends BasePayService im return close(refundOrder.getTradeNo(), refundOrder.getOutTradeNo()); } - /** - * 查询退款 - * - * @param tradeNo 支付平台订单号 - * @param outTradeNo 商户单号 - * - * @return 返回支付方查询退款后的结果 - */ - @Override - public Map refundquery(String tradeNo, String outTradeNo) { - return Collections.emptyMap(); - } /** * 查询退款 diff --git a/pay-java-paypal/src/main/java/com/egzosn/pay/paypal/api/PayPalPayService.java b/pay-java-paypal/src/main/java/com/egzosn/pay/paypal/api/PayPalPayService.java index e522814..ea120e7 100644 --- a/pay-java-paypal/src/main/java/com/egzosn/pay/paypal/api/PayPalPayService.java +++ b/pay-java-paypal/src/main/java/com/egzosn/pay/paypal/api/PayPalPayService.java @@ -16,7 +16,7 @@ import com.egzosn.pay.paypal.bean.order.*; import org.apache.http.Header; import org.apache.http.entity.ContentType; import org.apache.http.message.BasicHeader; -import java.awt.image.BufferedImage; + import java.io.UnsupportedEncodingException; import java.math.BigDecimal; import java.util.*; @@ -239,22 +239,7 @@ public class PayPalPayService extends BasePayService{ public Map close(String tradeNo, String outTradeNo) { return null; } - /** - * 申请退款接口 - * 废弃 - * @param tradeNo 支付平台订单号 - * @param outTradeNo 商户单号 - * @param refundAmount 退款金额 - * @param totalAmount 总金额 - * @return 返回支付方申请退款后的结果 - * @see #refund(RefundOrder) - * @deprecated {@link #refund(RefundOrder)} - */ - @Deprecated - @Override - public Map refund(String tradeNo, String outTradeNo, BigDecimal refundAmount, BigDecimal totalAmount) { - return refund(new RefundOrder( tradeNo, outTradeNo, refundAmount, totalAmount)); - } + /** @@ -284,18 +269,6 @@ public class PayPalPayService extends BasePayService{ JSONObject resp = getHttpRequestTemplate().postForObject(getReqUrl(PayPalTransactionType.REFUND), httpEntity, JSONObject.class, refundOrder.getTradeNo()); return resp; } - /** - * 查询退款 - * - * @param tradeNo 支付平台订单号 - * @param outTradeNo 商户单号 - * @return 返回支付方查询退款后的结果 - */ - @Override - public Map refundquery(String tradeNo, String outTradeNo) { - JSONObject resp = getHttpRequestTemplate().getForObject(getReqUrl(PayPalTransactionType.REFUND_QUERY), authHeader(), JSONObject.class, tradeNo); - return resp; - } /** * 查询退款 @@ -305,7 +278,8 @@ public class PayPalPayService extends BasePayService{ */ @Override public Map refundquery(RefundOrder refundOrder) { - return refundquery(refundOrder.getTradeNo(), refundOrder.getOutTradeNo()); + JSONObject resp = getHttpRequestTemplate().getForObject(getReqUrl(PayPalTransactionType.REFUND_QUERY), authHeader(), JSONObject.class, refundOrder.getTradeNo()); + return resp; } @Override diff --git a/pay-java-union/src/main/java/com/egzosn/pay/union/api/UnionPayService.java b/pay-java-union/src/main/java/com/egzosn/pay/union/api/UnionPayService.java index 50dbcf8..978d180 100644 --- a/pay-java-union/src/main/java/com/egzosn/pay/union/api/UnionPayService.java +++ b/pay-java-union/src/main/java/com/egzosn/pay/union/api/UnionPayService.java @@ -9,7 +9,6 @@ import com.egzosn.pay.common.exception.PayErrorException; import com.egzosn.pay.common.http.HttpConfigStorage; import com.egzosn.pay.common.http.UriVariables; import com.egzosn.pay.common.util.DateUtils; -import com.egzosn.pay.common.util.MatrixToImageWriter; import com.egzosn.pay.common.util.Util; import com.egzosn.pay.common.util.sign.CertDescriptor; import com.egzosn.pay.common.util.sign.SignUtils; @@ -609,40 +608,12 @@ public class UnionPayService extends BasePayService { return Collections.emptyMap(); } - /** - * 申请退款接口 - * - * @param tradeNo 支付平台订单号 - * @param outTradeNo 商户单号 - * @param refundAmount 退款金额 - * @param totalAmount 总金额 - * @return 返回支付方申请退款后的结果 - * @see #refund(RefundOrder) - */ - @Deprecated - @Override - public Map refund(String tradeNo, String outTradeNo, BigDecimal refundAmount, BigDecimal totalAmount) { - return refund(new RefundOrder(tradeNo, outTradeNo, refundAmount, totalAmount)); - } - @Override public Map refund(RefundOrder refundOrder) { return unionRefundOrConsumeUndo(refundOrder, UnionTransactionType.REFUND); } - /** - * 查询退款 - * - * @param tradeNo 支付平台订单号 - * @param outTradeNo 商户单号 - * @return 返回支付方查询退款后的结果 - */ - @Override - public Map refundquery(String tradeNo, String outTradeNo) { - return Collections.emptyMap(); - } - /** * 查询退款 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 8d4e387..ee1ecd9 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 @@ -385,13 +385,14 @@ public class WxYouDianPayService extends BasePayService refund(String tradeNo, String outTradeNo, BigDecimal refundAmount, BigDecimal totalAmount) { - return refund(new RefundOrder(tradeNo, outTradeNo,refundAmount, totalAmount)); - } - + /** + * 申请退款接口 + * + * @param refundOrder 退款订单信息 + * @return 返回支付方申请退款后的结果 + */ @Override public Map refund(RefundOrder refundOrder) { String apbNonce = SignUtils.randomStr(); @@ -413,11 +414,6 @@ public class WxYouDianPayService extends BasePayService refundquery(String tradeNo, String outTradeNo) { - return Collections.emptyMap(); - } - /** * 查询退款 * diff --git a/pay-java-yiji/src/main/java/com/egzosn/pay/yiji/api/YiJiPayService.java b/pay-java-yiji/src/main/java/com/egzosn/pay/yiji/api/YiJiPayService.java index afd5d43..232d126 100644 --- a/pay-java-yiji/src/main/java/com/egzosn/pay/yiji/api/YiJiPayService.java +++ b/pay-java-yiji/src/main/java/com/egzosn/pay/yiji/api/YiJiPayService.java @@ -10,7 +10,7 @@ import com.egzosn.pay.common.util.Util; import com.egzosn.pay.common.util.sign.SignUtils; import com.egzosn.pay.common.util.str.StringUtils; import com.egzosn.pay.yiji.bean.YiJiTransactionType; -import java.math.BigDecimal; + import java.util.Collections; import java.util.Date; import java.util.Map; @@ -292,24 +292,6 @@ public class YiJiPayService extends BasePayService { - /** - * 申请退款接口 - * 废弃 - * - * @param tradeNo 支付平台订单号 - * @param outTradeNo 商户单号 - * @param refundAmount 退款金额 - * @param totalAmount 总金额 - * @return 返回支付方申请退款后的结果 - * @see #refund(RefundOrder, com.egzosn.pay.common.api.Callback) - * @deprecated 版本替代 {@link #refund(RefundOrder, com.egzosn.pay.common.api.Callback)} - */ - @Deprecated - @Override - public Map refund(String tradeNo, String outTradeNo, BigDecimal refundAmount, BigDecimal totalAmount) { - return refund(new RefundOrder(tradeNo, outTradeNo, refundAmount, totalAmount)); - } - /** * 申请退款接口 @@ -329,18 +311,6 @@ public class YiJiPayService extends BasePayService { return getHttpRequestTemplate().postForObject(getReqUrl(YiJiTransactionType.tradeRefund), orderInfo, JSONObject.class); } - /** - * 查询退款 - * - * @param tradeNo 支付平台订单号 - * @param outTradeNo 商户单号 - * @return 返回支付方查询退款后的结果 - */ - @Override - public Map refundquery(String tradeNo, String outTradeNo) { - return Collections.emptyMap(); - } - /** * 查询退款 * -- Gitee From c84182cf4422622e7e25c784e1ebe7682bfcd740 Mon Sep 17 00:00:00 2001 From: egzosn Date: Sun, 17 May 2020 22:43:40 +0800 Subject: [PATCH 114/299] =?UTF-8?q?=E6=96=B0=E5=A2=9E=E9=80=80=E6=AC=BE?= =?UTF-8?q?=E7=94=A8=E6=88=B7,=20=E4=BB=A3=E7=A0=81=E6=A0=BC=E5=BC=8F?= =?UTF-8?q?=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/egzosn/pay/ali/api/AliPayService.java | 1 + .../com/egzosn/pay/common/api/BasePayService.java | 1 - .../com/egzosn/pay/common/bean/RefundOrder.java | 14 +++++++++++++- .../pay/wx/youdian/api/WxYouDianPayService.java | 3 +-- 4 files changed, 15 insertions(+), 4 deletions(-) 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 af3ceec..ef9a180 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 @@ -15,6 +15,7 @@ import com.egzosn.pay.common.util.DateUtils; import com.egzosn.pay.common.util.Util; import com.egzosn.pay.common.util.sign.SignUtils; import com.egzosn.pay.common.util.str.StringUtils; + import java.util.Date; import java.util.LinkedHashMap; import java.util.Map; diff --git a/pay-java-common/src/main/java/com/egzosn/pay/common/api/BasePayService.java b/pay-java-common/src/main/java/com/egzosn/pay/common/api/BasePayService.java index 04e6141..b7d8124 100644 --- a/pay-java-common/src/main/java/com/egzosn/pay/common/api/BasePayService.java +++ b/pay-java-common/src/main/java/com/egzosn/pay/common/api/BasePayService.java @@ -14,7 +14,6 @@ import org.apache.commons.logging.LogFactory; import java.awt.image.BufferedImage; import java.io.InputStream; import java.io.UnsupportedEncodingException; -import java.math.BigDecimal; import java.util.*; /** diff --git a/pay-java-common/src/main/java/com/egzosn/pay/common/bean/RefundOrder.java b/pay-java-common/src/main/java/com/egzosn/pay/common/bean/RefundOrder.java index be5061a..b8bce56 100644 --- a/pay-java-common/src/main/java/com/egzosn/pay/common/bean/RefundOrder.java +++ b/pay-java-common/src/main/java/com/egzosn/pay/common/bean/RefundOrder.java @@ -5,7 +5,7 @@ import java.util.Date; /** * 退款订单信息 - * @author: egan + * @author egan *
  *      email egzosn@gmail.com
  *      date 2018/1/15 21:40
@@ -46,6 +46,10 @@ public class RefundOrder {
      * 退款说明
      */
     private String description;
+    /**
+     * 退款用户
+     */
+    private String userId;
 
     public String getRefundNo() {
         return refundNo;
@@ -111,6 +115,14 @@ public class RefundOrder {
         this.description = description;
     }
 
+    public String getUserId() {
+        return userId;
+    }
+
+    public void setUserId(String userId) {
+        this.userId = userId;
+    }
+
     public RefundOrder() {
     }
 
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 ee1ecd9..e28cb10 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
@@ -7,14 +7,13 @@ import com.egzosn.pay.common.bean.*;
 import com.egzosn.pay.common.bean.result.PayError;
 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.Util;
 import com.egzosn.pay.common.util.sign.SignUtils;
 import com.egzosn.pay.common.util.str.StringUtils;
 import com.egzosn.pay.wx.youdian.bean.WxYoudianPayMessage;
 import com.egzosn.pay.wx.youdian.bean.YdPayError;
 import com.egzosn.pay.wx.youdian.bean.YoudianTransactionType;
-import java.awt.image.BufferedImage;
+
 import java.io.InputStream;
 import java.math.BigDecimal;
 import java.util.*;
-- 
Gitee


From 575cf77a38805168bef135cf36065601ec2cf401 Mon Sep 17 00:00:00 2001
From: egzosn 
Date: Sun, 17 May 2020 22:47:15 +0800
Subject: [PATCH 115/299] =?UTF-8?q?1.=E4=BF=AE=E5=A4=8D=E9=81=97=E6=BC=8F?=
 =?UTF-8?q?=E7=9A=84RSA=E8=AF=81=E4=B9=A6=E5=AD=97=E7=AC=A6=E8=BD=AC?=
 =?UTF-8?q?=E6=B5=81=E5=85=B3=E9=97=AD=E5=A4=84=E7=90=86=202.=E5=BC=82?=
 =?UTF-8?q?=E5=B8=B8=E4=BF=A1=E6=81=AF=E5=85=B7=E4=BD=93=E5=8C=96?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 .../pay/common/util/sign/encrypt/RSA.java     | 51 ++++++++++---------
 .../pay/common/util/sign/encrypt/RSA2.java    | 10 ++--
 2 files changed, 34 insertions(+), 27 deletions(-)

diff --git a/pay-java-common/src/main/java/com/egzosn/pay/common/util/sign/encrypt/RSA.java b/pay-java-common/src/main/java/com/egzosn/pay/common/util/sign/encrypt/RSA.java
index ca29fea..3bcf772 100644
--- a/pay-java-common/src/main/java/com/egzosn/pay/common/util/sign/encrypt/RSA.java
+++ b/pay-java-common/src/main/java/com/egzosn/pay/common/util/sign/encrypt/RSA.java
@@ -3,6 +3,7 @@ package com.egzosn.pay.common.util.sign.encrypt;
 
 import javax.crypto.Cipher;
 import java.io.*;
+import java.security.GeneralSecurityException;
 import java.security.KeyFactory;
 import java.security.PrivateKey;
 import java.security.PublicKey;
@@ -34,9 +35,9 @@ public class RSA{
 	 */
 	public static String sign(String content, String privateKey, String signAlgorithms, String characterEncoding) {
 		try {
-			PKCS8EncodedKeySpec priPKCS8 = new PKCS8EncodedKeySpec( Base64.decode(privateKey));
-			KeyFactory keyf 			= KeyFactory.getInstance(ALGORITHM);
-			PrivateKey priKey 			= keyf.generatePrivate(priPKCS8);
+			PKCS8EncodedKeySpec priPKCS8 = new PKCS8EncodedKeySpec(Base64.decode(privateKey));
+			KeyFactory keyf = KeyFactory.getInstance(ALGORITHM);
+			PrivateKey priKey = keyf.generatePrivate(priPKCS8);
 
 			java.security.Signature signature = java.security.Signature.getInstance(signAlgorithms);
 
@@ -111,11 +112,11 @@ public class RSA{
 	*/
 	public static boolean verify(String content, String sign, String publicKey, String signAlgorithms, String characterEncoding){
 		try {
-	        PublicKey pubKey 		= getPublicKey(publicKey, ALGORITHM);
+	        PublicKey pubKey = getPublicKey(publicKey, ALGORITHM);
 			java.security.Signature signature = java.security.Signature.getInstance(signAlgorithms);
 			signature.initVerify(pubKey);
-			signature.update( content.getBytes(characterEncoding) );
-			return signature.verify( Base64.decode(sign) );
+			signature.update(content.getBytes(characterEncoding) );
+			return signature.verify(Base64.decode(sign) );
 		} catch (Exception e) {
 			e.printStackTrace();
 		}
@@ -135,8 +136,8 @@ public class RSA{
 		try {
 			java.security.Signature signature = java.security.Signature.getInstance(signAlgorithms);
 			signature.initVerify(publicKey);
-			signature.update( content.getBytes(characterEncoding) );
-			return signature.verify( Base64.decode(sign) );
+			signature.update(content.getBytes(characterEncoding) );
+			return signature.verify(Base64.decode(sign) );
 		} catch (Exception e) {
 			e.printStackTrace();
 		}
@@ -176,9 +177,9 @@ public class RSA{
 	* @return 解密后的字符串
 	 * @throws Exception 解密异常
 	*/
-	public static String decrypt(String content, String privateKey, String characterEncoding) throws Exception {
-        PrivateKey prikey 	= getPrivateKey(privateKey);
-        Cipher cipher 		= Cipher.getInstance(ALGORITHM);
+	public static String decrypt(String content, String privateKey, String characterEncoding) throws GeneralSecurityException, IOException {
+        PrivateKey prikey = getPrivateKey(privateKey);
+        Cipher cipher = Cipher.getInstance(ALGORITHM);
         cipher.init(Cipher.DECRYPT_MODE, prikey);
        try(InputStream ins 	= new ByteArrayInputStream(Base64.decode(content));   ByteArrayOutputStream writer = new ByteArrayOutputStream();) {
 
@@ -204,14 +205,14 @@ public class RSA{
 	   }
     }
 
-	
+
 	/**
 	* 得到私钥
 	* @param key 密钥字符串(经过base64编码)
-	 * @throws Exception 加密异常
+	 * @throws GeneralSecurityException 加密异常
 	 * @return 私钥
 	*/
-	public static PrivateKey getPrivateKey(String key) throws Exception {
+	public static PrivateKey getPrivateKey(String key) throws GeneralSecurityException {
 
 		byte[] keyBytes;
 		keyBytes = Base64.decode(key);
@@ -225,26 +226,30 @@ public class RSA{
 	* 得到公钥
 	* @param key 密钥字符串(经过base64编码)
 	* @param signAlgorithms 密钥类型
-	 * @throws Exception 加密异常
+	 * @throws GeneralSecurityException 加密异常
+	 * @throws IOException 加密异常
 	 * @return 公钥
 	*/
-	public static PublicKey getPublicKey(String key, String signAlgorithms) throws Exception {
-		return getPublicKey(new ByteArrayInputStream(key.getBytes("ISO8859-1")), signAlgorithms);
+	public static PublicKey getPublicKey(String key, String signAlgorithms) throws GeneralSecurityException, IOException {
+		try (ByteArrayInputStream is = new ByteArrayInputStream(key.getBytes("ISO8859-1"))){
+			return getPublicKey(is, signAlgorithms);
+		}
 	}
 
 
 	/**
 	* 得到公钥
 	* @param key 密钥字符串(经过base64编码)
-	 * @throws Exception 加密异常
+	 * @throws GeneralSecurityException 加密异常
+	 * @throws IOException 加密异常
 	 * @return 公钥
 	*/
-	public static PublicKey getPublicKey(String key) throws Exception {
+	public static PublicKey getPublicKey(String key) throws GeneralSecurityException, IOException {
 
 		return getPublicKey(key, ALGORITHM);
 	}
 
-	public static PublicKey getPublicKey(InputStream inputStream, String keyAlgorithm) throws Exception {
+	public static PublicKey getPublicKey(InputStream inputStream, String keyAlgorithm) throws IOException, GeneralSecurityException {
 		try (BufferedReader br = new BufferedReader(new InputStreamReader(inputStream));) {
 			StringBuilder sb = new StringBuilder();
 			String readLine = null;
@@ -262,14 +267,14 @@ public class RSA{
 		}
 	}
 
-	public static byte[] encrypt(byte[] plainBytes, PublicKey publicKey, int keyLength, int reserveSize, String cipherAlgorithm) throws Exception {
+	public static byte[] encrypt(byte[] plainBytes, PublicKey publicKey, int keyLength, int reserveSize, String cipherAlgorithm) throws IOException, GeneralSecurityException {
 		int keyByteSize = keyLength / 8;
 		int encryptBlockSize = keyByteSize - reserveSize;
 		int nBlock = plainBytes.length / encryptBlockSize;
 		if ((plainBytes.length % encryptBlockSize) != 0) {
 			nBlock += 1;
 		}
-		try (ByteArrayOutputStream outbuf = new ByteArrayOutputStream(nBlock * keyByteSize);) {
+		try (ByteArrayOutputStream outbuf = new ByteArrayOutputStream(nBlock * keyByteSize)) {
 			Cipher cipher = Cipher.getInstance(cipherAlgorithm);
 			cipher.init(Cipher.ENCRYPT_MODE, publicKey);
 			for (int offset = 0; offset < plainBytes.length; offset += encryptBlockSize) {
@@ -284,7 +289,7 @@ public class RSA{
 			return outbuf.toByteArray();
 		}
 	}
-	public static String encrypt(String content, String publicKey, String cipherAlgorithm, String characterEncoding ) throws Exception {
+	public static String encrypt(String content, String publicKey, String cipherAlgorithm, String characterEncoding ) throws IOException, GeneralSecurityException {
 		return Base64.encode(RSA.encrypt(content.getBytes(characterEncoding), RSA.getPublicKey(publicKey),1024, 11, cipherAlgorithm));
 	}
 
diff --git a/pay-java-common/src/main/java/com/egzosn/pay/common/util/sign/encrypt/RSA2.java b/pay-java-common/src/main/java/com/egzosn/pay/common/util/sign/encrypt/RSA2.java
index 680b7b0..5aa464d 100644
--- a/pay-java-common/src/main/java/com/egzosn/pay/common/util/sign/encrypt/RSA2.java
+++ b/pay-java-common/src/main/java/com/egzosn/pay/common/util/sign/encrypt/RSA2.java
@@ -1,6 +1,8 @@
 
 package com.egzosn.pay.common.util.sign.encrypt;
 
+import java.io.IOException;
+import java.security.GeneralSecurityException;
 import java.security.PrivateKey;
 import java.security.PublicKey;
 
@@ -63,11 +65,11 @@ public class RSA2 {
 	* @return 解密后的字符串
 	 * @throws Exception 解密异常
 	*/
-	public static String decrypt(String content, String privateKey, String characterEncoding) throws Exception {
+	public static String decrypt(String content, String privateKey, String characterEncoding) throws GeneralSecurityException, IOException {
         return RSA.decrypt(content, privateKey, characterEncoding);
     }
 
-	
+
 	/**
 	* 得到私钥
 	* @param key 密钥字符串(经过base64编码)
@@ -79,7 +81,7 @@ public class RSA2 {
 	}
 
 
-	public static String encrypt(String content, String publicKey, String cipherAlgorithm, String characterEncoding ) throws Exception {
-		return Base64.encode(RSA.encrypt(content.getBytes(characterEncoding), RSA.getPublicKey(publicKey),2048, 11, cipherAlgorithm));
+	public static String encrypt(String content, String publicKey, String cipherAlgorithm, String characterEncoding ) throws GeneralSecurityException, IOException {
+		return Base64.encode(RSA.encrypt(content.getBytes(characterEncoding), RSA.getPublicKey(publicKey), 2048, 11, cipherAlgorithm));
 	}
 }
-- 
Gitee


From 370f7941ec403b523760c93b306dd44bbbb444e3 Mon Sep 17 00:00:00 2001
From: egzosn 
Date: Sun, 17 May 2020 23:03:12 +0800
Subject: [PATCH 116/299] =?UTF-8?q?=E7=A7=BB=E9=99=A4=E8=BF=87=E6=97=B6?=
 =?UTF-8?q?=E7=9A=84=E9=80=80=E6=AC=BE=E6=96=B9=E6=B3=95?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 pay-java-demo/pom.xml                                  |  2 +-
 .../egzosn/pay/demo/controller/AliPayController.java   | 10 +++++++---
 .../com/egzosn/pay/demo/controller/PayController.java  |  8 +++++---
 .../pay/demo/controller/PayPalPayController.java       |  4 +++-
 .../egzosn/pay/demo/controller/UnionPayController.java | 10 ----------
 5 files changed, 16 insertions(+), 18 deletions(-)

diff --git a/pay-java-demo/pom.xml b/pay-java-demo/pom.xml
index e7599d0..3aed877 100644
--- a/pay-java-demo/pom.xml
+++ b/pay-java-demo/pom.xml
@@ -5,7 +5,7 @@
     
         pay-java-parent
         com.egzosn
-        2.13.1
+        2.13.2-SNAPSHOT
     
     4.0.0
     war
diff --git a/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/AliPayController.java b/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/AliPayController.java
index 64e235f..e9f5ee6 100644
--- a/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/AliPayController.java
+++ b/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/AliPayController.java
@@ -7,7 +7,10 @@ import com.egzosn.pay.ali.api.AliPayService;
 import com.egzosn.pay.ali.bean.AliTransactionType;
 import com.egzosn.pay.ali.bean.AliTransferType;
 import com.egzosn.pay.ali.bean.OrderSettle;
-import com.egzosn.pay.common.bean.*;
+import com.egzosn.pay.common.bean.PayOrder;
+import com.egzosn.pay.common.bean.RefundOrder;
+import com.egzosn.pay.common.bean.TransactionType;
+import com.egzosn.pay.common.bean.TransferOrder;
 import com.egzosn.pay.common.http.HttpConfigStorage;
 import com.egzosn.pay.common.http.UriVariables;
 import com.egzosn.pay.common.util.sign.SignUtils;
@@ -17,6 +20,7 @@ import com.egzosn.pay.demo.service.interceptor.AliPayMessageInterceptor;
 import org.springframework.beans.factory.config.AutowireCapableBeanFactory;
 import org.springframework.web.bind.annotation.RequestMapping;
 import org.springframework.web.bind.annotation.RestController;
+
 import javax.annotation.PostConstruct;
 import javax.annotation.Resource;
 import javax.imageio.ImageIO;
@@ -287,8 +291,8 @@ public class AliPayController {
      * @return 返回支付方查询退款后的结果
      */
     @RequestMapping("refundquery")
-    public Map refundquery(QueryOrder order) {
-        return service.refundquery(order.getTradeNo(), order.getOutTradeNo());
+    public Map refundquery(RefundOrder order) {
+        return service.refundquery(order);
     }
 
     /**
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 0705246..c407a58 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
@@ -436,9 +436,11 @@ public class PayController {
      * @return 返回支付方查询退款后的结果
      */
     @RequestMapping("refundquery")
-    public Map refundquery(QueryOrder order) {
-        PayResponse payResponse = service.getPayResponse(order.getPayId());
-        return payResponse.getService().refundquery(order.getTradeNo(), order.getOutTradeNo());
+    public Map refundquery(Integer payId, RefundOrder order) {
+        PayResponse payResponse = service.getPayResponse(payId);
+
+
+        return payResponse.getService().refundquery(order);
     }
 
     /**
diff --git a/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/PayPalPayController.java b/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/PayPalPayController.java
index c2bee1a..f9a7873 100644
--- a/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/PayPalPayController.java
+++ b/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/PayPalPayController.java
@@ -2,7 +2,9 @@ package com.egzosn.pay.demo.controller;
 
 
 import com.egzosn.pay.common.api.PayService;
-import com.egzosn.pay.common.bean.*;
+import com.egzosn.pay.common.bean.DefaultCurType;
+import com.egzosn.pay.common.bean.PayOrder;
+import com.egzosn.pay.common.bean.RefundOrder;
 import com.egzosn.pay.common.http.HttpConfigStorage;
 import com.egzosn.pay.paypal.api.PayPalConfigStorage;
 import com.egzosn.pay.paypal.api.PayPalPayService;
diff --git a/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/UnionPayController.java b/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/UnionPayController.java
index f751cbe..b7b4b61 100644
--- a/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/UnionPayController.java
+++ b/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/UnionPayController.java
@@ -270,16 +270,6 @@ public class UnionPayController {
         return service.refund(order);
     }
 
-    /**
-     * 查询退款
-     *
-     * @param order 订单的请求体
-     * @return 返回支付方查询退款后的结果
-     */
-    @RequestMapping("refundquery")
-    public Map refundquery(QueryOrder order) {
-        return service.refundquery(order.getTradeNo(), order.getOutTradeNo());
-    }
 
     /**
      * 下载对账单
-- 
Gitee


From b17dd02d654defb68fab64d679ceec5c573696ee Mon Sep 17 00:00:00 2001
From: egzosn 
Date: Sun, 17 May 2020 23:34:41 +0800
Subject: [PATCH 117/299] =?UTF-8?q?=E9=80=80=E6=AC=BE=E5=AF=B9=E8=B1=A1?=
 =?UTF-8?q?=E6=96=B0=E5=A2=9E=E9=99=84=E5=8A=A0=E5=B1=9E=E6=80=A7=EF=BC=8C?=
 =?UTF-8?q?=E7=94=A8=E4=BA=8E=E5=85=BC=E5=AE=B9=E6=9B=B4=E5=A4=9A=E7=A7=8D?=
 =?UTF-8?q?=E6=94=AF=E4=BB=98=E5=B9=B3=E5=8F=B0?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 .../com/egzosn/pay/common/bean/Order.java     |  8 +++++
 .../com/egzosn/pay/common/bean/PayOrder.java  |  1 +
 .../egzosn/pay/common/bean/RefundOrder.java   | 31 ++++++++++++++++++-
 .../egzosn/pay/common/bean/TransferOrder.java |  1 +
 pay-java-union/src/test/java/PayTest.java     |  2 +-
 5 files changed, 41 insertions(+), 2 deletions(-)

diff --git a/pay-java-common/src/main/java/com/egzosn/pay/common/bean/Order.java b/pay-java-common/src/main/java/com/egzosn/pay/common/bean/Order.java
index 5f18b00..4ea0d8f 100644
--- a/pay-java-common/src/main/java/com/egzosn/pay/common/bean/Order.java
+++ b/pay-java-common/src/main/java/com/egzosn/pay/common/bean/Order.java
@@ -25,4 +25,12 @@ public interface Order {
      */
     Object getAttr(String key);
 
+
+    /**
+     * 添加订单信息
+     * @param key key
+     * @param value 值
+     */
+    void addAttr(String key, Object value);
+
 }
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 1982763..76a3451 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
@@ -277,6 +277,7 @@ public class PayOrder implements Order {
      * @param key key
      * @param value 值
      */
+    @Override
     public void addAttr(String key, Object value) {
         getAttrs().put(key, value);
     }
diff --git a/pay-java-common/src/main/java/com/egzosn/pay/common/bean/RefundOrder.java b/pay-java-common/src/main/java/com/egzosn/pay/common/bean/RefundOrder.java
index b8bce56..62b8089 100644
--- a/pay-java-common/src/main/java/com/egzosn/pay/common/bean/RefundOrder.java
+++ b/pay-java-common/src/main/java/com/egzosn/pay/common/bean/RefundOrder.java
@@ -2,6 +2,8 @@ package com.egzosn.pay.common.bean;
 
 import java.math.BigDecimal;
 import java.util.Date;
+import java.util.HashMap;
+import java.util.Map;
 
 /**
  * 退款订单信息
@@ -11,7 +13,7 @@ import java.util.Date;
  *      date 2018/1/15 21:40
  *   
*/ -public class RefundOrder { +public class RefundOrder implements Order { /** * 退款单号,每次进行退款的单号,此处唯一 */ @@ -51,6 +53,11 @@ public class RefundOrder { */ private String userId; + /** + * 订单附加信息,可用于预设未提供的参数,这里会覆盖以上所有的订单信息, + */ + private Map attr; + public String getRefundNo() { return refundNo; } @@ -147,5 +154,27 @@ public class RefundOrder { this.totalAmount = totalAmount; } + @Override + public Map getAttrs() { + if (null == attr){ + attr = new HashMap<>(); + } + return attr; + } + + @Override + public Object getAttr(String key) { + return getAttrs().get(key); + } + + /** + * 添加订单信息 + * @param key key + * @param value 值 + */ + @Override + public void addAttr(String key, Object value) { + getAttrs().put(key, value); + } } diff --git a/pay-java-common/src/main/java/com/egzosn/pay/common/bean/TransferOrder.java b/pay-java-common/src/main/java/com/egzosn/pay/common/bean/TransferOrder.java index 83f05b8..bd3ecbb 100644 --- a/pay-java-common/src/main/java/com/egzosn/pay/common/bean/TransferOrder.java +++ b/pay-java-common/src/main/java/com/egzosn/pay/common/bean/TransferOrder.java @@ -217,6 +217,7 @@ public class TransferOrder implements Order { * @param key key * @param value 值 */ + @Override public void addAttr(String key, Object value) { getAttrs().put(key, value); } diff --git a/pay-java-union/src/test/java/PayTest.java b/pay-java-union/src/test/java/PayTest.java index f64b70b..ff2f114 100644 --- a/pay-java-union/src/test/java/PayTest.java +++ b/pay-java-union/src/test/java/PayTest.java @@ -93,7 +93,7 @@ public class PayTest { /*-----------退货交易:后台资金类交易,有同步应答和后台通知应答------------------------------*/ payOrder.setTransactionType(UnionTransactionType.REFUND); - params = service.refund("原交易查询流水号", "订单号", null,new BigDecimal("退款金额" )); + params = service.refund(new RefundOrder("原交易查询流水号", "订单号", null, new BigDecimal("退款金额" ))); /*-----------退货交易:后台资金类交易,有同步应答和后台通知应答------------------------------*/ -- Gitee From dec64805f2a1687c9f4fae9365a18bff57499ec7 Mon Sep 17 00:00:00 2001 From: egzosn Date: Sun, 17 May 2020 23:35:20 +0800 Subject: [PATCH 118/299] =?UTF-8?q?=E9=80=80=E6=AC=BE=E5=AF=B9=E8=B1=A1?= =?UTF-8?q?=E6=96=B0=E5=A2=9E=E9=99=84=E5=8A=A0=E5=B1=9E=E6=80=A7=EF=BC=8C?= =?UTF-8?q?=E7=94=A8=E4=BA=8E=E5=85=BC=E5=AE=B9=E6=9B=B4=E5=A4=9A=E7=A7=8D?= =?UTF-8?q?=E6=94=AF=E4=BB=98=E5=B9=B3=E5=8F=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/egzosn/pay/common/bean/Order.java | 6 +++++- .../java/com/egzosn/pay/common/bean/PayOrder.java | 4 ++-- .../com/egzosn/pay/common/bean/RefundOrder.java | 10 ++++++---- .../com/egzosn/pay/common/bean/TransferOrder.java | 15 +++++++++------ 4 files changed, 22 insertions(+), 13 deletions(-) diff --git a/pay-java-common/src/main/java/com/egzosn/pay/common/bean/Order.java b/pay-java-common/src/main/java/com/egzosn/pay/common/bean/Order.java index 4ea0d8f..fd16e1c 100644 --- a/pay-java-common/src/main/java/com/egzosn/pay/common/bean/Order.java +++ b/pay-java-common/src/main/java/com/egzosn/pay/common/bean/Order.java @@ -15,11 +15,14 @@ public interface Order { /** * 获取订单属性 这里可用做覆盖已设置的订单信息属性,订单信息在签名前进行覆盖。 + * * @return 属性 */ Map getAttrs(); + /** * 获取订单属性 这里可用做覆盖已设置的订单信息属性,订单信息在签名前进行覆盖。 + * * @param key 属性名 * @return 属性 */ @@ -28,7 +31,8 @@ public interface Order { /** * 添加订单信息 - * @param key key + * + * @param key key * @param value 值 */ void addAttr(String key, Object value); 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 76a3451..35987ab 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 @@ -1,12 +1,12 @@ package com.egzosn.pay.common.bean; +import com.egzosn.pay.common.util.str.StringUtils; + import java.math.BigDecimal; import java.util.Date; import java.util.HashMap; import java.util.Map; -import com.egzosn.pay.common.util.str.StringUtils; - /** * 支付订单信息 * diff --git a/pay-java-common/src/main/java/com/egzosn/pay/common/bean/RefundOrder.java b/pay-java-common/src/main/java/com/egzosn/pay/common/bean/RefundOrder.java index 62b8089..30e5959 100644 --- a/pay-java-common/src/main/java/com/egzosn/pay/common/bean/RefundOrder.java +++ b/pay-java-common/src/main/java/com/egzosn/pay/common/bean/RefundOrder.java @@ -7,8 +7,9 @@ import java.util.Map; /** * 退款订单信息 - * @author egan - *
+ *
+ * @author egan
+ * 
  *      email egzosn@gmail.com
  *      date 2018/1/15 21:40
  *   
@@ -156,7 +157,7 @@ public class RefundOrder implements Order { @Override public Map getAttrs() { - if (null == attr){ + if (null == attr) { attr = new HashMap<>(); } return attr; @@ -170,7 +171,8 @@ public class RefundOrder implements Order { /** * 添加订单信息 - * @param key key + * + * @param key key * @param value 值 */ @Override diff --git a/pay-java-common/src/main/java/com/egzosn/pay/common/bean/TransferOrder.java b/pay-java-common/src/main/java/com/egzosn/pay/common/bean/TransferOrder.java index bd3ecbb..73a1162 100644 --- a/pay-java-common/src/main/java/com/egzosn/pay/common/bean/TransferOrder.java +++ b/pay-java-common/src/main/java/com/egzosn/pay/common/bean/TransferOrder.java @@ -6,6 +6,7 @@ import java.util.Map; /** * 转账订单 + * * @author egan *
  * email egzosn@gmail.com
@@ -27,12 +28,12 @@ public class TransferOrder implements Order {
     /**
      * 收款方账户, 用户openid,卡号等等
      */
-    private String  payeeAccount ;
+    private String payeeAccount;
 
     /**
      * 转账金额
      */
-    private BigDecimal amount ;
+    private BigDecimal amount;
 
     /**
      * 付款人名称
@@ -55,11 +56,11 @@ public class TransferOrder implements Order {
 
     /**
      * 收款开户行
-      */
+     */
     private Bank bank;
 
     /**
-     *  收款开户行地址
+     * 收款开户行地址
      */
     private String payeeBankAddress;
 
@@ -198,9 +199,10 @@ public class TransferOrder implements Order {
     public void setIp(String ip) {
         this.ip = ip;
     }
+
     @Override
     public Map getAttrs() {
-        if (null == attr){
+        if (null == attr) {
             attr = new HashMap<>();
         }
         return attr;
@@ -214,7 +216,8 @@ public class TransferOrder implements Order {
 
     /**
      * 添加订单信息
-     * @param key key
+     *
+     * @param key   key
      * @param value 值
      */
     @Override
-- 
Gitee


From c31f9184789d06dacf20b4991e8d09f4d6d0a57a Mon Sep 17 00:00:00 2001
From: egzosn 
Date: Sun, 17 May 2020 23:36:00 +0800
Subject: [PATCH 119/299] =?UTF-8?q?=E7=99=BE=E5=BA=A6=E6=94=AF=E4=BB=98?=
 =?UTF-8?q?=E9=80=80=E6=AC=BE=E5=85=BC=E5=AE=B9pay-java=E6=8E=A5=E5=8F=A3?=
 =?UTF-8?q?=E5=AE=9A=E4=B9=89?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 .../egzosn/pay/baidu/api/BaiduPayService.java | 206 ++++++++----------
 .../pay/baidu/bean/BaiduRefundOrder.java      |  86 ++++----
 2 files changed, 127 insertions(+), 165 deletions(-)

diff --git a/pay-java-baidu/src/main/java/com/egzosn/pay/baidu/api/BaiduPayService.java b/pay-java-baidu/src/main/java/com/egzosn/pay/baidu/api/BaiduPayService.java
index ef1b1cb..87e4c1c 100644
--- a/pay-java-baidu/src/main/java/com/egzosn/pay/baidu/api/BaiduPayService.java
+++ b/pay-java-baidu/src/main/java/com/egzosn/pay/baidu/api/BaiduPayService.java
@@ -38,25 +38,25 @@ public class BaiduPayService extends BasePayService {
     public static final String SIGN = "sign";
     public static final String METHOD = "method";
     public static final String TYPE = "type";
-    
+
     public static final Integer RESPONSE_SUCCESS = 2;
     public static final String RESPONSE_STATUS = "status";
-    
-    
+
+
     public BaiduPayService(BaiduPayConfigStorage payConfigStorage) {
         super(payConfigStorage);
     }
-    
+
     public BaiduPayService(BaiduPayConfigStorage payConfigStorage,
                            HttpConfigStorage configStorage) {
         super(payConfigStorage, configStorage);
     }
-    
+
     /**
      * 验证响应
      *
      * @param params 回调回来的参数集
-     * @return
+     * @return 结果
      */
     @Override
     public boolean verify(Map params) {
@@ -65,13 +65,13 @@ public class BaiduPayService extends BasePayService {
         }
         return signVerify(params, String.valueOf(params.get(RSA_SIGN))) && verifySource(String.valueOf(params.get(TP_ORDER_ID)));
     }
-    
+
     /**
      * 验证签名
      *
      * @param params 参数集
      * @param sign   签名原文
-     * @return
+     * @return 结果
      */
     @Override
     public boolean signVerify(Map params, String sign) {
@@ -80,7 +80,7 @@ public class BaiduPayService extends BasePayService {
         LOG.debug("百度返回的签名: " + rsaSign + " 本地产生的签名: " + targetRsaSign);
         return StringUtils.equals(rsaSign, targetRsaSign);
     }
-    
+
     @Override
     public boolean verifySource(String id) {
         return true;
@@ -90,7 +90,7 @@ public class BaiduPayService extends BasePayService {
      * 返回创建的订单信息
      *
      * @param order 支付订单
-     * @return
+     * @return 结果
      */
     @Override
     public Map orderInfo(PayOrder order) {
@@ -99,11 +99,11 @@ public class BaiduPayService extends BasePayService {
         params.put(RSA_SIGN, rsaSign);
         return params;
     }
-    
+
     /**
      * 获取"查询支付状态"所需参数
      *
-     * @return
+     * @return 结果
      */
     public Map getUseQueryPay() {
         String appKey = payConfigStorage.getAppKey();
@@ -112,12 +112,12 @@ public class BaiduPayService extends BasePayService {
         result.put(APP_ID, payConfigStorage.getAppid());
         return result;
     }
-    
+
     /**
      * 获取"创建订单"所需参数
      *
-     * @param order
-     * @return
+     * @param order 订单信息
+     * @return 结果
      */
     private Map getUseOrderInfoParams(PayOrder order) {
         BaiduPayOrder payOrder = (BaiduPayOrder) order;
@@ -131,32 +131,32 @@ public class BaiduPayService extends BasePayService {
         result.put(SIGN_FIELDS_RANGE, payOrder.getSignFieldsRange());
         result.put(BIZ_INFO, JSON.toJSONString(payOrder.getBizInfo()));
         result.put(TOTAL_AMOUNT, String.valueOf(Util.conversionAmount(order.getPrice())));
-        
+
         return result;
     }
-    
+
     /**
      * 获取输出消息,用户返回给支付端
      *
      * @param code    状态
      * @param message 消息
-     * @return
+     * @return 结果
      */
     @Override
     @Deprecated
     public PayOutMessage getPayOutMessage(String code, String message) {
         throw new UnsupportedOperationException("请使用 " + getClass().getName() + "#getPayOutMessageUseBaidu");
     }
-    
+
     /**
      * 请求业务方退款审核/响应处理
      * http://smartprogram.baidu.com/docs/develop/function/tune_up_examine/
      *
-     * @param errno
-     * @param message
-     * @param auditStatus
-     * @param refundPayMoney
-     * @return
+     * @param errno          错误代码
+     * @param message        消息
+     * @param auditStatus    状态
+     * @param refundPayMoney 退款金额
+     * @return 结果
      */
     public PayOutMessage getApplyRefundOutMessageUseBaidu(Integer errno,
                                                           String message,
@@ -172,16 +172,16 @@ public class BaiduPayService extends BasePayService {
                 .content("message", message)
                 .content("data", data)
                 .build();
-        
+
     }
-    
+
     /**
      * 通知退款状态/响应处理
      * http://smartprogram.baidu.com/docs/develop/function/tune_up_drawback/
      *
-     * @param errno
-     * @param message
-     * @return
+     * @param errno   错误代码
+     * @param message 消息
+     * @return 结果
      */
     public PayOutMessage getRefundOutMessageUseBaidu(Integer errno,
                                                      String message) {
@@ -190,17 +190,17 @@ public class BaiduPayService extends BasePayService {
                 .content("message", message)
                 .content("data", "{}")
                 .build();
-        
+
     }
-    
+
     /**
      * 支付通知/响应处理
      *
-     * @param errno
-     * @param message
-     * @param isConsumed
-     * @param isErrorOrder
-     * @return
+     * @param errno        错误代码
+     * @param message      消息
+     * @param isConsumed   是否消费
+     * @param isErrorOrder 错误订单
+     * @return 结果
      */
     public PayOutMessage getPayOutMessageUseBaidu(Integer errno,
                                                   String message,
@@ -220,157 +220,125 @@ public class BaiduPayService extends BasePayService {
                 .content("data", data)
                 .build();
     }
-    
+
     /**
      * 支付通知/响应处理
      * http://smartprogram.baidu.com/docs/develop/function/tune_up_notice/
      *
-     * @param code
-     * @param message
-     * @param isConsumed
-     * @return
+     * @param code       状态码
+     * @param message    消息
+     * @param isConsumed 是否消费
+     * @return 结果
      */
     public PayOutMessage getPayOutMessageUseBaidu(Integer code,
                                                   String message,
                                                   Integer isConsumed) {
         return getPayOutMessageUseBaidu(code, message, isConsumed, null);
     }
-    
+
     /**
      * 支付通知/响应处理
      * http://smartprogram.baidu.com/docs/develop/function/tune_up_notice/
      *
      * @param payMessage 支付回调消息
-     * @return
+     * @return 结果
      */
     @Override
     public PayOutMessage successPayOutMessage(PayMessage payMessage) {
         return getPayOutMessageUseBaidu(0, "success", 2);
     }
-    
+
     /**
      * 获取输出消息,用户返回给支付端, 针对于web端
      *
      * @param orderInfo 发起支付的订单信息
      * @param method    请求方式  "post" "get",
-     * @return
+     * @return 结果
      */
     @Override
-    @Deprecated
     public String buildRequest(Map orderInfo,
                                MethodType method) {
         throw new UnsupportedOperationException("百度不支持PC支付");
     }
 
 
-
     /**
      * 百度不支持扫码付
      *
      * @param order 发起支付的订单信息
-     * @return
+     * @return 结果
      */
     @Override
     public String getQrPay(PayOrder order) {
         throw new UnsupportedOperationException("百度不支持扫码付");
     }
-    
+
     /**
      * 百度不支持刷卡付
      *
      * @param order 发起支付的订单信息
-     * @return
+     * @return 结果
      */
     @Override
     public Map microPay(PayOrder order) {
         throw new UnsupportedOperationException("百度不支持刷卡付");
     }
-    
+
     /**
      * 查询订单
      *
      * @param tradeNo    支付平台订单号
      * @param outTradeNo 商户单号
-     * @return
+     * @return 结果
      */
     @Override
     public Map query(String tradeNo, String outTradeNo) {
         return secondaryInterface(tradeNo, outTradeNo, BaiduTransactionType.PAY_QUERY);
     }
-    
+
     /**
      * 百度不支持该操作
      *
      * @param tradeNo    支付平台订单号
      * @param outTradeNo 商户单号
-     * @return
+     * @return 结果
      */
     @Override
-    @Deprecated
     public Map close(String tradeNo, String outTradeNo) {
         throw new UnsupportedOperationException("不支持该操作");
     }
 
-    
+
     /**
      * 退款
      *
-     * @param orderId
-     * @param userId
-     * @param refundType
-     * @param tpOrderId
-     * @param refundReason
-     * @return
-     */
-    public Map refundUseBaidu(Long orderId,
-                                              Long userId,
-                                              Integer refundType,
-                                              String tpOrderId,
-                                              String refundReason) {
-        return refundUseBaidu(new BaiduRefundOrder(orderId, userId, refundType, refundReason, tpOrderId));
-    }
-    
-    /**
-     * 退款, 请使用 {@link com.egzosn.pay.baidu.api.BaiduPayService#refundUseBaidu}
-     *
      * @param refundOrder 退款订单信息
-     * @return
+     * @return 退款结果
      */
     @Override
-    @Deprecated
     public Map refund(RefundOrder refundOrder) {
-        throw new UnsupportedOperationException("请使用 " + getClass().getName() + "#refundUseBaidu");
-    }
-    
-    /**
-     * 退款, 请使用 {@link com.egzosn.pay.baidu.api.BaiduPayService#refundUseBaidu}
-     *
-     * @param refundOrder
-     * @return
-     */
-    public Map refundUseBaidu(BaiduRefundOrder refundOrder) {
         Map parameters = getUseQueryPay();
         BaiduTransactionType transactionType = BaiduTransactionType.APPLY_REFUND;
         parameters.put(METHOD, transactionType.getMethod());
-        parameters.put(ORDER_ID, refundOrder.getTradeNo());
+        parameters.put(ORDER_ID, refundOrder.getOutTradeNo());
         parameters.put(USER_ID, refundOrder.getUserId());
-        parameters.put("refundType", refundOrder.getRefundType());
-        parameters.put("refundReason", String.valueOf(refundOrder.getRefundReason()));
-        parameters.put(TP_ORDER_ID, refundOrder.getTpOrderId());
-        parameters.put("applyRefundMoney", refundOrder.getApplyRefundMoney());
-        parameters.put("bizRefundBatchId", refundOrder.getBizRefundBatchId());
+        setParameters(parameters, "refundType", refundOrder);
+        parameters.put("refundReason", refundOrder.getDescription());
+        parameters.put(TP_ORDER_ID, refundOrder.getTradeNo());
+        parameters.put("applyRefundMoney", refundOrder.getRefundAmount());
+        parameters.put("bizRefundBatchId", refundOrder.getRefundNo());
         parameters.put(APP_KEY, payConfigStorage.getAppKey());
         parameters.put(RSA_SIGN, getRsaSign(parameters, RSA_SIGN));
         return requestTemplate.getForObject(String.format("%s?%s", getReqUrl(transactionType), UriVariables.getMapToParameters(parameters)), JSONObject.class);
+
     }
-    
 
-    
+
     /**
      * 退费查询
      *
      * @param refundOrder 退款订单单号信息
-     * @return
+     * @return 退款查询结果
      */
     @Override
     public Map refundquery(RefundOrder refundOrder) {
@@ -385,45 +353,45 @@ public class BaiduPayService extends BasePayService {
         parameters.put(RSA_SIGN, getRsaSign(parameters, RSA_SIGN));
         return requestTemplate.getForObject(String.format("%s?%s", getReqUrl(transactionType), UriVariables.getMapToParameters(parameters)), JSONObject.class);
     }
-    
+
     /**
      * 下载资金账单
      *
-     * @param billDate     账单时间:日账单格式为yyyy-MM-dd,月账单格式为yyyy-MM。
-     * @param access_token
-     * @return
+     * @param billDate    账单时间:日账单格式为yyyy-MM-dd
+     * @param accessToken 用户token
+     * @return 对账单
      */
     @Override
-    public Map downloadbill(Date billDate, String access_token) {
+    public Map downloadbill(Date billDate, String accessToken) {
         Map parameters = new HashMap<>();
-        parameters.put("access_token", access_token);
+        parameters.put("access_token", accessToken);
         parameters.put("billTime", DateUtils.formatDay(billDate));
         return requestTemplate.getForObject(String.format("%s?%s", getReqUrl(BaiduTransactionType.DOWNLOAD_BILL),
                 UriVariables.getMapToParameters(parameters)), JSONObject.class);
     }
-    
+
     /**
      * 下载订单对账单
      *
-     * @param billDate
-     * @param access_token
-     * @return
+     * @param billDate    账单时间:日账单格式为yyyy-MM-dd
+     * @param accessToken 用户token
+     * @return 账单结果
      */
-    public Map downloadOrderBill(Date billDate, String access_token) {
+    public Map downloadOrderBill(Date billDate, String accessToken) {
         Map parameters = new HashMap<>();
-        parameters.put("access_token", access_token);
+        parameters.put("access_token", accessToken);
         parameters.put("billTime", DateUtils.formatDay(billDate));
         return requestTemplate.getForObject(String.format("%s?%s", getReqUrl(BaiduTransactionType.DOWNLOAD_ORDER_BILL),
                 UriVariables.getMapToParameters(parameters)), JSONObject.class);
     }
-    
+
     /**
      * 通用查询接口
      *
-     * @param orderId
-     * @param siteId
+     * @param orderId         订单id
+     * @param siteId          用户id
      * @param transactionType 交易类型
-     * @return
+     * @return 结果
      */
     @Override
     public Map secondaryInterface(Object orderId,
@@ -432,34 +400,34 @@ public class BaiduPayService extends BasePayService {
         if (!BaiduTransactionType.PAY_QUERY.equals(transactionType)) {
             throw new UnsupportedOperationException("不支持该操作");
         }
-        
+
         Map parameters = getUseQueryPay();
         parameters.put(ORDER_ID, orderId);
         parameters.put(SITE_ID, siteId);
         parameters.put(SIGN, getRsaSign(parameters, SIGN));
         return requestTemplate.getForObject(String.format("%s?%s", getReqUrl(transactionType), UriVariables.getMapToParameters(parameters)), JSONObject.class);
     }
-    
+
     /**
      * 获取支付请求地址
      *
      * @param transactionType 交易类型
-     * @return
+     * @return 请求URL
      */
     @Override
     public String getReqUrl(TransactionType transactionType) {
         return ((BaiduTransactionType) transactionType).getUrl();
     }
-    
+
     /**
      * 签名
      *
-     * @param params
-     * @param ignoreKeys
-     * @return
+     * @param params     参数
+     * @param ignoreKeys 忽略字段
+     * @return 签名结果
      */
     private String getRsaSign(Map params, String... ignoreKeys) {
         String waitSignVal = SignUtils.parameterText(params, "&", false, ignoreKeys);
-        return SignUtils.RSA.createSign(waitSignVal, payConfigStorage.getKeyPrivate(), "UTF-8");
+        return SignUtils.valueOf(payConfigStorage.getSignType()).createSign(waitSignVal, payConfigStorage.getKeyPrivate(), payConfigStorage.getInputCharset());
     }
 }
diff --git a/pay-java-baidu/src/main/java/com/egzosn/pay/baidu/bean/BaiduRefundOrder.java b/pay-java-baidu/src/main/java/com/egzosn/pay/baidu/bean/BaiduRefundOrder.java
index 6386be8..91d6510 100644
--- a/pay-java-baidu/src/main/java/com/egzosn/pay/baidu/bean/BaiduRefundOrder.java
+++ b/pay-java-baidu/src/main/java/com/egzosn/pay/baidu/bean/BaiduRefundOrder.java
@@ -5,73 +5,67 @@ import com.egzosn.pay.common.bean.RefundOrder;
 import java.math.BigDecimal;
 
 public class BaiduRefundOrder extends RefundOrder {
-    /**
-     * 平台用户ID
-     */
-    private Long userId;
+
     /**
      * 退款类型
      */
     private Integer refundType;
-    /**
-     * 退款原因
-     */
-    private String refundReason;
-    /**
-     * 平台订单ID
-     */
-    private String tpOrderId;
-    /**
-     * 退款金额,单位:分,发起部分退款时必传
-     */
-    private BigDecimal applyRefundMoney;
-    /**
-     * 业务方退款批次id,退款业务流水唯一编号,发起部分退款时必传
-     */
-    private String bizRefundBatchId;
-    
+
+
     public BaiduRefundOrder(Long orderId,
-                            Long userId,
+                            String userId,
                             Integer refundType,
                             String refundReason,
                             String tpOrderId) {
         super();
-        setTradeNo(String.valueOf(orderId));
-        this.userId = userId;
-        this.refundType = refundType;
-        this.refundReason = refundReason;
-        this.tpOrderId = tpOrderId;
+        setOutTradeNo(String.valueOf(orderId));
+        setUserId(userId);
+        setRefundType(refundType);
+        setDescription(refundReason);
+        setTradeNo(tpOrderId);
     }
-    
+
+    /**
+     * 退款金额,单位:分,发起部分退款时必传
+     *
+     * @return 退款金额
+     */
     public BigDecimal getApplyRefundMoney() {
-        return applyRefundMoney;
+        return getRefundAmount();
     }
-    
+
+    /**
+     * 退款金额,单位:分,发起部分退款时必传
+     *
+     * @param applyRefundMoney 退款金额
+     */
     public void setApplyRefundMoney(BigDecimal applyRefundMoney) {
         setRefundAmount(applyRefundMoney);
     }
-    
+
+    /**
+     * 业务方退款批次id,退款业务流水唯一编号,发起部分退款时必传
+     *
+     * @return 退款业务流水
+     */
     public String getBizRefundBatchId() {
-        return bizRefundBatchId;
+        return getRefundNo();
     }
-    
+
+    /**
+     * 业务方退款批次id,退款业务流水唯一编号,发起部分退款时必传
+     */
     public void setBizRefundBatchId(String bizRefundBatchId) {
-        this.bizRefundBatchId = bizRefundBatchId;
+        setRefundNo(bizRefundBatchId);
     }
-    
-    public Long getUserId() {
-        return userId;
+
+    public void setRefundType(Integer refundType) {
+        this.refundType = refundType;
+        addAttr("refundType", refundType);
     }
-    
+
     public Integer getRefundType() {
         return refundType;
     }
-    
-    public String getRefundReason() {
-        return refundReason;
-    }
-    
-    public String getTpOrderId() {
-        return tpOrderId;
-    }
+
 }
-- 
Gitee


From 7f90f489ce96de464b16133b43ed3fe613b1d06a Mon Sep 17 00:00:00 2001
From: egzosn 
Date: Sun, 17 May 2020 23:36:25 +0800
Subject: [PATCH 120/299] =?UTF-8?q?=E5=BE=AE=E4=BF=A1=E5=8F=91=E7=BA=A2?=
 =?UTF-8?q?=E5=8C=85=E6=8E=A5=E5=8F=A3=E5=AE=9E=E7=8E=B0?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 .../pay/demo/controller/WxPayController.java  |  66 ++++++--
 .../com/egzosn/pay/wx/api/WxPayService.java   | 160 ++++++------------
 .../egzosn/pay/wx/api/WxRedPackService.java   |  33 ++++
 .../com/egzosn/pay/wx/bean/RedpackOrder.java  | 156 +++++++----------
 .../egzosn/pay/wx/bean/WxSendredpackType.java |   2 +-
 5 files changed, 202 insertions(+), 215 deletions(-)
 create mode 100644 pay-java-wx/src/main/java/com/egzosn/pay/wx/api/WxRedPackService.java

diff --git a/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/WxPayController.java b/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/WxPayController.java
index 4f72fb3..ef58916 100644
--- a/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/WxPayController.java
+++ b/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/WxPayController.java
@@ -2,27 +2,16 @@
 package com.egzosn.pay.demo.controller;
 
 
-import com.egzosn.pay.common.api.Callback;
-import com.egzosn.pay.common.api.PayService;
 import com.egzosn.pay.common.bean.*;
 import com.egzosn.pay.common.http.HttpConfigStorage;
-import com.egzosn.pay.common.http.UriVariables;
-import com.egzosn.pay.demo.entity.PayType;
 import com.egzosn.pay.demo.request.QueryOrder;
-import com.egzosn.pay.demo.service.PayResponse;
-import com.egzosn.pay.demo.service.handler.AliPayMessageHandler;
-import com.egzosn.pay.demo.service.handler.WxPayMessageHandler;
 import com.egzosn.pay.wx.api.WxPayConfigStorage;
 import com.egzosn.pay.wx.api.WxPayService;
-import com.egzosn.pay.wx.bean.WxBank;
-import com.egzosn.pay.wx.bean.WxTransactionType;
-import com.egzosn.pay.wx.bean.WxTransferType;
-import org.springframework.beans.factory.config.AutowireCapableBeanFactory;
+import com.egzosn.pay.wx.bean.*;
 import org.springframework.web.bind.annotation.RequestMapping;
 import org.springframework.web.bind.annotation.RestController;
 
 import javax.annotation.PostConstruct;
-import javax.annotation.Resource;
 import javax.imageio.ImageIO;
 import javax.servlet.http.HttpServletRequest;
 import java.io.ByteArrayOutputStream;
@@ -43,7 +32,7 @@ import java.util.UUID;
 @RequestMapping("wx")
 public class WxPayController {
 
-    private PayService service = null;
+    private WxPayService service = null;
 
 
 
@@ -334,8 +323,8 @@ public class WxPayController {
      * @return 返回支付方查询退款后的结果
      */
     @RequestMapping("refundquery")
-    public Map refundquery(QueryOrder order) {
-        return service.refundquery(order.getTradeNo(), order.getOutTradeNo());
+    public Map refundquery(RefundOrder order) {
+        return service.refundquery(order);
     }
 
     /**
@@ -428,4 +417,51 @@ public class WxPayController {
        //默认查询银行卡的记录 com.egzosn.pay.wx.bean.WxTransferType#QUERY_BANK
         return service.transferQuery(outNo, wxTransferType);
     }
+
+    /**
+     * 微信发红包
+     * @param redpackOrder 红包订单
+     * @return 结果
+     */
+    public Map sendredpack(RedpackOrder redpackOrder) {
+        redpackOrder.setTransferType(WxSendredpackType.SENDREDPACK);
+        return service.sendredpack(redpackOrder);
+    }
+
+    /**
+     * 发放裂变红包
+     * @param redpackOrder 红包订单
+     * @return 结果
+     */
+    public Map sendgroupredpack(RedpackOrder redpackOrder) {
+        redpackOrder.setTransferType(WxSendredpackType.SENDGROUPREDPACK);
+        return service.sendredpack(redpackOrder);
+    }
+
+
+    /**
+     * 小程序发红包
+     * @param redpackOrder 红包订单
+     * @return 结果
+     */
+    public Map sendminiprogramhb(RedpackOrder redpackOrder) {
+        redpackOrder.setTransferType(WxSendredpackType.SENDMINIPROGRAMHB);
+        return service.sendredpack(redpackOrder);
+    }
+
+
+    /**
+     * 查询红包记录
+     * 用于商户对已发放的红包进行查询红包的具体信息,可支持普通红包和裂变包
+     * 查询红包记录API只支持查询30天内的红包订单,30天之前的红包订单请登录商户平台查询。
+     *
+     * @param mchBillno 商户发放红包的商户订单号
+     * @return 返回查询结果
+     */
+    public Map gethbinfo(String mchBillno) {
+        return service.gethbinfo(mchBillno);
+    }
+
+
+
 }
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 a69bff2..b882ddc 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
@@ -2,23 +2,22 @@ package com.egzosn.pay.wx.api;
 
 import com.alibaba.fastjson.JSONObject;
 import com.egzosn.pay.common.api.BasePayService;
-import com.egzosn.pay.common.api.Callback;
 import com.egzosn.pay.common.bean.*;
 import com.egzosn.pay.common.bean.result.PayException;
 import com.egzosn.pay.common.exception.PayErrorException;
 import com.egzosn.pay.common.http.HttpConfigStorage;
 import com.egzosn.pay.common.util.DateUtils;
 import com.egzosn.pay.common.util.Util;
+import com.egzosn.pay.common.util.XML;
 import com.egzosn.pay.common.util.sign.SignUtils;
 import com.egzosn.pay.common.util.sign.encrypt.RSA2;
 import com.egzosn.pay.common.util.str.StringUtils;
 import com.egzosn.pay.wx.bean.*;
-import com.egzosn.pay.common.util.XML;
 
 import java.io.IOException;
 import java.io.InputStream;
-import java.math.BigDecimal;
 import java.net.URLEncoder;
+import java.security.GeneralSecurityException;
 import java.util.*;
 
 import static com.egzosn.pay.wx.api.WxConst.*;
@@ -29,13 +28,11 @@ import static com.egzosn.pay.wx.bean.WxTransferType.*;
  *
  * @author egan
  * 
- *                 email egzosn@gmail.com
- *                 date 2016-5-18 14:09:01
- *                 
+ * email egzosn@gmail.com + * date 2016-5-18 14:09:01 + *
*/ -public class WxPayService extends BasePayService { - - +public class WxPayService extends BasePayService implements WxRedPackService { /** @@ -94,15 +91,15 @@ public class WxPayService extends BasePayService { public boolean verify(Map params) { if (!(SUCCESS.equals(params.get(RETURN_CODE)) && SUCCESS.equals(params.get(RESULT_CODE)))) { - if (LOG.isErrorEnabled()){ - LOG.error(String.format("微信支付异常:return_code=%s,参数集=%s", params.get(RETURN_CODE), params)); + if (LOG.isErrorEnabled()) { +LOG.error(String.format("微信支付异常:return_code=%s,参数集=%s", params.get(RETURN_CODE), params)); } return false; } if (null == params.get(SIGN)) { - if (LOG.isDebugEnabled()){ - LOG.debug(String.format("微信支付异常:签名为空!%s=%s", OUT_TRADE_NO, params.get(OUT_TRADE_NO))); + if (LOG.isDebugEnabled()) { +LOG.debug(String.format("微信支付异常:签名为空!%s=%s", OUT_TRADE_NO, params.get(OUT_TRADE_NO))); } return false; } @@ -243,24 +240,24 @@ public class WxPayService extends BasePayService { if (verify(preOrderHandler(result, order))) { //如果是扫码支付或者刷卡付无需处理,直接返回 if (((WxTransactionType) order.getTransactionType()).isReturn()) { - return result; +return result; } Map params = new TreeMap(); if (WxTransactionType.JSAPI == order.getTransactionType()) { - params.put("signType", payConfigStorage.getSignType()); - params.put("appId", payConfigStorage.getAppid()); - params.put("timeStamp", System.currentTimeMillis() / 1000); - params.put("nonceStr", result.get(NONCE_STR)); - params.put("package", "prepay_id=" + result.get("prepay_id")); +params.put("signType", payConfigStorage.getSignType()); +params.put("appId", payConfigStorage.getAppid()); +params.put("timeStamp", System.currentTimeMillis() / 1000); +params.put("nonceStr", result.get(NONCE_STR)); +params.put("package", "prepay_id=" + result.get("prepay_id")); } else if (WxTransactionType.APP == order.getTransactionType()) { - params.put("partnerid", payConfigStorage.getPid()); - params.put(APPID, payConfigStorage.getAppid()); - params.put("prepayid", result.get("prepay_id")); - params.put("timestamp", System.currentTimeMillis() / 1000); - params.put("noncestr", result.get(NONCE_STR)); - params.put("package", "Sign=WXPay"); +params.put("partnerid", payConfigStorage.getPid()); +params.put(APPID, payConfigStorage.getAppid()); +params.put("prepayid", result.get("prepay_id")); +params.put("timestamp", System.currentTimeMillis() / 1000); +params.put("noncestr", result.get(NONCE_STR)); +params.put("package", "Sign=WXPay"); } String paySign = createSign(SignUtils.parameterText(params), payConfigStorage.getInputCharset()); params.put(SIGN, paySign); @@ -480,23 +477,6 @@ public class WxPayService extends BasePayService { return secondaryInterface(transactionId, outTradeNo, WxTransactionType.REVERSE); } - /** - * 退款 - * - * @param transactionId 微信订单号 - * @param outTradeNo 商户单号 - * @param refundAmount 退款金额 - * @param totalAmount 总金额 - * @return 返回支付方申请退款后的结果 - * @see #refund(RefundOrder, Callback) - */ - @Deprecated - @Override - public Map refund(String transactionId, String outTradeNo, BigDecimal refundAmount, BigDecimal totalAmount) { - - return refund(new RefundOrder(transactionId, outTradeNo, refundAmount, totalAmount)); - } - @@ -527,18 +507,6 @@ public class WxPayService extends BasePayService { } - /** - * 查询退款 - * - * @param transactionId 支付平台订单号 - * @param outTradeNo 商户单号 - * @return 返回支付方查询退款后的结果 - */ - @Override - public Map refundquery(String transactionId, String outTradeNo) { - return secondaryInterface(transactionId, outTradeNo, WxTransactionType.REFUNDQUERY); - } - /** * 查询退款 * @@ -607,7 +575,7 @@ public class WxPayService extends BasePayService { if (transactionType == WxTransactionType.DOWNLOADBILL) { if (transactionIdOrBillDate instanceof Date) { - return downloadbill((Date) transactionIdOrBillDate, outTradeNoBillType); +return downloadbill((Date) transactionIdOrBillDate, outTradeNoBillType); } throw new PayErrorException(new PayException(FAILURE, "非法类型异常:" + transactionIdOrBillDate.getClass())); } @@ -631,13 +599,12 @@ public class WxPayService extends BasePayService { * @param order 转账订单 *
      *
-     *                                        注意事项:
-     *                                        ◆ 当返回错误码为“SYSTEMERROR”时,请不要更换商户订单号,一定要使用原商户订单号重试,否则可能造成重复支付等资金风险。
-     *                                        ◆ XML具有可扩展性,因此返回参数可能会有新增,而且顺序可能不完全遵循此文档规范,如果在解析回包的时候发生错误,请商户务必不要换单重试,请商户联系客服确认付款情况。如果有新回包字段,会更新到此API文档中。
-     *                                        ◆ 因为错误代码字段err_code的值后续可能会增加,所以商户如果遇到回包返回新的错误码,请商户务必不要换单重试,请商户联系客服确认付款情况。如果有新的错误码,会更新到此API文档中。
-     *                                        ◆ 错误代码描述字段err_code_des只供人工定位问题时做参考,系统实现时请不要依赖这个字段来做自动化处理。
-     *
-     *                                        
+ *注意事项: + *◆ 当返回错误码为“SYSTEMERROR”时,请不要更换商户订单号,一定要使用原商户订单号重试,否则可能造成重复支付等资金风险。 + *◆ XML具有可扩展性,因此返回参数可能会有新增,而且顺序可能不完全遵循此文档规范,如果在解析回包的时候发生错误,请商户务必不要换单重试,请商户联系客服确认付款情况。如果有新回包字段,会更新到此API文档中。 + *◆ 因为错误代码字段err_code的值后续可能会增加,所以商户如果遇到回包返回新的错误码,请商户务必不要换单重试,请商户联系客服确认付款情况。如果有新的错误码,会更新到此API文档中。 + *◆ 错误代码描述字段err_code_des只供人工定位问题时做参考,系统实现时请不要依赖这个字段来做自动化处理。 + *
* @return 对应的转账结果 */ @Override @@ -711,11 +678,11 @@ public class WxPayService extends BasePayService { * * @param outNo 商户转账订单号 * @param wxTransferType 微信转账类型,.....这里没办法了只能这样写(┬_┬),请见谅 {@link com.egzosn.pay.wx.bean.WxTransferType} - *

- *

- * 企业付款到零钱 - * 商户企业付款到银行卡 - *

+ *

+ *

+ * 企业付款到零钱 + * 商户企业付款到银行卡 + *

* @return 对应的转账订单 */ @Override @@ -740,7 +707,7 @@ public class WxPayService extends BasePayService { public String keyPublic(String content) { try { return RSA2.encrypt(content, payConfigStorage.getKeyPublic(), CIPHER_ALGORITHM, payConfigStorage.getInputCharset()); - } catch (Exception e) { + } catch (GeneralSecurityException | IOException e) { throw new PayErrorException(new WxPayError(FAILURE, e.getLocalizedMessage())); } } @@ -758,70 +725,51 @@ public class WxPayService extends BasePayService { /** * 微信发红包 - * @author: faymanwang 1057438332@qq.com + * * @param redpackOrder 红包实体 * @return 返回发红包实体后的结果 + * @author: faymanwang 1057438332@qq.com */ + @Override public Map sendredpack(RedpackOrder redpackOrder) { Map parameters = new TreeMap(); - redpackParam(redpackOrder, parameters); - parameters.put(SIGN, createSign(SignUtils.parameterText(parameters, "&", SIGN), payConfigStorage.getInputCharset())); - return requestTemplate.postForObject(getReqUrl( WxSendredpackType.SENDREDPACK), XML.getMap2Xml(parameters) , JSONObject.class); - } + redPackParam(redpackOrder, parameters); + if (WxSendredpackType.SENDGROUPREDPACK == redpackOrder.getTransferType()) { + parameters.put("amt_type", "ALL_RAND"); + } else if (WxSendredpackType.SENDMINIPROGRAMHB == redpackOrder.getTransferType()) { + parameters.put("notify_way", "MINI_PROGRAM_JSAPI"); + } - /** - * 发放裂变红包 - * 裂变红包:一次可以发放一组红包。首先领取的用户为种子用户,种子用户领取一组红包当中的一个,并可以通过社交分享将剩下的红包给其他用户。裂变红包充分利用了人际传播的优势。 - * - * @author: faymanwang 1057438332@qq.com - * @param redpackOrder 红包实体 - * @return 返回发红包实体后的结果 - */ - public Map sendgroupredpack(RedpackOrder redpackOrder) { - Map parameters = new TreeMap(); - redpackParam(redpackOrder, parameters); - parameters.put("amt_type", "ALL_RAND"); parameters.put(SIGN, createSign(SignUtils.parameterText(parameters, "&", SIGN), payConfigStorage.getInputCharset())); - return requestTemplate.postForObject(getReqUrl( WxSendredpackType.SENDGROUPREDPACK), XML.getMap2Xml(parameters) , JSONObject.class); + return requestTemplate.postForObject(getReqUrl(redpackOrder.getTransferType()), XML.getMap2Xml(parameters), JSONObject.class); } - /** - * 小程序发红包 - * @author: faymanwang 1057438332@qq.com - * @param redpackOrder 红包实体 - * @return 返回发红包实体后的结果 - */ - public Map sendminiprogramhb(RedpackOrder redpackOrder) { - Map parameters = new TreeMap(); - redpackParam(redpackOrder, parameters); - parameters.put("notify_way", "MINI_PROGRAM_JSAPI"); - parameters.put(SIGN, createSign(SignUtils.parameterText(parameters, "&", SIGN), payConfigStorage.getInputCharset())); - return requestTemplate.postForObject(getReqUrl( WxSendredpackType.SENDMINIPROGRAMHB), XML.getMap2Xml(parameters) , JSONObject.class); - } /** * 查询红包记录 * 用于商户对已发放的红包进行查询红包的具体信息,可支持普通红包和裂变包 * 查询红包记录API只支持查询30天内的红包订单,30天之前的红包订单请登录商户平台查询。 * - * @author: faymanwang 1057438332@qq.com * @param mchBillno 商户发放红包的商户订单号 * @return 返回查询结果 + * @author: faymanwang 1057438332@qq.com */ - public Map gethbinfo (String mchBillno) { + @Override + public Map gethbinfo(String mchBillno) { Map parameters = this.getPublicParameters(); parameters.put("mch_billno", mchBillno); parameters.put("bill_type", "MCHT"); parameters.put(SIGN, createSign(SignUtils.parameterText(parameters, "&", SIGN), payConfigStorage.getInputCharset())); - return requestTemplate.postForObject(getReqUrl( WxSendredpackType.GETHBINFO), XML.getMap2Xml(parameters) , JSONObject.class); + return requestTemplate.postForObject(getReqUrl(WxSendredpackType.GETHBINFO), XML.getMap2Xml(parameters), JSONObject.class); } /** * 微信红包构造参数方法 + * * @param redpackOrder 红包实体 - * @param parameters + * @param parameters 接收参数 */ - private void redpackParam(RedpackOrder redpackOrder, Map parameters) { + private void redPackParam(RedpackOrder redpackOrder, Map parameters) { parameters.put(NONCE_STR, SignUtils.randomStr()); parameters.put(MCH_ID, payConfigStorage.getPid()); parameters.put("wxappid", payConfigStorage.getAppid()); @@ -829,12 +777,12 @@ public class WxPayService extends BasePayService { parameters.put("re_openid", redpackOrder.getReOpenid()); parameters.put("mch_billno", redpackOrder.getMchBillno()); parameters.put("total_amount", Util.conversionCentAmount(redpackOrder.getTotalAmount())); - parameters.put("total_num", redpackOrder.getTotalNum() > 0 ? redpackOrder.getTotalNum() : 1); + parameters.put("total_num", Math.max(redpackOrder.getTotalNum(), 1)); parameters.put("wishing", redpackOrder.getWishing()); parameters.put("client_ip", StringUtils.isNotEmpty(redpackOrder.getIp()) ? redpackOrder.getIp() : "192.168.0.1"); parameters.put("act_name", redpackOrder.getActName()); parameters.put("remark", redpackOrder.getRemark()); - if(StringUtils.isNotEmpty(redpackOrder.getSceneId())){ + if (StringUtils.isNotEmpty(redpackOrder.getSceneId())) { parameters.put("scene_id", redpackOrder.getSceneId()); } } diff --git a/pay-java-wx/src/main/java/com/egzosn/pay/wx/api/WxRedPackService.java b/pay-java-wx/src/main/java/com/egzosn/pay/wx/api/WxRedPackService.java new file mode 100644 index 0000000..341f3a8 --- /dev/null +++ b/pay-java-wx/src/main/java/com/egzosn/pay/wx/api/WxRedPackService.java @@ -0,0 +1,33 @@ +package com.egzosn.pay.wx.api; + +import com.egzosn.pay.wx.bean.RedpackOrder; + +import java.util.Map; + +/** + * 微信红包服务 + * @author egan + *
+ * email egzosn@gmail.com
+ * date 2020/5/17 22:24
+ * 
+ */ +public interface WxRedPackService { + /** + * 微信发红包 + * + * @param redpackOrder 红包实体 + * @return 返回发红包实体后的结果 + */ + Map sendredpack(RedpackOrder redpackOrder); + + /** + * 查询红包记录 + * 用于商户对已发放的红包进行查询红包的具体信息,可支持普通红包和裂变包 + * 查询红包记录API只支持查询30天内的红包订单,30天之前的红包订单请登录商户平台查询。 + * + * @param mchBillno 商户发放红包的商户订单号 + * @return 返回查询结果 + */ + Map gethbinfo(String mchBillno); +} diff --git a/pay-java-wx/src/main/java/com/egzosn/pay/wx/bean/RedpackOrder.java b/pay-java-wx/src/main/java/com/egzosn/pay/wx/bean/RedpackOrder.java index 801f94f..300c85e 100644 --- a/pay-java-wx/src/main/java/com/egzosn/pay/wx/bean/RedpackOrder.java +++ b/pay-java-wx/src/main/java/com/egzosn/pay/wx/bean/RedpackOrder.java @@ -1,152 +1,122 @@ package com.egzosn.pay.wx.bean; +import com.egzosn.pay.common.bean.TransferOrder; + import java.math.BigDecimal; /** - * @description: + * @description: 发红包订单 * @author: 保网 faymanwang 1057438332@qq.com * @time: 2020/5/15 12:40 */ -public class RedpackOrder { - /** - * 商户订单号(每个订单号必须唯一。取值范围:0~9,a~z,A~Z) - * 接口根据商户订单号支持重入,如出现超时可再调用 - */ - private String mchBillno; - - /** - * 商户名称:红包发送者名称 - */ - private String sendName; - - /** - * 用户openid - */ - private String reOpenid; - - /** - * 付款金额 每个红包金额必须在默认额度内(默认大于1元,小于200元,可在产品设置中自行申请调高额度) - */ - private BigDecimal totalAmount; - - /** - * 红包发放总人数 - * 普通红包:1 - * 裂变:必须介于(包括)3到20之间 - */ - private int totalNum; +public class RedpackOrder extends TransferOrder { - /** - * 红包祝福语 - */ - private String wishing; - - /** - * 操作者ip,根据支付平台所需进行设置 - */ - private String ip; /** - * 活动名称 - */ - private String actName; - - /** - * 备注 - */ - private String remark; - - /** - * 发放红包使用场景,红包金额大于200或者小于1元时必传 - * PRODUCT_1:商品促销 - * PRODUCT_2:抽奖 - * PRODUCT_3:虚拟物品兑奖 - * PRODUCT_4:企业内部福利 - * PRODUCT_5:渠道分润 - * PRODUCT_6:保险回馈 - * PRODUCT_7:彩票派奖 - * PRODUCT_8:税务刮奖 + * 商户订单号(每个订单号必须唯一。取值范围:0~9,a~z,A~Z) + * 接口根据商户订单号支持重入,如出现超时可再调用 + * @return 商户订单号 */ - private String sceneId; - - public String getMchBillno() { - return mchBillno; + return getOutNo(); } public void setMchBillno(String mchBillno) { - this.mchBillno = mchBillno; + setOutNo(mchBillno); } + /** + * 商户名称:红包发送者名称 + * @return 红包发送者名称 + */ public String getSendName() { - return sendName; + return getPayerName(); } public void setSendName(String sendName) { - this.sendName = sendName; + super.setPayerName(sendName); } + /** + * 用户openid + * @return 用户openid + */ public String getReOpenid() { - return reOpenid; + return getPayeeAccount(); } public void setReOpenid(String reOpenid) { - this.reOpenid = reOpenid; + super.setPayeeAccount(reOpenid); } + /** + * 付款金额 每个红包金额必须在默认额度内(默认大于1元,小于200元,可在产品设置中自行申请调高额度) + * @return 付款金额 + */ public BigDecimal getTotalAmount() { - return totalAmount; + return getAmount(); } public void setTotalAmount(BigDecimal totalAmount) { - this.totalAmount = totalAmount; + super.setAmount(totalAmount); } - + /** + * 红包发放总人数 + * 普通红包:1 + * 裂变:必须介于(包括)3到20之间 + */ public int getTotalNum() { - return totalNum; + Object totalNum = getAttr("total_num"); + return null == totalNum ? 1 : (Integer)totalNum; } public void setTotalNum(int totalNum) { - this.totalNum = totalNum; + addAttr("total_num", totalNum); } - + /** + * 红包祝福语 + */ public String getWishing() { - return wishing; - } + return (String) getAttr("wishing"); - public void setWishing(String wishing) { - this.wishing = wishing; } - public String getIp() { - return ip; + public void setWishing(String wishing) { + addAttr("wishing", wishing); } - public void setIp(String ip) { - this.ip = ip; - } + /** + * 活动名称 + */ public String getActName() { - return actName; + return (String) getAttr("act_name"); } public void setActName(String actName) { - this.actName = actName; + addAttr("act_name", actName); } - public String getRemark() { - return remark; + public String getSceneId() { + return (String) getAttr("scene_id"); } - - public void setRemark(String remark) { - this.remark = remark; + /** + * 发放红包使用场景,红包金额大于200或者小于1元时必传 + * PRODUCT_1:商品促销 + * PRODUCT_2:抽奖 + * PRODUCT_3:虚拟物品兑奖 + * PRODUCT_4:企业内部福利 + * PRODUCT_5:渠道分润 + * PRODUCT_6:保险回馈 + * PRODUCT_7:彩票派奖 + * PRODUCT_8:税务刮奖 + */ + public void setSceneId(String sceneId) { + addAttr("scene_id", sceneId); } - public String getSceneId() { - return sceneId; - } - public void setSceneId(String sceneId) { - this.sceneId = sceneId; + public void setTransferType(WxSendredpackType transferType) { + super.setTransferType(transferType); } } diff --git a/pay-java-wx/src/main/java/com/egzosn/pay/wx/bean/WxSendredpackType.java b/pay-java-wx/src/main/java/com/egzosn/pay/wx/bean/WxSendredpackType.java index 6e114b2..623b667 100644 --- a/pay-java-wx/src/main/java/com/egzosn/pay/wx/bean/WxSendredpackType.java +++ b/pay-java-wx/src/main/java/com/egzosn/pay/wx/bean/WxSendredpackType.java @@ -3,7 +3,7 @@ package com.egzosn.pay.wx.bean; import com.egzosn.pay.common.bean.TransferType; /** - * @description: + * @description: 红包交易类型 * @author: faymanwang * @time: 2020/5/14 20:11 */ -- Gitee From 9ed210856f3da7eeeddf7307947a7eb761fc0370 Mon Sep 17 00:00:00 2001 From: egzosn Date: Sun, 17 May 2020 23:57:10 +0800 Subject: [PATCH 121/299] =?UTF-8?q?=E5=BE=AE=E4=BF=A1=E5=8F=91=E7=BA=A2?= =?UTF-8?q?=E5=8C=85=E6=8E=A5=E5=8F=A3=E5=AE=9E=E7=8E=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/egzosn/pay/wx/api/WxPayService.java | 58 +++++++++---------- 1 file changed, 29 insertions(+), 29 deletions(-) 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 b882ddc..825d711 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 @@ -92,14 +92,14 @@ public class WxPayService extends BasePayService implements if (!(SUCCESS.equals(params.get(RETURN_CODE)) && SUCCESS.equals(params.get(RESULT_CODE)))) { if (LOG.isErrorEnabled()) { -LOG.error(String.format("微信支付异常:return_code=%s,参数集=%s", params.get(RETURN_CODE), params)); + LOG.error(String.format("微信支付异常:return_code=%s,参数集=%s", params.get(RETURN_CODE), params)); } return false; } if (null == params.get(SIGN)) { if (LOG.isDebugEnabled()) { -LOG.debug(String.format("微信支付异常:签名为空!%s=%s", OUT_TRADE_NO, params.get(OUT_TRADE_NO))); + LOG.debug(String.format("微信支付异常:签名为空!%s=%s", OUT_TRADE_NO, params.get(OUT_TRADE_NO))); } return false; } @@ -240,24 +240,24 @@ LOG.debug(String.format("微信支付异常:签名为空!%s=%s", OUT_TRADE_N if (verify(preOrderHandler(result, order))) { //如果是扫码支付或者刷卡付无需处理,直接返回 if (((WxTransactionType) order.getTransactionType()).isReturn()) { -return result; + return result; } Map params = new TreeMap(); if (WxTransactionType.JSAPI == order.getTransactionType()) { -params.put("signType", payConfigStorage.getSignType()); -params.put("appId", payConfigStorage.getAppid()); -params.put("timeStamp", System.currentTimeMillis() / 1000); -params.put("nonceStr", result.get(NONCE_STR)); -params.put("package", "prepay_id=" + result.get("prepay_id")); + params.put("signType", payConfigStorage.getSignType()); + params.put("appId", payConfigStorage.getAppid()); + params.put("timeStamp", System.currentTimeMillis() / 1000); + params.put("nonceStr", result.get(NONCE_STR)); + params.put("package", "prepay_id=" + result.get("prepay_id")); } else if (WxTransactionType.APP == order.getTransactionType()) { -params.put("partnerid", payConfigStorage.getPid()); -params.put(APPID, payConfigStorage.getAppid()); -params.put("prepayid", result.get("prepay_id")); -params.put("timestamp", System.currentTimeMillis() / 1000); -params.put("noncestr", result.get(NONCE_STR)); -params.put("package", "Sign=WXPay"); + params.put("partnerid", payConfigStorage.getPid()); + params.put(APPID, payConfigStorage.getAppid()); + params.put("prepayid", result.get("prepay_id")); + params.put("timestamp", System.currentTimeMillis() / 1000); + params.put("noncestr", result.get(NONCE_STR)); + params.put("package", "Sign=WXPay"); } String paySign = createSign(SignUtils.parameterText(params), payConfigStorage.getInputCharset()); params.put(SIGN, paySign); @@ -478,8 +478,6 @@ params.put("package", "Sign=WXPay"); } - - /** * 申请退款接口 * @@ -575,7 +573,7 @@ params.put("package", "Sign=WXPay"); if (transactionType == WxTransactionType.DOWNLOADBILL) { if (transactionIdOrBillDate instanceof Date) { -return downloadbill((Date) transactionIdOrBillDate, outTradeNoBillType); + return downloadbill((Date) transactionIdOrBillDate, outTradeNoBillType); } throw new PayErrorException(new PayException(FAILURE, "非法类型异常:" + transactionIdOrBillDate.getClass())); } @@ -599,12 +597,12 @@ return downloadbill((Date) transactionIdOrBillDate, outTradeNoBillType); * @param order 转账订单 *
      *
-     *注意事项:
-     *◆ 当返回错误码为“SYSTEMERROR”时,请不要更换商户订单号,一定要使用原商户订单号重试,否则可能造成重复支付等资金风险。
-     *◆ XML具有可扩展性,因此返回参数可能会有新增,而且顺序可能不完全遵循此文档规范,如果在解析回包的时候发生错误,请商户务必不要换单重试,请商户联系客服确认付款情况。如果有新回包字段,会更新到此API文档中。
-     *◆ 因为错误代码字段err_code的值后续可能会增加,所以商户如果遇到回包返回新的错误码,请商户务必不要换单重试,请商户联系客服确认付款情况。如果有新的错误码,会更新到此API文档中。
-     *◆ 错误代码描述字段err_code_des只供人工定位问题时做参考,系统实现时请不要依赖这个字段来做自动化处理。
-     *
+ * 注意事项: + * ◆ 当返回错误码为“SYSTEMERROR”时,请不要更换商户订单号,一定要使用原商户订单号重试,否则可能造成重复支付等资金风险。 + * ◆ XML具有可扩展性,因此返回参数可能会有新增,而且顺序可能不完全遵循此文档规范,如果在解析回包的时候发生错误,请商户务必不要换单重试,请商户联系客服确认付款情况。如果有新回包字段,会更新到此API文档中。 + * ◆ 因为错误代码字段err_code的值后续可能会增加,所以商户如果遇到回包返回新的错误码,请商户务必不要换单重试,请商户联系客服确认付款情况。如果有新的错误码,会更新到此API文档中。 + * ◆ 错误代码描述字段err_code_des只供人工定位问题时做参考,系统实现时请不要依赖这个字段来做自动化处理。 + * * @return 对应的转账结果 */ @Override @@ -678,11 +676,11 @@ return downloadbill((Date) transactionIdOrBillDate, outTradeNoBillType); * * @param outNo 商户转账订单号 * @param wxTransferType 微信转账类型,.....这里没办法了只能这样写(┬_┬),请见谅 {@link com.egzosn.pay.wx.bean.WxTransferType} - *

- *

- * 企业付款到零钱 - * 商户企业付款到银行卡 - *

+ *

+ *

+ * 企业付款到零钱 + * 商户企业付款到银行卡 + *

* @return 对应的转账订单 */ @Override @@ -734,8 +732,10 @@ return downloadbill((Date) transactionIdOrBillDate, outTradeNoBillType); public Map sendredpack(RedpackOrder redpackOrder) { Map parameters = new TreeMap(); redPackParam(redpackOrder, parameters); + parameters.put("total_num", 1); if (WxSendredpackType.SENDGROUPREDPACK == redpackOrder.getTransferType()) { parameters.put("amt_type", "ALL_RAND"); + parameters.remove("total_num"); } else if (WxSendredpackType.SENDMINIPROGRAMHB == redpackOrder.getTransferType()) { parameters.put("notify_way", "MINI_PROGRAM_JSAPI"); } @@ -767,7 +767,7 @@ return downloadbill((Date) transactionIdOrBillDate, outTradeNoBillType); * 微信红包构造参数方法 * * @param redpackOrder 红包实体 - * @param parameters 接收参数 + * @param parameters 接收参数 */ private void redPackParam(RedpackOrder redpackOrder, Map parameters) { parameters.put(NONCE_STR, SignUtils.randomStr()); -- Gitee From 31b9210b97d3acb6bd11a681c13fad0d8063016e Mon Sep 17 00:00:00 2001 From: faymanwang <1057438332@qq.com> Date: Mon, 18 May 2020 11:14:24 +0800 Subject: [PATCH 122/299] =?UTF-8?q?=E5=BE=AE=E4=BF=A1=E6=94=AF=E4=BB=98-?= =?UTF-8?q?=E5=8F=91=E7=BA=A2=E5=8C=85=E6=A8=A1=E5=9D=97-=E5=BE=AE?= =?UTF-8?q?=E4=BF=A1=E6=94=AF=E4=BB=98-=E5=8F=91=E7=BA=A2=E5=8C=85?= =?UTF-8?q?=E6=A8=A1=E5=9D=97-=E7=8E=B0=E9=87=91=E7=BA=A2=E5=8C=85?= =?UTF-8?q?=EF=BC=8C=E5=B0=8F=E7=A8=8B=E5=BA=8F=E5=8F=AA=E8=83=BD=E4=B8=BA?= =?UTF-8?q?1=20=EF=BC=9B=E8=A3=82=E5=8F=98=E7=BA=A2=E5=8C=85=E9=BB=98?= =?UTF-8?q?=E8=AE=A4=E4=B8=8D=E5=B0=8F=E4=BA=8E3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/egzosn/pay/wx/api/WxPayService.java | 6 ++-- pay-java-wx/src/test/java/PayTest.java | 34 +++++++++++++++++-- 2 files changed, 35 insertions(+), 5 deletions(-) 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 825d711..feaff86 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 @@ -732,10 +732,10 @@ public class WxPayService extends BasePayService implements public Map sendredpack(RedpackOrder redpackOrder) { Map parameters = new TreeMap(); redPackParam(redpackOrder, parameters); - parameters.put("total_num", 1); if (WxSendredpackType.SENDGROUPREDPACK == redpackOrder.getTransferType()) { + //现金红包,小程序红包默认传1.裂变红包取传入值,且需要大于3 + parameters.put("total_num", Math.max(redpackOrder.getTotalNum(), 3)); parameters.put("amt_type", "ALL_RAND"); - parameters.remove("total_num"); } else if (WxSendredpackType.SENDMINIPROGRAMHB == redpackOrder.getTransferType()) { parameters.put("notify_way", "MINI_PROGRAM_JSAPI"); } @@ -777,7 +777,7 @@ public class WxPayService extends BasePayService implements parameters.put("re_openid", redpackOrder.getReOpenid()); parameters.put("mch_billno", redpackOrder.getMchBillno()); parameters.put("total_amount", Util.conversionCentAmount(redpackOrder.getTotalAmount())); - parameters.put("total_num", Math.max(redpackOrder.getTotalNum(), 1)); + parameters.put("total_num", 1); parameters.put("wishing", redpackOrder.getWishing()); parameters.put("client_ip", StringUtils.isNotEmpty(redpackOrder.getIp()) ? redpackOrder.getIp() : "192.168.0.1"); parameters.put("act_name", redpackOrder.getActName()); diff --git a/pay-java-wx/src/test/java/PayTest.java b/pay-java-wx/src/test/java/PayTest.java index d793613..e0a604b 100644 --- a/pay-java-wx/src/test/java/PayTest.java +++ b/pay-java-wx/src/test/java/PayTest.java @@ -1,9 +1,12 @@ -import com.egzosn.pay.common.api.PayService; +import com.egzosn.pay.common.bean.CertStoreType; import com.egzosn.pay.common.bean.MethodType; import com.egzosn.pay.common.bean.PayOrder; +import com.egzosn.pay.common.http.HttpConfigStorage; import com.egzosn.pay.wx.api.WxPayConfigStorage; import com.egzosn.pay.wx.api.WxPayService; +import com.egzosn.pay.wx.bean.RedpackOrder; +import com.egzosn.pay.wx.bean.WxSendredpackType; import com.egzosn.pay.wx.bean.WxTransactionType; import java.awt.image.BufferedImage; @@ -39,7 +42,7 @@ public class PayTest { //是否为测试账号,沙箱环境 此处暂未实现 wxPayConfigStorage.setTest(true); //支付服务 - PayService service = new WxPayService(wxPayConfigStorage); + WxPayService service = new WxPayService(wxPayConfigStorage); //支付订单基础信息 PayOrder payOrder = new PayOrder("订单title", "摘要", new BigDecimal(0.01) , UUID.randomUUID().toString().replace("-", "")); /*-----------扫码付-------------------*/ @@ -84,5 +87,32 @@ public class PayTest { /*-----------回调处理-------------------*/ + + HttpConfigStorage httpConfigStorage = new HttpConfigStorage(); + //ssl 退款证书相关 + httpConfigStorage.setKeystore("D:/work/pay/src/main/resources/certificates/1220429901_apiclient_cert.p12"); + httpConfigStorage.setStorePassword("默认商户号"); + //设置ssl证书对应的存储方式,这里默认为文件地址 + httpConfigStorage.setCertStoreType(CertStoreType.PATH); + service.setRequestTemplateConfigStorage(httpConfigStorage); + + RedpackOrder redpackOrder = new RedpackOrder(); + + redpackOrder.setSendName("测试"); + //faymanwang- opid + redpackOrder.setReOpenid("om3rxjhD1rhGrP6oLydMgLcN5n10"); + //红包流水 + redpackOrder.setMchBillno("red202005181"); + redpackOrder.setTotalAmount(new BigDecimal(1.5)); + redpackOrder.setSceneId("PRODUCT_1"); + //现金红包,小程序默认为1 裂变默认为3 + redpackOrder.setTotalNum(4); + redpackOrder.setWishing("请勿领取"); + redpackOrder.setActName("请勿领取测试红包"); + redpackOrder.setRemark("测试支付-by fayman"); + //设置发红包方式 + redpackOrder.setTransferType(WxSendredpackType.SENDGROUPREDPACK); + Map sendredpack = service.sendredpack(redpackOrder); + System.out.println(sendredpack); } } -- Gitee From 739f850902ed20fa792718a080fa01cc8927fc3c Mon Sep 17 00:00:00 2001 From: egzosn Date: Mon, 18 May 2020 21:55:18 +0800 Subject: [PATCH 123/299] =?UTF-8?q?APP=E6=8E=A5=E5=8F=A3=E5=AE=9E=E7=8E=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../egzosn/pay/common/api/BasePayService.java | 11 +++- .../com/egzosn/pay/common/api/PayService.java | 16 ++--- .../pay/demo/controller/AliPayController.java | 2 +- .../pay/demo/controller/WxPayController.java | 2 +- .../egzosn/pay/union/api/UnionPayService.java | 60 +++++++++++-------- 5 files changed, 54 insertions(+), 37 deletions(-) diff --git a/pay-java-common/src/main/java/com/egzosn/pay/common/api/BasePayService.java b/pay-java-common/src/main/java/com/egzosn/pay/common/api/BasePayService.java index b7d8124..261c794 100644 --- a/pay-java-common/src/main/java/com/egzosn/pay/common/api/BasePayService.java +++ b/pay-java-common/src/main/java/com/egzosn/pay/common/api/BasePayService.java @@ -125,7 +125,6 @@ public abstract class BasePayService implements Pay * @param characterEncoding 字符编码 * @return 签名 */ - @Override public String createSign(Map content, String characterEncoding) { return SignUtils.valueOf(payConfigStorage.getSignType()).sign(content, payConfigStorage.getKeyPrivate(), characterEncoding); } @@ -141,6 +140,16 @@ public abstract class BasePayService implements Pay Map orderInfo = orderInfo(order); return buildRequest(orderInfo, MethodType.POST); } + /** + * app支付 + * @param order 订单信息 + * @param 预订单类型 + * @return 对应app所需参数信息 + */ + @Override + public Map app(O order) { + return orderInfo(order); + } /** * 生成二维码支付 diff --git a/pay-java-common/src/main/java/com/egzosn/pay/common/api/PayService.java b/pay-java-common/src/main/java/com/egzosn/pay/common/api/PayService.java index 9397046..d93bca9 100644 --- a/pay-java-common/src/main/java/com/egzosn/pay/common/api/PayService.java +++ b/pay-java-common/src/main/java/com/egzosn/pay/common/api/PayService.java @@ -98,15 +98,13 @@ public interface PayService { * @return 对应页面重定向信息 */ String toPay(O order); - /** - * 创建签名 - * - * @param content 需要签名的内容 - * @param characterEncoding 字符编码 - * @return 签名 + * app支付 + * @param order 订单信息 + * @param 预订单类型 + * @return 对应app所需参数信息 */ - String createSign(String content, String characterEncoding); + Map app(O order); /** * 创建签名 @@ -115,7 +113,9 @@ public interface PayService { * @param characterEncoding 字符编码 * @return 签名 */ - String createSign(Map content, String characterEncoding); + String createSign(String content, String characterEncoding); + + /** * 将请求参数或者请求流转化为 Map diff --git a/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/AliPayController.java b/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/AliPayController.java index e9f5ee6..93a50af 100644 --- a/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/AliPayController.java +++ b/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/AliPayController.java @@ -113,7 +113,7 @@ public class AliPayController { PayOrder order = new PayOrder("订单title", "摘要", new BigDecimal(0.01), UUID.randomUUID().toString().replace("-", "")); //App支付 order.setTransactionType(AliTransactionType.APP); - data.put("orderInfo", UriVariables.getMapToParameters(service.orderInfo(order))); + data.put("orderInfo", UriVariables.getMapToParameters(service.app(order))); return data; } diff --git a/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/WxPayController.java b/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/WxPayController.java index ef58916..0728800 100644 --- a/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/WxPayController.java +++ b/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/WxPayController.java @@ -144,7 +144,7 @@ public class WxPayController { PayOrder order = new PayOrder("订单title", "摘要", new BigDecimal(0.01), UUID.randomUUID().toString().replace("-", "")); //App支付 order.setTransactionType(WxTransactionType.APP); - data.put("orderInfo", service.orderInfo(order)); + data.put("orderInfo", service.app(order)); return data; } diff --git a/pay-java-union/src/main/java/com/egzosn/pay/union/api/UnionPayService.java b/pay-java-union/src/main/java/com/egzosn/pay/union/api/UnionPayService.java index 978d180..6e97c3d 100644 --- a/pay-java-union/src/main/java/com/egzosn/pay/union/api/UnionPayService.java +++ b/pay-java-union/src/main/java/com/egzosn/pay/union/api/UnionPayService.java @@ -23,6 +23,9 @@ import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; import java.math.BigDecimal; +import java.security.GeneralSecurityException; +import java.security.InvalidAlgorithmParameterException; +import java.security.NoSuchAlgorithmException; import java.security.cert.*; import java.sql.Timestamp; import java.text.DateFormat; @@ -359,21 +362,35 @@ public class UnionPayService extends BasePayService { CertPathBuilder builder = CertPathBuilder.getInstance("PKIX"); - @SuppressWarnings("unused") - PKIXCertPathBuilderResult result = (PKIXCertPathBuilderResult) builder.build(pkixParams); + /*PKIXCertPathBuilderResult result = (PKIXCertPathBuilderResult)*/ builder.build(pkixParams); return cert; } catch (java.security.cert.CertPathBuilderException e) { LOG.error("verify certificate chain fail.", e); } catch (CertificateExpiredException e) { LOG.error(e); - } catch (CertificateNotYetValidException e) { - LOG.error(e); - } catch (Exception e) { + } catch (GeneralSecurityException e) { LOG.error(e); } return null; } + /** + * 发送订单 + * + * @param order 发起支付的订单信息 + * @return 返回支付结果 + */ + + public JSONObject postOrder(PayOrder order) { + Map params = orderInfo(order); + String responseStr = getHttpRequestTemplate().postForObject(this.getBackTransUrl(), params, String.class); + JSONObject response = UriVariables.getParametersToMap(responseStr); + if (response.isEmpty()) { + throw new PayErrorException(new PayException("failure", "响应内容有误!", responseStr)); + } + return response; + } + @Override public String toPay(PayOrder order) { @@ -395,20 +412,15 @@ public class UnionPayService extends BasePayService { @Override public String getQrPay(PayOrder order) { order.setTransactionType(UnionTransactionType.APPLY_QR_CODE); - Map params = orderInfo(order); - String responseStr = getHttpRequestTemplate().postForObject(this.getBackTransUrl(), params, String.class); - Map response = UriVariables.getParametersToMap(responseStr); - if (response.isEmpty()) { - throw new PayErrorException(new PayException("failure", "响应内容有误!", responseStr)); - } + JSONObject response = postOrder(order); if (this.verify(response)) { if (SDKConstants.OK_RESP_CODE.equals(response.get(SDKConstants.param_respCode))) { //成功 return (String) response.get(SDKConstants.param_qrCode); } - throw new PayErrorException(new PayException((String) response.get(SDKConstants.param_respCode), (String) response.get(SDKConstants.param_respMsg), responseStr)); + throw new PayErrorException(new PayException((String) response.get(SDKConstants.param_respCode), (String) response.get(SDKConstants.param_respMsg), response.toJSONString())); } - throw new PayErrorException(new PayException("failure", "验证签名失败", responseStr)); + throw new PayErrorException(new PayException("failure", "验证签名失败", response.toJSONString())); } /** @@ -420,9 +432,8 @@ public class UnionPayService extends BasePayService { @Override public Map microPay(PayOrder order) { order.setTransactionType(UnionTransactionType.CONSUME); - Map params = orderInfo(order); - String responseStr = getHttpRequestTemplate().postForObject(this.getBackTransUrl(), params, String.class); - return UriVariables.getParametersToMap(responseStr); + JSONObject response = postOrder(order); + return response; } @@ -499,17 +510,14 @@ public class UnionPayService extends BasePayService { /** * 功能:将订单信息进行签名并提交请求 - * 业务范围:手机控件支付产品(WAP), + * 业务范围:手机支付控件(含安卓Pay) * @param order 订单信息 * @return 成功:返回支付结果 失败:返回 */ - public Map sendHttpRequest(PayOrder order){ - Map params = orderInfo(order); - String responseStr = getHttpRequestTemplate().postForObject(this.getBackTransUrl(), params, String.class); - Map response = UriVariables.getParametersToMap(responseStr); - if (response.isEmpty()) { - throw new PayErrorException(new PayException("failure", "响应内容有误!", responseStr)); - } + @Override + public Map app(PayOrder order){ + order.setTransactionType(UnionTransactionType.APP); + JSONObject response = postOrder(order); if (this.verify(response)) { if (SDKConstants.OK_RESP_CODE.equals(response.get(SDKConstants.param_respCode))) { // //成功,获取tn号 @@ -517,9 +525,9 @@ public class UnionPayService extends BasePayService { // //TODO return response; } - throw new PayErrorException(new PayException((String) response.get(SDKConstants.param_respCode), (String) response.get(SDKConstants.param_respMsg), responseStr)); + throw new PayErrorException(new PayException((String) response.get(SDKConstants.param_respCode), (String) response.get(SDKConstants.param_respMsg), response.toJSONString())); } - throw new PayErrorException(new PayException("failure", "验证签名失败", responseStr)); + throw new PayErrorException(new PayException("failure", "验证签名失败", response.toJSONString())); } /** -- Gitee From 780a4c52e4bd4267d2ff24c2d74a32532b0d9974 Mon Sep 17 00:00:00 2001 From: egzosn Date: Tue, 19 May 2020 23:12:26 +0800 Subject: [PATCH 124/299] =?UTF-8?q?=E6=94=AF=E4=BB=98=E5=AE=9D=E8=BD=AC?= =?UTF-8?q?=E8=B4=A6=EF=BC=8C=E5=8F=91=E7=BA=A2=E5=8C=85=E5=AE=9E=E7=8E=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../pay/ali/bean/AliTransactionType.java | 8 --- .../egzosn/pay/ali/bean/AliTransferOrder.java | 58 +++++++++++++++ .../egzosn/pay/ali/bean/AliTransferType.java | 72 +++++++++++++++++-- .../com/egzosn/pay/ali/bean/IdentityType.java | 19 +++++ 4 files changed, 143 insertions(+), 14 deletions(-) create mode 100644 pay-java-ali/src/main/java/com/egzosn/pay/ali/bean/AliTransferOrder.java create mode 100644 pay-java-ali/src/main/java/com/egzosn/pay/ali/bean/IdentityType.java 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 2073ba3..13e4f12 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 @@ -88,14 +88,6 @@ public enum AliTransactionType implements TransactionType { * 下载对账单 */ DOWNLOADBILL("alipay.data.dataservice.bill.downloadurl.query"), - /** - * 转账到支付宝 - */ - TRANS("alipay.fund.trans.toaccount.transfer"), - /** - * 转账查询 - */ - TRANS_QUERY("alipay.fund.trans.order.query"), /** * 查询刷脸结果信息 * 暂时未接入 diff --git a/pay-java-ali/src/main/java/com/egzosn/pay/ali/bean/AliTransferOrder.java b/pay-java-ali/src/main/java/com/egzosn/pay/ali/bean/AliTransferOrder.java new file mode 100644 index 0000000..1882f84 --- /dev/null +++ b/pay-java-ali/src/main/java/com/egzosn/pay/ali/bean/AliTransferOrder.java @@ -0,0 +1,58 @@ +package com.egzosn.pay.ali.bean; + +import com.egzosn.pay.common.bean.TransferOrder; + +/** + * 支付转账(红包)订单 + * @author egan + * date 2020/5/18 21:08 + * email egzosn@gmail.com + */ +public class AliTransferOrder extends TransferOrder { + + private String orderTitle; + private String identity; + private String identityType; + private String name; + private String businessParams; + + public String getOrderTitle() { + return orderTitle; + } + + public void setOrderTitle(String orderTitle) { + this.orderTitle = orderTitle; + } + + public String getIdentity() { + return identity; + } + + public void setIdentity(String identity) { + this.identity = identity; + } + + public String getIdentityType() { + return identityType; + } + + public void setIdentityType(String identityType) { + this.identityType = identityType; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getBusinessParams() { + return businessParams; + } + + public void setBusinessParams(String businessParams) { + this.businessParams = businessParams; + } +} diff --git a/pay-java-ali/src/main/java/com/egzosn/pay/ali/bean/AliTransferType.java b/pay-java-ali/src/main/java/com/egzosn/pay/ali/bean/AliTransferType.java index 9f35aec..0d8459f 100644 --- a/pay-java-ali/src/main/java/com/egzosn/pay/ali/bean/AliTransferType.java +++ b/pay-java-ali/src/main/java/com/egzosn/pay/ali/bean/AliTransferType.java @@ -2,6 +2,9 @@ package com.egzosn.pay.ali.bean; import com.egzosn.pay.common.bean.TransferType; +import java.util.HashMap; +import java.util.Map; + /** * 收款方账户类型 * @author egan @@ -10,17 +13,59 @@ import com.egzosn.pay.common.bean.TransferType; */ public enum AliTransferType implements TransferType { /** - * 支付宝账号对应的支付宝唯一用户号。以2088开头的16位纯数字组成。 + * 单笔无密转账到支付宝账户固定为 + */ + TRANS_ACCOUNT_NO_PWD("alipay.fund.trans.uni.transfer", "DIRECT_TRANSFER"), + /** + * 单笔无密转账到银行卡固定为 */ - ALIPAY_USERID, + TRANS_BANKCARD_NO_PWD("alipay.fund.trans.uni.transfer", "DIRECT_TRANSFER"), /** - * 支付宝登录号,支持邮箱和手机号格式。 + * 收发现金红包固定为 */ - ALIPAY_LOGONID + STD_RED_PACKET("alipay.fund.trans.uni.transfer", "DIRECT_TRANSFER"), + /** + * 现金红包无线支付接口 + */ + STD_RED_PACKET_APP("alipay.fund.trans.app.pay", "PERSONAL_PAY"){ + + /** + * 获取转账类型 + * + * @return 转账类型 + */ + @Override + public String getType() { + return STD_RED_PACKET.name(); + } + }, + + /** + * 转账查询 + */ + TRANS_QUERY("alipay.fund.trans.order.query") + ; + /** + * 接口名称 + */ + private String method; + /** + * 业务场景 + */ + private String bizScene; + + AliTransferType(String method) { + this.method = method; + } + + AliTransferType(String method, String bizScene) { + this.method = method; + this.bizScene = bizScene; + } /** - * 获取转账类型 + * 获取转账类型, product_code 业务产品码 * * @return 转账类型 */ @@ -29,6 +74,10 @@ public enum AliTransferType implements TransferType { return name(); } + public String getBizScene() { + return bizScene; + } + /** * 获取接口 * @@ -36,6 +85,17 @@ public enum AliTransferType implements TransferType { */ @Override public String getMethod() { - return name(); + return method; + } + + /** + * 设置属性 + * @param attr 已有属性对象 + * @return 属性对象 + */ + public Map setAttr(Map attr){ + attr.put("product_code", getType()); + attr.put("biz_scene", getBizScene()); + return attr; } } diff --git a/pay-java-ali/src/main/java/com/egzosn/pay/ali/bean/IdentityType.java b/pay-java-ali/src/main/java/com/egzosn/pay/ali/bean/IdentityType.java new file mode 100644 index 0000000..955c8a4 --- /dev/null +++ b/pay-java-ali/src/main/java/com/egzosn/pay/ali/bean/IdentityType.java @@ -0,0 +1,19 @@ +package com.egzosn.pay.ali.bean; + +/** + * 参与方的标识类型 + * @author egan + * date 2020/5/19 21:45 + * email egzosn@gmail.com + */ +public enum IdentityType { + /** + * 支付宝的会员ID + */ + ALIPAY_USER_ID, + /** + * 支付宝登录号,支持邮箱和手机号格式 + */ + ALIPAY_LOGON_ID + +} -- Gitee From f37878ad683b9e10ee3b4dbe80dc2b173696310e Mon Sep 17 00:00:00 2001 From: egzosn Date: Sun, 24 May 2020 23:27:10 +0800 Subject: [PATCH 125/299] =?UTF-8?q?=E6=A0=87=E8=AE=B0=E5=8D=B3=E5=B0=86?= =?UTF-8?q?=E5=BA=9F=E5=BC=83=E7=9A=84=E6=96=B9=E6=B3=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/egzosn/pay/common/api/PayService.java | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/pay-java-common/src/main/java/com/egzosn/pay/common/api/PayService.java b/pay-java-common/src/main/java/com/egzosn/pay/common/api/PayService.java index d93bca9..52bbb96 100644 --- a/pay-java-common/src/main/java/com/egzosn/pay/common/api/PayService.java +++ b/pay-java-common/src/main/java/com/egzosn/pay/common/api/PayService.java @@ -54,10 +54,11 @@ public interface PayService { /** * 回调校验 - * + * 后面版本废弃 * @param params 回调回来的参数集 * @return 签名校验 true通过 */ + @Deprecated boolean verify(Map params); /** @@ -73,10 +74,11 @@ public interface PayService { /** * 支付宝需要,微信是否也需要再次校验来源,进行订单查询 * 校验数据来源 - * + * 后面版本废弃 * @param id 业务id, 数据的真实性. * @return true通过 */ + @Deprecated boolean verifySource(String id); @@ -305,18 +307,19 @@ public interface PayService { /** * 通用查询接口 - * + * 接下来移除此方法 * @param tradeNoOrBillDate 支付平台订单号或者账单类型, 具体请 * 类型为{@link String }或者 {@link Date },类型须强制限制,类型不对应则抛出异常{@link PayErrorException} * @param outTradeNoBillType 商户单号或者 账单类型 * @param transactionType 交易类型 * @return 返回支付方对应接口的结果 */ + @Deprecated Map secondaryInterface(Object tradeNoOrBillDate, String outTradeNoBillType, TransactionType transactionType); /** * 通用查询接口 - * + * 接下来移除此方法 * @param tradeNoOrBillDate 支付平台订单号或者账单日期, 具体请 类型为{@link String }或者 {@link Date },类型须强制限制,类型不对应则抛出异常{@link PayErrorException} * @param outTradeNoBillType 商户单号或者 账单类型 * @param transactionType 交易类型 @@ -324,6 +327,7 @@ public interface PayService { * @param 返回类型 * @return 返回支付方对应接口的结果 */ + @Deprecated T secondaryInterface(Object tradeNoOrBillDate, String outTradeNoBillType, TransactionType transactionType, Callback callback); -- Gitee From 84b898f4208b15e45f98f32926ad6b88fc704529 Mon Sep 17 00:00:00 2001 From: egzosn Date: Sun, 24 May 2020 23:29:18 +0800 Subject: [PATCH 126/299] =?UTF-8?q?=E8=BD=AC=E8=B4=A6=E8=AE=A2=E5=8D=95?= =?UTF-8?q?=E8=AE=BE=E7=BD=AE=E5=B1=9E=E6=80=A7=E4=BF=A1=E6=81=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/egzosn/pay/common/bean/TransferType.java | 12 ++++++++++-- .../java/com/egzosn/pay/wx/bean/WxTransferType.java | 9 +++++++++ 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/pay-java-common/src/main/java/com/egzosn/pay/common/bean/TransferType.java b/pay-java-common/src/main/java/com/egzosn/pay/common/bean/TransferType.java index 867acf9..24b34d5 100644 --- a/pay-java-common/src/main/java/com/egzosn/pay/common/bean/TransferType.java +++ b/pay-java-common/src/main/java/com/egzosn/pay/common/bean/TransferType.java @@ -1,5 +1,7 @@ package com.egzosn.pay.common.bean; +import java.util.Map; + /** * 转账类型 * @author egan @@ -7,6 +9,12 @@ package com.egzosn.pay.common.bean; * date 2018/9/28.19:45 */ public interface TransferType extends TransactionType{ - - + /** + * 设置属性 + * + * @param attr 已有属性对象 + * @param order 转账订单 + * @return 属性对象 + */ + Map setAttr(Map attr, TransferOrder order); } diff --git a/pay-java-wx/src/main/java/com/egzosn/pay/wx/bean/WxTransferType.java b/pay-java-wx/src/main/java/com/egzosn/pay/wx/bean/WxTransferType.java index e0f2760..1983c55 100644 --- a/pay-java-wx/src/main/java/com/egzosn/pay/wx/bean/WxTransferType.java +++ b/pay-java-wx/src/main/java/com/egzosn/pay/wx/bean/WxTransferType.java @@ -1,7 +1,10 @@ package com.egzosn.pay.wx.bean; +import com.egzosn.pay.common.bean.TransferOrder; import com.egzosn.pay.common.bean.TransferType; +import java.util.Map; + /** * 微信转账类型 * @author egan @@ -41,4 +44,10 @@ public enum WxTransferType implements TransferType{ public String getMethod() { return this.method; } + + + @Override + public Map setAttr(Map attr, TransferOrder order) { + return attr; + } } -- Gitee From 9153a3857dbe433bb0f2782bedd0e7f8d5526fb8 Mon Sep 17 00:00:00 2001 From: egzosn Date: Sun, 24 May 2020 23:32:20 +0800 Subject: [PATCH 127/299] =?UTF-8?q?=E6=A0=87=E8=AE=B0=E5=8D=B3=E5=B0=86?= =?UTF-8?q?=E5=BA=9F=E5=BC=83=E7=9A=84=E6=96=B9=E6=B3=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../main/java/com/egzosn/pay/common/api/PayService.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/pay-java-common/src/main/java/com/egzosn/pay/common/api/PayService.java b/pay-java-common/src/main/java/com/egzosn/pay/common/api/PayService.java index 52bbb96..910fc7e 100644 --- a/pay-java-common/src/main/java/com/egzosn/pay/common/api/PayService.java +++ b/pay-java-common/src/main/java/com/egzosn/pay/common/api/PayService.java @@ -54,20 +54,20 @@ public interface PayService { /** * 回调校验 - * 后面版本废弃 + * * @param params 回调回来的参数集 * @return 签名校验 true通过 */ - @Deprecated - boolean verify(Map params); + boolean verify(Map params); /** * 签名校验 - * + * 后面版本废弃 * @param params 参数集 * @param sign 签名原文 * @return 签名校验 true通过 */ + @Deprecated boolean signVerify(Map params, String sign); -- Gitee From 9929598373f13c804f319358c685f9e378b72950 Mon Sep 17 00:00:00 2001 From: egzosn Date: Sun, 24 May 2020 23:33:34 +0800 Subject: [PATCH 128/299] =?UTF-8?q?=E6=94=AF=E4=BB=98=E5=AE=9D=E6=96=B0?= =?UTF-8?q?=E7=89=88=E8=BD=AC=E8=B4=A6(=E5=8F=91=E7=BA=A2=E5=8C=85)?= =?UTF-8?q?=E5=AE=9E=E7=8E=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/egzosn/pay/ali/api/AliPayService.java | 110 ++++++++++++------ .../egzosn/pay/ali/bean/AliTransferOrder.java | 109 +++++++++++++++-- .../egzosn/pay/ali/bean/AliTransferType.java | 21 ++-- .../com/egzosn/pay/ali/bean/IdentityType.java | 19 --- .../com/egzosn/pay/ali/bean/OrderSettle.java | 31 ++++- 5 files changed, 219 insertions(+), 71 deletions(-) delete mode 100644 pay-java-ali/src/main/java/com/egzosn/pay/ali/bean/IdentityType.java 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 ef9a180..9b7aa7d 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 @@ -4,6 +4,7 @@ import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONObject; import com.egzosn.pay.ali.bean.AliPayMessage; import com.egzosn.pay.ali.bean.AliTransactionType; +import com.egzosn.pay.ali.bean.AliTransferType; import com.egzosn.pay.ali.bean.OrderSettle; import com.egzosn.pay.common.api.BasePayService; import com.egzosn.pay.common.bean.*; @@ -16,10 +17,7 @@ import com.egzosn.pay.common.util.Util; import com.egzosn.pay.common.util.sign.SignUtils; import com.egzosn.pay.common.util.str.StringUtils; -import java.util.Date; -import java.util.LinkedHashMap; -import java.util.Map; -import java.util.TreeMap; +import java.util.*; /** * 支付宝支付服务 @@ -39,29 +37,37 @@ public class AliPayService extends BasePayService { * 沙箱测试环境账号 */ private static final String DEV_REQ_URL = "https://openapi.alipaydev.com/gateway.do"; - - public static final String SIGN = "sign"; - - public static final String SUCCESS_CODE = "10000"; - - public static final String CODE = "code"; + + private static final String SIGN = "sign"; + + private static final String SUCCESS_CODE = "10000"; + + private static final String CODE = "code"; /** * 附加参数 */ - public static final String PASSBACK_PARAMS = "passback_params"; + private static final String PASSBACK_PARAMS = "passback_params"; /** * 产品代码 */ - public static final String PRODUCT_CODE = "product_code"; + private static final String PRODUCT_CODE = "product_code"; /** * 返回地址 */ - public static final String RETURN_URL = "return_url"; + private static final String RETURN_URL = "return_url"; /** * 请求内容 */ - public static final String BIZ_CONTENT = "biz_content"; + private static final String BIZ_CONTENT = "biz_content"; + /** + * 应用授权概述 + */ + private static final String APP_AUTH_TOKEN = "app_auth_token"; + /** + * 收款方信息 + */ + private static final String PAYEE_INFO = "payee_info"; /** * 获取对应的请求地址 @@ -193,7 +199,7 @@ public class AliPayService extends BasePayService { orderInfo.put("notify_url", payConfigStorage.getNotifyUrl()); orderInfo.put("format", "json"); - + setAppAuthToken(orderInfo, order.getAttrs()); Map bizContent = new TreeMap<>(); bizContent.put("body", order.getBody()); @@ -233,8 +239,8 @@ public class AliPayService extends BasePayService { if (null != order.getExpirationTime()) { bizContent.put(order.getTransactionType() == AliTransactionType.SWEEPPAY ? "qr_code_timeout_express" : "timeout_express", DateUtils.minutesRemaining(order.getExpirationTime()) + "m"); } + bizContent.putAll(order.getAttrs()); orderInfo.put(BIZ_CONTENT, JSON.toJSONString(bizContent)); - orderInfo.putAll(order.getAttrs()); return preOrderHandler(orderInfo, order); } @@ -363,7 +369,10 @@ public class AliPayService extends BasePayService { public Map settle(OrderSettle order){ //获取公共参数 Map parameters = getPublicParameters(AliTransactionType.SETTLE); - parameters.put(BIZ_CONTENT, JSON.toJSONString(order.toBizContent())); + setAppAuthToken(parameters, order.getAttrs()); + final Map bizContent = order.toBizContent(); + bizContent.putAll(order.getAttrs()); + parameters.put(BIZ_CONTENT, JSON.toJSONString(bizContent)); //设置签名 setSign(parameters); return getHttpRequestTemplate().postForObject(getReqUrl() + "?" + UriVariables.getMapToParameters(parameters), null, JSONObject.class); @@ -410,6 +419,16 @@ public class AliPayService extends BasePayService { return secondaryInterface(tradeNo, outTradeNo, AliTransactionType.CANCEL); } + /** + * 设置支付宝授权Token + * @param parameters 参数 + * @param attrs 订单属性 + * @return 参数 + */ + private void setAppAuthToken(Map parameters, Map attrs) { + setParameters(parameters, APP_AUTH_TOKEN, (String) attrs.remove(APP_AUTH_TOKEN)); + } + /** * 申请退款接口 @@ -421,12 +440,13 @@ public class AliPayService extends BasePayService { public Map refund(RefundOrder refundOrder) { //获取公共参数 Map parameters = getPublicParameters(AliTransactionType.REFUND); - + setAppAuthToken(parameters, refundOrder.getAttrs()); Map bizContent = getBizContent(refundOrder.getTradeNo(), refundOrder.getOutTradeNo(), null); if (!StringUtils.isEmpty(refundOrder.getRefundNo())) { bizContent.put("out_request_no", refundOrder.getRefundNo()); } bizContent.put("refund_amount", Util.conversionAmount(refundOrder.getRefundAmount())); + bizContent.putAll(refundOrder.getAttrs()); //设置请求参数的集合 parameters.put(BIZ_CONTENT, JSON.toJSONString(bizContent)); //设置签名 @@ -435,6 +455,7 @@ public class AliPayService extends BasePayService { } + /** * 查询退款 * @@ -443,17 +464,16 @@ public class AliPayService extends BasePayService { */ @Override public Map refundquery(RefundOrder refundOrder) { - //获取公共参数 Map parameters = getPublicParameters(AliTransactionType.REFUNDQUERY); - + setAppAuthToken(parameters, refundOrder.getAttrs()); Map bizContent = getBizContent(refundOrder.getTradeNo(), refundOrder.getOutTradeNo(), null); if (!StringUtils.isEmpty(refundOrder.getRefundNo())) { bizContent.put("out_request_no", refundOrder.getRefundNo()); } + bizContent.putAll(refundOrder.getAttrs()); //设置请求参数的集合 parameters.put(BIZ_CONTENT, JSON.toJSONString(bizContent)); - //设置签名 setSign(parameters); return requestTemplate.getForObject(getReqUrl() + "?" + UriVariables.getMapToParameters(parameters), JSONObject.class); @@ -484,6 +504,7 @@ public class AliPayService extends BasePayService { } + /** * @param tradeNoOrBillDate 支付平台订单号或者账单类型, 具体请 * 类型为{@link String }或者 {@link Date },类型须强制限制,类型不对应则抛出异常{@link PayErrorException} @@ -491,6 +512,7 @@ public class AliPayService extends BasePayService { * @param transactionType 交易类型 * @return 返回支付方对应接口的结果 */ + @Override public Map secondaryInterface(Object tradeNoOrBillDate, String outTradeNoBillType, TransactionType transactionType) { @@ -507,36 +529,38 @@ public class AliPayService extends BasePayService { //获取公共参数 Map parameters = getPublicParameters(transactionType); + //设置请求参数的集合 parameters.put(BIZ_CONTENT, getContentToJson((String) tradeNoOrBillDate, outTradeNoBillType)); //设置签名 setSign(parameters); + return requestTemplate.getForObject(getReqUrl() + "?" + UriVariables.getMapToParameters(parameters), JSONObject.class); } /** - * 转账 + * 新版转账转账 * * @param order 转账订单 * @return 对应的转账结果 */ @Override public Map transfer(TransferOrder order) { + final TransferType transferType = order.getTransferType(); //获取公共参数 - Map parameters = getPublicParameters(AliTransactionType.TRANS); + Map parameters = getPublicParameters(transferType); + setAppAuthToken(parameters, order.getAttrs()); - Map bizContent = new TreeMap(); + Map bizContent = new LinkedHashMap(); bizContent.put("out_biz_no", order.getOutNo()); - //默认 支付宝登录号,支持邮箱和手机号格式。 - bizContent.put("payee_type", "ALIPAY_LOGONID"); - if (null != order.getTransferType()) { - bizContent.put("payee_type", order.getTransferType().getType()); - } - bizContent.put("payee_account", order.getPayeeAccount()); - bizContent.put("amount", Util.conversionAmount(order.getAmount())); - bizContent.put("payer_show_name", order.getPayerName()); - bizContent.put("payee_real_name", order.getPayeeName()); + bizContent.put("trans_amount", order.getAmount()); + transferType.setAttr(bizContent, order); + setParameters(bizContent, "order_title", order); + setParameters(bizContent, "original_order_id", order); + setPayeeInfo(bizContent, order); bizContent.put("remark", order.getRemark()); + setParameters(bizContent, "business_params", order); + //设置请求参数的集合 parameters.put(BIZ_CONTENT, JSON.toJSONString(bizContent)); //设置签名 @@ -544,6 +568,24 @@ public class AliPayService extends BasePayService { return getHttpRequestTemplate().postForObject(getReqUrl() + "?" + UriVariables.getMapToParameters(parameters), null, JSONObject.class); } + private Map setPayeeInfo(Map bizContent, Order order){ + final Object attr = order.getAttr(PAYEE_INFO); + + if (attr instanceof String){ + bizContent.put(PAYEE_INFO, attr); + } + if (attr instanceof TreeMap){ + bizContent.put(PAYEE_INFO, attr); + } + if (attr instanceof Map){ + Map payeeInfo = new TreeMap((Map)attr); + bizContent.put(PAYEE_INFO, payeeInfo); + } + return bizContent; + } + + + /** * 转账查询 * @@ -554,7 +596,7 @@ public class AliPayService extends BasePayService { @Override public Map transferQuery(String outNo, String tradeNo) { //获取公共参数 - Map parameters = getPublicParameters(AliTransactionType.TRANS_QUERY); + Map parameters = getPublicParameters(AliTransferType.TRANS_QUERY); Map bizContent = new TreeMap(); if (StringUtils.isEmpty(outNo)) { diff --git a/pay-java-ali/src/main/java/com/egzosn/pay/ali/bean/AliTransferOrder.java b/pay-java-ali/src/main/java/com/egzosn/pay/ali/bean/AliTransferOrder.java index 1882f84..7a2b5f2 100644 --- a/pay-java-ali/src/main/java/com/egzosn/pay/ali/bean/AliTransferOrder.java +++ b/pay-java-ali/src/main/java/com/egzosn/pay/ali/bean/AliTransferOrder.java @@ -1,9 +1,13 @@ package com.egzosn.pay.ali.bean; +import com.alibaba.fastjson.JSONObject; import com.egzosn.pay.common.bean.TransferOrder; +import java.math.BigDecimal; + /** * 支付转账(红包)订单 + * * @author egan * date 2020/5/18 21:08 * email egzosn@gmail.com @@ -13,46 +17,137 @@ public class AliTransferOrder extends TransferOrder { private String orderTitle; private String identity; private String identityType; - private String name; private String businessParams; + /** + * 商户端的唯一订单号,对于同一笔转账请求,商户需保证该订单号唯一。 + * + * @return 商户端的唯一订单号 + */ + public String getOutBizNo() { + return getOutNo(); + } + + public void setOutBizNo(String outBizNo) { + setOutNo(outBizNo); + } + + /** + * 订单总金额,单位为元,精确到小数点后两位,STD_RED_PACKET产品取值范围[0.01,100000000]; + * TRANS_ACCOUNT_NO_PWD产品取值范围[0.1,100000000] + * + * @return 订单总金额 + */ + public BigDecimal getTransAmount() { + return getAmount(); + } + + public void setTransAmount(BigDecimal transAmount) { + setAmount(transAmount); + } + + /** + * 转账业务的标题,用于在支付宝用户的账单里显示 + * + * @return 转账业务的标题 + */ public String getOrderTitle() { - return orderTitle; + return (String) getAttr("order_title"); } public void setOrderTitle(String orderTitle) { - this.orderTitle = orderTitle; + addAttr("order_title", orderTitle); } + /** + * 描述特定的业务场景,可传的参数如下: + * DIRECT_TRANSFER:单笔无密转账到支付宝/银行卡, B2C现金红包; + * PERSONAL_COLLECTION:C2C现金红包-领红包 + * + * @return 描述特定的业务场景 + */ + public String getBizScene() { + return (String) getAttr("biz_scene"); + } + + public void setBizScene(String bizScene) { + addAttr("biz_scene", bizScene); + } + + /** + * 收款方信息 + * + * @return 收款方信息 + */ + public JSONObject getPayeeinfo() { + JSONObject payeeInfo = (JSONObject) getAttr("payee_info"); + if (null == payeeInfo) { + payeeInfo = new JSONObject(); + addAttr("payee_info", payeeInfo); + } + return payeeInfo; + } + + /** + * 参与方的唯一标识 + * + * @return 参与方的唯一标识 + */ public String getIdentity() { return identity; } public void setIdentity(String identity) { this.identity = identity; + getPayeeinfo().put("identity", identity); } + /** + * 参与方的标识类型,目前支持如下类型: + * 1、ALIPAY_USER_ID 支付宝的会员ID + * 2、ALIPAY_LOGON_ID:支付宝登录号,支持邮箱和手机号格式 + * + * @return + */ public String getIdentityType() { return identityType; } public void setIdentityType(String identityType) { this.identityType = identityType; + getPayeeinfo().put("identity_type", identityType); } + + /** + * 参与方真实姓名 + * + * @return 参与方真实姓名 + */ public String getName() { - return name; + return getPayeeName(); } public void setName(String name) { - this.name = name; + setPayeeName(name); + getPayeeinfo().put("name", name); } + /** + * 转账业务请求的扩展参数,支持传入的扩展参数如下: + * 1、sub_biz_scene 子业务场景,红包业务必传,取值REDPACKET,C2C现金红包、B2C现金红包均需传入; + *

+ * 2、withdraw_timeliness为转账到银行卡的预期到账时间,可选(不传入则默认为T1),取值T0表示预期T+0到账,取值T1表示预期T+1到账,因到账时效受银行机构处理影响,支付宝无法保证一定是T0或者T1到账; + * + * @return 转账业务请求的扩展参数 + */ public String getBusinessParams() { - return businessParams; + return (String) getAttr("business_params"); } public void setBusinessParams(String businessParams) { - this.businessParams = businessParams; + addAttr("business_params", businessParams); } + + } diff --git a/pay-java-ali/src/main/java/com/egzosn/pay/ali/bean/AliTransferType.java b/pay-java-ali/src/main/java/com/egzosn/pay/ali/bean/AliTransferType.java index 0d8459f..df10e73 100644 --- a/pay-java-ali/src/main/java/com/egzosn/pay/ali/bean/AliTransferType.java +++ b/pay-java-ali/src/main/java/com/egzosn/pay/ali/bean/AliTransferType.java @@ -1,15 +1,16 @@ package com.egzosn.pay.ali.bean; +import com.egzosn.pay.common.bean.TransferOrder; import com.egzosn.pay.common.bean.TransferType; -import java.util.HashMap; import java.util.Map; /** - * 收款方账户类型 + * 收款方账户类型 + * * @author egan - * email egzosn@gmail.com - * date 2018/9/28.20:32 + * email egzosn@gmail.com + * date 2018/9/28.20:32 */ public enum AliTransferType implements TransferType { /** @@ -27,8 +28,7 @@ public enum AliTransferType implements TransferType { /** * 现金红包无线支付接口 */ - STD_RED_PACKET_APP("alipay.fund.trans.app.pay", "PERSONAL_PAY"){ - + STD_RED_PACKET_APP("alipay.fund.trans.app.pay", "PERSONAL_PAY") { /** * 获取转账类型 * @@ -43,9 +43,7 @@ public enum AliTransferType implements TransferType { /** * 转账查询 */ - TRANS_QUERY("alipay.fund.trans.order.query") - - ; + TRANS_QUERY("alipay.fund.trans.order.query"); /** * 接口名称 */ @@ -90,10 +88,13 @@ public enum AliTransferType implements TransferType { /** * 设置属性 + * * @param attr 已有属性对象 + * @param order 转账订单 * @return 属性对象 */ - public Map setAttr(Map attr){ + @Override + public Map setAttr(Map attr, TransferOrder order) { attr.put("product_code", getType()); attr.put("biz_scene", getBizScene()); return attr; diff --git a/pay-java-ali/src/main/java/com/egzosn/pay/ali/bean/IdentityType.java b/pay-java-ali/src/main/java/com/egzosn/pay/ali/bean/IdentityType.java deleted file mode 100644 index 955c8a4..0000000 --- a/pay-java-ali/src/main/java/com/egzosn/pay/ali/bean/IdentityType.java +++ /dev/null @@ -1,19 +0,0 @@ -package com.egzosn.pay.ali.bean; - -/** - * 参与方的标识类型 - * @author egan - * date 2020/5/19 21:45 - * email egzosn@gmail.com - */ -public enum IdentityType { - /** - * 支付宝的会员ID - */ - ALIPAY_USER_ID, - /** - * 支付宝登录号,支持邮箱和手机号格式 - */ - ALIPAY_LOGON_ID - -} diff --git a/pay-java-ali/src/main/java/com/egzosn/pay/ali/bean/OrderSettle.java b/pay-java-ali/src/main/java/com/egzosn/pay/ali/bean/OrderSettle.java index f4227b9..e111bc1 100644 --- a/pay-java-ali/src/main/java/com/egzosn/pay/ali/bean/OrderSettle.java +++ b/pay-java-ali/src/main/java/com/egzosn/pay/ali/bean/OrderSettle.java @@ -1,9 +1,11 @@ package com.egzosn.pay.ali.bean; +import com.egzosn.pay.common.bean.Order; import com.egzosn.pay.common.util.Util; import com.egzosn.pay.common.util.str.StringUtils; import java.math.BigDecimal; +import java.util.HashMap; import java.util.Map; import java.util.TreeMap; @@ -13,7 +15,7 @@ import java.util.TreeMap; * email egzosn@gmail.com * date 2019/4/28.20:29 */ -public class OrderSettle { +public class OrderSettle implements Order { /** * 结算请求流水号 开发者自行生成并保证唯一性 @@ -52,6 +54,11 @@ public class OrderSettle { */ private String operatorId; + /** + * 订单附加信息,可用于预设未提供的参数,这里会覆盖以上所有的订单信息, + */ + private Map attr; + public String getOutRequestNo() { return outRequestNo; } @@ -158,6 +165,28 @@ public class OrderSettle { return bizContent; } + @Override + public Map getAttrs() { + if (null == attr){ + attr = new HashMap<>(); + } + return attr; + } + @Override + public Object getAttr(String key) { + return getAttrs().get(key); + } + + + /** + * 添加订单信息 + * @param key key + * @param value 值 + */ + @Override + public void addAttr(String key, Object value) { + getAttrs().put(key, value); + } } -- Gitee From 00a44a8c29971ddc377ec410e4e416c35b177063 Mon Sep 17 00:00:00 2001 From: egan Date: Tue, 26 May 2020 11:06:11 +0800 Subject: [PATCH 129/299] update README.md. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index c5d602a..2675b6f 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -全能第三方支付对接Java开发工具包.优雅的轻量级支付模块集成支付对接支付整合(微信,支付宝,银联,友店,富友,跨境支付paypal,payoneer(P卡派安盈)易极付)app,扫码,网页支付刷卡付条码付刷脸付转账服务商模式、支持多种支付类型多支付账户,支付与业务完全剥离,简单几行代码即可实现支付,简单快速完成支付模块的开发,可轻松嵌入到任何系统里 目前仅是一个开发工具包(即SDK),只提供简单Web实现,建议使用maven或gradle引用本项目即可使用本SDK提供的各种支付相关的功能 +全能第三方支付对接Java开发工具包.优雅的轻量级支付模块集成支付对接支付整合(微信,支付宝,银联,友店,富友,跨境支付paypal,payoneer(P卡派安盈)易极付)app,扫码,网页支付刷卡付条码付刷脸付转账红包服务商模式、支持多种支付类型多支付账户,支付与业务完全剥离,简单几行代码即可实现支付,简单快速完成支付模块的开发,可轻松嵌入到任何系统里 目前仅是一个开发工具包(即SDK),只提供简单Web实现,建议使用maven或gradle引用本项目即可使用本SDK提供的各种支付相关的功能 ### 特性 -- Gitee From 550099ea2911ed8a14b6e8ace0b551e4062b0562 Mon Sep 17 00:00:00 2001 From: egzosn <930928.lI1> Date: Mon, 25 May 2020 00:01:31 +0800 Subject: [PATCH 130/299] =?UTF-8?q?=E6=94=AF=E4=BB=98=E5=AE=9D=E8=BD=AC?= =?UTF-8?q?=E8=B4=A6=EF=BC=88=E7=BA=A2=E5=8C=85=EF=BC=89=E8=AE=A2=E5=8D=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../egzosn/pay/ali/bean/AliTransferOrder.java | 22 ++++++++++--------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/pay-java-ali/src/main/java/com/egzosn/pay/ali/bean/AliTransferOrder.java b/pay-java-ali/src/main/java/com/egzosn/pay/ali/bean/AliTransferOrder.java index 7a2b5f2..3b55b7b 100644 --- a/pay-java-ali/src/main/java/com/egzosn/pay/ali/bean/AliTransferOrder.java +++ b/pay-java-ali/src/main/java/com/egzosn/pay/ali/bean/AliTransferOrder.java @@ -1,9 +1,9 @@ package com.egzosn.pay.ali.bean; -import com.alibaba.fastjson.JSONObject; import com.egzosn.pay.common.bean.TransferOrder; import java.math.BigDecimal; +import java.util.TreeMap; /** * 支付转账(红包)订单 @@ -14,10 +14,8 @@ import java.math.BigDecimal; */ public class AliTransferOrder extends TransferOrder { - private String orderTitle; private String identity; private String identityType; - private String businessParams; /** * 商户端的唯一订单号,对于同一笔转账请求,商户需保证该订单号唯一。 @@ -79,13 +77,17 @@ public class AliTransferOrder extends TransferOrder { * * @return 收款方信息 */ - public JSONObject getPayeeinfo() { - JSONObject payeeInfo = (JSONObject) getAttr("payee_info"); - if (null == payeeInfo) { - payeeInfo = new JSONObject(); - addAttr("payee_info", payeeInfo); + private TreeMap getPayeeinfo() { + Object payeeInfo = getAttr("payee_info"); + if (null == payeeInfo ){ + TreeMap payee = new TreeMap<>(); + addAttr("payee_info", payee); + return payee; + }else if (payeeInfo instanceof TreeMap){ + return (TreeMap) payeeInfo; } - return payeeInfo; + return null; + } /** @@ -107,7 +109,7 @@ public class AliTransferOrder extends TransferOrder { * 1、ALIPAY_USER_ID 支付宝的会员ID * 2、ALIPAY_LOGON_ID:支付宝登录号,支持邮箱和手机号格式 * - * @return + * @return 参与方的标识类型 */ public String getIdentityType() { return identityType; -- Gitee From 674e2b81f0c495496cb70b71c6770819439e3342 Mon Sep 17 00:00:00 2001 From: egzosn <930928.lI1> Date: Mon, 25 May 2020 00:02:27 +0800 Subject: [PATCH 131/299] =?UTF-8?q?=E7=A7=BB=E9=99=A4=E8=BF=87=E6=97=B6?= =?UTF-8?q?=E7=9A=84=E9=80=80=E6=AC=BE=E6=96=B9=E6=B3=95=20app=E6=8E=A5?= =?UTF-8?q?=E5=8F=A3=E6=A1=88=E4=BE=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/egzosn/pay/demo/controller/PayController.java | 4 ++-- .../com/egzosn/pay/demo/controller/UnionPayController.java | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) 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 c407a58..c7adb42 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 @@ -167,14 +167,14 @@ public class PayController { * @param price 金额 * @return 支付预订单信息 */ - @RequestMapping("getOrderInfo") + @RequestMapping("app") public Map getOrderInfo(Integer payId, String transactionType, BigDecimal price) { //获取对应的支付账户操作工具(可根据账户id) PayResponse payResponse = service.getPayResponse(payId); Map data = new HashMap<>(); data.put("code", 0); PayOrder order = new PayOrder("订单title", "摘要", null == price ? new BigDecimal(0.01) : price, UUID.randomUUID().toString().replace("-", ""), PayType.valueOf(payResponse.getStorage().getPayType()).getTransactionType(transactionType)); - data.put("orderInfo", payResponse.getService().orderInfo(order)); + data.put("orderInfo", payResponse.getService().app(order)); return data; } diff --git a/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/UnionPayController.java b/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/UnionPayController.java index b7b4b61..8bda76f 100644 --- a/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/UnionPayController.java +++ b/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/UnionPayController.java @@ -139,7 +139,7 @@ public class UnionPayController { //APPLE支付 苹果付 // order.setTransactionType(UnionTransactionType.APPLE); - data.put("orderInfo", service.orderInfo(order)); + data.put("orderInfo", service.app(order)); return data; } -- Gitee From eecc98910ca8e9be893e2b3627647b7f82c65f46 Mon Sep 17 00:00:00 2001 From: egzosn <930928.lI1> Date: Mon, 25 May 2020 00:02:56 +0800 Subject: [PATCH 132/299] =?UTF-8?q?=E8=BD=AC=E8=B4=A6=EF=BC=88=E7=BA=A2?= =?UTF-8?q?=E5=8C=85=EF=BC=89=E6=8E=A5=E5=85=A5=E6=A1=88=E4=BE=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../pay/demo/controller/AliPayController.java | 36 +++++++------------ .../pay/demo/controller/WxPayController.java | 12 ------- 2 files changed, 13 insertions(+), 35 deletions(-) diff --git a/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/AliPayController.java b/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/AliPayController.java index 93a50af..7ee3fbf 100644 --- a/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/AliPayController.java +++ b/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/AliPayController.java @@ -5,12 +5,11 @@ package com.egzosn.pay.demo.controller; import com.egzosn.pay.ali.api.AliPayConfigStorage; import com.egzosn.pay.ali.api.AliPayService; import com.egzosn.pay.ali.bean.AliTransactionType; +import com.egzosn.pay.ali.bean.AliTransferOrder; import com.egzosn.pay.ali.bean.AliTransferType; import com.egzosn.pay.ali.bean.OrderSettle; import com.egzosn.pay.common.bean.PayOrder; import com.egzosn.pay.common.bean.RefundOrder; -import com.egzosn.pay.common.bean.TransactionType; -import com.egzosn.pay.common.bean.TransferOrder; import com.egzosn.pay.common.http.HttpConfigStorage; import com.egzosn.pay.common.http.UriVariables; import com.egzosn.pay.common.util.sign.SignUtils; @@ -307,18 +306,6 @@ public class AliPayController { } - /** - * 通用查询接口,根据 AliTransactionType 类型进行实现,此接口不包括退款 - * - * @param order 订单的请求体 - * @return 返回支付方对应接口的结果 - */ - @RequestMapping("secondaryInterface") - public Map secondaryInterface(QueryOrder order) { - TransactionType type = AliTransactionType.valueOf(order.getTransactionType()); - return service.secondaryInterface(order.getTradeNoOrBillDate(), order.getOutTradeNoBillType(), type); - } - /** * 转账 * @@ -327,15 +314,18 @@ public class AliPayController { * @return 对应的转账结果 */ @RequestMapping("transfer") - public Map transfer(TransferOrder order) { -// order.setOutNo("转账单号"); -// order.setPayeeAccount("收款方账户,支付宝登录号,支持邮箱和手机号格式"); -// order.setAmount(new BigDecimal(10)); -// order.setPayerName("付款方姓名, 非必填"); -// order.setPayeeName("收款方真实姓名, 非必填"); -// order.setRemark("转账备注, 非必填"); - //收款方账户类型 ,默认值 ALIPAY_LOGONID:支付宝登录号,支持邮箱和手机号格式。 - order.setTransferType(AliTransferType.ALIPAY_LOGONID); + public Map transfer(AliTransferOrder order) { + order.setOutBizNo("转账单号"); + order.setTransAmount(new BigDecimal(10)); + order.setOrderTitle("转账业务的标题"); + order.setIdentity("参与方的唯一标识"); + order.setIdentityType("参与方的标识类型,目前支持如下类型:"); + order.setName("参与方真实姓名"); + order.setRemark("转账备注, 非必填"); + //单笔无密转账到支付宝账户 + order.setTransferType(AliTransferType.TRANS_ACCOUNT_NO_PWD); + //单笔无密转账到银行卡 +// order.setTransferType(AliTransferType.TRANS_BANKCARD_NO_PWD); return service.transfer(order); } diff --git a/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/WxPayController.java b/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/WxPayController.java index 0728800..096f609 100644 --- a/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/WxPayController.java +++ b/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/WxPayController.java @@ -339,18 +339,6 @@ public class WxPayController { } - /** - * 通用查询接口,根据 WxTransactionType 类型进行实现,此接口不包括退款 - * - * @param order 订单的请求体 - * @return 返回支付方对应接口的结果 - */ - @RequestMapping("secondaryInterface") - public Map secondaryInterface(QueryOrder order) { - TransactionType type = WxTransactionType.valueOf(order.getTransactionType()); - return service.secondaryInterface(order.getTradeNoOrBillDate(), order.getOutTradeNoBillType(), type); - } - /** -- Gitee From c71add18c6137bc527cf39cffe59f2c01b24b2f3 Mon Sep 17 00:00:00 2001 From: egzosn <930928.lI1> Date: Mon, 25 May 2020 00:06:21 +0800 Subject: [PATCH 133/299] =?UTF-8?q?=E6=94=AF=E4=BB=98=E5=AE=9D=E8=BD=AC?= =?UTF-8?q?=E8=B4=A6=EF=BC=88=E7=BA=A2=E5=8C=85=EF=BC=89=E8=AE=A2=E5=8D=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/egzosn/pay/ali/bean/AliTransferOrder.java | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/pay-java-ali/src/main/java/com/egzosn/pay/ali/bean/AliTransferOrder.java b/pay-java-ali/src/main/java/com/egzosn/pay/ali/bean/AliTransferOrder.java index 3b55b7b..31c26e3 100644 --- a/pay-java-ali/src/main/java/com/egzosn/pay/ali/bean/AliTransferOrder.java +++ b/pay-java-ali/src/main/java/com/egzosn/pay/ali/bean/AliTransferOrder.java @@ -79,14 +79,12 @@ public class AliTransferOrder extends TransferOrder { */ private TreeMap getPayeeinfo() { Object payeeInfo = getAttr("payee_info"); - if (null == payeeInfo ){ - TreeMap payee = new TreeMap<>(); - addAttr("payee_info", payee); - return payee; - }else if (payeeInfo instanceof TreeMap){ + if (null != payeeInfo && payeeInfo instanceof TreeMap){ return (TreeMap) payeeInfo; } - return null; + TreeMap payee = new TreeMap<>(); + addAttr("payee_info", payee); + return payee; } -- Gitee From b0d6141152dc0537f01de39c909964ea4675448a Mon Sep 17 00:00:00 2001 From: egzosn <930928.lI1> Date: Wed, 27 May 2020 09:54:27 +0800 Subject: [PATCH 134/299] =?UTF-8?q?=E5=BE=AE=E4=BF=A1=E7=BA=A2=E5=8C=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/egzosn/pay/wx/bean/WxSendredpackType.java | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/pay-java-wx/src/main/java/com/egzosn/pay/wx/bean/WxSendredpackType.java b/pay-java-wx/src/main/java/com/egzosn/pay/wx/bean/WxSendredpackType.java index 623b667..248e6f4 100644 --- a/pay-java-wx/src/main/java/com/egzosn/pay/wx/bean/WxSendredpackType.java +++ b/pay-java-wx/src/main/java/com/egzosn/pay/wx/bean/WxSendredpackType.java @@ -1,10 +1,13 @@ package com.egzosn.pay.wx.bean; +import com.egzosn.pay.common.bean.TransferOrder; import com.egzosn.pay.common.bean.TransferType; +import java.util.Map; + /** * @description: 红包交易类型 - * @author: faymanwang + * @author faymanwang * @time: 2020/5/14 20:11 */ public enum WxSendredpackType implements TransferType { @@ -40,4 +43,9 @@ public enum WxSendredpackType implements TransferType { public String getMethod() { return this.method; } + + @Override + public Map setAttr(Map attr, TransferOrder order) { + return attr; + } } -- Gitee From a41615e9a26eab2587fc3660b27824803a3a73e9 Mon Sep 17 00:00:00 2001 From: egzosn Date: Mon, 1 Jun 2020 22:35:41 +0800 Subject: [PATCH 135/299] =?UTF-8?q?=E7=89=88=E6=9C=AC=E5=8D=87=E7=BA=A72.1?= =?UTF-8?q?3.2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 1 - pay-java-ali/pom.xml | 2 +- pay-java-baidu/pom.xml | 2 +- pay-java-common/pom.xml | 2 +- pay-java-demo/pom.xml | 2 +- .../com/egzosn/pay/demo/controller/UnionPayController.java | 2 +- pay-java-fuiou/pom.xml | 2 +- pay-java-payoneer/pom.xml | 2 +- pay-java-paypal/pom.xml | 2 +- pay-java-union/pom.xml | 2 +- .../main/java/com/egzosn/pay/union/api/UnionPayService.java | 4 +++- pay-java-wx-youdian/pom.xml | 2 +- pay-java-wx/pom.xml | 2 +- pay-java-yiji/pom.xml | 2 +- pom.xml | 4 ++-- 15 files changed, 17 insertions(+), 16 deletions(-) diff --git a/.gitignore b/.gitignore index 4ed83a9..770ee41 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,3 @@ -<<<<<<< HEAD # Compiled class file *.class diff --git a/pay-java-ali/pom.xml b/pay-java-ali/pom.xml index d909904..afec0f0 100644 --- a/pay-java-ali/pom.xml +++ b/pay-java-ali/pom.xml @@ -5,7 +5,7 @@ pay-java-parent com.egzosn - 2.13.2-SNAPSHOT + 2.13.2 4.0.0 pay-java-ali diff --git a/pay-java-baidu/pom.xml b/pay-java-baidu/pom.xml index 9713046..133d4d8 100644 --- a/pay-java-baidu/pom.xml +++ b/pay-java-baidu/pom.xml @@ -5,7 +5,7 @@ pay-java-parent com.egzosn - 2.13.2-SNAPSHOT + 2.13.2 4.0.0 pay-java-baidu diff --git a/pay-java-common/pom.xml b/pay-java-common/pom.xml index 9a0d533..aae7d6c 100644 --- a/pay-java-common/pom.xml +++ b/pay-java-common/pom.xml @@ -5,7 +5,7 @@ pay-java-parent com.egzosn - 2.13.2-SNAPSHOT + 2.13.2 4.0.0 jar diff --git a/pay-java-demo/pom.xml b/pay-java-demo/pom.xml index c02296e..c95b537 100644 --- a/pay-java-demo/pom.xml +++ b/pay-java-demo/pom.xml @@ -5,7 +5,7 @@ pay-java-parent com.egzosn - 2.13.2-SNAPSHOT + 2.13.2 4.0.0 war diff --git a/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/UnionPayController.java b/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/UnionPayController.java index 8bda76f..6ced1dc 100644 --- a/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/UnionPayController.java +++ b/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/UnionPayController.java @@ -118,7 +118,7 @@ public class UnionPayController { //手机控件支付产品 PayOrder order = new PayOrder("订单title", "摘要", null == price ? new BigDecimal(0.01) : price, UUID.randomUUID().toString().replace("-", "") ,UnionTransactionType.WAP); - return service.sendHttpRequest(order); + return service.app(order); } diff --git a/pay-java-fuiou/pom.xml b/pay-java-fuiou/pom.xml index b3c1383..e5fe927 100644 --- a/pay-java-fuiou/pom.xml +++ b/pay-java-fuiou/pom.xml @@ -5,7 +5,7 @@ pay-java-parent com.egzosn - 2.13.2-SNAPSHOT + 2.13.2 4.0.0 pay-java-fuiou diff --git a/pay-java-payoneer/pom.xml b/pay-java-payoneer/pom.xml index 22940d6..2f48989 100644 --- a/pay-java-payoneer/pom.xml +++ b/pay-java-payoneer/pom.xml @@ -5,7 +5,7 @@ pay-java-parent com.egzosn - 2.13.2-SNAPSHOT + 2.13.2 4.0.0 pay-java-payoneer diff --git a/pay-java-paypal/pom.xml b/pay-java-paypal/pom.xml index 7f56a2e..69a23cb 100644 --- a/pay-java-paypal/pom.xml +++ b/pay-java-paypal/pom.xml @@ -5,7 +5,7 @@ pay-java-parent com.egzosn - 2.13.2-SNAPSHOT + 2.13.2 4.0.0 diff --git a/pay-java-union/pom.xml b/pay-java-union/pom.xml index 61cee3e..6f98cb3 100644 --- a/pay-java-union/pom.xml +++ b/pay-java-union/pom.xml @@ -5,7 +5,7 @@ pay-java-parent com.egzosn - 2.13.2-SNAPSHOT + 2.13.2 4.0.0 diff --git a/pay-java-union/src/main/java/com/egzosn/pay/union/api/UnionPayService.java b/pay-java-union/src/main/java/com/egzosn/pay/union/api/UnionPayService.java index 6e97c3d..419beb0 100644 --- a/pay-java-union/src/main/java/com/egzosn/pay/union/api/UnionPayService.java +++ b/pay-java-union/src/main/java/com/egzosn/pay/union/api/UnionPayService.java @@ -516,7 +516,9 @@ public class UnionPayService extends BasePayService { */ @Override public Map app(PayOrder order){ - order.setTransactionType(UnionTransactionType.APP); + if (null == order.getTransactionType()){ + order.setTransactionType(UnionTransactionType.APP); + } JSONObject response = postOrder(order); if (this.verify(response)) { if (SDKConstants.OK_RESP_CODE.equals(response.get(SDKConstants.param_respCode))) { diff --git a/pay-java-wx-youdian/pom.xml b/pay-java-wx-youdian/pom.xml index 7ce65c9..0adb30f 100644 --- a/pay-java-wx-youdian/pom.xml +++ b/pay-java-wx-youdian/pom.xml @@ -5,7 +5,7 @@ pay-java-parent com.egzosn - 2.13.2-SNAPSHOT + 2.13.2 4.0.0 pay-java-wx-youdian diff --git a/pay-java-wx/pom.xml b/pay-java-wx/pom.xml index a305a8c..696288d 100644 --- a/pay-java-wx/pom.xml +++ b/pay-java-wx/pom.xml @@ -5,7 +5,7 @@ pay-java-parent com.egzosn - 2.13.2-SNAPSHOT + 2.13.2 4.0.0 pay-java-wx diff --git a/pay-java-yiji/pom.xml b/pay-java-yiji/pom.xml index 44ba156..40a4186 100644 --- a/pay-java-yiji/pom.xml +++ b/pay-java-yiji/pom.xml @@ -5,7 +5,7 @@ pay-java-parent com.egzosn - 2.13.2-SNAPSHOT + 2.13.2 4.0.0 diff --git a/pom.xml b/pom.xml index f2ecd1f..b6e033c 100644 --- a/pom.xml +++ b/pom.xml @@ -7,7 +7,7 @@ com.egzosn pay-java-parent pom - 2.13.2-SNAPSHOT + 2.13.2 Pay Java - Parent Pay Java Parent @@ -64,7 +64,7 @@ - 2.13.2-SNAPSHOT + 2.13.2 4.5.4 1.2.17 1.2.58 -- Gitee From b0e83247dc1b6408a0e582ef4d30f8da3d2d5646 Mon Sep 17 00:00:00 2001 From: egzosn Date: Mon, 1 Jun 2020 23:12:18 +0800 Subject: [PATCH 136/299] =?UTF-8?q?=E7=89=88=E6=9C=AC=E5=8D=87=E7=BA=A72.1?= =?UTF-8?q?3.2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../pay/common/util/sign/encrypt/RSA.java | 3 ++- .../pay/common/util/sign/encrypt/RSA2.java | 18 ++++++++++++++---- .../java/com/egzosn/pay/wx/api/WxConst.java | 16 +++++----------- .../com/egzosn/pay/wx/api/WxPayService.java | 1 - .../com/egzosn/pay/wx/bean/RedpackOrder.java | 10 +++++++--- .../com/egzosn/pay/wx/bean/WxPayError.java | 2 +- .../egzosn/pay/wx/bean/WxSendredpackType.java | 4 ++-- pom.xml | 2 +- 8 files changed, 32 insertions(+), 24 deletions(-) diff --git a/pay-java-common/src/main/java/com/egzosn/pay/common/util/sign/encrypt/RSA.java b/pay-java-common/src/main/java/com/egzosn/pay/common/util/sign/encrypt/RSA.java index 3bcf772..d75d1c8 100644 --- a/pay-java-common/src/main/java/com/egzosn/pay/common/util/sign/encrypt/RSA.java +++ b/pay-java-common/src/main/java/com/egzosn/pay/common/util/sign/encrypt/RSA.java @@ -175,7 +175,8 @@ public class RSA{ * @param privateKey 商户私钥 * @param characterEncoding 编码格式 * @return 解密后的字符串 - * @throws Exception 解密异常 + * @throws GeneralSecurityException 解密异常 + * @throws IOException IOException */ public static String decrypt(String content, String privateKey, String characterEncoding) throws GeneralSecurityException, IOException { PrivateKey prikey = getPrivateKey(privateKey); diff --git a/pay-java-common/src/main/java/com/egzosn/pay/common/util/sign/encrypt/RSA2.java b/pay-java-common/src/main/java/com/egzosn/pay/common/util/sign/encrypt/RSA2.java index 5aa464d..46cae1c 100644 --- a/pay-java-common/src/main/java/com/egzosn/pay/common/util/sign/encrypt/RSA2.java +++ b/pay-java-common/src/main/java/com/egzosn/pay/common/util/sign/encrypt/RSA2.java @@ -63,7 +63,8 @@ public class RSA2 { * @param privateKey 商户私钥 * @param characterEncoding 编码格式 * @return 解密后的字符串 - * @throws Exception 解密异常 + * @throws GeneralSecurityException 解密异常 + * @throws IOException 解密异常 */ public static String decrypt(String content, String privateKey, String characterEncoding) throws GeneralSecurityException, IOException { return RSA.decrypt(content, privateKey, characterEncoding); @@ -73,14 +74,23 @@ public class RSA2 { /** * 得到私钥 * @param key 密钥字符串(经过base64编码) - * @throws Exception 加密异常 + * @throws GeneralSecurityException 加密异常 * @return 私钥 */ - public static PrivateKey getPrivateKey(String key) throws Exception { + public static PrivateKey getPrivateKey(String key) throws GeneralSecurityException { return RSA.getPrivateKey(key); } - + /** + * + * @param content 加密文本 + * @param publicKey 公钥 + * @param cipherAlgorithm 算法 + * @param characterEncoding 编码类型 + * @return 加密后文本 + * @throws GeneralSecurityException 加密异常 + * @throws IOException IOException + */ public static String encrypt(String content, String publicKey, String cipherAlgorithm, String characterEncoding ) throws GeneralSecurityException, IOException { return Base64.encode(RSA.encrypt(content.getBytes(characterEncoding), RSA.getPublicKey(publicKey), 2048, 11, cipherAlgorithm)); } diff --git a/pay-java-wx/src/main/java/com/egzosn/pay/wx/api/WxConst.java b/pay-java-wx/src/main/java/com/egzosn/pay/wx/api/WxConst.java index 08994f3..b2cac70 100644 --- a/pay-java-wx/src/main/java/com/egzosn/pay/wx/api/WxConst.java +++ b/pay-java-wx/src/main/java/com/egzosn/pay/wx/api/WxConst.java @@ -1,18 +1,12 @@ package com.egzosn.pay.wx.api; -import com.egzosn.pay.common.bean.Order; -import com.egzosn.pay.common.bean.PayOrder; - -import java.math.BigDecimal; -import java.util.HashMap; -import java.util.Map; - /** + * 常量 * @author egan - * @date 2020/3/10 21:22 - * 郑灶生 - *
- * email: zheng.zaosheng@iwhalecloud.com + *

+ * email egzosn@gmail.com
+ * date 2020/3/10 21:22
+ * 
*/ public interface WxConst { /** 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 feaff86..f6bb106 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 @@ -677,7 +677,6 @@ public class WxPayService extends BasePayService implements * @param outNo 商户转账订单号 * @param wxTransferType 微信转账类型,.....这里没办法了只能这样写(┬_┬),请见谅 {@link com.egzosn.pay.wx.bean.WxTransferType} *

- *

* 企业付款到零钱 * 商户企业付款到银行卡 *

diff --git a/pay-java-wx/src/main/java/com/egzosn/pay/wx/bean/RedpackOrder.java b/pay-java-wx/src/main/java/com/egzosn/pay/wx/bean/RedpackOrder.java index 300c85e..8354f3c 100644 --- a/pay-java-wx/src/main/java/com/egzosn/pay/wx/bean/RedpackOrder.java +++ b/pay-java-wx/src/main/java/com/egzosn/pay/wx/bean/RedpackOrder.java @@ -5,9 +5,9 @@ import com.egzosn.pay.common.bean.TransferOrder; import java.math.BigDecimal; /** - * @description: 发红包订单 - * @author: 保网 faymanwang 1057438332@qq.com - * @time: 2020/5/15 12:40 + * 发红包订单 + * @author 保网 faymanwang 1057438332@qq.com + * 2020/5/15 12:40 */ public class RedpackOrder extends TransferOrder { @@ -64,6 +64,7 @@ public class RedpackOrder extends TransferOrder { * 红包发放总人数 * 普通红包:1 * 裂变:必须介于(包括)3到20之间 + * @return 红包发放总人数 */ public int getTotalNum() { Object totalNum = getAttr("total_num"); @@ -75,6 +76,7 @@ public class RedpackOrder extends TransferOrder { } /** * 红包祝福语 + * @return 红包祝福语 */ public String getWishing() { return (String) getAttr("wishing"); @@ -88,6 +90,7 @@ public class RedpackOrder extends TransferOrder { /** * 活动名称 + * @return 活动名称 */ public String getActName() { return (String) getAttr("act_name"); @@ -110,6 +113,7 @@ public class RedpackOrder extends TransferOrder { * PRODUCT_6:保险回馈 * PRODUCT_7:彩票派奖 * PRODUCT_8:税务刮奖 + * @param sceneId 红包使用场景 */ public void setSceneId(String sceneId) { addAttr("scene_id", sceneId); diff --git a/pay-java-wx/src/main/java/com/egzosn/pay/wx/bean/WxPayError.java b/pay-java-wx/src/main/java/com/egzosn/pay/wx/bean/WxPayError.java index ab86676..f079de1 100644 --- a/pay-java-wx/src/main/java/com/egzosn/pay/wx/bean/WxPayError.java +++ b/pay-java-wx/src/main/java/com/egzosn/pay/wx/bean/WxPayError.java @@ -21,7 +21,7 @@ import com.egzosn.pay.common.bean.result.PayError; /** * 微信支付异常 - * @author: egan + * @author egan *
  *
  * email egzosn@gmail.com
diff --git a/pay-java-wx/src/main/java/com/egzosn/pay/wx/bean/WxSendredpackType.java b/pay-java-wx/src/main/java/com/egzosn/pay/wx/bean/WxSendredpackType.java
index 248e6f4..27b1bae 100644
--- a/pay-java-wx/src/main/java/com/egzosn/pay/wx/bean/WxSendredpackType.java
+++ b/pay-java-wx/src/main/java/com/egzosn/pay/wx/bean/WxSendredpackType.java
@@ -6,9 +6,9 @@ import com.egzosn.pay.common.bean.TransferType;
 import java.util.Map;
 
 /**
- * @description: 红包交易类型
+ *  红包交易类型
  * @author faymanwang
- * @time: 2020/5/14 20:11
+ *  2020/5/14 20:11
  */
 public enum WxSendredpackType  implements TransferType {
     /**
diff --git a/pom.xml b/pom.xml
index b6e033c..9b7e104 100644
--- a/pom.xml
+++ b/pom.xml
@@ -58,7 +58,7 @@
         pay-java-paypal
         pay-java-yiji
         pay-java-baidu
-        pay-java-demo
+        
 
     
 
-- 
Gitee


From ea2d1d5ae58d8a9a1271d754772ba3daf8eef91e Mon Sep 17 00:00:00 2001
From: egzosn 
Date: Mon, 1 Jun 2020 23:21:19 +0800
Subject: [PATCH 137/299] =?UTF-8?q?=E7=89=88=E6=9C=AC=E5=8D=87=E7=BA=A72.1?=
 =?UTF-8?q?3.2?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 pom.xml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/pom.xml b/pom.xml
index 9b7e104..b6e033c 100644
--- a/pom.xml
+++ b/pom.xml
@@ -58,7 +58,7 @@
         pay-java-paypal
         pay-java-yiji
         pay-java-baidu
-        
+        pay-java-demo
 
     
 
-- 
Gitee


From 839187116fec27e6bf3c67b8decac6a654904d01 Mon Sep 17 00:00:00 2001
From: egzosn 
Date: Tue, 2 Jun 2020 10:29:34 +0800
Subject: [PATCH 138/299] 2.13.2

---
 README.md | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/README.md b/README.md
index 2675b6f..20fc538 100644
--- a/README.md
+++ b/README.md
@@ -21,7 +21,7 @@
 
     com.egzosn
     {module-name}
-    2.13.1
+    2.13.2
 
 
 ```
-- 
Gitee


From ec946d832b2c4112db78738dca401fcfc5894206 Mon Sep 17 00:00:00 2001
From: egzosn 
Date: Sun, 26 Jul 2020 22:09:07 +0800
Subject: [PATCH 139/299] =?UTF-8?q?=E9=80=80=E6=AC=BE=E9=99=84=E5=8A=A0?=
 =?UTF-8?q?=E5=8F=82=E6=95=B0?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 pay-java-ali/pom.xml                          |  2 +-
 pay-java-baidu/pom.xml                        |  2 +-
 pay-java-common/pom.xml                       |  2 +-
 pay-java-demo/pom.xml                         |  2 +-
 pay-java-fuiou/pom.xml                        |  2 +-
 .../egzosn/pay/fuiou/api/FuiouPayService.java |  1 +
 pay-java-payoneer/pom.xml                     |  2 +-
 pay-java-paypal/pom.xml                       |  2 +-
 pay-java-union/pom.xml                        |  2 +-
 .../egzosn/pay/union/api/UnionPayService.java | 30 ++++++++++++-------
 pay-java-wx-youdian/pom.xml                   |  2 +-
 pay-java-wx/pom.xml                           |  2 +-
 .../com/egzosn/pay/wx/api/WxPayService.java   |  2 ++
 pay-java-yiji/pom.xml                         |  2 +-
 pom.xml                                       |  4 +--
 15 files changed, 35 insertions(+), 24 deletions(-)

diff --git a/pay-java-ali/pom.xml b/pay-java-ali/pom.xml
index afec0f0..e279f8e 100644
--- a/pay-java-ali/pom.xml
+++ b/pay-java-ali/pom.xml
@@ -5,7 +5,7 @@
     
         pay-java-parent
         com.egzosn
-        2.13.2
+        2.13.3-SNAPSHOT
     
     4.0.0
     pay-java-ali
diff --git a/pay-java-baidu/pom.xml b/pay-java-baidu/pom.xml
index 133d4d8..5e39ceb 100644
--- a/pay-java-baidu/pom.xml
+++ b/pay-java-baidu/pom.xml
@@ -5,7 +5,7 @@
     
         pay-java-parent
         com.egzosn
-        2.13.2
+        2.13.3-SNAPSHOT
     
     4.0.0
     pay-java-baidu
diff --git a/pay-java-common/pom.xml b/pay-java-common/pom.xml
index aae7d6c..1c79c00 100644
--- a/pay-java-common/pom.xml
+++ b/pay-java-common/pom.xml
@@ -5,7 +5,7 @@
     
         pay-java-parent
         com.egzosn
-        2.13.2
+        2.13.3-SNAPSHOT
     
     4.0.0
     jar
diff --git a/pay-java-demo/pom.xml b/pay-java-demo/pom.xml
index c95b537..9cec5a2 100644
--- a/pay-java-demo/pom.xml
+++ b/pay-java-demo/pom.xml
@@ -5,7 +5,7 @@
     
         pay-java-parent
         com.egzosn
-        2.13.2
+        2.13.3-SNAPSHOT
     
     4.0.0
     war
diff --git a/pay-java-fuiou/pom.xml b/pay-java-fuiou/pom.xml
index e5fe927..7d1b18e 100644
--- a/pay-java-fuiou/pom.xml
+++ b/pay-java-fuiou/pom.xml
@@ -5,7 +5,7 @@
     
         pay-java-parent
         com.egzosn
-        2.13.2
+        2.13.3-SNAPSHOT
     
     4.0.0
     pay-java-fuiou
diff --git a/pay-java-fuiou/src/main/java/com/egzosn/pay/fuiou/api/FuiouPayService.java b/pay-java-fuiou/src/main/java/com/egzosn/pay/fuiou/api/FuiouPayService.java
index 1446463..8f6b095 100644
--- a/pay-java-fuiou/src/main/java/com/egzosn/pay/fuiou/api/FuiouPayService.java
+++ b/pay-java-fuiou/src/main/java/com/egzosn/pay/fuiou/api/FuiouPayService.java
@@ -391,6 +391,7 @@ public class FuiouPayService extends BasePayService {
         params.put("refund_amt", Util.conversionCentAmount(refundOrder.getRefundAmount()));
         //备注
         params.put("rem", "");
+        params.putAll(refundOrder.getAttrs());
         params.put("md5", createSign(SignUtils.parameters2MD5Str(params, "|"), payConfigStorage.getInputCharset()));
         JSONObject resultJson = getHttpRequestTemplate().postForObject(getReqUrl() + URL_FuiouSmpRefundGate, params, JSONObject.class);
         return resultJson;
diff --git a/pay-java-payoneer/pom.xml b/pay-java-payoneer/pom.xml
index 2f48989..7a79e0c 100644
--- a/pay-java-payoneer/pom.xml
+++ b/pay-java-payoneer/pom.xml
@@ -5,7 +5,7 @@
     
         pay-java-parent
         com.egzosn
-        2.13.2
+        2.13.3-SNAPSHOT
     
     4.0.0
     pay-java-payoneer
diff --git a/pay-java-paypal/pom.xml b/pay-java-paypal/pom.xml
index 69a23cb..0fba2f8 100644
--- a/pay-java-paypal/pom.xml
+++ b/pay-java-paypal/pom.xml
@@ -5,7 +5,7 @@
     
         pay-java-parent
         com.egzosn
-        2.13.2
+        2.13.3-SNAPSHOT
     
     4.0.0
 
diff --git a/pay-java-union/pom.xml b/pay-java-union/pom.xml
index 6f98cb3..abff084 100644
--- a/pay-java-union/pom.xml
+++ b/pay-java-union/pom.xml
@@ -5,7 +5,7 @@
     
         pay-java-parent
         com.egzosn
-        2.13.2
+        2.13.3-SNAPSHOT
     
     4.0.0
 
diff --git a/pay-java-union/src/main/java/com/egzosn/pay/union/api/UnionPayService.java b/pay-java-union/src/main/java/com/egzosn/pay/union/api/UnionPayService.java
index 419beb0..24d64da 100644
--- a/pay-java-union/src/main/java/com/egzosn/pay/union/api/UnionPayService.java
+++ b/pay-java-union/src/main/java/com/egzosn/pay/union/api/UnionPayService.java
@@ -34,7 +34,7 @@ import java.util.*;
 
 /**
  * @author Actinia
- *         
+ * 
  *         email hayesfu@qq.com
  *         create 2017 2017/11/5
  *         
@@ -62,6 +62,7 @@ public class UnionPayService extends BasePayService { * 证书解释器 */ private CertDescriptor certDescriptor; + /** * 构造函数 * @@ -100,6 +101,7 @@ public class UnionPayService extends BasePayService { return this; } + /** * 获取支付请求地址 * @@ -110,6 +112,7 @@ public class UnionPayService extends BasePayService { public String getReqUrl(TransactionType transactionType) { return (payConfigStorage.isTest() ? TEST_BASE_DOMAIN : RELEASE_BASE_DOMAIN); } + /** * 根据是否为沙箱环境进行获取请求地址 * @@ -227,6 +230,7 @@ public class UnionPayService extends BasePayService { * 超过此时间后,除网银交易外,其他交易银联系统会拒绝受理,提示超时。 跳转银行网银交易如果超时后交易成功,会自动退款,大约5个工作日金额返还到持卡人账户。 * 此时间建议取支付时的北京时间加15分钟。 * 超过超时时间调查询接口应答origRespCode不是A6或者00的就可以判断为失败。 + * * @param expirationTime 超时时间 * @return 具体的时间字符串 */ @@ -237,6 +241,7 @@ public class UnionPayService extends BasePayService { } return DateUtils.formatDate(new Timestamp(System.currentTimeMillis() + 30 * 60 * 1000), DateUtils.YYYYMMDDHHMMSS); } + /** * 返回创建的订单信息 * @@ -256,7 +261,7 @@ public class UnionPayService extends BasePayService { params.put(SDKConstants.param_orderId, order.getOutTradeNo()); - if (StringUtils.isNotEmpty(order.getAddition())){ + if (StringUtils.isNotEmpty(order.getAddition())) { params.put(SDKConstants.param_reqReserved, order.getAddition()); } switch (type) { @@ -286,7 +291,7 @@ public class UnionPayService extends BasePayService { params.put("orderDesc", order.getSubject()); } params.putAll(order.getAttrs()); - params = preOrderHandler(params, order); + params = preOrderHandler(params, order); return setSign(params); } @@ -362,7 +367,8 @@ public class UnionPayService extends BasePayService { CertPathBuilder builder = CertPathBuilder.getInstance("PKIX"); - /*PKIXCertPathBuilderResult result = (PKIXCertPathBuilderResult)*/ builder.build(pkixParams); + /*PKIXCertPathBuilderResult result = (PKIXCertPathBuilderResult)*/ + builder.build(pkixParams); return cert; } catch (java.security.cert.CertPathBuilderException e) { LOG.error("verify certificate chain fail.", e); @@ -384,7 +390,7 @@ public class UnionPayService extends BasePayService { public JSONObject postOrder(PayOrder order) { Map params = orderInfo(order); String responseStr = getHttpRequestTemplate().postForObject(this.getBackTransUrl(), params, String.class); - JSONObject response = UriVariables.getParametersToMap(responseStr); + JSONObject response = UriVariables.getParametersToMap(responseStr); if (response.isEmpty()) { throw new PayErrorException(new PayException("failure", "响应内容有误!", responseStr)); } @@ -394,9 +400,9 @@ public class UnionPayService extends BasePayService { @Override public String toPay(PayOrder order) { - if (null == order.getTransactionType()){ + if (null == order.getTransactionType()) { order.setTransactionType(UnionTransactionType.WEB); - }else if (UnionTransactionType.WEB != order.getTransactionType() && UnionTransactionType.WAP != order.getTransactionType() && UnionTransactionType.B2B != order.getTransactionType()){ + } else if (UnionTransactionType.WEB != order.getTransactionType() && UnionTransactionType.WAP != order.getTransactionType() && UnionTransactionType.B2B != order.getTransactionType()) { throw new PayErrorException(new PayException("-1", "错误的交易类型:" + order.getTransactionType())); } @@ -511,12 +517,13 @@ public class UnionPayService extends BasePayService { /** * 功能:将订单信息进行签名并提交请求 * 业务范围:手机支付控件(含安卓Pay) - * @param order 订单信息 - * @return 成功:返回支付结果 失败:返回 + * + * @param order 订单信息 + * @return 成功:返回支付结果 失败:返回 */ @Override - public Map app(PayOrder order){ - if (null == order.getTransactionType()){ + public Map app(PayOrder order) { + if (null == order.getTransactionType()) { order.setTransactionType(UnionTransactionType.APP); } JSONObject response = postOrder(order); @@ -590,6 +597,7 @@ public class UnionPayService extends BasePayService { params.put(SDKConstants.param_orderId, refundOrder.getRefundNo()); params.put(SDKConstants.param_txnAmt, Util.conversionCentAmount(refundOrder.getRefundAmount())); params.put(SDKConstants.param_origQryId, refundOrder.getTradeNo()); + params.putAll(refundOrder.getAttrs()); this.setSign(params); String responseStr = getHttpRequestTemplate().postForObject(this.getBackTransUrl(), params, String.class); JSONObject response = UriVariables.getParametersToMap(responseStr); diff --git a/pay-java-wx-youdian/pom.xml b/pay-java-wx-youdian/pom.xml index 0adb30f..d43cfda 100644 --- a/pay-java-wx-youdian/pom.xml +++ b/pay-java-wx-youdian/pom.xml @@ -5,7 +5,7 @@ pay-java-parent com.egzosn - 2.13.2 + 2.13.3-SNAPSHOT 4.0.0 pay-java-wx-youdian diff --git a/pay-java-wx/pom.xml b/pay-java-wx/pom.xml index 696288d..f2dca76 100644 --- a/pay-java-wx/pom.xml +++ b/pay-java-wx/pom.xml @@ -5,7 +5,7 @@ pay-java-parent com.egzosn - 2.13.2 + 2.13.3-SNAPSHOT 4.0.0 pay-java-wx 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 f6bb106..b91cbaf 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 @@ -499,6 +499,8 @@ public class WxPayService extends BasePayService implements parameters.put("refund_fee_type", refundOrder.getCurType().getType()); } setParameters(parameters, "refund_desc", refundOrder.getDescription()); + //附加参数,这里可进行覆盖前面所有参数 + parameters.putAll(refundOrder.getAttrs()); //设置签名 setSign(parameters); return requestTemplate.postForObject(getReqUrl(WxTransactionType.REFUND), XML.getMap2Xml(parameters), JSONObject.class); diff --git a/pay-java-yiji/pom.xml b/pay-java-yiji/pom.xml index 40a4186..45ce907 100644 --- a/pay-java-yiji/pom.xml +++ b/pay-java-yiji/pom.xml @@ -5,7 +5,7 @@ pay-java-parent com.egzosn - 2.13.2 + 2.13.3-SNAPSHOT 4.0.0 diff --git a/pom.xml b/pom.xml index b6e033c..952c79d 100644 --- a/pom.xml +++ b/pom.xml @@ -7,7 +7,7 @@ com.egzosn pay-java-parent pom - 2.13.2 + 2.13.3-SNAPSHOT Pay Java - Parent Pay Java Parent @@ -64,7 +64,7 @@ - 2.13.2 + 2.13.3-SNAPSHOT 4.5.4 1.2.17 1.2.58 -- Gitee From defb571001a97e4e8a1cfcb8e58ab47c7f76c9a5 Mon Sep 17 00:00:00 2001 From: faymanwang <1057438332@qq.com> Date: Fri, 31 Jul 2020 10:36:49 +0800 Subject: [PATCH 140/299] =?UTF-8?q?=E5=BE=AE=E4=BF=A1=E6=94=AF=E4=BB=98-?= =?UTF-8?q?=E4=B8=8B=E8=BD=BD=E8=B4=A6=E5=8D=95-=E6=94=AF=E6=8C=81gzip?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/egzosn/pay/wx/api/WxPayService.java | 50 ++++++++++++++++--- 1 file changed, 42 insertions(+), 8 deletions(-) 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 b91cbaf..a325220 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 @@ -536,15 +536,16 @@ public class WxPayService extends BasePayService implements */ @Override public Map downloadbill(Date billDate, String billType) { + Map parameters = getDownloadBillParam(billDate, billType,false); + return downBillRet(parameters); + } - //获取公共参数 - Map parameters = getPublicParameters(); - - parameters.put("bill_type", billType); - //目前只支持日账单 - - parameters.put("bill_date", DateUtils.formatDate(billDate, DateUtils.YYYYMMDD)); - + /** + * 账单根据参数返回结果 + * @param parameters + * @return + */ + private Map downBillRet(Map parameters) { //设置签名 setSign(parameters); String respStr = requestTemplate.postForObject(getReqUrl(WxTransactionType.DOWNLOADBILL), XML.getMap2Xml(parameters), String.class); @@ -559,6 +560,39 @@ public class WxPayService extends BasePayService implements return ret; } + /** + * 下载账单公共参数 + * @param billDate 账单类型,商户通过接口或商户经开放平台授权后其所属服务商通过接口可以获取以下账单类型:trade、signcustomer;trade指商户基于支付宝交易收单的业务账单;signcustomer是指基于商户支付宝余额收入及支出等资金变动的帐务账单; + * @param billType 账单时间:日账单格式为yyyy-MM-dd,月账单格式为yyyy-MM。 + * @param tarType 账单返回格式 默认返回流false ,gzip 时候true + * @return + */ + private Map getDownloadBillParam(Date billDate, String billType,boolean tarType) { + //获取公共参数 + Map parameters = getPublicParameters(); + parameters.put("bill_type", billType); + //目前只支持日账单 + parameters.put("bill_date", DateUtils.formatDate(billDate, DateUtils.YYYYMMDD)); + if(tarType){ + parameters.put("tar_type", "GZIP"); + } + return parameters; + } + + /** + * 目前只支持日账单 + * + * @param billDate 账单类型,商户通过接口或商户经开放平台授权后其所属服务商通过接口可以获取以下账单类型:trade、signcustomer;trade指商户基于支付宝交易收单的业务账单;signcustomer是指基于商户支付宝余额收入及支出等资金变动的帐务账单; + * @param billType 账单时间:日账单格式为yyyy-MM-dd,月账单格式为yyyy-MM。 + * @param tarType 账单返回格式 默认返回流false ,gzip 时候true + * @return 返回支付方下载对账单的结果 + */ + public Map downloadbill(Date billDate, String billType, Boolean tarType) { + Map parameters = getDownloadBillParam(billDate, billType,tarType); + //设置签名 + return downBillRet(parameters); + } + /** * @param transactionIdOrBillDate 支付平台订单号或者账单日期, 具体请 类型为{@link String }或者 {@link Date },类型须强制限制,类型不对应则抛出异常{@link PayErrorException} -- Gitee From 94376027f6044c533e1c0a419d265ae6c3806432 Mon Sep 17 00:00:00 2001 From: faymanwang <1057438332@qq.com> Date: Fri, 31 Jul 2020 11:25:03 +0800 Subject: [PATCH 141/299] =?UTF-8?q?=E5=BE=AE=E4=BF=A1=E6=94=AF=E4=BB=98-?= =?UTF-8?q?=E8=B4=A6=E5=8D=95-=E5=A2=9E=E5=8A=A0=E6=8E=A5=E5=8F=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/egzosn/pay/wx/api/WxBillService.java | 15 +++++++++ .../com/egzosn/pay/wx/api/WxPayService.java | 31 ++++++++++--------- 2 files changed, 31 insertions(+), 15 deletions(-) create mode 100644 pay-java-wx/src/main/java/com/egzosn/pay/wx/api/WxBillService.java diff --git a/pay-java-wx/src/main/java/com/egzosn/pay/wx/api/WxBillService.java b/pay-java-wx/src/main/java/com/egzosn/pay/wx/api/WxBillService.java new file mode 100644 index 0000000..f8e5529 --- /dev/null +++ b/pay-java-wx/src/main/java/com/egzosn/pay/wx/api/WxBillService.java @@ -0,0 +1,15 @@ +package com.egzosn.pay.wx.api; + +import java.util.Date; +import java.util.Map; + +/** + * @description:账单接口 + * @author: faymanwang + * @email: 1057438332@qq.com + * @time: 2020/7/31 11:21 + */ +public interface WxBillService { + + public Map downloadbill(Date billDate, String billType, Boolean tarType); +} 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 a325220..2601edc 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 @@ -32,7 +32,7 @@ import static com.egzosn.pay.wx.bean.WxTransferType.*; * date 2016-5-18 14:09:01 *
*/ -public class WxPayService extends BasePayService implements WxRedPackService { +public class WxPayService extends BasePayService implements WxRedPackService,WxBillService { /** @@ -540,6 +540,21 @@ public class WxPayService extends BasePayService implements return downBillRet(parameters); } + /** + * 目前只支持日账单,增加账单返回格式 + * + * @param billDate 账单类型,商户通过接口或商户经开放平台授权后其所属服务商通过接口可以获取以下账单类型:trade、signcustomer;trade指商户基于支付宝交易收单的业务账单;signcustomer是指基于商户支付宝余额收入及支出等资金变动的帐务账单; + * @param billType 账单时间:日账单格式为yyyy-MM-dd,月账单格式为yyyy-MM。 + * @param tarType 账单返回格式 默认返回流false ,gzip 时候true + * @return 返回支付方下载对账单的结果 + */ + @Override + public Map downloadbill(Date billDate, String billType, Boolean tarType) { + Map parameters = getDownloadBillParam(billDate, billType,tarType?true:false); + //设置签名 + return downBillRet(parameters); + } + /** * 账单根据参数返回结果 * @param parameters @@ -579,20 +594,6 @@ public class WxPayService extends BasePayService implements return parameters; } - /** - * 目前只支持日账单 - * - * @param billDate 账单类型,商户通过接口或商户经开放平台授权后其所属服务商通过接口可以获取以下账单类型:trade、signcustomer;trade指商户基于支付宝交易收单的业务账单;signcustomer是指基于商户支付宝余额收入及支出等资金变动的帐务账单; - * @param billType 账单时间:日账单格式为yyyy-MM-dd,月账单格式为yyyy-MM。 - * @param tarType 账单返回格式 默认返回流false ,gzip 时候true - * @return 返回支付方下载对账单的结果 - */ - public Map downloadbill(Date billDate, String billType, Boolean tarType) { - Map parameters = getDownloadBillParam(billDate, billType,tarType); - //设置签名 - return downBillRet(parameters); - } - /** * @param transactionIdOrBillDate 支付平台订单号或者账单日期, 具体请 类型为{@link String }或者 {@link Date },类型须强制限制,类型不对应则抛出异常{@link PayErrorException} -- Gitee From 4085ed7ce6b3263277fcc214d237bfbcad7ffdfb Mon Sep 17 00:00:00 2001 From: faymanwang <1057438332@qq.com> Date: Fri, 31 Jul 2020 11:48:35 +0800 Subject: [PATCH 142/299] =?UTF-8?q?=E5=BE=AE=E4=BF=A1=E6=94=AF=E4=BB=98-?= =?UTF-8?q?=E8=B4=A6=E5=8D=95-boolean?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/main/java/com/egzosn/pay/wx/api/WxBillService.java | 2 +- .../src/main/java/com/egzosn/pay/wx/api/WxPayService.java | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/pay-java-wx/src/main/java/com/egzosn/pay/wx/api/WxBillService.java b/pay-java-wx/src/main/java/com/egzosn/pay/wx/api/WxBillService.java index f8e5529..babaf51 100644 --- a/pay-java-wx/src/main/java/com/egzosn/pay/wx/api/WxBillService.java +++ b/pay-java-wx/src/main/java/com/egzosn/pay/wx/api/WxBillService.java @@ -11,5 +11,5 @@ import java.util.Map; */ public interface WxBillService { - public Map downloadbill(Date billDate, String billType, Boolean tarType); + public Map downloadbill(Date billDate, String billType, boolean tarType); } 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 2601edc..1cb4265 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 @@ -549,8 +549,8 @@ public class WxPayService extends BasePayService implements * @return 返回支付方下载对账单的结果 */ @Override - public Map downloadbill(Date billDate, String billType, Boolean tarType) { - Map parameters = getDownloadBillParam(billDate, billType,tarType?true:false); + public Map downloadbill(Date billDate, String billType, boolean tarType) { + Map parameters = getDownloadBillParam(billDate, billType,tarType==true?true:false); //设置签名 return downBillRet(parameters); } -- Gitee From 34f2923194ca938e959d3919c6857e24cb41e546 Mon Sep 17 00:00:00 2001 From: faymanwang <1057438332@qq.com> Date: Fri, 31 Jul 2020 16:03:53 +0800 Subject: [PATCH 143/299] =?UTF-8?q?=E5=BE=AE=E4=BF=A1=E6=94=AF=E4=BB=98-?= =?UTF-8?q?=E8=B4=A6=E5=8D=95-=E8=B4=A6=E5=8D=95=E6=8C=81=E4=B9=85?= =?UTF-8?q?=E5=8C=96=E8=B7=AF=E5=BE=84=E6=80=9D=E8=B7=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/egzosn/pay/wx/api/WxPayService.java | 77 ++++++++++++++----- 1 file changed, 59 insertions(+), 18 deletions(-) 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 1cb4265..320479a 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 @@ -14,8 +14,7 @@ import com.egzosn.pay.common.util.sign.encrypt.RSA2; import com.egzosn.pay.common.util.str.StringUtils; import com.egzosn.pay.wx.bean.*; -import java.io.IOException; -import java.io.InputStream; +import java.io.*; import java.net.URLEncoder; import java.security.GeneralSecurityException; import java.util.*; @@ -537,7 +536,18 @@ public class WxPayService extends BasePayService implements @Override public Map downloadbill(Date billDate, String billType) { Map parameters = getDownloadBillParam(billDate, billType,false); - return downBillRet(parameters); + //设置签名 + setSign(parameters); + String respStr = requestTemplate.postForObject(getReqUrl(WxTransactionType.DOWNLOADBILL), XML.getMap2Xml(parameters), String.class); + if (respStr.indexOf("<") == 0) { + return XML.toJSONObject(respStr); + } + + Map ret = new HashMap(3); + ret.put(RETURN_CODE, SUCCESS); + ret.put(RETURN_MSG_CODE, "ok"); + ret.put("data", respStr); + return ret; } /** @@ -551,30 +561,61 @@ public class WxPayService extends BasePayService implements @Override public Map downloadbill(Date billDate, String billType, boolean tarType) { Map parameters = getDownloadBillParam(billDate, billType,tarType==true?true:false); - //设置签名 - return downBillRet(parameters); - } - - /** - * 账单根据参数返回结果 - * @param parameters - * @return - */ - private Map downBillRet(Map parameters) { - //设置签名 + //设置签名 setSign(parameters); - String respStr = requestTemplate.postForObject(getReqUrl(WxTransactionType.DOWNLOADBILL), XML.getMap2Xml(parameters), String.class); - if (respStr.indexOf("<") == 0) { - return XML.toJSONObject(respStr); + InputStream inputStream = requestTemplate.postForObject(getReqUrl(WxTransactionType.DOWNLOADBILL), XML.getMap2Xml(parameters), InputStream.class); + + //写到本地1.zip 里面有个1文件,用txt打开即可。返回路径,这个类需要抽离到工具类 + //入参需要文件存放路径,这样四个参数略多,是否需要封装一个实体。或者直接更改第三个参数就是路径,有路径默认传true + try { + writeToLocal("D:\\1.zip", inputStream); + } catch (IOException e) { + e.printStackTrace(); } Map ret = new HashMap(3); ret.put(RETURN_CODE, SUCCESS); ret.put(RETURN_MSG_CODE, "ok"); - ret.put("data", respStr); +// ret.put("data", hashMap); return ret; } + /** + * 将InputStream写入本地文件 + * @param destination 写入本地目录 + * @param inputStream 输入流 + * @throws IOException IOException + */ + private void writeToLocal(String destination, InputStream inputStream) + throws IOException { + + // 判断字节大小 + if (inputStream.available() != 0) { + System.out.println("结果大小:" + inputStream.available()); + File file = new File(destination); + if (!file.getParentFile().exists()) { + boolean result = file.getParentFile().mkdirs(); + if (!result) { + System.out.println("创建失败"); + } + } + OutputStream out = new FileOutputStream(file); + int size = 0; + int len = 0; + byte[] buf = new byte[1024]; + while ((size = inputStream.read(buf)) != -1) { + len += size; + out.write(buf, 0, size); + } + System.out.println("最终写入字节数大小:" + len); + inputStream.close(); + out.close(); + + } + + } + + /** * 下载账单公共参数 * @param billDate 账单类型,商户通过接口或商户经开放平台授权后其所属服务商通过接口可以获取以下账单类型:trade、signcustomer;trade指商户基于支付宝交易收单的业务账单;signcustomer是指基于商户支付宝余额收入及支出等资金变动的帐务账单; -- Gitee From 9ebd5d1596e7b3e76fd1e2c61bc12c6d7f757bf8 Mon Sep 17 00:00:00 2001 From: faymanwang <1057438332@qq.com> Date: Thu, 6 Aug 2020 09:22:04 +0800 Subject: [PATCH 144/299] =?UTF-8?q?=E5=BE=AE=E4=BF=A1=E6=94=AF=E4=BB=98-?= =?UTF-8?q?=E8=B4=A6=E5=8D=95-=E8=B4=A6=E5=8D=95=E6=8C=81=E4=B9=85?= =?UTF-8?q?=E5=8C=96=EF=BC=8C=E8=A7=A3=E5=8E=8B=E6=B5=81=E4=B9=8B=E5=90=8E?= =?UTF-8?q?=EF=BC=8C=E6=8C=89=E6=9C=88=E5=AD=98=E5=82=A8=E6=A0=BC=E5=BC=8F?= =?UTF-8?q?=E4=B8=BATXT?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/egzosn/pay/common/util/DateUtils.java | 1 + .../com/egzosn/pay/wx/api/WxBillService.java | 2 +- .../java/com/egzosn/pay/wx/api/WxConst.java | 1 + .../com/egzosn/pay/wx/api/WxPayService.java | 53 +++++++++++++------ 4 files changed, 41 insertions(+), 16 deletions(-) diff --git a/pay-java-common/src/main/java/com/egzosn/pay/common/util/DateUtils.java b/pay-java-common/src/main/java/com/egzosn/pay/common/util/DateUtils.java index f8141d9..63c2d64 100644 --- a/pay-java-common/src/main/java/com/egzosn/pay/common/util/DateUtils.java +++ b/pay-java-common/src/main/java/com/egzosn/pay/common/util/DateUtils.java @@ -59,6 +59,7 @@ public final class DateUtils { public static final String YYYYMMDD = "yyyyMMdd"; public static final String YYYYMMDDHHMMSS = "yyyyMMddHHmmss"; public static final String MMDD = "MMdd"; + public static final String YYYYMM = "yyyyMM"; public static String formatDate(Date date, String pattern) { diff --git a/pay-java-wx/src/main/java/com/egzosn/pay/wx/api/WxBillService.java b/pay-java-wx/src/main/java/com/egzosn/pay/wx/api/WxBillService.java index babaf51..bf01bbd 100644 --- a/pay-java-wx/src/main/java/com/egzosn/pay/wx/api/WxBillService.java +++ b/pay-java-wx/src/main/java/com/egzosn/pay/wx/api/WxBillService.java @@ -11,5 +11,5 @@ import java.util.Map; */ public interface WxBillService { - public Map downloadbill(Date billDate, String billType, boolean tarType); + public Map downloadbill(Date billDate, String billType, String path); } diff --git a/pay-java-wx/src/main/java/com/egzosn/pay/wx/api/WxConst.java b/pay-java-wx/src/main/java/com/egzosn/pay/wx/api/WxConst.java index b2cac70..ac656b4 100644 --- a/pay-java-wx/src/main/java/com/egzosn/pay/wx/api/WxConst.java +++ b/pay-java-wx/src/main/java/com/egzosn/pay/wx/api/WxConst.java @@ -19,6 +19,7 @@ public interface WxConst { String SANDBOXNEW = "sandboxnew/"; String SUCCESS = "SUCCESS"; + String FAIL = "FAIL"; String RETURN_CODE = "return_code"; String SIGN = "sign"; String CIPHER_ALGORITHM = "RSA/ECB/OAEPWITHSHA-1ANDMGF1PADDING"; 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 320479a..009edf4 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 @@ -18,6 +18,10 @@ import java.io.*; import java.net.URLEncoder; import java.security.GeneralSecurityException; import java.util.*; +import java.util.zip.GZIPInputStream; +import java.util.zip.GZIPOutputStream; +import java.util.zip.ZipEntry; +import java.util.zip.ZipInputStream; import static com.egzosn.pay.wx.api.WxConst.*; import static com.egzosn.pay.wx.bean.WxTransferType.*; @@ -555,29 +559,50 @@ public class WxPayService extends BasePayService implements * * @param billDate 账单类型,商户通过接口或商户经开放平台授权后其所属服务商通过接口可以获取以下账单类型:trade、signcustomer;trade指商户基于支付宝交易收单的业务账单;signcustomer是指基于商户支付宝余额收入及支出等资金变动的帐务账单; * @param billType 账单时间:日账单格式为yyyy-MM-dd,月账单格式为yyyy-MM。 - * @param tarType 账单返回格式 默认返回流false ,gzip 时候true + * @param path 账单返回格式 账单存储的基础路径,按月切割 * @return 返回支付方下载对账单的结果 */ @Override - public Map downloadbill(Date billDate, String billType, boolean tarType) { - Map parameters = getDownloadBillParam(billDate, billType,tarType==true?true:false); + public Map downloadbill(Date billDate, String billType, String path) { + Map parameters = getDownloadBillParam(billDate, billType,true); //设置签名 setSign(parameters); InputStream inputStream = requestTemplate.postForObject(getReqUrl(WxTransactionType.DOWNLOADBILL), XML.getMap2Xml(parameters), InputStream.class); - - //写到本地1.zip 里面有个1文件,用txt打开即可。返回路径,这个类需要抽离到工具类 - //入参需要文件存放路径,这样四个参数略多,是否需要封装一个实体。或者直接更改第三个参数就是路径,有路径默认传true try { - writeToLocal("D:\\1.zip", inputStream); - } catch (IOException e) { + //解压流 + inputStream = uncompress(inputStream); + writeToLocal(path+DateUtils.formatDate(new Date(), DateUtils.YYYYMM)+"/"+DateUtils.formatDate(new Date(), DateUtils.YYYYMMDDHHMMSS)+".txt", inputStream); + Map ret = new HashMap(3); + ret.put(RETURN_CODE, SUCCESS); + ret.put(RETURN_MSG_CODE, "ok"); + ret.put("data", path); + return ret; + } catch (Exception e) { e.printStackTrace(); + Map ret = new HashMap(3); + ret.put(RETURN_CODE, FAIL); + ret.put(RETURN_MSG_CODE, "fail"); + ret.put("data", e.getMessage()); + return ret; } + } - Map ret = new HashMap(3); - ret.put(RETURN_CODE, SUCCESS); - ret.put(RETURN_MSG_CODE, "ok"); -// ret.put("data", hashMap); - return ret; + /** + * GZIP解压缩 + * + * @param input + * @return + */ + public static InputStream uncompress(InputStream input) throws IOException { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + GZIPInputStream ungzip = new GZIPInputStream(input); + byte[] buffer = new byte[1024]; + int n; + while ((n = ungzip.read(buffer)) >= 0) { + out.write(buffer, 0, n); + } + InputStream is = new ByteArrayInputStream(out.toByteArray()); + return is; } /** @@ -610,9 +635,7 @@ public class WxPayService extends BasePayService implements System.out.println("最终写入字节数大小:" + len); inputStream.close(); out.close(); - } - } -- Gitee From 545bc0c466b465703ab27804eaeb180f5b7c263a Mon Sep 17 00:00:00 2001 From: egzosn Date: Sun, 16 Aug 2020 22:51:15 +0800 Subject: [PATCH 145/299] =?UTF-8?q?=E9=80=80=E6=AC=BE=E7=BB=93=E6=9E=9C?= =?UTF-8?q?=E9=9B=86=E7=BB=9F=E4=B8=80=E5=A4=84=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pay-java-ali/README.md | 2 +- .../com/egzosn/pay/ali/api/AliPayService.java | 7 +- .../egzosn/pay/ali/bean/AliRefundResult.java | 377 +++++++++++++ .../pay/ali/bean/PresetPayToolInfo.java | 44 ++ .../egzosn/pay/ali/bean/TradeFundBill.java | 82 +++ .../egzosn/pay/baidu/api/BaiduPayService.java | 50 +- .../egzosn/pay/common/api/BasePayService.java | 2 +- .../com/egzosn/pay/common/api/PayService.java | 2 +- .../pay/common/bean/BaseRefundResult.java | 337 ++++++++++++ .../egzosn/pay/common/bean/MethodType.java | 2 +- .../com/egzosn/pay/common/bean/Order.java | 3 +- .../egzosn/pay/common/bean/RefundResult.java | 103 ++++ .../java/com/egzosn/pay/common/util/XML.java | 5 - pay-java-demo/pom.xml | 2 +- .../pay/demo/controller/AliPayController.java | 3 +- .../pay/demo/controller/PayController.java | 2 +- .../demo/controller/PayPalPayController.java | 3 +- .../demo/controller/UnionPayController.java | 3 +- .../pay/demo/controller/WxPayController.java | 2 +- .../egzosn/pay/fuiou/api/FuiouPayService.java | 5 +- .../pay/fuiou/bean/FuiouRefundResult.java | 166 ++++++ pay-java-payoneer/README.md | 2 +- .../pay/payoneer/api/PayoneerPayService.java | 50 +- pay-java-paypal/README.md | 2 +- .../pay/paypal/api/PayPalPayService.java | 49 +- pay-java-union/README.md | 2 +- .../egzosn/pay/union/api/UnionPayService.java | 14 +- .../pay/union/bean/UnionRefundResult.java | 326 ++++++++++++ .../wx/youdian/api/WxYouDianPayService.java | 51 +- pay-java-wx/README.md | 2 +- .../com/egzosn/pay/wx/api/WxPayService.java | 4 +- .../egzosn/pay/wx/bean/WxRefundResult.java | 496 ++++++++++++++++++ .../egzosn/pay/yiji/api/YiJiPayService.java | 50 +- pom.xml | 5 +- 34 files changed, 2209 insertions(+), 46 deletions(-) create mode 100644 pay-java-ali/src/main/java/com/egzosn/pay/ali/bean/AliRefundResult.java create mode 100644 pay-java-ali/src/main/java/com/egzosn/pay/ali/bean/PresetPayToolInfo.java create mode 100644 pay-java-ali/src/main/java/com/egzosn/pay/ali/bean/TradeFundBill.java create mode 100644 pay-java-common/src/main/java/com/egzosn/pay/common/bean/BaseRefundResult.java create mode 100644 pay-java-common/src/main/java/com/egzosn/pay/common/bean/RefundResult.java create mode 100644 pay-java-fuiou/src/main/java/com/egzosn/pay/fuiou/bean/FuiouRefundResult.java create mode 100644 pay-java-union/src/main/java/com/egzosn/pay/union/bean/UnionRefundResult.java create mode 100644 pay-java-wx/src/main/java/com/egzosn/pay/wx/bean/WxRefundResult.java diff --git a/pay-java-ali/README.md b/pay-java-ali/README.md index e7a2f5e..824213f 100644 --- a/pay-java-ali/README.md +++ b/pay-java-ali/README.md @@ -236,7 +236,7 @@ RefundOrder order = new RefundOrder("支付宝单号", "我方系统单号", "退款金额", "订单总金额"); //非必填, 根据业务需求而定,可用于多次退款 order.setRefundNo("退款单号") - Map result = service.refund(order); + AliRefundResult result = service.refund(order); ``` 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 9b7aa7d..4101dc3 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 @@ -3,6 +3,7 @@ package com.egzosn.pay.ali.api; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONObject; import com.egzosn.pay.ali.bean.AliPayMessage; +import com.egzosn.pay.ali.bean.AliRefundResult; import com.egzosn.pay.ali.bean.AliTransactionType; import com.egzosn.pay.ali.bean.AliTransferType; import com.egzosn.pay.ali.bean.OrderSettle; @@ -437,7 +438,7 @@ public class AliPayService extends BasePayService { * @return 返回支付方申请退款后的结果 */ @Override - public Map refund(RefundOrder refundOrder) { + public AliRefundResult refund(RefundOrder refundOrder) { //获取公共参数 Map parameters = getPublicParameters(AliTransactionType.REFUND); setAppAuthToken(parameters, refundOrder.getAttrs()); @@ -451,7 +452,9 @@ public class AliPayService extends BasePayService { parameters.put(BIZ_CONTENT, JSON.toJSONString(bizContent)); //设置签名 setSign(parameters); - return requestTemplate.getForObject(getReqUrl() + "?" + UriVariables.getMapToParameters(parameters), JSONObject.class); + final AliRefundResult refundResult = AliRefundResult.create(requestTemplate.getForObject(getReqUrl() + "?" + UriVariables.getMapToParameters(parameters), JSONObject.class)); + refundResult.setOutRequestNo(refundOrder.getRefundNo()); + return refundResult; } diff --git a/pay-java-ali/src/main/java/com/egzosn/pay/ali/bean/AliRefundResult.java b/pay-java-ali/src/main/java/com/egzosn/pay/ali/bean/AliRefundResult.java new file mode 100644 index 0000000..6fbf661 --- /dev/null +++ b/pay-java-ali/src/main/java/com/egzosn/pay/ali/bean/AliRefundResult.java @@ -0,0 +1,377 @@ +package com.egzosn.pay.ali.bean; + +import java.math.BigDecimal; +import java.util.Date; +import java.util.Map; + +import com.alibaba.fastjson.JSONObject; +import com.alibaba.fastjson.annotation.JSONField; +import com.egzosn.pay.common.bean.BaseRefundResult; +import com.egzosn.pay.common.bean.CurType; +import com.egzosn.pay.common.bean.DefaultCurType; +import com.egzosn.pay.common.bean.RefundOrder; + +/** + * 支付宝退款结果返回 + * + * @author Egan + *
+ * email egzosn@gmail.com
+ * date 2020/8/16 17:53
+ * 
+ */ +public class AliRefundResult extends BaseRefundResult { + + /** + * 网关返回码,详见文档 40004 + */ + private String code; + /** + * 网关返回码描述,详见文档 Business Failed + */ + private String msg; + /** + * 业务返回码,参见具体的API接口文档 ACQ.TRADE_HAS_SUCCESS + */ + @JSONField(name = "sub_code") + private String subCode; + + /** + * 业务返回码描述,参见具体的API接口文档 交易已被支付 + */ + @JSONField(name = "sub_msg") + private String subMsg; + /** + * 签名,详见文档 + */ + private String sign; + /** + * 支付宝交易号 + */ + @JSONField(name = "trade_no") + private String tradeNo; + /** + * 商户订单号 6823789339978248 + */ + @JSONField(name = "out_trade_no") + private String outTradeNo; + /** + * 标识一次退款请求,同一笔交易多次退款需要保证唯一 + *

+ * 因支付宝退款结果中没有返回此参数,该参数从{@link RefundOrder#getRefundNo()}获取 + */ + private String outRequestNo; + /** + * 用户的登录id 159****5620 + */ + @JSONField(name = "buyer_logon_id") + private String buyerLogonId; + /** + * 本次退款是否发生了资金变化 Y + */ + @JSONField(name = "fund_change") + private String fundChange; + /** + * 退款总金额 88.88 + */ + @JSONField(name = "refund_fee") + private BigDecimal refundFee; + /** + * 退款币种信息 USD + */ + @JSONField(name = "refund_currency") + private DefaultCurType refundCurrency; + /** + * 退款支付时间 2014-11-27 15:45:57 + */ + @JSONField(name = "gmt_refund_pay") + private Date gmtRefundPay; + + /** + * 退款使用的资金渠道。 + * 只有在签约中指定需要返回资金明细,或者入参的query_options中指定时才返回该字段信息。 + */ + @JSONField(name = "refund_detail_item_list") + private TradeFundBill refundDetailItemList; + /** + * 交易在支付时候的门店名称 + */ + @JSONField(name = "store_name") + private String storeName; + /** + * 买家在支付宝的用户id 2088101117955611 + */ + @JSONField(name = "buyer_user_id") + private String buyerUserId; + /** + * 退回的前置资产列表 + */ + @JSONField(name = "refund_preset_paytool_list") + private PresetPayToolInfo refundPresetPaytoolList; + /** + * 退款清算编号,用于清算对账使用; + * 只在银行间联交易场景下返回该信息; 2018101610032004620239146945 + */ + @JSONField(name = "refund_settlement_id") + private String refundSettlementId; + /** + * 本次退款金额中买家退款金额 88.88 + */ + @JSONField(name = "present_refund_buyer_amount") + private String presentRefundBuyerAmount; + /** + * 本次退款金额中平台优惠退款金额 88.88 + */ + @JSONField(name = "present_refund_discount_amount") + private String presentRefundDiscountAmount; + /** + * 本次退款金额中商家优惠退款金额 88.88 + */ + @JSONField(name = "present_refund_mdiscount_amount") + private String presentRefundMdiscountAmount; + + + + /** + * 获取退款请求结果状态码 + * + * @return 状态码 + */ + @Override + public String getCode() { + return code; + } + + /** + * 获取退款请求结果状态提示信息 + * + * @return 提示信息 + */ + @Override + public String getMsg() { + return msg; + } + + /** + * 返回业务结果状态码 + * + * @return 业务结果状态码 + */ + @Override + public String getResultCode() { + return subCode; + } + + /** + * 返回业务结果状态提示信息 + * + * @return 业务结果状态提示信息 + */ + @Override + public String getResultMsg() { + return subMsg; + } + + /** + * 退款金额 + * + * @return 退款金额 + */ + @Override + public BigDecimal getRefundFee() { + return refundFee; + } + + /** + * 退款币种信息 + * + * @return 币种信息 + */ + @Override + public CurType getRefundCurrency() { + return refundCurrency; + } + + /** + * 支付平台交易号 + * 发起支付时 支付平台(如支付宝)返回的交易订单号 + * + * @return 支付平台交易号 + */ + @Override + public String getTradeNo() { + return tradeNo; + } + + /** + * 支付订单号 + * 发起支付时,用户系统的订单号 + * + * @return 支付订单号 + */ + @Override + public String getOutTradeNo() { + return outTradeNo; + } + + /** + * 商户退款单号 + * + * @return 商户退款单号 + */ + @Override + public String getRefundNo() { + return outRequestNo; + } + + + + public void setCode(String code) { + this.code = code; + } + + public void setMsg(String msg) { + this.msg = msg; + } + + public String getSubCode() { + return subCode; + } + + public void setSubCode(String subCode) { + this.subCode = subCode; + } + + public String getSubMsg() { + return subMsg; + } + + public void setSubMsg(String subMsg) { + this.subMsg = subMsg; + } + + public String getSign() { + return sign; + } + + public void setSign(String sign) { + this.sign = sign; + } + + public void setTradeNo(String tradeNo) { + this.tradeNo = tradeNo; + } + + public void setOutTradeNo(String outTradeNo) { + this.outTradeNo = outTradeNo; + } + + public String getOutRequestNo() { + return outRequestNo; + } + + public void setOutRequestNo(String outRequestNo) { + this.outRequestNo = outRequestNo; + } + + public String getBuyerLogonId() { + return buyerLogonId; + } + + public void setBuyerLogonId(String buyerLogonId) { + this.buyerLogonId = buyerLogonId; + } + + public String getFundChange() { + return fundChange; + } + + public void setFundChange(String fundChange) { + this.fundChange = fundChange; + } + + public void setRefundFee(BigDecimal refundFee) { + this.refundFee = refundFee; + } + + public void setRefundCurrency(DefaultCurType refundCurrency) { + this.refundCurrency = refundCurrency; + } + + public Date getGmtRefundPay() { + return gmtRefundPay; + } + + public void setGmtRefundPay(Date gmtRefundPay) { + this.gmtRefundPay = gmtRefundPay; + } + + public TradeFundBill getRefundDetailItemList() { + return refundDetailItemList; + } + + public void setRefundDetailItemList(TradeFundBill refundDetailItemList) { + this.refundDetailItemList = refundDetailItemList; + } + + public String getStoreName() { + return storeName; + } + + public void setStoreName(String storeName) { + this.storeName = storeName; + } + + public String getBuyerUserId() { + return buyerUserId; + } + + public void setBuyerUserId(String buyerUserId) { + this.buyerUserId = buyerUserId; + } + + public PresetPayToolInfo getRefundPresetPaytoolList() { + return refundPresetPaytoolList; + } + + public void setRefundPresetPaytoolList(PresetPayToolInfo refundPresetPaytoolList) { + this.refundPresetPaytoolList = refundPresetPaytoolList; + } + + public String getRefundSettlementId() { + return refundSettlementId; + } + + public void setRefundSettlementId(String refundSettlementId) { + this.refundSettlementId = refundSettlementId; + } + + public String getPresentRefundBuyerAmount() { + return presentRefundBuyerAmount; + } + + public void setPresentRefundBuyerAmount(String presentRefundBuyerAmount) { + this.presentRefundBuyerAmount = presentRefundBuyerAmount; + } + + public String getPresentRefundDiscountAmount() { + return presentRefundDiscountAmount; + } + + public void setPresentRefundDiscountAmount(String presentRefundDiscountAmount) { + this.presentRefundDiscountAmount = presentRefundDiscountAmount; + } + + public String getPresentRefundMdiscountAmount() { + return presentRefundMdiscountAmount; + } + + public void setPresentRefundMdiscountAmount(String presentRefundMdiscountAmount) { + this.presentRefundMdiscountAmount = presentRefundMdiscountAmount; + } + public static final AliRefundResult create(Map result){ + AliRefundResult refundResult = new JSONObject(result).toJavaObject(AliRefundResult.class); + refundResult.setAttrs(result); + return refundResult; + } +} diff --git a/pay-java-ali/src/main/java/com/egzosn/pay/ali/bean/PresetPayToolInfo.java b/pay-java-ali/src/main/java/com/egzosn/pay/ali/bean/PresetPayToolInfo.java new file mode 100644 index 0000000..d7617c4 --- /dev/null +++ b/pay-java-ali/src/main/java/com/egzosn/pay/ali/bean/PresetPayToolInfo.java @@ -0,0 +1,44 @@ +package com.egzosn.pay.ali.bean; + +import java.math.BigDecimal; + +import com.alibaba.fastjson.annotation.JSONField; + +/** + * 退回的前置资产列表 + * + * @author Egan + *

+ * email egzosn@gmail.com
+ * date 2020/8/16 18:58
+ * 
+ */ +public class PresetPayToolInfo { + + /** + * 必填 32前置资产金额 12.21 + */ + private BigDecimal[] amount; + + /** + * 前置资产类型编码,和收单支付传入的preset_pay_tool里面的类型编码保持一致。盒马礼品卡:HEMA;抓猫猫红包:T_CAT_COUPON + */ + @JSONField(name = "assert_type_code") + private String assertTypeCode; + + public BigDecimal[] getAmount() { + return amount; + } + + public void setAmount(BigDecimal[] amount) { + this.amount = amount; + } + + public String getAssertTypeCode() { + return assertTypeCode; + } + + public void setAssertTypeCode(String assertTypeCode) { + this.assertTypeCode = assertTypeCode; + } +} diff --git a/pay-java-ali/src/main/java/com/egzosn/pay/ali/bean/TradeFundBill.java b/pay-java-ali/src/main/java/com/egzosn/pay/ali/bean/TradeFundBill.java new file mode 100644 index 0000000..f2862ff --- /dev/null +++ b/pay-java-ali/src/main/java/com/egzosn/pay/ali/bean/TradeFundBill.java @@ -0,0 +1,82 @@ +package com.egzosn.pay.ali.bean; + +import java.math.BigDecimal; + +import com.alibaba.fastjson.annotation.JSONField; + +/** + * 退款使用的资金渠道。 + * 只有在签约中指定需要返回资金明细,或者入参的query_options中指定时才返回该字段信息。 + * @author Egan + *
+ * email egzosn@gmail.com
+ * date 2020/8/16 18:51
+ * 
+ */ +public class TradeFundBill { + /** + * 交易使用的资金渠道,详见 支付渠道列表 ALIPAYACCOUNT + */ + @JSONField(name = "fund_channel") + private String fundChannel; + /** + * 银行卡支付时的银行代码 CEB + */ + @JSONField(name = "bank_code") + private String bankCode; + /** + * 该支付工具类型所使用的金额 10 + */ + private BigDecimal amount; + + /** + * 渠道实际付款金额 11.21 + */ + @JSONField(name = "real_amount") + private BigDecimal realAmount; + /** + * 渠道所使用的资金类型,目前只在资金渠道(fund_channel)是银行卡渠道(BANKCARD)的情况下才返回该信息(DEBIT_CARD:借记卡,CREDIT_CARD:信用卡,MIXED_CARD:借贷合一卡) DEBIT_CARD + */ + @JSONField(name = "fund_type") + private String fundType; + + public String getFundChannel() { + return fundChannel; + } + + public void setFundChannel(String fundChannel) { + this.fundChannel = fundChannel; + } + + public String getBankCode() { + return bankCode; + } + + public void setBankCode(String bankCode) { + this.bankCode = bankCode; + } + + public BigDecimal getAmount() { + return amount; + } + + public void setAmount(BigDecimal amount) { + this.amount = amount; + } + + public BigDecimal getRealAmount() { + return realAmount; + } + + public void setRealAmount(BigDecimal realAmount) { + this.realAmount = realAmount; + } + + public String getFundType() { + return fundType; + } + + public void setFundType(String fundType) { + this.fundType = fundType; + } +} diff --git a/pay-java-baidu/src/main/java/com/egzosn/pay/baidu/api/BaiduPayService.java b/pay-java-baidu/src/main/java/com/egzosn/pay/baidu/api/BaiduPayService.java index 87e4c1c..febafbf 100644 --- a/pay-java-baidu/src/main/java/com/egzosn/pay/baidu/api/BaiduPayService.java +++ b/pay-java-baidu/src/main/java/com/egzosn/pay/baidu/api/BaiduPayService.java @@ -316,7 +316,7 @@ public class BaiduPayService extends BasePayService { * @return 退款结果 */ @Override - public Map refund(RefundOrder refundOrder) { + public BaseRefundResult refund(RefundOrder refundOrder) { Map parameters = getUseQueryPay(); BaiduTransactionType transactionType = BaiduTransactionType.APPLY_REFUND; parameters.put(METHOD, transactionType.getMethod()); @@ -329,7 +329,53 @@ public class BaiduPayService extends BasePayService { parameters.put("bizRefundBatchId", refundOrder.getRefundNo()); parameters.put(APP_KEY, payConfigStorage.getAppKey()); parameters.put(RSA_SIGN, getRsaSign(parameters, RSA_SIGN)); - return requestTemplate.getForObject(String.format("%s?%s", getReqUrl(transactionType), UriVariables.getMapToParameters(parameters)), JSONObject.class); + final JSONObject result = requestTemplate.getForObject(String.format("%s?%s", getReqUrl(transactionType), UriVariables.getMapToParameters(parameters)), JSONObject.class); + return new BaseRefundResult(result) { + @Override + public String getCode() { + return getAttrString(RESPONSE_STATUS); + } + + @Override + public String getMsg() { + return null; + } + + @Override + public String getResultCode() { + return null; + } + + @Override + public String getResultMsg() { + return null; + } + + @Override + public BigDecimal getRefundFee() { + return null; + } + + @Override + public CurType getRefundCurrency() { + return null; + } + + @Override + public String getTradeNo() { + return null; + } + + @Override + public String getOutTradeNo() { + return null; + } + + @Override + public String getRefundNo() { + return null; + } + }; } diff --git a/pay-java-common/src/main/java/com/egzosn/pay/common/api/BasePayService.java b/pay-java-common/src/main/java/com/egzosn/pay/common/api/BasePayService.java index 261c794..fbc5b59 100644 --- a/pay-java-common/src/main/java/com/egzosn/pay/common/api/BasePayService.java +++ b/pay-java-common/src/main/java/com/egzosn/pay/common/api/BasePayService.java @@ -263,7 +263,7 @@ public abstract class BasePayService implements Pay @Override public T refund(RefundOrder refundOrder, Callback callback) { - return callback.perform(refund(refundOrder)); + return callback.perform(refund(refundOrder).getAttrs()); } diff --git a/pay-java-common/src/main/java/com/egzosn/pay/common/api/PayService.java b/pay-java-common/src/main/java/com/egzosn/pay/common/api/PayService.java index 910fc7e..0c1b6ac 100644 --- a/pay-java-common/src/main/java/com/egzosn/pay/common/api/PayService.java +++ b/pay-java-common/src/main/java/com/egzosn/pay/common/api/PayService.java @@ -252,7 +252,7 @@ public interface PayService { * @param refundOrder 退款订单信息 * @return 返回支付方申请退款后的结果 */ - Map refund(RefundOrder refundOrder); + RefundResult refund(RefundOrder refundOrder); /** * 申请退款接口 diff --git a/pay-java-common/src/main/java/com/egzosn/pay/common/bean/BaseRefundResult.java b/pay-java-common/src/main/java/com/egzosn/pay/common/bean/BaseRefundResult.java new file mode 100644 index 0000000..b92763a --- /dev/null +++ b/pay-java-common/src/main/java/com/egzosn/pay/common/bean/BaseRefundResult.java @@ -0,0 +1,337 @@ +package com.egzosn.pay.common.bean; + +import java.math.BigDecimal; +import java.util.Collection; +import java.util.Map; +import java.util.Set; + +/** + * 基础的退款结果对象 + * + * @author Egan + *
+ * email egzosn@gmail.com
+ * date 2020/8/16 19:29
+ * 
+ */ +public abstract class BaseRefundResult implements RefundResult { + + /** + * 属性集,支付宝退款结果 + */ + private Map attrs; + + + public BaseRefundResult() { + } + + public BaseRefundResult(Map attrs) { + this.attrs = attrs; + + } + + /** + * 获取退款结果原信息集 + * + * @return 属性 + */ + @Override + public Map getAttrs() { + return attrs; + } + + public void setAttrs(Map attrs) { + this.attrs = attrs; + } + + /** + * 获取退款结果属性值 + * + * @param key 属性名 + * @return 属性 + */ + @Override + public Object getAttr(String key) { + return attrs.get(key); + } + + /** + * 获取退款结果属性值 + * + * @param key 属性名 + * @return 属性值 + */ + @Override + public String getAttrString(String key) { + return attrs.get(key).toString(); + } + + /** + * 获取退款结果属性值 + * + * @param key 属性名 + * @return 属性值 + */ + @Override + public BigDecimal getAttrDecimal(String key) { + return new BigDecimal(getAttrString(key)); + } + + /** + * Returns the number of key-value mappings in this map. If the + * map contains more than Integer.MAX_VALUE elements, returns + * Integer.MAX_VALUE. + * + * @return the number of key-value mappings in this map + */ + @Override + public int size() { + return attrs.size(); + } + + /** + * Returns true if this map contains no key-value mappings. + * + * @return true if this map contains no key-value mappings + */ + @Override + public boolean isEmpty() { + return attrs.isEmpty(); + } + + /** + * Returns true if this map contains a mapping for the specified + * key. More formally, returns true if and only if + * this map contains a mapping for a key k such that + * (key==null ? k==null : key.equals(k)). (There can be + * at most one such mapping.) + * + * @param key key whose presence in this map is to be tested + * @return true if this map contains a mapping for the specified + * key + * @throws ClassCastException if the key is of an inappropriate type for + * this map + * (optional) + * @throws NullPointerException if the specified key is null and this map + * does not permit null keys + * (optional) + */ + @Override + public boolean containsKey(Object key) { + return attrs.containsKey(key); + } + + /** + * Returns true if this map maps one or more keys to the + * specified value. More formally, returns true if and only if + * this map contains at least one mapping to a value v such that + * (value==null ? v==null : value.equals(v)). This operation + * will probably require time linear in the map size for most + * implementations of the Map interface. + * + * @param value value whose presence in this map is to be tested + * @return true if this map maps one or more keys to the + * specified value + * @throws ClassCastException if the value is of an inappropriate type for + * this map + * (optional) + * @throws NullPointerException if the specified value is null and this + * map does not permit null values + * (optional) + */ + @Override + public boolean containsValue(Object value) { + return attrs.containsValue(value); + } + + /** + * Returns the value to which the specified key is mapped, + * or {@code null} if this map contains no mapping for the key. + * + *

More formally, if this map contains a mapping from a key + * {@code k} to a value {@code v} such that {@code (key==null ? k==null : + * key.equals(k))}, then this method returns {@code v}; otherwise + * it returns {@code null}. (There can be at most one such mapping.) + * + *

If this map permits null values, then a return value of + * {@code null} does not necessarily indicate that the map + * contains no mapping for the key; it's also possible that the map + * explicitly maps the key to {@code null}. The {@link #containsKey + * containsKey} operation may be used to distinguish these two cases. + * + * @param key the key whose associated value is to be returned + * @return the value to which the specified key is mapped, or + * {@code null} if this map contains no mapping for the key + * @throws ClassCastException if the key is of an inappropriate type for + * this map + * (optional) + * @throws NullPointerException if the specified key is null and this map + * does not permit null keys + * (optional) + */ + @Override + public Object get(Object key) { + return attrs.get(key); + } + + /** + * Associates the specified value with the specified key in this map + * (optional operation). If the map previously contained a mapping for + * the key, the old value is replaced by the specified value. (A map + * m is said to contain a mapping for a key k if and only + * if {@link #containsKey(Object) m.containsKey(k)} would return + * true.) + * + * @param key key with which the specified value is to be associated + * @param value value to be associated with the specified key + * @return the previous value associated with key, or + * null if there was no mapping for key. + * (A null return can also indicate that the map + * previously associated null with key, + * if the implementation supports null values.) + * @throws UnsupportedOperationException if the put operation + * is not supported by this map + * @throws ClassCastException if the class of the specified key or value + * prevents it from being stored in this map + * @throws NullPointerException if the specified key or value is null + * and this map does not permit null keys or values + * @throws IllegalArgumentException if some property of the specified key + * or value prevents it from being stored in this map + */ + @Override + public Object put(String key, Object value) { + return attrs.put(key, value); + } + + /** + * Removes the mapping for a key from this map if it is present + * (optional operation). More formally, if this map contains a mapping + * from key k to value v such that + * (key==null ? k==null : key.equals(k)), that mapping + * is removed. (The map can contain at most one such mapping.) + * + *

Returns the value to which this map previously associated the key, + * or null if the map contained no mapping for the key. + * + *

If this map permits null values, then a return value of + * null does not necessarily indicate that the map + * contained no mapping for the key; it's also possible that the map + * explicitly mapped the key to null. + * + *

The map will not contain a mapping for the specified key once the + * call returns. + * + * @param key key whose mapping is to be removed from the map + * @return the previous value associated with key, or + * null if there was no mapping for key. + * @throws UnsupportedOperationException if the remove operation + * is not supported by this map + * @throws ClassCastException if the key is of an inappropriate type for + * this map + * (optional) + * @throws NullPointerException if the specified key is null and this + * map does not permit null keys + * (optional) + */ + @Override + public Object remove(Object key) { + return attrs.remove(key); + } + + /** + * Copies all of the mappings from the specified map to this map + * (optional operation). The effect of this call is equivalent to that + * of calling {@link #put(Object, Object) put(k, v)} on this map once + * for each mapping from key k to value v in the + * specified map. The behavior of this operation is undefined if the + * specified map is modified while the operation is in progress. + * + * @param m mappings to be stored in this map + * @throws UnsupportedOperationException if the putAll operation + * is not supported by this map + * @throws ClassCastException if the class of a key or value in the + * specified map prevents it from being stored in this map + * @throws NullPointerException if the specified map is null, or if + * this map does not permit null keys or values, and the + * specified map contains null keys or values + * @throws IllegalArgumentException if some property of a key or value in + * the specified map prevents it from being stored in this map + */ + @Override + public void putAll(Map m) { + attrs.putAll(m); + } + + /** + * Removes all of the mappings from this map (optional operation). + * The map will be empty after this call returns. + * + * @throws UnsupportedOperationException if the clear operation + * is not supported by this map + */ + @Override + public void clear() { + attrs.clear(); + } + + /** + * Returns a {@link Set} view of the keys contained in this map. + * The set is backed by the map, so changes to the map are + * reflected in the set, and vice-versa. If the map is modified + * while an iteration over the set is in progress (except through + * the iterator's own remove operation), the results of + * the iteration are undefined. The set supports element removal, + * which removes the corresponding mapping from the map, via the + * Iterator.remove, Set.remove, + * removeAll, retainAll, and clear + * operations. It does not support the add or addAll + * operations. + * + * @return a set view of the keys contained in this map + */ + @Override + public Set keySet() { + return attrs.keySet(); + } + + /** + * Returns a {@link Collection} view of the values contained in this map. + * The collection is backed by the map, so changes to the map are + * reflected in the collection, and vice-versa. If the map is + * modified while an iteration over the collection is in progress + * (except through the iterator's own remove operation), + * the results of the iteration are undefined. The collection + * supports element removal, which removes the corresponding + * mapping from the map, via the Iterator.remove, + * Collection.remove, removeAll, + * retainAll and clear operations. It does not + * support the add or addAll operations. + * + * @return a collection view of the values contained in this map + */ + @Override + public Collection values() { + return attrs.values(); + } + + /** + * Returns a {@link Set} view of the mappings contained in this map. + * The set is backed by the map, so changes to the map are + * reflected in the set, and vice-versa. If the map is modified + * while an iteration over the set is in progress (except through + * the iterator's own remove operation, or through the + * setValue operation on a map entry returned by the + * iterator) the results of the iteration are undefined. The set + * supports element removal, which removes the corresponding + * mapping from the map, via the Iterator.remove, + * Set.remove, removeAll, retainAll and + * clear operations. It does not support the + * add or addAll operations. + * + * @return a set view of the mappings contained in this map + */ + @Override + public Set> entrySet() { + return attrs.entrySet(); + } +} 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 36b02f5..dd9f31b 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 egan. + * Copyright 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. diff --git a/pay-java-common/src/main/java/com/egzosn/pay/common/bean/Order.java b/pay-java-common/src/main/java/com/egzosn/pay/common/bean/Order.java index fd16e1c..9ba9820 100644 --- a/pay-java-common/src/main/java/com/egzosn/pay/common/bean/Order.java +++ b/pay-java-common/src/main/java/com/egzosn/pay/common/bean/Order.java @@ -1,5 +1,6 @@ package com.egzosn.pay.common.bean; +import java.io.Serializable; import java.util.Map; /** @@ -11,7 +12,7 @@ import java.util.Map; * date 2020/01/05 13:34 * */ -public interface Order { +public interface Order extends Serializable { /** * 获取订单属性 这里可用做覆盖已设置的订单信息属性,订单信息在签名前进行覆盖。 diff --git a/pay-java-common/src/main/java/com/egzosn/pay/common/bean/RefundResult.java b/pay-java-common/src/main/java/com/egzosn/pay/common/bean/RefundResult.java new file mode 100644 index 0000000..50ca0c1 --- /dev/null +++ b/pay-java-common/src/main/java/com/egzosn/pay/common/bean/RefundResult.java @@ -0,0 +1,103 @@ +package com.egzosn.pay.common.bean; + +import java.io.Serializable; +import java.math.BigDecimal; +import java.util.Map; + +/** + * 退款结果 + * + * 这里继承Map为兼容方案,后续版本进行删除 + * @author Egan + *
+ * email egzosn@gmail.com
+ * date 2020/8/16 9:55
+ * 
+ */ +public interface RefundResult extends Map, Serializable { + /** + * 获取退款结果原信息集 + * + * @return 属性 + */ + Map getAttrs(); + + /** + * 获取退款结果属性值 + * + * @param key 属性名 + * @return 属性值 + */ + Object getAttr(String key); + /** + * 获取退款结果属性值 + * + * @param key 属性名 + * @return 属性值 + */ + String getAttrString(String key); + /** + * 获取退款结果属性值 + * + * @param key 属性名 + * @return 属性值 + */ + BigDecimal getAttrDecimal(String key); + + + /** + * 获取退款请求结果状态码 + * @return 状态码 + */ + String getCode(); + + /** + * 获取退款请求结果状态提示信息 + * @return 提示信息 + */ + String getMsg(); + + /** + * 返回业务结果状态码 + * @return 业务结果状态码 + */ + String getResultCode(); + + /** + * 返回业务结果状态提示信息 + * @return 业务结果状态提示信息 + */ + String getResultMsg(); + + /** + * 退款金额 + * @return 退款金额 + */ + BigDecimal getRefundFee(); + /** + * 退款币种信息 + * @return 币种信息 + */ + CurType getRefundCurrency(); + + /** + * 支付平台交易号 + * 发起支付时 支付平台(如支付宝)返回的交易订单号 + * + * @return 支付平台交易号 + */ + String getTradeNo(); + + /** + * 支付订单号 + * 发起支付时,用户系统的订单号 + * @return 支付订单号 + */ + String getOutTradeNo(); + + /** + * 商户退款单号 + * @return 商户退款单号 + */ + String getRefundNo(); +} 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 a323e8a..1ee67ec 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 @@ -352,9 +352,4 @@ public class XML { } - public static void main(String[] args) { - String text = "01张三2张4"; - System.out.println( getMap2Xml(toJSONObject(text), "datas", "utf-8")); - - } } diff --git a/pay-java-demo/pom.xml b/pay-java-demo/pom.xml index 9cec5a2..79c6805 100644 --- a/pay-java-demo/pom.xml +++ b/pay-java-demo/pom.xml @@ -86,7 +86,7 @@ com.fasterxml.jackson.core jackson-databind - 2.9.10.1 + 2.11.2 diff --git a/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/AliPayController.java b/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/AliPayController.java index 7ee3fbf..2f528e7 100644 --- a/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/AliPayController.java +++ b/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/AliPayController.java @@ -4,6 +4,7 @@ package com.egzosn.pay.demo.controller; import com.egzosn.pay.ali.api.AliPayConfigStorage; import com.egzosn.pay.ali.api.AliPayService; +import com.egzosn.pay.ali.bean.AliRefundResult; import com.egzosn.pay.ali.bean.AliTransactionType; import com.egzosn.pay.ali.bean.AliTransferOrder; import com.egzosn.pay.ali.bean.AliTransferType; @@ -279,7 +280,7 @@ public class AliPayController { * @return 返回支付方申请退款后的结果 */ @RequestMapping("refund") - public Map refund(RefundOrder order) { + public AliRefundResult refund(RefundOrder order) { return service.refund(order); } 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 c7adb42..bd998bc 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 @@ -422,7 +422,7 @@ public class PayController { * @return 返回支付方申请退款后的结果 */ @RequestMapping("refund") - public Map refund(Integer payId, RefundOrder order) { + public RefundResult refund(Integer payId, RefundOrder order) { PayResponse payResponse = service.getPayResponse(payId); // return payResponse.getService().refund(order.getTradeNo(), order.getOutTradeNo(), order.getRefundAmount(), order.getTotalAmount()); diff --git a/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/PayPalPayController.java b/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/PayPalPayController.java index f9a7873..fe84f00 100644 --- a/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/PayPalPayController.java +++ b/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/PayPalPayController.java @@ -5,6 +5,7 @@ import com.egzosn.pay.common.api.PayService; import com.egzosn.pay.common.bean.DefaultCurType; import com.egzosn.pay.common.bean.PayOrder; import com.egzosn.pay.common.bean.RefundOrder; +import com.egzosn.pay.common.bean.RefundResult; import com.egzosn.pay.common.http.HttpConfigStorage; import com.egzosn.pay.paypal.api.PayPalConfigStorage; import com.egzosn.pay.paypal.api.PayPalPayService; @@ -86,7 +87,7 @@ public class PayPalPayController { * @return 返回支付方申请退款后的结果 */ @RequestMapping("refund") - public Map refund() { + public RefundResult refund() { // TODO 这里需要 refundAmount, curType, description, tradeNo RefundOrder order = new RefundOrder(); order.setCurType(DefaultCurType.USD); diff --git a/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/UnionPayController.java b/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/UnionPayController.java index 6ced1dc..8c1e6e5 100644 --- a/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/UnionPayController.java +++ b/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/UnionPayController.java @@ -12,6 +12,7 @@ import com.egzosn.pay.common.util.sign.SignUtils; import com.egzosn.pay.demo.request.QueryOrder; import com.egzosn.pay.union.api.UnionPayConfigStorage; import com.egzosn.pay.union.api.UnionPayService; +import com.egzosn.pay.union.bean.UnionRefundResult; import com.egzosn.pay.union.bean.UnionTransactionType; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @@ -266,7 +267,7 @@ public class UnionPayController { * @return 返回支付方申请退款后的结果 */ @RequestMapping("refund") - public Map refund(RefundOrder order) { + public UnionRefundResult refund(RefundOrder order) { return service.refund(order); } diff --git a/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/WxPayController.java b/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/WxPayController.java index 096f609..a554c73 100644 --- a/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/WxPayController.java +++ b/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/WxPayController.java @@ -309,7 +309,7 @@ public class WxPayController { * @return 返回支付方申请退款后的结果 */ @RequestMapping("refund") - public Map refund(RefundOrder order) { + public WxRefundResult refund(RefundOrder order) { if("ssl 退款证书".equals(KEYSTORE)){ throw new RuntimeException("请设置好SSL退款证书"); } diff --git a/pay-java-fuiou/src/main/java/com/egzosn/pay/fuiou/api/FuiouPayService.java b/pay-java-fuiou/src/main/java/com/egzosn/pay/fuiou/api/FuiouPayService.java index 8f6b095..43f3348 100644 --- a/pay-java-fuiou/src/main/java/com/egzosn/pay/fuiou/api/FuiouPayService.java +++ b/pay-java-fuiou/src/main/java/com/egzosn/pay/fuiou/api/FuiouPayService.java @@ -9,6 +9,7 @@ import com.egzosn.pay.common.util.DateUtils; import com.egzosn.pay.common.util.Util; import com.egzosn.pay.common.util.sign.SignUtils; import com.egzosn.pay.common.util.str.StringUtils; +import com.egzosn.pay.fuiou.bean.FuiouRefundResult; import com.egzosn.pay.fuiou.bean.FuiouTransactionType; import java.awt.image.BufferedImage; @@ -379,7 +380,7 @@ public class FuiouPayService extends BasePayService { * @return 退款返回结果集 */ @Override - public Map refund(RefundOrder refundOrder) { + public RefundResult refund(RefundOrder refundOrder) { Map params = new HashMap<>(); //商户代码 params.put("mchnt_cd", payConfigStorage.getPid()); @@ -394,7 +395,7 @@ public class FuiouPayService extends BasePayService { params.putAll(refundOrder.getAttrs()); params.put("md5", createSign(SignUtils.parameters2MD5Str(params, "|"), payConfigStorage.getInputCharset())); JSONObject resultJson = getHttpRequestTemplate().postForObject(getReqUrl() + URL_FuiouSmpRefundGate, params, JSONObject.class); - return resultJson; + return FuiouRefundResult.create(resultJson); } diff --git a/pay-java-fuiou/src/main/java/com/egzosn/pay/fuiou/bean/FuiouRefundResult.java b/pay-java-fuiou/src/main/java/com/egzosn/pay/fuiou/bean/FuiouRefundResult.java new file mode 100644 index 0000000..e85dfc1 --- /dev/null +++ b/pay-java-fuiou/src/main/java/com/egzosn/pay/fuiou/bean/FuiouRefundResult.java @@ -0,0 +1,166 @@ +package com.egzosn.pay.fuiou.bean; + +import java.math.BigDecimal; +import java.util.Map; + +import com.alibaba.fastjson.JSONObject; +import com.alibaba.fastjson.annotation.JSONField; +import com.egzosn.pay.common.bean.BaseRefundResult; +import com.egzosn.pay.common.bean.CurType; + +/** + * 富友退款结果 + * @author Egan、 + *
+ * email egzosn@gmail.com
+ * date 2020/8/16 21:17
+ * 
+ */ +public class FuiouRefundResult extends BaseRefundResult { + + private String originOrderId; + /** + * 退款状态 + * ‘0’ – 已受理 + * ‘1’ – 成功 + * ‘2’ – 失败 + */ + @JSONField(name = "order_st") + private String orderSt; + /** + * 错误代码 + * 5341表示退款成功 + */ + @JSONField(name = "order_pay_code") + private String orderPayCode; + /** + * 错误中文描述 + * 5341表示退款成功 + */ + @JSONField(name = "order_pay_error") + private String orderPayError; + /** + * 富友流水号 + * 供商户查询支付交易状态及对账用 + */ + @JSONField(name = "fy_ssn") + private String fySsn; + /** + * 保留字段 + */ + private String resv1; + /** + * MD5摘要数据 + * mchnt_cd + "|" + order_st + "|" + + * order_pay_code + "|" + + * order_pay_error + "|" + fy_ssn + "|" + + * resv1 + "|" + mchnt_key + * 做MD5摘要 + * 其中mchnt_key 为32位的商户密钥,系统分配 + */ + private String md5; + + /** + * 退款金额 + */ + private BigDecimal refundAmt; + + /** + * 获取退款请求结果状态码 + * + * @return 状态码 + */ + @Override + public String getCode() { + return orderSt; + } + + /** + * 获取退款请求结果状态提示信息 + * + * @return 提示信息 + */ + @Override + public String getMsg() { + return orderPayError; + } + + /** + * 返回业务结果状态码 + * + * @return 业务结果状态码 + */ + @Override + public String getResultCode() { + return orderPayCode; + } + + /** + * 返回业务结果状态提示信息 + * + * @return 业务结果状态提示信息 + */ + @Override + public String getResultMsg() { + return orderPayError; + } + + /** + * 退款金额 + * + * @return 退款金额 + */ + @Override + public BigDecimal getRefundFee() { + return null; + } + + /** + * 退款币种信息 + * + * @return 币种信息 + */ + @Override + public CurType getRefundCurrency() { + return null; + } + + /** + * 支付平台交易号 + * 发起支付时 支付平台(如支付宝)返回的交易订单号 + * + * @return 支付平台交易号 + */ + @Override + public String getTradeNo() { + return fySsn; + } + + /** + * 支付订单号 + * 发起支付时,用户系统的订单号 + * + * @return 支付订单号 + */ + @Override + public String getOutTradeNo() { + return originOrderId; + } + + /** + * 商户退款单号 + * + * @return 商户退款单号 + */ + @Override + public String getRefundNo() { + return null; + } + + public static final FuiouRefundResult create(Map result){ + FuiouRefundResult refundResult = new JSONObject(result).toJavaObject(FuiouRefundResult.class); + refundResult.setAttrs(result); + return refundResult; + } + +} diff --git a/pay-java-payoneer/README.md b/pay-java-payoneer/README.md index 06e14f3..c809467 100644 --- a/pay-java-payoneer/README.md +++ b/pay-java-payoneer/README.md @@ -120,7 +120,7 @@ //Map result = service.refund(null, "我方系统单号", null, null); //支付宝单号与我方系统单号二选一 RefundOrder order = new RefundOrder(null, "我方系统单号", null, null); - Map result = service.refund(order); + RefundResult result = service.refund(order); ``` diff --git a/pay-java-payoneer/src/main/java/com/egzosn/pay/payoneer/api/PayoneerPayService.java b/pay-java-payoneer/src/main/java/com/egzosn/pay/payoneer/api/PayoneerPayService.java index 8596b7b..617d79a 100644 --- a/pay-java-payoneer/src/main/java/com/egzosn/pay/payoneer/api/PayoneerPayService.java +++ b/pay-java-payoneer/src/main/java/com/egzosn/pay/payoneer/api/PayoneerPayService.java @@ -18,6 +18,7 @@ import org.apache.http.Header; import org.apache.http.entity.ContentType; import org.apache.http.message.BasicHeader; +import java.math.BigDecimal; import java.util.*; /** @@ -323,8 +324,53 @@ public class PayoneerPayService extends BasePayService im * @return 返回支付方申请退款后的结果 */ @Override - public Map refund(RefundOrder refundOrder) { - return close(refundOrder.getTradeNo(), refundOrder.getOutTradeNo()); + public RefundResult refund(RefundOrder refundOrder) { + return new BaseRefundResult(close(refundOrder.getTradeNo(), refundOrder.getOutTradeNo())) { + @Override + public String getCode() { + return getAttrString(CODE); + } + + @Override + public String getMsg() { + return null; + } + + @Override + public String getResultCode() { + return null; + } + + @Override + public String getResultMsg() { + return null; + } + + @Override + public BigDecimal getRefundFee() { + return null; + } + + @Override + public CurType getRefundCurrency() { + return null; + } + + @Override + public String getTradeNo() { + return null; + } + + @Override + public String getOutTradeNo() { + return null; + } + + @Override + public String getRefundNo() { + return null; + } + }; } diff --git a/pay-java-paypal/README.md b/pay-java-paypal/README.md index 590af71..5730273 100644 --- a/pay-java-paypal/README.md +++ b/pay-java-paypal/README.md @@ -95,6 +95,6 @@ order.setDescription(" description "); order.setTradeNo("paypal 平台的单号"); order.setRefundAmount(new BigDecimal(0.01)); - Map result = service.refund(order); + RefundResult result = service.refund(order); ``` diff --git a/pay-java-paypal/src/main/java/com/egzosn/pay/paypal/api/PayPalPayService.java b/pay-java-paypal/src/main/java/com/egzosn/pay/paypal/api/PayPalPayService.java index ea120e7..b1447ed 100644 --- a/pay-java-paypal/src/main/java/com/egzosn/pay/paypal/api/PayPalPayService.java +++ b/pay-java-paypal/src/main/java/com/egzosn/pay/paypal/api/PayPalPayService.java @@ -249,7 +249,7 @@ public class PayPalPayService extends BasePayService{ * @return 返回支付方申请退款后的结果 */ @Override - public Map refund(RefundOrder refundOrder) { + public RefundResult refund(RefundOrder refundOrder) { JSONObject request = new JSONObject(); if (null != refundOrder.getRefundAmount() && BigDecimal.ZERO.compareTo( refundOrder.getRefundAmount()) == -1){ @@ -267,7 +267,52 @@ public class PayPalPayService extends BasePayService{ HttpStringEntity httpEntity = new HttpStringEntity(request.toJSONString(), ContentType.APPLICATION_JSON); httpEntity.setHeaders(authHeader()); JSONObject resp = getHttpRequestTemplate().postForObject(getReqUrl(PayPalTransactionType.REFUND), httpEntity, JSONObject.class, refundOrder.getTradeNo()); - return resp; + return new BaseRefundResult(resp) { + @Override + public String getCode() { + return getAttrString("state"); + } + + @Override + public String getMsg() { + return null; + } + + @Override + public String getResultCode() { + return null; + } + + @Override + public String getResultMsg() { + return null; + } + + @Override + public BigDecimal getRefundFee() { + return null; + } + + @Override + public CurType getRefundCurrency() { + return null; + } + + @Override + public String getTradeNo() { + return null; + } + + @Override + public String getOutTradeNo() { + return null; + } + + @Override + public String getRefundNo() { + return null; + } + }; } /** diff --git a/pay-java-union/README.md b/pay-java-union/README.md index 40951df..4d370b1 100644 --- a/pay-java-union/README.md +++ b/pay-java-union/README.md @@ -190,6 +190,6 @@ RefundOrder order = new RefundOrder(null, "原交易查询流水号", "退款金额", "订单总金额"); order.setRefundNo("退款单号") - Map result = service.refund(order); + UnionRefundResult result = service.refund(order); ``` diff --git a/pay-java-union/src/main/java/com/egzosn/pay/union/api/UnionPayService.java b/pay-java-union/src/main/java/com/egzosn/pay/union/api/UnionPayService.java index 24d64da..953c645 100644 --- a/pay-java-union/src/main/java/com/egzosn/pay/union/api/UnionPayService.java +++ b/pay-java-union/src/main/java/com/egzosn/pay/union/api/UnionPayService.java @@ -17,6 +17,7 @@ import com.egzosn.pay.common.util.sign.encrypt.RSA2; import com.egzosn.pay.common.util.str.StringUtils; import com.egzosn.pay.union.bean.SDKConstants; import com.egzosn.pay.union.bean.UnionPayMessage; +import com.egzosn.pay.union.bean.UnionRefundResult; import com.egzosn.pay.union.bean.UnionTransactionType; import java.io.ByteArrayInputStream; @@ -591,7 +592,7 @@ public class UnionPayService extends BasePayService { * @param type UnionTransactionType.REFUND 或者UnionTransactionType.CONSUME_UNDO * @return 返回支付方申请退款后的结果 */ - public Map unionRefundOrConsumeUndo(RefundOrder refundOrder, UnionTransactionType type) { + public UnionRefundResult unionRefundOrConsumeUndo(RefundOrder refundOrder, UnionTransactionType type) { Map params = this.getCommonParam(); type.convertMap(params); params.put(SDKConstants.param_orderId, refundOrder.getRefundNo()); @@ -601,12 +602,11 @@ public class UnionPayService extends BasePayService { this.setSign(params); String responseStr = getHttpRequestTemplate().postForObject(this.getBackTransUrl(), params, String.class); JSONObject response = UriVariables.getParametersToMap(responseStr); + if (this.verify(response)) { - if (SDKConstants.OK_RESP_CODE.equals(response.getString(SDKConstants.param_respCode))) { -// String origRespCode = response.getString(SDKConstants.param_origRespCode); - //交易成功,更新商户订单状态 - //TODO - return response; + final UnionRefundResult refundResult = UnionRefundResult.create(response); + if (SDKConstants.OK_RESP_CODE.equals(refundResult.getRespCode())) { + return refundResult; } throw new PayErrorException(new PayException(response.getString(SDKConstants.param_respCode), response.getString(SDKConstants.param_respMsg), response.toJSONString())); @@ -628,7 +628,7 @@ public class UnionPayService extends BasePayService { @Override - public Map refund(RefundOrder refundOrder) { + public UnionRefundResult refund(RefundOrder refundOrder) { return unionRefundOrConsumeUndo(refundOrder, UnionTransactionType.REFUND); } diff --git a/pay-java-union/src/main/java/com/egzosn/pay/union/bean/UnionRefundResult.java b/pay-java-union/src/main/java/com/egzosn/pay/union/bean/UnionRefundResult.java new file mode 100644 index 0000000..36990ca --- /dev/null +++ b/pay-java-union/src/main/java/com/egzosn/pay/union/bean/UnionRefundResult.java @@ -0,0 +1,326 @@ +package com.egzosn.pay.union.bean; + +import java.math.BigDecimal; +import java.util.Map; + +import com.alibaba.fastjson.JSONObject; +import com.egzosn.pay.common.bean.BaseRefundResult; +import com.egzosn.pay.common.bean.CurType; + +/** + * 银联退款结果 + * + * @author Egan + * email egzosn@gmail.com + * date 2020/8/16 22:15 + */ +public class UnionRefundResult extends BaseRefundResult { + + /** + * 二维码数据 + */ + private String qrCode; + /** + * 签名 + */ + private String signature; + /** + * 签名方法 + */ + private String signMethod; + /** + * 应答码 + */ + private String respCode; + /** + * 应答信息 + */ + private String respMsg; + /** + * 签名公钥证书 + */ + private String signPubKeyCert; + /** + * 版本号 + */ + private String version; + /** + * 编码方式 + */ + private String encoding; + /** + * 产品类型 + */ + private String bizType; + /** + * 订单发送时间 + */ + private String txnTime; + + /** + * 交易类型 + */ + private String txnType; + /** + * 交易子类 + */ + private String txnSubType; + /** + * 接入类型 + * 0:商户直连接入 + * 1:收单机构接入 + * 2:平台商户接入 + */ + private String accessType; + /** + * 请求方保留域 + */ + private String reqReserved; + /** + * 商户代码 + */ + private String merId; + /** + * 商户订单号 + */ + private String orderId; + /** + * 保留域 + */ + private String reserved; + + + /** + * 获取退款请求结果状态码 + * + * @return 状态码 + */ + @Override + public String getCode() { + return respCode; + } + + /** + * 获取退款请求结果状态提示信息 + * + * @return 提示信息 + */ + @Override + public String getMsg() { + return respMsg; + } + + /** + * 返回业务结果状态码 + * + * @return 业务结果状态码 + */ + @Override + public String getResultCode() { + return null; + } + + /** + * 返回业务结果状态提示信息 + * + * @return 业务结果状态提示信息 + */ + @Override + public String getResultMsg() { + return null; + } + + /** + * 退款金额 + * + * @return 退款金额 + */ + @Override + public BigDecimal getRefundFee() { + return null; + } + + /** + * 退款币种信息 + * + * @return 币种信息 + */ + @Override + public CurType getRefundCurrency() { + return null; + } + + /** + * 支付平台交易号 + * 发起支付时 支付平台(如支付宝)返回的交易订单号 + * + * @return 支付平台交易号 + */ + @Override + public String getTradeNo() { + return null; + } + + /** + * 支付订单号 + * 发起支付时,用户系统的订单号 + * + * @return 支付订单号 + */ + @Override + public String getOutTradeNo() { + return orderId; + } + + /** + * 商户退款单号 + * + * @return 商户退款单号 + */ + @Override + public String getRefundNo() { + return null; + } + + public String getQrCode() { + return qrCode; + } + + public void setQrCode(String qrCode) { + this.qrCode = qrCode; + } + + public String getSignature() { + return signature; + } + + public void setSignature(String signature) { + this.signature = signature; + } + + public String getSignMethod() { + return signMethod; + } + + public void setSignMethod(String signMethod) { + this.signMethod = signMethod; + } + + public String getRespCode() { + return respCode; + } + + public void setRespCode(String respCode) { + this.respCode = respCode; + } + + public String getRespMsg() { + return respMsg; + } + + public void setRespMsg(String respMsg) { + this.respMsg = respMsg; + } + + public String getSignPubKeyCert() { + return signPubKeyCert; + } + + public void setSignPubKeyCert(String signPubKeyCert) { + this.signPubKeyCert = signPubKeyCert; + } + + public String getVersion() { + return version; + } + + public void setVersion(String version) { + this.version = version; + } + + public String getEncoding() { + return encoding; + } + + public void setEncoding(String encoding) { + this.encoding = encoding; + } + + public String getBizType() { + return bizType; + } + + public void setBizType(String bizType) { + this.bizType = bizType; + } + + public String getTxnTime() { + return txnTime; + } + + public void setTxnTime(String txnTime) { + this.txnTime = txnTime; + } + + public String getTxnType() { + return txnType; + } + + public void setTxnType(String txnType) { + this.txnType = txnType; + } + + public String getTxnSubType() { + return txnSubType; + } + + public void setTxnSubType(String txnSubType) { + this.txnSubType = txnSubType; + } + + public String getAccessType() { + return accessType; + } + + public void setAccessType(String accessType) { + this.accessType = accessType; + } + + public String getReqReserved() { + return reqReserved; + } + + public void setReqReserved(String reqReserved) { + this.reqReserved = reqReserved; + } + + public String getMerId() { + return merId; + } + + public void setMerId(String merId) { + this.merId = merId; + } + + public String getOrderId() { + return orderId; + } + + public void setOrderId(String orderId) { + this.orderId = orderId; + } + + public String getReserved() { + return reserved; + } + + public void setReserved(String reserved) { + this.reserved = reserved; + } + + public static final UnionRefundResult create(Map result){ + UnionRefundResult refundResult = new JSONObject(result).toJavaObject(UnionRefundResult.class); + refundResult.setAttrs(result); + return refundResult; + } +} 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 e28cb10..82f6c8a 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 @@ -393,7 +393,7 @@ public class WxYouDianPayService extends BasePayService refund(RefundOrder refundOrder) { + public RefundResult refund(RefundOrder refundOrder) { String apbNonce = SignUtils.randomStr(); TreeMap data = new TreeMap<>(); data.put("access_token", payConfigStorage.getAccessToken()); @@ -408,8 +408,53 @@ public class WxYouDianPayService extends BasePayService implements * @return 返回支付方申请退款后的结果 */ @Override - public Map refund(RefundOrder refundOrder) { + public WxRefundResult refund(RefundOrder refundOrder) { //获取公共参数 Map parameters = getPublicParameters(); @@ -506,7 +506,7 @@ public class WxPayService extends BasePayService implements parameters.putAll(refundOrder.getAttrs()); //设置签名 setSign(parameters); - return requestTemplate.postForObject(getReqUrl(WxTransactionType.REFUND), XML.getMap2Xml(parameters), JSONObject.class); + return WxRefundResult.create(requestTemplate.postForObject(getReqUrl(WxTransactionType.REFUND), XML.getMap2Xml(parameters), JSONObject.class)); } diff --git a/pay-java-wx/src/main/java/com/egzosn/pay/wx/bean/WxRefundResult.java b/pay-java-wx/src/main/java/com/egzosn/pay/wx/bean/WxRefundResult.java new file mode 100644 index 0000000..e17c385 --- /dev/null +++ b/pay-java-wx/src/main/java/com/egzosn/pay/wx/bean/WxRefundResult.java @@ -0,0 +1,496 @@ +package com.egzosn.pay.wx.bean; + +import java.math.BigDecimal; +import java.util.Map; + +import com.alibaba.fastjson.JSONObject; +import com.alibaba.fastjson.annotation.JSONField; +import com.egzosn.pay.common.bean.BaseRefundResult; +import com.egzosn.pay.common.bean.CurType; + +/** + * 微信退款结果 + * @author Egan + *

+ * email egzosn@gmail.com
+ * date 2020/8/16 21:29
+ */
+public class WxRefundResult extends BaseRefundResult {
+
+    /**
+     * 返回状态码
+     * SUCCESS/FAIL
+     * 此字段是通信标识,表示接口层的请求结果,并非退款状态。
+     */
+    @JSONField(name = "return_code")
+    private String returnCode;
+    /**
+     * 返回信息
+     * 当return_code为FAIL时返回信息为错误原因 ,例如
+     * 签名失败
+     * 参数格式校验错误
+     */
+    @JSONField(name = "return_msg")
+    private String returnMsg;
+    /**
+     * 业务结果
+     * SUCCESS/FAIL
+     * SUCCESS退款申请接收成功,结果通过退款查询接口查询
+     * FAIL 提交业务失败
+     */
+    @JSONField(name = "result_code")
+    private String resultCode;
+    /**
+     * 错误代码
+     * 名称 	描述 	原因 	解决方案
+     * SYSTEMERROR 	接口返回错误 	系统超时等 	请不要更换商户退款单号,请使用相同参数再次调用API。
+     * BIZERR_NEED_RETRY 	退款业务流程错误,需要商户触发重试来解决 	并发情况下,业务被拒绝,商户重试即可解决 	请不要更换商户退款单号,请使用相同参数再次调用API。
+     * TRADE_OVERDUE 	订单已经超过退款期限 	订单已经超过可退款的最大期限(支付后一年内可退款) 	请选择其他方式自行退款
+     * ERROR 	业务错误 	申请退款业务发生错误 	该错误都会返回具体的错误原因,请根据实际返回做相应处理。
+     * USER_ACCOUNT_ABNORMAL 	退款请求失败 	用户帐号注销 	此状态代表退款申请失败,商户可自行处理退款。
+     * INVALID_REQ_TOO_MUCH 	无效请求过多 	连续错误请求数过多被系统短暂屏蔽 	请检查业务是否正常,确认业务正常后请在1分钟后再来重试
+     * NOTENOUGH 	余额不足 	商户可用退款余额不足 	此状态代表退款申请失败,商户可根据具体的错误提示做相应的处理。
+     * INVALID_TRANSACTIONID 	无效transaction_id 	请求参数未按指引进行填写 	请求参数错误,检查原交易号是否存在或发起支付交易接口返回失败
+     * PARAM_ERROR 	参数错误 	请求参数未按指引进行填写 	请求参数错误,请重新检查再调用退款申请
+     * APPID_NOT_EXIST 	APPID不存在 	参数中缺少APPID 	请检查APPID是否正确
+     * MCHID_NOT_EXIST 	MCHID不存在 	参数中缺少MCHID 	请检查MCHID是否正确
+     * ORDERNOTEXIST 	订单号不存在 	缺少有效的订单号 	请检查你的订单号是否正确且是否已支付,未支付的订单不能发起退款
+     * REQUIRE_POST_METHOD 	请使用post方法 	未使用post传递参数  	请检查请求参数是否通过post方法提交
+     * SIGNERROR 	签名错误 	参数签名结果不正确 	请检查签名参数和方法是否都符合签名算法要求
+     * XML_FORMAT_ERROR 	XML格式错误 	XML格式错误 	请检查XML参数格式是否正确
+     * FREQUENCY_LIMITED 	频率限制 	2个月之前的订单申请退款有频率限制 	该笔退款未受理,请降低频率后重试
+     * NOAUTH 	异常IP请求不予受理 	请求ip异常 	如果是动态ip,请登录商户平台后台关闭ip安全配置;
+     * 如果是静态ip,请确认商户平台配置的请求ip 在不在配的ip列表里
+     * CERT_ERROR 	证书校验错误 	请检查证书是否正确,证书是否过期或作废。 	请检查证书是否正确,证书是否过期或作废。
+     * REFUND_FEE_MISMATCH 	订单金额或退款金额与之前请求不一致,请核实后再试 	订单金额或退款金额与之前请求不一致,请核实后再试 	订单金额或退款金额与之前请求不一致,请核实后再试
+     * INVALID_REQUEST 	请求参数符合参数格式,但不符合业务规则 	此状态代表退款申请失败,商户可根据具体的错误提示做相应的处理。 	此状态代表退款申请失败,商户可根据具体的错误提示做相应的处理。
+     */
+    @JSONField(name = "err_code")
+    private String errCode;
+    /**
+     * 错误代码描述
+     */
+    @JSONField(name = "err_code_des")
+    private String errCodeDes;
+    /**
+     * 应用id
+     * 公众账号ID
+     * 微信分配的公众账号ID
+     */
+    @JSONField(name = "appid")
+    private String appid;
+    /**
+     * 商户号
+     * 微信支付分配的商户号
+     */
+    @JSONField(name = "mch_id")
+    private String mchId;
+    /**
+     *  	随机字符串,不长于32位
+     */
+    @JSONField(name = "nonce_str")
+    private String nonceStr;
+    /**
+     * 签名
+     */
+    @JSONField(name = "sign")
+    private String sign;
+    /**
+     * 微信订单号
+     */
+    @JSONField(name = "transaction_id")
+    private String transactionId;
+    /**
+     * 商户订单号
+     */
+    @JSONField(name = "out_trade_no")
+    private String outTradeNo;
+    /**
+     * 商户退款单号
+     */
+    @JSONField(name = "out_refund_no")
+    private String outRefundNo;
+    /**
+     * 微信退款单号
+     */
+    @JSONField(name = "refund_id")
+    private String refundId;
+    /**
+     * 退款金额
+     * 退款总金额,单位为分,可以做部分退款
+     */
+    @JSONField(name = "refund_fee")
+    private BigDecimal refundFee;
+    /**
+     * 应结退款金额
+     * 去掉非充值代金券退款金额后的退款金额,退款金额=申请退款金额-非充值代金券退款金额,退款金额<=申请退款金额
+     */
+    @JSONField(name = "settlement_refund_fee")
+    private BigDecimal settlementRefundFee;
+    /**
+     * 标价金额
+     * 订单总金额,单位为分,只能为整数,详见支付金额
+     */
+    @JSONField(name = "total_fee")
+    private BigDecimal totalFee;
+    /**
+     * 应结订单金额
+     * 去掉非充值代金券金额后的订单总金额,应结订单金额=订单金额-非充值代金券金额,应结订单金额<=订单金额。
+     */
+    @JSONField(name = "settlement_total_fee")
+    private BigDecimal settlementTotalFee;
+    /**
+     * 标价币种
+     * 订单金额货币类型,符合ISO 4217标准的三位字母代码,默认人民币:CNY,其他值列表详见货币类型
+     */
+    @JSONField(name = "fee_type")
+    private CurType feeType;
+    /**
+     * 现金支付金额
+     * 现金支付金额,单位为分,只能为整数,详见支付金额
+     */
+    @JSONField(name = "cash_fee")
+    private BigDecimal cashFee;
+    /**
+     * 现金支付币种
+     * 货币类型,符合ISO 4217标准的三位字母代码,默认人民币:CNY,其他值列表详见货币类型
+     */
+    @JSONField(name = "cash_fee_type")
+    private CurType cashFeeType;
+    /**
+     * 现金退款金额
+     * 现金退款金额,单位为分,只能为整数,详见支付金额
+     */
+    @JSONField(name = "cash_refund_fee")
+    private BigDecimal cashRefundFee;
+    /**
+     * 代金券类型
+     * CASH--充值代金券
+     *
+     * NO_CASH---非充值代金券
+     *
+     * 订单使用代金券时有返回(取值:CASH、NO_CASH)。$n为下标,从0开始编号,举例:coupon_type_0
+     * 这里只接收0的,其余请自行获取
+     */
+    @JSONField(name = "coupon_type_0")
+    private String couponType0;
+    /**
+     * 代金券退款总金额
+     * 代金券退款金额<=退款金额,退款金额-代金券或立减优惠退款金额为现金,说明详见代金券或立减优惠
+     */
+    @JSONField(name = "coupon_refund_fee")
+    private BigDecimal couponRefundFee;
+    /**
+     * 单个代金券退款金额
+     * 代金券退款金额<=退款金额,退款金额-代金券或立减优惠退款金额为现金,说明详见代金券或立减优惠
+     * 这里只接收0的,其余请自行获取
+     */
+    @JSONField(name = "coupon_refund_fee_0")
+    private BigDecimal couponRefundFee0;
+    @JSONField(name = "coupon_refund_count")
+    /**
+     * 退款代金券使用数量
+     * 退款代金券使用数量
+     */
+    private Integer couponRefundCount;
+    /**
+     * 退款代金券ID, $n为下标,从0开始编号
+     * 这里只接收0的,其余请自行获取
+     */
+    @JSONField(name = "coupon_refund_id_0")
+    private Integer couponRefundId0;
+    /**
+     * 获取退款请求结果状态码
+     *
+     * @return 状态码
+     */
+    @Override
+    public String getCode() {
+        return returnCode;
+    }
+
+    /**
+     * 获取退款请求结果状态提示信息
+     *
+     * @return 提示信息
+     */
+    @Override
+    public String getMsg() {
+        return returnMsg;
+    }
+
+    /**
+     * 返回业务结果状态码
+     *
+     * @return 业务结果状态码
+     */
+    @Override
+    public String getResultCode() {
+        return resultCode;
+    }
+
+    /**
+     * 返回业务结果状态提示信息
+     *
+     * @return 业务结果状态提示信息
+     */
+    @Override
+    public String getResultMsg() {
+        return errCodeDes;
+    }
+
+    /**
+     * 退款金额
+     *
+     * @return 退款金额
+     */
+    @Override
+    public BigDecimal getRefundFee() {
+        return refundFee;
+    }
+
+    /**
+     * 退款币种信息
+     *
+     * @return 币种信息
+     */
+    @Override
+    public CurType getRefundCurrency() {
+        return feeType;
+    }
+
+    /**
+     * 支付平台交易号
+     * 发起支付时 支付平台(如支付宝)返回的交易订单号
+     *
+     * @return 支付平台交易号
+     */
+    @Override
+    public String getTradeNo() {
+        return transactionId;
+    }
+
+    /**
+     * 支付订单号
+     * 发起支付时,用户系统的订单号
+     *
+     * @return 支付订单号
+     */
+    @Override
+    public String getOutTradeNo() {
+        return outTradeNo;
+    }
+
+    /**
+     * 商户退款单号
+     *
+     * @return 商户退款单号
+     */
+    @Override
+    public String getRefundNo() {
+        return outRefundNo;
+    }
+
+    public String getReturnCode() {
+        return returnCode;
+    }
+
+    public void setReturnCode(String returnCode) {
+        this.returnCode = returnCode;
+    }
+
+    public String getReturnMsg() {
+        return returnMsg;
+    }
+
+    public void setReturnMsg(String returnMsg) {
+        this.returnMsg = returnMsg;
+    }
+
+    public void setResultCode(String resultCode) {
+        this.resultCode = resultCode;
+    }
+
+    public String getErrCode() {
+        return errCode;
+    }
+
+    public void setErrCode(String errCode) {
+        this.errCode = errCode;
+    }
+
+    public String getErrCodeDes() {
+        return errCodeDes;
+    }
+
+    public void setErrCodeDes(String errCodeDes) {
+        this.errCodeDes = errCodeDes;
+    }
+
+    public String getAppid() {
+        return appid;
+    }
+
+    public void setAppid(String appid) {
+        this.appid = appid;
+    }
+
+    public String getMchId() {
+        return mchId;
+    }
+
+    public void setMchId(String mchId) {
+        this.mchId = mchId;
+    }
+
+    public String getNonceStr() {
+        return nonceStr;
+    }
+
+    public void setNonceStr(String nonceStr) {
+        this.nonceStr = nonceStr;
+    }
+
+    public String getSign() {
+        return sign;
+    }
+
+    public void setSign(String sign) {
+        this.sign = sign;
+    }
+
+    public String getTransactionId() {
+        return transactionId;
+    }
+
+    public void setTransactionId(String transactionId) {
+        this.transactionId = transactionId;
+    }
+
+    public void setOutTradeNo(String outTradeNo) {
+        this.outTradeNo = outTradeNo;
+    }
+
+    public String getOutRefundNo() {
+        return outRefundNo;
+    }
+
+    public void setOutRefundNo(String outRefundNo) {
+        this.outRefundNo = outRefundNo;
+    }
+
+    public String getRefundId() {
+        return refundId;
+    }
+
+    public void setRefundId(String refundId) {
+        this.refundId = refundId;
+    }
+
+    public void setRefundFee(BigDecimal refundFee) {
+        this.refundFee = refundFee;
+    }
+
+    public BigDecimal getSettlementRefundFee() {
+        return settlementRefundFee;
+    }
+
+    public void setSettlementRefundFee(BigDecimal settlementRefundFee) {
+        this.settlementRefundFee = settlementRefundFee;
+    }
+
+    public BigDecimal getTotalFee() {
+        return totalFee;
+    }
+
+    public void setTotalFee(BigDecimal totalFee) {
+        this.totalFee = totalFee;
+    }
+
+    public BigDecimal getSettlementTotalFee() {
+        return settlementTotalFee;
+    }
+
+    public void setSettlementTotalFee(BigDecimal settlementTotalFee) {
+        this.settlementTotalFee = settlementTotalFee;
+    }
+
+    public CurType getFeeType() {
+        return feeType;
+    }
+
+    public void setFeeType(CurType feeType) {
+        this.feeType = feeType;
+    }
+
+    public BigDecimal getCashFee() {
+        return cashFee;
+    }
+
+    public void setCashFee(BigDecimal cashFee) {
+        this.cashFee = cashFee;
+    }
+
+    public CurType getCashFeeType() {
+        return cashFeeType;
+    }
+
+    public void setCashFeeType(CurType cashFeeType) {
+        this.cashFeeType = cashFeeType;
+    }
+
+    public BigDecimal getCashRefundFee() {
+        return cashRefundFee;
+    }
+
+    public void setCashRefundFee(BigDecimal cashRefundFee) {
+        this.cashRefundFee = cashRefundFee;
+    }
+
+    public String getCouponType0() {
+        return couponType0;
+    }
+
+    public void setCouponType0(String couponType0) {
+        this.couponType0 = couponType0;
+    }
+
+    public BigDecimal getCouponRefundFee() {
+        return couponRefundFee;
+    }
+
+    public void setCouponRefundFee(BigDecimal couponRefundFee) {
+        this.couponRefundFee = couponRefundFee;
+    }
+
+    public BigDecimal getCouponRefundFee0() {
+        return couponRefundFee0;
+    }
+
+    public void setCouponRefundFee0(BigDecimal couponRefundFee0) {
+        this.couponRefundFee0 = couponRefundFee0;
+    }
+
+    public Integer getCouponRefundCount() {
+        return couponRefundCount;
+    }
+
+    public void setCouponRefundCount(Integer couponRefundCount) {
+        this.couponRefundCount = couponRefundCount;
+    }
+
+    public Integer getCouponRefundId0() {
+        return couponRefundId0;
+    }
+
+    public void setCouponRefundId0(Integer couponRefundId0) {
+        this.couponRefundId0 = couponRefundId0;
+    }
+
+
+    public static final WxRefundResult create(Map result){
+        WxRefundResult refundResult = new JSONObject(result).toJavaObject(WxRefundResult.class);
+        refundResult.setAttrs(result);
+        return refundResult;
+    }
+}
diff --git a/pay-java-yiji/src/main/java/com/egzosn/pay/yiji/api/YiJiPayService.java b/pay-java-yiji/src/main/java/com/egzosn/pay/yiji/api/YiJiPayService.java
index 232d126..ccc553a 100644
--- a/pay-java-yiji/src/main/java/com/egzosn/pay/yiji/api/YiJiPayService.java
+++ b/pay-java-yiji/src/main/java/com/egzosn/pay/yiji/api/YiJiPayService.java
@@ -11,6 +11,7 @@ import com.egzosn.pay.common.util.sign.SignUtils;
 import com.egzosn.pay.common.util.str.StringUtils;
 import com.egzosn.pay.yiji.bean.YiJiTransactionType;
 
+import java.math.BigDecimal;
 import java.util.Collections;
 import java.util.Date;
 import java.util.Map;
@@ -300,7 +301,7 @@ public class YiJiPayService extends BasePayService {
      * @return 返回支付方申请退款后的结果
      */
     @Override
-    public Map refund(RefundOrder refundOrder) {
+    public RefundResult refund(RefundOrder refundOrder) {
         Map orderInfo = getPublicParameters(YiJiTransactionType.tradeRefund);
         orderInfo.put("orderNo", refundOrder.getOutTradeNo());
         orderInfo.put("outOrderNo", refundOrder.getOutTradeNo());
@@ -308,7 +309,52 @@ public class YiJiPayService extends BasePayService {
         orderInfo.put("refundTime", DateUtils.formatDay(refundOrder.getOrderDate()));
         orderInfo.put("refundReason", refundOrder.getDescription());
         setSign(orderInfo);
-        return getHttpRequestTemplate().postForObject(getReqUrl(YiJiTransactionType.tradeRefund), orderInfo, JSONObject.class);
+        return new BaseRefundResult(getHttpRequestTemplate().postForObject(getReqUrl(YiJiTransactionType.tradeRefund), orderInfo, JSONObject.class)) {
+            @Override
+            public String getCode() {
+                return null;
+            }
+
+            @Override
+            public String getMsg() {
+                return null;
+            }
+
+            @Override
+            public String getResultCode() {
+                return null;
+            }
+
+            @Override
+            public String getResultMsg() {
+                return null;
+            }
+
+            @Override
+            public BigDecimal getRefundFee() {
+                return null;
+            }
+
+            @Override
+            public CurType getRefundCurrency() {
+                return null;
+            }
+
+            @Override
+            public String getTradeNo() {
+                return null;
+            }
+
+            @Override
+            public String getOutTradeNo() {
+                return null;
+            }
+
+            @Override
+            public String getRefundNo() {
+                return null;
+            }
+        };
     }
 
     /**
diff --git a/pom.xml b/pom.xml
index 952c79d..4a5dee5 100644
--- a/pom.xml
+++ b/pom.xml
@@ -67,7 +67,7 @@
         2.13.3-SNAPSHOT
         4.5.4
         1.2.17
-        1.2.58
+        1.2.73
         3.3.1
         5.5.1
     
@@ -92,10 +92,11 @@
             
                 com.alibaba
                 fastjson
-                ${fastjson.version}
+                1.2.73
             
 
 
+
             
             
                 log4j
-- 
Gitee


From c5604f5d3a9319bcbd10dfe8bb05af8cf910ace8 Mon Sep 17 00:00:00 2001
From: egzosn 
Date: Mon, 12 Oct 2020 22:51:52 +0800
Subject: [PATCH 146/299] =?UTF-8?q?=E4=BB=A3=E7=A0=81=E6=95=B4=E7=90=86?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 .../pay/demo/controller/AliPayController.java | 29 ++++++++++++++++++-
 .../pay/paypal/api/PayPalConfigStorage.java   |  6 +++-
 .../pay/union/api/UnionPayConfigStorage.java  | 17 +++++++++--
 .../egzosn/pay/union/api/UnionPayService.java |  2 +-
 .../api/WxYouDianPayConfigStorage.java        | 12 +++++---
 5 files changed, 57 insertions(+), 9 deletions(-)

diff --git a/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/AliPayController.java b/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/AliPayController.java
index 2f528e7..b71161f 100644
--- a/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/AliPayController.java
+++ b/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/AliPayController.java
@@ -9,6 +9,7 @@ import com.egzosn.pay.ali.bean.AliTransactionType;
 import com.egzosn.pay.ali.bean.AliTransferOrder;
 import com.egzosn.pay.ali.bean.AliTransferType;
 import com.egzosn.pay.ali.bean.OrderSettle;
+import com.egzosn.pay.common.bean.CertStoreType;
 import com.egzosn.pay.common.bean.PayOrder;
 import com.egzosn.pay.common.bean.RefundOrder;
 import com.egzosn.pay.common.http.HttpConfigStorage;
@@ -47,13 +48,39 @@ public class AliPayController {
     private AliPayService service = null;
     @Resource
     private AutowireCapableBeanFactory spring;
+    /**
+     * 设置普通公钥的方式
+     * 普通公钥方式与证书公钥方式为两者取其一的方式
+     * @param aliPayConfigStorage 支付宝配置信息
+     *
+     */
+    private static void keyPublic(AliPayConfigStorage aliPayConfigStorage){
+        aliPayConfigStorage.setKeyPublic("MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDIgHnOn7LLILlKETd6BFRJ0GqgS2Y3mn1wMQmyh9zEyWlz5p1zrahRahbXAfCfSqshSNfqOmAQzSHRVjCqjsAw1jyqrXaPdKBmr90DIpIxmIyKXv4GGAkPyJ/6FTFY99uhpiq0qadD/uSzQsefWo0aTvP/65zi3eof7TcZ32oWpwIDAQAB");
+    }
+
+    /**
+     * 设置证书公钥信息
+     * 普通公钥方式与证书公钥方式为两者取其一的方式
+     * @param aliPayConfigStorage 支付宝配置信息
+     */
+    private static void certKeyPublic(AliPayConfigStorage aliPayConfigStorage){
+        //设置为证书方式
+        aliPayConfigStorage.setCertSign(true);
+        //设置证书存储方式,这里为路径
+        aliPayConfigStorage.setCertStoreType(CertStoreType.PATH);
+        aliPayConfigStorage.setMerchantCert("请填写您的应用公钥证书文件路径,例如:d:/appCertPublicKey_2019051064521003.crt");
+        aliPayConfigStorage.setAliPayCert("请填写您的支付宝公钥证书文件路径,例如:d:/alipayCertPublicKey_RSA2.crt");
+        aliPayConfigStorage.setAliPayRootCert("请填写您的支付宝根证书文件路径,例如:d:/alipayRootCert.crt");
+    }
 
     @PostConstruct
     public void init() {
         AliPayConfigStorage aliPayConfigStorage = new AliPayConfigStorage();
         aliPayConfigStorage.setPid("2088102169916436");
         aliPayConfigStorage.setAppid("2016080400165436");
-        aliPayConfigStorage.setKeyPublic("MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDIgHnOn7LLILlKETd6BFRJ0GqgS2Y3mn1wMQmyh9zEyWlz5p1zrahRahbXAfCfSqshSNfqOmAQzSHRVjCqjsAw1jyqrXaPdKBmr90DIpIxmIyKXv4GGAkPyJ/6FTFY99uhpiq0qadD/uSzQsefWo0aTvP/65zi3eof7TcZ32oWpwIDAQAB");
+        //普通公钥方式与证书公钥方式为两者取其一的方式
+        keyPublic(aliPayConfigStorage);
+//        certKeyPublic(aliPayConfigStorage);
         aliPayConfigStorage.setKeyPrivate("MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBAKroe/8h5vC4L6T+B2WdXiVwGsMvUKgb2XsKix6VY3m2wcf6tyzpNRDCNykbIwGtaeo7FshN+qZxdXHLiIam9goYncBit/8ojfLGy2gLxO/PXfzGxYGs0KsDZ+ryVPPmE34ZZ8jiJpR0ygzCFl8pN3QJPJRGTJn5+FTT9EF/9zyZAgMBAAECgYAktngcYC35u7cQXDk+jMVyiVhWYU2ULxdSpPspgLGzrZyG1saOcTIi/XVX8Spd6+B6nmLQeF/FbU3rOeuD8U2clzul2Z2YMbJ0FYay9oVZFfp5gTEFpFRTVfzqUaZQBIjJe/xHL9kQVqc5xHlE/LVA27/Kx3dbC35Y7B4EVBDYAQJBAOhsX8ZreWLKPhXiXHTyLmNKhOHJc+0tFH7Ktise/0rNspojU7o9prOatKpNylp9v6kux7migcMRdVUWWiVe+4ECQQC8PqsuEz7B0yqirQchRg1DbHjh64bw9Kj82EN1/NzOUd53tP9tg+SO97EzsibK1F7tOcuwqsa7n2aY48mQ+y0ZAkBndA2xcRcnvOOjtAz5VO8G7R12rse181HjGfG6AeMadbKg30aeaGCyIxN1loiSfNR5xsPJwibGIBg81mUrqzqBAkB+K6rkaPXJR9XtzvdWb/N3235yPkDlw7Z4MiOVM3RzvR/VMDV7m8lXoeDde2zQyeMOMYy6ztwA6WgE1bhGOnQRAkEAouUBv1sVdSBlsexX15qphOmAevzYrpufKgJIRLFWQxroXMS7FTesj+f+FmGrpPCxIde1dqJ8lqYLTyJmbzMPYw==");
         aliPayConfigStorage.setNotifyUrl("http://pay.egzosn.com/payBack.json");
         aliPayConfigStorage.setReturnUrl("http://pay.egzosn.com/payBack.html");
diff --git a/pay-java-paypal/src/main/java/com/egzosn/pay/paypal/api/PayPalConfigStorage.java b/pay-java-paypal/src/main/java/com/egzosn/pay/paypal/api/PayPalConfigStorage.java
index 8169fa7..ccffe2e 100644
--- a/pay-java-paypal/src/main/java/com/egzosn/pay/paypal/api/PayPalConfigStorage.java
+++ b/pay-java-paypal/src/main/java/com/egzosn/pay/paypal/api/PayPalConfigStorage.java
@@ -1,5 +1,7 @@
 package com.egzosn.pay.paypal.api;
 
+import java.util.concurrent.locks.ReentrantLock;
+
 import com.egzosn.pay.common.api.BasePayConfigStorage;
 
 /**
@@ -62,5 +64,7 @@ public class PayPalConfigStorage extends BasePayConfigStorage {
         return getNotifyUrl();
     }
 
-
+    public PayPalConfigStorage() {
+        setAccessTokenLock(new ReentrantLock());
+    }
 }
diff --git a/pay-java-union/src/main/java/com/egzosn/pay/union/api/UnionPayConfigStorage.java b/pay-java-union/src/main/java/com/egzosn/pay/union/api/UnionPayConfigStorage.java
index 24864d3..5497b07 100644
--- a/pay-java-union/src/main/java/com/egzosn/pay/union/api/UnionPayConfigStorage.java
+++ b/pay-java-union/src/main/java/com/egzosn/pay/union/api/UnionPayConfigStorage.java
@@ -41,6 +41,10 @@ public class UnionPayConfigStorage extends BasePayConfigStorage {
      * 应用私钥证书
      */
     private Object keyPrivateCert;
+    /**
+     * 应用私钥证书,rsa_private pkcs8格式 生成签名时使用
+     */
+    private String keyPrivateCertPwd;
 
     /**
      * 中级证书
@@ -55,6 +59,7 @@ public class UnionPayConfigStorage extends BasePayConfigStorage {
      * 证书存储类型
      */
     private CertStoreType certStoreType;
+
     /**
      * 设置私钥证书
      *
@@ -128,9 +133,17 @@ public class UnionPayConfigStorage extends BasePayConfigStorage {
         return certStoreType.getInputStream(acpRootCert);
     }
 
+    /**
+     * 获取私钥证书密码
+     * @return 私钥证书密码
+     */
+    public String getKeyPrivateCertPwd() {
+        return keyPrivateCertPwd;
+    }
 
-
-
+    public void setKeyPrivateCertPwd(String keyPrivateCertPwd) {
+        this.keyPrivateCertPwd = keyPrivateCertPwd;
+    }
     @Override
     public String getAppid() {
         return null;
diff --git a/pay-java-union/src/main/java/com/egzosn/pay/union/api/UnionPayService.java b/pay-java-union/src/main/java/com/egzosn/pay/union/api/UnionPayService.java
index 953c645..d1ef681 100644
--- a/pay-java-union/src/main/java/com/egzosn/pay/union/api/UnionPayService.java
+++ b/pay-java-union/src/main/java/com/egzosn/pay/union/api/UnionPayService.java
@@ -70,7 +70,7 @@ public class UnionPayService extends BasePayService {
      * @param payConfigStorage 支付配置
      */
     public UnionPayService(UnionPayConfigStorage payConfigStorage) {
-        super(payConfigStorage);
+        this(payConfigStorage, null);
     }
 
     public UnionPayService(UnionPayConfigStorage payConfigStorage, HttpConfigStorage configStorage) {
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 7548278..be789e9 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
@@ -1,14 +1,16 @@
 package com.egzosn.pay.wx.youdian.api;
 
+import java.util.concurrent.locks.ReentrantLock;
+
 import com.egzosn.pay.common.api.BasePayConfigStorage;
 
 /**
  * 支付客户端配置存储
  *
  * @author egan
- *         

- * email egzosn@gmail.com - * date 2017/01/12 22:58 + *

+ * email egzosn@gmail.com + * date 2017/01/12 22:58 */ public class WxYouDianPayConfigStorage extends BasePayConfigStorage { @@ -49,5 +51,7 @@ public class WxYouDianPayConfigStorage extends BasePayConfigStorage { return getAccessToken(); } - + public WxYouDianPayConfigStorage() { + setAccessTokenLock(new ReentrantLock()); + } } -- Gitee From 708a7b16b60ee5f421e2ad3a3852e5fb8e0f895e Mon Sep 17 00:00:00 2001 From: egzosn Date: Mon, 12 Oct 2020 22:52:22 +0800 Subject: [PATCH 147/299] =?UTF-8?q?=E6=94=AF=E4=BB=98=E5=AE=9D=E8=AF=81?= =?UTF-8?q?=E4=B9=A6=E5=85=AC=E9=92=A5=E5=AE=9E=E7=8E=B0=E6=96=B9=E5=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pay-java-ali/README.md | 30 +- .../pay/ali/api/AliPayConfigStorage.java | 112 +- .../com/egzosn/pay/ali/api/AliPayService.java | 181 +- .../com/egzosn/pay/ali/bean/AliPayConst.java | 59 + .../egzosn/pay/ali/bean/CertEnvironment.java | 93 + .../pay/ali/utils/AntCertificationUtil.java | 389 +++ pay-java-ali/src/test/java/PayTest.java | 30 +- .../pay/common/api/BasePayConfigStorage.java | 92 +- .../com/egzosn/pay/common/api/CertStore.java | 2 +- .../pay/common/api/PayConfigStorage.java | 35 +- .../com/egzosn/pay/common/bean/Attrs.java | 31 + .../egzosn/pay/common/bean/CertStoreType.java | 46 +- .../com/egzosn/pay/common/bean/Order.java | 17 +- .../egzosn/pay/common/bean/PayMessage.java | 3 + .../com/egzosn/pay/common/bean/PayOrder.java | 2 +- .../com/egzosn/pay/common/util/IOUtils.java | 2415 +++++++++++++++++ .../pay/common/util/sign/SecureUtil.java | 4 +- 17 files changed, 3371 insertions(+), 170 deletions(-) create mode 100644 pay-java-ali/src/main/java/com/egzosn/pay/ali/bean/AliPayConst.java create mode 100644 pay-java-ali/src/main/java/com/egzosn/pay/ali/bean/CertEnvironment.java create mode 100644 pay-java-ali/src/main/java/com/egzosn/pay/ali/utils/AntCertificationUtil.java create mode 100644 pay-java-common/src/main/java/com/egzosn/pay/common/bean/Attrs.java create mode 100644 pay-java-common/src/main/java/com/egzosn/pay/common/util/IOUtils.java diff --git a/pay-java-ali/README.md b/pay-java-ali/README.md index 824213f..aebefc7 100644 --- a/pay-java-ali/README.md +++ b/pay-java-ali/README.md @@ -3,10 +3,10 @@ ## 支付宝支付简单例子 #### 支付配置 - +##### 普通公钥 ```java - - + + AliPayConfigStorage aliPayConfigStorage = new AliPayConfigStorage(); aliPayConfigStorage.setPid("合作者id"); aliPayConfigStorage.setAppid("应用id"); @@ -21,6 +21,30 @@ aliPayConfigStorage.setTest(true); ``` +##### 证书公钥 +```java + + + AliPayConfigStorage aliPayConfigStorage = new AliPayConfigStorage(); + aliPayConfigStorage.setPid("合作者id"); + aliPayConfigStorage.setAppid("应用id"); + aliPayConfigStorage.setKeyPrivate("应用私钥"); + //设置为证书方式 + aliPayConfigStorage.setCertSign(true); + //设置证书存储方式,这里为路径 + aliPayConfigStorage.setCertStoreType(CertStoreType.PATH); + aliPayConfigStorage.setMerchantCert("请填写您的应用公钥证书文件路径,例如:d:/appCertPublicKey_2019051064521003.crt"); + aliPayConfigStorage.setAliPayCert("请填写您的支付宝公钥证书文件路径,例如:d:/alipayCertPublicKey_RSA2.crt"); + aliPayConfigStorage.setAliPayRootCert("请填写您的支付宝根证书文件路径,例如:d:/alipayRootCert.crt"); + aliPayConfigStorage.setNotifyUrl("异步回调地址"); + aliPayConfigStorage.setReturnUrl("同步回调地址"); + aliPayConfigStorage.setSignType("签名方式"); + aliPayConfigStorage.setSeller("收款账号"); + aliPayConfigStorage.setInputCharset("utf-8"); + //是否为测试账号,沙箱环境 + aliPayConfigStorage.setTest(true); + +``` #### 网络请求配置 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 3eef8c4..cc2028e 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 @@ -1,17 +1,31 @@ package com.egzosn.pay.ali.api; +import java.io.IOException; +import java.io.InputStream; + +import com.egzosn.pay.ali.bean.CertEnvironment; import com.egzosn.pay.common.api.BasePayConfigStorage; +import com.egzosn.pay.common.api.CertStore; +import com.egzosn.pay.common.bean.result.PayException; +import com.egzosn.pay.common.exception.PayErrorException; /** * 支付配置存储 * * @author egan - *

- * email egzosn@gmail.com - * date 2016-5-18 14:09:01 + *

+ * email egzosn@gmail.com + * date 2016-5-18 14:09:01 + * + * + * 以下证书签名相关触发前提是 {@link BasePayConfigStorage#isCertSign}等于true的情况。不然走的就是普通的方式 */ public class AliPayConfigStorage extends BasePayConfigStorage { + /** + * ISV代商户代用,指定appAuthToken + */ + private String appAuthToken; /** * 商户应用id */ @@ -27,6 +41,40 @@ public class AliPayConfigStorage extends BasePayConfigStorage { private String seller; + + /** + * 应用公钥证书 + */ + private Object merchantCert; + + /** + * 支付宝公钥证书 + */ + private Object aliPayCert; + /** + * 支付宝CA证书,根证书 + */ + private Object aliPayRootCert; + + /** + * 证书存储类型 + */ + private CertStore certStoreType; + + /** + * 证书信息 + */ + private CertEnvironment certEnvironment; + + + public String getAppAuthToken() { + return appAuthToken; + } + + public void setAppAuthToken(String appAuthToken) { + this.appAuthToken = appAuthToken; + } + public void setAppid(String appid) { this.appid = appid; } @@ -55,5 +103,63 @@ public class AliPayConfigStorage extends BasePayConfigStorage { this.seller = seller; } + public Object getMerchantCert() { + return merchantCert; + } + + public void setMerchantCert(Object merchantCert) { + this.merchantCert = merchantCert; + } + + public Object getAliPayCert() { + return aliPayCert; + } + + public void setAliPayCert(Object aliPayCert) { + this.aliPayCert = aliPayCert; + } + + public Object getAliPayRootCert() { + return aliPayRootCert; + } + + public void setAliPayRootCert(Object aliPayRootCert) { + this.aliPayRootCert = aliPayRootCert; + } + + public CertStore getCertStoreType() { + return certStoreType; + } + + public void setCertStoreType(CertStore certStoreType) { + this.certStoreType = certStoreType; + } + + public CertEnvironment getCertEnvironment() { + return certEnvironment; + } + + public void setCertEnvironment(CertEnvironment certEnvironment) { + this.certEnvironment = certEnvironment; + } + + /** + * 初始化证书信息 + */ + public void loadCertEnvironment() { + if (isCertSign() && null != this.certEnvironment){ + return; + } + try (InputStream merchantCertStream = certStoreType.getInputStream(merchantCert); + InputStream aliPayCertStream = certStoreType.getInputStream(aliPayCert); + InputStream aliPayRootCertStream = certStoreType.getInputStream(aliPayRootCert); + ){ + this.certEnvironment = new CertEnvironment(merchantCertStream, aliPayCertStream, aliPayRootCertStream); + } + catch (IOException e) { + throw new PayErrorException(new PayException("读取证书异常", e.getMessage())); + } + } + } 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 4101dc3..a1a6515 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 @@ -1,14 +1,40 @@ package com.egzosn.pay.ali.api; +import java.util.Date; +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.TreeMap; + +import static com.egzosn.pay.ali.bean.AliPayConst.APP_AUTH_TOKEN; +import static com.egzosn.pay.ali.bean.AliPayConst.BIZ_CONTENT; +import static com.egzosn.pay.ali.bean.AliPayConst.CODE; +import static com.egzosn.pay.ali.bean.AliPayConst.HTTPS_REQ_URL; +import static com.egzosn.pay.ali.bean.AliPayConst.PASSBACK_PARAMS; +import static com.egzosn.pay.ali.bean.AliPayConst.PAYEE_INFO; +import static com.egzosn.pay.ali.bean.AliPayConst.PRODUCT_CODE; +import static com.egzosn.pay.ali.bean.AliPayConst.RETURN_URL; +import static com.egzosn.pay.ali.bean.AliPayConst.SIGN; +import static com.egzosn.pay.ali.bean.AliPayConst.SUCCESS_CODE; + import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONObject; +import com.egzosn.pay.ali.bean.AliPayConst; import com.egzosn.pay.ali.bean.AliPayMessage; import com.egzosn.pay.ali.bean.AliRefundResult; import com.egzosn.pay.ali.bean.AliTransactionType; import com.egzosn.pay.ali.bean.AliTransferType; +import com.egzosn.pay.ali.bean.CertEnvironment; import com.egzosn.pay.ali.bean.OrderSettle; import com.egzosn.pay.common.api.BasePayService; -import com.egzosn.pay.common.bean.*; +import com.egzosn.pay.common.bean.MethodType; +import com.egzosn.pay.common.bean.Order; +import com.egzosn.pay.common.bean.PayMessage; +import com.egzosn.pay.common.bean.PayOrder; +import com.egzosn.pay.common.bean.PayOutMessage; +import com.egzosn.pay.common.bean.RefundOrder; +import com.egzosn.pay.common.bean.TransactionType; +import com.egzosn.pay.common.bean.TransferOrder; +import com.egzosn.pay.common.bean.TransferType; import com.egzosn.pay.common.bean.result.PayException; import com.egzosn.pay.common.exception.PayErrorException; import com.egzosn.pay.common.http.HttpConfigStorage; @@ -18,57 +44,17 @@ import com.egzosn.pay.common.util.Util; import com.egzosn.pay.common.util.sign.SignUtils; import com.egzosn.pay.common.util.str.StringUtils; -import java.util.*; - /** * 支付宝支付服务 * * @author egan - *

- * email egzosn@gmail.com - * date 2017-2-22 20:09 + *

+ * email egzosn@gmail.com + * date 2017-2-22 20:09 */ public class AliPayService extends BasePayService { - - /** - * 正式测试环境 - */ - private static final String HTTPS_REQ_URL = "https://openapi.alipay.com/gateway.do"; - /** - * 沙箱测试环境账号 - */ - private static final String DEV_REQ_URL = "https://openapi.alipaydev.com/gateway.do"; - private static final String SIGN = "sign"; - private static final String SUCCESS_CODE = "10000"; - - private static final String CODE = "code"; - /** - * 附加参数 - */ - private static final String PASSBACK_PARAMS = "passback_params"; - /** - * 产品代码 - */ - private static final String PRODUCT_CODE = "product_code"; - /** - * 返回地址 - */ - private static final String RETURN_URL = "return_url"; - - /** - * 请求内容 - */ - private static final String BIZ_CONTENT = "biz_content"; - /** - * 应用授权概述 - */ - private static final String APP_AUTH_TOKEN = "app_auth_token"; - /** - * 收款方信息 - */ - private static final String PAYEE_INFO = "payee_info"; /** * 获取对应的请求地址 @@ -77,8 +63,9 @@ public class AliPayService extends BasePayService { */ @Override public String getReqUrl(TransactionType transactionType) { - return payConfigStorage.isTest() ? DEV_REQ_URL : HTTPS_REQ_URL; + return payConfigStorage.isTest() ? AliPayConst.DEV_REQ_URL : HTTPS_REQ_URL; } + /** * 获取对应的请求地址 * @@ -89,12 +76,25 @@ public class AliPayService extends BasePayService { } + /** + * 设置支付配置 + * + * @param payConfigStorage 支付配置 + */ + @Override + public AliPayService setPayConfigStorage(AliPayConfigStorage payConfigStorage) { + payConfigStorage.loadCertEnvironment(); + super.setPayConfigStorage(payConfigStorage); + return this; + } + public AliPayService(AliPayConfigStorage payConfigStorage, HttpConfigStorage configStorage) { super(payConfigStorage, configStorage); + } public AliPayService(AliPayConfigStorage payConfigStorage) { - super(payConfigStorage); + this(payConfigStorage, null); } @@ -131,18 +131,37 @@ public class AliPayService extends BasePayService { if (SIGN.equals(entry.getKey())) { continue; } - TreeMap response = new TreeMap((Map )entry.getValue()); + TreeMap response = new TreeMap((Map) entry.getValue()); LinkedHashMap linkedHashMap = new LinkedHashMap<>(); linkedHashMap.put(CODE, response.remove(CODE)); linkedHashMap.put("msg", response.remove("msg")); linkedHashMap.putAll(response); - return SignUtils.valueOf(payConfigStorage.getSignType()).verify(JSON.toJSONString(linkedHashMap), sign, payConfigStorage.getKeyPublic(), payConfigStorage.getInputCharset()); + return SignUtils.valueOf(payConfigStorage.getSignType()).verify(JSON.toJSONString(linkedHashMap), sign, getKeyPublic(params), payConfigStorage.getInputCharset()); } } - - return SignUtils.valueOf(payConfigStorage.getSignType()).verify(params, sign, payConfigStorage.getKeyPublic(), payConfigStorage.getInputCharset()); + return SignUtils.valueOf(payConfigStorage.getSignType()).verify(params, sign, getKeyPublic(params), payConfigStorage.getInputCharset()); } + /** + * 获取公钥信息 + * @param params 响应参数 + * @return 公钥信息 + */ + private String getKeyPublic(Map params){ + if (!payConfigStorage.isCertSign()){ + return payConfigStorage.getKeyPublic(); + } + return payConfigStorage.getCertEnvironment().getAliPayPublicKey(getAliPayCertSN(params)); + } + /** + * 从响应Map中提取支付宝公钥证书序列号 + * + * @param respMap 响应Map + * @return 支付宝公钥证书序列号 + */ + public String getAliPayCertSN(java.util.Map respMap){ + return (String) respMap.get(AliPayConst.ALIPAY_CERT_SN_FIELD); + } /** * 校验数据来源 @@ -200,6 +219,7 @@ public class AliPayService extends BasePayService { orderInfo.put("notify_url", payConfigStorage.getNotifyUrl()); orderInfo.put("format", "json"); + setAppAuthToken(orderInfo, order.getAttrs()); Map bizContent = new TreeMap<>(); @@ -240,9 +260,11 @@ public class AliPayService extends BasePayService { if (null != order.getExpirationTime()) { bizContent.put(order.getTransactionType() == AliTransactionType.SWEEPPAY ? "qr_code_timeout_express" : "timeout_express", DateUtils.minutesRemaining(order.getExpirationTime()) + "m"); } + + bizContent.putAll(order.getAttrs()); orderInfo.put(BIZ_CONTENT, JSON.toJSONString(bizContent)); - return preOrderHandler(orderInfo, order); + return preOrderHandler(orderInfo, order); } /** @@ -258,9 +280,22 @@ public class AliPayService extends BasePayService { orderInfo.put("charset", payConfigStorage.getInputCharset()); orderInfo.put("timestamp", DateUtils.format(new Date())); orderInfo.put("version", "1.0"); + loadCertSn(orderInfo); return orderInfo; } + /** + * 加载证书序列 + * @param orderInfo 订单信息 + */ + private void loadCertSn(Map orderInfo){ + if (payConfigStorage.isCertSign()){ + final CertEnvironment certEnvironment = payConfigStorage.getCertEnvironment(); + setParameters(orderInfo, "app_cert_sn", certEnvironment.getMerchantCertSN()); + setParameters(orderInfo, "alipay_root_cert_sn", certEnvironment.getRootCertSN()); + } + } + /** * 获取输出消息,用户返回给支付端 @@ -308,7 +343,7 @@ public class AliPayService extends BasePayService { String bizContent = (String) orderInfo.remove(BIZ_CONTENT); formHtml.append(getReqUrl()).append("?").append(UriVariables.getMapToParameters(orderInfo)) .append("\" method=\"").append(method.name().toLowerCase()).append("\">"); - formHtml.append(""); + formHtml.append(""); formHtml.append(""); formHtml.append(""); @@ -316,7 +351,6 @@ public class AliPayService extends BasePayService { } - /** * 获取输出二维码信息, * @@ -324,7 +358,7 @@ public class AliPayService extends BasePayService { * @return 返回二维码信息,,支付时需要的 */ @Override - public String getQrPay(PayOrder order){ + public String getQrPay(PayOrder order) { order.setTransactionType(AliTransactionType.SWEEPPAY); Map orderInfo = orderInfo(order); //预订单 @@ -345,9 +379,9 @@ public class AliPayService extends BasePayService { */ @Override public Map microPay(PayOrder order) { - if (null == order.getTransactionType()){ + if (null == order.getTransactionType()) { order.setTransactionType(AliTransactionType.BAR_CODE); - }else if (order.getTransactionType() != AliTransactionType.BAR_CODE && order.getTransactionType() != AliTransactionType.WAVE_CODE && order.getTransactionType() != AliTransactionType.SECURITY_CODE){ + } else if (order.getTransactionType() != AliTransactionType.BAR_CODE && order.getTransactionType() != AliTransactionType.WAVE_CODE && order.getTransactionType() != AliTransactionType.SECURITY_CODE) { throw new PayErrorException(new PayException("-1", "错误的交易类型:" + order.getTransactionType())); } @@ -364,10 +398,11 @@ public class AliPayService extends BasePayService { /** * 统一收单交易结算接口 + * * @param order 交易结算信息 * @return 结算结果 */ - public Map settle(OrderSettle order){ + public Map settle(OrderSettle order) { //获取公共参数 Map parameters = getPublicParameters(AliTransactionType.SETTLE); setAppAuthToken(parameters, order.getAttrs()); @@ -422,13 +457,24 @@ public class AliPayService extends BasePayService { /** * 设置支付宝授权Token + * * @param parameters 参数 - * @param attrs 订单属性 + * @param attrs 订单属性 * @return 参数 */ private void setAppAuthToken(Map parameters, Map attrs) { + setAppAuthToken(parameters); setParameters(parameters, APP_AUTH_TOKEN, (String) attrs.remove(APP_AUTH_TOKEN)); } + /** + * 设置支付宝授权Token + * + * @param parameters 参数 + * @return 参数 + */ + private void setAppAuthToken(Map parameters) { + setParameters(parameters, APP_AUTH_TOKEN, payConfigStorage.getAppAuthToken()); + } /** @@ -458,7 +504,6 @@ public class AliPayService extends BasePayService { } - /** * 查询退款 * @@ -507,7 +552,6 @@ public class AliPayService extends BasePayService { } - /** * @param tradeNoOrBillDate 支付平台订单号或者账单类型, 具体请 * 类型为{@link String }或者 {@link Date },类型须强制限制,类型不对应则抛出异常{@link PayErrorException} @@ -558,11 +602,11 @@ public class AliPayService extends BasePayService { bizContent.put("out_biz_no", order.getOutNo()); bizContent.put("trans_amount", order.getAmount()); transferType.setAttr(bizContent, order); - setParameters(bizContent, "order_title", order); - setParameters(bizContent, "original_order_id", order); + setParameters(bizContent, "order_title", order); + setParameters(bizContent, "original_order_id", order); setPayeeInfo(bizContent, order); bizContent.put("remark", order.getRemark()); - setParameters(bizContent, "business_params", order); + setParameters(bizContent, "business_params", order); //设置请求参数的集合 parameters.put(BIZ_CONTENT, JSON.toJSONString(bizContent)); @@ -571,24 +615,23 @@ public class AliPayService extends BasePayService { return getHttpRequestTemplate().postForObject(getReqUrl() + "?" + UriVariables.getMapToParameters(parameters), null, JSONObject.class); } - private Map setPayeeInfo(Map bizContent, Order order){ + private Map setPayeeInfo(Map bizContent, Order order) { final Object attr = order.getAttr(PAYEE_INFO); - if (attr instanceof String){ + if (attr instanceof String) { bizContent.put(PAYEE_INFO, attr); } - if (attr instanceof TreeMap){ + if (attr instanceof TreeMap) { bizContent.put(PAYEE_INFO, attr); } - if (attr instanceof Map){ - Map payeeInfo = new TreeMap((Map)attr); + if (attr instanceof Map) { + Map payeeInfo = new TreeMap((Map) attr); bizContent.put(PAYEE_INFO, payeeInfo); } return bizContent; } - /** * 转账查询 * diff --git a/pay-java-ali/src/main/java/com/egzosn/pay/ali/bean/AliPayConst.java b/pay-java-ali/src/main/java/com/egzosn/pay/ali/bean/AliPayConst.java new file mode 100644 index 0000000..801e548 --- /dev/null +++ b/pay-java-ali/src/main/java/com/egzosn/pay/ali/bean/AliPayConst.java @@ -0,0 +1,59 @@ +package com.egzosn.pay.ali.bean; + +/** + * 常量 + * @author Egan + *

+ * email egzosn@gmail.com
+ * date 2020/10/8
+ * 
+ */ +public final class AliPayConst { + private AliPayConst() { + } + + + /** + * 正式测试环境 + */ + public static final String HTTPS_REQ_URL = "https://openapi.alipay.com/gateway.do"; + /** + * 沙箱测试环境账号 + */ + public static final String DEV_REQ_URL = "https://openapi.alipaydev.com/gateway.do"; + + public static final String SIGN = "sign"; + + public static final String SUCCESS_CODE = "10000"; + + public static final String CODE = "code"; + /** + * 附加参数 + */ + public static final String PASSBACK_PARAMS = "passback_params"; + /** + * 产品代码 + */ + public static final String PRODUCT_CODE = "product_code"; + /** + * 返回地址 + */ + public static final String RETURN_URL = "return_url"; + + /** + * 请求内容 + */ + public static final String BIZ_CONTENT = "biz_content"; + /** + * 应用授权概述 + */ + public static final String APP_AUTH_TOKEN = "app_auth_token"; + /** + * 收款方信息 + */ + public static final String PAYEE_INFO = "payee_info"; + /** + * 收款方信息 + */ + public static final String ALIPAY_CERT_SN_FIELD = "alipay_cert_sn"; +} diff --git a/pay-java-ali/src/main/java/com/egzosn/pay/ali/bean/CertEnvironment.java b/pay-java-ali/src/main/java/com/egzosn/pay/ali/bean/CertEnvironment.java new file mode 100644 index 0000000..bc71470 --- /dev/null +++ b/pay-java-ali/src/main/java/com/egzosn/pay/ali/bean/CertEnvironment.java @@ -0,0 +1,93 @@ +/** + * Alipay.com Inc. Copyright (c) 2004-2020 All Rights Reserved. + */ +package com.egzosn.pay.ali.bean; + +import com.egzosn.pay.ali.utils.AntCertificationUtil; +import com.egzosn.pay.common.bean.result.PayException; +import com.egzosn.pay.common.exception.PayErrorException; +import com.egzosn.pay.common.util.str.StringUtils; + +import java.io.InputStream; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +/** + * 证书模式运行时环境 + * + * @author zhongyu + * @version $Id: CertEnvironment.java, v 0.1 2020年01月02日 5:21 PM zhongyu Exp $ + * + * @author egan update 2020/10/12 + * + */ +public class CertEnvironment { + /** + * 支付宝根证书内容 + */ + private String rootCertContent; + + /** + * 支付宝根证书序列号 + */ + private String rootCertSN; + + /** + * 商户应用公钥证书序列号 + */ + private String merchantCertSN; + /** + * 默认的支付宝公钥证书序列号 + */ + private String aliPayPublicKeySN; + + /** + * 缓存的不同支付宝公钥证书序列号对应的支付宝公钥 + */ + private Map cachedAliPayPublicKey = new ConcurrentHashMap(); + + /** + * 构造证书运行环境 + * + * @param merchantCert 商户公钥证书路径 + * @param aliPayCert 支付宝公钥证书路径 + * @param aliPayRootCert 支付宝根证书路径 + */ + public CertEnvironment(InputStream merchantCert, InputStream aliPayCert, InputStream aliPayRootCert) { + if (null == merchantCert || null == aliPayCert || null == aliPayRootCert) { + throw new PayErrorException(new PayException("", "证书参数merchantCert、aliPayCert或aliPayRootCert设置不完整。")); + } + + this.rootCertContent = AntCertificationUtil.readFromInputStream(aliPayRootCert); + this.rootCertSN = AntCertificationUtil.getRootCertSN(rootCertContent); + this.merchantCertSN = AntCertificationUtil.getCertSN(AntCertificationUtil.readFromInputStream((merchantCert))); + + String aliPayPublicCertContent = AntCertificationUtil.readFromInputStream(aliPayCert); + aliPayPublicKeySN = AntCertificationUtil.getCertSN(aliPayPublicCertContent); + cachedAliPayPublicKey.put(aliPayPublicKeySN, + AntCertificationUtil.getCertPublicKey(aliPayPublicCertContent)); + } + + public String getRootCertSN() { + return rootCertSN; + } + + public String getMerchantCertSN() { + return merchantCertSN; + } + + public String getAliPayPublicKey(String sn) { + //如果没有指定sn,则默认取缓存中的第一个值 + if (StringUtils.isEmpty(sn)) { + return cachedAliPayPublicKey.values().iterator().next(); + } + + if (cachedAliPayPublicKey.containsKey(sn)) { + return cachedAliPayPublicKey.get(sn); + } else { + //网关在支付宝公钥证书变更前,一定会确认通知到商户并在商户做出反馈后,才会更新该商户的支付宝公钥证书 + //TODO: 后续可以考虑加入自动升级支付宝公钥证书逻辑,注意并发更新冲突问题 + throw new PayErrorException(new PayException("", "支付宝公钥证书[" + sn + "]已过期,请重新下载最新支付宝公钥证书并替换原证书文件")); + } + } +} \ No newline at end of file diff --git a/pay-java-ali/src/main/java/com/egzosn/pay/ali/utils/AntCertificationUtil.java b/pay-java-ali/src/main/java/com/egzosn/pay/ali/utils/AntCertificationUtil.java new file mode 100644 index 0000000..b4b94d4 --- /dev/null +++ b/pay-java-ali/src/main/java/com/egzosn/pay/ali/utils/AntCertificationUtil.java @@ -0,0 +1,389 @@ +package com.egzosn.pay.ali.utils; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.math.BigInteger; +import java.nio.charset.StandardCharsets; +import java.security.GeneralSecurityException; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.security.Principal; +import java.security.PublicKey; +import java.security.cert.Certificate; +import java.security.cert.CertificateException; +import java.security.cert.CertificateExpiredException; +import java.security.cert.CertificateFactory; +import java.security.cert.CertificateNotYetValidException; +import java.security.cert.X509Certificate; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import com.egzosn.pay.common.bean.result.PayException; +import com.egzosn.pay.common.exception.PayErrorException; +import com.egzosn.pay.common.util.IOUtils; +import com.egzosn.pay.common.util.sign.encrypt.Base64; +import com.egzosn.pay.common.util.str.StringUtils; + +/** + * 证书文件可信校验 + * + * @author junying.wjy + * @version $Id: AntCertificationUtil.java, v 0.1 2019-07-29 下午04:46 junying.wjy Exp $ + * + * @author egan update 2020/10/12 + * + */ +public class AntCertificationUtil { + private static final Log LOGGER = LogFactory.getLog(AntCertificationUtil.class); + + /** + * 验证证书是否可信 + * + * @param certContent 需要验证的目标证书或者证书链 + * @param rootCertContent 可信根证书列表 + */ + public static boolean isTrusted(String certContent, String rootCertContent) { + X509Certificate[] certificates; + try { + certificates = readPemCertChain(certContent); + } catch (Exception e) { + LOGGER.error("读取证书失败", e); + throw new RuntimeException(e); + } + + List rootCerts = new ArrayList(); + try { + X509Certificate[] certs = readPemCertChain(rootCertContent); + rootCerts.addAll(Arrays.asList(certs)); + } catch (Exception e) { + LOGGER.error("读取根证书失败", e); + throw new RuntimeException(e); + } + + return verifyCertChain(certificates, rootCerts.toArray(new X509Certificate[rootCerts.size()])); + } + + /** + * 验证证书是否是信任证书库中证书签发的 + * + * @param cert 目标验证证书 + * @param rootCerts 可信根证书列表 + * @return 验证结果 + */ + private static boolean verifyCert(X509Certificate cert, X509Certificate[] rootCerts) { + try { + cert.checkValidity(); + } catch (CertificateExpiredException e) { + LOGGER.error("证书已经过期", e); + return false; + } catch (CertificateNotYetValidException e) { + LOGGER.error("证书未激活", e); + return false; + } + + Map subjectMap = new HashMap(); + + for (X509Certificate root : rootCerts) { + subjectMap.put(root.getSubjectDN(), root); + } + + Principal issuerDN = cert.getIssuerDN(); + X509Certificate issuer = subjectMap.get(issuerDN); + if (issuer == null) { + LOGGER.error("证书链验证失败"); + return false; + } + try { + PublicKey publicKey = issuer.getPublicKey(); + verifySignature(publicKey, cert); + } catch (PayErrorException e) { + LOGGER.error("证书链验证失败", e); + return false; + } + return true; + } + + /** + * 验证证书链是否是信任证书库中证书签发的 + * + * @param certs 目标验证证书列表 + * @param rootCerts 可信根证书列表 + * @return 验证结果 + */ + private static boolean verifyCertChain(X509Certificate[] certs, X509Certificate[] rootCerts) { + boolean sorted = sortByDn(certs); + if (!sorted) { + LOGGER.error("证书链验证失败:不是完整的证书链"); + return false; + } + + //先验证第一个证书是不是信任库中证书签发的 + X509Certificate prev = certs[0]; + boolean firstOK = verifyCert(prev, rootCerts); + if (!firstOK || certs.length == 1) { + return firstOK; + } + + //验证证书链 + for (int i = 1; i < certs.length; i++) { + X509Certificate cert = certs[i]; + if (!checkValidity(cert)){ + return false; + } + verifySignature(prev.getPublicKey(), cert); + prev = cert; + } + + return true; + } + + + /** + * 验证证书链是否是信任证书库中证书签发的 + * + * @param cert 目标验证证书 + * @return 验证结果 + */ + private static boolean checkValidity(X509Certificate cert) { + try { + cert.checkValidity(); + } catch (CertificateExpiredException e) { + LOGGER.error("证书已经过期"); + return false; + } catch (CertificateNotYetValidException e) { + LOGGER.error("证书未激活"); + return false; + } + return true; + } + + + + private static void verifySignature(PublicKey publicKey, X509Certificate cert){ + try { + cert.verify(publicKey); + } + catch (GeneralSecurityException e) { + throw new PayErrorException(new PayException("证书校验失败", e.getMessage())); + } + } + + /** + * 将证书链按照完整的签发顺序进行排序,排序后证书链为:[issuerA, subjectA]-[issuerA, subjectB]-[issuerB, subjectC]-[issuerC, subjectD]... + * + * @param certs 证书链 + * @return true:排序成功,false:证书链不完整 + */ + private static boolean sortByDn(X509Certificate[] certs) { + //主题和证书的映射 + Map subjectMap = new HashMap(); + //签发者和证书的映射 + Map issuerMap = new HashMap(); + //是否包含自签名证书 + boolean hasSelfSignedCert = false; + + for (X509Certificate cert : certs) { + if (isSelfSigned(cert)) { + if (hasSelfSignedCert) { + return false; + } + hasSelfSignedCert = true; + } + + Principal subjectDN = cert.getSubjectDN(); + Principal issuerDN = cert.getIssuerDN(); + + subjectMap.put(subjectDN, cert); + issuerMap.put(issuerDN, cert); + } + + List certChain = new ArrayList(); + + X509Certificate current = certs[0]; + addressingUp(subjectMap, certChain, current); + addressingDown(issuerMap, certChain, current); + + //说明证书链不完整 + if (certs.length != certChain.size()) { + return false; + } + + //将证书链复制到原先的数据 + for (int i = 0; i < certChain.size(); i++) { + certs[i] = certChain.get(i); + } + return true; + } + + /** + * 验证证书是否是自签发的 + * + * @param cert 目标证书 + * @return true;自签发,false;不是自签发 + */ + private static boolean isSelfSigned(X509Certificate cert) { + return cert.getSubjectDN().equals(cert.getIssuerDN()); + } + + /** + * 向上构造证书链 + * + * @param subjectMap 主题和证书的映射 + * @param certChain 证书链 + * @param current 当前需要插入证书链的证书,include + */ + private static void addressingUp(final Map subjectMap, List certChain, + final X509Certificate current) { + certChain.add(0, current); + if (isSelfSigned(current)) { + return; + } + Principal issuerDN = current.getIssuerDN(); + X509Certificate issuer = subjectMap.get(issuerDN); + if (issuer == null) { + return; + } + addressingUp(subjectMap, certChain, issuer); + } + + /** + * 向下构造证书链 + * + * @param issuerMap 签发者和证书的映射 + * @param certChain 证书链 + * @param current 当前需要插入证书链的证书,exclude + */ + private static void addressingDown(final Map issuerMap, List certChain, + final X509Certificate current) { + Principal subjectDN = current.getSubjectDN(); + X509Certificate subject = issuerMap.get(subjectDN); + if (subject == null) { + return; + } + if (isSelfSigned(subject)) { + return; + } + certChain.add(subject); + addressingDown(issuerMap, certChain, subject); + } + + private static X509Certificate[] readPemCertChain(String cert){ + ByteArrayInputStream inputStream = new ByteArrayInputStream(cert.getBytes()); + CertificateFactory factory = null; + try { + factory = CertificateFactory.getInstance("X.509"); + Collection certificates = factory.generateCertificates(inputStream); + return certificates.toArray(new X509Certificate[certificates.size()]); + } catch (CertificateException e) { + LOGGER.error("提取根证书失败", e); + } + return null; + + } + + /** + * 获取支付宝根证书序列号 + * + * @param rootCertContent 支付宝根证书内容 + * @return 支付宝根证书序列号 + */ + public static String getRootCertSN(String rootCertContent) { + String rootCertSN = null; + try { + X509Certificate[] x509Certificates = readPemCertChain(rootCertContent); + if (null == x509Certificates){ + return null; + } + MessageDigest md = MessageDigest.getInstance("MD5"); + for (X509Certificate c : x509Certificates) { + if (c.getSigAlgOID().startsWith("1.2.840.113549.1.1")) { + md.update((c.getIssuerX500Principal().getName() + c.getSerialNumber()).getBytes()); + String certSN = new BigInteger(1, md.digest()).toString(16); + //BigInteger会把0省略掉,需补全至32位 + certSN = fillMD5(certSN); + if (StringUtils.isEmpty(rootCertSN)) { + rootCertSN = certSN; + } else { + rootCertSN = rootCertSN + "_" + certSN; + } + } + + } + } catch (NoSuchAlgorithmException e) { + LOGGER.error("提取根证书失败", e); + } + return rootCertSN; + } + + /** + * 获取公钥证书序列号 + * + * @param certContent 公钥证书内容 + * @return 公钥证书序列号 + */ + public static String getCertSN(String certContent) { + try { + InputStream inputStream = new ByteArrayInputStream(certContent.getBytes()); + CertificateFactory factory = CertificateFactory.getInstance("X.509", "BC"); + X509Certificate cert = (X509Certificate) factory.generateCertificate(inputStream); + return md5((cert.getIssuerX500Principal().getName() + cert.getSerialNumber()).getBytes()); + } catch (GeneralSecurityException e) { + throw new PayErrorException(new PayException(" 获取公钥证书序列号异常", e.getMessage())); + } + } + + private static String md5(byte[] bytes) { + MessageDigest md = null; + try { + md = MessageDigest.getInstance("MD5"); + } + catch (NoSuchAlgorithmException e) { + throw new PayErrorException(new PayException("", e.getMessage())); + } + md.update(bytes); + String certSN = new BigInteger(1, md.digest()).toString(16); + //BigInteger会把0省略掉,需补全至32位 + certSN = fillMD5(certSN); + return certSN; + } + + private static String fillMD5(String md5) { + return md5.length() == 32 ? md5 : fillMD5("0" + md5); + } + + /** + * 提取公钥证书中的公钥 + * + * @param certContent 公钥证书内容 + * @return 公钥证书中的公钥 + */ + public static String getCertPublicKey(String certContent) { + try { + InputStream inputStream = new ByteArrayInputStream(certContent.getBytes()); + CertificateFactory factory = CertificateFactory.getInstance("X.509", "BC"); + X509Certificate cert = (X509Certificate) factory.generateCertificate(inputStream); + return Base64.encode(cert.getPublicKey().getEncoded()); + } catch (GeneralSecurityException e) { + throw new PayErrorException(new PayException(" 提取公钥证书中的公钥异常", e.getMessage())); + } + } + + + + + public static String readFromInputStream(InputStream cert) { + try { + return new String(IOUtils.toByteArray(cert), StandardCharsets.UTF_8); + } catch (IOException e) { + throw new PayErrorException(new PayException("读取证书异常", e.getMessage())); + } + } +} diff --git a/pay-java-ali/src/test/java/PayTest.java b/pay-java-ali/src/test/java/PayTest.java index b266376..244297e 100644 --- a/pay-java-ali/src/test/java/PayTest.java +++ b/pay-java-ali/src/test/java/PayTest.java @@ -2,6 +2,7 @@ import com.egzosn.pay.ali.api.AliPayConfigStorage; import com.egzosn.pay.ali.api.AliPayService; import com.egzosn.pay.ali.bean.AliTransactionType; import com.egzosn.pay.common.api.PayService; +import com.egzosn.pay.common.bean.CertStoreType; import com.egzosn.pay.common.bean.MethodType; import com.egzosn.pay.common.bean.PayOrder; @@ -20,12 +21,39 @@ import java.util.UUID; */ public class PayTest { + /** + * 设置普通公钥的方式 + * 普通公钥方式与证书公钥方式为两者取其一的方式 + * @param aliPayConfigStorage 支付宝配置信息 + * + */ + private static void keyPublic(AliPayConfigStorage aliPayConfigStorage){ + aliPayConfigStorage.setKeyPublic("支付宝公钥"); + } + + /** + * 设置证书公钥信息 + * 普通公钥方式与证书公钥方式为两者取其一的方式 + * @param aliPayConfigStorage 支付宝配置信息 + */ + private static void certKeyPublic(AliPayConfigStorage aliPayConfigStorage){ + //设置为证书方式 + aliPayConfigStorage.setCertSign(true); + //设置证书存储方式,这里为路径 + aliPayConfigStorage.setCertStoreType(CertStoreType.PATH); + aliPayConfigStorage.setMerchantCert("请填写您的应用公钥证书文件路径,例如:d:/appCertPublicKey_2019051064521003.crt"); + aliPayConfigStorage.setAliPayCert("请填写您的支付宝公钥证书文件路径,例如:d:/alipayCertPublicKey_RSA2.crt"); + aliPayConfigStorage.setAliPayRootCert("请填写您的支付宝根证书文件路径,例如:d:/alipayRootCert.crt"); + } + public static void main(String[] args) { AliPayConfigStorage aliPayConfigStorage = new AliPayConfigStorage(); aliPayConfigStorage.setPid("合作者id"); aliPayConfigStorage.setAppid("应用id"); - aliPayConfigStorage.setKeyPublic("支付宝公钥"); + //普通公钥方式与证书公钥方式为两者取其一的方式 + keyPublic(aliPayConfigStorage); +// certKeyPublic(aliPayConfigStorage); aliPayConfigStorage.setKeyPrivate("应用私钥"); aliPayConfigStorage.setNotifyUrl("异步回调地址"); aliPayConfigStorage.setReturnUrl("同步回调地址"); diff --git a/pay-java-common/src/main/java/com/egzosn/pay/common/api/BasePayConfigStorage.java b/pay-java-common/src/main/java/com/egzosn/pay/common/api/BasePayConfigStorage.java index 3964139..24b7af8 100644 --- a/pay-java-common/src/main/java/com/egzosn/pay/common/api/BasePayConfigStorage.java +++ b/pay-java-common/src/main/java/com/egzosn/pay/common/api/BasePayConfigStorage.java @@ -1,11 +1,10 @@ package com.egzosn.pay.common.api; -import com.egzosn.pay.common.bean.CertStoreType; -import com.egzosn.pay.common.bean.MsgType; -import com.egzosn.pay.common.util.sign.CertDescriptor; - +import java.util.HashMap; +import java.util.Map; import java.util.concurrent.locks.Lock; -import java.util.concurrent.locks.ReentrantLock; + +import com.egzosn.pay.common.bean.MsgType; /** * 支付基础配置存储 @@ -24,10 +23,7 @@ public abstract class BasePayConfigStorage implements PayConfigStorage { * 应用私钥,rsa_private pkcs8格式 生成签名时使用 */ private String keyPrivate; - /** - * 应用私钥证书,rsa_private pkcs8格式 生成签名时使用 - */ - private String keyPrivateCertPwd; + /** * 支付平台公钥(签名校验使用) */ @@ -58,21 +54,22 @@ public abstract class BasePayConfigStorage implements PayConfigStorage { /** * 消息来源类型 */ + @Deprecated private MsgType msgType; /** * 访问令牌 每次请求其他方法都要传入的值 */ - private String accessToken; + private volatile String accessToken; /** * access token 到期时间时间戳 */ - private long expiresTime; + private volatile long expiresTime; /** * 授权码锁 */ - private Lock accessTokenLock = new ReentrantLock(); + private Lock accessTokenLock; /** * 是否为沙箱环境,默认为正式环境 */ @@ -81,8 +78,12 @@ public abstract class BasePayConfigStorage implements PayConfigStorage { /** * 是否为证书签名 */ - private boolean isCertSign = false; + private boolean certSign = false; + /** + * 配置附加信息,可用于预设未提供的参数,这里会覆盖以上所有的配置信息, + */ + private volatile Map attr; @Override public Object getAttach() { @@ -102,14 +103,6 @@ public abstract class BasePayConfigStorage implements PayConfigStorage { this.keyPrivate = keyPrivate; } - @Override - public String getKeyPrivateCertPwd() { - return keyPrivateCertPwd; - } - - public void setKeyPrivateCertPwd(String keyPrivateCertPwd) { - this.keyPrivateCertPwd = keyPrivateCertPwd; - } @Override public String getKeyPublic() { @@ -173,23 +166,31 @@ public abstract class BasePayConfigStorage implements PayConfigStorage { public void setMsgType(MsgType msgType) { this.msgType = msgType; } - - @Override + /** + * 获取访问令牌 + * @return 访问令牌 + */ public String getAccessToken() { return this.accessToken; } - - @Override + /** + * 获取access token锁 + * @return access token锁 + */ public Lock getAccessTokenLock() { return this.accessTokenLock; } - - @Override + /** + * 强制将access token过期掉 + * @return 过期时间 + */ public long getExpiresTime() { return expiresTime; } - - @Override + /** + * 访问令牌是否过期 + * @return true过期 + */ public boolean isAccessTokenExpired() { return System.currentTimeMillis() > this.expiresTime; } @@ -197,8 +198,7 @@ public abstract class BasePayConfigStorage implements PayConfigStorage { @Override public synchronized void updateAccessToken(String accessToken, int expiresInSeconds) { - this.accessToken = accessToken; - this.expiresTime = System.currentTimeMillis() + (expiresInSeconds - 600) * 1000L; + updateAccessToken(accessToken, System.currentTimeMillis() + (expiresInSeconds - 600) * 1000L); } @Override @@ -207,7 +207,10 @@ public abstract class BasePayConfigStorage implements PayConfigStorage { this.expiresTime = expiresTime; } - @Override + + /** + * 强制将access token过期掉 + */ public void expireAccessToken() { this.expiresTime = 0; } @@ -239,12 +242,33 @@ public abstract class BasePayConfigStorage implements PayConfigStorage { } public boolean isCertSign() { - return isCertSign; + return certSign; } public void setCertSign(boolean certSign) { - isCertSign = certSign; + this.certSign = certSign; + } + + @Override + public Map getAttrs() { + if (null == attr){ + attr = new HashMap<>(); + } + return attr; + } + + @Override + public Object getAttr(String key) { + return getAttrs().get(key); } + /** + * 添加配置信息 + * @param key key + * @param value 值 + */ + public void addAttr(String key, Object value) { + getAttrs().put(key, value); + } } diff --git a/pay-java-common/src/main/java/com/egzosn/pay/common/api/CertStore.java b/pay-java-common/src/main/java/com/egzosn/pay/common/api/CertStore.java index cb119a4..bbbb082 100644 --- a/pay-java-common/src/main/java/com/egzosn/pay/common/api/CertStore.java +++ b/pay-java-common/src/main/java/com/egzosn/pay/common/api/CertStore.java @@ -18,5 +18,5 @@ public interface CertStore { * @return 输入流 * @throws IOException 找不到文件异常 */ - public abstract InputStream getInputStream(Object cert) throws IOException; + InputStream getInputStream(Object cert) throws IOException; } diff --git a/pay-java-common/src/main/java/com/egzosn/pay/common/api/PayConfigStorage.java b/pay-java-common/src/main/java/com/egzosn/pay/common/api/PayConfigStorage.java index 8abdb23..df85e6c 100644 --- a/pay-java-common/src/main/java/com/egzosn/pay/common/api/PayConfigStorage.java +++ b/pay-java-common/src/main/java/com/egzosn/pay/common/api/PayConfigStorage.java @@ -1,5 +1,6 @@ package com.egzosn.pay.common.api; +import com.egzosn.pay.common.bean.Attrs; import com.egzosn.pay.common.bean.MsgType; import com.egzosn.pay.common.util.sign.CertDescriptor; @@ -13,7 +14,7 @@ import java.util.concurrent.locks.Lock; * date 2016-5-18 14:09:01 * */ - public interface PayConfigStorage { + public interface PayConfigStorage extends Attrs { /** * 附加支付配置 @@ -21,11 +22,6 @@ import java.util.concurrent.locks.Lock; */ Object getAttach(); - /** - * 获取私钥证书密码 - * @return 私钥证书密码 - */ - String getKeyPrivateCertPwd(); /** * 应用id * @return 应用id @@ -98,35 +94,11 @@ import java.util.concurrent.locks.Lock; * @see MsgType * @return "text" 或者 "xml",json */ + @Deprecated MsgType getMsgType(); - /** - * 获取访问令牌 - * @return 访问令牌 - */ - String getAccessToken(); - - /** - * 访问令牌是否过期 - * @return true过期 - */ - boolean isAccessTokenExpired(); - /** - * 获取access token锁 - * @return access token锁 - */ - Lock getAccessTokenLock(); - /** - * 强制将access token过期掉 - */ - void expireAccessToken(); - /** - * 强制将access token过期掉 - * @return 过期时间 - */ - long getExpiresTime(); /** * 应该是线程安全的 @@ -150,5 +122,4 @@ import java.util.concurrent.locks.Lock; - } diff --git a/pay-java-common/src/main/java/com/egzosn/pay/common/bean/Attrs.java b/pay-java-common/src/main/java/com/egzosn/pay/common/bean/Attrs.java new file mode 100644 index 0000000..cd7d883 --- /dev/null +++ b/pay-java-common/src/main/java/com/egzosn/pay/common/bean/Attrs.java @@ -0,0 +1,31 @@ +package com.egzosn.pay.common.bean; + +import java.io.Serializable; +import java.util.Map; + +/** + * 属性信息 + * + * @author Egan + *
+ * email egzosn@gmail.com
+ * date 2020/10/8
+ * 
+ */ +public interface Attrs extends Serializable { + + /** + * 获取属性 这里可用做覆盖已设置的信息属性,订单信息在签名前进行覆盖。 + * + * @return 属性 + */ + Map getAttrs(); + + /** + * 获取属性 这里可用做覆盖已设置的订单信息属性,订单信息在签名前进行覆盖。 + * + * @param key 属性名 + * @return 属性 + */ + Object getAttr(String key); +} diff --git a/pay-java-common/src/main/java/com/egzosn/pay/common/bean/CertStoreType.java b/pay-java-common/src/main/java/com/egzosn/pay/common/bean/CertStoreType.java index d7b6895..20488f9 100644 --- a/pay-java-common/src/main/java/com/egzosn/pay/common/bean/CertStoreType.java +++ b/pay-java-common/src/main/java/com/egzosn/pay/common/bean/CertStoreType.java @@ -1,6 +1,8 @@ package com.egzosn.pay.common.bean; import com.egzosn.pay.common.api.CertStore; +import com.egzosn.pay.common.bean.result.PayException; +import com.egzosn.pay.common.exception.PayErrorException; import com.egzosn.pay.common.http.HttpRequestTemplate; import java.io.*; @@ -15,7 +17,23 @@ import java.io.*; public enum CertStoreType implements CertStore { /** - * 路径,建议绝对路径 + * 无存储类型,表示无需要转换为输入流 + */ + NONE{ + /** + * 证书信息转化为对应的输入流 + * + * @param cert 证书信息 + * @return 输入流 + * @throws IOException 找不到文件异常 + */ + @Override + public InputStream getInputStream(Object cert) throws IOException { + return null; + } + }, + /** + * 文件路径,建议绝对路径 */ PATH { /** @@ -30,6 +48,22 @@ public enum CertStoreType implements CertStore { return new FileInputStream(new File((String) cert)); } }, + /** + * class路径 + */ + CLASS_PATH { + /** + * 证书信息转化为对应的输入流 + * + * @param cert 证书信息 + * @return 输入流 + * @throws IOException 找不到文件异常 + */ + @Override + public InputStream getInputStream(Object cert) throws IOException { + return Thread.currentThread().getContextClassLoader().getResourceAsStream((String) cert); + } + }, /** * 文件流转化成字符串存储至文件或者数据库中 */ @@ -98,14 +132,10 @@ public enum CertStoreType implements CertStore { Class clazz = Class.forName((String) beanClazz); CertStore certStore = (CertStore)clazz.newInstance(); return certStore.getInputStream(beanClazz); - } catch (ClassNotFoundException e) { - e.printStackTrace(); - } catch (IllegalAccessException e) { - e.printStackTrace(); - } catch (InstantiationException e) { - e.printStackTrace(); + } catch (ReflectiveOperationException e) { + throw new PayErrorException(new PayException("证书获取异常", e.getMessage())); } - return null; + } }; diff --git a/pay-java-common/src/main/java/com/egzosn/pay/common/bean/Order.java b/pay-java-common/src/main/java/com/egzosn/pay/common/bean/Order.java index 9ba9820..62a2612 100644 --- a/pay-java-common/src/main/java/com/egzosn/pay/common/bean/Order.java +++ b/pay-java-common/src/main/java/com/egzosn/pay/common/bean/Order.java @@ -12,22 +12,7 @@ import java.util.Map; * date 2020/01/05 13:34 * */ -public interface Order extends Serializable { - - /** - * 获取订单属性 这里可用做覆盖已设置的订单信息属性,订单信息在签名前进行覆盖。 - * - * @return 属性 - */ - Map getAttrs(); - - /** - * 获取订单属性 这里可用做覆盖已设置的订单信息属性,订单信息在签名前进行覆盖。 - * - * @param key 属性名 - * @return 属性 - */ - Object getAttr(String key); +public interface Order extends Attrs { /** diff --git a/pay-java-common/src/main/java/com/egzosn/pay/common/bean/PayMessage.java b/pay-java-common/src/main/java/com/egzosn/pay/common/bean/PayMessage.java index 1836758..cf09fc1 100644 --- a/pay-java-common/src/main/java/com/egzosn/pay/common/bean/PayMessage.java +++ b/pay-java-common/src/main/java/com/egzosn/pay/common/bean/PayMessage.java @@ -18,6 +18,7 @@ import java.util.Map; */ public class PayMessage implements Serializable { private Map payMessage = null; + @Deprecated private String msgType; private String payType; private String transactionType; @@ -50,10 +51,12 @@ public class PayMessage implements Serializable { this.payMessage = payMessage; } + @Deprecated public String getMsgType() { return msgType; } + @Deprecated public void setMsgType(String msgType) { this.msgType = msgType; } 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 35987ab..92995ab 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 @@ -90,7 +90,7 @@ public class PayOrder implements Order { /** * 订单附加信息,可用于预设未提供的参数,这里会覆盖以上所有的订单信息, */ - private Map attr; + private volatile Map attr; public PayOrder() { diff --git a/pay-java-common/src/main/java/com/egzosn/pay/common/util/IOUtils.java b/pay-java-common/src/main/java/com/egzosn/pay/common/util/IOUtils.java new file mode 100644 index 0000000..25c6761 --- /dev/null +++ b/pay-java-common/src/main/java/com/egzosn/pay/common/util/IOUtils.java @@ -0,0 +1,2415 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.egzosn.pay.common.util; + +import java.io.BufferedInputStream; +import java.io.BufferedReader; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.CharArrayWriter; +import java.io.Closeable; +import java.io.EOFException; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.OutputStream; +import java.io.OutputStreamWriter; +import java.io.PrintWriter; +import java.io.Reader; +import java.io.StringWriter; +import java.io.UnsupportedEncodingException; +import java.io.Writer; +import java.net.HttpURLConnection; +import java.net.ServerSocket; +import java.net.Socket; +import java.net.URI; +import java.net.URL; +import java.net.URLConnection; +import java.nio.channels.Selector; +import java.nio.charset.Charset; +import java.nio.charset.UnsupportedCharsetException; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +import org.apache.commons.codec.Charsets; + +/** + * General IO stream manipulation utilities. + *

+ * This class provides static utility methods for input/output operations. + *

    + *
  • closeQuietly - these methods close a stream ignoring nulls and exceptions + *
  • toXxx/read - these methods read data from a stream + *
  • write - these methods write data to a stream + *
  • copy - these methods copy all the data from one stream to another + *
  • contentEquals - these methods compare the content of two streams + *
+ *

+ * The byte-to-char methods and char-to-byte methods involve a conversion step. + * Two methods are provided in each case, one that uses the platform default + * encoding and the other which allows you to specify an encoding. You are + * encouraged to always specify an encoding because relying on the platform + * default can lead to unexpected results, for example when moving from + * development to production. + *

+ * All the methods in this class that read a stream are buffered internally. + * This means that there is no cause to use a BufferedInputStream + * or BufferedReader. The default buffer size of 4K has been shown + * to be efficient in tests. + *

+ * Wherever possible, the methods in this class do not flush or close + * the stream. This is to avoid making non-portable assumptions about the + * streams' origin and further use. Thus the caller is still responsible for + * closing streams after use. + *

+ * Origin of code: Excalibur. + * + * @version $Id: IOUtils.java 1326636 2012-04-16 14:54:53Z ggregory $ + * + */ +public class IOUtils { + // NOTE: This class is focussed on InputStream, OutputStream, Reader and + // Writer. Each method should take at least one of these as a parameter, + // or return one of them. + + private static final int EOF = -1; + /** + * The system line separator string. + */ + public static final String LINE_SEPARATOR; + + static { + // avoid security issues + StringWriter buf = new StringWriter(4); + PrintWriter out = new PrintWriter(buf); + out.println(); + LINE_SEPARATOR = buf.toString(); + out.close(); + } + + /** + * The default buffer size ({@value}) to use for + * {@link #copyLarge(InputStream, OutputStream)} + * and + * {@link #copyLarge(Reader, Writer)} + */ + private static final int DEFAULT_BUFFER_SIZE = 1024 * 4; + + /** + * The default buffer size to use for the skip() methods. + */ + private static final int SKIP_BUFFER_SIZE = 2048; + + // Allocated in the relevant skip method if necessary. + /* + * N.B. no need to synchronize these because: + * - we don't care if the buffer is created multiple times (the data is ignored) + * - we always use the same size buffer, so if it it is recreated it will still be OK + * (if the buffer size were variable, we would need to synch. to ensure some other thread + * did not create a smaller one) + */ + private static char[] SKIP_CHAR_BUFFER; + private static byte[] SKIP_BYTE_BUFFER; + + /** + * Instances should NOT be constructed in standard programming. + */ + public IOUtils() { + super(); + } + + //----------------------------------------------------------------------- + + /** + * Closes a URLConnection. + * + * @param conn the connection to close. + * @since 2.4 + */ + public static void close(URLConnection conn) { + if (conn instanceof HttpURLConnection) { + ((HttpURLConnection) conn).disconnect(); + } + } + + /** + * Unconditionally close an Reader. + *

+ * Equivalent to {@link Reader#close()}, except any exceptions will be ignored. + * This is typically used in finally blocks. + *

+ * Example code: + *

+     *   char[] data = new char[1024];
+     *   Reader in = null;
+     *   try {
+     *       in = new FileReader("foo.txt");
+     *       in.read(data);
+     *       in.close(); //close errors are handled
+     *   } catch (Exception e) {
+     *       // error handling
+     *   } finally {
+     *       IOUtils.closeQuietly(in);
+     *   }
+     * 
+ * + * @param input the Reader to close, may be null or already closed + */ + public static void closeQuietly(Reader input) { + closeQuietly((Closeable) input); + } + + /** + * Unconditionally close a Writer. + *

+ * Equivalent to {@link Writer#close()}, except any exceptions will be ignored. + * This is typically used in finally blocks. + *

+ * Example code: + *

+     *   Writer out = null;
+     *   try {
+     *       out = new StringWriter();
+     *       out.write("Hello World");
+     *       out.close(); //close errors are handled
+     *   } catch (Exception e) {
+     *       // error handling
+     *   } finally {
+     *       IOUtils.closeQuietly(out);
+     *   }
+     * 
+ * + * @param output the Writer to close, may be null or already closed + */ + public static void closeQuietly(Writer output) { + closeQuietly((Closeable) output); + } + + /** + * Unconditionally close an InputStream. + *

+ * Equivalent to {@link InputStream#close()}, except any exceptions will be ignored. + * This is typically used in finally blocks. + *

+ * Example code: + *

+     *   byte[] data = new byte[1024];
+     *   InputStream in = null;
+     *   try {
+     *       in = new FileInputStream("foo.txt");
+     *       in.read(data);
+     *       in.close(); //close errors are handled
+     *   } catch (Exception e) {
+     *       // error handling
+     *   } finally {
+     *       IOUtils.closeQuietly(in);
+     *   }
+     * 
+ * + * @param input the InputStream to close, may be null or already closed + */ + public static void closeQuietly(InputStream input) { + closeQuietly((Closeable) input); + } + + /** + * Unconditionally close an OutputStream. + *

+ * Equivalent to {@link OutputStream#close()}, except any exceptions will be ignored. + * This is typically used in finally blocks. + *

+ * Example code: + *

+     * byte[] data = "Hello, World".getBytes();
+     *
+     * OutputStream out = null;
+     * try {
+     *     out = new FileOutputStream("foo.txt");
+     *     out.write(data);
+     *     out.close(); //close errors are handled
+     * } catch (IOException e) {
+     *     // error handling
+     * } finally {
+     *     IOUtils.closeQuietly(out);
+     * }
+     * 
+ * + * @param output the OutputStream to close, may be null or already closed + */ + public static void closeQuietly(OutputStream output) { + closeQuietly((Closeable) output); + } + + /** + * Unconditionally close a Closeable. + *

+ * Equivalent to {@link Closeable#close()}, except any exceptions will be ignored. + * This is typically used in finally blocks. + *

+ * Example code: + *

+     *   Closeable closeable = null;
+     *   try {
+     *       closeable = new FileReader("foo.txt");
+     *       // process closeable
+     *       closeable.close();
+     *   } catch (Exception e) {
+     *       // error handling
+     *   } finally {
+     *       IOUtils.closeQuietly(closeable);
+     *   }
+     * 
+ * + * @param closeable the object to close, may be null or already closed + * @since 2.0 + */ + public static void closeQuietly(Closeable closeable) { + try { + if (closeable != null) { + closeable.close(); + } + } + catch (IOException ioe) { + // ignore + } + } + + /** + * Unconditionally close a Socket. + *

+ * Equivalent to {@link Socket#close()}, except any exceptions will be ignored. + * This is typically used in finally blocks. + *

+ * Example code: + *

+     *   Socket socket = null;
+     *   try {
+     *       socket = new Socket("http://www.foo.com/", 80);
+     *       // process socket
+     *       socket.close();
+     *   } catch (Exception e) {
+     *       // error handling
+     *   } finally {
+     *       IOUtils.closeQuietly(socket);
+     *   }
+     * 
+ * + * @param sock the Socket to close, may be null or already closed + * @since 2.0 + */ + public static void closeQuietly(Socket sock) { + if (sock != null) { + try { + sock.close(); + } + catch (IOException ioe) { + // ignored + } + } + } + + /** + * Unconditionally close a Selector. + *

+ * Equivalent to {@link Selector#close()}, except any exceptions will be ignored. + * This is typically used in finally blocks. + *

+ * Example code: + *

+     *   Selector selector = null;
+     *   try {
+     *       selector = Selector.open();
+     *       // process socket
+     *
+     *   } catch (Exception e) {
+     *       // error handling
+     *   } finally {
+     *       IOUtils.closeQuietly(selector);
+     *   }
+     * 
+ * + * @param selector the Selector to close, may be null or already closed + * @since 2.2 + */ + public static void closeQuietly(Selector selector) { + if (selector != null) { + try { + selector.close(); + } + catch (IOException ioe) { + // ignored + } + } + } + + /** + * Unconditionally close a ServerSocket. + *

+ * Equivalent to {@link ServerSocket#close()}, except any exceptions will be ignored. + * This is typically used in finally blocks. + *

+ * Example code: + *

+     *   ServerSocket socket = null;
+     *   try {
+     *       socket = new ServerSocket();
+     *       // process socket
+     *       socket.close();
+     *   } catch (Exception e) {
+     *       // error handling
+     *   } finally {
+     *       IOUtils.closeQuietly(socket);
+     *   }
+     * 
+ * + * @param sock the ServerSocket to close, may be null or already closed + * @since 2.2 + */ + public static void closeQuietly(ServerSocket sock) { + if (sock != null) { + try { + sock.close(); + } + catch (IOException ioe) { + // ignored + } + } + } + + /** + * Fetches entire contents of an InputStream and represent + * same data as result InputStream. + *

+ * This method is useful where, + *

    + *
  • Source InputStream is slow.
  • + *
  • It has network resources associated, so we cannot keep it open for + * long time.
  • + *
  • It has network timeout associated.
  • + *
+ * It can be used in favor of {@link #toByteArray(InputStream)}, since it + * avoids unnecessary allocation and copy of byte[].
+ * This method buffers the input internally, so there is no need to use a + * BufferedInputStream. + * + * @param input Stream to be fully buffered. + * @return A fully buffered stream. + * @throws IOException if an I/O error occurs + * @since 2.0 + */ + public static InputStream toBufferedInputStream(InputStream input) { + return new BufferedInputStream(input); + } + + /** + * Returns the given reader if it is a {@link BufferedReader}, otherwise creates a toBufferedReader for the given + * reader. + * + * @param reader the reader to wrap or return + * @return the given reader or a new {@link BufferedReader} for the given reader + * @since 2.2 + */ + public static BufferedReader toBufferedReader(Reader reader) { + return reader instanceof BufferedReader ? (BufferedReader) reader : new BufferedReader(reader); + } + + // read toByteArray + //----------------------------------------------------------------------- + + /** + * Get the contents of an InputStream as a byte[]. + *

+ * This method buffers the input internally, so there is no need to use a + * BufferedInputStream. + * + * @param input the InputStream to read from + * @return the requested byte array + * @throws NullPointerException if the input is null + * @throws IOException if an I/O error occurs + */ + public static byte[] toByteArray(InputStream input) throws IOException { + ByteArrayOutputStream output = new ByteArrayOutputStream(); + copy(input, output); + return output.toByteArray(); + } + + /** + * Get contents of an InputStream as a byte[]. + * Use this method instead of toByteArray(InputStream) + * when InputStream size is known. + * NOTE: the method checks that the length can safely be cast to an int without truncation + * before using {@link IOUtils#toByteArray(java.io.InputStream, int)} to read into the byte array. + * (Arrays can have no more than Integer.MAX_VALUE entries anyway) + * + * @param input the InputStream to read from + * @param size the size of InputStream + * @return the requested byte array + * @throws IOException if an I/O error occurs or InputStream size differ from parameter size + * @throws IllegalArgumentException if size is less than zero or size is greater than Integer.MAX_VALUE + * @see IOUtils#toByteArray(java.io.InputStream, int) + * @since 2.1 + */ + public static byte[] toByteArray(InputStream input, long size) throws IOException { + + if (size > Integer.MAX_VALUE) { + throw new IllegalArgumentException("Size cannot be greater than Integer max value: " + size); + } + + return toByteArray(input, (int) size); + } + + /** + * Get the contents of an InputStream as a byte[]. + * Use this method instead of toByteArray(InputStream) + * when InputStream size is known + * + * @param input the InputStream to read from + * @param size the size of InputStream + * @return the requested byte array + * @throws IOException if an I/O error occurs or InputStream size differ from parameter size + * @throws IllegalArgumentException if size is less than zero + * @since 2.1 + */ + public static byte[] toByteArray(InputStream input, int size) throws IOException { + + if (size < 0) { + throw new IllegalArgumentException("Size must be equal or greater than zero: " + size); + } + + if (size == 0) { + return new byte[0]; + } + + byte[] data = new byte[size]; + int offset = 0; + int readed; + + while (offset < size && (readed = input.read(data, offset, size - offset)) != EOF) { + offset += readed; + } + + if (offset != size) { + throw new IOException("Unexpected readed size. current: " + offset + ", excepted: " + size); + } + + return data; + } + + /** + * Get the contents of a Reader as a byte[] + * using the default character encoding of the platform. + *

+ * This method buffers the input internally, so there is no need to use a + * BufferedReader. + * + * @param input the Reader to read from + * @return the requested byte array + * @throws NullPointerException if the input is null + * @throws IOException if an I/O error occurs + */ + public static byte[] toByteArray(Reader input) throws IOException { + return toByteArray(input, Charset.defaultCharset()); + } + + /** + * Get the contents of a Reader as a byte[] + * using the specified character encoding. + *

+ * This method buffers the input internally, so there is no need to use a + * BufferedReader. + * + * @param input the Reader to read from + * @param encoding the encoding to use, null means platform default + * @return the requested byte array + * @throws NullPointerException if the input is null + * @throws IOException if an I/O error occurs + * @since 2.3 + */ + public static byte[] toByteArray(Reader input, Charset encoding) throws IOException { + ByteArrayOutputStream output = new ByteArrayOutputStream(); + copy(input, output, encoding); + return output.toByteArray(); + } + + /** + * Get the contents of a Reader as a byte[] + * using the specified character encoding. + *

+ * Character encoding names can be found at + * IANA. + *

+ * This method buffers the input internally, so there is no need to use a + * BufferedReader. + * + * @param input the Reader to read from + * @param encoding the encoding to use, null means platform default + * @return the requested byte array + * @throws NullPointerException if the input is null + * @throws IOException if an I/O error occurs + * @throws UnsupportedCharsetException thrown instead of {@link UnsupportedEncodingException} in version 2.2 if the encoding is not + * supported. + * @since 1.1 + */ + public static byte[] toByteArray(Reader input, String encoding) throws IOException { + return toByteArray(input, Charsets.toCharset(encoding)); + } + + /** + * Get the contents of a String as a byte[] + * using the default character encoding of the platform. + *

+ * This is the same as {@link String#getBytes()}. + * + * @param input the String to convert + * @return the requested byte array + * @throws NullPointerException if the input is null + * @throws IOException if an I/O error occurs (never occurs) + * @deprecated Use {@link String#getBytes()} + */ + @Deprecated + public static byte[] toByteArray(String input) throws IOException { + return input.getBytes(); + } + + /** + * Get the contents of a URI as a byte[]. + * + * @param uri the URI to read + * @return the requested byte array + * @throws NullPointerException if the uri is null + * @throws IOException if an I/O exception occurs + * @since 2.4 + */ + public static byte[] toByteArray(URI uri) throws IOException { + return IOUtils.toByteArray(uri.toURL()); + } + + /** + * Get the contents of a URL as a byte[]. + * + * @param url the URL to read + * @return the requested byte array + * @throws NullPointerException if the input is null + * @throws IOException if an I/O exception occurs + * @since 2.4 + */ + public static byte[] toByteArray(URL url) throws IOException { + URLConnection conn = url.openConnection(); + try { + return IOUtils.toByteArray(conn); + } + finally { + close(conn); + } + } + + /** + * Get the contents of a URLConnection as a byte[]. + * + * @param urlConn the URLConnection to read + * @return the requested byte array + * @throws NullPointerException if the urlConn is null + * @throws IOException if an I/O exception occurs + * @since 2.4 + */ + public static byte[] toByteArray(URLConnection urlConn) throws IOException { + InputStream inputStream = urlConn.getInputStream(); + try { + return IOUtils.toByteArray(inputStream); + } + finally { + inputStream.close(); + } + } + + // read char[] + //----------------------------------------------------------------------- + + /** + * Get the contents of an InputStream as a character array + * using the default character encoding of the platform. + *

+ * This method buffers the input internally, so there is no need to use a + * BufferedInputStream. + * + * @param is the InputStream to read from + * @return the requested character array + * @throws NullPointerException if the input is null + * @throws IOException if an I/O error occurs + * @since 1.1 + */ + public static char[] toCharArray(InputStream is) throws IOException { + return toCharArray(is, Charset.defaultCharset()); + } + + /** + * Get the contents of an InputStream as a character array + * using the specified character encoding. + *

+ * This method buffers the input internally, so there is no need to use a + * BufferedInputStream. + * + * @param is the InputStream to read from + * @param encoding the encoding to use, null means platform default + * @return the requested character array + * @throws NullPointerException if the input is null + * @throws IOException if an I/O error occurs + * @since 2.3 + */ + public static char[] toCharArray(InputStream is, Charset encoding) + throws IOException { + CharArrayWriter output = new CharArrayWriter(); + copy(is, output, encoding); + return output.toCharArray(); + } + + /** + * Get the contents of an InputStream as a character array + * using the specified character encoding. + *

+ * Character encoding names can be found at + * IANA. + *

+ * This method buffers the input internally, so there is no need to use a + * BufferedInputStream. + * + * @param is the InputStream to read from + * @param encoding the encoding to use, null means platform default + * @return the requested character array + * @throws NullPointerException if the input is null + * @throws IOException if an I/O error occurs + * @throws UnsupportedCharsetException thrown instead of {@link UnsupportedEncodingException} in version 2.2 if the encoding is not + * supported. + * @since 1.1 + */ + public static char[] toCharArray(InputStream is, String encoding) throws IOException { + return toCharArray(is, Charsets.toCharset(encoding)); + } + + /** + * Get the contents of a Reader as a character array. + *

+ * This method buffers the input internally, so there is no need to use a + * BufferedReader. + * + * @param input the Reader to read from + * @return the requested character array + * @throws NullPointerException if the input is null + * @throws IOException if an I/O error occurs + * @since 1.1 + */ + public static char[] toCharArray(Reader input) throws IOException { + CharArrayWriter sw = new CharArrayWriter(); + copy(input, sw); + return sw.toCharArray(); + } + + // read toString + //----------------------------------------------------------------------- + + /** + * Get the contents of an InputStream as a String + * using the default character encoding of the platform. + *

+ * This method buffers the input internally, so there is no need to use a + * BufferedInputStream. + * + * @param input the InputStream to read from + * @return the requested String + * @throws NullPointerException if the input is null + * @throws IOException if an I/O error occurs + */ + public static String toString(InputStream input) throws IOException { + return toString(input, Charset.defaultCharset()); + } + + /** + * Get the contents of an InputStream as a String + * using the specified character encoding. + *

+ * This method buffers the input internally, so there is no need to use a + * BufferedInputStream. + *

+ * + * @param input the InputStream to read from + * @param encoding the encoding to use, null means platform default + * @return the requested String + * @throws NullPointerException if the input is null + * @throws IOException if an I/O error occurs + * @since 2.3 + */ + public static String toString(InputStream input, Charset encoding) throws IOException { + StringWriter sw = new StringWriter(); + copy(input, sw, encoding); + return sw.toString(); + } + + /** + * Get the contents of an InputStream as a String + * using the specified character encoding. + *

+ * Character encoding names can be found at + * IANA. + *

+ * This method buffers the input internally, so there is no need to use a + * BufferedInputStream. + * + * @param input the InputStream to read from + * @param encoding the encoding to use, null means platform default + * @return the requested String + * @throws NullPointerException if the input is null + * @throws IOException if an I/O error occurs + * @throws UnsupportedCharsetException thrown instead of {@link UnsupportedEncodingException} in version 2.2 if the encoding is not + * supported. + */ + public static String toString(InputStream input, String encoding) + throws IOException { + return toString(input, Charsets.toCharset(encoding)); + } + + /** + * Get the contents of a Reader as a String. + *

+ * This method buffers the input internally, so there is no need to use a + * BufferedReader. + * + * @param input the Reader to read from + * @return the requested String + * @throws NullPointerException if the input is null + * @throws IOException if an I/O error occurs + */ + public static String toString(Reader input) throws IOException { + StringWriter sw = new StringWriter(); + copy(input, sw); + return sw.toString(); + } + + /** + * Gets the contents at the given URI. + * + * @param uri The URI source. + * @return The contents of the URL as a String. + * @throws IOException if an I/O exception occurs. + * @since 2.1 + */ + public static String toString(URI uri) throws IOException { + return toString(uri, Charset.defaultCharset()); + } + + /** + * Gets the contents at the given URI. + * + * @param uri The URI source. + * @param encoding The encoding name for the URL contents. + * @return The contents of the URL as a String. + * @throws IOException if an I/O exception occurs. + * @since 2.3. + */ + public static String toString(URI uri, Charset encoding) throws IOException { + return toString(uri.toURL(), Charsets.toCharset(encoding)); + } + + /** + * Gets the contents at the given URI. + * + * @param uri The URI source. + * @param encoding The encoding name for the URL contents. + * @return The contents of the URL as a String. + * @throws IOException if an I/O exception occurs. + * @throws UnsupportedCharsetException thrown instead of {@link UnsupportedEncodingException} in version 2.2 if the encoding is not + * supported. + * @since 2.1 + */ + public static String toString(URI uri, String encoding) throws IOException { + return toString(uri, Charsets.toCharset(encoding)); + } + + /** + * Gets the contents at the given URL. + * + * @param url The URL source. + * @return The contents of the URL as a String. + * @throws IOException if an I/O exception occurs. + * @since 2.1 + */ + public static String toString(URL url) throws IOException { + return toString(url, Charset.defaultCharset()); + } + + /** + * Gets the contents at the given URL. + * + * @param url The URL source. + * @param encoding The encoding name for the URL contents. + * @return The contents of the URL as a String. + * @throws IOException if an I/O exception occurs. + * @since 2.3 + */ + public static String toString(URL url, Charset encoding) throws IOException { + InputStream inputStream = url.openStream(); + try { + return toString(inputStream, encoding); + } + finally { + inputStream.close(); + } + } + + /** + * Gets the contents at the given URL. + * + * @param url The URL source. + * @param encoding The encoding name for the URL contents. + * @return The contents of the URL as a String. + * @throws IOException if an I/O exception occurs. + * @throws UnsupportedCharsetException thrown instead of {@link UnsupportedEncodingException} in version 2.2 if the encoding is not + * supported. + * @since 2.1 + */ + public static String toString(URL url, String encoding) throws IOException { + return toString(url, Charsets.toCharset(encoding)); + } + + /** + * Get the contents of a byte[] as a String + * using the default character encoding of the platform. + * + * @param input the byte array to read from + * @return the requested String + * @throws NullPointerException if the input is null + * @throws IOException if an I/O error occurs (never occurs) + * @deprecated Use {@link String#String(byte[])} + */ + @Deprecated + public static String toString(byte[] input) throws IOException { + return new String(input); + } + + /** + * Get the contents of a byte[] as a String + * using the specified character encoding. + *

+ * Character encoding names can be found at + * IANA. + * + * @param input the byte array to read from + * @param encoding the encoding to use, null means platform default + * @return the requested String + * @throws NullPointerException if the input is null + * @throws IOException if an I/O error occurs (never occurs) + */ + public static String toString(byte[] input, String encoding) throws IOException { + return new String(input, Charsets.toCharset(encoding)); + } + + // readLines + //----------------------------------------------------------------------- + + /** + * Get the contents of an InputStream as a list of Strings, + * one entry per line, using the default character encoding of the platform. + *

+ * This method buffers the input internally, so there is no need to use a + * BufferedInputStream. + * + * @param input the InputStream to read from, not null + * @return the list of Strings, never null + * @throws NullPointerException if the input is null + * @throws IOException if an I/O error occurs + * @since 1.1 + */ + public static List readLines(InputStream input) throws IOException { + return readLines(input, Charset.defaultCharset()); + } + + /** + * Get the contents of an InputStream as a list of Strings, + * one entry per line, using the specified character encoding. + *

+ * This method buffers the input internally, so there is no need to use a + * BufferedInputStream. + * + * @param input the InputStream to read from, not null + * @param encoding the encoding to use, null means platform default + * @return the list of Strings, never null + * @throws NullPointerException if the input is null + * @throws IOException if an I/O error occurs + * @since 2.3 + */ + public static List readLines(InputStream input, Charset encoding) throws IOException { + InputStreamReader reader = new InputStreamReader(input, Charsets.toCharset(encoding)); + return readLines(reader); + } + + /** + * Get the contents of an InputStream as a list of Strings, + * one entry per line, using the specified character encoding. + *

+ * Character encoding names can be found at + * IANA. + *

+ * This method buffers the input internally, so there is no need to use a + * BufferedInputStream. + * + * @param input the InputStream to read from, not null + * @param encoding the encoding to use, null means platform default + * @return the list of Strings, never null + * @throws NullPointerException if the input is null + * @throws IOException if an I/O error occurs + * @throws UnsupportedCharsetException thrown instead of {@link UnsupportedEncodingException} in version 2.2 if the encoding is not + * supported. + * @since 1.1 + */ + public static List readLines(InputStream input, String encoding) throws IOException { + return readLines(input, Charsets.toCharset(encoding)); + } + + /** + * Get the contents of a Reader as a list of Strings, + * one entry per line. + *

+ * This method buffers the input internally, so there is no need to use a + * BufferedReader. + * + * @param input the Reader to read from, not null + * @return the list of Strings, never null + * @throws NullPointerException if the input is null + * @throws IOException if an I/O error occurs + * @since 1.1 + */ + public static List readLines(Reader input) throws IOException { + BufferedReader reader = toBufferedReader(input); + List list = new ArrayList(); + String line = reader.readLine(); + while (line != null) { + list.add(line); + line = reader.readLine(); + } + return list; + } + + + //----------------------------------------------------------------------- + + /** + * Convert the specified CharSequence to an input stream, encoded as bytes + * using the default character encoding of the platform. + * + * @param input the CharSequence to convert + * @return an input stream + * @since 2.0 + */ + public static InputStream toInputStream(CharSequence input) { + return toInputStream(input, Charset.defaultCharset()); + } + + /** + * Convert the specified CharSequence to an input stream, encoded as bytes + * using the specified character encoding. + * + * @param input the CharSequence to convert + * @param encoding the encoding to use, null means platform default + * @return an input stream + * @since 2.3 + */ + public static InputStream toInputStream(CharSequence input, Charset encoding) { + return toInputStream(input.toString(), encoding); + } + + /** + * Convert the specified CharSequence to an input stream, encoded as bytes + * using the specified character encoding. + *

+ * Character encoding names can be found at + * IANA. + * + * @param input the CharSequence to convert + * @param encoding the encoding to use, null means platform default + * @return an input stream + * @throws IOException if the encoding is invalid + * @throws UnsupportedCharsetException thrown instead of {@link UnsupportedEncodingException} in version 2.2 if the encoding is not + * supported. + * @since 2.0 + */ + public static InputStream toInputStream(CharSequence input, String encoding) throws IOException { + return toInputStream(input, Charsets.toCharset(encoding)); + } + + //----------------------------------------------------------------------- + + /** + * Convert the specified string to an input stream, encoded as bytes + * using the default character encoding of the platform. + * + * @param input the string to convert + * @return an input stream + * @since 1.1 + */ + public static InputStream toInputStream(String input) { + return toInputStream(input, Charset.defaultCharset()); + } + + /** + * Convert the specified string to an input stream, encoded as bytes + * using the specified character encoding. + * + * @param input the string to convert + * @param encoding the encoding to use, null means platform default + * @return an input stream + * @since 2.3 + */ + public static InputStream toInputStream(String input, Charset encoding) { + return new ByteArrayInputStream(input.getBytes(Charsets.toCharset(encoding))); + } + + /** + * Convert the specified string to an input stream, encoded as bytes + * using the specified character encoding. + *

+ * Character encoding names can be found at + * IANA. + * + * @param input the string to convert + * @param encoding the encoding to use, null means platform default + * @return an input stream + * @throws IOException if the encoding is invalid + * @throws UnsupportedCharsetException thrown instead of {@link UnsupportedEncodingException} in version 2.2 if the encoding is not + * supported. + * @since 1.1 + */ + public static InputStream toInputStream(String input, String encoding) throws IOException { + byte[] bytes = input.getBytes(Charsets.toCharset(encoding)); + return new ByteArrayInputStream(bytes); + } + + // write byte[] + //----------------------------------------------------------------------- + + /** + * Writes bytes from a byte[] to an OutputStream. + * + * @param data the byte array to write, do not modify during output, + * null ignored + * @param output the OutputStream to write to + * @throws NullPointerException if output is null + * @throws IOException if an I/O error occurs + * @since 1.1 + */ + public static void write(byte[] data, OutputStream output) + throws IOException { + if (data != null) { + output.write(data); + } + } + + /** + * Writes bytes from a byte[] to chars on a Writer + * using the default character encoding of the platform. + *

+ * This method uses {@link String#String(byte[])}. + * + * @param data the byte array to write, do not modify during output, + * null ignored + * @param output the Writer to write to + * @throws NullPointerException if output is null + * @throws IOException if an I/O error occurs + * @since 1.1 + */ + public static void write(byte[] data, Writer output) throws IOException { + write(data, output, Charset.defaultCharset()); + } + + /** + * Writes bytes from a byte[] to chars on a Writer + * using the specified character encoding. + *

+ * This method uses {@link String#String(byte[], String)}. + * + * @param data the byte array to write, do not modify during output, + * null ignored + * @param output the Writer to write to + * @param encoding the encoding to use, null means platform default + * @throws NullPointerException if output is null + * @throws IOException if an I/O error occurs + * @since 2.3 + */ + public static void write(byte[] data, Writer output, Charset encoding) throws IOException { + if (data != null) { + output.write(new String(data, Charsets.toCharset(encoding))); + } + } + + /** + * Writes bytes from a byte[] to chars on a Writer + * using the specified character encoding. + *

+ * Character encoding names can be found at + * IANA. + *

+ * This method uses {@link String#String(byte[], String)}. + * + * @param data the byte array to write, do not modify during output, + * null ignored + * @param output the Writer to write to + * @param encoding the encoding to use, null means platform default + * @throws NullPointerException if output is null + * @throws IOException if an I/O error occurs + * @throws UnsupportedCharsetException thrown instead of {@link UnsupportedEncodingException} in version 2.2 if the encoding is not + * supported. + * @since 1.1 + */ + public static void write(byte[] data, Writer output, String encoding) throws IOException { + write(data, output, Charsets.toCharset(encoding)); + } + + // write char[] + //----------------------------------------------------------------------- + + /** + * Writes chars from a char[] to a Writer + * using the default character encoding of the platform. + * + * @param data the char array to write, do not modify during output, + * null ignored + * @param output the Writer to write to + * @throws NullPointerException if output is null + * @throws IOException if an I/O error occurs + * @since 1.1 + */ + public static void write(char[] data, Writer output) throws IOException { + if (data != null) { + output.write(data); + } + } + + /** + * Writes chars from a char[] to bytes on an + * OutputStream. + *

+ * This method uses {@link String#String(char[])} and + * {@link String#getBytes()}. + * + * @param data the char array to write, do not modify during output, + * null ignored + * @param output the OutputStream to write to + * @throws NullPointerException if output is null + * @throws IOException if an I/O error occurs + * @since 1.1 + */ + public static void write(char[] data, OutputStream output) + throws IOException { + write(data, output, Charset.defaultCharset()); + } + + /** + * Writes chars from a char[] to bytes on an + * OutputStream using the specified character encoding. + *

+ * This method uses {@link String#String(char[])} and + * {@link String#getBytes(String)}. + * + * @param data the char array to write, do not modify during output, + * null ignored + * @param output the OutputStream to write to + * @param encoding the encoding to use, null means platform default + * @throws NullPointerException if output is null + * @throws IOException if an I/O error occurs + * @since 2.3 + */ + public static void write(char[] data, OutputStream output, Charset encoding) throws IOException { + if (data != null) { + output.write(new String(data).getBytes(Charsets.toCharset(encoding))); + } + } + + /** + * Writes chars from a char[] to bytes on an + * OutputStream using the specified character encoding. + *

+ * Character encoding names can be found at + * IANA. + *

+ * This method uses {@link String#String(char[])} and + * {@link String#getBytes(String)}. + * + * @param data the char array to write, do not modify during output, + * null ignored + * @param output the OutputStream to write to + * @param encoding the encoding to use, null means platform default + * @throws NullPointerException if output is null + * @throws IOException if an I/O error occurs + * @throws UnsupportedCharsetException thrown instead of {@link UnsupportedEncodingException} in version 2.2 if the encoding is not + * supported. + * @since 1.1 + */ + public static void write(char[] data, OutputStream output, String encoding) + throws IOException { + write(data, output, Charsets.toCharset(encoding)); + } + + // write CharSequence + //----------------------------------------------------------------------- + + /** + * Writes chars from a CharSequence to a Writer. + * + * @param data the CharSequence to write, null ignored + * @param output the Writer to write to + * @throws NullPointerException if output is null + * @throws IOException if an I/O error occurs + * @since 2.0 + */ + public static void write(CharSequence data, Writer output) throws IOException { + if (data != null) { + write(data.toString(), output); + } + } + + /** + * Writes chars from a CharSequence to bytes on an + * OutputStream using the default character encoding of the + * platform. + *

+ * This method uses {@link String#getBytes()}. + * + * @param data the CharSequence to write, null ignored + * @param output the OutputStream to write to + * @throws NullPointerException if output is null + * @throws IOException if an I/O error occurs + * @since 2.0 + */ + public static void write(CharSequence data, OutputStream output) + throws IOException { + write(data, output, Charset.defaultCharset()); + } + + /** + * Writes chars from a CharSequence to bytes on an + * OutputStream using the specified character encoding. + *

+ * This method uses {@link String#getBytes(String)}. + * + * @param data the CharSequence to write, null ignored + * @param output the OutputStream to write to + * @param encoding the encoding to use, null means platform default + * @throws NullPointerException if output is null + * @throws IOException if an I/O error occurs + * @since 2.3 + */ + public static void write(CharSequence data, OutputStream output, Charset encoding) throws IOException { + if (data != null) { + write(data.toString(), output, encoding); + } + } + + /** + * Writes chars from a CharSequence to bytes on an + * OutputStream using the specified character encoding. + *

+ * Character encoding names can be found at + * IANA. + *

+ * This method uses {@link String#getBytes(String)}. + * + * @param data the CharSequence to write, null ignored + * @param output the OutputStream to write to + * @param encoding the encoding to use, null means platform default + * @throws NullPointerException if output is null + * @throws IOException if an I/O error occurs + * @throws UnsupportedCharsetException thrown instead of {@link UnsupportedEncodingException} in version 2.2 if the encoding is not + * supported. + * @since 2.0 + */ + public static void write(CharSequence data, OutputStream output, String encoding) throws IOException { + write(data, output, Charsets.toCharset(encoding)); + } + + // write String + //----------------------------------------------------------------------- + + /** + * Writes chars from a String to a Writer. + * + * @param data the String to write, null ignored + * @param output the Writer to write to + * @throws NullPointerException if output is null + * @throws IOException if an I/O error occurs + * @since 1.1 + */ + public static void write(String data, Writer output) throws IOException { + if (data != null) { + output.write(data); + } + } + + /** + * Writes chars from a String to bytes on an + * OutputStream using the default character encoding of the + * platform. + *

+ * This method uses {@link String#getBytes()}. + * + * @param data the String to write, null ignored + * @param output the OutputStream to write to + * @throws NullPointerException if output is null + * @throws IOException if an I/O error occurs + * @since 1.1 + */ + public static void write(String data, OutputStream output) + throws IOException { + write(data, output, Charset.defaultCharset()); + } + + /** + * Writes chars from a String to bytes on an + * OutputStream using the specified character encoding. + *

+ * This method uses {@link String#getBytes(String)}. + * + * @param data the String to write, null ignored + * @param output the OutputStream to write to + * @param encoding the encoding to use, null means platform default + * @throws NullPointerException if output is null + * @throws IOException if an I/O error occurs + * @since 2.3 + */ + public static void write(String data, OutputStream output, Charset encoding) throws IOException { + if (data != null) { + output.write(data.getBytes(Charsets.toCharset(encoding))); + } + } + + /** + * Writes chars from a String to bytes on an + * OutputStream using the specified character encoding. + *

+ * Character encoding names can be found at + * IANA. + *

+ * This method uses {@link String#getBytes(String)}. + * + * @param data the String to write, null ignored + * @param output the OutputStream to write to + * @param encoding the encoding to use, null means platform default + * @throws NullPointerException if output is null + * @throws IOException if an I/O error occurs + * @throws UnsupportedCharsetException thrown instead of {@link UnsupportedEncodingException} in version 2.2 if the encoding is not + * supported. + * @since 1.1 + */ + public static void write(String data, OutputStream output, String encoding) + throws IOException { + write(data, output, Charsets.toCharset(encoding)); + } + + // write StringBuffer + //----------------------------------------------------------------------- + + /** + * Writes chars from a StringBuffer to a Writer. + * + * @param data the StringBuffer to write, null ignored + * @param output the Writer to write to + * @throws NullPointerException if output is null + * @throws IOException if an I/O error occurs + * @since 1.1 + * @deprecated replaced by write(CharSequence, Writer) + */ + @Deprecated + public static void write(StringBuffer data, Writer output) + throws IOException { + if (data != null) { + output.write(data.toString()); + } + } + + /** + * Writes chars from a StringBuffer to bytes on an + * OutputStream using the default character encoding of the + * platform. + *

+ * This method uses {@link String#getBytes()}. + * + * @param data the StringBuffer to write, null ignored + * @param output the OutputStream to write to + * @throws NullPointerException if output is null + * @throws IOException if an I/O error occurs + * @since 1.1 + * @deprecated replaced by write(CharSequence, OutputStream) + */ + @Deprecated + public static void write(StringBuffer data, OutputStream output) + throws IOException { + write(data, output, (String) null); + } + + /** + * Writes chars from a StringBuffer to bytes on an + * OutputStream using the specified character encoding. + *

+ * Character encoding names can be found at + * IANA. + *

+ * This method uses {@link String#getBytes(String)}. + * + * @param data the StringBuffer to write, null ignored + * @param output the OutputStream to write to + * @param encoding the encoding to use, null means platform default + * @throws NullPointerException if output is null + * @throws IOException if an I/O error occurs + * @throws UnsupportedCharsetException thrown instead of {@link UnsupportedEncodingException} in version 2.2 if the encoding is not + * supported. + * @since 1.1 + * @deprecated replaced by write(CharSequence, OutputStream, String) + */ + @Deprecated + public static void write(StringBuffer data, OutputStream output, String encoding) throws IOException { + if (data != null) { + output.write(data.toString().getBytes(Charsets.toCharset(encoding))); + } + } + + // writeLines + //----------------------------------------------------------------------- + + /** + * Writes the toString() value of each item in a collection to + * an OutputStream line by line, using the default character + * encoding of the platform and the specified line ending. + * + * @param lines the lines to write, null entries produce blank lines + * @param lineEnding the line separator to use, null is system default + * @param output the OutputStream to write to, not null, not closed + * @throws NullPointerException if the output is null + * @throws IOException if an I/O error occurs + * @since 1.1 + */ + public static void writeLines(Collection lines, String lineEnding, + OutputStream output) throws IOException { + writeLines(lines, lineEnding, output, Charset.defaultCharset()); + } + + /** + * Writes the toString() value of each item in a collection to + * an OutputStream line by line, using the specified character + * encoding and the specified line ending. + * + * @param lines the lines to write, null entries produce blank lines + * @param lineEnding the line separator to use, null is system default + * @param output the OutputStream to write to, not null, not closed + * @param encoding the encoding to use, null means platform default + * @throws NullPointerException if the output is null + * @throws IOException if an I/O error occurs + * @since 2.3 + */ + public static void writeLines(Collection lines, String lineEnding, OutputStream output, Charset encoding) + throws IOException { + if (lines == null) { + return; + } + if (lineEnding == null) { + lineEnding = LINE_SEPARATOR; + } + Charset cs = Charsets.toCharset(encoding); + for (Object line : lines) { + if (line != null) { + output.write(line.toString().getBytes(cs)); + } + output.write(lineEnding.getBytes(cs)); + } + } + + /** + * Writes the toString() value of each item in a collection to + * an OutputStream line by line, using the specified character + * encoding and the specified line ending. + *

+ * Character encoding names can be found at + * IANA. + * + * @param lines the lines to write, null entries produce blank lines + * @param lineEnding the line separator to use, null is system default + * @param output the OutputStream to write to, not null, not closed + * @param encoding the encoding to use, null means platform default + * @throws NullPointerException if the output is null + * @throws IOException if an I/O error occurs + * @throws UnsupportedCharsetException thrown instead of {@link UnsupportedEncodingException} in version 2.2 if the encoding is not + * supported. + * @since 1.1 + */ + public static void writeLines(Collection lines, String lineEnding, + OutputStream output, String encoding) throws IOException { + writeLines(lines, lineEnding, output, Charsets.toCharset(encoding)); + } + + /** + * Writes the toString() value of each item in a collection to + * a Writer line by line, using the specified line ending. + * + * @param lines the lines to write, null entries produce blank lines + * @param lineEnding the line separator to use, null is system default + * @param writer the Writer to write to, not null, not closed + * @throws NullPointerException if the input is null + * @throws IOException if an I/O error occurs + * @since 1.1 + */ + public static void writeLines(Collection lines, String lineEnding, + Writer writer) throws IOException { + if (lines == null) { + return; + } + if (lineEnding == null) { + lineEnding = LINE_SEPARATOR; + } + for (Object line : lines) { + if (line != null) { + writer.write(line.toString()); + } + writer.write(lineEnding); + } + } + + // copy from InputStream + //----------------------------------------------------------------------- + + /** + * Copy bytes from an InputStream to an + * OutputStream. + *

+ * This method buffers the input internally, so there is no need to use a + * BufferedInputStream. + *

+ * Large streams (over 2GB) will return a bytes copied value of + * -1 after the copy has completed since the correct + * number of bytes cannot be returned as an int. For large streams + * use the copyLarge(InputStream, OutputStream) method. + * + * @param input the InputStream to read from + * @param output the OutputStream to write to + * @return the number of bytes copied, or -1 if > Integer.MAX_VALUE + * @throws NullPointerException if the input or output is null + * @throws IOException if an I/O error occurs + * @since 1.1 + */ + public static int copy(InputStream input, OutputStream output) throws IOException { + long count = copyLarge(input, output); + if (count > Integer.MAX_VALUE) { + return -1; + } + return (int) count; + } + + /** + * Copy bytes from a large (over 2GB) InputStream to an + * OutputStream. + *

+ * This method buffers the input internally, so there is no need to use a + * BufferedInputStream. + *

+ * The buffer size is given by {@link #DEFAULT_BUFFER_SIZE}. + * + * @param input the InputStream to read from + * @param output the OutputStream to write to + * @return the number of bytes copied + * @throws NullPointerException if the input or output is null + * @throws IOException if an I/O error occurs + * @since 1.3 + */ + public static long copyLarge(InputStream input, OutputStream output) + throws IOException { + return copyLarge(input, output, new byte[DEFAULT_BUFFER_SIZE]); + } + + /** + * Copy bytes from a large (over 2GB) InputStream to an + * OutputStream. + *

+ * This method uses the provided buffer, so there is no need to use a + * BufferedInputStream. + *

+ * + * @param input the InputStream to read from + * @param output the OutputStream to write to + * @param buffer the buffer to use for the copy + * @return the number of bytes copied + * @throws NullPointerException if the input or output is null + * @throws IOException if an I/O error occurs + * @since 2.2 + */ + public static long copyLarge(InputStream input, OutputStream output, byte[] buffer) + throws IOException { + long count = 0; + int n = 0; + while (EOF != (n = input.read(buffer))) { + output.write(buffer, 0, n); + count += n; + } + return count; + } + + /** + * Copy some or all bytes from a large (over 2GB) InputStream to an + * OutputStream, optionally skipping input bytes. + *

+ * This method buffers the input internally, so there is no need to use a + * BufferedInputStream. + *

+ * The buffer size is given by {@link #DEFAULT_BUFFER_SIZE}. + * + * @param input the InputStream to read from + * @param output the OutputStream to write to + * @param inputOffset : number of bytes to skip from input before copying + * -ve values are ignored + * @param length : number of bytes to copy. -ve means all + * @return the number of bytes copied + * @throws NullPointerException if the input or output is null + * @throws IOException if an I/O error occurs + * @since 2.2 + */ + public static long copyLarge(InputStream input, OutputStream output, long inputOffset, long length) + throws IOException { + return copyLarge(input, output, inputOffset, length, new byte[DEFAULT_BUFFER_SIZE]); + } + + /** + * Copy some or all bytes from a large (over 2GB) InputStream to an + * OutputStream, optionally skipping input bytes. + *

+ * This method uses the provided buffer, so there is no need to use a + * BufferedInputStream. + *

+ * + * @param input the InputStream to read from + * @param output the OutputStream to write to + * @param inputOffset : number of bytes to skip from input before copying + * -ve values are ignored + * @param length : number of bytes to copy. -ve means all + * @param buffer the buffer to use for the copy + * @return the number of bytes copied + * @throws NullPointerException if the input or output is null + * @throws IOException if an I/O error occurs + * @since 2.2 + */ + public static long copyLarge(InputStream input, OutputStream output, + final long inputOffset, final long length, byte[] buffer) throws IOException { + if (inputOffset > 0) { + skipFully(input, inputOffset); + } + if (length == 0) { + return 0; + } + final int bufferLength = buffer.length; + int bytesToRead = bufferLength; + if (length > 0 && length < bufferLength) { + bytesToRead = (int) length; + } + int read; + long totalRead = 0; + while (bytesToRead > 0 && EOF != (read = input.read(buffer, 0, bytesToRead))) { + output.write(buffer, 0, read); + totalRead += read; + if (length > 0) { // only adjust length if not reading to the end + // Note the cast must work because buffer.length is an integer + bytesToRead = (int) Math.min(length - totalRead, bufferLength); + } + } + return totalRead; + } + + /** + * Copy bytes from an InputStream to chars on a + * Writer using the default character encoding of the platform. + *

+ * This method buffers the input internally, so there is no need to use a + * BufferedInputStream. + *

+ * This method uses {@link InputStreamReader}. + * + * @param input the InputStream to read from + * @param output the Writer to write to + * @throws NullPointerException if the input or output is null + * @throws IOException if an I/O error occurs + * @since 1.1 + */ + public static void copy(InputStream input, Writer output) + throws IOException { + copy(input, output, Charset.defaultCharset()); + } + + /** + * Copy bytes from an InputStream to chars on a + * Writer using the specified character encoding. + *

+ * This method buffers the input internally, so there is no need to use a + * BufferedInputStream. + *

+ * This method uses {@link InputStreamReader}. + * + * @param input the InputStream to read from + * @param output the Writer to write to + * @param encoding the encoding to use, null means platform default + * @throws NullPointerException if the input or output is null + * @throws IOException if an I/O error occurs + * @since 2.3 + */ + public static void copy(InputStream input, Writer output, Charset encoding) throws IOException { + InputStreamReader in = new InputStreamReader(input, Charsets.toCharset(encoding)); + copy(in, output); + } + + /** + * Copy bytes from an InputStream to chars on a + * Writer using the specified character encoding. + *

+ * This method buffers the input internally, so there is no need to use a + * BufferedInputStream. + *

+ * Character encoding names can be found at + * IANA. + *

+ * This method uses {@link InputStreamReader}. + * + * @param input the InputStream to read from + * @param output the Writer to write to + * @param encoding the encoding to use, null means platform default + * @throws NullPointerException if the input or output is null + * @throws IOException if an I/O error occurs + * @throws UnsupportedCharsetException thrown instead of {@link UnsupportedEncodingException} in version 2.2 if the encoding is not + * supported. + * @since 1.1 + */ + public static void copy(InputStream input, Writer output, String encoding) throws IOException { + copy(input, output, Charsets.toCharset(encoding)); + } + + // copy from Reader + //----------------------------------------------------------------------- + + /** + * Copy chars from a Reader to a Writer. + *

+ * This method buffers the input internally, so there is no need to use a + * BufferedReader. + *

+ * Large streams (over 2GB) will return a chars copied value of + * -1 after the copy has completed since the correct + * number of chars cannot be returned as an int. For large streams + * use the copyLarge(Reader, Writer) method. + * + * @param input the Reader to read from + * @param output the Writer to write to + * @return the number of characters copied, or -1 if > Integer.MAX_VALUE + * @throws NullPointerException if the input or output is null + * @throws IOException if an I/O error occurs + * @since 1.1 + */ + public static int copy(Reader input, Writer output) throws IOException { + long count = copyLarge(input, output); + if (count > Integer.MAX_VALUE) { + return -1; + } + return (int) count; + } + + /** + * Copy chars from a large (over 2GB) Reader to a Writer. + *

+ * This method buffers the input internally, so there is no need to use a + * BufferedReader. + *

+ * The buffer size is given by {@link #DEFAULT_BUFFER_SIZE}. + * + * @param input the Reader to read from + * @param output the Writer to write to + * @return the number of characters copied + * @throws NullPointerException if the input or output is null + * @throws IOException if an I/O error occurs + * @since 1.3 + */ + public static long copyLarge(Reader input, Writer output) throws IOException { + return copyLarge(input, output, new char[DEFAULT_BUFFER_SIZE]); + } + + /** + * Copy chars from a large (over 2GB) Reader to a Writer. + *

+ * This method uses the provided buffer, so there is no need to use a + * BufferedReader. + *

+ * + * @param input the Reader to read from + * @param output the Writer to write to + * @param buffer the buffer to be used for the copy + * @return the number of characters copied + * @throws NullPointerException if the input or output is null + * @throws IOException if an I/O error occurs + * @since 2.2 + */ + public static long copyLarge(Reader input, Writer output, char[] buffer) throws IOException { + long count = 0; + int n = 0; + while (EOF != (n = input.read(buffer))) { + output.write(buffer, 0, n); + count += n; + } + return count; + } + + /** + * Copy some or all chars from a large (over 2GB) InputStream to an + * OutputStream, optionally skipping input chars. + *

+ * This method buffers the input internally, so there is no need to use a + * BufferedReader. + *

+ * The buffer size is given by {@link #DEFAULT_BUFFER_SIZE}. + * + * @param input the Reader to read from + * @param output the Writer to write to + * @param inputOffset : number of chars to skip from input before copying + * -ve values are ignored + * @param length : number of chars to copy. -ve means all + * @return the number of chars copied + * @throws NullPointerException if the input or output is null + * @throws IOException if an I/O error occurs + * @since 2.2 + */ + public static long copyLarge(Reader input, Writer output, final long inputOffset, final long length) + throws IOException { + return copyLarge(input, output, inputOffset, length, new char[DEFAULT_BUFFER_SIZE]); + } + + /** + * Copy some or all chars from a large (over 2GB) InputStream to an + * OutputStream, optionally skipping input chars. + *

+ * This method uses the provided buffer, so there is no need to use a + * BufferedReader. + *

+ * + * @param input the Reader to read from + * @param output the Writer to write to + * @param inputOffset : number of chars to skip from input before copying + * -ve values are ignored + * @param length : number of chars to copy. -ve means all + * @param buffer the buffer to be used for the copy + * @return the number of chars copied + * @throws NullPointerException if the input or output is null + * @throws IOException if an I/O error occurs + * @since 2.2 + */ + public static long copyLarge(Reader input, Writer output, final long inputOffset, final long length, char[] buffer) + throws IOException { + if (inputOffset > 0) { + skipFully(input, inputOffset); + } + if (length == 0) { + return 0; + } + int bytesToRead = buffer.length; + if (length > 0 && length < buffer.length) { + bytesToRead = (int) length; + } + int read; + long totalRead = 0; + while (bytesToRead > 0 && EOF != (read = input.read(buffer, 0, bytesToRead))) { + output.write(buffer, 0, read); + totalRead += read; + if (length > 0) { // only adjust length if not reading to the end + // Note the cast must work because buffer.length is an integer + bytesToRead = (int) Math.min(length - totalRead, buffer.length); + } + } + return totalRead; + } + + /** + * Copy chars from a Reader to bytes on an + * OutputStream using the default character encoding of the + * platform, and calling flush. + *

+ * This method buffers the input internally, so there is no need to use a + * BufferedReader. + *

+ * Due to the implementation of OutputStreamWriter, this method performs a + * flush. + *

+ * This method uses {@link OutputStreamWriter}. + * + * @param input the Reader to read from + * @param output the OutputStream to write to + * @throws NullPointerException if the input or output is null + * @throws IOException if an I/O error occurs + * @since 1.1 + */ + public static void copy(Reader input, OutputStream output) + throws IOException { + copy(input, output, Charset.defaultCharset()); + } + + /** + * Copy chars from a Reader to bytes on an + * OutputStream using the specified character encoding, and + * calling flush. + *

+ * This method buffers the input internally, so there is no need to use a + * BufferedReader. + *

+ *

+ * Due to the implementation of OutputStreamWriter, this method performs a + * flush. + *

+ *

+ * This method uses {@link OutputStreamWriter}. + *

+ * + * @param input the Reader to read from + * @param output the OutputStream to write to + * @param encoding the encoding to use, null means platform default + * @throws NullPointerException if the input or output is null + * @throws IOException if an I/O error occurs + * @since 2.3 + */ + public static void copy(Reader input, OutputStream output, Charset encoding) throws IOException { + OutputStreamWriter out = new OutputStreamWriter(output, Charsets.toCharset(encoding)); + copy(input, out); + // XXX Unless anyone is planning on rewriting OutputStreamWriter, + // we have to flush here. + out.flush(); + } + + /** + * Copy chars from a Reader to bytes on an + * OutputStream using the specified character encoding, and + * calling flush. + *

+ * This method buffers the input internally, so there is no need to use a + * BufferedReader. + *

+ * Character encoding names can be found at + * IANA. + *

+ * Due to the implementation of OutputStreamWriter, this method performs a + * flush. + *

+ * This method uses {@link OutputStreamWriter}. + * + * @param input the Reader to read from + * @param output the OutputStream to write to + * @param encoding the encoding to use, null means platform default + * @throws NullPointerException if the input or output is null + * @throws IOException if an I/O error occurs + * @throws UnsupportedCharsetException thrown instead of {@link UnsupportedEncodingException} in version 2.2 if the encoding is not + * supported. + * @since 1.1 + */ + public static void copy(Reader input, OutputStream output, String encoding) throws IOException { + copy(input, output, Charsets.toCharset(encoding)); + } + + // content equals + //----------------------------------------------------------------------- + + /** + * Compare the contents of two Streams to determine if they are equal or + * not. + *

+ * This method buffers the input internally using + * BufferedInputStream if they are not already buffered. + * + * @param input1 the first stream + * @param input2 the second stream + * @return true if the content of the streams are equal or they both don't + * exist, false otherwise + * @throws NullPointerException if either input is null + * @throws IOException if an I/O error occurs + */ + public static boolean contentEquals(InputStream input1, InputStream input2) + throws IOException { + if (!(input1 instanceof BufferedInputStream)) { + input1 = new BufferedInputStream(input1); + } + if (!(input2 instanceof BufferedInputStream)) { + input2 = new BufferedInputStream(input2); + } + + int ch = input1.read(); + while (EOF != ch) { + int ch2 = input2.read(); + if (ch != ch2) { + return false; + } + ch = input1.read(); + } + + int ch2 = input2.read(); + return ch2 == EOF; + } + + /** + * Compare the contents of two Readers to determine if they are equal or + * not. + *

+ * This method buffers the input internally using + * BufferedReader if they are not already buffered. + * + * @param input1 the first reader + * @param input2 the second reader + * @return true if the content of the readers are equal or they both don't + * exist, false otherwise + * @throws NullPointerException if either input is null + * @throws IOException if an I/O error occurs + * @since 1.1 + */ + public static boolean contentEquals(Reader input1, Reader input2) + throws IOException { + + input1 = toBufferedReader(input1); + input2 = toBufferedReader(input2); + + int ch = input1.read(); + while (EOF != ch) { + int ch2 = input2.read(); + if (ch != ch2) { + return false; + } + ch = input1.read(); + } + + int ch2 = input2.read(); + return ch2 == EOF; + } + + /** + * Compare the contents of two Readers to determine if they are equal or + * not, ignoring EOL characters. + *

+ * This method buffers the input internally using + * BufferedReader if they are not already buffered. + * + * @param input1 the first reader + * @param input2 the second reader + * @return true if the content of the readers are equal (ignoring EOL differences), false otherwise + * @throws NullPointerException if either input is null + * @throws IOException if an I/O error occurs + * @since 2.2 + */ + public static boolean contentEqualsIgnoreEOL(Reader input1, Reader input2) + throws IOException { + BufferedReader br1 = toBufferedReader(input1); + BufferedReader br2 = toBufferedReader(input2); + + String line1 = br1.readLine(); + String line2 = br2.readLine(); + while (line1 != null && line2 != null && line1.equals(line2)) { + line1 = br1.readLine(); + line2 = br2.readLine(); + } + return line1 == null ? line2 == null ? true : false : line1.equals(line2); + } + + /** + * Skip bytes from an input byte stream. + * This implementation guarantees that it will read as many bytes + * as possible before giving up; this may not always be the case for + * subclasses of {@link Reader}. + * + * @param input byte stream to skip + * @param toSkip number of bytes to skip. + * @return number of bytes actually skipped. + * @throws IOException if there is a problem reading the file + * @throws IllegalArgumentException if toSkip is negative + * @see InputStream#skip(long) + * @since 2.0 + */ + public static long skip(InputStream input, long toSkip) throws IOException { + if (toSkip < 0) { + throw new IllegalArgumentException("Skip count must be non-negative, actual: " + toSkip); + } + /* + * N.B. no need to synchronize this because: - we don't care if the buffer is created multiple times (the data + * is ignored) - we always use the same size buffer, so if it it is recreated it will still be OK (if the buffer + * size were variable, we would need to synch. to ensure some other thread did not create a smaller one) + */ + if (SKIP_BYTE_BUFFER == null) { + SKIP_BYTE_BUFFER = new byte[SKIP_BUFFER_SIZE]; + } + long remain = toSkip; + while (remain > 0) { + long n = input.read(SKIP_BYTE_BUFFER, 0, (int) Math.min(remain, SKIP_BUFFER_SIZE)); + if (n < 0) { // EOF + break; + } + remain -= n; + } + return toSkip - remain; + } + + /** + * Skip characters from an input character stream. + * This implementation guarantees that it will read as many characters + * as possible before giving up; this may not always be the case for + * subclasses of {@link Reader}. + * + * @param input character stream to skip + * @param toSkip number of characters to skip. + * @return number of characters actually skipped. + * @throws IOException if there is a problem reading the file + * @throws IllegalArgumentException if toSkip is negative + * @see Reader#skip(long) + * @since 2.0 + */ + public static long skip(Reader input, long toSkip) throws IOException { + if (toSkip < 0) { + throw new IllegalArgumentException("Skip count must be non-negative, actual: " + toSkip); + } + /* + * N.B. no need to synchronize this because: - we don't care if the buffer is created multiple times (the data + * is ignored) - we always use the same size buffer, so if it it is recreated it will still be OK (if the buffer + * size were variable, we would need to synch. to ensure some other thread did not create a smaller one) + */ + if (SKIP_CHAR_BUFFER == null) { + SKIP_CHAR_BUFFER = new char[SKIP_BUFFER_SIZE]; + } + long remain = toSkip; + while (remain > 0) { + long n = input.read(SKIP_CHAR_BUFFER, 0, (int) Math.min(remain, SKIP_BUFFER_SIZE)); + if (n < 0) { // EOF + break; + } + remain -= n; + } + return toSkip - remain; + } + + /** + * Skip the requested number of bytes or fail if there are not enough left. + *

+ * This allows for the possibility that {@link InputStream#skip(long)} may + * not skip as many bytes as requested (most likely because of reaching EOF). + * + * @param input stream to skip + * @param toSkip the number of bytes to skip + * @throws IOException if there is a problem reading the file + * @throws IllegalArgumentException if toSkip is negative + * @throws EOFException if the number of bytes skipped was incorrect + * @see InputStream#skip(long) + * @since 2.0 + */ + public static void skipFully(InputStream input, long toSkip) throws IOException { + if (toSkip < 0) { + throw new IllegalArgumentException("Bytes to skip must not be negative: " + toSkip); + } + long skipped = skip(input, toSkip); + if (skipped != toSkip) { + throw new EOFException("Bytes to skip: " + toSkip + " actual: " + skipped); + } + } + + /** + * Skip the requested number of characters or fail if there are not enough left. + *

+ * This allows for the possibility that {@link Reader#skip(long)} may + * not skip as many characters as requested (most likely because of reaching EOF). + * + * @param input stream to skip + * @param toSkip the number of characters to skip + * @throws IOException if there is a problem reading the file + * @throws IllegalArgumentException if toSkip is negative + * @throws EOFException if the number of characters skipped was incorrect + * @see Reader#skip(long) + * @since 2.0 + */ + public static void skipFully(Reader input, long toSkip) throws IOException { + long skipped = skip(input, toSkip); + if (skipped != toSkip) { + throw new EOFException("Chars to skip: " + toSkip + " actual: " + skipped); + } + } + + + /** + * Read characters from an input character stream. + * This implementation guarantees that it will read as many characters + * as possible before giving up; this may not always be the case for + * subclasses of {@link Reader}. + * + * @param input where to read input from + * @param buffer destination + * @param offset inital offset into buffer + * @param length length to read, must be >= 0 + * @return actual length read; may be less than requested if EOF was reached + * @throws IOException if a read error occurs + * @since 2.2 + */ + public static int read(Reader input, char[] buffer, int offset, int length) throws IOException { + if (length < 0) { + throw new IllegalArgumentException("Length must not be negative: " + length); + } + int remaining = length; + while (remaining > 0) { + int location = length - remaining; + int count = input.read(buffer, offset + location, remaining); + if (EOF == count) { // EOF + break; + } + remaining -= count; + } + return length - remaining; + } + + /** + * Read characters from an input character stream. + * This implementation guarantees that it will read as many characters + * as possible before giving up; this may not always be the case for + * subclasses of {@link Reader}. + * + * @param input where to read input from + * @param buffer destination + * @return actual length read; may be less than requested if EOF was reached + * @throws IOException if a read error occurs + * @since 2.2 + */ + public static int read(Reader input, char[] buffer) throws IOException { + return read(input, buffer, 0, buffer.length); + } + + /** + * Read bytes from an input stream. + * This implementation guarantees that it will read as many bytes + * as possible before giving up; this may not always be the case for + * subclasses of {@link InputStream}. + * + * @param input where to read input from + * @param buffer destination + * @param offset inital offset into buffer + * @param length length to read, must be >= 0 + * @return actual length read; may be less than requested if EOF was reached + * @throws IOException if a read error occurs + * @since 2.2 + */ + public static int read(InputStream input, byte[] buffer, int offset, int length) throws IOException { + if (length < 0) { + throw new IllegalArgumentException("Length must not be negative: " + length); + } + int remaining = length; + while (remaining > 0) { + int location = length - remaining; + int count = input.read(buffer, offset + location, remaining); + if (EOF == count) { // EOF + break; + } + remaining -= count; + } + return length - remaining; + } + + /** + * Read bytes from an input stream. + * This implementation guarantees that it will read as many bytes + * as possible before giving up; this may not always be the case for + * subclasses of {@link InputStream}. + * + * @param input where to read input from + * @param buffer destination + * @return actual length read; may be less than requested if EOF was reached + * @throws IOException if a read error occurs + * @since 2.2 + */ + public static int read(InputStream input, byte[] buffer) throws IOException { + return read(input, buffer, 0, buffer.length); + } + + /** + * Read the requested number of characters or fail if there are not enough left. + *

+ * This allows for the possibility that {@link Reader#read(char[], int, int)} may + * not read as many characters as requested (most likely because of reaching EOF). + * + * @param input where to read input from + * @param buffer destination + * @param offset inital offset into buffer + * @param length length to read, must be >= 0 + * @throws IOException if there is a problem reading the file + * @throws IllegalArgumentException if length is negative + * @throws EOFException if the number of characters read was incorrect + * @since 2.2 + */ + public static void readFully(Reader input, char[] buffer, int offset, int length) throws IOException { + int actual = read(input, buffer, offset, length); + if (actual != length) { + throw new EOFException("Length to read: " + length + " actual: " + actual); + } + } + + /** + * Read the requested number of characters or fail if there are not enough left. + *

+ * This allows for the possibility that {@link Reader#read(char[], int, int)} may + * not read as many characters as requested (most likely because of reaching EOF). + * + * @param input where to read input from + * @param buffer destination + * @throws IOException if there is a problem reading the file + * @throws IllegalArgumentException if length is negative + * @throws EOFException if the number of characters read was incorrect + * @since 2.2 + */ + public static void readFully(Reader input, char[] buffer) throws IOException { + readFully(input, buffer, 0, buffer.length); + } + + /** + * Read the requested number of bytes or fail if there are not enough left. + *

+ * This allows for the possibility that {@link InputStream#read(byte[], int, int)} may + * not read as many bytes as requested (most likely because of reaching EOF). + * + * @param input where to read input from + * @param buffer destination + * @param offset inital offset into buffer + * @param length length to read, must be >= 0 + * @throws IOException if there is a problem reading the file + * @throws IllegalArgumentException if length is negative + * @throws EOFException if the number of bytes read was incorrect + * @since 2.2 + */ + public static void readFully(InputStream input, byte[] buffer, int offset, int length) throws IOException { + int actual = read(input, buffer, offset, length); + if (actual != length) { + throw new EOFException("Length to read: " + length + " actual: " + actual); + } + } + + /** + * Read the requested number of bytes or fail if there are not enough left. + *

+ * This allows for the possibility that {@link InputStream#read(byte[], int, int)} may + * not read as many bytes as requested (most likely because of reaching EOF). + * + * @param input where to read input from + * @param buffer destination + * @throws IOException if there is a problem reading the file + * @throws IllegalArgumentException if length is negative + * @throws EOFException if the number of bytes read was incorrect + * @since 2.2 + */ + public static void readFully(InputStream input, byte[] buffer) throws IOException { + readFully(input, buffer, 0, buffer.length); + } +} diff --git a/pay-java-common/src/main/java/com/egzosn/pay/common/util/sign/SecureUtil.java b/pay-java-common/src/main/java/com/egzosn/pay/common/util/sign/SecureUtil.java index 096442b..63cbc1b 100644 --- a/pay-java-common/src/main/java/com/egzosn/pay/common/util/sign/SecureUtil.java +++ b/pay-java-common/src/main/java/com/egzosn/pay/common/util/sign/SecureUtil.java @@ -60,7 +60,7 @@ public class SecureUtil { */ public static byte[] sha1X16 (String data, String encoding) { try { - byte[] bytes = digestByData(data.getBytes(encoding),ALGORITHM_SHA1); + byte[] bytes = digestByData(data.getBytes(encoding), ALGORITHM_SHA1); StringBuilder sha1StrBuff = new StringBuilder(); for (int i = 0; i < bytes.length; i++) { if (Integer.toHexString(0xFF & bytes[i]).length() == 1) { @@ -91,7 +91,7 @@ public class SecureUtil { public static String sha256X16Str(String data, String encoding) { byte[] bytes =null; try { - bytes = digestByData(data.getBytes(encoding),ALGORITHM_SHA1); + bytes = digestByData(data.getBytes(encoding), ALGORITHM_SHA1); } catch (UnsupportedEncodingException e) { throw new PayErrorException(new PayException("error", e.getLocalizedMessage())); } -- Gitee From a1be9c0dfd08d2d124ae41389823d01498d6cba4 Mon Sep 17 00:00:00 2001 From: egzosn Date: Mon, 26 Oct 2020 22:58:28 +0800 Subject: [PATCH 148/299] =?UTF-8?q?=E5=BE=AE=E4=BF=A1=E6=B2=99=E7=9B=92?= =?UTF-8?q?=E6=B5=8B=E8=AF=95=E6=B5=81=E7=A8=8B=E4=B8=AD=EF=BC=8C=E5=8F=91?= =?UTF-8?q?=E9=80=81xml=E6=A0=BC=E5=BC=8F=E7=9A=84=E5=8F=82=E6=95=B0?= =?UTF-8?q?=E8=AF=B7=E6=B1=82application/xml?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../pay/common/http/ClientHttpRequest.java | 1 + .../com/egzosn/pay/wx/api/WxPayService.java | 70 +++++++++++++++---- 2 files changed, 56 insertions(+), 15 deletions(-) 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 bfc799b..f1b0aba 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 @@ -38,6 +38,7 @@ import static com.egzosn.pay.common.http.UriVariables.getMapToParameters; public class ClientHttpRequest extends HttpEntityEnclosingRequestBase implements org.apache.http.client.ResponseHandler { protected static final Log LOG = LogFactory.getLog(ClientHttpRequest.class); public static final ContentType APPLICATION_FORM_URLENCODED_UTF_8 = ContentType.create("application/x-www-form-urlencoded", Consts.UTF_8); + public static final ContentType APPLICATION_XML_UTF_8 = ContentType.create("application/xml", Consts.UTF_8); /** 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 9d86e36..ce72660 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 @@ -1,30 +1,69 @@ package com.egzosn.pay.wx.api; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.net.URLEncoder; +import java.security.GeneralSecurityException; +import java.util.Date; +import java.util.HashMap; +import java.util.Map; +import java.util.SortedMap; +import java.util.TreeMap; +import java.util.zip.GZIPInputStream; + +import static com.egzosn.pay.wx.api.WxConst.APPID; +import static com.egzosn.pay.wx.api.WxConst.CIPHER_ALGORITHM; +import static com.egzosn.pay.wx.api.WxConst.FAIL; +import static com.egzosn.pay.wx.api.WxConst.FAILURE; +import static com.egzosn.pay.wx.api.WxConst.HMACSHA256; +import static com.egzosn.pay.wx.api.WxConst.HMAC_SHA256; +import static com.egzosn.pay.wx.api.WxConst.MCH_ID; +import static com.egzosn.pay.wx.api.WxConst.NONCE_STR; +import static com.egzosn.pay.wx.api.WxConst.OUT_TRADE_NO; +import static com.egzosn.pay.wx.api.WxConst.RESULT_CODE; +import static com.egzosn.pay.wx.api.WxConst.RETURN_CODE; +import static com.egzosn.pay.wx.api.WxConst.RETURN_MSG_CODE; +import static com.egzosn.pay.wx.api.WxConst.SANDBOXNEW; +import static com.egzosn.pay.wx.api.WxConst.SIGN; +import static com.egzosn.pay.wx.api.WxConst.SUCCESS; +import static com.egzosn.pay.wx.api.WxConst.URI; +import static com.egzosn.pay.wx.bean.WxTransferType.GETTRANSFERINFO; +import static com.egzosn.pay.wx.bean.WxTransferType.QUERY_BANK; +import static com.egzosn.pay.wx.bean.WxTransferType.TRANSFERS; + import com.alibaba.fastjson.JSONObject; import com.egzosn.pay.common.api.BasePayService; -import com.egzosn.pay.common.bean.*; +import com.egzosn.pay.common.bean.MethodType; +import com.egzosn.pay.common.bean.PayMessage; +import com.egzosn.pay.common.bean.PayOrder; +import com.egzosn.pay.common.bean.PayOutMessage; +import com.egzosn.pay.common.bean.RefundOrder; +import com.egzosn.pay.common.bean.SignType; +import com.egzosn.pay.common.bean.TransactionType; +import com.egzosn.pay.common.bean.TransferOrder; import com.egzosn.pay.common.bean.result.PayException; import com.egzosn.pay.common.exception.PayErrorException; +import com.egzosn.pay.common.http.ClientHttpRequest; import com.egzosn.pay.common.http.HttpConfigStorage; +import com.egzosn.pay.common.http.HttpStringEntity; import com.egzosn.pay.common.util.DateUtils; import com.egzosn.pay.common.util.Util; import com.egzosn.pay.common.util.XML; import com.egzosn.pay.common.util.sign.SignUtils; import com.egzosn.pay.common.util.sign.encrypt.RSA2; import com.egzosn.pay.common.util.str.StringUtils; -import com.egzosn.pay.wx.bean.*; - -import java.io.*; -import java.net.URLEncoder; -import java.security.GeneralSecurityException; -import java.util.*; -import java.util.zip.GZIPInputStream; -import java.util.zip.GZIPOutputStream; -import java.util.zip.ZipEntry; -import java.util.zip.ZipInputStream; - -import static com.egzosn.pay.wx.api.WxConst.*; -import static com.egzosn.pay.wx.bean.WxTransferType.*; +import com.egzosn.pay.wx.bean.RedpackOrder; +import com.egzosn.pay.wx.bean.WxPayError; +import com.egzosn.pay.wx.bean.WxPayMessage; +import com.egzosn.pay.wx.bean.WxRefundResult; +import com.egzosn.pay.wx.bean.WxSendredpackType; +import com.egzosn.pay.wx.bean.WxTransactionType; +import com.egzosn.pay.wx.bean.WxTransferType; /** * 微信支付服务 @@ -305,7 +344,8 @@ public class WxPayService extends BasePayService implements String sign = createSign(SignUtils.parameterText(parameters, "&", SIGN, "appId"), payConfigStorage.getInputCharset(), false); parameters.put(SIGN, sign); - JSONObject result = requestTemplate.postForObject(getReqUrl(WxTransactionType.GETSIGNKEY), XML.getMap2Xml(parameters), JSONObject.class); + HttpStringEntity entity = new HttpStringEntity(XML.getMap2Xml(parameters), ClientHttpRequest.APPLICATION_XML_UTF_8); + JSONObject result = requestTemplate.postForObject(getReqUrl(WxTransactionType.GETSIGNKEY), entity, JSONObject.class); if (SUCCESS.equals(result.get(RETURN_CODE))) { return result.getString("sandbox_signkey"); } -- Gitee From dea01ea02054797be7baf5ded34d28004663ee7e Mon Sep 17 00:00:00 2001 From: wangshirui Date: Wed, 28 Oct 2020 18:52:33 +0800 Subject: [PATCH 149/299] =?UTF-8?q?Fix:=20=E5=BE=AE=E4=BF=A1=E7=BB=9F?= =?UTF-8?q?=E4=B8=80=E4=B8=8B=E5=8D=95=E6=8E=A5=E5=8F=A3=E8=AF=B7=E6=B1=82?= =?UTF-8?q?=E5=8F=82=E6=95=B0=E6=A0=BC=E5=BC=8F=E9=94=99=E8=AF=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/main/java/com/egzosn/pay/wx/api/WxPayService.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) 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 ce72660..661ebb5 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 @@ -256,8 +256,10 @@ public class WxPayService extends BasePayService implements LOG.debug("requestXML:" + requestXML); } + HttpStringEntity entity = new HttpStringEntity(requestXML, ClientHttpRequest.APPLICATION_XML_UTF_8); + //调起支付的参数列表 - JSONObject result = requestTemplate.postForObject(getReqUrl(order.getTransactionType()), requestXML, JSONObject.class); + JSONObject result = requestTemplate.postForObject(getReqUrl(order.getTransactionType()), entity, JSONObject.class); if (!SUCCESS.equals(result.get(RETURN_CODE)) || !SUCCESS.equals(result.get(RESULT_CODE))) { throw new PayErrorException(new WxPayError(result.getString(RESULT_CODE), result.getString(RETURN_MSG_CODE), result.toJSONString())); -- Gitee From a3f5fe80164054d33fc3db7b7295301fd38c62cf Mon Sep 17 00:00:00 2001 From: egzosn Date: Thu, 12 Nov 2020 22:15:14 +0800 Subject: [PATCH 150/299] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E8=AF=81=E4=B9=A6IO?= =?UTF-8?q?=E8=AF=BB=E5=8F=96=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/egzosn/pay/ali/api/AliPayConfigStorage.java | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) 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 cc2028e..cad2cfe 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 @@ -41,7 +41,6 @@ public class AliPayConfigStorage extends BasePayConfigStorage { private String seller; - /** * 应用公钥证书 */ @@ -147,16 +146,14 @@ public class AliPayConfigStorage extends BasePayConfigStorage { * 初始化证书信息 */ public void loadCertEnvironment() { - if (isCertSign() && null != this.certEnvironment){ + if (!isCertSign() || null == this.certEnvironment){ return; } - try (InputStream merchantCertStream = certStoreType.getInputStream(merchantCert); - InputStream aliPayCertStream = certStoreType.getInputStream(aliPayCert); - InputStream aliPayRootCertStream = certStoreType.getInputStream(aliPayRootCert); - ){ + try (InputStream merchantCertStream = certStoreType.getInputStream(merchantCert); + InputStream aliPayCertStream = certStoreType.getInputStream(aliPayCert); + InputStream aliPayRootCertStream = certStoreType.getInputStream(aliPayRootCert)) { this.certEnvironment = new CertEnvironment(merchantCertStream, aliPayCertStream, aliPayRootCertStream); - } - catch (IOException e) { + } catch (IOException e) { throw new PayErrorException(new PayException("读取证书异常", e.getMessage())); } } -- Gitee From 012b71739de1dd41bbd3ec552c999939d9fc32d6 Mon Sep 17 00:00:00 2001 From: egzosn Date: Fri, 13 Nov 2020 22:29:47 +0800 Subject: [PATCH 151/299] .. --- pay-java-ali/README.md | 2 +- pay-java-ali/src/test/java/PayTest.java | 2 +- .../java/com/egzosn/pay/common/util/Util.java | 2 +- pay-java-demo/README.md | 6 +++--- .../pay/demo/controller/AliPayController.java | 14 +++++++------- .../pay/demo/controller/FuiouPayController.java | 2 +- .../pay/demo/controller/PayController.java | 16 ++++++++-------- .../demo/controller/PayPalPayController.java | 4 ++-- .../pay/demo/controller/UnionPayController.java | 14 +++++++------- .../pay/demo/controller/WxPayController.java | 17 ++++++++--------- pay-java-fuiou/README.md | 2 +- pay-java-fuiou/src/test/java/PayTest.java | 2 +- pay-java-union/README.md | 2 +- pay-java-union/src/test/java/PayTest.java | 2 +- pay-java-wx-youdian/README.md | 2 +- pay-java-wx-youdian/src/test/java/PayTest.java | 2 +- pay-java-wx/README.md | 4 ++-- pay-java-wx/src/test/java/PayTest.java | 2 +- 18 files changed, 48 insertions(+), 49 deletions(-) diff --git a/pay-java-ali/README.md b/pay-java-ali/README.md index aebefc7..0f27875 100644 --- a/pay-java-ali/README.md +++ b/pay-java-ali/README.md @@ -109,7 +109,7 @@ ```java //支付订单基础信息 - PayOrder payOrder = new PayOrder("订单title", "摘要", new BigDecimal(0.01) , UUID.randomUUID().toString().replace("-", "")); + PayOrder payOrder = new PayOrder("订单title", "摘要", BigDecimal.valueOf(0.01) , UUID.randomUUID().toString().replace("-", "")); ``` diff --git a/pay-java-ali/src/test/java/PayTest.java b/pay-java-ali/src/test/java/PayTest.java index 244297e..c098259 100644 --- a/pay-java-ali/src/test/java/PayTest.java +++ b/pay-java-ali/src/test/java/PayTest.java @@ -65,7 +65,7 @@ public class PayTest { //支付服务 PayService service = new AliPayService(aliPayConfigStorage); //支付订单基础信息 - PayOrder payOrder = new PayOrder("订单title", "摘要", new BigDecimal(0.01) , UUID.randomUUID().toString().replace("-", "")); + PayOrder payOrder = new PayOrder("订单title", "摘要", BigDecimal.valueOf(0.01) , UUID.randomUUID().toString().replace("-", "")); /*-----------扫码付-------------------*/ payOrder.setTransactionType(AliTransactionType.SWEEPPAY); //获取扫码付的二维码 diff --git a/pay-java-common/src/main/java/com/egzosn/pay/common/util/Util.java b/pay-java-common/src/main/java/com/egzosn/pay/common/util/Util.java index e67ce7e..e8c2b14 100644 --- a/pay-java-common/src/main/java/com/egzosn/pay/common/util/Util.java +++ b/pay-java-common/src/main/java/com/egzosn/pay/common/util/Util.java @@ -451,7 +451,7 @@ public class Util { * @param algorism 十进制的数字 * @return String 对应的十六进制字符串 */ - public static String algorismToHEXString(int algorism) { + public static String algorismToHexString(int algorism) { String result = ""; result = Integer.toHexString(algorism); diff --git a/pay-java-demo/README.md b/pay-java-demo/README.md index 0f00886..d6b9a7e 100644 --- a/pay-java-demo/README.md +++ b/pay-java-demo/README.md @@ -334,7 +334,7 @@ public class ApyAccountService { //获取对应的支付账户操作工具(可根据账户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)); + PayOrder order = new PayOrder("订单title", "摘要", null == price ? BigDecimal.valueOf(0.01) : price, UUID.randomUUID().toString().replace("-", ""), PayType.valueOf(payResponse.getStorage().getPayType()).getTransactionType(transactionType)); //此处只有刷卡支付(银行卡支付)时需要 if (StringUtils.isNotEmpty(bankType)){ @@ -355,7 +355,7 @@ public class ApyAccountService { //获取对应的支付账户操作工具(可根据账户id) PayResponse payResponse = service.getPayResponse(payId); //获取订单信息 - Map orderInfo = payResponse.getService().orderInfo(new PayOrder("订单title", "摘要", null == price ? new BigDecimal(0.01) : price, UUID.randomUUID().toString().replace("-", ""), PayType.valueOf(payResponse.getStorage().getPayType()).getTransactionType(transactionType))); + Map orderInfo = payResponse.getService().orderInfo(new PayOrder("订单title", "摘要", null == price ? BigDecimal.valueOf(0.01) : price, UUID.randomUUID().toString().replace("-", ""), PayType.valueOf(payResponse.getStorage().getPayType()).getTransactionType(transactionType))); ByteArrayOutputStream baos = new ByteArrayOutputStream(); ImageIO.write(payResponse.getService().genQrPay(orderInfo), "JPEG", baos); @@ -376,7 +376,7 @@ public class ApyAccountService { PayResponse payResponse = service.getPayResponse(payId); Map data = new HashMap<>(); data.put("code", 0); - PayOrder order = new PayOrder("订单title", "摘要", null == price ? new BigDecimal(0.01) : price, UUID.randomUUID().toString().replace("-", ""), PayType.valueOf(payResponse.getStorage().getPayType()).getTransactionType(transactionType)); + PayOrder order = new PayOrder("订单title", "摘要", null == price ? BigDecimal.valueOf(0.01) : price, UUID.randomUUID().toString().replace("-", ""), PayType.valueOf(payResponse.getStorage().getPayType()).getTransactionType(transactionType)); data.put("orderInfo", payResponse.getService().orderInfo(order)); return data; } diff --git a/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/AliPayController.java b/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/AliPayController.java index b71161f..c0a918a 100644 --- a/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/AliPayController.java +++ b/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/AliPayController.java @@ -115,9 +115,9 @@ public class AliPayController { @RequestMapping(value = "toPay.html", produces = "text/html;charset=UTF-8") public String toPay( BigDecimal price) { //及时收款 - PayOrder order = new PayOrder("订单title", "摘要", null == price ? new BigDecimal(0.01) : price, UUID.randomUUID().toString().replace("-", ""), AliTransactionType.PAGE); + PayOrder order = new PayOrder("订单title", "摘要", null == price ? BigDecimal.valueOf(0.01) : price, UUID.randomUUID().toString().replace("-", ""), AliTransactionType.PAGE); //WAP -// PayOrder order = new PayOrder("订单title", "摘要", null == price ? new BigDecimal(0.01) : price, UUID.randomUUID().toString().replace("-", ""), AliTransactionType.WAP); +// PayOrder order = new PayOrder("订单title", "摘要", null == price ? BigDecimal.valueOf(0.01) : price, UUID.randomUUID().toString().replace("-", ""), AliTransactionType.WAP); // Map orderInfo = service.orderInfo(order); // return service.buildRequest(orderInfo, MethodType.POST); @@ -137,7 +137,7 @@ public class AliPayController { public Map app() { Map data = new HashMap<>(); data.put("code", 0); - PayOrder order = new PayOrder("订单title", "摘要", new BigDecimal(0.01), UUID.randomUUID().toString().replace("-", "")); + PayOrder order = new PayOrder("订单title", "摘要", BigDecimal.valueOf(0.01), UUID.randomUUID().toString().replace("-", "")); //App支付 order.setTransactionType(AliTransactionType.APP); data.put("orderInfo", UriVariables.getMapToParameters(service.app(order))); @@ -155,7 +155,7 @@ public class AliPayController { public byte[] toQrPay( BigDecimal price) throws IOException { //获取对应的支付账户操作工具(可根据账户id) ByteArrayOutputStream baos = new ByteArrayOutputStream(); - ImageIO.write(service.genQrPay( new PayOrder("订单title", "摘要", null == price ? new BigDecimal(0.01) : price, System.currentTimeMillis()+"", AliTransactionType.SWEEPPAY)), "JPEG", baos); + ImageIO.write(service.genQrPay( new PayOrder("订单title", "摘要", null == price ? BigDecimal.valueOf(0.01) : price, System.currentTimeMillis()+"", AliTransactionType.SWEEPPAY)), "JPEG", baos); return baos.toByteArray(); } /** @@ -168,7 +168,7 @@ public class AliPayController { @RequestMapping(value = "getQrPay.json") public String getQrPay(BigDecimal price) throws IOException { //获取对应的支付账户操作工具(可根据账户id) - return service.getQrPay( new PayOrder("订单title", "摘要", null == price ? new BigDecimal(0.01) : price, System.currentTimeMillis()+"", AliTransactionType.SWEEPPAY)); + return service.getQrPay( new PayOrder("订单title", "摘要", null == price ? BigDecimal.valueOf(0.01) : price, System.currentTimeMillis()+"", AliTransactionType.SWEEPPAY)); } @@ -182,9 +182,9 @@ public class AliPayController { public Map microPay(BigDecimal price, String authCode) { //获取对应的支付账户操作工具(可根据账户id) //条码付 - PayOrder order = new PayOrder("egan order", "egan order", null == price ? new BigDecimal(0.01) : price, UUID.randomUUID().toString().replace("-", ""), AliTransactionType.BAR_CODE); + PayOrder order = new PayOrder("egan order", "egan order", null == price ? BigDecimal.valueOf(0.01) : price, UUID.randomUUID().toString().replace("-", ""), AliTransactionType.BAR_CODE); //声波付 -// PayOrder order = new PayOrder("egan order", "egan order", null == price ? new BigDecimal(0.01) : price, UUID.randomUUID().toString().replace("-", ""), AliTransactionType.WAVE_CODE); +// PayOrder order = new PayOrder("egan order", "egan order", null == price ? BigDecimal.valueOf(0.01) : price, UUID.randomUUID().toString().replace("-", ""), AliTransactionType.WAVE_CODE); //设置授权码,条码等 order.setAuthCode(authCode); //支付结果 diff --git a/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/FuiouPayController.java b/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/FuiouPayController.java index 06d1114..e3e79a0 100644 --- a/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/FuiouPayController.java +++ b/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/FuiouPayController.java @@ -65,7 +65,7 @@ public class FuiouPayController { @RequestMapping(value = "toPay.html", produces = "text/html;charset=UTF-8") public String toPay( BigDecimal price) { //支付订单基础信息 - PayOrder order = new PayOrder("订单title", "摘要", new BigDecimal(0.01) , UUID.randomUUID().toString().replace("-", "").substring(2)); + PayOrder order = new PayOrder("订单title", "摘要", BigDecimal.valueOf(0.01) , UUID.randomUUID().toString().replace("-", "").substring(2)); order.setTransactionType(FuiouTransactionType.B2C); //获取支付所需的信息 // Map directOrderInfo = service.orderInfo(order); 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 bd998bc..8c4f2fc 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 @@ -88,7 +88,7 @@ public class PayController { //获取对应的支付账户操作工具(可根据账户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)); + PayOrder order = new PayOrder("订单title", "摘要", null == price ? BigDecimal.valueOf(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(); @@ -123,7 +123,7 @@ public class PayController { //获取对应的支付账户操作工具(可根据账户id) PayResponse payResponse = service.getPayResponse(2); - PayOrder order = new PayOrder("订单title", "摘要", new BigDecimal(0.01), UUID.randomUUID().toString().replace("-", ""), WxTransactionType.MWEB); + PayOrder order = new PayOrder("订单title", "摘要", BigDecimal.valueOf(0.01), UUID.randomUUID().toString().replace("-", ""), WxTransactionType.MWEB); order.setSpbillCreateIp(request.getHeader("X-Real-IP")); StringBuffer requestURL = request.getRequestURL(); //设置网页地址 @@ -150,7 +150,7 @@ public class PayController { //获取对应的支付账户操作工具(可根据账户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("JSAPI")); + PayOrder order = new PayOrder("订单title", "摘要", null == price ? BigDecimal.valueOf(0.01) : price, UUID.randomUUID().toString().replace("-", ""), PayType.valueOf(payResponse.getStorage().getPayType()).getTransactionType("JSAPI")); order.setOpenid(openid); Map orderInfo = payResponse.getService().orderInfo(order); @@ -173,7 +173,7 @@ public class PayController { PayResponse payResponse = service.getPayResponse(payId); Map data = new HashMap<>(); data.put("code", 0); - PayOrder order = new PayOrder("订单title", "摘要", null == price ? new BigDecimal(0.01) : price, UUID.randomUUID().toString().replace("-", ""), PayType.valueOf(payResponse.getStorage().getPayType()).getTransactionType(transactionType)); + PayOrder order = new PayOrder("订单title", "摘要", null == price ? BigDecimal.valueOf(0.01) : price, UUID.randomUUID().toString().replace("-", ""), PayType.valueOf(payResponse.getStorage().getPayType()).getTransactionType(transactionType)); data.put("orderInfo", payResponse.getService().app(order)); return data; } @@ -192,7 +192,7 @@ public class PayController { //获取对应的支付账户操作工具(可根据账户id) PayResponse payResponse = service.getPayResponse(payId); - PayOrder order = new PayOrder("egan order", "egan order", null == price ? new BigDecimal(0.01) : price, UUID.randomUUID().toString().replace("-", ""), PayType.valueOf(payResponse.getStorage().getPayType()).getTransactionType(transactionType)); + PayOrder order = new PayOrder("egan order", "egan order", null == price ? BigDecimal.valueOf(0.01) : price, UUID.randomUUID().toString().replace("-", ""), PayType.valueOf(payResponse.getStorage().getPayType()).getTransactionType(transactionType)); //设置授权码,条码等 order.setAuthCode(authCode); //支付结果 @@ -225,7 +225,7 @@ public class PayController { PayResponse payResponse = service.getPayResponse(payId); ByteArrayOutputStream baos = new ByteArrayOutputStream(); - ImageIO.write(payResponse.getService().genQrPay(new PayOrder("订单title", "摘要", null == price ? new BigDecimal(0.01) : price, System.currentTimeMillis() + "", PayType.valueOf(payResponse.getStorage().getPayType()).getTransactionType(transactionType))), "JPEG", baos); + ImageIO.write(payResponse.getService().genQrPay(new PayOrder("订单title", "摘要", null == price ? BigDecimal.valueOf(0.01) : price, System.currentTimeMillis() + "", PayType.valueOf(payResponse.getStorage().getPayType()).getTransactionType(transactionType))), "JPEG", baos); return baos.toByteArray(); } /** @@ -240,7 +240,7 @@ public class PayController { //获取对应的支付账户操作工具(可根据账户id) //获取对应的支付账户操作工具(可根据账户id) PayResponse payResponse = service.getPayResponse(payId); - return payResponse.getService().getQrPay( new PayOrder("订单title", "摘要", null == price ? new BigDecimal(0.01) : price, System.currentTimeMillis() + "", PayType.valueOf(payResponse.getStorage().getPayType()).getTransactionType(transactionType))); + return payResponse.getService().getQrPay( new PayOrder("订单title", "摘要", null == price ? BigDecimal.valueOf(0.01) : price, System.currentTimeMillis() + "", PayType.valueOf(payResponse.getStorage().getPayType()).getTransactionType(transactionType))); } /** * 获取一码付二维码图像 @@ -288,7 +288,7 @@ public class PayController { StringBuilder html = new StringBuilder(); //订单 - PayOrder payOrder = new PayOrder("订单title", "摘要", null == price ? new BigDecimal(0.01) : price, System.currentTimeMillis() + ""); + PayOrder payOrder = new PayOrder("订单title", "摘要", null == price ? BigDecimal.valueOf(0.01) : price, System.currentTimeMillis() + ""); String ua = request.getHeader("user-agent"); if (ua.contains("MicroMessenger")) { payOrder.setTransactionType(WxTransactionType.NATIVE); diff --git a/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/PayPalPayController.java b/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/PayPalPayController.java index fe84f00..bd4e344 100644 --- a/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/PayPalPayController.java +++ b/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/PayPalPayController.java @@ -68,7 +68,7 @@ public class PayPalPayController { @RequestMapping(value = "toPay.html", produces = "text/html;charset=UTF-8") public String toPay(BigDecimal price) { //及时收款 - PayOrder order = new PayOrder("订单title", "摘要", null == price ? new BigDecimal(0.01) : price, UUID.randomUUID().toString().replace("-", ""), PayPalTransactionType.sale); + PayOrder order = new PayOrder("订单title", "摘要", null == price ? BigDecimal.valueOf(0.01) : price, UUID.randomUUID().toString().replace("-", ""), PayPalTransactionType.sale); // Map orderInfo = service.orderInfo(order); // return service.buildRequest(orderInfo, MethodType.POST); @@ -93,7 +93,7 @@ public class PayPalPayController { order.setCurType(DefaultCurType.USD); order.setDescription(" description "); order.setTradeNo("paypal 平台的单号"); - order.setRefundAmount(new BigDecimal(0.01)); + order.setRefundAmount(BigDecimal.valueOf(0.01)); return service.refund(order); } diff --git a/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/UnionPayController.java b/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/UnionPayController.java index 8c1e6e5..978c90c 100644 --- a/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/UnionPayController.java +++ b/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/UnionPayController.java @@ -97,10 +97,10 @@ public class UnionPayController { @RequestMapping(value = "toPay.html", produces = "text/html;charset=UTF-8") public String toPay( BigDecimal price) { //网关支付(WEB)/手机网页支付 - PayOrder order = new PayOrder("订单title", "摘要", null == price ? new BigDecimal(0.01) : price, UUID.randomUUID().toString().replace("-", ""), + PayOrder order = new PayOrder("订单title", "摘要", null == price ? BigDecimal.valueOf(0.01) : price, UUID.randomUUID().toString().replace("-", ""), WEB); //企业网银支付(B2B支付) -// PayOrder order = new PayOrder("订单title", "摘要", null == price ? new BigDecimal(0.01) : price, UUID.randomUUID().toString().replace("-", ""), UnionTransactionType.B2B); +// PayOrder order = new PayOrder("订单title", "摘要", null == price ? BigDecimal.valueOf(0.01) : price, UUID.randomUUID().toString().replace("-", ""), UnionTransactionType.B2B); // Map orderInfo = service.orderInfo(order); // return service.buildRequest(orderInfo, MethodType.POST); @@ -117,7 +117,7 @@ public class UnionPayController { @RequestMapping(value = "toPay.json") public Map sendHttpRequest( BigDecimal price) { //手机控件支付产品 - PayOrder order = new PayOrder("订单title", "摘要", null == price ? new BigDecimal(0.01) : price, UUID.randomUUID().toString().replace("-", "") + PayOrder order = new PayOrder("订单title", "摘要", null == price ? BigDecimal.valueOf(0.01) : price, UUID.randomUUID().toString().replace("-", "") ,UnionTransactionType.WAP); return service.app(order); } @@ -133,7 +133,7 @@ public class UnionPayController { public Map app() { Map data = new HashMap<>(); data.put("code", 0); - PayOrder order = new PayOrder("订单title", "摘要", new BigDecimal(0.01), SignUtils.randomStr()); + PayOrder order = new PayOrder("订单title", "摘要", BigDecimal.valueOf(0.01), SignUtils.randomStr()); //App支付 order.setTransactionType(UnionTransactionType.APP); @@ -155,7 +155,7 @@ public class UnionPayController { public byte[] toWxQrPay( BigDecimal price) throws IOException { //获取对应的支付账户操作工具(可根据账户id) ByteArrayOutputStream baos = new ByteArrayOutputStream(); - ImageIO.write(service.genQrPay( new PayOrder("订单title", "摘要", null == price ? new BigDecimal(0.01) : price, System.currentTimeMillis()+"", UnionTransactionType.APPLY_QR_CODE)), "JPEG", baos); + ImageIO.write(service.genQrPay( new PayOrder("订单title", "摘要", null == price ? BigDecimal.valueOf(0.01) : price, System.currentTimeMillis()+"", UnionTransactionType.APPLY_QR_CODE)), "JPEG", baos); return baos.toByteArray(); } /** @@ -168,7 +168,7 @@ public class UnionPayController { @RequestMapping(value = "getQrPay.json") public String getQrPay(BigDecimal price) throws IOException { //获取对应的支付账户操作工具(可根据账户id) - return service.getQrPay( new PayOrder("订单title", "摘要", null == price ? new BigDecimal(0.01) : price, System.currentTimeMillis()+"", UnionTransactionType.APPLY_QR_CODE)); + return service.getQrPay( new PayOrder("订单title", "摘要", null == price ? BigDecimal.valueOf(0.01) : price, System.currentTimeMillis()+"", UnionTransactionType.APPLY_QR_CODE)); } /** @@ -181,7 +181,7 @@ public class UnionPayController { public Map microPay(BigDecimal price, String authCode) { //获取对应的支付账户操作工具(可根据账户id) //条码付 - PayOrder order = new PayOrder("egan order", "egan order", null == price ? new BigDecimal(0.01) : price, SignUtils.randomStr(), UnionTransactionType.CONSUME); + PayOrder order = new PayOrder("egan order", "egan order", null == price ? BigDecimal.valueOf(0.01) : price, SignUtils.randomStr(), UnionTransactionType.CONSUME); //设置授权码,条码等 order.setAuthCode(authCode); //支付结果 diff --git a/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/WxPayController.java b/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/WxPayController.java index a554c73..1269ebc 100644 --- a/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/WxPayController.java +++ b/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/WxPayController.java @@ -38,7 +38,6 @@ public class WxPayController { //ssl 退款证书相关 不使用可注释 private static String KEYSTORE = "ssl 退款证书"; - private static String STORE_PASSWORD = "ssl 证书对应的密码, 默认为商户号"; @PostConstruct public void init() { @@ -67,7 +66,7 @@ public class WxPayController { //TODO 这里也支持输入流的入参。 // httpConfigStorage.setKeystore(WxPayController.class.getResourceAsStream("/证书文件")); httpConfigStorage.setKeystore(KEYSTORE); - httpConfigStorage.setStorePassword(STORE_PASSWORD); + httpConfigStorage.setStorePassword("ssl 证书对应的密码, 默认为商户号"); //设置ssl证书对应的存储方式,这里默认为文件地址 httpConfigStorage.setCertStoreType(CertStoreType.PATH); } @@ -97,7 +96,7 @@ public class WxPayController { */ @RequestMapping(value = "toPay.html", produces = "text/html;charset=UTF-8") public String toPay( HttpServletRequest request, BigDecimal price) { - PayOrder order = new PayOrder("订单title", "摘要", null == price ? new BigDecimal(0.01) : price , UUID.randomUUID().toString().replace("-", ""), WxTransactionType.MWEB); + PayOrder order = new PayOrder("订单title", "摘要", null == price ? BigDecimal.valueOf(0.01) : price , UUID.randomUUID().toString().replace("-", ""), WxTransactionType.MWEB); order.setSpbillCreateIp(request.getHeader("X-Real-IP")); StringBuffer requestURL = request.getRequestURL(); //设置网页地址 @@ -121,7 +120,7 @@ public class WxPayController { @RequestMapping(value = "jsapi" ) public Map toPay(String openid, BigDecimal price) { - PayOrder order = new PayOrder("订单title", "摘要", null == price ? new BigDecimal(0.01) : price, UUID.randomUUID().toString().replace("-", ""), WxTransactionType.JSAPI); + PayOrder order = new PayOrder("订单title", "摘要", null == price ? BigDecimal.valueOf(0.01) : price, UUID.randomUUID().toString().replace("-", ""), WxTransactionType.JSAPI); order.setOpenid(openid); Map orderInfo = service.orderInfo(order); @@ -141,7 +140,7 @@ public class WxPayController { public Map app() { Map data = new HashMap<>(); data.put("code", 0); - PayOrder order = new PayOrder("订单title", "摘要", new BigDecimal(0.01), UUID.randomUUID().toString().replace("-", "")); + PayOrder order = new PayOrder("订单title", "摘要", BigDecimal.valueOf(0.01), UUID.randomUUID().toString().replace("-", "")); //App支付 order.setTransactionType(WxTransactionType.APP); data.put("orderInfo", service.app(order)); @@ -159,7 +158,7 @@ public class WxPayController { public byte[] toWxQrPay( BigDecimal price) throws IOException { //获取对应的支付账户操作工具(可根据账户id) ByteArrayOutputStream baos = new ByteArrayOutputStream(); - ImageIO.write(service.genQrPay( new PayOrder("订单title", "摘要", null == price ? new BigDecimal(0.01) : price, System.currentTimeMillis()+"", WxTransactionType.NATIVE)), "JPEG", baos); + ImageIO.write(service.genQrPay( new PayOrder("订单title", "摘要", null == price ? BigDecimal.valueOf(0.01) : price, System.currentTimeMillis()+"", WxTransactionType.NATIVE)), "JPEG", baos); return baos.toByteArray(); } @@ -173,7 +172,7 @@ public class WxPayController { @RequestMapping(value = "getQrPay.json") public String getQrPay(BigDecimal price) throws IOException { //获取对应的支付账户操作工具(可根据账户id) - return service.getQrPay( new PayOrder("订单title", "摘要", null == price ? new BigDecimal(0.01) : price, System.currentTimeMillis()+"", WxTransactionType.NATIVE)); + return service.getQrPay( new PayOrder("订单title", "摘要", null == price ? BigDecimal.valueOf(0.01) : price, System.currentTimeMillis()+"", WxTransactionType.NATIVE)); } /** * 刷卡付,pos主动扫码付款(条码付) @@ -185,7 +184,7 @@ public class WxPayController { public Map microPay( BigDecimal price, String authCode) { //获取对应的支付账户操作工具(可根据账户id) //条码付 - PayOrder order = new PayOrder("egan order", "egan order", null == price ? new BigDecimal(0.01) : price, UUID.randomUUID().toString().replace("-", ""), WxTransactionType.MICROPAY); + PayOrder order = new PayOrder("egan order", "egan order", null == price ? BigDecimal.valueOf(0.01) : price, UUID.randomUUID().toString().replace("-", ""), WxTransactionType.MICROPAY); //设置授权码,条码等 order.setAuthCode(authCode); //支付结果 @@ -212,7 +211,7 @@ public class WxPayController { @RequestMapping(value = "facePay") public Map facePay(BigDecimal price, String authCode, String openid) { //获取对应的支付账户操作工具(可根据账户id) - PayOrder order = new PayOrder("egan order", "egan order", null == price ? new BigDecimal(0.01) : price, UUID.randomUUID().toString().replace("-", ""), WxTransactionType.FACEPAY); + PayOrder order = new PayOrder("egan order", "egan order", null == price ? BigDecimal.valueOf(0.01) : price, UUID.randomUUID().toString().replace("-", ""), WxTransactionType.FACEPAY); //设置人脸凭证 order.setAuthCode(authCode); // 用户在商户 appid下的唯一标识 diff --git a/pay-java-fuiou/README.md b/pay-java-fuiou/README.md index deb86df..ea87ca4 100644 --- a/pay-java-fuiou/README.md +++ b/pay-java-fuiou/README.md @@ -51,7 +51,7 @@ ```java //支付订单基础信息 - PayOrder payOrder = new PayOrder("订单title", "摘要", new BigDecimal(0.01) , UUID.randomUUID().toString().replace("-", "").substring(2)); + PayOrder payOrder = new PayOrder("订单title", "摘要", BigDecimal.valueOf(0.01) , UUID.randomUUID().toString().replace("-", "").substring(2)); ``` diff --git a/pay-java-fuiou/src/test/java/PayTest.java b/pay-java-fuiou/src/test/java/PayTest.java index a59cca0..53dfd25 100644 --- a/pay-java-fuiou/src/test/java/PayTest.java +++ b/pay-java-fuiou/src/test/java/PayTest.java @@ -37,7 +37,7 @@ public class PayTest { //支付服务 PayService service = new FuiouPayService(fuiouPayConfigStorage); //支付订单基础信息 - PayOrder payOrder = new PayOrder("订单title", "摘要", new BigDecimal(0.01) , UUID.randomUUID().toString().replace("-", "").substring(2)); + PayOrder payOrder = new PayOrder("订单title", "摘要", BigDecimal.valueOf(0.01) , UUID.randomUUID().toString().replace("-", "").substring(2)); /*----------- 网页支付-------------------*/ diff --git a/pay-java-union/README.md b/pay-java-union/README.md index 4d370b1..7d604a8 100644 --- a/pay-java-union/README.md +++ b/pay-java-union/README.md @@ -80,7 +80,7 @@ ```java //支付订单基础信息 - PayOrder payOrder = new PayOrder("订单title", "摘要", new BigDecimal(0.01) , UUID.randomUUID().toString().replace("-", "")); + PayOrder payOrder = new PayOrder("订单title", "摘要", BigDecimal.valueOf(0.01) , UUID.randomUUID().toString().replace("-", "")); ``` diff --git a/pay-java-union/src/test/java/PayTest.java b/pay-java-union/src/test/java/PayTest.java index ff2f114..93ef3d5 100644 --- a/pay-java-union/src/test/java/PayTest.java +++ b/pay-java-union/src/test/java/PayTest.java @@ -52,7 +52,7 @@ public class PayTest { //支付服务 UnionPayService service = new UnionPayService(unionPayConfigStorage); //支付订单基础信息 - PayOrder payOrder = new PayOrder("订单title", "摘要", new BigDecimal(0.01) , new SimpleDateFormat("yyyyMMddHHmmss").format(System.currentTimeMillis())); + PayOrder payOrder = new PayOrder("订单title", "摘要", BigDecimal.valueOf(0.01) , new SimpleDateFormat("yyyyMMddHHmmss").format(System.currentTimeMillis())); /*----------- 网页支付-------------------*/ diff --git a/pay-java-wx-youdian/README.md b/pay-java-wx-youdian/README.md index 89445d1..e7f855c 100644 --- a/pay-java-wx-youdian/README.md +++ b/pay-java-wx-youdian/README.md @@ -50,7 +50,7 @@ ```java //支付订单基础信息 - PayOrder payOrder = new PayOrder("订单title", "摘要", new BigDecimal(0.01) , UUID.randomUUID().toString().replace("-", "")); + PayOrder payOrder = new PayOrder("订单title", "摘要", BigDecimal.valueOf(0.01) , UUID.randomUUID().toString().replace("-", "")); ``` diff --git a/pay-java-wx-youdian/src/test/java/PayTest.java b/pay-java-wx-youdian/src/test/java/PayTest.java index 069cca4..e4d3089 100644 --- a/pay-java-wx-youdian/src/test/java/PayTest.java +++ b/pay-java-wx-youdian/src/test/java/PayTest.java @@ -37,7 +37,7 @@ public class PayTest { //支付服务 PayService service = new WxYouDianPayService(wxPayConfigStorage); //支付订单基础信息 - PayOrder payOrder = new PayOrder("订单title", "摘要", new BigDecimal(0.01) , UUID.randomUUID().toString().replace("-", "")); + PayOrder payOrder = new PayOrder("订单title", "摘要", BigDecimal.valueOf(0.01) , UUID.randomUUID().toString().replace("-", "")); /*-----------扫码付-------------------*/ payOrder.setTransactionType(YoudianTransactionType.NATIVE); //获取扫码付的二维码 diff --git a/pay-java-wx/README.md b/pay-java-wx/README.md index ba8783b..488a177 100644 --- a/pay-java-wx/README.md +++ b/pay-java-wx/README.md @@ -81,7 +81,7 @@ ```java //支付订单基础信息 - PayOrder payOrder = new PayOrder("订单title", "摘要", new BigDecimal(0.01) , UUID.randomUUID().toString().replace("-", "")); + PayOrder payOrder = new PayOrder("订单title", "摘要", BigDecimal.valueOf(0.01) , UUID.randomUUID().toString().replace("-", "")); ``` @@ -146,7 +146,7 @@ /*-----------刷脸付-------------------*/ //获取对应的支付账户操作工具(可根据账户id) - PayOrder order = new PayOrder("egan order", "egan order", null == price ? new BigDecimal(0.01) : price, UUID.randomUUID().toString().replace("-", ""), WxTransactionType.FACEPAY); + PayOrder order = new PayOrder("egan order", "egan order", null == price ? BigDecimal.valueOf(0.01) : price, UUID.randomUUID().toString().replace("-", ""), WxTransactionType.FACEPAY); //设置人脸凭证 order.setAuthCode(authCode); // 用户在商户 appid下的唯一标识 diff --git a/pay-java-wx/src/test/java/PayTest.java b/pay-java-wx/src/test/java/PayTest.java index e0a604b..4588157 100644 --- a/pay-java-wx/src/test/java/PayTest.java +++ b/pay-java-wx/src/test/java/PayTest.java @@ -44,7 +44,7 @@ public class PayTest { //支付服务 WxPayService service = new WxPayService(wxPayConfigStorage); //支付订单基础信息 - PayOrder payOrder = new PayOrder("订单title", "摘要", new BigDecimal(0.01) , UUID.randomUUID().toString().replace("-", "")); + PayOrder payOrder = new PayOrder("订单title", "摘要", BigDecimal.valueOf(0.01) , UUID.randomUUID().toString().replace("-", "")); /*-----------扫码付-------------------*/ payOrder.setTransactionType(WxTransactionType.NATIVE); //获取扫码付的二维码 -- Gitee From 85015a0131819cb52a43a86b5fc011210bff7def Mon Sep 17 00:00:00 2001 From: egzosn Date: Fri, 13 Nov 2020 22:45:10 +0800 Subject: [PATCH 152/299] .. --- pay-java-paypal/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pay-java-paypal/README.md b/pay-java-paypal/README.md index 5730273..5ca93d0 100644 --- a/pay-java-paypal/README.md +++ b/pay-java-paypal/README.md @@ -94,7 +94,7 @@ order.setCurType(CurType.USD); order.setDescription(" description "); order.setTradeNo("paypal 平台的单号"); - order.setRefundAmount(new BigDecimal(0.01)); + order.setRefundAmount(BigDecimal.valueOf(0.01)); RefundResult result = service.refund(order); ``` -- Gitee From d6b6819324f28bea1f5b241f8f4308586e51e68a Mon Sep 17 00:00:00 2001 From: egzosn Date: Thu, 12 Nov 2020 23:32:59 +0800 Subject: [PATCH 153/299] =?UTF-8?q?=E4=BB=A3=E7=A0=81=E6=95=B4=E7=90=86?= =?UTF-8?q?=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../egzosn/pay/ali/bean/CertEnvironment.java | 8 +- .../egzosn/pay/common/api/BasePayService.java | 31 +++++-- .../com/egzosn/pay/common/api/CertStore.java | 5 +- .../pay/common/api/PayConfigStorage.java | 83 +++++++++++-------- .../egzosn/pay/common/bean/CertStoreType.java | 8 +- .../com/egzosn/pay/common/bean/Order.java | 3 - .../egzosn/pay/common/bean/PayMessage.java | 3 - .../pay/common/http/ClientHttpRequest.java | 38 +++++---- 8 files changed, 108 insertions(+), 71 deletions(-) diff --git a/pay-java-ali/src/main/java/com/egzosn/pay/ali/bean/CertEnvironment.java b/pay-java-ali/src/main/java/com/egzosn/pay/ali/bean/CertEnvironment.java index bc71470..f8b262c 100644 --- a/pay-java-ali/src/main/java/com/egzosn/pay/ali/bean/CertEnvironment.java +++ b/pay-java-ali/src/main/java/com/egzosn/pay/ali/bean/CertEnvironment.java @@ -3,15 +3,15 @@ */ package com.egzosn.pay.ali.bean; +import java.io.InputStream; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + import com.egzosn.pay.ali.utils.AntCertificationUtil; import com.egzosn.pay.common.bean.result.PayException; import com.egzosn.pay.common.exception.PayErrorException; import com.egzosn.pay.common.util.str.StringUtils; -import java.io.InputStream; -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; - /** * 证书模式运行时环境 * diff --git a/pay-java-common/src/main/java/com/egzosn/pay/common/api/BasePayService.java b/pay-java-common/src/main/java/com/egzosn/pay/common/api/BasePayService.java index fbc5b59..605ed7d 100644 --- a/pay-java-common/src/main/java/com/egzosn/pay/common/api/BasePayService.java +++ b/pay-java-common/src/main/java/com/egzosn/pay/common/api/BasePayService.java @@ -1,20 +1,34 @@ package com.egzosn.pay.common.api; +import java.awt.image.BufferedImage; +import java.io.InputStream; +import java.io.UnsupportedEncodingException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Date; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.TreeMap; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + import com.alibaba.fastjson.JSON; -import com.egzosn.pay.common.bean.*; +import com.egzosn.pay.common.bean.MethodType; +import com.egzosn.pay.common.bean.Order; +import com.egzosn.pay.common.bean.PayMessage; +import com.egzosn.pay.common.bean.PayOrder; +import com.egzosn.pay.common.bean.PayOutMessage; +import com.egzosn.pay.common.bean.RefundOrder; +import com.egzosn.pay.common.bean.TransactionType; +import com.egzosn.pay.common.bean.TransferOrder; import com.egzosn.pay.common.exception.PayErrorException; import com.egzosn.pay.common.http.HttpConfigStorage; import com.egzosn.pay.common.http.HttpRequestTemplate; import com.egzosn.pay.common.util.MatrixToImageWriter; import com.egzosn.pay.common.util.sign.SignUtils; import com.egzosn.pay.common.util.str.StringUtils; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; - -import java.awt.image.BufferedImage; -import java.io.InputStream; -import java.io.UnsupportedEncodingException; -import java.util.*; /** * 支付基础服务 @@ -455,6 +469,7 @@ public abstract class BasePayService implements Pay protected Map setParameters(Map parameters, String key, Order order) { Object attr = order.getAttr(key); if (null != attr && !"".equals(attr)) { + order.getAttrs().remove(key); parameters.put(key, attr); } return parameters; diff --git a/pay-java-common/src/main/java/com/egzosn/pay/common/api/CertStore.java b/pay-java-common/src/main/java/com/egzosn/pay/common/api/CertStore.java index bbbb082..dde73fb 100644 --- a/pay-java-common/src/main/java/com/egzosn/pay/common/api/CertStore.java +++ b/pay-java-common/src/main/java/com/egzosn/pay/common/api/CertStore.java @@ -5,9 +5,10 @@ import java.io.InputStream; /** * 证书存储方式 + * * @author egan - * email egzosn@gmail.com - * date 2019/10/13.23:09 + * email egzosn@gmail.com + * date 2019/10/13.23:09 */ public interface CertStore { diff --git a/pay-java-common/src/main/java/com/egzosn/pay/common/api/PayConfigStorage.java b/pay-java-common/src/main/java/com/egzosn/pay/common/api/PayConfigStorage.java index df85e6c..3885601 100644 --- a/pay-java-common/src/main/java/com/egzosn/pay/common/api/PayConfigStorage.java +++ b/pay-java-common/src/main/java/com/egzosn/pay/common/api/PayConfigStorage.java @@ -2,113 +2,126 @@ package com.egzosn.pay.common.api; import com.egzosn.pay.common.bean.Attrs; import com.egzosn.pay.common.bean.MsgType; -import com.egzosn.pay.common.util.sign.CertDescriptor; - -import java.util.concurrent.locks.Lock; /** * 支付客户端配置存储 - * @author egan + * + * @author egan *

  *     email egzosn@gmail.com
  *     date 2016-5-18 14:09:01
  * 
*/ - public interface PayConfigStorage extends Attrs { +public interface PayConfigStorage extends Attrs { /** * 附加支付配置 + * * @return 附加信息 */ - Object getAttach(); + Object getAttach(); /** - * 应用id - * @return 应用id + * 应用id + * + * @return 应用id */ - String getAppid(); + String getAppid(); /** * 合作商唯一标识 - * @return 合作商唯一标识 + * + * @return 合作商唯一标识 */ - String getPid(); + String getPid(); /** * 获取收款账号 - * @return 收款账号 + * + * @return 收款账号 */ - String getSeller(); + String getSeller(); /** * 授权令牌 - * @return 授权令牌 + * + * @return 授权令牌 */ - String getToken(); + String getToken(); /** * 服务端异步回调Url - * @return 异步回调Url + * + * @return 异步回调Url */ - String getNotifyUrl(); + String getNotifyUrl(); + /** * 服务端同步回调Url - * @return 同步回调Url + * + * @return 同步回调Url */ - String getReturnUrl(); + String getReturnUrl(); + /** - * 签名方式 - * @return 签名方式 + * 签名方式 + * + * @return 签名方式 */ - String getSignType(); + String getSignType(); /** - * 字符编码格式 + * 字符编码格式 + * * @return 字符编码 */ - String getInputCharset(); + String getInputCharset(); /** * 支付平台公钥(签名校验使用) + * * @return 公钥 */ - String getKeyPublic(); + String getKeyPublic(); /** - * 应用私钥(生成签名时使用) + * 应用私钥(生成签名时使用) + * * @return 私钥 */ - String getKeyPrivate(); + String getKeyPrivate(); /** * 支付类型 自定义 * 这里暂定 aliPay 支付宝, wxPay微信支付 + * * @return 支付类型 */ - String getPayType(); + String getPayType(); /** * 消息类型 - * @see #getMsgType - * @see MsgType + * * @return "text" 或者 "xml",json + * @see #getMsgType + * @see MsgType */ @Deprecated - MsgType getMsgType(); - - + MsgType getMsgType(); /** * 应该是线程安全的 - * @param accessToken 新的accessToken值 + * + * @param accessToken 新的accessToken值 * @param expiresInSeconds 过期时间,以秒为单位 多少秒 */ void updateAccessToken(String accessToken, int expiresInSeconds); /** * 应该是线程安全的 + * * @param accessToken 新的accessToken值 * @param expiresTime 过期时间,时间戳 */ @@ -116,10 +129,10 @@ import java.util.concurrent.locks.Lock; /** * 是否为测试环境, true测试环境 + * * @return true测试环境 */ boolean isTest(); - } diff --git a/pay-java-common/src/main/java/com/egzosn/pay/common/bean/CertStoreType.java b/pay-java-common/src/main/java/com/egzosn/pay/common/bean/CertStoreType.java index 20488f9..4517b83 100644 --- a/pay-java-common/src/main/java/com/egzosn/pay/common/bean/CertStoreType.java +++ b/pay-java-common/src/main/java/com/egzosn/pay/common/bean/CertStoreType.java @@ -1,12 +1,16 @@ package com.egzosn.pay.common.bean; +import java.io.ByteArrayInputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; + import com.egzosn.pay.common.api.CertStore; import com.egzosn.pay.common.bean.result.PayException; import com.egzosn.pay.common.exception.PayErrorException; import com.egzosn.pay.common.http.HttpRequestTemplate; -import java.io.*; - /** * 证书存储类型 * diff --git a/pay-java-common/src/main/java/com/egzosn/pay/common/bean/Order.java b/pay-java-common/src/main/java/com/egzosn/pay/common/bean/Order.java index 62a2612..0c06f36 100644 --- a/pay-java-common/src/main/java/com/egzosn/pay/common/bean/Order.java +++ b/pay-java-common/src/main/java/com/egzosn/pay/common/bean/Order.java @@ -1,8 +1,5 @@ package com.egzosn.pay.common.bean; -import java.io.Serializable; -import java.util.Map; - /** * 支付订单信息 * diff --git a/pay-java-common/src/main/java/com/egzosn/pay/common/bean/PayMessage.java b/pay-java-common/src/main/java/com/egzosn/pay/common/bean/PayMessage.java index cf09fc1..11c247a 100644 --- a/pay-java-common/src/main/java/com/egzosn/pay/common/bean/PayMessage.java +++ b/pay-java-common/src/main/java/com/egzosn/pay/common/bean/PayMessage.java @@ -2,9 +2,6 @@ package com.egzosn.pay.common.bean; import java.io.Serializable; import java.math.BigDecimal; -import java.text.ParseException; -import java.text.SimpleDateFormat; -import java.util.Date; import java.util.Map; /** 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 f1b0aba..f58b25f 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 @@ -1,16 +1,22 @@ package com.egzosn.pay.common.http; -import com.alibaba.fastjson.JSON; -import com.alibaba.fastjson.JSONException; -import com.alibaba.fastjson.JSONObject; -import com.egzosn.pay.common.bean.MethodType; -import com.egzosn.pay.common.bean.result.PayException; -import com.egzosn.pay.common.exception.PayErrorException; -import com.egzosn.pay.common.util.XML; -import com.egzosn.pay.common.util.str.StringUtils; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.net.URI; +import java.nio.charset.Charset; +import java.util.Map; + import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; -import org.apache.http.*; +import org.apache.http.Consts; +import org.apache.http.Header; +import org.apache.http.HttpEntity; +import org.apache.http.HttpHost; +import org.apache.http.HttpResponse; +import org.apache.http.StatusLine; import org.apache.http.client.ClientProtocolException; import org.apache.http.client.HttpResponseException; import org.apache.http.client.config.RequestConfig; @@ -19,13 +25,17 @@ import org.apache.http.entity.ContentType; import org.apache.http.entity.StringEntity; import org.apache.http.util.EntityUtils; -import java.io.*; -import java.net.URI; -import java.nio.charset.Charset; -import java.util.Map; - import static com.egzosn.pay.common.http.UriVariables.getMapToParameters; +import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.JSONException; +import com.alibaba.fastjson.JSONObject; +import com.egzosn.pay.common.bean.MethodType; +import com.egzosn.pay.common.bean.result.PayException; +import com.egzosn.pay.common.exception.PayErrorException; +import com.egzosn.pay.common.util.XML; +import com.egzosn.pay.common.util.str.StringUtils; + /** * 一个HTTP请求的客户端 * -- Gitee From def4e71a1842bab242b30c38330fe75afef39b48 Mon Sep 17 00:00:00 2001 From: egzosn Date: Thu, 12 Nov 2020 23:33:25 +0800 Subject: [PATCH 154/299] =?UTF-8?q?=E5=8A=A0=E5=85=A5=E8=87=AA=E5=AE=9A?= =?UTF-8?q?=E4=B9=89=E5=B1=9E=E6=80=A7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/egzosn/pay/ali/api/AliPayService.java | 65 ++++++++++++++----- .../com/egzosn/pay/ali/bean/AliPayConst.java | 4 ++ .../com/egzosn/pay/wx/api/WxPayService.java | 10 +-- 3 files changed, 53 insertions(+), 26 deletions(-) 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 a1a6515..93d859b 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 @@ -9,6 +9,7 @@ import static com.egzosn.pay.ali.bean.AliPayConst.APP_AUTH_TOKEN; import static com.egzosn.pay.ali.bean.AliPayConst.BIZ_CONTENT; import static com.egzosn.pay.ali.bean.AliPayConst.CODE; import static com.egzosn.pay.ali.bean.AliPayConst.HTTPS_REQ_URL; +import static com.egzosn.pay.ali.bean.AliPayConst.NOTIFY_URL; import static com.egzosn.pay.ali.bean.AliPayConst.PASSBACK_PARAMS; import static com.egzosn.pay.ali.bean.AliPayConst.PAYEE_INFO; import static com.egzosn.pay.ali.bean.AliPayConst.PRODUCT_CODE; @@ -55,7 +56,6 @@ import com.egzosn.pay.common.util.str.StringUtils; public class AliPayService extends BasePayService { - /** * 获取对应的请求地址 * @@ -144,22 +144,24 @@ public class AliPayService extends BasePayService { /** * 获取公钥信息 + * * @param params 响应参数 * @return 公钥信息 */ - private String getKeyPublic(Map params){ - if (!payConfigStorage.isCertSign()){ + private String getKeyPublic(Map params) { + if (!payConfigStorage.isCertSign()) { return payConfigStorage.getKeyPublic(); } return payConfigStorage.getCertEnvironment().getAliPayPublicKey(getAliPayCertSN(params)); } + /** * 从响应Map中提取支付宝公钥证书序列号 * * @param respMap 响应Map * @return 支付宝公钥证书序列号 */ - public String getAliPayCertSN(java.util.Map respMap){ + public String getAliPayCertSN(java.util.Map respMap) { return (String) respMap.get(AliPayConst.ALIPAY_CERT_SN_FIELD); } @@ -203,6 +205,15 @@ public class AliPayService extends BasePayService { return setSign(getOrder(order)); } + private void setNotifyUrl(Map orderInfo, PayOrder order) { + orderInfo.put(NOTIFY_URL, payConfigStorage.getNotifyUrl()); + setParameters(orderInfo, NOTIFY_URL, order); + } + + private void setReturnUrl(Map orderInfo, PayOrder order) { + orderInfo.put(RETURN_URL, payConfigStorage.getReturnUrl()); + setParameters(orderInfo, RETURN_URL, order); + } /** * 支付宝创建订单信息 @@ -216,10 +227,8 @@ public class AliPayService extends BasePayService { Map orderInfo = getPublicParameters(order.getTransactionType()); - - orderInfo.put("notify_url", payConfigStorage.getNotifyUrl()); + setNotifyUrl(orderInfo, order); orderInfo.put("format", "json"); - setAppAuthToken(orderInfo, order.getAttrs()); Map bizContent = new TreeMap<>(); @@ -232,12 +241,12 @@ public class AliPayService extends BasePayService { case PAGE: bizContent.put(PASSBACK_PARAMS, order.getAddition()); bizContent.put(PRODUCT_CODE, "FAST_INSTANT_TRADE_PAY"); - orderInfo.put(RETURN_URL, payConfigStorage.getReturnUrl()); + setReturnUrl(orderInfo, order); break; case WAP: bizContent.put(PASSBACK_PARAMS, order.getAddition()); bizContent.put(PRODUCT_CODE, "QUICK_WAP_PAY"); - orderInfo.put(RETURN_URL, payConfigStorage.getReturnUrl()); + setReturnUrl(orderInfo, order); break; case APP: bizContent.put(PASSBACK_PARAMS, order.getAddition()); @@ -257,16 +266,31 @@ public class AliPayService extends BasePayService { break; } - if (null != order.getExpirationTime()) { - bizContent.put(order.getTransactionType() == AliTransactionType.SWEEPPAY ? "qr_code_timeout_express" : "timeout_express", DateUtils.minutesRemaining(order.getExpirationTime()) + "m"); - } - + setExpirationTime(bizContent, order); bizContent.putAll(order.getAttrs()); orderInfo.put(BIZ_CONTENT, JSON.toJSONString(bizContent)); return preOrderHandler(orderInfo, order); } + private Map setExpirationTime(Map bizContent, PayOrder order) { + if (null == order.getExpirationTime()) { + return bizContent; + } + bizContent.put("timeout_express", DateUtils.minutesRemaining(order.getExpirationTime()) + "m"); + switch ((AliTransactionType) order.getTransactionType()) { + case SWEEPPAY: + bizContent.put("qr_code_timeout_express", DateUtils.minutesRemaining(order.getExpirationTime()) + "m"); + case PAGE: + case WAP: + case APP: + bizContent.put("time_expire", DateUtils.formatDate(order.getExpirationTime(), "yyyy-MM-dd HH:mm")); + break; + default: + } + return bizContent; + } + /** * 获取公共请求参数 * @@ -286,10 +310,11 @@ public class AliPayService extends BasePayService { /** * 加载证书序列 + * * @param orderInfo 订单信息 */ - private void loadCertSn(Map orderInfo){ - if (payConfigStorage.isCertSign()){ + private void loadCertSn(Map orderInfo) { + if (payConfigStorage.isCertSign()) { final CertEnvironment certEnvironment = payConfigStorage.getCertEnvironment(); setParameters(orderInfo, "app_cert_sn", certEnvironment.getMerchantCertSN()); setParameters(orderInfo, "alipay_root_cert_sn", certEnvironment.getRootCertSN()); @@ -325,7 +350,8 @@ public class AliPayService extends BasePayService { public String toPay(PayOrder order) { if (null == order.getTransactionType()) { order.setTransactionType(AliTransactionType.PAGE); - } else if (order.getTransactionType() != AliTransactionType.PAGE && order.getTransactionType() != AliTransactionType.WAP) { + } + else if (order.getTransactionType() != AliTransactionType.PAGE && order.getTransactionType() != AliTransactionType.WAP) { throw new PayErrorException(new PayException("-1", "错误的交易类型:" + order.getTransactionType())); } return super.toPay(order); @@ -381,7 +407,8 @@ public class AliPayService extends BasePayService { public Map microPay(PayOrder order) { if (null == order.getTransactionType()) { order.setTransactionType(AliTransactionType.BAR_CODE); - } else if (order.getTransactionType() != AliTransactionType.BAR_CODE && order.getTransactionType() != AliTransactionType.WAVE_CODE && order.getTransactionType() != AliTransactionType.SECURITY_CODE) { + } + else if (order.getTransactionType() != AliTransactionType.BAR_CODE && order.getTransactionType() != AliTransactionType.WAVE_CODE && order.getTransactionType() != AliTransactionType.SECURITY_CODE) { throw new PayErrorException(new PayException("-1", "错误的交易类型:" + order.getTransactionType())); } @@ -466,6 +493,7 @@ public class AliPayService extends BasePayService { setAppAuthToken(parameters); setParameters(parameters, APP_AUTH_TOKEN, (String) attrs.remove(APP_AUTH_TOKEN)); } + /** * 设置支付宝授权Token * @@ -647,7 +675,8 @@ public class AliPayService extends BasePayService { Map bizContent = new TreeMap(); if (StringUtils.isEmpty(outNo)) { bizContent.put("order_id", tradeNo); - } else { + } + else { bizContent.put("out_biz_no", outNo); } //设置请求参数的集合 diff --git a/pay-java-ali/src/main/java/com/egzosn/pay/ali/bean/AliPayConst.java b/pay-java-ali/src/main/java/com/egzosn/pay/ali/bean/AliPayConst.java index 801e548..a56db71 100644 --- a/pay-java-ali/src/main/java/com/egzosn/pay/ali/bean/AliPayConst.java +++ b/pay-java-ali/src/main/java/com/egzosn/pay/ali/bean/AliPayConst.java @@ -39,6 +39,10 @@ public final class AliPayConst { * 返回地址 */ public static final String RETURN_URL = "return_url"; + /** + * 回调地址 + */ + public static final String NOTIFY_URL = "notify_url"; /** * 请求内容 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 661ebb5..4d9769a 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 @@ -132,20 +132,13 @@ public class WxPayService extends BasePayService implements @Override public boolean verify(Map params) { - if (!(SUCCESS.equals(params.get(RETURN_CODE)) && SUCCESS.equals(params.get(RESULT_CODE)))) { + if (null == params.get(SIGN) || !(SUCCESS.equals(params.get(RETURN_CODE)) && SUCCESS.equals(params.get(RESULT_CODE))) ) { if (LOG.isErrorEnabled()) { LOG.error(String.format("微信支付异常:return_code=%s,参数集=%s", params.get(RETURN_CODE), params)); } return false; } - if (null == params.get(SIGN)) { - if (LOG.isDebugEnabled()) { - LOG.debug(String.format("微信支付异常:签名为空!%s=%s", 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) { @@ -230,6 +223,7 @@ public class WxPayService extends BasePayService implements parameters.put("total_fee", Util.conversionCentAmount(order.getPrice())); setParameters(parameters, "attach", order.getAddition()); parameters.put("notify_url", payConfigStorage.getNotifyUrl()); + setParameters(parameters, "notify_url", order); parameters.put("trade_type", order.getTransactionType().getType()); if (null != order.getExpirationTime()) { parameters.put("time_start", DateUtils.formatDate(new Date(), DateUtils.YYYYMMDDHHMMSS)); -- Gitee From 2d257798f4e557065a07167b805b8716a634def5 Mon Sep 17 00:00:00 2001 From: egzosn Date: Wed, 18 Nov 2020 22:19:12 +0800 Subject: [PATCH 155/299] =?UTF-8?q?seller=5Fid=20=E9=9D=9E=E5=BF=85?= =?UTF-8?q?=E5=A1=AB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/egzosn/pay/ali/api/AliPayService.java | 2 +- pay-java-demo/src/main/webapp/gzh.png | Bin 0 -> 131545 bytes pay-java-demo/src/main/webapp/wx.jpg | Bin 0 -> 86036 bytes 3 files changed, 1 insertion(+), 1 deletion(-) create mode 100644 pay-java-demo/src/main/webapp/gzh.png create mode 100644 pay-java-demo/src/main/webapp/wx.jpg 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 93d859b..879cdd0 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 @@ -233,7 +233,7 @@ public class AliPayService extends BasePayService { Map bizContent = new TreeMap<>(); bizContent.put("body", order.getBody()); - bizContent.put("seller_id", payConfigStorage.getSeller()); + setParameters(bizContent, "seller_id", payConfigStorage.getSeller()); bizContent.put("subject", order.getSubject()); bizContent.put("out_trade_no", order.getOutTradeNo()); bizContent.put("total_amount", Util.conversionAmount(order.getPrice()).toString()); diff --git a/pay-java-demo/src/main/webapp/gzh.png b/pay-java-demo/src/main/webapp/gzh.png new file mode 100644 index 0000000000000000000000000000000000000000..bb9a069e24afa708eabced9fcc2b43bd3481c989 GIT binary patch literal 131545 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 literal 0 HcmV?d00001 diff --git a/pay-java-demo/src/main/webapp/wx.jpg b/pay-java-demo/src/main/webapp/wx.jpg new file mode 100644 index 0000000000000000000000000000000000000000..35c20dea9618701db1dc3ec85477e9264d510aee GIT binary patch literal 86036 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 literal 0 HcmV?d00001 -- Gitee From 519c278b54abad6b6323dcc9f8dcda93ef532af7 Mon Sep 17 00:00:00 2001 From: egzosn Date: Wed, 18 Nov 2020 22:26:22 +0800 Subject: [PATCH 156/299] =?UTF-8?q?=E5=85=AC=E4=BC=97=E5=8F=B7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 20fc538..f482e40 100644 --- a/README.md +++ b/README.md @@ -63,6 +63,8 @@ android 例子 [pay-java-android](https://gitee.com/egzosn/pay-java-android) 非常欢迎和感谢对本项目发起Pull Request的同学,不过本项目基于git flow开发流程,因此在发起Pull Request的时候请选择develop分支。 +作者公众号(每周输出)![公众号](https://egzosn.gitee.io/pay-java-parent/gzh.png "gzh.png") + E-Mail:egzosn@gmail.com QQ群:542193977 -- Gitee From 6b2b80b62ea8a5deda239e25c543eb0d695ad558 Mon Sep 17 00:00:00 2001 From: egzosn Date: Wed, 18 Nov 2020 22:27:22 +0800 Subject: [PATCH 157/299] =?UTF-8?q?seller=5Fid=20=E9=9D=9E=E5=BF=85?= =?UTF-8?q?=E5=A1=AB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index f482e40..adeac87 100644 --- a/README.md +++ b/README.md @@ -69,4 +69,4 @@ E-Mail:egzosn@gmail.com QQ群:542193977 -微信群: ![微信群](https://www.egzosn.com/images/wx.jpg "wx.jpg") +微信群: ![微信群](https://egzosn.gitee.io/pay-java-parent/wx.jpg "wx.jpg") -- Gitee From c117dc51ce007e77da8326a610221db92cc6bd83 Mon Sep 17 00:00:00 2001 From: egzosn Date: Wed, 18 Nov 2020 22:30:15 +0800 Subject: [PATCH 158/299] md --- README.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index adeac87..576cce4 100644 --- a/README.md +++ b/README.md @@ -63,10 +63,12 @@ android 例子 [pay-java-android](https://gitee.com/egzosn/pay-java-android) 非常欢迎和感谢对本项目发起Pull Request的同学,不过本项目基于git flow开发流程,因此在发起Pull Request的时候请选择develop分支。 -作者公众号(每周输出)![公众号](https://egzosn.gitee.io/pay-java-parent/gzh.png "gzh.png") +作者公众号(每周输出) +![公众号](https://egzosn.gitee.io/pay-java-parent/gzh.png "gzh.png") E-Mail:egzosn@gmail.com QQ群:542193977 -微信群: ![微信群](https://egzosn.gitee.io/pay-java-parent/wx.jpg "wx.jpg") +微信群: +![微信群](https://egzosn.gitee.io/pay-java-parent/wx.jpg "wx.jpg") -- Gitee From ad8e592c90e7d24e72ea62fe52e6a9b2565e8531 Mon Sep 17 00:00:00 2001 From: egzosn Date: Mon, 30 Nov 2020 09:58:08 +0800 Subject: [PATCH 159/299] =?UTF-8?q?=E8=AF=81=E4=B9=A6=E5=88=9D=E5=A7=8B?= =?UTF-8?q?=E5=88=A4=E6=96=AD=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/egzosn/pay/ali/api/AliPayConfigStorage.java | 7 +++---- .../main/java/com/egzosn/pay/ali/api/AliPayService.java | 2 +- 2 files changed, 4 insertions(+), 5 deletions(-) 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 cad2cfe..d9d907d 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 @@ -16,9 +16,8 @@ import com.egzosn.pay.common.exception.PayErrorException; *

* email egzosn@gmail.com * date 2016-5-18 14:09:01 - * - * - * 以下证书签名相关触发前提是 {@link BasePayConfigStorage#isCertSign}等于true的情况。不然走的就是普通的方式 + *

+ * 以下证书签名相关触发前提是 {@link BasePayConfigStorage#isCertSign}等于true的情况。不然走的就是普通的方式 */ public class AliPayConfigStorage extends BasePayConfigStorage { @@ -146,7 +145,7 @@ public class AliPayConfigStorage extends BasePayConfigStorage { * 初始化证书信息 */ public void loadCertEnvironment() { - if (!isCertSign() || null == this.certEnvironment){ + if (!isCertSign() || null != this.certEnvironment) { return; } try (InputStream merchantCertStream = certStoreType.getInputStream(merchantCert); 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 879cdd0..c0dcb7d 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 @@ -90,7 +90,7 @@ public class AliPayService extends BasePayService { public AliPayService(AliPayConfigStorage payConfigStorage, HttpConfigStorage configStorage) { super(payConfigStorage, configStorage); - + payConfigStorage.loadCertEnvironment(); } public AliPayService(AliPayConfigStorage payConfigStorage) { -- Gitee From ec5dcffb91f394b2b91173dc793872332e12b531 Mon Sep 17 00:00:00 2001 From: egzosn Date: Sun, 29 Nov 2020 10:33:52 +0800 Subject: [PATCH 160/299] =?UTF-8?q?=E8=AF=81=E4=B9=A6=E6=96=B9=E5=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pay-java-ali/pom.xml | 6 +++++- .../egzosn/pay/ali/utils/AntCertificationUtil.java | 7 ++++++- .../pay/demo/controller/AliPayController.java | 13 +++++++------ .../pay/demo/controller/UnionPayController.java | 5 +---- 4 files changed, 19 insertions(+), 12 deletions(-) diff --git a/pay-java-ali/pom.xml b/pay-java-ali/pom.xml index e279f8e..cda91f0 100644 --- a/pay-java-ali/pom.xml +++ b/pay-java-ali/pom.xml @@ -19,7 +19,11 @@ pay-java-common
- + + org.bouncycastle + bcprov-jdk15on + 1.59 + diff --git a/pay-java-ali/src/main/java/com/egzosn/pay/ali/utils/AntCertificationUtil.java b/pay-java-ali/src/main/java/com/egzosn/pay/ali/utils/AntCertificationUtil.java index b4b94d4..7dae2ac 100644 --- a/pay-java-ali/src/main/java/com/egzosn/pay/ali/utils/AntCertificationUtil.java +++ b/pay-java-ali/src/main/java/com/egzosn/pay/ali/utils/AntCertificationUtil.java @@ -10,6 +10,7 @@ import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.security.Principal; import java.security.PublicKey; +import java.security.Security; import java.security.cert.Certificate; import java.security.cert.CertificateException; import java.security.cert.CertificateExpiredException; @@ -25,6 +26,7 @@ import java.util.Map; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +import org.bouncycastle.jce.provider.BouncyCastleProvider; import com.egzosn.pay.common.bean.result.PayException; import com.egzosn.pay.common.exception.PayErrorException; @@ -43,7 +45,10 @@ import com.egzosn.pay.common.util.str.StringUtils; */ public class AntCertificationUtil { private static final Log LOGGER = LogFactory.getLog(AntCertificationUtil.class); - + static { + Security.removeProvider("SunEC"); + Security.addProvider(new BouncyCastleProvider()); + } /** * 验证证书是否可信 * diff --git a/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/AliPayController.java b/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/AliPayController.java index c0a918a..9eb3606 100644 --- a/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/AliPayController.java +++ b/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/AliPayController.java @@ -68,9 +68,9 @@ public class AliPayController { aliPayConfigStorage.setCertSign(true); //设置证书存储方式,这里为路径 aliPayConfigStorage.setCertStoreType(CertStoreType.PATH); - aliPayConfigStorage.setMerchantCert("请填写您的应用公钥证书文件路径,例如:d:/appCertPublicKey_2019051064521003.crt"); - aliPayConfigStorage.setAliPayCert("请填写您的支付宝公钥证书文件路径,例如:d:/alipayCertPublicKey_RSA2.crt"); - aliPayConfigStorage.setAliPayRootCert("请填写您的支付宝根证书文件路径,例如:d:/alipayRootCert.crt"); + aliPayConfigStorage.setMerchantCert("E:\\Documents\\支付宝开放平台开发助手\\CSR\\appCertPublicKey_2016080400165436.crt"); + aliPayConfigStorage.setAliPayCert("E:\\Documents\\支付宝开放平台开发助手\\CSR\\alipayCertPublicKey_RSA2.crt"); + aliPayConfigStorage.setAliPayRootCert("E:\\Documents\\支付宝开放平台开发助手\\CSR\\alipayRootCert.crt"); } @PostConstruct @@ -79,9 +79,10 @@ public class AliPayController { aliPayConfigStorage.setPid("2088102169916436"); aliPayConfigStorage.setAppid("2016080400165436"); //普通公钥方式与证书公钥方式为两者取其一的方式 - keyPublic(aliPayConfigStorage); -// certKeyPublic(aliPayConfigStorage); - aliPayConfigStorage.setKeyPrivate("MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBAKroe/8h5vC4L6T+B2WdXiVwGsMvUKgb2XsKix6VY3m2wcf6tyzpNRDCNykbIwGtaeo7FshN+qZxdXHLiIam9goYncBit/8ojfLGy2gLxO/PXfzGxYGs0KsDZ+ryVPPmE34ZZ8jiJpR0ygzCFl8pN3QJPJRGTJn5+FTT9EF/9zyZAgMBAAECgYAktngcYC35u7cQXDk+jMVyiVhWYU2ULxdSpPspgLGzrZyG1saOcTIi/XVX8Spd6+B6nmLQeF/FbU3rOeuD8U2clzul2Z2YMbJ0FYay9oVZFfp5gTEFpFRTVfzqUaZQBIjJe/xHL9kQVqc5xHlE/LVA27/Kx3dbC35Y7B4EVBDYAQJBAOhsX8ZreWLKPhXiXHTyLmNKhOHJc+0tFH7Ktise/0rNspojU7o9prOatKpNylp9v6kux7migcMRdVUWWiVe+4ECQQC8PqsuEz7B0yqirQchRg1DbHjh64bw9Kj82EN1/NzOUd53tP9tg+SO97EzsibK1F7tOcuwqsa7n2aY48mQ+y0ZAkBndA2xcRcnvOOjtAz5VO8G7R12rse181HjGfG6AeMadbKg30aeaGCyIxN1loiSfNR5xsPJwibGIBg81mUrqzqBAkB+K6rkaPXJR9XtzvdWb/N3235yPkDlw7Z4MiOVM3RzvR/VMDV7m8lXoeDde2zQyeMOMYy6ztwA6WgE1bhGOnQRAkEAouUBv1sVdSBlsexX15qphOmAevzYrpufKgJIRLFWQxroXMS7FTesj+f+FmGrpPCxIde1dqJ8lqYLTyJmbzMPYw=="); +// keyPublic(aliPayConfigStorage); + certKeyPublic(aliPayConfigStorage); +// aliPayConfigStorage.setKeyPrivate("MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBAKroe/8h5vC4L6T+B2WdXiVwGsMvUKgb2XsKix6VY3m2wcf6tyzpNRDCNykbIwGtaeo7FshN+qZxdXHLiIam9goYncBit/8ojfLGy2gLxO/PXfzGxYGs0KsDZ+ryVPPmE34ZZ8jiJpR0ygzCFl8pN3QJPJRGTJn5+FTT9EF/9zyZAgMBAAECgYAktngcYC35u7cQXDk+jMVyiVhWYU2ULxdSpPspgLGzrZyG1saOcTIi/XVX8Spd6+B6nmLQeF/FbU3rOeuD8U2clzul2Z2YMbJ0FYay9oVZFfp5gTEFpFRTVfzqUaZQBIjJe/xHL9kQVqc5xHlE/LVA27/Kx3dbC35Y7B4EVBDYAQJBAOhsX8ZreWLKPhXiXHTyLmNKhOHJc+0tFH7Ktise/0rNspojU7o9prOatKpNylp9v6kux7migcMRdVUWWiVe+4ECQQC8PqsuEz7B0yqirQchRg1DbHjh64bw9Kj82EN1/NzOUd53tP9tg+SO97EzsibK1F7tOcuwqsa7n2aY48mQ+y0ZAkBndA2xcRcnvOOjtAz5VO8G7R12rse181HjGfG6AeMadbKg30aeaGCyIxN1loiSfNR5xsPJwibGIBg81mUrqzqBAkB+K6rkaPXJR9XtzvdWb/N3235yPkDlw7Z4MiOVM3RzvR/VMDV7m8lXoeDde2zQyeMOMYy6ztwA6WgE1bhGOnQRAkEAouUBv1sVdSBlsexX15qphOmAevzYrpufKgJIRLFWQxroXMS7FTesj+f+FmGrpPCxIde1dqJ8lqYLTyJmbzMPYw=="); + aliPayConfigStorage.setKeyPrivate("MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQCVEtN86O7ueJCaEWOg0cY/S5eXo8dViq/7iLCa9S4rLYJot665vhW2hnP9J5U8p1Zqa0PnTqYC5cH1Rs8pik5d8ebCtSbbvftN8lT1YHO88m1SAYGomQyDHbhwp+DEWor9rciAYNSKnuxV3Vjncv628E7Bf55/5dwP7SZBQox9Qjbjb6kkVWvghabCAsKDtRoc12weD3O1Opyf4iCFo/Ofl84ILH8PCS3W7CTBAhLaEYXGTcqDQGS8lTrPpMtDZyB1GWnBKdIAsfzFRbFkfzvNEJ1kqPvRfY6hcDA0lqgFdsr9Jlc4zGCABunKs+cviQLY4LtkZUXp76PCP1VrJhpJAgMBAAECggEBAJQKdSI3xIfoDVojaLgdeu4CqNG7oMovh2KZNSE92RKiCD7sIsVsou13ippeA9OPLr9SNMXxkeWQu9RT9IkBQ6ACCbNU6PkSKw4WWQ6xMV7ztWLaAiQnkYhoLHEcFpeIfz07psDu6EOdXYBL5+05gwCzltKMZSAIakkaroHij9gXL4w8LXDKfhL3HiUg0fupyQtOMYhTUbHwl3/WdNYpBFZB/peyzMNY5LDpRhWyz+3lyWjI+fPCnsCnC1QgqRRcYuX4MHSrmvAE3MrnGLAsRje9Muq8zGK1sLRtA2q4zoRQZ/dpT4yk08JD3pzRz1JhFPmhIRfAjCLaQGK5y/CFQvECgYEA5ZyMOrea4zycqh+YHyhwdo5A/oMHXPlcdeKK+i0MULVbfUNHCOEz/GabgadoB37JuDBpjhcN8x93RR3XSnGxxGF3vAcU+X9bZltstmUwOd1NkK73+u84uaThCQSEk5+WtEXXCkHkS09+m8cuBBpQ8mlIzgPN9JAIvDwIrjye6vUCgYEApjTAnSNr4ZjdP8Dwr3ItaL5s1y1TG3X5LyLErHpTec43ZznxL3aNNwhozuAXYYQIMrpLQ//eLf4v7oPtVfDP7tb6Qw11Up3LI9hZoQ3h1Vvd7Z275Qh6Xk5vUQJW7v+TJEZ8YMT/ZAfz3nl2KRzzV3mWgiT1J4J2SgKCapT6RYUCgYEAqMBjd4wVc8jKQ2YxAUVRUnC+Z8+ZC8XhJGq5RaDyGicP9IILpQUOlgQ7ahDFLi0KyCocied7wyFvIQcLgZUJ15Di1PhHfA8T96hdv7eiuARjy2AstOo4QIhjM6N0ZKK3ugpSPyGqTYdBP/Ala7KDp4WlknPNXF63bNCaeKPpEDkCgYAUYTuNCR7dVsUqYBojKTjwMwldiS07nMelQ/ohvA4oTWT1v2EXlM3UBa/0Tf4P8wSeU8GO4Pz5XsW51WtwskqM2sdtGWVdcAPMrWawHOeoJc+cp0620sdOcQetQ0AtpiEAvbPamc1HEsKQcQxQFkCwZTRTWPzGj3S5tn0PXJ0jkQKBgQDVkh2ySpk9361zpzSzi3nSKYQ2E0TRecE/98FQY5IXkm75BhT8o1t289pxJaXuhKkOBh4Pwjjc2P1zy0UK6Aih2nuH8FxKYPumNid5dz19AB0uTBR36jriWena0eDFhVRVwePb2u5N80bV8v4kZ0OTLxi4Y8DLggJns67IKuwB5A=="); aliPayConfigStorage.setNotifyUrl("http://pay.egzosn.com/payBack.json"); aliPayConfigStorage.setReturnUrl("http://pay.egzosn.com/payBack.html"); aliPayConfigStorage.setSignType(SignUtils.RSA.name()); diff --git a/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/UnionPayController.java b/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/UnionPayController.java index 978c90c..15a6b40 100644 --- a/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/UnionPayController.java +++ b/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/UnionPayController.java @@ -42,7 +42,7 @@ public class UnionPayController { private UnionPayService service = null; - @PostConstruct +// @PostConstruct public void init() { UnionPayConfigStorage unionPayConfigStorage = new UnionPayConfigStorage(); unionPayConfigStorage.setMerId("700000000000001"); @@ -59,9 +59,6 @@ public class UnionPayController { //设置证书对应的存储方式,这里默认为文件地址 unionPayConfigStorage.setCertStoreType(CertStoreType.URL); - - - //前台通知网址 即SDKConstants.param_frontUrl unionPayConfigStorage.setReturnUrl("http://www.pay.egzosn.com/payBack.json"); //后台通知地址 即SDKConstants.param_backUrl -- Gitee From 7e05435b1fd5df91036283ab5db45c06d02fed29 Mon Sep 17 00:00:00 2001 From: egzosn Date: Sun, 29 Nov 2020 11:45:40 +0800 Subject: [PATCH 161/299] =?UTF-8?q?=E6=94=AF=E4=BB=98=E5=AE=9D=E5=85=AC?= =?UTF-8?q?=E9=92=A5=E8=AF=81=E4=B9=A6=E6=A1=88=E4=BE=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../pay/demo/controller/AliPayController.java | 10 +-- .../ali/alipayCertPublicKey_RSA2.crt | 22 +++++ .../src/main/resources/ali/alipayRootCert.crt | 88 +++++++++++++++++++ .../ali/appCertPublicKey_2016080400165436.crt | 19 ++++ ...w.egzosn.com_\347\247\201\351\222\245.txt" | 1 + 5 files changed, 135 insertions(+), 5 deletions(-) create mode 100644 pay-java-demo/src/main/resources/ali/alipayCertPublicKey_RSA2.crt create mode 100644 pay-java-demo/src/main/resources/ali/alipayRootCert.crt create mode 100644 pay-java-demo/src/main/resources/ali/appCertPublicKey_2016080400165436.crt create mode 100644 "pay-java-demo/src/main/resources/ali/www.egzosn.com_\347\247\201\351\222\245.txt" diff --git a/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/AliPayController.java b/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/AliPayController.java index 9eb3606..616636f 100644 --- a/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/AliPayController.java +++ b/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/AliPayController.java @@ -68,9 +68,9 @@ public class AliPayController { aliPayConfigStorage.setCertSign(true); //设置证书存储方式,这里为路径 aliPayConfigStorage.setCertStoreType(CertStoreType.PATH); - aliPayConfigStorage.setMerchantCert("E:\\Documents\\支付宝开放平台开发助手\\CSR\\appCertPublicKey_2016080400165436.crt"); - aliPayConfigStorage.setAliPayCert("E:\\Documents\\支付宝开放平台开发助手\\CSR\\alipayCertPublicKey_RSA2.crt"); - aliPayConfigStorage.setAliPayRootCert("E:\\Documents\\支付宝开放平台开发助手\\CSR\\alipayRootCert.crt"); + aliPayConfigStorage.setMerchantCert("E:\\pay-java-parent\\pay-java-demo\\src\\main\\resources\\ali\\alipayCertPublicKey_RSA2.crt"); + aliPayConfigStorage.setAliPayCert("E:\\pay-java-parent\\pay-java-demo\\src\\main\\resources\\ali\\alipayCertPublicKey_RSA2.crt"); + aliPayConfigStorage.setAliPayRootCert("E:\\pay-java-parent\\pay-java-demo\\src\\main\\resources\\ali\\alipayRootCert.crt"); } @PostConstruct @@ -82,10 +82,10 @@ public class AliPayController { // keyPublic(aliPayConfigStorage); certKeyPublic(aliPayConfigStorage); // aliPayConfigStorage.setKeyPrivate("MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBAKroe/8h5vC4L6T+B2WdXiVwGsMvUKgb2XsKix6VY3m2wcf6tyzpNRDCNykbIwGtaeo7FshN+qZxdXHLiIam9goYncBit/8ojfLGy2gLxO/PXfzGxYGs0KsDZ+ryVPPmE34ZZ8jiJpR0ygzCFl8pN3QJPJRGTJn5+FTT9EF/9zyZAgMBAAECgYAktngcYC35u7cQXDk+jMVyiVhWYU2ULxdSpPspgLGzrZyG1saOcTIi/XVX8Spd6+B6nmLQeF/FbU3rOeuD8U2clzul2Z2YMbJ0FYay9oVZFfp5gTEFpFRTVfzqUaZQBIjJe/xHL9kQVqc5xHlE/LVA27/Kx3dbC35Y7B4EVBDYAQJBAOhsX8ZreWLKPhXiXHTyLmNKhOHJc+0tFH7Ktise/0rNspojU7o9prOatKpNylp9v6kux7migcMRdVUWWiVe+4ECQQC8PqsuEz7B0yqirQchRg1DbHjh64bw9Kj82EN1/NzOUd53tP9tg+SO97EzsibK1F7tOcuwqsa7n2aY48mQ+y0ZAkBndA2xcRcnvOOjtAz5VO8G7R12rse181HjGfG6AeMadbKg30aeaGCyIxN1loiSfNR5xsPJwibGIBg81mUrqzqBAkB+K6rkaPXJR9XtzvdWb/N3235yPkDlw7Z4MiOVM3RzvR/VMDV7m8lXoeDde2zQyeMOMYy6ztwA6WgE1bhGOnQRAkEAouUBv1sVdSBlsexX15qphOmAevzYrpufKgJIRLFWQxroXMS7FTesj+f+FmGrpPCxIde1dqJ8lqYLTyJmbzMPYw=="); - aliPayConfigStorage.setKeyPrivate("MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQCVEtN86O7ueJCaEWOg0cY/S5eXo8dViq/7iLCa9S4rLYJot665vhW2hnP9J5U8p1Zqa0PnTqYC5cH1Rs8pik5d8ebCtSbbvftN8lT1YHO88m1SAYGomQyDHbhwp+DEWor9rciAYNSKnuxV3Vjncv628E7Bf55/5dwP7SZBQox9Qjbjb6kkVWvghabCAsKDtRoc12weD3O1Opyf4iCFo/Ofl84ILH8PCS3W7CTBAhLaEYXGTcqDQGS8lTrPpMtDZyB1GWnBKdIAsfzFRbFkfzvNEJ1kqPvRfY6hcDA0lqgFdsr9Jlc4zGCABunKs+cviQLY4LtkZUXp76PCP1VrJhpJAgMBAAECggEBAJQKdSI3xIfoDVojaLgdeu4CqNG7oMovh2KZNSE92RKiCD7sIsVsou13ippeA9OPLr9SNMXxkeWQu9RT9IkBQ6ACCbNU6PkSKw4WWQ6xMV7ztWLaAiQnkYhoLHEcFpeIfz07psDu6EOdXYBL5+05gwCzltKMZSAIakkaroHij9gXL4w8LXDKfhL3HiUg0fupyQtOMYhTUbHwl3/WdNYpBFZB/peyzMNY5LDpRhWyz+3lyWjI+fPCnsCnC1QgqRRcYuX4MHSrmvAE3MrnGLAsRje9Muq8zGK1sLRtA2q4zoRQZ/dpT4yk08JD3pzRz1JhFPmhIRfAjCLaQGK5y/CFQvECgYEA5ZyMOrea4zycqh+YHyhwdo5A/oMHXPlcdeKK+i0MULVbfUNHCOEz/GabgadoB37JuDBpjhcN8x93RR3XSnGxxGF3vAcU+X9bZltstmUwOd1NkK73+u84uaThCQSEk5+WtEXXCkHkS09+m8cuBBpQ8mlIzgPN9JAIvDwIrjye6vUCgYEApjTAnSNr4ZjdP8Dwr3ItaL5s1y1TG3X5LyLErHpTec43ZznxL3aNNwhozuAXYYQIMrpLQ//eLf4v7oPtVfDP7tb6Qw11Up3LI9hZoQ3h1Vvd7Z275Qh6Xk5vUQJW7v+TJEZ8YMT/ZAfz3nl2KRzzV3mWgiT1J4J2SgKCapT6RYUCgYEAqMBjd4wVc8jKQ2YxAUVRUnC+Z8+ZC8XhJGq5RaDyGicP9IILpQUOlgQ7ahDFLi0KyCocied7wyFvIQcLgZUJ15Di1PhHfA8T96hdv7eiuARjy2AstOo4QIhjM6N0ZKK3ugpSPyGqTYdBP/Ala7KDp4WlknPNXF63bNCaeKPpEDkCgYAUYTuNCR7dVsUqYBojKTjwMwldiS07nMelQ/ohvA4oTWT1v2EXlM3UBa/0Tf4P8wSeU8GO4Pz5XsW51WtwskqM2sdtGWVdcAPMrWawHOeoJc+cp0620sdOcQetQ0AtpiEAvbPamc1HEsKQcQxQFkCwZTRTWPzGj3S5tn0PXJ0jkQKBgQDVkh2ySpk9361zpzSzi3nSKYQ2E0TRecE/98FQY5IXkm75BhT8o1t289pxJaXuhKkOBh4Pwjjc2P1zy0UK6Aih2nuH8FxKYPumNid5dz19AB0uTBR36jriWena0eDFhVRVwePb2u5N80bV8v4kZ0OTLxi4Y8DLggJns67IKuwB5A=="); + aliPayConfigStorage.setKeyPrivate("MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQCw7MD2Cwv/jnXssFjXnGx3JlGF57gJa2aYbJRV8MnNiPVpX4Ha+8ZjnQDhvkrWH4hHmzcujOr213HqloMpUSYBzCPiXGVRUUvdimejcHHTod7nI4g6nztzzfey/TXNDHmp7vY3pOIcjB0Zn0pkNAz2tKAFkqb4raHOqTB0QA0zD24Cn+26J2UJyYRcgeH0GtSQuUrm7yaGsuKakh+qtgWF6R71n5PMGOTQ5LH3i0WVHfCBkNGgJC6yC96HR4D7cosoyKD0+lp8UB/NVUWl7Tt/KLOgFUwh0GKSYFfv56O/VBV2+xqCGE4PlZESfVuOqz5vjjxzw3xDAUJrV8hSX/AJAgMBAAECggEBAKE0d3U4B4yo/2XUIH8EdgfykCFUSum6RFbpyBauORHfksyaSzV+ZvtomN8XhhSn0oJ8OMFfgM+86nz2+zdwSxMkMCYWTfLUAi4v59KRqAVO3kz4oS3Y3FDeAK3D7XuRvGFL7GgzAhtEx1cLPrsiehVn6s5pG15GxsIIgq/JlL1J88wn1zENLrVHmD6z/JpXvfb/RS1yR+5lyoohp4g0Ph9jJ3bCyUbRpK0QkPEzgAuWL0K2ITCL7PYHNAplI8d2xHHOLF9Qdjyx+ZrQ/RxtqzfyWzhqjsmp2qlgNCxWlt3woS9UhDB+nRvjEoWTJmIOszAMYuj8wGlX+3Ui3ALOdQECgYEA25EqnFPFinUnzgNvB6NYmh5STmZun6s4bUOLqwefKtEvrOtRwTu7sB7NIf37fizG3/MJUWHxiLy2/3ub4d2JxdDNBtJoEqnp6QB12qglCNa4CajdjtJa1dR81F9QvytsqEkmPYXFPPyviB0FcSIDAGMb3IbwvIfzBPY9WY8dJnECgYEAzkg3yKEFBZ8BU0WQ+3hyfKUoAhBEnxouxRSTBcXxwstJRiqaGTVe5aoJGQI+0xS7Z6q07XDtN2t97s6DnRLWbljsX6B64itzNhXRyzjdD3iZDU/KSw7khjhXf8XOZaj9eXmACDiUnkEn1xsM8bLiRGqB8y5f3aMY/RpuACGXnxkCgYEAx/zwT9Vpr1RIfjfYcJ+Su0X0994K0roUukj0tUJK8qf4gcsQ+y1aJe/YLib1ZBaKyj7G9O5+HmqtUAUZld/AdoJZzOXmz2EeYhD+R7wxh1xz4rCBpW3qOKvDS3jJxmZaIOoHv6/RWFxb0WGFrGcrTrX3EaWDLmWxr4pNlP5qsbECgYATllntrBR8/ycyEAX/SuWcHlaZM5BAh0zvm8+GGdCmDYWMqxjs0duL9URd4o+ynWJaKqR5c2KjA4r2tRdcP+Cqo7j2L5fbiAKtnQ7JvEGJaYsm72+nBuf+MrVkRZUepBhFg5r7rNu31zoAO+pTvQetNWvXeozRz93ckrjlPEtYaQKBgQDFwbV92rlRMLjZzlY+o0knoeJBjPQmPdiBTpGNimdy9L4c2Ure7affjcUiYhkKqrK5k5SScJTATgyQ7JF346FdtUtZ/6Kkj1RwJmmprPrDa9CATLoTle7g9OVd4sHT2ITHZMzPaF3ILvzcwJ70AD1xcxCQb+/7sDPmw7Mc8gOA7Q=="); aliPayConfigStorage.setNotifyUrl("http://pay.egzosn.com/payBack.json"); aliPayConfigStorage.setReturnUrl("http://pay.egzosn.com/payBack.html"); - aliPayConfigStorage.setSignType(SignUtils.RSA.name()); + aliPayConfigStorage.setSignType(SignUtils.RSA2.name()); aliPayConfigStorage.setSeller("2088102169916436"); aliPayConfigStorage.setInputCharset("utf-8"); //是否为测试账号,沙箱环境 diff --git a/pay-java-demo/src/main/resources/ali/alipayCertPublicKey_RSA2.crt b/pay-java-demo/src/main/resources/ali/alipayCertPublicKey_RSA2.crt new file mode 100644 index 0000000..75b39f2 --- /dev/null +++ b/pay-java-demo/src/main/resources/ali/alipayCertPublicKey_RSA2.crt @@ -0,0 +1,22 @@ +-----BEGIN CERTIFICATE----- +MIIDqDCCApCgAwIBAgIQIBkQFQ/dyVOPUjiooDnPNjANBgkqhkiG9w0BAQsFADCBkTELMAkGA1UE +BhMCQ04xGzAZBgNVBAoMEkFudCBGaW5hbmNpYWwgdGVzdDElMCMGA1UECwwcQ2VydGlmaWNhdGlv +biBBdXRob3JpdHkgdGVzdDE+MDwGA1UEAww1QW50IEZpbmFuY2lhbCBDZXJ0aWZpY2F0aW9uIEF1 +dGhvcml0eSBDbGFzcyAyIFIxIHRlc3QwHhcNMTkxMDE1MTMzMjEzWhcNMjIxMDEzMTMzMjEzWjB6 +MQswCQYDVQQGEwJDTjEVMBMGA1UECgwM5rKZ566x546v5aKDMQ8wDQYDVQQLDAZBbGlwYXkxQzBB +BgNVBAMMOuaUr+S7mOWunSjkuK3lm70p572R57uc5oqA5pyv5pyJ6ZmQ5YWs5Y+4LTIwODgxMDIx +Njk5MTY0MzYwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC0iWE/WO7qDt8whynlxH6/ +tuTtFQj6KtHcdSbR6ff0r9ScYHIC6UYUtlT3/8jAi7fEJN8aREdZXy/2IwmE2bFapvHB6FuVMKXh +/i4QPP2cQJzVDM56wY/HqcyxsDHz7eP9nanU4KT4azOz+dQESNVj+Xw05CgTmQQ3JaXRYFMU8841 +abxXTZg7x03HS7b16CHR2TXs55L6LkbQX45jvFad+NKs6o9gC2ij1xwRBQhuGJbPlfdOqaTOM0vc +miOy+vKYvKb3vPQI5h+XVGU/y2piE0oszQWZWd1F9w70eBPHWY7i2PaGwNk38CcBve9QtoyURLEi +D6LrJQGkQ6LCP7MtAgMBAAGjEjAQMA4GA1UdDwEB/wQEAwIE8DANBgkqhkiG9w0BAQsFAAOCAQEA +j6xIDkX/GbcQoeMTrJIP2sI4AsStxwG5QjYh021hA74agx0XGQyWrRhXtKpIyEfNxwSZaWgoJqZ3 +uBGTMfiVxjoI1xvHrv5kLQAU8lAt3thdwMYkwP56u51jT15V56OzwvWycGplEwGBw/ln6nc6lwcA +FTPnBR82Wv0J3+B6iSyw/bqXAiWM9ftIv9ex10YIjBRGC0kK0OFS2h1Cq/lEuFSW59cbo5h901Jx +yPVVa6qPYdkLatI/Cxexan+oKJG1BpUoRsKx95wPPV/jxtjP/b8i4qvq71V/h35LaX0uMkgVMJj0 +7SFBl8ciLbSrWZfmMt/sXQszNIhXU/hZwslNQA== +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIE4jCCAsqgAwIBAgIIddq/0OOwJzIwDQYJKoZIhvcNAQELBQAwejELMAkGA1UEBhMCQ04xFjAUBgNVBAoMDUFudCBGaW5hbmNpYWwxIDAeBgNVBAsMF0NlcnRpZmljYXRpb24gQXV0aG9yaXR5MTEwLwYDVQQDDChBbnQgRmluYW5jaWFsIENlcnRpZmljYXRpb24gQXV0aG9yaXR5IFIxMB4XDTE4MDMyMjE0MzAzMVoXDTM3MTEyNjE0MzAzMVowgYIxCzAJBgNVBAYTAkNOMRYwFAYDVQQKDA1BbnQgRmluYW5jaWFsMSAwHgYDVQQLDBdDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTE5MDcGA1UEAwwwQW50IEZpbmFuY2lhbCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSBDbGFzcyAxIFIxMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA3OruCD7d4evJiEKxzJQYmwp5ziF7lT4fMpBb+WLme42Ulrkh4cJCDEOTUL29Yb8NyQ6cRe9UHLupI2HbByDZoSJl7nkxyi5NGJLaADC7wnFBJq39WMaVBzouFo0yQkkYNbbkJm+MsV4obu3l2xFGQx72bz6ThDJLpfYJbnGXqC4Bcyn8ubj1ddrJ0VsGdj/3Knmuo7XWLYqqN/qomK3LJIpfhVozi0b2FWQl+lE9urch+FVhXSg0AlRGn8FTOVNlKrY+hAKZGZhqC+J+BD4GL3hQZzVeNl0tMmSGz474lnt7DExNq33WfyJkn5UIoCfg8Tno7XTnocmBzbNYPq1aSQIDAQABo2MwYTAfBgNVHSMEGDAWgBRfdLQEwE8HWurlsdsio4dBspzhATAdBgNVHQ4EFgQUcQfiBGEW5OXyZesxD8ng9Dya1ZEwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwDQYJKoZIhvcNAQELBQADggIBAGkc6bDB4YQCa5D6IbgRtLfiqTQb5DeCp38uVKKrUl8ZCE5U+wXSu/fFhOkIs+Aq9tEOdPWgi7TAxPMMbfJRTAF20qK1qF/X6lwRYGSi7LBbEhmI7dYFKj7i7z6fupBMVaI4C4O8KJGfovp3qD/FkXyb13Lo8vL85Ll0BFk89Qbim0qYhW2JRsf9G46vBeIZaoMm8iMv9vvlVgMs30R96W+gZBgvlIE4ah+oOEUd+G/V74vTbaXtWI8gkmwCzs/yUGW2g2ERHqZ3ksq4xwL+mNmqRNzq3aC9iA4p0uoHp/els89vWHCUaPjHmEnhx+M843/WVjN8LWpoeQ+wc7Wz1jfYy0e+JXidqWkPn7qorlEQTfzcFBZh+YHnV6oVtcG5iYatRKVTAPA+RrjJnEEzn6hAIPsiYsLmdA18f6ruuUUuKRukAEbCQ9q9L1gyOkaz2LxZj5kOFyemDa3pjqESuHuazztnOvs6u4YrH03CPyK3G/6MhCNEJTxGDYy+8bRtNsTGRUbdmhZm/u8tjYIreNEy55f4WYlb72R6PODBLXmf4HWWPpyX1Zy9TEhmFsuPfelhBrdmBVM1iTwVFLW7gLqoEwzYhMt5KRPjmfCc2P2pcbpLnYNcbSYiykFsCa7jHG0137Jv8Z/QH2N9r4+xdER7SW40ndmD59ynmGvrWUMj +-----END CERTIFICATE----- diff --git a/pay-java-demo/src/main/resources/ali/alipayRootCert.crt b/pay-java-demo/src/main/resources/ali/alipayRootCert.crt new file mode 100644 index 0000000..76417c5 --- /dev/null +++ b/pay-java-demo/src/main/resources/ali/alipayRootCert.crt @@ -0,0 +1,88 @@ +-----BEGIN CERTIFICATE----- +MIIBszCCAVegAwIBAgIIaeL+wBcKxnswDAYIKoEcz1UBg3UFADAuMQswCQYDVQQG +EwJDTjEOMAwGA1UECgwFTlJDQUMxDzANBgNVBAMMBlJPT1RDQTAeFw0xMjA3MTQw +MzExNTlaFw00MjA3MDcwMzExNTlaMC4xCzAJBgNVBAYTAkNOMQ4wDAYDVQQKDAVO +UkNBQzEPMA0GA1UEAwwGUk9PVENBMFkwEwYHKoZIzj0CAQYIKoEcz1UBgi0DQgAE +MPCca6pmgcchsTf2UnBeL9rtp4nw+itk1Kzrmbnqo05lUwkwlWK+4OIrtFdAqnRT +V7Q9v1htkv42TsIutzd126NdMFswHwYDVR0jBBgwFoAUTDKxl9kzG8SmBcHG5Yti +W/CXdlgwDAYDVR0TBAUwAwEB/zALBgNVHQ8EBAMCAQYwHQYDVR0OBBYEFEwysZfZ +MxvEpgXBxuWLYlvwl3ZYMAwGCCqBHM9VAYN1BQADSAAwRQIgG1bSLeOXp3oB8H7b +53W+CKOPl2PknmWEq/lMhtn25HkCIQDaHDgWxWFtnCrBjH16/W3Ezn7/U/Vjo5xI +pDoiVhsLwg== +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIF0zCCA7ugAwIBAgIIH8+hjWpIDREwDQYJKoZIhvcNAQELBQAwejELMAkGA1UE +BhMCQ04xFjAUBgNVBAoMDUFudCBGaW5hbmNpYWwxIDAeBgNVBAsMF0NlcnRpZmlj +YXRpb24gQXV0aG9yaXR5MTEwLwYDVQQDDChBbnQgRmluYW5jaWFsIENlcnRpZmlj +YXRpb24gQXV0aG9yaXR5IFIxMB4XDTE4MDMyMTEzNDg0MFoXDTM4MDIyODEzNDg0 +MFowejELMAkGA1UEBhMCQ04xFjAUBgNVBAoMDUFudCBGaW5hbmNpYWwxIDAeBgNV +BAsMF0NlcnRpZmljYXRpb24gQXV0aG9yaXR5MTEwLwYDVQQDDChBbnQgRmluYW5j +aWFsIENlcnRpZmljYXRpb24gQXV0aG9yaXR5IFIxMIICIjANBgkqhkiG9w0BAQEF +AAOCAg8AMIICCgKCAgEAtytTRcBNuur5h8xuxnlKJetT65cHGemGi8oD+beHFPTk +rUTlFt9Xn7fAVGo6QSsPb9uGLpUFGEdGmbsQ2q9cV4P89qkH04VzIPwT7AywJdt2 +xAvMs+MgHFJzOYfL1QkdOOVO7NwKxH8IvlQgFabWomWk2Ei9WfUyxFjVO1LVh0Bp +dRBeWLMkdudx0tl3+21t1apnReFNQ5nfX29xeSxIhesaMHDZFViO/DXDNW2BcTs6 +vSWKyJ4YIIIzStumD8K1xMsoaZBMDxg4itjWFaKRgNuPiIn4kjDY3kC66Sl/6yTl +YUz8AybbEsICZzssdZh7jcNb1VRfk79lgAprm/Ktl+mgrU1gaMGP1OE25JCbqli1 +Pbw/BpPynyP9+XulE+2mxFwTYhKAwpDIDKuYsFUXuo8t261pCovI1CXFzAQM2w7H +DtA2nOXSW6q0jGDJ5+WauH+K8ZSvA6x4sFo4u0KNCx0ROTBpLif6GTngqo3sj+98 +SZiMNLFMQoQkjkdN5Q5g9N6CFZPVZ6QpO0JcIc7S1le/g9z5iBKnifrKxy0TQjtG +PsDwc8ubPnRm/F82RReCoyNyx63indpgFfhN7+KxUIQ9cOwwTvemmor0A+ZQamRe +9LMuiEfEaWUDK+6O0Gl8lO571uI5onYdN1VIgOmwFbe+D8TcuzVjIZ/zvHrAGUcC +AwEAAaNdMFswCwYDVR0PBAQDAgEGMAwGA1UdEwQFMAMBAf8wHQYDVR0OBBYEFF90 +tATATwda6uWx2yKjh0GynOEBMB8GA1UdIwQYMBaAFF90tATATwda6uWx2yKjh0Gy +nOEBMA0GCSqGSIb3DQEBCwUAA4ICAQCVYaOtqOLIpsrEikE5lb+UARNSFJg6tpkf +tJ2U8QF/DejemEHx5IClQu6ajxjtu0Aie4/3UnIXop8nH/Q57l+Wyt9T7N2WPiNq +JSlYKYbJpPF8LXbuKYG3BTFTdOVFIeRe2NUyYh/xs6bXGr4WKTXb3qBmzR02FSy3 +IODQw5Q6zpXj8prYqFHYsOvGCEc1CwJaSaYwRhTkFedJUxiyhyB5GQwoFfExCVHW +05ZFCAVYFldCJvUzfzrWubN6wX0DD2dwultgmldOn/W/n8at52mpPNvIdbZb2F41 +T0YZeoWnCJrYXjq/32oc1cmifIHqySnyMnavi75DxPCdZsCOpSAT4j4lAQRGsfgI +kkLPGQieMfNNkMCKh7qjwdXAVtdqhf0RVtFILH3OyEodlk1HYXqX5iE5wlaKzDop +PKwf2Q3BErq1xChYGGVS+dEvyXc/2nIBlt7uLWKp4XFjqekKbaGaLJdjYP5b2s7N +1dM0MXQ/f8XoXKBkJNzEiM3hfsU6DOREgMc1DIsFKxfuMwX3EkVQM1If8ghb6x5Y +jXayv+NLbidOSzk4vl5QwngO/JYFMkoc6i9LNwEaEtR9PhnrdubxmrtM+RjfBm02 +77q3dSWFESFQ4QxYWew4pHE0DpWbWy/iMIKQ6UZ5RLvB8GEcgt8ON7BBJeMc+Dyi +kT9qhqn+lw== +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIICiDCCAgygAwIBAgIIQX76UsB/30owDAYIKoZIzj0EAwMFADB6MQswCQYDVQQG +EwJDTjEWMBQGA1UECgwNQW50IEZpbmFuY2lhbDEgMB4GA1UECwwXQ2VydGlmaWNh +dGlvbiBBdXRob3JpdHkxMTAvBgNVBAMMKEFudCBGaW5hbmNpYWwgQ2VydGlmaWNh +dGlvbiBBdXRob3JpdHkgRTEwHhcNMTkwNDI4MTYyMDQ0WhcNNDkwNDIwMTYyMDQ0 +WjB6MQswCQYDVQQGEwJDTjEWMBQGA1UECgwNQW50IEZpbmFuY2lhbDEgMB4GA1UE +CwwXQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkxMTAvBgNVBAMMKEFudCBGaW5hbmNp +YWwgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgRTEwdjAQBgcqhkjOPQIBBgUrgQQA +IgNiAASCCRa94QI0vR5Up9Yr9HEupz6hSoyjySYqo7v837KnmjveUIUNiuC9pWAU +WP3jwLX3HkzeiNdeg22a0IZPoSUCpasufiLAnfXh6NInLiWBrjLJXDSGaY7vaokt +rpZvAdmjXTBbMAsGA1UdDwQEAwIBBjAMBgNVHRMEBTADAQH/MB0GA1UdDgQWBBRZ +4ZTgDpksHL2qcpkFkxD2zVd16TAfBgNVHSMEGDAWgBRZ4ZTgDpksHL2qcpkFkxD2 +zVd16TAMBggqhkjOPQQDAwUAA2gAMGUCMQD4IoqT2hTUn0jt7oXLdMJ8q4vLp6sg +wHfPiOr9gxreb+e6Oidwd2LDnC4OUqCWiF8CMAzwKs4SnDJYcMLf2vpkbuVE4dTH +Rglz+HGcTLWsFs4KxLsq7MuU+vJTBUeDJeDjdA== +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIDxTCCAq2gAwIBAgIUEMdk6dVgOEIS2cCP0Q43P90Ps5YwDQYJKoZIhvcNAQEF +BQAwajELMAkGA1UEBhMCQ04xEzARBgNVBAoMCmlUcnVzQ2hpbmExHDAaBgNVBAsM +E0NoaW5hIFRydXN0IE5ldHdvcmsxKDAmBgNVBAMMH2lUcnVzQ2hpbmEgQ2xhc3Mg +MiBSb290IENBIC0gRzMwHhcNMTMwNDE4MDkzNjU2WhcNMzMwNDE4MDkzNjU2WjBq +MQswCQYDVQQGEwJDTjETMBEGA1UECgwKaVRydXNDaGluYTEcMBoGA1UECwwTQ2hp +bmEgVHJ1c3QgTmV0d29yazEoMCYGA1UEAwwfaVRydXNDaGluYSBDbGFzcyAyIFJv +b3QgQ0EgLSBHMzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAOPPShpV +nJbMqqCw6Bz1kehnoPst9pkr0V9idOwU2oyS47/HjJXk9Rd5a9xfwkPO88trUpz5 +4GmmwspDXjVFu9L0eFaRuH3KMha1Ak01citbF7cQLJlS7XI+tpkTGHEY5pt3EsQg +wykfZl/A1jrnSkspMS997r2Gim54cwz+mTMgDRhZsKK/lbOeBPpWtcFizjXYCqhw +WktvQfZBYi6o4sHCshnOswi4yV1p+LuFcQ2ciYdWvULh1eZhLxHbGXyznYHi0dGN +z+I9H8aXxqAQfHVhbdHNzi77hCxFjOy+hHrGsyzjrd2swVQ2iUWP8BfEQqGLqM1g +KgWKYfcTGdbPB1MCAwEAAaNjMGEwHQYDVR0OBBYEFG/oAMxTVe7y0+408CTAK8hA +uTyRMB8GA1UdIwQYMBaAFG/oAMxTVe7y0+408CTAK8hAuTyRMA8GA1UdEwEB/wQF +MAMBAf8wDgYDVR0PAQH/BAQDAgEGMA0GCSqGSIb3DQEBBQUAA4IBAQBLnUTfW7hp +emMbuUGCk7RBswzOT83bDM6824EkUnf+X0iKS95SUNGeeSWK2o/3ALJo5hi7GZr3 +U8eLaWAcYizfO99UXMRBPw5PRR+gXGEronGUugLpxsjuynoLQu8GQAeysSXKbN1I +UugDo9u8igJORYA+5ms0s5sCUySqbQ2R5z/GoceyI9LdxIVa1RjVX8pYOj8JFwtn +DJN3ftSFvNMYwRuILKuqUYSHc2GPYiHVflDh5nDymCMOQFcFG3WsEuB+EYQPFgIU +1DHmdZcz7Llx8UOZXX2JupWCYzK1XhJb+r4hK5ncf/w8qGtYlmyJpxk3hr1TfUJX +Yf4Zr0fJsGuv +-----END CERTIFICATE----- \ No newline at end of file diff --git a/pay-java-demo/src/main/resources/ali/appCertPublicKey_2016080400165436.crt b/pay-java-demo/src/main/resources/ali/appCertPublicKey_2016080400165436.crt new file mode 100644 index 0000000..e5107d3 --- /dev/null +++ b/pay-java-demo/src/main/resources/ali/appCertPublicKey_2016080400165436.crt @@ -0,0 +1,19 @@ +-----BEGIN CERTIFICATE----- +MIIDjzCCAnegAwIBAgIQICARMFLSkg9etOjTIGp0hjANBgkqhkiG9w0BAQsFADCBkTELMAkGA1UE +BhMCQ04xGzAZBgNVBAoMEkFudCBGaW5hbmNpYWwgdGVzdDElMCMGA1UECwwcQ2VydGlmaWNhdGlv +biBBdXRob3JpdHkgdGVzdDE+MDwGA1UEAww1QW50IEZpbmFuY2lhbCBDZXJ0aWZpY2F0aW9uIEF1 +dGhvcml0eSBDbGFzcyAyIFIxIHRlc3QwHhcNMjAxMTMwMDMwMzAyWhcNMjMxMTI5MDMwMzAyWjBh +MQswCQYDVQQGEwJDTjEVMBMGA1UECgwM5rKZ566x546v5aKDMQ8wDQYDVQQLDAZBbGlwYXkxKjAo +BgNVBAMMITIwODgxMDIxNjk5MTY0MzYtMjAxNjA4MDQwMDE2NTQzNjCCASIwDQYJKoZIhvcNAQEB +BQADggEPADCCAQoCggEBALDswPYLC/+OdeywWNecbHcmUYXnuAlrZphslFXwyc2I9Wlfgdr7xmOd +AOG+StYfiEebNy6M6vbXceqWgylRJgHMI+JcZVFRS92KZ6NwcdOh3ucjiDqfO3PN97L9Nc0Meanu +9jek4hyMHRmfSmQ0DPa0oAWSpvitoc6pMHRADTMPbgKf7bonZQnJhFyB4fQa1JC5SubvJoay4pqS +H6q2BYXpHvWfk8wY5NDksfeLRZUd8IGQ0aAkLrIL3odHgPtyiyjIoPT6WnxQH81VRaXtO38os6AV +TCHQYpJgV+/no79UFXb7GoIYTg+VkRJ9W46rPm+OPHPDfEMBQmtXyFJf8AkCAwEAAaMSMBAwDgYD +VR0PAQH/BAQDAgTwMA0GCSqGSIb3DQEBCwUAA4IBAQChVg6GZf8LcjlMlIt7RhQZjbycGlRB5pp2 +bckojulTEoHbmjvjL9LRJAtjuHQ1Teuy3meK6iT/J+O8PWXEigLtX+K+5Xne3JpRcztAciRmWhLe +dAEvh1aPPmiG11ZDPC2dDOtTQVIleLpbFtYkqn9IVsU2n+2lmM7aMbw1969wADE4mov2X3m6bnuI +CzrPGpWiI0MwLp00e0sNHnHXpzENxtabBqabYvL/AH7QZgAmX0JTxYwx0zW7r1JiAc+42Eh/qwqE +TJa+tOY+Iig5GcFkc6fE7guTNGxz2OhKr2whNq6uRnLbA+Xwx11NhE1K3NHNrZZpSf3QW1//xJoC +zlBJ +-----END CERTIFICATE----- \ No newline at end of file diff --git "a/pay-java-demo/src/main/resources/ali/www.egzosn.com_\347\247\201\351\222\245.txt" "b/pay-java-demo/src/main/resources/ali/www.egzosn.com_\347\247\201\351\222\245.txt" new file mode 100644 index 0000000..33cfb63 --- /dev/null +++ "b/pay-java-demo/src/main/resources/ali/www.egzosn.com_\347\247\201\351\222\245.txt" @@ -0,0 +1 @@ +MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQCw7MD2Cwv/jnXssFjXnGx3JlGF57gJa2aYbJRV8MnNiPVpX4Ha+8ZjnQDhvkrWH4hHmzcujOr213HqloMpUSYBzCPiXGVRUUvdimejcHHTod7nI4g6nztzzfey/TXNDHmp7vY3pOIcjB0Zn0pkNAz2tKAFkqb4raHOqTB0QA0zD24Cn+26J2UJyYRcgeH0GtSQuUrm7yaGsuKakh+qtgWF6R71n5PMGOTQ5LH3i0WVHfCBkNGgJC6yC96HR4D7cosoyKD0+lp8UB/NVUWl7Tt/KLOgFUwh0GKSYFfv56O/VBV2+xqCGE4PlZESfVuOqz5vjjxzw3xDAUJrV8hSX/AJAgMBAAECggEBAKE0d3U4B4yo/2XUIH8EdgfykCFUSum6RFbpyBauORHfksyaSzV+ZvtomN8XhhSn0oJ8OMFfgM+86nz2+zdwSxMkMCYWTfLUAi4v59KRqAVO3kz4oS3Y3FDeAK3D7XuRvGFL7GgzAhtEx1cLPrsiehVn6s5pG15GxsIIgq/JlL1J88wn1zENLrVHmD6z/JpXvfb/RS1yR+5lyoohp4g0Ph9jJ3bCyUbRpK0QkPEzgAuWL0K2ITCL7PYHNAplI8d2xHHOLF9Qdjyx+ZrQ/RxtqzfyWzhqjsmp2qlgNCxWlt3woS9UhDB+nRvjEoWTJmIOszAMYuj8wGlX+3Ui3ALOdQECgYEA25EqnFPFinUnzgNvB6NYmh5STmZun6s4bUOLqwefKtEvrOtRwTu7sB7NIf37fizG3/MJUWHxiLy2/3ub4d2JxdDNBtJoEqnp6QB12qglCNa4CajdjtJa1dR81F9QvytsqEkmPYXFPPyviB0FcSIDAGMb3IbwvIfzBPY9WY8dJnECgYEAzkg3yKEFBZ8BU0WQ+3hyfKUoAhBEnxouxRSTBcXxwstJRiqaGTVe5aoJGQI+0xS7Z6q07XDtN2t97s6DnRLWbljsX6B64itzNhXRyzjdD3iZDU/KSw7khjhXf8XOZaj9eXmACDiUnkEn1xsM8bLiRGqB8y5f3aMY/RpuACGXnxkCgYEAx/zwT9Vpr1RIfjfYcJ+Su0X0994K0roUukj0tUJK8qf4gcsQ+y1aJe/YLib1ZBaKyj7G9O5+HmqtUAUZld/AdoJZzOXmz2EeYhD+R7wxh1xz4rCBpW3qOKvDS3jJxmZaIOoHv6/RWFxb0WGFrGcrTrX3EaWDLmWxr4pNlP5qsbECgYATllntrBR8/ycyEAX/SuWcHlaZM5BAh0zvm8+GGdCmDYWMqxjs0duL9URd4o+ynWJaKqR5c2KjA4r2tRdcP+Cqo7j2L5fbiAKtnQ7JvEGJaYsm72+nBuf+MrVkRZUepBhFg5r7rNu31zoAO+pTvQetNWvXeozRz93ckrjlPEtYaQKBgQDFwbV92rlRMLjZzlY+o0knoeJBjPQmPdiBTpGNimdy9L4c2Ure7affjcUiYhkKqrK5k5SScJTATgyQ7JF346FdtUtZ/6Kkj1RwJmmprPrDa9CATLoTle7g9OVd4sHT2ITHZMzPaF3ILvzcwJ70AD1xcxCQb+/7sDPmw7Mc8gOA7Q== \ No newline at end of file -- Gitee From fc60b4553ef2149c258ee3b3e531bcb7c2a4ac35 Mon Sep 17 00:00:00 2001 From: egzosn Date: Tue, 1 Dec 2020 22:42:52 +0800 Subject: [PATCH 162/299] 2.13.3 --- README.md | 2 +- pay-java-ali/pom.xml | 2 +- pay-java-baidu/pom.xml | 2 +- pay-java-common/pom.xml | 2 +- .../com/egzosn/pay/common/util/IOUtils.java | 60 +++++-------------- pay-java-demo/pom.xml | 2 +- pay-java-fuiou/pom.xml | 2 +- pay-java-payoneer/pom.xml | 2 +- pay-java-paypal/pom.xml | 2 +- pay-java-union/pom.xml | 2 +- pay-java-wx-youdian/pom.xml | 2 +- pay-java-wx/pom.xml | 2 +- .../com/egzosn/pay/wx/api/WxBillService.java | 8 +-- .../com/egzosn/pay/wx/api/WxPayService.java | 5 +- .../egzosn/pay/wx/bean/WxRefundResult.java | 11 ++-- pay-java-yiji/pom.xml | 2 +- pom.xml | 4 +- 17 files changed, 42 insertions(+), 70 deletions(-) diff --git a/README.md b/README.md index 576cce4..d904512 100644 --- a/README.md +++ b/README.md @@ -21,7 +21,7 @@ com.egzosn {module-name} - 2.13.2 + 2.13.3 ``` diff --git a/pay-java-ali/pom.xml b/pay-java-ali/pom.xml index cda91f0..e48d462 100644 --- a/pay-java-ali/pom.xml +++ b/pay-java-ali/pom.xml @@ -5,7 +5,7 @@ pay-java-parent com.egzosn - 2.13.3-SNAPSHOT + 2.13.3 4.0.0 pay-java-ali diff --git a/pay-java-baidu/pom.xml b/pay-java-baidu/pom.xml index 5e39ceb..4f4a174 100644 --- a/pay-java-baidu/pom.xml +++ b/pay-java-baidu/pom.xml @@ -5,7 +5,7 @@ pay-java-parent com.egzosn - 2.13.3-SNAPSHOT + 2.13.3 4.0.0 pay-java-baidu diff --git a/pay-java-common/pom.xml b/pay-java-common/pom.xml index 1c79c00..0adcfa5 100644 --- a/pay-java-common/pom.xml +++ b/pay-java-common/pom.xml @@ -5,7 +5,7 @@ pay-java-parent com.egzosn - 2.13.3-SNAPSHOT + 2.13.3 4.0.0 jar diff --git a/pay-java-common/src/main/java/com/egzosn/pay/common/util/IOUtils.java b/pay-java-common/src/main/java/com/egzosn/pay/common/util/IOUtils.java index 25c6761..9b15a56 100644 --- a/pay-java-common/src/main/java/com/egzosn/pay/common/util/IOUtils.java +++ b/pay-java-common/src/main/java/com/egzosn/pay/common/util/IOUtils.java @@ -16,29 +16,10 @@ */ package com.egzosn.pay.common.util; -import java.io.BufferedInputStream; -import java.io.BufferedReader; -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.CharArrayWriter; -import java.io.Closeable; -import java.io.EOFException; -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.io.OutputStream; -import java.io.OutputStreamWriter; -import java.io.PrintWriter; -import java.io.Reader; -import java.io.StringWriter; -import java.io.UnsupportedEncodingException; -import java.io.Writer; -import java.net.HttpURLConnection; -import java.net.ServerSocket; -import java.net.Socket; -import java.net.URI; -import java.net.URL; -import java.net.URLConnection; +import org.apache.commons.codec.Charsets; + +import java.io.*; +import java.net.*; import java.nio.channels.Selector; import java.nio.charset.Charset; import java.nio.charset.UnsupportedCharsetException; @@ -46,8 +27,6 @@ import java.util.ArrayList; import java.util.Collection; import java.util.List; -import org.apache.commons.codec.Charsets; - /** * General IO stream manipulation utilities. *

@@ -80,7 +59,6 @@ import org.apache.commons.codec.Charsets; * Origin of code: Excalibur. * * @version $Id: IOUtils.java 1326636 2012-04-16 14:54:53Z ggregory $ - * */ public class IOUtils { // NOTE: This class is focussed on InputStream, OutputStream, Reader and @@ -283,8 +261,7 @@ public class IOUtils { if (closeable != null) { closeable.close(); } - } - catch (IOException ioe) { + } catch (IOException ioe) { // ignore } } @@ -316,8 +293,7 @@ public class IOUtils { if (sock != null) { try { sock.close(); - } - catch (IOException ioe) { + } catch (IOException ioe) { // ignored } } @@ -350,8 +326,7 @@ public class IOUtils { if (selector != null) { try { selector.close(); - } - catch (IOException ioe) { + } catch (IOException ioe) { // ignored } } @@ -384,8 +359,7 @@ public class IOUtils { if (sock != null) { try { sock.close(); - } - catch (IOException ioe) { + } catch (IOException ioe) { // ignored } } @@ -409,7 +383,6 @@ public class IOUtils { * * @param input Stream to be fully buffered. * @return A fully buffered stream. - * @throws IOException if an I/O error occurs * @since 2.0 */ public static InputStream toBufferedInputStream(InputStream input) { @@ -612,8 +585,7 @@ public class IOUtils { URLConnection conn = url.openConnection(); try { return IOUtils.toByteArray(conn); - } - finally { + } finally { close(conn); } } @@ -631,8 +603,7 @@ public class IOUtils { InputStream inputStream = urlConn.getInputStream(); try { return IOUtils.toByteArray(inputStream); - } - finally { + } finally { inputStream.close(); } } @@ -864,8 +835,7 @@ public class IOUtils { InputStream inputStream = url.openStream(); try { return toString(inputStream, encoding); - } - finally { + } finally { inputStream.close(); } } @@ -2252,7 +2222,7 @@ public class IOUtils { * @param input where to read input from * @param buffer destination * @param offset inital offset into buffer - * @param length length to read, must be >= 0 + * @param length length to read, must * @return actual length read; may be less than requested if EOF was reached * @throws IOException if a read error occurs * @since 2.2 @@ -2298,7 +2268,7 @@ public class IOUtils { * @param input where to read input from * @param buffer destination * @param offset inital offset into buffer - * @param length length to read, must be >= 0 + * @param length length to read, * @return actual length read; may be less than requested if EOF was reached * @throws IOException if a read error occurs * @since 2.2 @@ -2344,7 +2314,7 @@ public class IOUtils { * @param input where to read input from * @param buffer destination * @param offset inital offset into buffer - * @param length length to read, must be >= 0 + * @param length length to read, must * @throws IOException if there is a problem reading the file * @throws IllegalArgumentException if length is negative * @throws EOFException if the number of characters read was incorrect @@ -2383,7 +2353,7 @@ public class IOUtils { * @param input where to read input from * @param buffer destination * @param offset inital offset into buffer - * @param length length to read, must be >= 0 + * @param length length to read * @throws IOException if there is a problem reading the file * @throws IllegalArgumentException if length is negative * @throws EOFException if the number of bytes read was incorrect diff --git a/pay-java-demo/pom.xml b/pay-java-demo/pom.xml index 79c6805..e844498 100644 --- a/pay-java-demo/pom.xml +++ b/pay-java-demo/pom.xml @@ -5,7 +5,7 @@ pay-java-parent com.egzosn - 2.13.3-SNAPSHOT + 2.13.3 4.0.0 war diff --git a/pay-java-fuiou/pom.xml b/pay-java-fuiou/pom.xml index 7d1b18e..32e6b7f 100644 --- a/pay-java-fuiou/pom.xml +++ b/pay-java-fuiou/pom.xml @@ -5,7 +5,7 @@ pay-java-parent com.egzosn - 2.13.3-SNAPSHOT + 2.13.3 4.0.0 pay-java-fuiou diff --git a/pay-java-payoneer/pom.xml b/pay-java-payoneer/pom.xml index 7a79e0c..dcc61df 100644 --- a/pay-java-payoneer/pom.xml +++ b/pay-java-payoneer/pom.xml @@ -5,7 +5,7 @@ pay-java-parent com.egzosn - 2.13.3-SNAPSHOT + 2.13.3 4.0.0 pay-java-payoneer diff --git a/pay-java-paypal/pom.xml b/pay-java-paypal/pom.xml index 0fba2f8..f330606 100644 --- a/pay-java-paypal/pom.xml +++ b/pay-java-paypal/pom.xml @@ -5,7 +5,7 @@ pay-java-parent com.egzosn - 2.13.3-SNAPSHOT + 2.13.3 4.0.0 diff --git a/pay-java-union/pom.xml b/pay-java-union/pom.xml index abff084..e154945 100644 --- a/pay-java-union/pom.xml +++ b/pay-java-union/pom.xml @@ -5,7 +5,7 @@ pay-java-parent com.egzosn - 2.13.3-SNAPSHOT + 2.13.3 4.0.0 diff --git a/pay-java-wx-youdian/pom.xml b/pay-java-wx-youdian/pom.xml index d43cfda..2b218bb 100644 --- a/pay-java-wx-youdian/pom.xml +++ b/pay-java-wx-youdian/pom.xml @@ -5,7 +5,7 @@ pay-java-parent com.egzosn - 2.13.3-SNAPSHOT + 2.13.3 4.0.0 pay-java-wx-youdian diff --git a/pay-java-wx/pom.xml b/pay-java-wx/pom.xml index f2dca76..9877cd3 100644 --- a/pay-java-wx/pom.xml +++ b/pay-java-wx/pom.xml @@ -5,7 +5,7 @@ pay-java-parent com.egzosn - 2.13.3-SNAPSHOT + 2.13.3 4.0.0 pay-java-wx diff --git a/pay-java-wx/src/main/java/com/egzosn/pay/wx/api/WxBillService.java b/pay-java-wx/src/main/java/com/egzosn/pay/wx/api/WxBillService.java index bf01bbd..eeed364 100644 --- a/pay-java-wx/src/main/java/com/egzosn/pay/wx/api/WxBillService.java +++ b/pay-java-wx/src/main/java/com/egzosn/pay/wx/api/WxBillService.java @@ -4,12 +4,12 @@ import java.util.Date; import java.util.Map; /** - * @description:账单接口 + * 账单接口 * @author: faymanwang - * @email: 1057438332@qq.com - * @time: 2020/7/31 11:21 + * email: 1057438332@qq.com + * time: 2020/7/31 11:21 */ public interface WxBillService { - public Map downloadbill(Date billDate, String billType, String path); + Map downloadbill(Date billDate, String billType, String path); } 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 4d9769a..84d3e4c 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 @@ -626,8 +626,9 @@ public class WxPayService extends BasePayService implements /** * GZIP解压缩 * - * @param input - * @return + * @param input 输入流账单 + * @return 解压后输入流 + * @throws IOException IOException */ public static InputStream uncompress(InputStream input) throws IOException { ByteArrayOutputStream out = new ByteArrayOutputStream(); diff --git a/pay-java-wx/src/main/java/com/egzosn/pay/wx/bean/WxRefundResult.java b/pay-java-wx/src/main/java/com/egzosn/pay/wx/bean/WxRefundResult.java index e17c385..ce3ade7 100644 --- a/pay-java-wx/src/main/java/com/egzosn/pay/wx/bean/WxRefundResult.java +++ b/pay-java-wx/src/main/java/com/egzosn/pay/wx/bean/WxRefundResult.java @@ -11,9 +11,10 @@ import com.egzosn.pay.common.bean.CurType; /** * 微信退款结果 * @author Egan - *


+ * 
  * email egzosn@gmail.com
  * date 2020/8/16 21:29
+ * 
*/ public class WxRefundResult extends BaseRefundResult { @@ -123,7 +124,7 @@ public class WxRefundResult extends BaseRefundResult { private BigDecimal refundFee; /** * 应结退款金额 - * 去掉非充值代金券退款金额后的退款金额,退款金额=申请退款金额-非充值代金券退款金额,退款金额<=申请退款金额 + * 去掉非充值代金券退款金额后的退款金额,退款金额=申请退款金额-非充值代金券退款金额,退款金额<=申请退款金额 */ @JSONField(name = "settlement_refund_fee") private BigDecimal settlementRefundFee; @@ -135,7 +136,7 @@ public class WxRefundResult extends BaseRefundResult { private BigDecimal totalFee; /** * 应结订单金额 - * 去掉非充值代金券金额后的订单总金额,应结订单金额=订单金额-非充值代金券金额,应结订单金额<=订单金额。 + * 去掉非充值代金券金额后的订单总金额,应结订单金额=订单金额-非充值代金券金额,应结订单金额<=订单金额。 */ @JSONField(name = "settlement_total_fee") private BigDecimal settlementTotalFee; @@ -176,13 +177,13 @@ public class WxRefundResult extends BaseRefundResult { private String couponType0; /** * 代金券退款总金额 - * 代金券退款金额<=退款金额,退款金额-代金券或立减优惠退款金额为现金,说明详见代金券或立减优惠 + * 代金券退款金额<=退款金额,退款金额-代金券或立减优惠退款金额为现金,说明详见代金券或立减优惠 */ @JSONField(name = "coupon_refund_fee") private BigDecimal couponRefundFee; /** * 单个代金券退款金额 - * 代金券退款金额<=退款金额,退款金额-代金券或立减优惠退款金额为现金,说明详见代金券或立减优惠 + * 代金券退款金额<=退款金额,退款金额-代金券或立减优惠退款金额为现金,说明详见代金券或立减优惠 * 这里只接收0的,其余请自行获取 */ @JSONField(name = "coupon_refund_fee_0") diff --git a/pay-java-yiji/pom.xml b/pay-java-yiji/pom.xml index 45ce907..28b4f3d 100644 --- a/pay-java-yiji/pom.xml +++ b/pay-java-yiji/pom.xml @@ -5,7 +5,7 @@ pay-java-parent com.egzosn - 2.13.3-SNAPSHOT + 2.13.3 4.0.0 diff --git a/pom.xml b/pom.xml index 4a5dee5..43d418c 100644 --- a/pom.xml +++ b/pom.xml @@ -7,7 +7,7 @@ com.egzosn pay-java-parent pom - 2.13.3-SNAPSHOT + 2.13.3 Pay Java - Parent Pay Java Parent @@ -64,7 +64,7 @@ - 2.13.3-SNAPSHOT + 2.13.3 4.5.4 1.2.17 1.2.73 -- Gitee From d5a46707e2ce1d3b37fd450b7d002e1caa2482a3 Mon Sep 17 00:00:00 2001 From: egzosn Date: Tue, 1 Dec 2020 22:50:29 +0800 Subject: [PATCH 163/299] =?UTF-8?q?2.13.4=E5=BC=80=E5=8F=91=E5=BC=80?= =?UTF-8?q?=E5=A7=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pay-java-ali/pom.xml | 2 +- .../src/main/java/com/egzosn/pay/ali/api/AliPayService.java | 2 +- pay-java-baidu/pom.xml | 2 +- pay-java-common/pom.xml | 2 +- pay-java-demo/pom.xml | 2 +- pay-java-fuiou/pom.xml | 2 +- pay-java-payoneer/pom.xml | 2 +- pay-java-paypal/pom.xml | 2 +- pay-java-union/pom.xml | 2 +- pay-java-wx-youdian/pom.xml | 2 +- pay-java-wx/pom.xml | 2 +- pay-java-yiji/pom.xml | 2 +- pom.xml | 4 ++-- 13 files changed, 14 insertions(+), 14 deletions(-) diff --git a/pay-java-ali/pom.xml b/pay-java-ali/pom.xml index e48d462..f31a779 100644 --- a/pay-java-ali/pom.xml +++ b/pay-java-ali/pom.xml @@ -5,7 +5,7 @@ pay-java-parent com.egzosn - 2.13.3 + 2.13.4-SNAPSHOT 4.0.0 pay-java-ali 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 c0dcb7d..31c7110 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 @@ -245,7 +245,7 @@ public class AliPayService extends BasePayService { break; case WAP: bizContent.put(PASSBACK_PARAMS, order.getAddition()); - bizContent.put(PRODUCT_CODE, "QUICK_WAP_PAY"); + bizContent.put(PRODUCT_CODE, "QUICK_WAP_WAY"); setReturnUrl(orderInfo, order); break; case APP: diff --git a/pay-java-baidu/pom.xml b/pay-java-baidu/pom.xml index 4f4a174..228a3d0 100644 --- a/pay-java-baidu/pom.xml +++ b/pay-java-baidu/pom.xml @@ -5,7 +5,7 @@ pay-java-parent com.egzosn - 2.13.3 + 2.13.4-SNAPSHOT 4.0.0 pay-java-baidu diff --git a/pay-java-common/pom.xml b/pay-java-common/pom.xml index 0adcfa5..e0ad8d1 100644 --- a/pay-java-common/pom.xml +++ b/pay-java-common/pom.xml @@ -5,7 +5,7 @@ pay-java-parent com.egzosn - 2.13.3 + 2.13.4-SNAPSHOT 4.0.0 jar diff --git a/pay-java-demo/pom.xml b/pay-java-demo/pom.xml index e844498..7c0de2a 100644 --- a/pay-java-demo/pom.xml +++ b/pay-java-demo/pom.xml @@ -5,7 +5,7 @@ pay-java-parent com.egzosn - 2.13.3 + 2.13.4-SNAPSHOT 4.0.0 war diff --git a/pay-java-fuiou/pom.xml b/pay-java-fuiou/pom.xml index 32e6b7f..6a834c7 100644 --- a/pay-java-fuiou/pom.xml +++ b/pay-java-fuiou/pom.xml @@ -5,7 +5,7 @@ pay-java-parent com.egzosn - 2.13.3 + 2.13.4-SNAPSHOT 4.0.0 pay-java-fuiou diff --git a/pay-java-payoneer/pom.xml b/pay-java-payoneer/pom.xml index dcc61df..9df1ded 100644 --- a/pay-java-payoneer/pom.xml +++ b/pay-java-payoneer/pom.xml @@ -5,7 +5,7 @@ pay-java-parent com.egzosn - 2.13.3 + 2.13.4-SNAPSHOT 4.0.0 pay-java-payoneer diff --git a/pay-java-paypal/pom.xml b/pay-java-paypal/pom.xml index f330606..0abbb06 100644 --- a/pay-java-paypal/pom.xml +++ b/pay-java-paypal/pom.xml @@ -5,7 +5,7 @@ pay-java-parent com.egzosn - 2.13.3 + 2.13.4-SNAPSHOT 4.0.0 diff --git a/pay-java-union/pom.xml b/pay-java-union/pom.xml index e154945..6c539c1 100644 --- a/pay-java-union/pom.xml +++ b/pay-java-union/pom.xml @@ -5,7 +5,7 @@ pay-java-parent com.egzosn - 2.13.3 + 2.13.4-SNAPSHOT 4.0.0 diff --git a/pay-java-wx-youdian/pom.xml b/pay-java-wx-youdian/pom.xml index 2b218bb..2081227 100644 --- a/pay-java-wx-youdian/pom.xml +++ b/pay-java-wx-youdian/pom.xml @@ -5,7 +5,7 @@ pay-java-parent com.egzosn - 2.13.3 + 2.13.4-SNAPSHOT 4.0.0 pay-java-wx-youdian diff --git a/pay-java-wx/pom.xml b/pay-java-wx/pom.xml index 9877cd3..dfe0e5d 100644 --- a/pay-java-wx/pom.xml +++ b/pay-java-wx/pom.xml @@ -5,7 +5,7 @@ pay-java-parent com.egzosn - 2.13.3 + 2.13.4-SNAPSHOT 4.0.0 pay-java-wx diff --git a/pay-java-yiji/pom.xml b/pay-java-yiji/pom.xml index 28b4f3d..0a3ac4e 100644 --- a/pay-java-yiji/pom.xml +++ b/pay-java-yiji/pom.xml @@ -5,7 +5,7 @@ pay-java-parent com.egzosn - 2.13.3 + 2.13.4-SNAPSHOT 4.0.0 diff --git a/pom.xml b/pom.xml index 43d418c..fc3322b 100644 --- a/pom.xml +++ b/pom.xml @@ -7,7 +7,7 @@ com.egzosn pay-java-parent pom - 2.13.3 + 2.13.4-SNAPSHOT Pay Java - Parent Pay Java Parent @@ -64,7 +64,7 @@ - 2.13.3 + 2.13.4-SNAPSHOT 4.5.4 1.2.17 1.2.73 -- Gitee From 75d5ee32182064c2a22274ad824d206e64e9b1c4 Mon Sep 17 00:00:00 2001 From: wangshirui Date: Wed, 2 Dec 2020 18:03:28 +0800 Subject: [PATCH 164/299] =?UTF-8?q?[pay-common]=20Add:=20payBack=E7=9A=84?= =?UTF-8?q?=E6=96=B0=E9=87=8D=E8=BD=BD=E6=96=B9=E6=B3=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../egzosn/pay/common/api/BasePayService.java | 24 +++++++++++++++++++ .../com/egzosn/pay/common/api/PayService.java | 8 +++++++ 2 files changed, 32 insertions(+) diff --git a/pay-java-common/src/main/java/com/egzosn/pay/common/api/BasePayService.java b/pay-java-common/src/main/java/com/egzosn/pay/common/api/BasePayService.java index 605ed7d..a86cfc9 100644 --- a/pay-java-common/src/main/java/com/egzosn/pay/common/api/BasePayService.java +++ b/pay-java-common/src/main/java/com/egzosn/pay/common/api/BasePayService.java @@ -436,6 +436,30 @@ public abstract class BasePayService implements Pay return getPayMessageHandler().handle(payMessage, context, this); } + /** + * 使用转换过的参数进行回调处理 + * + * @param data 转化后的参数Map + * @return 获得回调响应信息 + */ + @Override + public PayOutMessage payBack(Map data) { + if (LOG.isDebugEnabled()) { + LOG.debug("回调响应:" + JSON.toJSONString(data)); + } + if (!verify(data)) { + return getPayOutMessage("fail", "失败"); + } + PayMessage payMessage = this.createMessage(data); + Map context = new HashMap(); + for (PayMessageInterceptor interceptor : interceptors) { + if (!interceptor.intercept(payMessage, context, this)) { + return successPayOutMessage(payMessage); + } + } + return getPayMessageHandler().handle(payMessage, context, this); + } + /** * 创建消息 * diff --git a/pay-java-common/src/main/java/com/egzosn/pay/common/api/PayService.java b/pay-java-common/src/main/java/com/egzosn/pay/common/api/PayService.java index 0c1b6ac..fa4cb17 100644 --- a/pay-java-common/src/main/java/com/egzosn/pay/common/api/PayService.java +++ b/pay-java-common/src/main/java/com/egzosn/pay/common/api/PayService.java @@ -379,6 +379,14 @@ public interface PayService { */ PayOutMessage payBack(Map parameterMap, InputStream is); + /** + * 使用转换过的参数进行回调处理 + * + * @param data 转化后的参数Map + * @return 获得回调响应信息 + */ + PayOutMessage payBack(Map data); + /** * 设置支付消息处理器,这里用于处理具体的支付业务 * -- Gitee From ea03f94668c1dcbe0c5c9b40b17a98ae53c6e1b1 Mon Sep 17 00:00:00 2001 From: egan Date: Fri, 4 Dec 2020 14:56:43 +0800 Subject: [PATCH 165/299] update pay-java-wx/README.md. --- pay-java-wx/README.md | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/pay-java-wx/README.md b/pay-java-wx/README.md index 488a177..da29557 100644 --- a/pay-java-wx/README.md +++ b/pay-java-wx/README.md @@ -112,6 +112,21 @@ ``` + +#### JSAPI支付 + +```java + + /*-----------JSAPI-------------------*/ + //公众号支付 + payOrder.setTransactionType(WxTransactionType.JSAPI); + //微信公众号对应微信付款用户的唯一标识 + payOrder.setOpenid(openid); + Map appOrderInfo = service.orderInfo(payOrder); + /*-----------/JSAPI-------------------*/ + +``` + #### 网页支付 ```java -- Gitee From 3805fcdc698c78ff37556f862cc1293116b05996 Mon Sep 17 00:00:00 2001 From: egzosn Date: Mon, 28 Dec 2020 10:23:30 +0800 Subject: [PATCH 166/299] =?UTF-8?q?=E9=80=80=E6=AC=BE=E5=BA=8F=E5=88=97?= =?UTF-8?q?=E5=8F=B7=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../egzosn/pay/common/bean/RefundResult.java | 20 +++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/pay-java-common/src/main/java/com/egzosn/pay/common/bean/RefundResult.java b/pay-java-common/src/main/java/com/egzosn/pay/common/bean/RefundResult.java index 50ca0c1..1304419 100644 --- a/pay-java-common/src/main/java/com/egzosn/pay/common/bean/RefundResult.java +++ b/pay-java-common/src/main/java/com/egzosn/pay/common/bean/RefundResult.java @@ -6,15 +6,16 @@ import java.util.Map; /** * 退款结果 - * + *

* 这里继承Map为兼容方案,后续版本进行删除 + * * @author Egan *

  * email egzosn@gmail.com
  * date 2020/8/16 9:55
  * 
*/ -public interface RefundResult extends Map, Serializable { +public interface RefundResult extends Serializable { /** * 获取退款结果原信息集 * @@ -29,6 +30,7 @@ public interface RefundResult extends Map, Serializable { * @return 属性值 */ Object getAttr(String key); + /** * 获取退款结果属性值 * @@ -36,6 +38,7 @@ public interface RefundResult extends Map, Serializable { * @return 属性值 */ String getAttrString(String key); + /** * 获取退款结果属性值 * @@ -47,42 +50,49 @@ public interface RefundResult extends Map, Serializable { /** * 获取退款请求结果状态码 + * * @return 状态码 */ String getCode(); /** * 获取退款请求结果状态提示信息 + * * @return 提示信息 */ String getMsg(); /** * 返回业务结果状态码 + * * @return 业务结果状态码 */ String getResultCode(); /** * 返回业务结果状态提示信息 + * * @return 业务结果状态提示信息 */ String getResultMsg(); /** * 退款金额 + * * @return 退款金额 */ BigDecimal getRefundFee(); + /** * 退款币种信息 + * * @return 币种信息 */ CurType getRefundCurrency(); /** - * 支付平台交易号 - * 发起支付时 支付平台(如支付宝)返回的交易订单号 + * 支付平台交易号 + * 发起支付时 支付平台(如支付宝)返回的交易订单号 * * @return 支付平台交易号 */ @@ -91,12 +101,14 @@ public interface RefundResult extends Map, Serializable { /** * 支付订单号 * 发起支付时,用户系统的订单号 + * * @return 支付订单号 */ String getOutTradeNo(); /** * 商户退款单号 + * * @return 商户退款单号 */ String getRefundNo(); -- Gitee From 6a72cc0906b9ec5ab484a2b74a386d375b53f987 Mon Sep 17 00:00:00 2001 From: egzosn Date: Mon, 28 Dec 2020 10:40:54 +0800 Subject: [PATCH 167/299] =?UTF-8?q?=E6=94=AF=E4=BB=98=E5=AE=9D=E5=85=AC?= =?UTF-8?q?=E9=92=A5=E8=AF=81=E4=B9=A6=E6=A1=88=E4=BE=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../pay/common/bean/BaseRefundResult.java | 259 ------------------ 1 file changed, 259 deletions(-) diff --git a/pay-java-common/src/main/java/com/egzosn/pay/common/bean/BaseRefundResult.java b/pay-java-common/src/main/java/com/egzosn/pay/common/bean/BaseRefundResult.java index b92763a..100a7b3 100644 --- a/pay-java-common/src/main/java/com/egzosn/pay/common/bean/BaseRefundResult.java +++ b/pay-java-common/src/main/java/com/egzosn/pay/common/bean/BaseRefundResult.java @@ -1,9 +1,7 @@ package com.egzosn.pay.common.bean; import java.math.BigDecimal; -import java.util.Collection; import java.util.Map; -import java.util.Set; /** * 基础的退款结果对象 @@ -77,261 +75,4 @@ public abstract class BaseRefundResult implements RefundResult { return new BigDecimal(getAttrString(key)); } - /** - * Returns the number of key-value mappings in this map. If the - * map contains more than Integer.MAX_VALUE elements, returns - * Integer.MAX_VALUE. - * - * @return the number of key-value mappings in this map - */ - @Override - public int size() { - return attrs.size(); - } - - /** - * Returns true if this map contains no key-value mappings. - * - * @return true if this map contains no key-value mappings - */ - @Override - public boolean isEmpty() { - return attrs.isEmpty(); - } - - /** - * Returns true if this map contains a mapping for the specified - * key. More formally, returns true if and only if - * this map contains a mapping for a key k such that - * (key==null ? k==null : key.equals(k)). (There can be - * at most one such mapping.) - * - * @param key key whose presence in this map is to be tested - * @return true if this map contains a mapping for the specified - * key - * @throws ClassCastException if the key is of an inappropriate type for - * this map - * (
optional) - * @throws NullPointerException if the specified key is null and this map - * does not permit null keys - * (optional) - */ - @Override - public boolean containsKey(Object key) { - return attrs.containsKey(key); - } - - /** - * Returns true if this map maps one or more keys to the - * specified value. More formally, returns true if and only if - * this map contains at least one mapping to a value v such that - * (value==null ? v==null : value.equals(v)). This operation - * will probably require time linear in the map size for most - * implementations of the Map interface. - * - * @param value value whose presence in this map is to be tested - * @return true if this map maps one or more keys to the - * specified value - * @throws ClassCastException if the value is of an inappropriate type for - * this map - * (optional) - * @throws NullPointerException if the specified value is null and this - * map does not permit null values - * (optional) - */ - @Override - public boolean containsValue(Object value) { - return attrs.containsValue(value); - } - - /** - * Returns the value to which the specified key is mapped, - * or {@code null} if this map contains no mapping for the key. - * - *

More formally, if this map contains a mapping from a key - * {@code k} to a value {@code v} such that {@code (key==null ? k==null : - * key.equals(k))}, then this method returns {@code v}; otherwise - * it returns {@code null}. (There can be at most one such mapping.) - * - *

If this map permits null values, then a return value of - * {@code null} does not necessarily indicate that the map - * contains no mapping for the key; it's also possible that the map - * explicitly maps the key to {@code null}. The {@link #containsKey - * containsKey} operation may be used to distinguish these two cases. - * - * @param key the key whose associated value is to be returned - * @return the value to which the specified key is mapped, or - * {@code null} if this map contains no mapping for the key - * @throws ClassCastException if the key is of an inappropriate type for - * this map - * (optional) - * @throws NullPointerException if the specified key is null and this map - * does not permit null keys - * (optional) - */ - @Override - public Object get(Object key) { - return attrs.get(key); - } - - /** - * Associates the specified value with the specified key in this map - * (optional operation). If the map previously contained a mapping for - * the key, the old value is replaced by the specified value. (A map - * m is said to contain a mapping for a key k if and only - * if {@link #containsKey(Object) m.containsKey(k)} would return - * true.) - * - * @param key key with which the specified value is to be associated - * @param value value to be associated with the specified key - * @return the previous value associated with key, or - * null if there was no mapping for key. - * (A null return can also indicate that the map - * previously associated null with key, - * if the implementation supports null values.) - * @throws UnsupportedOperationException if the put operation - * is not supported by this map - * @throws ClassCastException if the class of the specified key or value - * prevents it from being stored in this map - * @throws NullPointerException if the specified key or value is null - * and this map does not permit null keys or values - * @throws IllegalArgumentException if some property of the specified key - * or value prevents it from being stored in this map - */ - @Override - public Object put(String key, Object value) { - return attrs.put(key, value); - } - - /** - * Removes the mapping for a key from this map if it is present - * (optional operation). More formally, if this map contains a mapping - * from key k to value v such that - * (key==null ? k==null : key.equals(k)), that mapping - * is removed. (The map can contain at most one such mapping.) - * - *

Returns the value to which this map previously associated the key, - * or null if the map contained no mapping for the key. - * - *

If this map permits null values, then a return value of - * null does not necessarily indicate that the map - * contained no mapping for the key; it's also possible that the map - * explicitly mapped the key to null. - * - *

The map will not contain a mapping for the specified key once the - * call returns. - * - * @param key key whose mapping is to be removed from the map - * @return the previous value associated with key, or - * null if there was no mapping for key. - * @throws UnsupportedOperationException if the remove operation - * is not supported by this map - * @throws ClassCastException if the key is of an inappropriate type for - * this map - * (optional) - * @throws NullPointerException if the specified key is null and this - * map does not permit null keys - * (optional) - */ - @Override - public Object remove(Object key) { - return attrs.remove(key); - } - - /** - * Copies all of the mappings from the specified map to this map - * (optional operation). The effect of this call is equivalent to that - * of calling {@link #put(Object, Object) put(k, v)} on this map once - * for each mapping from key k to value v in the - * specified map. The behavior of this operation is undefined if the - * specified map is modified while the operation is in progress. - * - * @param m mappings to be stored in this map - * @throws UnsupportedOperationException if the putAll operation - * is not supported by this map - * @throws ClassCastException if the class of a key or value in the - * specified map prevents it from being stored in this map - * @throws NullPointerException if the specified map is null, or if - * this map does not permit null keys or values, and the - * specified map contains null keys or values - * @throws IllegalArgumentException if some property of a key or value in - * the specified map prevents it from being stored in this map - */ - @Override - public void putAll(Map m) { - attrs.putAll(m); - } - - /** - * Removes all of the mappings from this map (optional operation). - * The map will be empty after this call returns. - * - * @throws UnsupportedOperationException if the clear operation - * is not supported by this map - */ - @Override - public void clear() { - attrs.clear(); - } - - /** - * Returns a {@link Set} view of the keys contained in this map. - * The set is backed by the map, so changes to the map are - * reflected in the set, and vice-versa. If the map is modified - * while an iteration over the set is in progress (except through - * the iterator's own remove operation), the results of - * the iteration are undefined. The set supports element removal, - * which removes the corresponding mapping from the map, via the - * Iterator.remove, Set.remove, - * removeAll, retainAll, and clear - * operations. It does not support the add or addAll - * operations. - * - * @return a set view of the keys contained in this map - */ - @Override - public Set keySet() { - return attrs.keySet(); - } - - /** - * Returns a {@link Collection} view of the values contained in this map. - * The collection is backed by the map, so changes to the map are - * reflected in the collection, and vice-versa. If the map is - * modified while an iteration over the collection is in progress - * (except through the iterator's own remove operation), - * the results of the iteration are undefined. The collection - * supports element removal, which removes the corresponding - * mapping from the map, via the Iterator.remove, - * Collection.remove, removeAll, - * retainAll and clear operations. It does not - * support the add or addAll operations. - * - * @return a collection view of the values contained in this map - */ - @Override - public Collection values() { - return attrs.values(); - } - - /** - * Returns a {@link Set} view of the mappings contained in this map. - * The set is backed by the map, so changes to the map are - * reflected in the set, and vice-versa. If the map is modified - * while an iteration over the set is in progress (except through - * the iterator's own remove operation, or through the - * setValue operation on a map entry returned by the - * iterator) the results of the iteration are undefined. The set - * supports element removal, which removes the corresponding - * mapping from the map, via the Iterator.remove, - * Set.remove, removeAll, retainAll and - * clear operations. It does not support the - * add or addAll operations. - * - * @return a set view of the mappings contained in this map - */ - @Override - public Set> entrySet() { - return attrs.entrySet(); - } } -- Gitee From 0636608876ca593943e0d0f6794e275292c3d57a Mon Sep 17 00:00:00 2001 From: egzosn Date: Mon, 28 Dec 2020 10:43:30 +0800 Subject: [PATCH 168/299] =?UTF-8?q?=E6=94=AF=E4=BB=98=E5=AE=9D=E5=85=AC?= =?UTF-8?q?=E9=92=A5=E8=AF=81=E4=B9=A6=E6=A1=88=E4=BE=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pay-java-ali/pom.xml | 2 +- pay-java-baidu/pom.xml | 2 +- pay-java-common/pom.xml | 2 +- pay-java-demo/pom.xml | 2 +- pay-java-fuiou/pom.xml | 2 +- pay-java-payoneer/pom.xml | 2 +- pay-java-paypal/pom.xml | 2 +- pay-java-union/pom.xml | 2 +- .../egzosn/pay/union/api/UnionPayService.java | 2 +- pay-java-union/src/test/java/PayTest.java | 30 +++++++++---------- pay-java-wx-youdian/pom.xml | 2 +- pay-java-wx/pom.xml | 2 +- pay-java-yiji/pom.xml | 2 +- pom.xml | 6 ++-- 14 files changed, 30 insertions(+), 30 deletions(-) diff --git a/pay-java-ali/pom.xml b/pay-java-ali/pom.xml index f31a779..f014c96 100644 --- a/pay-java-ali/pom.xml +++ b/pay-java-ali/pom.xml @@ -5,7 +5,7 @@ pay-java-parent com.egzosn - 2.13.4-SNAPSHOT + 2.13.3-b1 4.0.0 pay-java-ali diff --git a/pay-java-baidu/pom.xml b/pay-java-baidu/pom.xml index 228a3d0..d336160 100644 --- a/pay-java-baidu/pom.xml +++ b/pay-java-baidu/pom.xml @@ -5,7 +5,7 @@ pay-java-parent com.egzosn - 2.13.4-SNAPSHOT + 2.13.3-b1 4.0.0 pay-java-baidu diff --git a/pay-java-common/pom.xml b/pay-java-common/pom.xml index e0ad8d1..2ca60b8 100644 --- a/pay-java-common/pom.xml +++ b/pay-java-common/pom.xml @@ -5,7 +5,7 @@ pay-java-parent com.egzosn - 2.13.4-SNAPSHOT + 2.13.3-b1 4.0.0 jar diff --git a/pay-java-demo/pom.xml b/pay-java-demo/pom.xml index 7c0de2a..6858975 100644 --- a/pay-java-demo/pom.xml +++ b/pay-java-demo/pom.xml @@ -5,7 +5,7 @@ pay-java-parent com.egzosn - 2.13.4-SNAPSHOT + 2.13.3-b1 4.0.0 war diff --git a/pay-java-fuiou/pom.xml b/pay-java-fuiou/pom.xml index 6a834c7..52f09fc 100644 --- a/pay-java-fuiou/pom.xml +++ b/pay-java-fuiou/pom.xml @@ -5,7 +5,7 @@ pay-java-parent com.egzosn - 2.13.4-SNAPSHOT + 2.13.3-b1 4.0.0 pay-java-fuiou diff --git a/pay-java-payoneer/pom.xml b/pay-java-payoneer/pom.xml index 9df1ded..d7e29fa 100644 --- a/pay-java-payoneer/pom.xml +++ b/pay-java-payoneer/pom.xml @@ -5,7 +5,7 @@ pay-java-parent com.egzosn - 2.13.4-SNAPSHOT + 2.13.3-b1 4.0.0 pay-java-payoneer diff --git a/pay-java-paypal/pom.xml b/pay-java-paypal/pom.xml index 0abbb06..3886fb3 100644 --- a/pay-java-paypal/pom.xml +++ b/pay-java-paypal/pom.xml @@ -5,7 +5,7 @@ pay-java-parent com.egzosn - 2.13.4-SNAPSHOT + 2.13.3-b1 4.0.0 diff --git a/pay-java-union/pom.xml b/pay-java-union/pom.xml index 6c539c1..a98787c 100644 --- a/pay-java-union/pom.xml +++ b/pay-java-union/pom.xml @@ -5,7 +5,7 @@ pay-java-parent com.egzosn - 2.13.4-SNAPSHOT + 2.13.3-b1 4.0.0 diff --git a/pay-java-union/src/main/java/com/egzosn/pay/union/api/UnionPayService.java b/pay-java-union/src/main/java/com/egzosn/pay/union/api/UnionPayService.java index d1ef681..6e576fb 100644 --- a/pay-java-union/src/main/java/com/egzosn/pay/union/api/UnionPayService.java +++ b/pay-java-union/src/main/java/com/egzosn/pay/union/api/UnionPayService.java @@ -580,7 +580,7 @@ public class UnionPayService extends BasePayService { * @param type UnionTransactionType.REFUND 或者UnionTransactionType.CONSUME_UNDO * @return 返回支付方申请退款后的结果 */ - public Map unionRefundOrConsumeUndo(String origQryId, String orderId, BigDecimal refundAmount, UnionTransactionType type) { + public UnionRefundResult unionRefundOrConsumeUndo(String origQryId, String orderId, BigDecimal refundAmount, UnionTransactionType type) { return unionRefundOrConsumeUndo(new RefundOrder(orderId, origQryId, refundAmount), type); } diff --git a/pay-java-union/src/test/java/PayTest.java b/pay-java-union/src/test/java/PayTest.java index 93ef3d5..659ea4c 100644 --- a/pay-java-union/src/test/java/PayTest.java +++ b/pay-java-union/src/test/java/PayTest.java @@ -1,18 +1,18 @@ +import java.awt.image.BufferedImage; +import java.math.BigDecimal; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.Map; + import com.egzosn.pay.common.bean.MethodType; import com.egzosn.pay.common.bean.PayOrder; import com.egzosn.pay.common.bean.RefundOrder; import com.egzosn.pay.union.api.UnionPayConfigStorage; import com.egzosn.pay.union.api.UnionPayService; +import com.egzosn.pay.union.bean.UnionRefundResult; import com.egzosn.pay.union.bean.UnionTransactionType; -import java.awt.image.BufferedImage; -import java.math.BigDecimal; -import java.text.SimpleDateFormat; -import java.util.Date; -import java.util.Map; - /** - * * Descrption:银联支付测试 * Author:Actinia * Date:2017/12/19 21:12 @@ -52,7 +52,7 @@ public class PayTest { //支付服务 UnionPayService service = new UnionPayService(unionPayConfigStorage); //支付订单基础信息 - PayOrder payOrder = new PayOrder("订单title", "摘要", BigDecimal.valueOf(0.01) , new SimpleDateFormat("yyyyMMddHHmmss").format(System.currentTimeMillis())); + PayOrder payOrder = new PayOrder("订单title", "摘要", BigDecimal.valueOf(0.01), new SimpleDateFormat("yyyyMMddHHmmss").format(System.currentTimeMillis())); /*----------- 网页支付-------------------*/ @@ -79,34 +79,34 @@ public class PayTest { /*-----------消费(被扫场景)待定------------------------------*/ payOrder.setTransactionType(UnionTransactionType.CONSUME); payOrder.setAuthCode("C2B码(条码号),1-20位数字"); - params = service.microPay(payOrder); + params = service.microPay(payOrder); /*-----------消费(被扫场景)------------------------------*/ // /*-----------消费撤销------------------------------*/ - params = service.unionRefundOrConsumeUndo(new RefundOrder( "订单号", "原交易查询流水号", new BigDecimal("退款金额" )),UnionTransactionType.CONSUME_UNDO); + UnionRefundResult refundResult = service.unionRefundOrConsumeUndo(new RefundOrder("订单号", "原交易查询流水号", new BigDecimal("退款金额")), UnionTransactionType.CONSUME_UNDO); // /*-----------消费撤销------------------------------*/ /*-----------交易状态查询交易:只有同步应答------------------------------*/ payOrder.setTransactionType(UnionTransactionType.QUERY); - params = service.query(null,"商户单号"); + params = service.query(null, "商户单号"); /*-----------交易状态查询交易:只有同步应答------------------------------*/ /*-----------退货交易:后台资金类交易,有同步应答和后台通知应答------------------------------*/ payOrder.setTransactionType(UnionTransactionType.REFUND); - params = service.refund(new RefundOrder("原交易查询流水号", "订单号", null, new BigDecimal("退款金额" ))); + refundResult = service.refund(new RefundOrder("原交易查询流水号", "订单号", null, new BigDecimal("退款金额"))); /*-----------退货交易:后台资金类交易,有同步应答和后台通知应答------------------------------*/ /*-----------文件传输类接口:后台获取对账文件交易,只有同步应答 ------------------------------*/ - Map fileConten = service.downloadbill(new Date(),"文件类型,一般商户填写00即可"); /*-----------退货交易:后台资金类交易,有同步应答和后台通知应答------------------------------*/ + Map fileConten = service.downloadbill(new Date(), "文件类型,一般商户填写00即可"); /*-----------退货交易:后台资金类交易,有同步应答和后台通知应答------------------------------*/ - /*-----------回调处理-------------------*/ + /*-----------回调处理-------------------*/ // HttpServletRequest request // params = service.getParameter2Map(request.getParameterMap(), request.getInputStream()); - if (service.verify(params)){ + if (service.verify(params)) { System.out.println("支付成功"); return; } diff --git a/pay-java-wx-youdian/pom.xml b/pay-java-wx-youdian/pom.xml index 2081227..c0db493 100644 --- a/pay-java-wx-youdian/pom.xml +++ b/pay-java-wx-youdian/pom.xml @@ -5,7 +5,7 @@ pay-java-parent com.egzosn - 2.13.4-SNAPSHOT + 2.13.3-b1 4.0.0 pay-java-wx-youdian diff --git a/pay-java-wx/pom.xml b/pay-java-wx/pom.xml index dfe0e5d..b9d7a20 100644 --- a/pay-java-wx/pom.xml +++ b/pay-java-wx/pom.xml @@ -5,7 +5,7 @@ pay-java-parent com.egzosn - 2.13.4-SNAPSHOT + 2.13.3-b1 4.0.0 pay-java-wx diff --git a/pay-java-yiji/pom.xml b/pay-java-yiji/pom.xml index 0a3ac4e..a03c038 100644 --- a/pay-java-yiji/pom.xml +++ b/pay-java-yiji/pom.xml @@ -5,7 +5,7 @@ pay-java-parent com.egzosn - 2.13.4-SNAPSHOT + 2.13.3-b1 4.0.0 diff --git a/pom.xml b/pom.xml index fc3322b..cfa1e34 100644 --- a/pom.xml +++ b/pom.xml @@ -7,7 +7,7 @@ com.egzosn pay-java-parent pom - 2.13.4-SNAPSHOT + 2.13.3-b1 Pay Java - Parent Pay Java Parent @@ -58,13 +58,13 @@ pay-java-paypal pay-java-yiji pay-java-baidu - pay-java-demo + - 2.13.4-SNAPSHOT + 2.13.3-b1 4.5.4 1.2.17 1.2.73 -- Gitee From 8b3e1f67917cb3e02beac7ba5cf39efbe8007040 Mon Sep 17 00:00:00 2001 From: egzosn Date: Mon, 28 Dec 2020 18:45:46 +0800 Subject: [PATCH 169/299] =?UTF-8?q?=E6=94=AF=E4=BB=98=E5=AE=9D=E5=85=AC?= =?UTF-8?q?=E9=92=A5=E8=AF=81=E4=B9=A6=E6=A1=88=E4=BE=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 2 +- .../egzosn/pay/common/bean/RefundResult.java | 1 - .../pay/paypal/api/PayPalConfigStorage.java | 1 + .../egzosn/pay/paypal/bean/order/Amount.java | 3 + .../pay/paypal/bean/order/CartBase.java | 45 +-- .../egzosn/pay/paypal/bean/order/Error.java | 279 ++++++++---------- .../pay/paypal/bean/order/ErrorDetails.java | 26 +- .../pay/paypal/bean/order/FmfDetails.java | 40 +-- .../egzosn/pay/paypal/bean/order/Order.java | 131 ++------ .../egzosn/pay/paypal/bean/order/Payee.java | 72 +---- .../egzosn/pay/paypal/bean/order/Phone.java | 40 +-- .../pay/paypal/bean/order/RedirectUrls.java | 26 +- .../egzosn/pay/paypal/bean/order/Refund.java | 121 ++------ pom.xml | 2 +- 14 files changed, 224 insertions(+), 565 deletions(-) diff --git a/README.md b/README.md index d904512..8675b94 100644 --- a/README.md +++ b/README.md @@ -21,7 +21,7 @@ com.egzosn {module-name} - 2.13.3 + 2.13.3-b1 ``` diff --git a/pay-java-common/src/main/java/com/egzosn/pay/common/bean/RefundResult.java b/pay-java-common/src/main/java/com/egzosn/pay/common/bean/RefundResult.java index 1304419..db31a45 100644 --- a/pay-java-common/src/main/java/com/egzosn/pay/common/bean/RefundResult.java +++ b/pay-java-common/src/main/java/com/egzosn/pay/common/bean/RefundResult.java @@ -7,7 +7,6 @@ import java.util.Map; /** * 退款结果 *

- * 这里继承Map为兼容方案,后续版本进行删除 * * @author Egan *

diff --git a/pay-java-paypal/src/main/java/com/egzosn/pay/paypal/api/PayPalConfigStorage.java b/pay-java-paypal/src/main/java/com/egzosn/pay/paypal/api/PayPalConfigStorage.java
index ccffe2e..3b29d41 100644
--- a/pay-java-paypal/src/main/java/com/egzosn/pay/paypal/api/PayPalConfigStorage.java
+++ b/pay-java-paypal/src/main/java/com/egzosn/pay/paypal/api/PayPalConfigStorage.java
@@ -59,6 +59,7 @@ public class PayPalConfigStorage extends BasePayConfigStorage {
 
     /**
      * 获取取消页面的url
+     * @return 取消页面的url
      */
     public String getCancelUrl() {
         return getNotifyUrl();
diff --git a/pay-java-paypal/src/main/java/com/egzosn/pay/paypal/bean/order/Amount.java b/pay-java-paypal/src/main/java/com/egzosn/pay/paypal/bean/order/Amount.java
index 34d645b..9d1ebb3 100644
--- a/pay-java-paypal/src/main/java/com/egzosn/pay/paypal/bean/order/Amount.java
+++ b/pay-java-paypal/src/main/java/com/egzosn/pay/paypal/bean/order/Amount.java
@@ -22,6 +22,9 @@ public class Amount {
 
 	/**
 	 * Parameterized Constructor
+	 *
+	 * @param currency 类型
+	 * @param total 金额
 	 */
 	public Amount(String currency, String total) {
 		this.currency = currency;
diff --git a/pay-java-paypal/src/main/java/com/egzosn/pay/paypal/bean/order/CartBase.java b/pay-java-paypal/src/main/java/com/egzosn/pay/paypal/bean/order/CartBase.java
index 077aceb..060b114 100644
--- a/pay-java-paypal/src/main/java/com/egzosn/pay/paypal/bean/order/CartBase.java
+++ b/pay-java-paypal/src/main/java/com/egzosn/pay/paypal/bean/order/CartBase.java
@@ -56,6 +56,7 @@ public class CartBase {
 
 	/**
 	 * Parameterized Constructor
+	 * @param amount 金额
 	 */
 	public CartBase(Amount amount) {
 		this.amount = amount;
@@ -63,72 +64,72 @@ public class CartBase {
 
 	/**
 	 * Merchant identifier to the purchase unit. Optional parameter
+	 * @return  identifier
 	 */
-	@SuppressWarnings("all")
 	public String getReferenceId() {
 		return this.referenceId;
 	}
 
 	/**
 	 * Amount being collected.
+	 * @return amount 金额
 	 */
-	@SuppressWarnings("all")
 	public Amount getAmount() {
 		return this.amount;
 	}
 
 	/**
 	 * Recipient of the funds in this transaction.
+	 * @return Recipient
 	 */
-	@SuppressWarnings("all")
 	public Payee getPayee() {
 		return this.payee;
 	}
 
 	/**
 	 * Description of what is being paid for.
+	 * @return Description
 	 */
-	@SuppressWarnings("all")
 	public String getDescription() {
 		return this.description;
 	}
 
 	/**
 	 * Note to the recipient of the funds in this transaction.
+	 * @return Note
 	 */
-	@SuppressWarnings("all")
 	public String getNoteToPayee() {
 		return this.noteToPayee;
 	}
 
 	/**
 	 * free-form field for the use of clients
+	 * @return custom
 	 */
-	@SuppressWarnings("all")
 	public String getCustom() {
 		return this.custom;
 	}
 
 	/**
 	 * invoice number to track this payment
+	 * @return invoice number
 	 */
-	@SuppressWarnings("all")
 	public String getInvoiceNumber() {
 		return this.invoiceNumber;
 	}
 
 	/**
 	 * Soft descriptor used when charging this funding source. If length exceeds max length, the value will be truncated
+	 * @return SoftDescriptor
 	 */
-	@SuppressWarnings("all")
 	public String getSoftDescriptor() {
 		return this.softDescriptor;
 	}
 
 	/**
 	 * Soft descriptor city used when charging this funding source. If length exceeds max length, the value will be truncated. Only supported when the `payment_method` is set to `credit_card`
+	 * @return Soft descriptor
 	 */
-	@SuppressWarnings("all")
 	public String getSoftDescriptorCity() {
 		return this.softDescriptorCity;
 	}
@@ -136,25 +137,25 @@ public class CartBase {
 
 	/**
 	 * URL to send payment notifications
+	 * @return URL
 	 */
-	@SuppressWarnings("all")
 	public String getNotifyUrl() {
 		return this.notifyUrl;
 	}
 
 	/**
 	 * Url on merchant site pertaining to this payment.
+	 * @return URL
 	 */
-	@SuppressWarnings("all")
 	public String getOrderUrl() {
 		return this.orderUrl;
 	}
 
 	/**
 	 * Merchant identifier to the purchase unit. Optional parameter
+	 * @param referenceId identifier
 	 * @return this
 	 */
-	@SuppressWarnings("all")
 	public CartBase setReferenceId(final String referenceId) {
 		this.referenceId = referenceId;
 		return this;
@@ -162,9 +163,9 @@ public class CartBase {
 
 	/**
 	 * Amount being collected.
+	 * @param amount 金额
 	 * @return this
 	 */
-	@SuppressWarnings("all")
 	public CartBase setAmount(final Amount amount) {
 		this.amount = amount;
 		return this;
@@ -172,9 +173,9 @@ public class CartBase {
 
 	/**
 	 * Recipient of the funds in this transaction.
+	 * @param payee Recipient
 	 * @return this
 	 */
-	@SuppressWarnings("all")
 	public CartBase setPayee(final Payee payee) {
 		this.payee = payee;
 		return this;
@@ -182,9 +183,9 @@ public class CartBase {
 
 	/**
 	 * Description of what is being paid for.
+	 * @param description description
 	 * @return this
 	 */
-	@SuppressWarnings("all")
 	public CartBase setDescription(final String description) {
 		this.description = description;
 		return this;
@@ -192,9 +193,9 @@ public class CartBase {
 
 	/**
 	 * Note to the recipient of the funds in this transaction.
+	 * @param noteToPayee noteToPayee
 	 * @return this
 	 */
-	@SuppressWarnings("all")
 	public CartBase setNoteToPayee(final String noteToPayee) {
 		this.noteToPayee = noteToPayee;
 		return this;
@@ -202,9 +203,9 @@ public class CartBase {
 
 	/**
 	 * free-form field for the use of clients
+	 * @param custom custom
 	 * @return this
 	 */
-	@SuppressWarnings("all")
 	public CartBase setCustom(final String custom) {
 		this.custom = custom;
 		return this;
@@ -212,9 +213,9 @@ public class CartBase {
 
 	/**
 	 * invoice number to track this payment
+	 * @param invoiceNumber invoiceNumber
 	 * @return this
 	 */
-	@SuppressWarnings("all")
 	public CartBase setInvoiceNumber(final String invoiceNumber) {
 		this.invoiceNumber = invoiceNumber;
 		return this;
@@ -222,9 +223,9 @@ public class CartBase {
 
 	/**
 	 * Soft descriptor used when charging this funding source. If length exceeds max length, the value will be truncated
+	 * @param softDescriptor softDescriptor
 	 * @return this
 	 */
-	@SuppressWarnings("all")
 	public CartBase setSoftDescriptor(final String softDescriptor) {
 		this.softDescriptor = softDescriptor;
 		return this;
@@ -232,9 +233,9 @@ public class CartBase {
 
 	/**
 	 * Soft descriptor city used when charging this funding source. If length exceeds max length, the value will be truncated. Only supported when the `payment_method` is set to `credit_card`
+	 * @param softDescriptorCity softDescriptorCity
 	 * @return this
 	 */
-	@SuppressWarnings("all")
 	public CartBase setSoftDescriptorCity(final String softDescriptorCity) {
 		this.softDescriptorCity = softDescriptorCity;
 		return this;
@@ -244,9 +245,9 @@ public class CartBase {
 
 	/**
 	 * URL to send payment notifications
+	 * @param notifyUrl notifyUrl
 	 * @return this
 	 */
-	@SuppressWarnings("all")
 	public CartBase setNotifyUrl(final String notifyUrl) {
 		this.notifyUrl = notifyUrl;
 		return this;
@@ -254,9 +255,9 @@ public class CartBase {
 
 	/**
 	 * Url on merchant site pertaining to this payment.
+	 * @param orderUrl orderUrl
 	 * @return this
 	 */
-	@SuppressWarnings("all")
 	public CartBase setOrderUrl(final String orderUrl) {
 		this.orderUrl = orderUrl;
 		return this;
diff --git a/pay-java-paypal/src/main/java/com/egzosn/pay/paypal/bean/order/Error.java b/pay-java-paypal/src/main/java/com/egzosn/pay/paypal/bean/order/Error.java
index 3d9069f..8543046 100644
--- a/pay-java-paypal/src/main/java/com/egzosn/pay/paypal/bean/order/Error.java
+++ b/pay-java-paypal/src/main/java/com/egzosn/pay/paypal/bean/order/Error.java
@@ -4,170 +4,121 @@ package com.egzosn.pay.paypal.bean.order;
 
 import java.util.List;
 
-public class Error  {
-	/**
-	 * Human readable, unique name of the error.
-	 */
-	private String name;
-	/**
-	 * Message describing the error.
-	 */
-	private String message;
-	/**
-	 * Additional details of the error
-	 */
-	private List details;
-	/**
-	 * URI for detailed information related to this error for the developer.
-	 */
-	private String informationLink;
-	/**
-	 * PayPal internal identifier used for correlation purposes.
-	 */
-	private String debugId;
-
-	/**
-	 * @deprecated This property is not available publicly
-	 * PayPal internal error code.
-	 */
-	@Deprecated
-	private String code;
-
-
-	/**
-	 * Default Constructor
-	 */
-	public Error() {
-	}
-
-	/**
-	 * Parameterized Constructor
-	 */
-	public Error(String name, String message, String informationLink, String debugId) {
-		this.name = name;
-		this.message = message;
-		this.informationLink = informationLink;
-		this.debugId = debugId;
-	}
-
-	public String toString() {
-		return "name: " + this.name + "\tmessage: " + this.message + "\tdetails: " + this.details + "\tdebug-id: " + this.debugId + "\tinformation-link: " + this.informationLink;
-	}
-
-	/**
-	 * Human readable, unique name of the error.
-	 */
-	@SuppressWarnings("all")
-	public String getName() {
-		return this.name;
-	}
-
-	/**
-	 * Message describing the error.
-	 */
-	@SuppressWarnings("all")
-	public String getMessage() {
-		return this.message;
-	}
-
-	/**
-	 * Additional details of the error
-	 */
-	@SuppressWarnings("all")
-	public List getDetails() {
-		return this.details;
-	}
-
-	/**
-	 * URI for detailed information related to this error for the developer.
-	 */
-	@SuppressWarnings("all")
-	public String getInformationLink() {
-		return this.informationLink;
-	}
-
-	/**
-	 * PayPal internal identifier used for correlation purposes.
-	 */
-	@SuppressWarnings("all")
-	public String getDebugId() {
-		return this.debugId;
-	}
-
-	/**
-	 * @deprecated This property is not available publicly
-	 * PayPal internal error code.
-	 */
-	@Deprecated
-	@SuppressWarnings("all")
-	public String getCode() {
-		return this.code;
-	}
-
-
-
-	/**
-	 * Human readable, unique name of the error.
-	 * @return this
-	 */
-	@SuppressWarnings("all")
-	public Error setName(final String name) {
-		this.name = name;
-		return this;
-	}
-
-	/**
-	 * Message describing the error.
-	 * @return this
-	 */
-	@SuppressWarnings("all")
-	public Error setMessage(final String message) {
-		this.message = message;
-		return this;
-	}
-
-	/**
-	 * Additional details of the error
-	 * @return this
-	 */
-	@SuppressWarnings("all")
-	public Error setDetails(final List details) {
-		this.details = details;
-		return this;
-	}
-
-	/**
-	 * URI for detailed information related to this error for the developer.
-	 * @return this
-	 */
-	@SuppressWarnings("all")
-	public Error setInformationLink(final String informationLink) {
-		this.informationLink = informationLink;
-		return this;
-	}
-
-	/**
-	 * PayPal internal identifier used for correlation purposes.
-	 * @return this
-	 */
-	@SuppressWarnings("all")
-	public Error setDebugId(final String debugId) {
-		this.debugId = debugId;
-		return this;
-	}
-
-
-	/**
-	 * @deprecated This property is not available publicly
-	 * PayPal internal error code.
-	 * @return this
-	 */
-	@Deprecated
-	@SuppressWarnings("all")
-	public Error setCode(final String code) {
-		this.code = code;
-		return this;
-	}
+public class Error {
+    /**
+     * Human readable, unique name of the error.
+     */
+    private String name;
+    /**
+     * Message describing the error.
+     */
+    private String message;
+    /**
+     * Additional details of the error
+     */
+    private List details;
+    /**
+     * URI for detailed information related to this error for the developer.
+     */
+    private String informationLink;
+    /**
+     * PayPal internal identifier used for correlation purposes.
+     */
+    private String debugId;
+
+    /**
+     * @deprecated This property is not available publicly
+     * PayPal internal error code.
+     */
+    @Deprecated
+    private String code;
+
+
+    /**
+     * Default Constructor
+     */
+    public Error() {
+    }
+
+    /**
+     * Parameterized Constructor
+     */
+    public Error(String name, String message, String informationLink, String debugId) {
+        this.name = name;
+        this.message = message;
+        this.informationLink = informationLink;
+        this.debugId = debugId;
+    }
+
+    public String toString() {
+        return "name: " + this.name + "\tmessage: " + this.message + "\tdetails: " + this.details + "\tdebug-id: " + this.debugId + "\tinformation-link: " + this.informationLink;
+    }
+
+
+    public String getName() {
+        return this.name;
+    }
+
+
+    public String getMessage() {
+        return this.message;
+    }
+
+
+    public List getDetails() {
+        return this.details;
+    }
+
+
+    public String getInformationLink() {
+        return this.informationLink;
+    }
+
+
+    public String getDebugId() {
+        return this.debugId;
+    }
+
+
+    public String getCode() {
+        return this.code;
+    }
+
+
+    public Error setName(final String name) {
+        this.name = name;
+        return this;
+    }
+
+
+    public Error setMessage(final String message) {
+        this.message = message;
+        return this;
+    }
+
+
+    public Error setDetails(final List details) {
+        this.details = details;
+        return this;
+    }
+
+
+    public Error setInformationLink(final String informationLink) {
+        this.informationLink = informationLink;
+        return this;
+    }
+
+
+    public Error setDebugId(final String debugId) {
+        this.debugId = debugId;
+        return this;
+    }
+
+
+    public Error setCode(final String code) {
+        this.code = code;
+        return this;
+    }
 
 
 }
diff --git a/pay-java-paypal/src/main/java/com/egzosn/pay/paypal/bean/order/ErrorDetails.java b/pay-java-paypal/src/main/java/com/egzosn/pay/paypal/bean/order/ErrorDetails.java
index d054eb1..31c6486 100644
--- a/pay-java-paypal/src/main/java/com/egzosn/pay/paypal/bean/order/ErrorDetails.java
+++ b/pay-java-paypal/src/main/java/com/egzosn/pay/paypal/bean/order/ErrorDetails.java
@@ -19,46 +19,30 @@ public class ErrorDetails {
 	public ErrorDetails() {
 	}
 
-	/**
-	 * Parameterized Constructor
-	 */
+
 	public ErrorDetails(String field, String issue) {
 		this.field = field;
 		this.issue = issue;
 	}
 
-	/**
-	 * Name of the field that caused the error.
-	 */
-	@SuppressWarnings("all")
+
 	public String getField() {
 		return this.field;
 	}
 
-	/**
-	 * Reason for the error.
-	 */
-	@SuppressWarnings("all")
+
 	public String getIssue() {
 		return this.issue;
 	}
 
 
-	/**
-	 * Name of the field that caused the error.
-	 * @return this
-	 */
-	@SuppressWarnings("all")
+
 	public ErrorDetails setField(final String field) {
 		this.field = field;
 		return this;
 	}
 
-	/**
-	 * Reason for the error.
-	 * @return this
-	 */
-	@SuppressWarnings("all")
+
 	public ErrorDetails setIssue(final String issue) {
 		this.issue = issue;
 		return this;
diff --git a/pay-java-paypal/src/main/java/com/egzosn/pay/paypal/bean/order/FmfDetails.java b/pay-java-paypal/src/main/java/com/egzosn/pay/paypal/bean/order/FmfDetails.java
index 2ef402f..0341cc4 100644
--- a/pay-java-paypal/src/main/java/com/egzosn/pay/paypal/bean/order/FmfDetails.java
+++ b/pay-java-paypal/src/main/java/com/egzosn/pay/paypal/bean/order/FmfDetails.java
@@ -19,71 +19,46 @@ public class FmfDetails{
 	 */
 	private String description;
 
-	/**
-	 * Type of filter.
-	 */
+
 	public String getFilterType() {
 		return this.filterType;
 	}
 
-	/**
-	 * Filter Identifier.
-	 */
-	
+
 	public String getFilterId() {
 		return this.filterId;
 	}
 
-	/**
-	 * Name of the filter
-	 */
-	
 	public String getName() {
 		return this.name;
 	}
 
-	/**
-	 * Description of the filter.
-	 */
+
 	
 	public String getDescription() {
 		return this.description;
 	}
 
-	/**
-	 * Type of filter.
-	 * @return this
-	 */
-	
+
 	public FmfDetails setFilterType(final String filterType) {
 		this.filterType = filterType;
 		return this;
 	}
 
-	/**
-	 * Filter Identifier.
-	 * @return this
-	 */
-	
+
 	public FmfDetails setFilterId(final String filterId) {
 		this.filterId = filterId;
 		return this;
 	}
 
-	/**
-	 * Name of the filter
-	 * @return this
-	 */
+
 	
 	public FmfDetails setName(final String name) {
 		this.name = name;
 		return this;
 	}
 
-	/**
-	 * Description of the filter.
-	 * @return this
-	 */
+
 	
 	public FmfDetails setDescription(final String description) {
 		this.description = description;
@@ -119,7 +94,6 @@ public class FmfDetails{
 	}
 
 	@Override
-	
 	public int hashCode() {
 		final int PRIME = 59;
 		int result = 1;
diff --git a/pay-java-paypal/src/main/java/com/egzosn/pay/paypal/bean/order/Order.java b/pay-java-paypal/src/main/java/com/egzosn/pay/paypal/bean/order/Order.java
index a9ecd54..afb681a 100644
--- a/pay-java-paypal/src/main/java/com/egzosn/pay/paypal/bean/order/Order.java
+++ b/pay-java-paypal/src/main/java/com/egzosn/pay/paypal/bean/order/Order.java
@@ -65,25 +65,16 @@ public class Order {
 	public Order() {
 	}
 
-	/**
-	 * Parameterized Constructor
-	 */
+
 	public Order(Amount amount) {
 		this.amount = amount;
 	}
 
 
-	/**
-	 * Identifier of the order transaction.
-	 */
-	
 	public String getId() {
 		return this.id;
 	}
 
-	/**
-	 * Identifier to the purchase unit associated with this object. Obsolete. Use one in cart_base.
-	 */
 	
 	public String getPurchaseUnitReferenceId() {
 		return this.purchaseUnitReferenceId;
@@ -91,109 +82,73 @@ public class Order {
 
 
 
-	/**
-	 * Amount being collected.
-	 */
-	
+
 	public Amount getAmount() {
 		return this.amount;
 	}
 
-	/**
-	 * specifies payment mode of the transaction
-	 */
+
 	
 	public String getPaymentMode() {
 		return this.paymentMode;
 	}
 
-	/**
-	 * State of the order transaction.
-	 */
-	
+
 	public String getState() {
 		return this.state;
 	}
 
-	/**
-	 * Reason code for the transaction state being Pending or Reversed. This field will replace pending_reason field eventually. Only supported when the `payment_method` is set to `paypal`.
-	 */
-	
+
 	public String getReasonCode() {
 		return this.reasonCode;
 	}
 
 
 
-	/**
-	 * The level of seller protection in force for the transaction.
-	 */
-	
+
 	public String getProtectionEligibility() {
 		return this.protectionEligibility;
 	}
 
-	/**
-	 * The kind of seller protection in force for the transaction. This property is returned only when the `protection_eligibility` property is set to `ELIGIBLE`or `PARTIALLY_ELIGIBLE`. Only supported when the `payment_method` is set to `paypal`. Allowed values:
`ITEM_NOT_RECEIVED_ELIGIBLE`- Sellers are protected against claims for items not received.
`UNAUTHORIZED_PAYMENT_ELIGIBLE`- Sellers are protected against claims for unauthorized payments.
One or both of the allowed values can be returned. - */ + public String getProtectionEligibilityType() { return this.protectionEligibilityType; } - /** - * ID of the Payment resource that this transaction is based on. - */ - + public String getParentPayment() { return this.parentPayment; } - /** - * Fraud Management Filter (FMF) details applied for the payment that could result in accept/deny/pending action. - */ - + public FmfDetails getFmfDetails() { return this.fmfDetails; } - /** - * Time the resource was created in UTC ISO8601 format. - */ - + public String getCreateTime() { return this.createTime; } - /** - * Time the resource was last updated in UTC ISO8601 format. - */ public String getUpdateTime() { return this.updateTime; } - /** - */ - + public List getLinks() { return this.links; } - /** - * Identifier of the order transaction. - * @return this - */ + public Order setId(final String id) { this.id = id; return this; } - /** - * Identifier to the purchase unit associated with this object. Obsolete. Use one in cart_base. - * @return this - */ + public Order setPurchaseUnitReferenceId(final String purchaseUnitReferenceId) { this.purchaseUnitReferenceId = purchaseUnitReferenceId; @@ -202,112 +157,70 @@ public class Order { - /** - * Amount being collected. - * @return this - */ - public Order setAmount(final Amount amount) { this.amount = amount; return this; } - /** - * specifies payment mode of the transaction - * @return this - */ - + public Order setPaymentMode(final String paymentMode) { this.paymentMode = paymentMode; return this; } - /** - * State of the order transaction. - * @return this - */ - + public Order setState(final String state) { this.state = state; return this; } - /** - * Reason code for the transaction state being Pending or Reversed. This field will replace pending_reason field eventually. Only supported when the `payment_method` is set to `paypal`. - * @return this - */ - + public Order setReasonCode(final String reasonCode) { this.reasonCode = reasonCode; return this; } - /** - * The level of seller protection in force for the transaction. - * @return this - */ - + public Order setProtectionEligibility(final String protectionEligibility) { this.protectionEligibility = protectionEligibility; return this; } - /** - * The kind of seller protection in force for the transaction. This property is returned only when the `protection_eligibility` property is set to `ELIGIBLE`or `PARTIALLY_ELIGIBLE`. Only supported when the `payment_method` is set to `paypal`. Allowed values:
`ITEM_NOT_RECEIVED_ELIGIBLE`- Sellers are protected against claims for items not received.
`UNAUTHORIZED_PAYMENT_ELIGIBLE`- Sellers are protected against claims for unauthorized payments.
One or both of the allowed values can be returned. - * @return this - */ + public Order setProtectionEligibilityType(final String protectionEligibilityType) { this.protectionEligibilityType = protectionEligibilityType; return this; } - /** - * ID of the Payment resource that this transaction is based on. - * @return this - */ - + public Order setParentPayment(final String parentPayment) { this.parentPayment = parentPayment; return this; } - /** - * Fraud Management Filter (FMF) details applied for the payment that could result in accept/deny/pending action. - * @return this - */ + public Order setFmfDetails(final FmfDetails fmfDetails) { this.fmfDetails = fmfDetails; return this; } - /** - * Time the resource was created in UTC ISO8601 format. - * @return this - */ + public Order setCreateTime(final String createTime) { this.createTime = createTime; return this; } - /** - * Time the resource was last updated in UTC ISO8601 format. - * @return this - */ public Order setUpdateTime(final String updateTime) { this.updateTime = updateTime; return this; } - /** - * - * @return this - */ - + public Order setLinks(final List links) { this.links = links; return this; diff --git a/pay-java-paypal/src/main/java/com/egzosn/pay/paypal/bean/order/Payee.java b/pay-java-paypal/src/main/java/com/egzosn/pay/paypal/bean/order/Payee.java index 9c09ebd..10e3b8f 100644 --- a/pay-java-paypal/src/main/java/com/egzosn/pay/paypal/bean/order/Payee.java +++ b/pay-java-paypal/src/main/java/com/egzosn/pay/paypal/bean/order/Payee.java @@ -40,116 +40,73 @@ public class Payee { public Payee() { } - /** - * Email Address associated with the Payee's PayPal Account. If the provided email address is not associated with any PayPal Account, the payee can only receive PayPal Wallet Payments. Direct Credit Card Payments will be denied due to card compliance requirements. - */ - @SuppressWarnings("all") + public String getEmail() { return this.email; } - /** - * Encrypted PayPal account identifier for the Payee. - */ - @SuppressWarnings("all") + public String getMerchantId() { return this.merchantId; } - /** - * First Name of the Payee. - */ - @SuppressWarnings("all") + public String getFirstName() { return this.firstName; } - /** - * Last Name of the Payee. - */ - @SuppressWarnings("all") + public String getLastName() { return this.lastName; } - /** - * Unencrypted PayPal account Number of the Payee - */ - @SuppressWarnings("all") + public String getAccountNumber() { return this.accountNumber; } - /** - * Information related to the Payee. - */ - @SuppressWarnings("all") + public Phone getPhone() { return this.phone; } - /** - * Email Address associated with the Payee's PayPal Account. If the provided email address is not associated with any PayPal Account, the payee can only receive PayPal Wallet Payments. Direct Credit Card Payments will be denied due to card compliance requirements. - * @return this - */ - @SuppressWarnings("all") + public Payee setEmail(final String email) { this.email = email; return this; } - /** - * Encrypted PayPal account identifier for the Payee. - * @return this - */ - @SuppressWarnings("all") + public Payee setMerchantId(final String merchantId) { this.merchantId = merchantId; return this; } - /** - * First Name of the Payee. - * @return this - */ - @SuppressWarnings("all") + public Payee setFirstName(final String firstName) { this.firstName = firstName; return this; } - /** - * Last Name of the Payee. - * @return this - */ - @SuppressWarnings("all") + public Payee setLastName(final String lastName) { this.lastName = lastName; return this; } - /** - * Unencrypted PayPal account Number of the Payee - * @return this - */ - @SuppressWarnings("all") + public Payee setAccountNumber(final String accountNumber) { this.accountNumber = accountNumber; return this; } - /** - * Information related to the Payee. - * @return this - */ - @SuppressWarnings("all") + public Payee setPhone(final Phone phone) { this.phone = phone; return this; } - @Override - @SuppressWarnings("all") + public boolean equals(final Object o) { if (o == this) return true; if (!(o instanceof Payee)) return false; @@ -177,13 +134,12 @@ public class Payee { return true; } - @SuppressWarnings("all") + protected boolean canEqual(final Object other) { return other instanceof Payee; } @Override - @SuppressWarnings("all") public int hashCode() { final int PRIME = 59; int result = 1; diff --git a/pay-java-paypal/src/main/java/com/egzosn/pay/paypal/bean/order/Phone.java b/pay-java-paypal/src/main/java/com/egzosn/pay/paypal/bean/order/Phone.java index b820b1b..8276fe3 100644 --- a/pay-java-paypal/src/main/java/com/egzosn/pay/paypal/bean/order/Phone.java +++ b/pay-java-paypal/src/main/java/com/egzosn/pay/paypal/bean/order/Phone.java @@ -26,70 +26,46 @@ public class Phone{ public Phone() { } - /** - * Parameterized Constructor - */ + public Phone(String countryCode, String nationalNumber) { this.countryCode = countryCode; this.nationalNumber = nationalNumber; } - /** - * Country code (from in E.164 format) - */ - @SuppressWarnings("all") + public String getCountryCode() { return this.countryCode; } - /** - * In-country phone number (from in E.164 format) - */ - @SuppressWarnings("all") + public String getNationalNumber() { return this.nationalNumber; } - /** - * Phone extension - */ - @SuppressWarnings("all") + public String getExtension() { return this.extension; } - /** - * Country code (from in E.164 format) - * @return this - */ - @SuppressWarnings("all") + public Phone setCountryCode(final String countryCode) { this.countryCode = countryCode; return this; } - /** - * In-country phone number (from in E.164 format) - * @return this - */ - @SuppressWarnings("all") + public Phone setNationalNumber(final String nationalNumber) { this.nationalNumber = nationalNumber; return this; } - /** - * Phone extension - * @return this - */ - @SuppressWarnings("all") + public Phone setExtension(final String extension) { this.extension = extension; return this; } @Override - @SuppressWarnings("all") public boolean equals(final Object o) { if (o == this) return true; if (!(o instanceof Phone)) return false; @@ -108,13 +84,11 @@ public class Phone{ return true; } - @SuppressWarnings("all") protected boolean canEqual(final Object other) { return other instanceof Phone; } @Override - @SuppressWarnings("all") public int hashCode() { final int PRIME = 59; int result = 1; diff --git a/pay-java-paypal/src/main/java/com/egzosn/pay/paypal/bean/order/RedirectUrls.java b/pay-java-paypal/src/main/java/com/egzosn/pay/paypal/bean/order/RedirectUrls.java index a98b498..05ea842 100644 --- a/pay-java-paypal/src/main/java/com/egzosn/pay/paypal/bean/order/RedirectUrls.java +++ b/pay-java-paypal/src/main/java/com/egzosn/pay/paypal/bean/order/RedirectUrls.java @@ -22,44 +22,30 @@ public class RedirectUrls{ public RedirectUrls() { } - /** - * Url where the payer would be redirected to after approving the payment. **Required for PayPal account payments.** - */ - @SuppressWarnings("all") + public String getReturnUrl() { return this.returnUrl; } - /** - * Url where the payer would be redirected to after canceling the payment. **Required for PayPal account payments.** - */ - @SuppressWarnings("all") + public String getCancelUrl() { return this.cancelUrl; } - /** - * Url where the payer would be redirected to after approving the payment. **Required for PayPal account payments.** - * @return this - */ - @SuppressWarnings("all") + public RedirectUrls setReturnUrl(final String returnUrl) { this.returnUrl = returnUrl; return this; } - /** - * Url where the payer would be redirected to after canceling the payment. **Required for PayPal account payments.** - * @return this - */ - @SuppressWarnings("all") + public RedirectUrls setCancelUrl(final String cancelUrl) { this.cancelUrl = cancelUrl; return this; } @Override - @SuppressWarnings("all") + public boolean equals(final Object o) { if (o == this) return true; if (!(o instanceof RedirectUrls)) return false; @@ -75,13 +61,11 @@ public class RedirectUrls{ return true; } - @SuppressWarnings("all") protected boolean canEqual(final Object other) { return other instanceof RedirectUrls; } @Override - @SuppressWarnings("all") public int hashCode() { final int PRIME = 59; int result = 1; diff --git a/pay-java-paypal/src/main/java/com/egzosn/pay/paypal/bean/order/Refund.java b/pay-java-paypal/src/main/java/com/egzosn/pay/paypal/bean/order/Refund.java index fa8fefe..72bc775 100644 --- a/pay-java-paypal/src/main/java/com/egzosn/pay/paypal/bean/order/Refund.java +++ b/pay-java-paypal/src/main/java/com/egzosn/pay/paypal/bean/order/Refund.java @@ -64,233 +64,152 @@ public class Refund { } - /** - * ID of the refund transaction. 17 characters max. - */ - + public String getId() { return this.id; } - /** - * Details including both refunded amount (to payer) and refunded fee (to payee). 10 characters max. - */ - + public Amount getAmount() { return this.amount; } - /** - * State of the refund. - */ - + public String getState() { return this.state; } - /** - * Reason description for the Sale transaction being refunded. - */ - public String getReason() { return this.reason; } - /** - * Your own invoice or tracking ID number. Character length and limitations: 127 single-byte alphanumeric characters. - */ - + public String getInvoiceNumber() { return this.invoiceNumber; } - /** - * ID of the Sale transaction being refunded. - */ + public String getSaleId() { return this.saleId; } - /** - * ID of the sale transaction being refunded. - */ public String getCaptureId() { return this.captureId; } - /** - * ID of the payment resource on which this transaction is based. - */ + public String getParentPayment() { return this.parentPayment; } - /** - * Description of what is being refunded for. - */ + public String getDescription() { return this.description; } - /** - * Time of refund as defined in [RFC 3339 Section 5.6](http://tools.ietf.org/html/rfc3339#section-5.6). - */ + public String getCreateTime() { return this.createTime; } - /** - * Time that the resource was last updated. - */ public String getUpdateTime() { return this.updateTime; } - /** - * The reason code for the refund state being pending - */ - + public String getReasonCode() { return this.reasonCode; } - /** - */ + public List getLinks() { return this.links; } - /** - * ID of the refund transaction. 17 characters max. - * @return this - */ - public Refund setId(final String id) { this.id = id; return this; } - /** - * Details including both refunded amount (to payer) and refunded fee (to payee). 10 characters max. - * @return this - */ - + public Refund setAmount(final Amount amount) { this.amount = amount; return this; } - /** - * State of the refund. - * @return this - */ + public Refund setState(final String state) { this.state = state; return this; } - /** - * Reason description for the Sale transaction being refunded. - * @return this - */ + public Refund setReason(final String reason) { this.reason = reason; return this; } - /** - * Your own invoice or tracking ID number. Character length and limitations: 127 single-byte alphanumeric characters. - * @return this - */ + public Refund setInvoiceNumber(final String invoiceNumber) { this.invoiceNumber = invoiceNumber; return this; } - /** - * ID of the Sale transaction being refunded. - * @return this - */ public Refund setSaleId(final String saleId) { this.saleId = saleId; return this; } - /** - * ID of the sale transaction being refunded. - * @return this - */ - + public Refund setCaptureId(final String captureId) { this.captureId = captureId; return this; } - /** - * ID of the payment resource on which this transaction is based. - * @return this - */ - + public Refund setParentPayment(final String parentPayment) { this.parentPayment = parentPayment; return this; } - /** - * Description of what is being refunded for. - * @return this - */ + public Refund setDescription(final String description) { this.description = description; return this; } - /** - * Time of refund as defined in [RFC 3339 Section 5.6](http://tools.ietf.org/html/rfc3339#section-5.6). - * @return this - */ + public Refund setCreateTime(final String createTime) { this.createTime = createTime; return this; } - /** - * Time that the resource was last updated. - * @return this - */ - + public Refund setUpdateTime(final String updateTime) { this.updateTime = updateTime; return this; } - /** - * The reason code for the refund state being pending - * @return this - */ public Refund setReasonCode(final String reasonCode) { this.reasonCode = reasonCode; return this; } - /** - * - * @return this - */ + public Refund setLinks(final List links) { this.links = links; diff --git a/pom.xml b/pom.xml index cfa1e34..2c2105e 100644 --- a/pom.xml +++ b/pom.xml @@ -58,7 +58,7 @@ pay-java-paypal pay-java-yiji pay-java-baidu - + pay-java-demo -- Gitee From 11bfc9af63715de7f55877b181624c14f4a5e5cc Mon Sep 17 00:00:00 2001 From: egzosn Date: Mon, 28 Dec 2020 20:07:28 +0800 Subject: [PATCH 170/299] =?UTF-8?q?=E6=94=AF=E4=BB=98=E5=AE=9D=E5=85=AC?= =?UTF-8?q?=E9=92=A5=E8=AF=81=E4=B9=A6=E6=A1=88=E4=BE=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pay-java-ali/pom.xml | 2 +- pay-java-baidu/pom.xml | 2 +- pay-java-common/pom.xml | 2 +- pay-java-demo/pom.xml | 2 +- pay-java-fuiou/pom.xml | 2 +- pay-java-payoneer/pom.xml | 2 +- pay-java-paypal/pom.xml | 2 +- pay-java-union/pom.xml | 2 +- pay-java-wx-youdian/pom.xml | 2 +- pay-java-wx/pom.xml | 2 +- pay-java-yiji/pom.xml | 2 +- pom.xml | 4 ++-- 12 files changed, 13 insertions(+), 13 deletions(-) diff --git a/pay-java-ali/pom.xml b/pay-java-ali/pom.xml index f014c96..f31a779 100644 --- a/pay-java-ali/pom.xml +++ b/pay-java-ali/pom.xml @@ -5,7 +5,7 @@ pay-java-parent com.egzosn - 2.13.3-b1 + 2.13.4-SNAPSHOT 4.0.0 pay-java-ali diff --git a/pay-java-baidu/pom.xml b/pay-java-baidu/pom.xml index d336160..228a3d0 100644 --- a/pay-java-baidu/pom.xml +++ b/pay-java-baidu/pom.xml @@ -5,7 +5,7 @@ pay-java-parent com.egzosn - 2.13.3-b1 + 2.13.4-SNAPSHOT 4.0.0 pay-java-baidu diff --git a/pay-java-common/pom.xml b/pay-java-common/pom.xml index 2ca60b8..e0ad8d1 100644 --- a/pay-java-common/pom.xml +++ b/pay-java-common/pom.xml @@ -5,7 +5,7 @@ pay-java-parent com.egzosn - 2.13.3-b1 + 2.13.4-SNAPSHOT 4.0.0 jar diff --git a/pay-java-demo/pom.xml b/pay-java-demo/pom.xml index 6858975..7c0de2a 100644 --- a/pay-java-demo/pom.xml +++ b/pay-java-demo/pom.xml @@ -5,7 +5,7 @@ pay-java-parent com.egzosn - 2.13.3-b1 + 2.13.4-SNAPSHOT 4.0.0 war diff --git a/pay-java-fuiou/pom.xml b/pay-java-fuiou/pom.xml index 52f09fc..6a834c7 100644 --- a/pay-java-fuiou/pom.xml +++ b/pay-java-fuiou/pom.xml @@ -5,7 +5,7 @@ pay-java-parent com.egzosn - 2.13.3-b1 + 2.13.4-SNAPSHOT 4.0.0 pay-java-fuiou diff --git a/pay-java-payoneer/pom.xml b/pay-java-payoneer/pom.xml index d7e29fa..9df1ded 100644 --- a/pay-java-payoneer/pom.xml +++ b/pay-java-payoneer/pom.xml @@ -5,7 +5,7 @@ pay-java-parent com.egzosn - 2.13.3-b1 + 2.13.4-SNAPSHOT 4.0.0 pay-java-payoneer diff --git a/pay-java-paypal/pom.xml b/pay-java-paypal/pom.xml index 3886fb3..0abbb06 100644 --- a/pay-java-paypal/pom.xml +++ b/pay-java-paypal/pom.xml @@ -5,7 +5,7 @@ pay-java-parent com.egzosn - 2.13.3-b1 + 2.13.4-SNAPSHOT 4.0.0 diff --git a/pay-java-union/pom.xml b/pay-java-union/pom.xml index a98787c..6c539c1 100644 --- a/pay-java-union/pom.xml +++ b/pay-java-union/pom.xml @@ -5,7 +5,7 @@ pay-java-parent com.egzosn - 2.13.3-b1 + 2.13.4-SNAPSHOT 4.0.0 diff --git a/pay-java-wx-youdian/pom.xml b/pay-java-wx-youdian/pom.xml index c0db493..2081227 100644 --- a/pay-java-wx-youdian/pom.xml +++ b/pay-java-wx-youdian/pom.xml @@ -5,7 +5,7 @@ pay-java-parent com.egzosn - 2.13.3-b1 + 2.13.4-SNAPSHOT 4.0.0 pay-java-wx-youdian diff --git a/pay-java-wx/pom.xml b/pay-java-wx/pom.xml index b9d7a20..dfe0e5d 100644 --- a/pay-java-wx/pom.xml +++ b/pay-java-wx/pom.xml @@ -5,7 +5,7 @@ pay-java-parent com.egzosn - 2.13.3-b1 + 2.13.4-SNAPSHOT 4.0.0 pay-java-wx diff --git a/pay-java-yiji/pom.xml b/pay-java-yiji/pom.xml index a03c038..0a3ac4e 100644 --- a/pay-java-yiji/pom.xml +++ b/pay-java-yiji/pom.xml @@ -5,7 +5,7 @@ pay-java-parent com.egzosn - 2.13.3-b1 + 2.13.4-SNAPSHOT 4.0.0 diff --git a/pom.xml b/pom.xml index 2c2105e..fc3322b 100644 --- a/pom.xml +++ b/pom.xml @@ -7,7 +7,7 @@ com.egzosn pay-java-parent pom - 2.13.3-b1 + 2.13.4-SNAPSHOT Pay Java - Parent Pay Java Parent @@ -64,7 +64,7 @@ - 2.13.3-b1 + 2.13.4-SNAPSHOT 4.5.4 1.2.17 1.2.73 -- Gitee From 0f9439fd4eb1535d0303d916e0a0a88f7d2ce9c4 Mon Sep 17 00:00:00 2001 From: egzosn Date: Thu, 31 Dec 2020 17:10:09 +0800 Subject: [PATCH 171/299] xcx --- pay-java-demo/src/main/webapp/xcx.png | Bin 0 -> 6950538 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 pay-java-demo/src/main/webapp/xcx.png diff --git a/pay-java-demo/src/main/webapp/xcx.png b/pay-java-demo/src/main/webapp/xcx.png new file mode 100644 index 0000000000000000000000000000000000000000..2d0024c60c63198c1cea4d8fb86b2f25e694e2f2 GIT binary patch literal 6950538 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 Date: Sun, 17 Jan 2021 22:38:11 +0800 Subject: [PATCH 172/299] =?UTF-8?q?=E7=A7=BB=E9=99=A4=E5=B7=B2=E5=BA=9F?= =?UTF-8?q?=E5=BC=83=E7=9A=84=E5=AD=97=E6=AE=B5=E4=B8=8E=E6=96=B9=E6=B3=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../pay/ali/api/AliPayConfigStorage.java | 26 +- .../com/egzosn/pay/ali/api/AliPayService.java | 11 +- .../pay/baidu/api/BaiduPayConfigStorage.java | 38 ++- .../egzosn/pay/baidu/api/BaiduPayService.java | 28 +- .../pay/common/api/BasePayConfigStorage.java | 32 +-- .../egzosn/pay/common/api/BasePayService.java | 34 +-- .../pay/common/api/PayConfigStorage.java | 23 +- .../pay/common/api/PayMessageRouter.java | 28 +- .../pay/common/api/PayMessageRouterRule.java | 80 ++---- .../com/egzosn/pay/common/api/PayService.java | 105 +++---- .../com/egzosn/pay/common/bean/MsgType.java | 30 -- .../egzosn/pay/common/bean/PayMessage.java | 45 ++- .../egzosn/pay/common/bean/PayOutMessage.java | 24 +- .../bean/outbuilder/PayJsonOutMessage.java | 7 +- .../bean/outbuilder/PayTextOutMessage.java | 2 - .../bean/outbuilder/PayXmlOutMessage.java | 2 - .../egzosn/pay/common/http/UriVariables.java | 75 ++--- .../pay/demo/controller/AliPayController.java | 90 +++--- .../pay/demo/controller/PayController.java | 101 ++++--- .../demo/controller/PayPalPayController.java | 49 ++-- .../controller/PayoneerPayController.java | 61 +++-- .../pay/demo/dao/ApyAccountRepository.java | 22 +- .../egzosn/pay/demo/entity/ApyAccount.java | 17 +- .../com/egzosn/pay/demo/entity/PayType.java | 26 +- .../egzosn/pay/demo/service/PayResponse.java | 32 ++- .../pay/fuiou/api/FuiouPayConfigStorage.java | 24 +- .../egzosn/pay/fuiou/api/FuiouPayService.java | 155 ++++++----- .../payoneer/api/PayoneerConfigStorage.java | 46 +++- .../pay/payoneer/api/PayoneerPayService.java | 80 +++--- .../pay/paypal/api/PayPalConfigStorage.java | 50 +++- .../pay/paypal/api/PayPalPayService.java | 147 +++++----- .../pay/union/api/UnionPayConfigStorage.java | 32 ++- .../egzosn/pay/union/api/UnionPayService.java | 87 +++--- .../api/WxYouDianPayConfigStorage.java | 11 + .../wx/youdian/api/WxYouDianPayService.java | 257 +++++++++--------- .../egzosn/pay/wx/api/WxPayConfigStorage.java | 69 +++-- .../com/egzosn/pay/wx/api/WxPayService.java | 124 ++++----- .../pay/yiji/api/YiJiPayConfigStorage.java | 25 +- .../egzosn/pay/yiji/api/YiJiPayService.java | 79 ++---- 39 files changed, 1095 insertions(+), 1079 deletions(-) delete mode 100644 pay-java-common/src/main/java/com/egzosn/pay/common/bean/MsgType.java 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 d9d907d..2acb5a6 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 @@ -28,7 +28,7 @@ public class AliPayConfigStorage extends BasePayConfigStorage { /** * 商户应用id */ - private String appid; + private String appId; /** * 商户签约拿到的pid,partner_id的简称,合作伙伴身份等同于 partner */ @@ -73,15 +73,30 @@ public class AliPayConfigStorage extends BasePayConfigStorage { this.appAuthToken = appAuthToken; } - public void setAppid(String appid) { - this.appid = appid; + public void setAppid(String appId) { + this.appId = appId; } @Override + @Deprecated public String getAppid() { - return appid; + return appId; } + /** + * 应用id + * 纠正名称 + * + * @return 应用id + */ + @Override + public String getAppId() { + return appId; + } + + public void setAppId(String appId) { + this.appId = appId; + } @Override public String getPid() { @@ -152,7 +167,8 @@ public class AliPayConfigStorage extends BasePayConfigStorage { InputStream aliPayCertStream = certStoreType.getInputStream(aliPayCert); InputStream aliPayRootCertStream = certStoreType.getInputStream(aliPayRootCert)) { this.certEnvironment = new CertEnvironment(merchantCertStream, aliPayCertStream, aliPayRootCertStream); - } catch (IOException e) { + } + catch (IOException e) { throw new PayErrorException(new PayException("读取证书异常", e.getMessage())); } } 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 31c7110..4e15880 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 @@ -123,7 +123,6 @@ public class AliPayService extends BasePayService { * @param sign 比对的签名结果 * @return 生成的签名结果 */ - @Override public boolean signVerify(Map params, String sign) { if (params instanceof JSONObject) { @@ -171,7 +170,6 @@ public class AliPayService extends BasePayService { * @param id 业务id, 数据的真实性. * @return true通过 */ - @Override public boolean verifySource(String id) { return true; } @@ -281,10 +279,11 @@ public class AliPayService extends BasePayService { switch ((AliTransactionType) order.getTransactionType()) { case SWEEPPAY: bizContent.put("qr_code_timeout_express", DateUtils.minutesRemaining(order.getExpirationTime()) + "m"); + break; case PAGE: case WAP: case APP: - bizContent.put("time_expire", DateUtils.formatDate(order.getExpirationTime(), "yyyy-MM-dd HH:mm")); + bizContent.put("time_expire", DateUtils.formatDate(order.getExpirationTime(), DateUtils.YYYY_MM_DD_HH_MM_SS)); break; default: } @@ -526,7 +525,9 @@ public class AliPayService extends BasePayService { parameters.put(BIZ_CONTENT, JSON.toJSONString(bizContent)); //设置签名 setSign(parameters); - final AliRefundResult refundResult = AliRefundResult.create(requestTemplate.getForObject(getReqUrl() + "?" + UriVariables.getMapToParameters(parameters), JSONObject.class)); + JSONObject result = requestTemplate.getForObject(getReqUrl() + "?" + UriVariables.getMapToParameters(parameters), JSONObject.class); + JSONObject refundResponse = result.getJSONObject("alipay_trade_refund_response"); + AliRefundResult refundResult = AliRefundResult.create(refundResponse); refundResult.setOutRequestNo(refundOrder.getRefundNo()); return refundResult; } @@ -587,8 +588,6 @@ public class AliPayService extends BasePayService { * @param transactionType 交易类型 * @return 返回支付方对应接口的结果 */ - - @Override public Map secondaryInterface(Object tradeNoOrBillDate, String outTradeNoBillType, TransactionType transactionType) { if (transactionType == AliTransactionType.REFUND) { diff --git a/pay-java-baidu/src/main/java/com/egzosn/pay/baidu/api/BaiduPayConfigStorage.java b/pay-java-baidu/src/main/java/com/egzosn/pay/baidu/api/BaiduPayConfigStorage.java index f1496e7..7786b80 100644 --- a/pay-java-baidu/src/main/java/com/egzosn/pay/baidu/api/BaiduPayConfigStorage.java +++ b/pay-java-baidu/src/main/java/com/egzosn/pay/baidu/api/BaiduPayConfigStorage.java @@ -3,51 +3,61 @@ package com.egzosn.pay.baidu.api; import com.egzosn.pay.common.api.BasePayConfigStorage; public class BaiduPayConfigStorage extends BasePayConfigStorage { - private String appid; + private String appId; private String dealId; - + @Override + @Deprecated public String getAppid() { - return this.appid; + return this.appId; } - + @Override public String getPid() { return getDealId(); } - + @Override public String getSeller() { throw new UnsupportedOperationException("不支持"); } - + public String getDealId() { return dealId; } - + public void setDealId(String dealId) { this.dealId = dealId; } - + public String getAppKey() { return this.getKeyPrivate(); } - + public void setAppKey(String appKey) { setKeyPrivate(appKey); } - + @Override public String getKeyPublic() { return super.getKeyPrivate(); } - + @Override public void setKeyPublic(String keyPublic) { super.setKeyPublic(keyPublic); } - - public void setAppid(String appid) { - this.appid = appid; + + public void setAppid(String appId) { + this.appId = appId; + } + + @Override + public String getAppId() { + return appId; + } + + public void setAppId(String appId) { + this.appId = appId; } } diff --git a/pay-java-baidu/src/main/java/com/egzosn/pay/baidu/api/BaiduPayService.java b/pay-java-baidu/src/main/java/com/egzosn/pay/baidu/api/BaiduPayService.java index febafbf..8c2095b 100644 --- a/pay-java-baidu/src/main/java/com/egzosn/pay/baidu/api/BaiduPayService.java +++ b/pay-java-baidu/src/main/java/com/egzosn/pay/baidu/api/BaiduPayService.java @@ -1,14 +1,25 @@ package com.egzosn.pay.baidu.api; +import java.math.BigDecimal; +import java.util.Date; +import java.util.HashMap; +import java.util.Map; + import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONObject; import com.egzosn.pay.baidu.bean.BaiduPayOrder; -import com.egzosn.pay.baidu.bean.BaiduRefundOrder; import com.egzosn.pay.baidu.bean.BaiduTransactionType; import com.egzosn.pay.baidu.bean.type.AuditStatus; import com.egzosn.pay.baidu.util.Asserts; import com.egzosn.pay.common.api.BasePayService; -import com.egzosn.pay.common.bean.*; +import com.egzosn.pay.common.bean.BaseRefundResult; +import com.egzosn.pay.common.bean.CurType; +import com.egzosn.pay.common.bean.MethodType; +import com.egzosn.pay.common.bean.PayMessage; +import com.egzosn.pay.common.bean.PayOrder; +import com.egzosn.pay.common.bean.PayOutMessage; +import com.egzosn.pay.common.bean.RefundOrder; +import com.egzosn.pay.common.bean.TransactionType; import com.egzosn.pay.common.http.HttpConfigStorage; import com.egzosn.pay.common.http.UriVariables; import com.egzosn.pay.common.util.DateUtils; @@ -16,11 +27,6 @@ import com.egzosn.pay.common.util.Util; import com.egzosn.pay.common.util.sign.SignUtils; import com.egzosn.pay.common.util.str.StringUtils; -import java.math.BigDecimal; -import java.util.Date; -import java.util.HashMap; -import java.util.Map; - public class BaiduPayService extends BasePayService { public static final String APP_KEY = "appKey"; @@ -63,7 +69,7 @@ public class BaiduPayService extends BasePayService { if (!RESPONSE_SUCCESS.equals(params.get(RESPONSE_STATUS))) { return false; } - return signVerify(params, String.valueOf(params.get(RSA_SIGN))) && verifySource(String.valueOf(params.get(TP_ORDER_ID))); + return signVerify(params, String.valueOf(params.get(RSA_SIGN))); } /** @@ -73,7 +79,6 @@ public class BaiduPayService extends BasePayService { * @param sign 签名原文 * @return 结果 */ - @Override public boolean signVerify(Map params, String sign) { String rsaSign = String.valueOf(params.get(RSA_SIGN)); String targetRsaSign = getRsaSign(params, RSA_SIGN); @@ -81,10 +86,6 @@ public class BaiduPayService extends BasePayService { return StringUtils.equals(rsaSign, targetRsaSign); } - @Override - public boolean verifySource(String id) { - return true; - } /** * 返回创建的订单信息 @@ -439,7 +440,6 @@ public class BaiduPayService extends BasePayService { * @param transactionType 交易类型 * @return 结果 */ - @Override public Map secondaryInterface(Object orderId, String siteId, TransactionType transactionType) { diff --git a/pay-java-common/src/main/java/com/egzosn/pay/common/api/BasePayConfigStorage.java b/pay-java-common/src/main/java/com/egzosn/pay/common/api/BasePayConfigStorage.java index 24b7af8..b8b2b17 100644 --- a/pay-java-common/src/main/java/com/egzosn/pay/common/api/BasePayConfigStorage.java +++ b/pay-java-common/src/main/java/com/egzosn/pay/common/api/BasePayConfigStorage.java @@ -4,8 +4,6 @@ import java.util.HashMap; import java.util.Map; import java.util.concurrent.locks.Lock; -import com.egzosn.pay.common.bean.MsgType; - /** * 支付基础配置存储 * @@ -51,12 +49,6 @@ public abstract class BasePayConfigStorage implements PayConfigStorage { */ private String payType; - /** - * 消息来源类型 - */ - @Deprecated - private MsgType msgType; - /** * 访问令牌 每次请求其他方法都要传入的值 @@ -158,37 +150,36 @@ public abstract class BasePayConfigStorage implements PayConfigStorage { this.payType = payType; } - @Override - public MsgType getMsgType() { - return msgType; - } - - public void setMsgType(MsgType msgType) { - this.msgType = msgType; - } /** * 获取访问令牌 - * @return 访问令牌 + * + * @return 访问令牌 */ public String getAccessToken() { return this.accessToken; } + /** * 获取access token锁 + * * @return access token锁 */ public Lock getAccessTokenLock() { return this.accessTokenLock; } + /** * 强制将access token过期掉 - * @return 过期时间 + * + * @return 过期时间 */ public long getExpiresTime() { return expiresTime; } + /** * 访问令牌是否过期 + * * @return true过期 */ public boolean isAccessTokenExpired() { @@ -251,7 +242,7 @@ public abstract class BasePayConfigStorage implements PayConfigStorage { @Override public Map getAttrs() { - if (null == attr){ + if (null == attr) { attr = new HashMap<>(); } return attr; @@ -265,7 +256,8 @@ public abstract class BasePayConfigStorage implements PayConfigStorage { /** * 添加配置信息 - * @param key key + * + * @param key key * @param value 值 */ public void addAttr(String key, Object value) { diff --git a/pay-java-common/src/main/java/com/egzosn/pay/common/api/BasePayService.java b/pay-java-common/src/main/java/com/egzosn/pay/common/api/BasePayService.java index 605ed7d..2716266 100644 --- a/pay-java-common/src/main/java/com/egzosn/pay/common/api/BasePayService.java +++ b/pay-java-common/src/main/java/com/egzosn/pay/common/api/BasePayService.java @@ -21,9 +21,7 @@ import com.egzosn.pay.common.bean.PayMessage; import com.egzosn.pay.common.bean.PayOrder; import com.egzosn.pay.common.bean.PayOutMessage; import com.egzosn.pay.common.bean.RefundOrder; -import com.egzosn.pay.common.bean.TransactionType; import com.egzosn.pay.common.bean.TransferOrder; -import com.egzosn.pay.common.exception.PayErrorException; import com.egzosn.pay.common.http.HttpConfigStorage; import com.egzosn.pay.common.http.HttpRequestTemplate; import com.egzosn.pay.common.util.MatrixToImageWriter; @@ -112,7 +110,8 @@ public abstract class BasePayService implements Pay String base64ClientID = null; try { base64ClientID = com.egzosn.pay.common.util.sign.encrypt.Base64.encode(String.format("%s:%s", user, password).getBytes("UTF-8")); - } catch (UnsupportedEncodingException e) { + } + catch (UnsupportedEncodingException e) { LOG.error(e); } @@ -154,10 +153,12 @@ public abstract class BasePayService implements Pay Map orderInfo = orderInfo(order); return buildRequest(orderInfo, MethodType.POST); } + /** * app支付 + * * @param order 订单信息 - * @param 预订单类型 + * @param 预订单类型 * @return 对应app所需参数信息 */ @Override @@ -173,7 +174,7 @@ public abstract class BasePayService implements Pay */ @Override public BufferedImage genQrPay(O order) { - return MatrixToImageWriter.writeInfoToJpgBuff(getQrPay(order)); + return MatrixToImageWriter.writeInfoToJpgBuff(getQrPay(order)); } /** @@ -199,7 +200,8 @@ public abstract class BasePayService implements Pay if (valueStr.equals(new String(valueStr.getBytes("iso8859-1"), "iso8859-1"))) { valueStr = new String(valueStr.getBytes("iso8859-1"), payConfigStorage.getInputCharset()); } - } catch (UnsupportedEncodingException e) { + } + catch (UnsupportedEncodingException e) { LOG.error(e); } } @@ -265,7 +267,6 @@ public abstract class BasePayService implements Pay } - /** * 申请退款接口 * @@ -281,7 +282,6 @@ public abstract class BasePayService implements Pay } - /** * 查询退款 * @@ -309,19 +309,6 @@ public abstract class BasePayService implements Pay return callback.perform(downloadbill(billDate, billType)); } - /** - * @param tradeNoOrBillDate 支付平台订单号或者账单类型, 具体请 类型为{@link String }或者 {@link Date },类型须强制限制,类型不对应则抛出异常{@link PayErrorException} - * @param outTradeNoBillType 商户单号或者 账单类型 - * @param transactionType 交易类型 - * @param callback 处理器 - * @param 返回类型 - * @return 返回支付方对应接口的结果 - */ - @Override - public T secondaryInterface(Object tradeNoOrBillDate, String outTradeNoBillType, TransactionType transactionType, Callback callback) { - return callback.perform(secondaryInterface(tradeNoOrBillDate, outTradeNoBillType, transactionType)); - } - /** * 转账 * @@ -450,12 +437,13 @@ public abstract class BasePayService implements Pay /** * 预订单回调处理器,用于订单信息的扩展 * 签名之前使用 - * 如果需要进行扩展请重写该方法即可 + * 如果需要进行扩展请重写该方法即可 + * * @param orderInfo 预订单信息 * @param orderInfo 订单信息 * @return 处理后订单信息 */ - public Map preOrderHandler(Map orderInfo, O payOrder){ + public Map preOrderHandler(Map orderInfo, O payOrder) { return orderInfo; } diff --git a/pay-java-common/src/main/java/com/egzosn/pay/common/api/PayConfigStorage.java b/pay-java-common/src/main/java/com/egzosn/pay/common/api/PayConfigStorage.java index 3885601..b6bc600 100644 --- a/pay-java-common/src/main/java/com/egzosn/pay/common/api/PayConfigStorage.java +++ b/pay-java-common/src/main/java/com/egzosn/pay/common/api/PayConfigStorage.java @@ -1,7 +1,6 @@ package com.egzosn.pay.common.api; import com.egzosn.pay.common.bean.Attrs; -import com.egzosn.pay.common.bean.MsgType; /** * 支付客户端配置存储 @@ -23,11 +22,22 @@ public interface PayConfigStorage extends Attrs { /** * 应用id + * 纠正名称 * * @return 应用id + * @see #getAppId() */ + @Deprecated String getAppid(); + /** + * 应用id + * 纠正名称 + * + * @return 应用id + */ + String getAppId(); + /** * 合作商唯一标识 * @@ -100,17 +110,6 @@ public interface PayConfigStorage extends Attrs { */ String getPayType(); - /** - * 消息类型 - * - * @return "text" 或者 "xml",json - * @see #getMsgType - * @see MsgType - */ - @Deprecated - MsgType getMsgType(); - - /** * 应该是线程安全的 * diff --git a/pay-java-common/src/main/java/com/egzosn/pay/common/api/PayMessageRouter.java b/pay-java-common/src/main/java/com/egzosn/pay/common/api/PayMessageRouter.java index 24e849b..2fe8825 100644 --- a/pay-java-common/src/main/java/com/egzosn/pay/common/api/PayMessageRouter.java +++ b/pay-java-common/src/main/java/com/egzosn/pay/common/api/PayMessageRouter.java @@ -1,11 +1,5 @@ package com.egzosn.pay.common.api; -import com.egzosn.pay.common.bean.PayMessage; -import com.egzosn.pay.common.bean.PayOutMessage; -import com.egzosn.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.Map; @@ -14,6 +8,13 @@ import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import com.egzosn.pay.common.bean.PayMessage; +import com.egzosn.pay.common.bean.PayOutMessage; +import com.egzosn.pay.common.util.LogExceptionHandler; + /** *
  * 支付消息路由器,通过代码化的配置,把来自支付的消息交给handler处理
@@ -124,15 +125,13 @@ public class PayMessageRouter {
      * 处理支付消息
      *
      * @param payMessage 支付消息
-     * @param storage 支付配置
+     * @param storage    支付配置
      * @return 支付输出结果
      */
     public PayOutMessage route(Map payMessage, PayConfigStorage storage) {
         PayMessage message = payService.createMessage(payMessage);
         message.setPayType(storage.getPayType());
-        if (null != storage.getMsgType()){
-            message.setMsgType(storage.getMsgType().name());
-        }
+
         return route(message);
     }
 
@@ -172,7 +171,8 @@ public class PayMessageRouter {
                             }
                         })
                 );
-            } else {
+            }
+            else {
                 res = rule.service(payMessage, payService, exceptionHandler);
                 // 在同步操作结束,session访问结束
                 if (LOG.isDebugEnabled()) {
@@ -190,9 +190,11 @@ public class PayMessageRouter {
                             future.get();
                             LOG.debug("End session access: async=true, fromPay=" + payMessage.getFromPay());
 
-                        } catch (InterruptedException e) {
+                        }
+                        catch (InterruptedException e) {
                             LOG.error("Error happened when wait task finish", e);
-                        } catch (ExecutionException e) {
+                        }
+                        catch (ExecutionException 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 97bd210..08477f5 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
@@ -1,20 +1,21 @@
 package com.egzosn.pay.common.api;
 
 
-import com.egzosn.pay.common.bean.PayMessage;
-import com.egzosn.pay.common.bean.PayOutMessage;
-import com.egzosn.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;
 
+import com.egzosn.pay.common.bean.PayMessage;
+import com.egzosn.pay.common.bean.PayOutMessage;
+import com.egzosn.pay.common.exception.PayErrorException;
+
 
 /**
  * Route规则 路由
- * @author  egan
+ *
+ * @author egan
  * 
  *  email egzosn@gmail.com
  *  date 2016-6-1 11:28:01
@@ -32,10 +33,6 @@ public class PayMessageRouterRule {
      */
     private boolean async = false;
 
-    /**
-     * 消息类型
-     */
-    private String msgType;
     /**
      * 支付类型
      */
@@ -74,7 +71,7 @@ public class PayMessageRouterRule {
     /**
      * 设置是否异步执行,默认是true
      *
-     * @param async  是否异步执行,默认是true
+     * @param async 是否异步执行,默认是true
      * @return Route规则
      */
     public PayMessageRouterRule async(boolean async) {
@@ -82,21 +79,11 @@ public class PayMessageRouterRule {
         return this;
     }
 
-    /**
-     * 如果msgType等于某值
-     *
-     * @param msgType 消息类型
-     * @return Route规则
-     */
-    public PayMessageRouterRule msgType(String msgType) {
-        this.msgType = msgType;
-        return this;
-    }
 
     /**
      * 如果payType等于某值
      *
-     * @param payType  支付类型
+     * @param payType 支付类型
      * @return Route规则
      */
     public PayMessageRouterRule payType(String payType) {
@@ -110,13 +97,12 @@ public class PayMessageRouterRule {
      * @param transactionType 交易类型
      * @return Route规则
      */
-    public PayMessageRouterRule transactionType(String ... transactionType) {
+    public PayMessageRouterRule transactionType(String... transactionType) {
         this.transactionType = transactionType;
         return this;
     }
 
 
-
     /**
      * 如果subject等于某值
      *
@@ -138,10 +124,11 @@ public class PayMessageRouterRule {
         this.rSubject = regex;
         return this;
     }
+
     /**
      * 如果subject匹配该正则表达式
      *
-     * @param key 需要匹配支付消息内键的名字
+     * @param key   需要匹配支付消息内键的名字
      * @param regex key值对应的正则
      * @return Route规则
      */
@@ -165,7 +152,7 @@ public class PayMessageRouterRule {
     /**
      * 设置消息拦截器
      *
-     * @param interceptor 消息拦截器
+     * @param interceptor       消息拦截器
      * @param otherInterceptors 其他消息拦截器
      * @return Route规则
      */
@@ -192,7 +179,7 @@ public class PayMessageRouterRule {
     /**
      * 设置消息处理器
      *
-     * @param handler 消息处理器
+     * @param handler       消息处理器
      * @param otherHandlers 其他消息处理器
      * @return Route规则
      */
@@ -229,41 +216,41 @@ public class PayMessageRouterRule {
     /**
      * 将支付事件修正为不区分大小写,
      * 比如框架定义的事件常量为
+     *
      * @param payMessage 支付消息
      * @return 是否匹配通过
      */
     protected boolean test(PayMessage payMessage) {
         return (
-                       (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.payType == null || this.payType.equals((payMessage.getPayType() == null ? null : payMessage.getPayType())))
                         &&
                         (this.transactionType == null || equalsTransactionType(payMessage.getTransactionType()))
                         &&
-                        (this.key == null ||this.rValue == null || Pattern
+                        (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()))
                         &&
                         (this.rSubject == null || Pattern
                                 .matches(this.rSubject, payMessage.getSubject() == null ? "" : payMessage.getSubject().trim()))
-                )
+        )
                 ;
     }
 
     /**
      * 匹配交易类型
+     *
      * @param transactionType 交易类型
      * @return 匹配交易类型
      */
     public boolean equalsTransactionType(String transactionType) {
-        if (null == transactionType){
+        if (null == transactionType) {
             return false;
         }
 
-        for (String type :this.getTransactionType()){
-            if (type.toLowerCase().equals((transactionType.toLowerCase()))){
+        for (String type : this.getTransactionType()) {
+            if (type.toLowerCase().equals((transactionType.toLowerCase()))) {
                 return true;
             }
         }
@@ -273,15 +260,16 @@ public class PayMessageRouterRule {
 
 
     /**
-     *  返回支付响应消息
-     * @param payMessage 支付消息
-     * @param payService 支付服务
+     * 返回支付响应消息
+     *
+     * @param payMessage       支付消息
+     * @param payService       支付服务
      * @param exceptionHandler 异常处理器
      * @return 支付响应消息
      */
     protected PayOutMessage service(PayMessage payMessage,
-                                        PayService payService,
-                                        PayErrorExceptionHandler exceptionHandler) {
+                                    PayService payService,
+                                    PayErrorExceptionHandler exceptionHandler) {
 
         try {
 
@@ -301,7 +289,8 @@ public class PayMessageRouterRule {
                 res = handler.handle(payMessage, context, payService);
             }
             return res;
-        } catch (PayErrorException e) {
+        }
+        catch (PayErrorException e) {
             exceptionHandler.handle(e);
         }
         return null;
@@ -320,15 +309,6 @@ public class PayMessageRouterRule {
         this.async = async;
     }
 
-
-    public String getMsgType() {
-        return msgType;
-    }
-
-    public void setMsgType(String msgType) {
-        this.msgType = msgType;
-    }
-
     public String getPayType() {
         return payType;
     }
diff --git a/pay-java-common/src/main/java/com/egzosn/pay/common/api/PayService.java b/pay-java-common/src/main/java/com/egzosn/pay/common/api/PayService.java
index 0c1b6ac..7a66d42 100644
--- a/pay-java-common/src/main/java/com/egzosn/pay/common/api/PayService.java
+++ b/pay-java-common/src/main/java/com/egzosn/pay/common/api/PayService.java
@@ -1,20 +1,26 @@
 package com.egzosn.pay.common.api;
 
-import com.egzosn.pay.common.bean.*;
-import com.egzosn.pay.common.exception.PayErrorException;
-import com.egzosn.pay.common.http.HttpConfigStorage;
-import com.egzosn.pay.common.http.HttpRequestTemplate;
-
 import java.awt.image.BufferedImage;
 import java.io.InputStream;
 import java.util.Date;
 import java.util.Map;
 
+import com.egzosn.pay.common.bean.MethodType;
+import com.egzosn.pay.common.bean.PayMessage;
+import com.egzosn.pay.common.bean.PayOrder;
+import com.egzosn.pay.common.bean.PayOutMessage;
+import com.egzosn.pay.common.bean.RefundOrder;
+import com.egzosn.pay.common.bean.RefundResult;
+import com.egzosn.pay.common.bean.TransactionType;
+import com.egzosn.pay.common.bean.TransferOrder;
+import com.egzosn.pay.common.http.HttpConfigStorage;
+import com.egzosn.pay.common.http.HttpRequestTemplate;
+
 /**
  * 支付服务
  *
  * @author egan
- *         
+ * 
  *         email egzosn@gmail.com
  *         date 2016-5-18 14:09:01
  *         
@@ -60,53 +66,35 @@ public interface PayService { */ boolean verify(Map params); - /** - * 签名校验 - * 后面版本废弃 - * @param params 参数集 - * @param sign 签名原文 - * @return 签名校验 true通过 - */ - @Deprecated - boolean signVerify(Map params, String sign); - - - /** - * 支付宝需要,微信是否也需要再次校验来源,进行订单查询 - * 校验数据来源 - * 后面版本废弃 - * @param id 业务id, 数据的真实性. - * @return true通过 - */ - @Deprecated - boolean verifySource(String id); /** * 返回创建的订单信息 * * @param order 支付订单 + * @param 预订单类型 * @return 订单信息 - * @param 预订单类型 * @see PayOrder 支付订单信息 */ - Map orderInfo(O order); + Map orderInfo(O order); /** * 页面转跳支付, 返回对应页面重定向信息 * * @param order 订单信息 - * @param 预订单类型 + * @param 预订单类型 * @return 对应页面重定向信息 */ - String toPay(O order); + String toPay(O order); + /** * app支付 + * * @param order 订单信息 - * @param 预订单类型 + * @param 预订单类型 * @return 对应app所需参数信息 */ - Map app(O order); + Map app(O order); /** * 创建签名 @@ -118,7 +106,6 @@ public interface PayService { String createSign(String content, String characterEncoding); - /** * 将请求参数或者请求流转化为 Map * @@ -161,27 +148,29 @@ public interface PayService { * 获取输出二维码,用户返回给支付端, * * @param order 发起支付的订单信息 - * @param 预订单类型 + * @param 预订单类型 * @return 返回图片信息,支付时需要的 */ - BufferedImage genQrPay(O order); + BufferedImage genQrPay(O order); + /** * 获取输出二维码信息, * * @param order 发起支付的订单信息 - * @param 预订单类型 + * @param 预订单类型 * @return 返回二维码信息,,支付时需要的 */ - String getQrPay(O order); + String getQrPay(O order); /** * 刷卡付,pos主动扫码付款(条码付) * 刷脸付 + * * @param order 发起支付的订单信息 - * @param 预订单类型 + * @param 预订单类型 * @return 返回支付结果 */ - Map microPay(O order); + Map microPay(O order); /** * 交易查询接口 @@ -245,7 +234,6 @@ public interface PayService { T cancel(String tradeNo, String outTradeNo, Callback callback); - /** * 申请退款接口 * @@ -265,7 +253,6 @@ public interface PayService { T refund(RefundOrder refundOrder, Callback callback); - /** * 查询退款 * @@ -305,32 +292,6 @@ public interface PayService { T downloadbill(Date billDate, String billType, Callback callback); - /** - * 通用查询接口 - * 接下来移除此方法 - * @param tradeNoOrBillDate 支付平台订单号或者账单类型, 具体请 - * 类型为{@link String }或者 {@link Date },类型须强制限制,类型不对应则抛出异常{@link PayErrorException} - * @param outTradeNoBillType 商户单号或者 账单类型 - * @param transactionType 交易类型 - * @return 返回支付方对应接口的结果 - */ - @Deprecated - Map secondaryInterface(Object tradeNoOrBillDate, String outTradeNoBillType, TransactionType transactionType); - - /** - * 通用查询接口 - * 接下来移除此方法 - * @param tradeNoOrBillDate 支付平台订单号或者账单日期, 具体请 类型为{@link String }或者 {@link Date },类型须强制限制,类型不对应则抛出异常{@link PayErrorException} - * @param outTradeNoBillType 商户单号或者 账单类型 - * @param transactionType 交易类型 - * @param callback 处理器 - * @param 返回类型 - * @return 返回支付方对应接口的结果 - */ - @Deprecated - T secondaryInterface(Object tradeNoOrBillDate, String outTradeNoBillType, TransactionType transactionType, Callback callback); - - /** * 转账 * @@ -409,6 +370,7 @@ public interface PayService { /** * 创建消息 + * * @param message 支付平台返回的消息 * @return 支付消息对象 */ @@ -417,12 +379,13 @@ public interface PayService { /** * 预订单回调处理器,用于订单信息的扩展 * 签名之前使用 - * 如果需要进行扩展请重写该方法即可 + * 如果需要进行扩展请重写该方法即可 + * * @param orderInfo 商户平台预订单信息 - * @param payOrder 订单信息 - * @param 预订单类型 + * @param payOrder 订单信息 + * @param 预订单类型 * @return 处理后订单信息 */ - Map preOrderHandler(Map orderInfo, O payOrder); + Map preOrderHandler(Map orderInfo, O payOrder); } diff --git a/pay-java-common/src/main/java/com/egzosn/pay/common/bean/MsgType.java b/pay-java-common/src/main/java/com/egzosn/pay/common/bean/MsgType.java deleted file mode 100644 index 8806133..0000000 --- a/pay-java-common/src/main/java/com/egzosn/pay/common/bean/MsgType.java +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright 2002-2017 the original egan or egan. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - - -package com.egzosn.pay.common.bean; - -/** - * 消息类型 - * @author: egan - *
- *     email egzosn@gmail.com
- *     date 2016/11/18 0:59
- *  
- */ -public enum MsgType { - text, xml,json -} diff --git a/pay-java-common/src/main/java/com/egzosn/pay/common/bean/PayMessage.java b/pay-java-common/src/main/java/com/egzosn/pay/common/bean/PayMessage.java index 11c247a..888b787 100644 --- a/pay-java-common/src/main/java/com/egzosn/pay/common/bean/PayMessage.java +++ b/pay-java-common/src/main/java/com/egzosn/pay/common/bean/PayMessage.java @@ -7,6 +7,7 @@ import java.util.Map; /** * 支付回调消息 * 基础实现,具体可根据具体支付回调的消息去实现 + * * @author egan *
  *     email egzosn@gmail.com
@@ -15,8 +16,6 @@ import java.util.Map;
  */
 public class PayMessage implements Serializable {
     private Map payMessage = null;
-    @Deprecated
-    private String msgType;
     private String payType;
     private String transactionType;
     private String fromPay;
@@ -30,16 +29,14 @@ public class PayMessage implements Serializable {
         this.payMessage = payMessage;
     }
 
-    public PayMessage(Map payMessage, String payType, String msgType) {
+    public PayMessage(Map payMessage, String payType) {
         this.payMessage = payMessage;
         this.payType = payType;
-        this.msgType = msgType;
     }
 
 
-    public PayMessage(Map payMessage, String msgType, String payType, String transactionType) {
+    public PayMessage(Map payMessage, String payType, String transactionType) {
         this.payMessage = payMessage;
-        this.msgType = msgType;
         this.payType = payType;
         this.transactionType = transactionType;
     }
@@ -48,16 +45,6 @@ public class PayMessage implements Serializable {
         this.payMessage = payMessage;
     }
 
-    @Deprecated
-    public String getMsgType() {
-        return msgType;
-    }
-
-    @Deprecated
-    public void setMsgType(String msgType) {
-        this.msgType = msgType;
-    }
-
 
     public String getPayType() {
         return payType;
@@ -72,7 +59,7 @@ public class PayMessage implements Serializable {
     }
 
     public void setTransactionType(String transactionType) {
-            this.transactionType = transactionType;
+        this.transactionType = transactionType;
     }
 
     public String getFromPay() {
@@ -90,28 +77,31 @@ public class PayMessage implements Serializable {
     public void setDescribe(String describe) {
         this.describe = describe;
     }
-    public String getDiscount(){
+
+    public String getDiscount() {
         return (String) payMessage.get("discount");
     }
-    public String getSubject(){
+
+    public String getSubject() {
         return (String) payMessage.get("subject");
     }
 
 
-
     /////////微信与支付宝共用
-    public String getOutTradeNo(){
+    public String getOutTradeNo() {
         return (String) payMessage.get("out_trade_no");
     }
 
-    public String getSign(){
+    public String getSign() {
         return (String) payMessage.get("sign");
     }
 
-    public Number getTotalFee(){
+    public Number getTotalFee() {
         String totalFee = (String) payMessage.get("total_fee");
-        if (null == totalFee || "".equals(totalFee)){    return 0;      }
-        if (isNumber(totalFee)){
+        if (null == totalFee || "".equals(totalFee)) {
+            return 0;
+        }
+        if (isNumber(totalFee)) {
             return new BigDecimal(totalFee);
         }
         return 0;
@@ -120,13 +110,11 @@ public class PayMessage implements Serializable {
     /////////微信与支付宝共用
 
 
-
-    public boolean isNumber(String str){
+    public boolean isNumber(String str) {
         return str.matches("^(-?[1-9]\\d*\\.?\\d*)|(-?0\\.\\d*[1-9])|(-?[0])|(-?[0]\\.\\d*)$");
     }
 
 
-
     @Override
     public String toString() {
         return payMessage.toString();
@@ -137,5 +125,4 @@ public class PayMessage implements Serializable {
     }
 
 
-
 }
diff --git a/pay-java-common/src/main/java/com/egzosn/pay/common/bean/PayOutMessage.java b/pay-java-common/src/main/java/com/egzosn/pay/common/bean/PayOutMessage.java
index 97f686f..2f50c10 100644
--- a/pay-java-common/src/main/java/com/egzosn/pay/common/bean/PayOutMessage.java
+++ b/pay-java-common/src/main/java/com/egzosn/pay/common/bean/PayOutMessage.java
@@ -1,15 +1,16 @@
 package com.egzosn.pay.common.bean;
 
+import java.io.Serializable;
+
 import com.alibaba.fastjson.JSONObject;
 import com.egzosn.pay.common.bean.outbuilder.JsonBuilder;
 import com.egzosn.pay.common.bean.outbuilder.TextBuilder;
 import com.egzosn.pay.common.bean.outbuilder.XmlBuilder;
 
-import java.io.Serializable;
-
 /**
- *  支付回调通知返回消息
- * @author  egan
+ * 支付回调通知返回消息
+ *
+ * @author egan
  * 
  *     email egzosn@gmail.com
  *     date 2016-6-1 11:40:30
@@ -17,7 +18,6 @@ import java.io.Serializable;
  */
 public abstract class PayOutMessage implements Serializable {
     protected String content;
-    protected String msgType;
 
 
     public String getContent() {
@@ -28,34 +28,32 @@ public abstract class PayOutMessage implements Serializable {
         this.content = content;
     }
 
-    public String getMsgType() {
-        return msgType;
-    }
-
-    public void setMsgType(String msgType) {
-        this.msgType = msgType;
-    }
-
     /**
      * 获得文本消息builder
+     *
      * @return 文本消息builder
      */
     public static TextBuilder TEXT() {
         return new TextBuilder();
     }
+
     /**
      * 获得XML消息builder
+     *
      * @return XML消息builder
      */
     public static XmlBuilder XML() {
         return new XmlBuilder();
     }
+
     /**
      * 获得Json消息builder
+     *
      * @return Json消息builder
      */
     public static JsonBuilder JSON() {
         return new JsonBuilder(new JSONObject());
     }
+
     public abstract String toMessage();
 }
diff --git a/pay-java-common/src/main/java/com/egzosn/pay/common/bean/outbuilder/PayJsonOutMessage.java b/pay-java-common/src/main/java/com/egzosn/pay/common/bean/outbuilder/PayJsonOutMessage.java
index 576134b..bf5e49b 100644
--- a/pay-java-common/src/main/java/com/egzosn/pay/common/bean/outbuilder/PayJsonOutMessage.java
+++ b/pay-java-common/src/main/java/com/egzosn/pay/common/bean/outbuilder/PayJsonOutMessage.java
@@ -1,19 +1,18 @@
 package com.egzosn.pay.common.bean.outbuilder;
 
-import com.egzosn.pay.common.bean.MsgType;
 import com.egzosn.pay.common.bean.PayOutMessage;
 
 /**
  * @author egan
- *  
+ * 
  *      email egzosn@gmail.com
  *      date 2016-6-1 11:40:30
  *   
*/ -public class PayJsonOutMessage extends PayOutMessage{ +public class PayJsonOutMessage extends PayOutMessage { public PayJsonOutMessage() { - this.msgType = MsgType.json.name(); + } @Override diff --git a/pay-java-common/src/main/java/com/egzosn/pay/common/bean/outbuilder/PayTextOutMessage.java b/pay-java-common/src/main/java/com/egzosn/pay/common/bean/outbuilder/PayTextOutMessage.java index 7d31196..dc99267 100644 --- a/pay-java-common/src/main/java/com/egzosn/pay/common/bean/outbuilder/PayTextOutMessage.java +++ b/pay-java-common/src/main/java/com/egzosn/pay/common/bean/outbuilder/PayTextOutMessage.java @@ -1,6 +1,5 @@ package com.egzosn.pay.common.bean.outbuilder; -import com.egzosn.pay.common.bean.MsgType; import com.egzosn.pay.common.bean.PayOutMessage; /** @@ -13,7 +12,6 @@ import com.egzosn.pay.common.bean.PayOutMessage; public class PayTextOutMessage extends PayOutMessage{ public PayTextOutMessage() { - this.msgType = MsgType.text.name(); } @Override diff --git a/pay-java-common/src/main/java/com/egzosn/pay/common/bean/outbuilder/PayXmlOutMessage.java b/pay-java-common/src/main/java/com/egzosn/pay/common/bean/outbuilder/PayXmlOutMessage.java index c4b96bd..576d1ba 100644 --- a/pay-java-common/src/main/java/com/egzosn/pay/common/bean/outbuilder/PayXmlOutMessage.java +++ b/pay-java-common/src/main/java/com/egzosn/pay/common/bean/outbuilder/PayXmlOutMessage.java @@ -1,6 +1,5 @@ package com.egzosn.pay.common.bean.outbuilder; -import com.egzosn.pay.common.bean.MsgType; import com.egzosn.pay.common.bean.PayOutMessage; /** @@ -15,7 +14,6 @@ public class PayXmlOutMessage extends PayOutMessage{ private String code; public PayXmlOutMessage() { - this.msgType = MsgType.xml.name(); } public String getCode() { diff --git a/pay-java-common/src/main/java/com/egzosn/pay/common/http/UriVariables.java b/pay-java-common/src/main/java/com/egzosn/pay/common/http/UriVariables.java index f183b68..e7b6ae4 100644 --- a/pay-java-common/src/main/java/com/egzosn/pay/common/http/UriVariables.java +++ b/pay-java-common/src/main/java/com/egzosn/pay/common/http/UriVariables.java @@ -1,14 +1,13 @@ package com.egzosn.pay.common.http; -import com.alibaba.fastjson.JSONObject; -import com.egzosn.pay.common.bean.result.PayException; -import com.egzosn.pay.common.exception.PayErrorException; - import java.io.UnsupportedEncodingException; import java.net.URLEncoder; import java.util.List; import java.util.Map; -import java.util.Set; + +import com.alibaba.fastjson.JSONObject; +import com.egzosn.pay.common.bean.result.PayException; +import com.egzosn.pay.common.exception.PayErrorException; /** * URL表达式处理器 @@ -23,22 +22,22 @@ public class UriVariables { /** * 依次匹配 - * @param uri 匹配的uri,带代表式 + * + * @param uri 匹配的uri,带代表式 * @param uriVariables 匹配表达式的值 * @return 匹配完的url * - * System.out.println(getUri("http://egan.in/{a}/ba/{a1}?{bb}={a1}", "no1", "no2", "no3", "no4")); - * 结果 http://egan.in/no1/ba/no2?no3=no4 + * System.out.println(getUri("http://egan.in/{a}/ba/{a1}?{bb}={a1}", "no1", "no2", "no3", "no4")); + * 结果 http://egan.in/no1/ba/no2?no3=no4 * - * */ public static String getUri(String uri, Object... uriVariables) { - if (null == uriVariables){ + if (null == uriVariables) { return uri; } - for (Object variable : uriVariables){ - if (null == variable){ + for (Object variable : uriVariables) { + if (null == variable) { continue; } uri = uri.replaceFirst("\\{\\w+\\}", variable.toString()); @@ -47,29 +46,29 @@ public class UriVariables { } - /** * 匹配Map.key - * @param uri 匹配的uri,带代表式 + * + * @param uri 匹配的uri,带代表式 * @param uriVariables 匹配表达式的值 * @return 匹配完的url * - * Map<String, Object> uriVariable = new HashMap<String, Object>(); - * uriVariable.put("a", "no1"); - * uriVariable.put("a1", "no2"); - * uriVariable.put("bb", "no3"); - * System.out.println(getUri("http://egan.in/{a}/ba/{a1}?{bb}={a1}", uriVariable)); - * 结果 http://egan.in/no1/ba/no2?no3=no2 + * Map<String, Object> uriVariable = new HashMap<String, Object>(); + * uriVariable.put("a", "no1"); + * uriVariable.put("a1", "no2"); + * uriVariable.put("bb", "no3"); + * System.out.println(getUri("http://egan.in/{a}/ba/{a1}?{bb}={a1}", uriVariable)); + * 结果 http://egan.in/no1/ba/no2?no3=no2 * */ public static String getUri(String uri, Map uriVariables) { - if (null == uriVariables){ + if (null == uriVariables) { return uri; } for (Map.Entry entry : uriVariables.entrySet()) { Object uriVariable = entry.getValue(); - if (null == uriVariable){ + if (null == uriVariable) { continue; } @@ -79,15 +78,15 @@ public class UriVariables { } - /** * Map转化为对应得参数字符串 + * * @param pe 参数 * @return 参数字符串 */ - public static String getMapToParameters(Map pe){ + public static String getMapToParameters(Map pe) { StringBuilder builder = new StringBuilder(); - for (Map.Entry entry : (Set)pe.entrySet()) { + for (Map.Entry entry : pe.entrySet()) { Object o = entry.getValue(); if (null == o) { @@ -106,14 +105,15 @@ public class UriVariables { continue; } String value = os[i].toString().trim(); - valueStr += (i == len - 1) ? value : value + ","; + valueStr += (i == len - 1) ? value : value + ","; } builder.append(entry.getKey()).append("=").append(URLEncoder.encode(valueStr, "utf-8")).append("&"); continue; } - builder.append(entry.getKey()).append("=").append(URLEncoder.encode( entry.getValue().toString(), "utf-8")).append("&"); - } catch (UnsupportedEncodingException e) { + builder.append(entry.getKey()).append("=").append(URLEncoder.encode(entry.getValue().toString(), "utf-8")).append("&"); + } + catch (UnsupportedEncodingException e) { e.printStackTrace(); } } @@ -129,7 +129,7 @@ public class UriVariables { * @param str 需要解析的字符串 * @return 解析的结果map */ - public static JSONObject getParametersToMap (String str) { + public static JSONObject getParametersToMap(String str) { JSONObject map = new JSONObject(); int len = str.length(); @@ -148,16 +148,19 @@ public class UriVariables { key = temp.toString(); temp.setLength(0); isKey = false; - } else { + } + else { temp.append(curChar); } - } else {// 如果当前生成的是value + } + else {// 如果当前生成的是value if (isOpen) { if (curChar == openName) { isOpen = false; } - } else {//如果没开启嵌套 + } + else {//如果没开启嵌套 if (curChar == '{') {//如果碰到,就开启嵌套 isOpen = true; openName = '}'; @@ -171,7 +174,8 @@ public class UriVariables { putKeyValueToMap(temp, isKey, key, map); temp.setLength(0); isKey = true; - } else { + } + else { temp.append(curChar); } } @@ -182,14 +186,15 @@ public class UriVariables { return map; } - private static void putKeyValueToMap (StringBuilder temp, boolean isKey, String key, Map map) { + private static void putKeyValueToMap(StringBuilder temp, boolean isKey, String key, Map map) { if (isKey) { key = temp.toString(); if (key.length() == 0) { throw new PayErrorException(new PayException("QString format illegal", "内容格式有误")); } map.put(key, ""); - } else { + } + else { if (key.length() == 0) { throw new PayErrorException(new PayException("QString format illegal", "内容格式有误")); } diff --git a/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/AliPayController.java b/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/AliPayController.java index 616636f..77a25b9 100644 --- a/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/AliPayController.java +++ b/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/AliPayController.java @@ -2,6 +2,22 @@ package com.egzosn.pay.demo.controller; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.math.BigDecimal; +import java.util.HashMap; +import java.util.Map; +import java.util.UUID; + +import javax.annotation.PostConstruct; +import javax.annotation.Resource; +import javax.imageio.ImageIO; +import javax.servlet.http.HttpServletRequest; + +import org.springframework.beans.factory.config.AutowireCapableBeanFactory; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + import com.egzosn.pay.ali.api.AliPayConfigStorage; import com.egzosn.pay.ali.api.AliPayService; import com.egzosn.pay.ali.bean.AliRefundResult; @@ -18,20 +34,6 @@ import com.egzosn.pay.common.util.sign.SignUtils; import com.egzosn.pay.demo.request.QueryOrder; import com.egzosn.pay.demo.service.handler.AliPayMessageHandler; import com.egzosn.pay.demo.service.interceptor.AliPayMessageInterceptor; -import org.springframework.beans.factory.config.AutowireCapableBeanFactory; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RestController; - -import javax.annotation.PostConstruct; -import javax.annotation.Resource; -import javax.imageio.ImageIO; -import javax.servlet.http.HttpServletRequest; -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.math.BigDecimal; -import java.util.HashMap; -import java.util.Map; -import java.util.UUID; /** @@ -48,22 +50,24 @@ public class AliPayController { private AliPayService service = null; @Resource private AutowireCapableBeanFactory spring; + /** * 设置普通公钥的方式 * 普通公钥方式与证书公钥方式为两者取其一的方式 - * @param aliPayConfigStorage 支付宝配置信息 * + * @param aliPayConfigStorage 支付宝配置信息 */ - private static void keyPublic(AliPayConfigStorage aliPayConfigStorage){ + private static void keyPublic(AliPayConfigStorage aliPayConfigStorage) { aliPayConfigStorage.setKeyPublic("MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDIgHnOn7LLILlKETd6BFRJ0GqgS2Y3mn1wMQmyh9zEyWlz5p1zrahRahbXAfCfSqshSNfqOmAQzSHRVjCqjsAw1jyqrXaPdKBmr90DIpIxmIyKXv4GGAkPyJ/6FTFY99uhpiq0qadD/uSzQsefWo0aTvP/65zi3eof7TcZ32oWpwIDAQAB"); } /** * 设置证书公钥信息 * 普通公钥方式与证书公钥方式为两者取其一的方式 + * * @param aliPayConfigStorage 支付宝配置信息 */ - private static void certKeyPublic(AliPayConfigStorage aliPayConfigStorage){ + private static void certKeyPublic(AliPayConfigStorage aliPayConfigStorage) { //设置为证书方式 aliPayConfigStorage.setCertSign(true); //设置证书存储方式,这里为路径 @@ -79,10 +83,10 @@ public class AliPayController { aliPayConfigStorage.setPid("2088102169916436"); aliPayConfigStorage.setAppid("2016080400165436"); //普通公钥方式与证书公钥方式为两者取其一的方式 -// keyPublic(aliPayConfigStorage); - certKeyPublic(aliPayConfigStorage); -// aliPayConfigStorage.setKeyPrivate("MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBAKroe/8h5vC4L6T+B2WdXiVwGsMvUKgb2XsKix6VY3m2wcf6tyzpNRDCNykbIwGtaeo7FshN+qZxdXHLiIam9goYncBit/8ojfLGy2gLxO/PXfzGxYGs0KsDZ+ryVPPmE34ZZ8jiJpR0ygzCFl8pN3QJPJRGTJn5+FTT9EF/9zyZAgMBAAECgYAktngcYC35u7cQXDk+jMVyiVhWYU2ULxdSpPspgLGzrZyG1saOcTIi/XVX8Spd6+B6nmLQeF/FbU3rOeuD8U2clzul2Z2YMbJ0FYay9oVZFfp5gTEFpFRTVfzqUaZQBIjJe/xHL9kQVqc5xHlE/LVA27/Kx3dbC35Y7B4EVBDYAQJBAOhsX8ZreWLKPhXiXHTyLmNKhOHJc+0tFH7Ktise/0rNspojU7o9prOatKpNylp9v6kux7migcMRdVUWWiVe+4ECQQC8PqsuEz7B0yqirQchRg1DbHjh64bw9Kj82EN1/NzOUd53tP9tg+SO97EzsibK1F7tOcuwqsa7n2aY48mQ+y0ZAkBndA2xcRcnvOOjtAz5VO8G7R12rse181HjGfG6AeMadbKg30aeaGCyIxN1loiSfNR5xsPJwibGIBg81mUrqzqBAkB+K6rkaPXJR9XtzvdWb/N3235yPkDlw7Z4MiOVM3RzvR/VMDV7m8lXoeDde2zQyeMOMYy6ztwA6WgE1bhGOnQRAkEAouUBv1sVdSBlsexX15qphOmAevzYrpufKgJIRLFWQxroXMS7FTesj+f+FmGrpPCxIde1dqJ8lqYLTyJmbzMPYw=="); - aliPayConfigStorage.setKeyPrivate("MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQCw7MD2Cwv/jnXssFjXnGx3JlGF57gJa2aYbJRV8MnNiPVpX4Ha+8ZjnQDhvkrWH4hHmzcujOr213HqloMpUSYBzCPiXGVRUUvdimejcHHTod7nI4g6nztzzfey/TXNDHmp7vY3pOIcjB0Zn0pkNAz2tKAFkqb4raHOqTB0QA0zD24Cn+26J2UJyYRcgeH0GtSQuUrm7yaGsuKakh+qtgWF6R71n5PMGOTQ5LH3i0WVHfCBkNGgJC6yC96HR4D7cosoyKD0+lp8UB/NVUWl7Tt/KLOgFUwh0GKSYFfv56O/VBV2+xqCGE4PlZESfVuOqz5vjjxzw3xDAUJrV8hSX/AJAgMBAAECggEBAKE0d3U4B4yo/2XUIH8EdgfykCFUSum6RFbpyBauORHfksyaSzV+ZvtomN8XhhSn0oJ8OMFfgM+86nz2+zdwSxMkMCYWTfLUAi4v59KRqAVO3kz4oS3Y3FDeAK3D7XuRvGFL7GgzAhtEx1cLPrsiehVn6s5pG15GxsIIgq/JlL1J88wn1zENLrVHmD6z/JpXvfb/RS1yR+5lyoohp4g0Ph9jJ3bCyUbRpK0QkPEzgAuWL0K2ITCL7PYHNAplI8d2xHHOLF9Qdjyx+ZrQ/RxtqzfyWzhqjsmp2qlgNCxWlt3woS9UhDB+nRvjEoWTJmIOszAMYuj8wGlX+3Ui3ALOdQECgYEA25EqnFPFinUnzgNvB6NYmh5STmZun6s4bUOLqwefKtEvrOtRwTu7sB7NIf37fizG3/MJUWHxiLy2/3ub4d2JxdDNBtJoEqnp6QB12qglCNa4CajdjtJa1dR81F9QvytsqEkmPYXFPPyviB0FcSIDAGMb3IbwvIfzBPY9WY8dJnECgYEAzkg3yKEFBZ8BU0WQ+3hyfKUoAhBEnxouxRSTBcXxwstJRiqaGTVe5aoJGQI+0xS7Z6q07XDtN2t97s6DnRLWbljsX6B64itzNhXRyzjdD3iZDU/KSw7khjhXf8XOZaj9eXmACDiUnkEn1xsM8bLiRGqB8y5f3aMY/RpuACGXnxkCgYEAx/zwT9Vpr1RIfjfYcJ+Su0X0994K0roUukj0tUJK8qf4gcsQ+y1aJe/YLib1ZBaKyj7G9O5+HmqtUAUZld/AdoJZzOXmz2EeYhD+R7wxh1xz4rCBpW3qOKvDS3jJxmZaIOoHv6/RWFxb0WGFrGcrTrX3EaWDLmWxr4pNlP5qsbECgYATllntrBR8/ycyEAX/SuWcHlaZM5BAh0zvm8+GGdCmDYWMqxjs0duL9URd4o+ynWJaKqR5c2KjA4r2tRdcP+Cqo7j2L5fbiAKtnQ7JvEGJaYsm72+nBuf+MrVkRZUepBhFg5r7rNu31zoAO+pTvQetNWvXeozRz93ckrjlPEtYaQKBgQDFwbV92rlRMLjZzlY+o0knoeJBjPQmPdiBTpGNimdy9L4c2Ure7affjcUiYhkKqrK5k5SScJTATgyQ7JF346FdtUtZ/6Kkj1RwJmmprPrDa9CATLoTle7g9OVd4sHT2ITHZMzPaF3ILvzcwJ70AD1xcxCQb+/7sDPmw7Mc8gOA7Q=="); + keyPublic(aliPayConfigStorage); + aliPayConfigStorage.setKeyPrivate("MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBAKroe/8h5vC4L6T+B2WdXiVwGsMvUKgb2XsKix6VY3m2wcf6tyzpNRDCNykbIwGtaeo7FshN+qZxdXHLiIam9goYncBit/8ojfLGy2gLxO/PXfzGxYGs0KsDZ+ryVPPmE34ZZ8jiJpR0ygzCFl8pN3QJPJRGTJn5+FTT9EF/9zyZAgMBAAECgYAktngcYC35u7cQXDk+jMVyiVhWYU2ULxdSpPspgLGzrZyG1saOcTIi/XVX8Spd6+B6nmLQeF/FbU3rOeuD8U2clzul2Z2YMbJ0FYay9oVZFfp5gTEFpFRTVfzqUaZQBIjJe/xHL9kQVqc5xHlE/LVA27/Kx3dbC35Y7B4EVBDYAQJBAOhsX8ZreWLKPhXiXHTyLmNKhOHJc+0tFH7Ktise/0rNspojU7o9prOatKpNylp9v6kux7migcMRdVUWWiVe+4ECQQC8PqsuEz7B0yqirQchRg1DbHjh64bw9Kj82EN1/NzOUd53tP9tg+SO97EzsibK1F7tOcuwqsa7n2aY48mQ+y0ZAkBndA2xcRcnvOOjtAz5VO8G7R12rse181HjGfG6AeMadbKg30aeaGCyIxN1loiSfNR5xsPJwibGIBg81mUrqzqBAkB+K6rkaPXJR9XtzvdWb/N3235yPkDlw7Z4MiOVM3RzvR/VMDV7m8lXoeDde2zQyeMOMYy6ztwA6WgE1bhGOnQRAkEAouUBv1sVdSBlsexX15qphOmAevzYrpufKgJIRLFWQxroXMS7FTesj+f+FmGrpPCxIde1dqJ8lqYLTyJmbzMPYw=="); +// certKeyPublic(aliPayConfigStorage); +// aliPayConfigStorage.setKeyPrivate("MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQCw7MD2Cwv/jnXssFjXnGx3JlGF57gJa2aYbJRV8MnNiPVpX4Ha+8ZjnQDhvkrWH4hHmzcujOr213HqloMpUSYBzCPiXGVRUUvdimejcHHTod7nI4g6nztzzfey/TXNDHmp7vY3pOIcjB0Zn0pkNAz2tKAFkqb4raHOqTB0QA0zD24Cn+26J2UJyYRcgeH0GtSQuUrm7yaGsuKakh+qtgWF6R71n5PMGOTQ5LH3i0WVHfCBkNGgJC6yC96HR4D7cosoyKD0+lp8UB/NVUWl7Tt/KLOgFUwh0GKSYFfv56O/VBV2+xqCGE4PlZESfVuOqz5vjjxzw3xDAUJrV8hSX/AJAgMBAAECggEBAKE0d3U4B4yo/2XUIH8EdgfykCFUSum6RFbpyBauORHfksyaSzV+ZvtomN8XhhSn0oJ8OMFfgM+86nz2+zdwSxMkMCYWTfLUAi4v59KRqAVO3kz4oS3Y3FDeAK3D7XuRvGFL7GgzAhtEx1cLPrsiehVn6s5pG15GxsIIgq/JlL1J88wn1zENLrVHmD6z/JpXvfb/RS1yR+5lyoohp4g0Ph9jJ3bCyUbRpK0QkPEzgAuWL0K2ITCL7PYHNAplI8d2xHHOLF9Qdjyx+ZrQ/RxtqzfyWzhqjsmp2qlgNCxWlt3woS9UhDB+nRvjEoWTJmIOszAMYuj8wGlX+3Ui3ALOdQECgYEA25EqnFPFinUnzgNvB6NYmh5STmZun6s4bUOLqwefKtEvrOtRwTu7sB7NIf37fizG3/MJUWHxiLy2/3ub4d2JxdDNBtJoEqnp6QB12qglCNa4CajdjtJa1dR81F9QvytsqEkmPYXFPPyviB0FcSIDAGMb3IbwvIfzBPY9WY8dJnECgYEAzkg3yKEFBZ8BU0WQ+3hyfKUoAhBEnxouxRSTBcXxwstJRiqaGTVe5aoJGQI+0xS7Z6q07XDtN2t97s6DnRLWbljsX6B64itzNhXRyzjdD3iZDU/KSw7khjhXf8XOZaj9eXmACDiUnkEn1xsM8bLiRGqB8y5f3aMY/RpuACGXnxkCgYEAx/zwT9Vpr1RIfjfYcJ+Su0X0994K0roUukj0tUJK8qf4gcsQ+y1aJe/YLib1ZBaKyj7G9O5+HmqtUAUZld/AdoJZzOXmz2EeYhD+R7wxh1xz4rCBpW3qOKvDS3jJxmZaIOoHv6/RWFxb0WGFrGcrTrX3EaWDLmWxr4pNlP5qsbECgYATllntrBR8/ycyEAX/SuWcHlaZM5BAh0zvm8+GGdCmDYWMqxjs0duL9URd4o+ynWJaKqR5c2KjA4r2tRdcP+Cqo7j2L5fbiAKtnQ7JvEGJaYsm72+nBuf+MrVkRZUepBhFg5r7rNu31zoAO+pTvQetNWvXeozRz93ckrjlPEtYaQKBgQDFwbV92rlRMLjZzlY+o0knoeJBjPQmPdiBTpGNimdy9L4c2Ure7affjcUiYhkKqrK5k5SScJTATgyQ7JF346FdtUtZ/6Kkj1RwJmmprPrDa9CATLoTle7g9OVd4sHT2ITHZMzPaF3ILvzcwJ70AD1xcxCQb+/7sDPmw7Mc8gOA7Q=="); aliPayConfigStorage.setNotifyUrl("http://pay.egzosn.com/payBack.json"); aliPayConfigStorage.setReturnUrl("http://pay.egzosn.com/payBack.html"); aliPayConfigStorage.setSignType(SignUtils.RSA2.name()); @@ -97,7 +101,7 @@ public class AliPayController { httpConfigStorage.setMaxTotal(20); //默认的每个路由的最大连接数 httpConfigStorage.setDefaultMaxPerRoute(10); - service = new AliPayService(aliPayConfigStorage, httpConfigStorage); + service = new AliPayService(aliPayConfigStorage, httpConfigStorage); //增加支付回调消息拦截器 service.addPayMessageInterceptor(new AliPayMessageInterceptor()); //设置回调消息处理 @@ -105,16 +109,15 @@ public class AliPayController { } - /** * 跳到支付页面 * 针对实时支付,即时付款 * - * @param price 金额 + * @param price 金额 * @return 跳到支付页面 */ @RequestMapping(value = "toPay.html", produces = "text/html;charset=UTF-8") - public String toPay( BigDecimal price) { + public String toPay(BigDecimal price) { //及时收款 PayOrder order = new PayOrder("订单title", "摘要", null == price ? BigDecimal.valueOf(0.01) : price, UUID.randomUUID().toString().replace("-", ""), AliTransactionType.PAGE); //WAP @@ -127,8 +130,6 @@ public class AliPayController { } - - /** * 获取支付预订单信息 * @@ -148,35 +149,39 @@ public class AliPayController { /** * 获取二维码图像 * 二维码支付 - * @param price 金额 + * + * @param price 金额 * @return 二维码图像 * @throws IOException IOException */ @RequestMapping(value = "toQrPay.jpg", produces = "image/jpeg;charset=UTF-8") - public byte[] toQrPay( BigDecimal price) throws IOException { + public byte[] toQrPay(BigDecimal price) throws IOException { //获取对应的支付账户操作工具(可根据账户id) ByteArrayOutputStream baos = new ByteArrayOutputStream(); - ImageIO.write(service.genQrPay( new PayOrder("订单title", "摘要", null == price ? BigDecimal.valueOf(0.01) : price, System.currentTimeMillis()+"", AliTransactionType.SWEEPPAY)), "JPEG", baos); + ImageIO.write(service.genQrPay(new PayOrder("订单title", "摘要", null == price ? BigDecimal.valueOf(0.01) : price, System.currentTimeMillis() + "", AliTransactionType.SWEEPPAY)), "JPEG", baos); return baos.toByteArray(); } + /** * 获取二维码地址 * 二维码支付 - * @param price 金额 + * + * @param price 金额 * @return 二维码图像 * @throws IOException IOException */ @RequestMapping(value = "getQrPay.json") public String getQrPay(BigDecimal price) throws IOException { //获取对应的支付账户操作工具(可根据账户id) - return service.getQrPay( new PayOrder("订单title", "摘要", null == price ? BigDecimal.valueOf(0.01) : price, System.currentTimeMillis()+"", AliTransactionType.SWEEPPAY)); + return service.getQrPay(new PayOrder("订单title", "摘要", null == price ? BigDecimal.valueOf(0.01) : price, System.currentTimeMillis() + "", AliTransactionType.SWEEPPAY)); } /** * 刷卡付,pos主动扫码付款(条码付) - * @param authCode 授权码,条码等 - * @param price 金额 + * + * @param authCode 授权码,条码等 + * @param price 金额 * @return 支付结果 */ @RequestMapping(value = "microPay") @@ -184,7 +189,7 @@ public class AliPayController { //获取对应的支付账户操作工具(可根据账户id) //条码付 PayOrder order = new PayOrder("egan order", "egan order", null == price ? BigDecimal.valueOf(0.01) : price, UUID.randomUUID().toString().replace("-", ""), AliTransactionType.BAR_CODE); - //声波付 + //声波付 // PayOrder order = new PayOrder("egan order", "egan order", null == price ? BigDecimal.valueOf(0.01) : price, UUID.randomUUID().toString().replace("-", ""), AliTransactionType.WAVE_CODE); //设置授权码,条码等 order.setAuthCode(authCode); @@ -204,15 +209,13 @@ public class AliPayController { /** * 支付回调地址 方式一 - * + *

* 方式二,{@link #payBack(HttpServletRequest)} 是属于简化方式, 试用与简单的业务场景 * - * * @param request 请求 - * * @return 返回对应的响应码 - * @see #payBack(HttpServletRequest) * @throws IOException IOException + * @see #payBack(HttpServletRequest) */ @Deprecated @RequestMapping(value = "payBackBefore.json") @@ -233,15 +236,15 @@ public class AliPayController { return service.getPayOutMessage("fail", "失败").toMessage(); } + /** * 支付回调地址 * * @param request 请求 - * * @return 返回对应的响应码 - * + *

* 业务处理在对应的PayMessageHandler里面处理,在哪里设置PayMessageHandler,详情查看{@link com.egzosn.pay.common.api.PayService#setPayMessageHandler(com.egzosn.pay.common.api.PayMessageHandler)} - * + *

* 如果未设置 {@link com.egzosn.pay.common.api.PayMessageHandler} 那么会使用默认的 {@link com.egzosn.pay.common.api.DefaultPayMessageHandler} * @throws IOException IOException */ @@ -290,6 +293,7 @@ public class AliPayController { public Map close(QueryOrder order) { return service.close(order.getTradeNo(), order.getOutTradeNo()); } + /** * 交易c撤销接口 * @@ -339,7 +343,6 @@ public class AliPayController { * 转账 * * @param order 转账订单 - * * @return 对应的转账结果 */ @RequestMapping("transfer") @@ -363,7 +366,6 @@ public class AliPayController { * * @param outNo 商户转账订单号 * @param tradeNo 支付平台转账订单号 - * * @return 对应的转账订单 */ @RequestMapping("transferQuery") 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 8c4f2fc..7f1b916 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 @@ -2,13 +2,37 @@ package com.egzosn.pay.demo.controller; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.math.BigDecimal; +import java.util.HashMap; +import java.util.Map; +import java.util.UUID; + +import javax.annotation.Resource; +import javax.imageio.ImageIO; +import javax.servlet.http.HttpServletRequest; + +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 static com.egzosn.pay.demo.dao.ApyAccountRepository.apyAccounts; + import com.alibaba.fastjson.JSONObject; import com.egzosn.pay.ali.api.AliPayService; import com.egzosn.pay.ali.bean.AliTransactionType; import com.egzosn.pay.common.api.PayConfigStorage; import com.egzosn.pay.common.api.PayMessageInterceptor; import com.egzosn.pay.common.api.PayService; -import com.egzosn.pay.common.bean.*; +import com.egzosn.pay.common.bean.MethodType; +import com.egzosn.pay.common.bean.PayMessage; +import com.egzosn.pay.common.bean.PayOrder; +import com.egzosn.pay.common.bean.PayOutMessage; +import com.egzosn.pay.common.bean.RefundOrder; +import com.egzosn.pay.common.bean.RefundResult; +import com.egzosn.pay.common.bean.TransferOrder; import com.egzosn.pay.common.http.UriVariables; import com.egzosn.pay.common.util.MatrixToImageWriter; import com.egzosn.pay.common.util.str.StringUtils; @@ -18,22 +42,6 @@ import com.egzosn.pay.demo.request.QueryOrder; import com.egzosn.pay.demo.service.ApyAccountService; import com.egzosn.pay.demo.service.PayResponse; 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; -import javax.servlet.http.HttpServletRequest; -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.math.BigDecimal; -import java.util.HashMap; -import java.util.Map; -import java.util.UUID; - -import static com.egzosn.pay.demo.dao.ApyAccountRepository.apyAccounts; /** * 发起支付入口 @@ -76,7 +84,7 @@ public class PayController { * 跳到支付页面 * 针对实时支付,即时付款 * - * @param request 请求 + * @param request 请求 * @param payId 账户id * @param transactionType 交易类型, 这个针对于每一个 支付类型的对应的几种交易方式 * @param bankType 针对刷卡支付,卡的类型,类型值 @@ -115,6 +123,7 @@ public class PayController { /** * 跳到支付页面 * 针对实时支付,即时付款 + * * @param request 请求 * @return 跳到支付页面 */ @@ -164,7 +173,7 @@ public class PayController { * * @param payId 支付账户id * @param transactionType 交易类型 - * @param price 金额 + * @param price 金额 * @return 支付预订单信息 */ @RequestMapping("app") @@ -188,7 +197,7 @@ public class PayController { * @return 支付结果 */ @RequestMapping(value = "microPay") - public Map microPay(Integer payId, String transactionType, BigDecimal price, String authCode) { + public Map microPay(Integer payId, String transactionType, BigDecimal price, String authCode) { //获取对应的支付账户操作工具(可根据账户id) PayResponse payResponse = service.getPayResponse(payId); @@ -200,7 +209,7 @@ public class PayController { PayConfigStorage storage = payResponse.getService().getPayConfigStorage(); //校验 if (payResponse.getService().verify(params)) { - PayMessage message = new PayMessage(params, storage.getPayType(), storage.getMsgType().name()); + PayMessage message = new PayMessage(params, storage.getPayType()); //支付校验通过后的处理 payResponse.getRouter().route(message); } @@ -215,7 +224,6 @@ public class PayController { * @param payId 账户id * @param transactionType 交易类型, 这个针对于每一个 支付类型的对应的几种交易方式 * @param price 金额 - * * @return 二维码图像 * @throws IOException IOException */ @@ -228,10 +236,12 @@ public class PayController { ImageIO.write(payResponse.getService().genQrPay(new PayOrder("订单title", "摘要", null == price ? BigDecimal.valueOf(0.01) : price, System.currentTimeMillis() + "", PayType.valueOf(payResponse.getStorage().getPayType()).getTransactionType(transactionType))), "JPEG", baos); return baos.toByteArray(); } + /** * 获取二维码地址 * 二维码支付 - * @param price 金额 + * + * @param price 金额 * @return 二维码图像 * @throws IOException IOException */ @@ -240,8 +250,9 @@ public class PayController { //获取对应的支付账户操作工具(可根据账户id) //获取对应的支付账户操作工具(可根据账户id) PayResponse payResponse = service.getPayResponse(payId); - return payResponse.getService().getQrPay( new PayOrder("订单title", "摘要", null == price ? BigDecimal.valueOf(0.01) : price, System.currentTimeMillis() + "", PayType.valueOf(payResponse.getStorage().getPayType()).getTransactionType(transactionType))); + return payResponse.getService().getQrPay(new PayOrder("订单title", "摘要", null == price ? BigDecimal.valueOf(0.01) : price, System.currentTimeMillis() + "", PayType.valueOf(payResponse.getStorage().getPayType()).getTransactionType(transactionType))); } + /** * 获取一码付二维码图像 * 二维码支付 @@ -249,7 +260,7 @@ public class PayController { * @param wxPayId 微信账户id * @param aliPayId 支付宝id * @param price 金额 - * @param request 请求 + * @param request 请求 * @return 二维码图像 * @throws IOException IOException */ @@ -279,7 +290,7 @@ public class PayController { * @param wxPayId 微信账户id * @param aliPayId 支付宝id * @param price 金额 - * @param request 请求 + * @param request 请求 * @return 支付宝与微信平台的判断 * @throws IOException IOException */ @@ -309,15 +320,13 @@ public class PayController { } - - /** * 支付回调地址 方式一 *

* 方式二,{@link #payBack(HttpServletRequest, Integer)} 是属于简化方式, 试用与简单的业务场景 * * @param request 请求 - * @param payId 账户id + * @param payId 账户id * @return 支付是否成功 * @throws IOException IOException */ @@ -361,14 +370,14 @@ public class PayController { * 方式二 * * @param request 请求 - * @param payId 账户id + * @param payId 账户id * @return 支付是否成功 * @throws IOException IOException - * 拦截器相关增加, 详情查看{@link com.egzosn.pay.common.api.PayService#addPayMessageInterceptor(PayMessageInterceptor)} - *

- * 业务处理在对应的PayMessageHandler里面处理,在哪里设置PayMessageHandler,详情查看{@link com.egzosn.pay.common.api.PayService#setPayMessageHandler(com.egzosn.pay.common.api.PayMessageHandler)} - *

- * 如果未设置 {@link com.egzosn.pay.common.api.PayMessageHandler} 那么会使用默认的 {@link com.egzosn.pay.common.api.DefaultPayMessageHandler} + * 拦截器相关增加, 详情查看{@link com.egzosn.pay.common.api.PayService#addPayMessageInterceptor(PayMessageInterceptor)} + *

+ * 业务处理在对应的PayMessageHandler里面处理,在哪里设置PayMessageHandler,详情查看{@link com.egzosn.pay.common.api.PayService#setPayMessageHandler(com.egzosn.pay.common.api.PayMessageHandler)} + *

+ * 如果未设置 {@link com.egzosn.pay.common.api.PayMessageHandler} 那么会使用默认的 {@link com.egzosn.pay.common.api.DefaultPayMessageHandler} */ @RequestMapping(value = "payBack{payId}.json") public String payBack(HttpServletRequest request, @PathVariable Integer payId) throws IOException { @@ -417,6 +426,7 @@ public class PayController { /** * 申请退款接口 + * * @param payId 账户id * @param order 订单的请求体 * @return 返回支付方申请退款后的结果 @@ -426,7 +436,8 @@ public class PayController { PayResponse payResponse = service.getPayResponse(payId); // return payResponse.getService().refund(order.getTradeNo(), order.getOutTradeNo(), order.getRefundAmount(), order.getTotalAmount()); - return payResponse.getService().refund(order); + final PayService service = payResponse.getService(); + return service.refund(order); } /** @@ -457,22 +468,9 @@ public class PayController { } - /** - * 通用查询接口,根据 TransactionType 类型进行实现,此接口不包括退款 - * - * @param order 订单的请求体 - * @return 返回支付方对应接口的结果 - */ - @RequestMapping("secondaryInterface") - public Map secondaryInterface(QueryOrder order) { - PayResponse payResponse = service.getPayResponse(order.getPayId()); - TransactionType type = PayType.valueOf(payResponse.getStorage().getPayType()).getTransactionType(order.getTransactionType()); - return payResponse.getService().secondaryInterface(order.getTradeNoOrBillDate(), order.getOutTradeNoBillType(), type); - } - - /** * 转账 + * * @param payId 账户id * @param order 转账订单 * @return 对应的转账结果 @@ -485,7 +483,8 @@ public class PayController { /** * 转账查询 - * @param payId 账户id + * + * @param payId 账户id * @param outNo 商户转账订单号 * @param tradeNo 支付平台转账订单号 * @return 对应的转账订单 diff --git a/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/PayPalPayController.java b/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/PayPalPayController.java index bd4e344..a9b6a08 100644 --- a/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/PayPalPayController.java +++ b/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/PayPalPayController.java @@ -1,6 +1,18 @@ package com.egzosn.pay.demo.controller; +import java.io.IOException; +import java.io.InputStream; +import java.math.BigDecimal; +import java.util.UUID; + +import javax.annotation.PostConstruct; +import javax.servlet.http.HttpServletRequest; + +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + import com.egzosn.pay.common.api.PayService; import com.egzosn.pay.common.bean.DefaultCurType; import com.egzosn.pay.common.bean.PayOrder; @@ -10,17 +22,6 @@ import com.egzosn.pay.common.http.HttpConfigStorage; import com.egzosn.pay.paypal.api.PayPalConfigStorage; import com.egzosn.pay.paypal.api.PayPalPayService; import com.egzosn.pay.paypal.bean.PayPalTransactionType; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RestController; - -import javax.annotation.PostConstruct; -import javax.servlet.http.HttpServletRequest; -import java.io.IOException; -import java.io.InputStream; -import java.math.BigDecimal; -import java.util.Map; -import java.util.UUID; /** * 发起支付入口 @@ -39,13 +40,13 @@ public class PayPalPayController { @PostConstruct public void init() { PayPalConfigStorage storage = new PayPalConfigStorage(); - storage.setClientID("AZ7HTcvrEAxYbzYx_iDZAi06GdqbjhqqQzFgPBFLxm2VUMzwlmiNUBk_y_5QNP4zWKblTuM6ZBAmxScd"); - storage.setClientSecret("EBMIjAag6NiRdXZxteTv0amEsmKN345xJv3bN7f_HRXSqcRJlW7PXhYXjI9sk5I4nKYOHgeqzhXCXKFo"); + storage.setClientID("AZDS0IhUZvJTO99unlvSDMfbZIP-p-UecYXZdJoweha9LFuqKXKcQIGZgfVaX6oGiAOJAUuJD7JwyTl1"); + storage.setClientSecret("EK2YaOrw3oLSDWIRzvb9BWGTjiPPhY1fFUu5ylhUsGYLc_h_dlpJ0hr_LDEkbO9MyKP2P83YcywbPaem"); storage.setTest(true); //发起付款后的页面转跳地址 storage.setReturnUrl("http://www.egzosn.com/payPal/payBack.json"); //取消按钮转跳地址,这里用异步通知地址的兼容的做法 - storage.setNotifyUrl("http://www.egzosn.com/pay/cancel"); + storage.setCancelUrl("http://www.egzosn.com/pay/cancel"); service = new PayPalPayService(storage); //请求连接池配置 @@ -76,11 +77,12 @@ public class PayPalPayController { String toPayHtml = service.toPay(order); //某些支付下单时无法设置单号,通过下单后返回对应单号,如 paypal,友店。 - String outTradeNo = order.getOutTradeNo(); - System.out.println("支付订单号:" + outTradeNo + " 这里可以进行回存"); + String tradeNo = order.getTradeNo(); + System.out.println("支付订单号:" + tradeNo + " 这里可以进行回存"); return toPayHtml; } + /** * 申请退款接口 * @@ -98,10 +100,13 @@ public class PayPalPayController { } + /* */ + /** * return url * PayPal确认付款调用的接口 * 用户确认付款后,paypal调用的这个方法执行付款 + * * @param request 请求 * @return 付款成功信息 * @throws IOException IOException @@ -118,17 +123,17 @@ public class PayPalPayController { return "failure"; } + /* */ + /** * 支付回调地址 * - * @param request 请求 - * + * @param request 请求 * @return 结果 * @throws IOException IOException - * 业务处理在对应的PayMessageHandler里面处理,在哪里设置PayMessageHandler,详情查看{@link com.egzosn.pay.common.api.PayService#setPayMessageHandler(com.egzosn.pay.common.api.PayMessageHandler)} - * - * 如果未设置 {@link com.egzosn.pay.common.api.PayMessageHandler} 那么会使用默认的 {@link com.egzosn.pay.common.api.DefaultPayMessageHandler} - * + * 业务处理在对应的PayMessageHandler里面处理,在哪里设置PayMessageHandler,详情查看{@link com.egzosn.pay.common.api.PayService#setPayMessageHandler(com.egzosn.pay.common.api.PayMessageHandler)} + *

+ * 如果未设置 {@link com.egzosn.pay.common.api.PayMessageHandler} 那么会使用默认的 {@link com.egzosn.pay.common.api.DefaultPayMessageHandler} */ @RequestMapping(value = "payBack.json") public String payBack(HttpServletRequest request) throws IOException { diff --git a/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/PayoneerPayController.java b/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/PayoneerPayController.java index 0fca69d..e6580b2 100644 --- a/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/PayoneerPayController.java +++ b/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/PayoneerPayController.java @@ -1,23 +1,30 @@ package com.egzosn.pay.demo.controller; -import com.egzosn.pay.common.bean.*; -import com.egzosn.pay.common.http.HttpConfigStorage; -import com.egzosn.pay.demo.request.QueryOrder; -import com.egzosn.pay.payoneer.api.PayoneerConfigStorage; -import com.egzosn.pay.payoneer.api.PayoneerPayService; -import com.egzosn.pay.payoneer.bean.PayoneerTransactionType; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.ResponseBody; -import org.springframework.web.bind.annotation.RestController; - -import javax.annotation.PostConstruct; -import javax.servlet.http.HttpServletRequest; import java.io.IOException; import java.math.BigDecimal; import java.util.LinkedHashMap; import java.util.Map; import java.util.UUID; +import javax.annotation.PostConstruct; +import javax.servlet.http.HttpServletRequest; + +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.ResponseBody; +import org.springframework.web.bind.annotation.RestController; + +import com.egzosn.pay.common.bean.DefaultCurType; +import com.egzosn.pay.common.bean.PayOrder; +import com.egzosn.pay.common.bean.RefundOrder; +import com.egzosn.pay.common.bean.RefundResult; +import com.egzosn.pay.common.bean.TransactionType; +import com.egzosn.pay.common.bean.TransferOrder; +import com.egzosn.pay.common.http.HttpConfigStorage; +import com.egzosn.pay.demo.request.QueryOrder; +import com.egzosn.pay.payoneer.api.PayoneerConfigStorage; +import com.egzosn.pay.payoneer.api.PayoneerPayService; +import com.egzosn.pay.payoneer.bean.PayoneerTransactionType; + /** * @author egan * email egzosn@gmail.com @@ -34,7 +41,6 @@ public class PayoneerPayController { public void init() { PayoneerConfigStorage configStorage = new PayoneerConfigStorage(); configStorage.setProgramId("商户id"); - configStorage.setMsgType(MsgType.json); configStorage.setInputCharset("utf-8"); configStorage.setUserName("PayoneerPay 用户名"); configStorage.setApiPassword("PayoneerPay API password"); @@ -63,11 +69,12 @@ public class PayoneerPayController { /** * 获取授权页面 + * * @param payeeId 用户id * @return 获取授权页面 */ @RequestMapping("getAuthorizationPage.json") - public Map getAuthorizationPage( String payeeId ){ + public Map getAuthorizationPage(String payeeId) { Map data = new LinkedHashMap<>(); data.put("code", 0); @@ -75,13 +82,14 @@ public class PayoneerPayController { return data; } - /** + /** * 获取授权用户信息,包含用户状态,注册时间,联系人信息,地址信息等等 + * * @param payeeId 用户id * @return 获取授权用户信息 */ @RequestMapping("getAuthorizationUser.json") - public Map getAuthorizationUser( String payeeId ){ + public Map getAuthorizationUser(String payeeId) { Map data = new LinkedHashMap<>(); data.put("code", 0); @@ -92,36 +100,36 @@ public class PayoneerPayController { /** * 主动收款 - * @param price 金额 - * @param userId 付款用户 + * + * @param price 金额 + * @param userId 付款用户 * @return 支付结果 */ @ResponseBody @RequestMapping(value = "microPay.json") - public Map microPay(BigDecimal price, String userId){ + public Map microPay(BigDecimal price, String userId) { PayOrder order = new PayOrder("Order_payment:", "Order payment", price, UUID.randomUUID().toString().replace("-", ""), PayoneerTransactionType.CHARGE); //币种 order.setCurType(DefaultCurType.USD); //设置授权码,条码等 - order.setAuthCode( userId); + order.setAuthCode(userId); //支付结果 Map params = service.microPay(order); - if (10700 == (Integer) params.get(PayoneerPayService.CODE)){ + if (10700 == (Integer) params.get(PayoneerPayService.CODE)) { System.out.println("未授权"); - }else if (0 == (Integer) params.get(PayoneerPayService.CODE)){ + } + else if (0 == (Integer) params.get(PayoneerPayService.CODE)) { System.out.println("收款成功"); } return params; } - /** * 用户授权回调地址 * * @param request 请求 - * * @return 是否成功 * @throws IOException IOException */ @@ -166,6 +174,7 @@ public class PayoneerPayController { public Map close(QueryOrder order) { return service.close(order.getTradeNo(), order.getOutTradeNo()); } + /** * 申请退款接口 * @@ -173,7 +182,7 @@ public class PayoneerPayController { * @return 返回支付方申请退款后的结果 */ @RequestMapping("refund") - public Map refund(RefundOrder order) { + public RefundResult refund(RefundOrder order) { return service.refund(order); } @@ -195,7 +204,6 @@ public class PayoneerPayController { * 转账 * * @param order 转账订单 - * * @return 对应的转账结果 */ @RequestMapping("transfer") @@ -213,7 +221,6 @@ public class PayoneerPayController { * * @param outNo 商户转账订单号 * @param tradeNo 支付平台转账订单号 - * * @return 对应的转账订单 */ @RequestMapping("transferQuery") 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 04aef65..58cf5e2 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 @@ -3,16 +3,16 @@ package com.egzosn.pay.demo.dao; -import com.egzosn.pay.common.bean.MsgType; +import java.util.HashMap; +import java.util.Map; + import com.egzosn.pay.common.util.sign.SignUtils; import com.egzosn.pay.demo.entity.ApyAccount; import com.egzosn.pay.demo.entity.PayType; -import java.util.HashMap; -import java.util.Map; - /** * 账户 + * * @author: egan * email egzosn@gmail.com * date 2016/11/18 1:21 @@ -21,12 +21,11 @@ import java.util.Map; public class ApyAccountRepository { // 这里简单模拟,引入orm等框架之后可自行删除 - public static Map apyAccounts = new HashMap<>(); + public static Map apyAccounts = new HashMap<>(); /** * 这里简单初始化,引入orm等框架之后可自行删除 - */ - { + */ { ApyAccount apyAccount1 = new ApyAccount(); apyAccount1.setPayId(1); apyAccount1.setPartner("2088102169916436"); @@ -41,7 +40,6 @@ public class ApyAccountRepository { apyAccount1.setSeller("2088102169916436"); apyAccount1.setSignType(SignUtils.RSA.name()); apyAccount1.setPayType(PayType.aliPay); - apyAccount1.setMsgType(MsgType.text); //设置测试环境 apyAccount1.setTest(true); apyAccounts.put(apyAccount1.getPayId(), apyAccount1); @@ -60,7 +58,6 @@ public class ApyAccountRepository { apyAccount2.setSeller("1469188802"); apyAccount2.setSignType(SignUtils.MD5.name()); apyAccount2.setPayType(PayType.wxPay); - apyAccount2.setMsgType(MsgType.xml); //设置测试环境 apyAccount2.setTest(false); apyAccounts.put(apyAccount2.getPayId(), apyAccount2); @@ -78,7 +75,6 @@ public class ApyAccountRepository { apyAccount3.setInputCharset("UTF-8"); apyAccount3.setSignType(SignUtils.MD5.name()); apyAccount3.setPayType(PayType.wxPay); - apyAccount3.setMsgType(MsgType.xml); apyAccounts.put(apyAccount3.getPayId(), apyAccount3); ApyAccount apyAccount4 = new ApyAccount(); @@ -95,7 +91,6 @@ public class ApyAccountRepository { apyAccount4.setInputCharset("UTF-8"); apyAccount4.setSignType(SignUtils.RSA2.name()); apyAccount4.setPayType(PayType.unionPay); - apyAccount4.setMsgType(MsgType.json); apyAccount4.setTest(true); apyAccounts.put(apyAccount4.getPayId(), apyAccount4); @@ -106,7 +101,6 @@ public class ApyAccountRepository { apyAccount5.setStorePassword("12BkDT8152Zj");//API password apyAccount5.setInputCharset("UTF-8"); apyAccount5.setPayType(PayType.payoneer); - apyAccount5.setMsgType(MsgType.json); apyAccount5.setTest(true); apyAccounts.put(apyAccount5.getPayId(), apyAccount5); @@ -116,7 +110,6 @@ public class ApyAccountRepository { apyAccount6.setPrivateKey("1EBMIjAag6NiRdXZxteTv0amEsmKN345xJv3bN7f_HRXSqcRJlW7PXhYXjI9sk5I4nKYOHgeqzhXCXKFo");//API password apyAccount6.setInputCharset("UTF-8"); apyAccount6.setPayType(PayType.payPal); - apyAccount6.setMsgType(MsgType.json); apyAccount6.setTest(true); apyAccounts.put(apyAccount6.getPayId(), apyAccount6); } @@ -125,10 +118,11 @@ public class ApyAccountRepository { /** * 根据id获取对应的账户信息 + * * @param payId 账户id * @return 账户信息 */ - public ApyAccount findByPayId(Integer payId){ + public ApyAccount findByPayId(Integer payId) { // TODO 2016/11/18 1:23 author: egan 这里简单模拟 具体实现 略。。 return apyAccounts.get(payId); } diff --git a/pay-java-demo/src/main/java/com/egzosn/pay/demo/entity/ApyAccount.java b/pay-java-demo/src/main/java/com/egzosn/pay/demo/entity/ApyAccount.java index 2ac2dd6..ccc202b 100644 --- a/pay-java-demo/src/main/java/com/egzosn/pay/demo/entity/ApyAccount.java +++ b/pay-java-demo/src/main/java/com/egzosn/pay/demo/entity/ApyAccount.java @@ -2,12 +2,11 @@ package com.egzosn.pay.demo.entity; -import com.egzosn.pay.common.bean.MsgType; - //import javax.persistence.*; /** * 支付账户 + * * @author: egan * email egzosn@gmail.com * date 2016/11/18 0:36 @@ -59,12 +58,9 @@ public class ApyAccount { // @Enumerated(EnumType.STRING) // @Column(name = "pay_type") private PayType payType; - // 消息类型,text,xml,json -// @Enumerated(EnumType.STRING) -// @Column(name = "msg_type") - private MsgType msgType; //是否为测试环境 private boolean isTest = false; + public Integer getPayId() { return payId; } @@ -145,14 +141,6 @@ public class ApyAccount { this.payType = payType; } - public MsgType getMsgType() { - return msgType; - } - - public void setMsgType(MsgType msgType) { - this.msgType = msgType; - } - public String getInputCharset() { return inputCharset; } @@ -199,7 +187,6 @@ public class ApyAccount { ", signType='" + signType + '\'' + ", inputCharset='" + inputCharset + '\'' + ", payType=" + payType + - ", msgType=" + msgType + '}'; } } 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 85b0b91..7437aa4 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 @@ -6,7 +6,6 @@ import com.egzosn.pay.ali.bean.AliTransactionType; import com.egzosn.pay.common.api.PayService; import com.egzosn.pay.common.bean.BasePayType; import com.egzosn.pay.common.bean.CertStoreType; -import com.egzosn.pay.common.bean.MsgType; import com.egzosn.pay.common.bean.TransactionType; import com.egzosn.pay.common.http.HttpConfigStorage; import com.egzosn.pay.demo.service.handler.WxPayMessageHandler; @@ -30,7 +29,6 @@ import com.egzosn.pay.wx.youdian.api.WxYouDianPayService; import com.egzosn.pay.wx.youdian.bean.YoudianTransactionType; - /** * 支付类型 * @@ -41,7 +39,7 @@ import com.egzosn.pay.wx.youdian.bean.YoudianTransactionType; public enum PayType implements BasePayType { - aliPay{ + aliPay { /** * @see com.egzosn.pay.ali.api.AliPayService * @param apyAccount @@ -61,7 +59,6 @@ public enum PayType implements BasePayType { configStorage.setSignType(apyAccount.getSignType()); configStorage.setSeller(apyAccount.getSeller()); configStorage.setPayType(apyAccount.getPayType().toString()); - configStorage.setMsgType(apyAccount.getMsgType()); configStorage.setInputCharset(apyAccount.getInputCharset()); configStorage.setTest(apyAccount.isTest()); //请求连接池配置 @@ -82,7 +79,7 @@ public enum PayType implements BasePayType { } - },wxPay { + }, wxPay { @Override public PayService getPayService(ApyAccount apyAccount) { WxPayConfigStorage wxPayConfigStorage = new WxPayConfigStorage(); @@ -95,7 +92,6 @@ public enum PayType implements BasePayType { wxPayConfigStorage.setReturnUrl(apyAccount.getReturnUrl()); wxPayConfigStorage.setSignType(apyAccount.getSignType()); wxPayConfigStorage.setPayType(apyAccount.getPayType().toString()); - wxPayConfigStorage.setMsgType(apyAccount.getMsgType()); wxPayConfigStorage.setInputCharset(apyAccount.getInputCharset()); wxPayConfigStorage.setTest(apyAccount.isTest()); @@ -124,7 +120,7 @@ public enum PayType implements BasePayType { return WxTransactionType.valueOf(transactionType); } - },youdianPay { + }, youdianPay { @Override public PayService getPayService(ApyAccount apyAccount) { // TODO 2017/1/23 14:12 author: egan 集群的话,友店可能会有bug。暂未测试集群环境 @@ -135,11 +131,10 @@ public enum PayType implements BasePayType { // wxPayConfigStorage.setReturnUrl(apyAccount.getReturnUrl()); wxPayConfigStorage.setSignType(apyAccount.getSignType()); wxPayConfigStorage.setPayType(apyAccount.getPayType().toString()); - wxPayConfigStorage.setMsgType(apyAccount.getMsgType()); wxPayConfigStorage.setSeller(apyAccount.getSeller()); wxPayConfigStorage.setInputCharset(apyAccount.getInputCharset()); wxPayConfigStorage.setTest(apyAccount.isTest()); - return new WxYouDianPayService(wxPayConfigStorage); + return new WxYouDianPayService(wxPayConfigStorage); } /** @@ -153,8 +148,7 @@ public enum PayType implements BasePayType { return YoudianTransactionType.valueOf(transactionType); } - },fuiou{ - + }, fuiou { @Override public PayService getPayService(ApyAccount apyAccount) { FuiouPayConfigStorage fuiouPayConfigStorage = new FuiouPayConfigStorage(); @@ -164,7 +158,6 @@ public enum PayType implements BasePayType { fuiouPayConfigStorage.setReturnUrl(apyAccount.getReturnUrl()); fuiouPayConfigStorage.setSignType(apyAccount.getSignType()); fuiouPayConfigStorage.setPayType(apyAccount.getPayType().toString()); - fuiouPayConfigStorage.setMsgType(apyAccount.getMsgType()); fuiouPayConfigStorage.setInputCharset(apyAccount.getInputCharset()); fuiouPayConfigStorage.setTest(apyAccount.isTest()); return new FuiouPayService(fuiouPayConfigStorage); @@ -176,8 +169,7 @@ public enum PayType implements BasePayType { } - },unionPay{ - + }, unionPay { @Override public PayService getPayService(ApyAccount apyAccount) { UnionPayConfigStorage unionPayConfigStorage = new UnionPayConfigStorage(); @@ -201,7 +193,6 @@ public enum PayType implements BasePayType { unionPayConfigStorage.setReturnUrl(apyAccount.getReturnUrl()); unionPayConfigStorage.setSignType(apyAccount.getSignType()); unionPayConfigStorage.setPayType(apyAccount.getPayType().toString()); - unionPayConfigStorage.setMsgType(apyAccount.getMsgType()); unionPayConfigStorage.setInputCharset(apyAccount.getInputCharset()); unionPayConfigStorage.setTest(apyAccount.isTest()); return new UnionPayService(unionPayConfigStorage); @@ -213,13 +204,12 @@ public enum PayType implements BasePayType { } - },payoneer{ + }, payoneer { @Override public PayService getPayService(ApyAccount apyAccount) { PayoneerConfigStorage configStorage = new PayoneerConfigStorage(); //设置商户Id configStorage.setProgramId(apyAccount.getPartner()); - configStorage.setMsgType(MsgType.json); configStorage.setInputCharset("utf-8"); //"PayoneerPay 用户名" configStorage.setUserName(apyAccount.getSeller()); @@ -243,7 +233,7 @@ public enum PayType implements BasePayType { } - },payPal{ + }, payPal { @Override public PayService getPayService(ApyAccount apyAccount) { PayPalConfigStorage storage = new PayPalConfigStorage(); 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 1ebfbaf..1be58b9 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 @@ -1,22 +1,28 @@ package com.egzosn.pay.demo.service; +import javax.annotation.Resource; + +import org.springframework.beans.factory.config.AutowireCapableBeanFactory; + import com.egzosn.pay.common.api.PayConfigStorage; import com.egzosn.pay.common.api.PayMessageHandler; import com.egzosn.pay.common.api.PayMessageRouter; import com.egzosn.pay.common.api.PayService; -import com.egzosn.pay.common.bean.MsgType; import com.egzosn.pay.common.http.HttpConfigStorage; import com.egzosn.pay.demo.entity.ApyAccount; import com.egzosn.pay.demo.entity.PayType; -import com.egzosn.pay.demo.service.handler.*; +import com.egzosn.pay.demo.service.handler.AliPayMessageHandler; +import com.egzosn.pay.demo.service.handler.FuiouPayMessageHandler; +import com.egzosn.pay.demo.service.handler.PayoneerMessageHandler; +import com.egzosn.pay.demo.service.handler.UnionPayMessageHandler; +import com.egzosn.pay.demo.service.handler.WxPayMessageHandler; +import com.egzosn.pay.demo.service.handler.YouDianPayMessageHandler; import com.egzosn.pay.demo.service.interceptor.AliPayMessageInterceptor; import com.egzosn.pay.demo.service.interceptor.YoudianPayMessageInterceptor; -import org.springframework.beans.factory.config.AutowireCapableBeanFactory; - -import javax.annotation.Resource; /** * 支付响应对象 + * * @author: egan * email egzosn@gmail.com * date 2016/11/18 0:34 @@ -38,6 +44,7 @@ public class PayResponse { /** * 初始化支付配置 + * * @param apyAccount 账户信息 * @see ApyAccount 对应表结构详情--》 /pay-java-demo/resources/apy_account.sql */ @@ -52,13 +59,14 @@ public class PayResponse { /** * 获取http配置,如果配置为null则为默认配置,无代理,无证书的请求方式。 - * 此处非必需 + * 此处非必需 + * * @param apyAccount 账户信息 * @return 请求配置 */ - public HttpConfigStorage getHttpConfigStorage(ApyAccount apyAccount){ + public HttpConfigStorage getHttpConfigStorage(ApyAccount apyAccount) { HttpConfigStorage httpConfigStorage = new HttpConfigStorage(); - /* 网路代理配置 根据需求进行设置*/ + /* 网路代理配置 根据需求进行设置*/ // //http代理地址 // httpConfigStorage.setHttpProxyHost("192.168.1.69"); // //代理端口 @@ -77,6 +85,7 @@ public class PayResponse { /** * 配置路由 + * * @param payId 指定账户id,用户多微信支付多支付宝支付 */ private void buildRouter(Integer payId) { @@ -84,7 +93,6 @@ public class PayResponse { router .rule() //消息类型 - .msgType(MsgType.text.name()) //支付账户事件类型 .payType(PayType.aliPay.name()) //拦截器 @@ -93,33 +101,27 @@ public class PayResponse { .handler(spring.getBean(AliPayMessageHandler.class)) .end() .rule() - .msgType(MsgType.xml.name()) .payType(PayType.wxPay.name()) .handler(autowire(new WxPayMessageHandler(payId))) .end() .rule() - .msgType(MsgType.json.name()) .payType(PayType.youdianPay.name()) .interceptor(new YoudianPayMessageInterceptor()) //拦截器 .handler(autowire(new YouDianPayMessageHandler(payId))) .end() .rule() - .msgType(MsgType.xml.name()) .payType(PayType.fuiou.name()) .handler(autowire(new FuiouPayMessageHandler(payId))) .end() .rule() - .msgType(MsgType.json.name()) .payType(PayType.unionPay.name()) .handler(autowire(new UnionPayMessageHandler(payId))) .end() .rule() - .msgType(MsgType.json.name()) .payType(PayType.payoneer.name()) .handler(autowire(new PayoneerMessageHandler(payId))) .end() .rule() - .msgType(MsgType.text.name()) .payType(PayType.payPal.name()) .handler(spring.getBean(AliPayMessageHandler.class)) .end() 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 2dd49a8..af8f37c 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 @@ -1,9 +1,10 @@ package com.egzosn.pay.fuiou.api; + import com.egzosn.pay.common.api.BasePayConfigStorage; /** * @author Actinia - *

+ * 
  * email hayesfu@qq.com
  * create 2017 2017/1/16 0016
  * 
@@ -15,7 +16,8 @@ public class FuiouPayConfigStorage extends BasePayConfigStorage { private String mchntCd; /** - * 应用id + * 应用id + * * @return 空 */ @Override @@ -23,21 +25,31 @@ public class FuiouPayConfigStorage extends BasePayConfigStorage { return null; } + /** + * 应用id + * 纠正名称 + * + * @return 应用id + */ + @Override + public String getAppId() { + return null; + } + /** * 合作商唯一标识 - * */ @Override - public String getPid () { + public String getPid() { return mchntCd; } - public String getMchntCd () { + public String getMchntCd() { return mchntCd; } - public void setMchntCd (String mchntCd) { + public void setMchntCd(String mchntCd) { this.mchntCd = mchntCd; } diff --git a/pay-java-fuiou/src/main/java/com/egzosn/pay/fuiou/api/FuiouPayService.java b/pay-java-fuiou/src/main/java/com/egzosn/pay/fuiou/api/FuiouPayService.java index 43f3348..418af4b 100644 --- a/pay-java-fuiou/src/main/java/com/egzosn/pay/fuiou/api/FuiouPayService.java +++ b/pay-java-fuiou/src/main/java/com/egzosn/pay/fuiou/api/FuiouPayService.java @@ -1,7 +1,21 @@ package com.egzosn.pay.fuiou.api; + +import java.io.InputStream; +import java.util.Collections; +import java.util.Date; +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.Map; + import com.alibaba.fastjson.JSONObject; import com.egzosn.pay.common.api.BasePayService; -import com.egzosn.pay.common.bean.*; +import com.egzosn.pay.common.bean.MethodType; +import com.egzosn.pay.common.bean.PayMessage; +import com.egzosn.pay.common.bean.PayOrder; +import com.egzosn.pay.common.bean.PayOutMessage; +import com.egzosn.pay.common.bean.RefundOrder; +import com.egzosn.pay.common.bean.RefundResult; +import com.egzosn.pay.common.bean.TransactionType; import com.egzosn.pay.common.exception.PayErrorException; import com.egzosn.pay.common.http.HttpConfigStorage; import com.egzosn.pay.common.http.UriVariables; @@ -12,15 +26,10 @@ import com.egzosn.pay.common.util.str.StringUtils; import com.egzosn.pay.fuiou.bean.FuiouRefundResult; import com.egzosn.pay.fuiou.bean.FuiouTransactionType; -import java.awt.image.BufferedImage; -import java.io.InputStream; -import java.math.BigDecimal; -import java.util.*; - /** * @author Actinia - *
- *email hayesfu@qq.com
+ * 
+ * email hayesfu@qq.com
  * create 2017 2017/1/16 0016
  * 
*/ @@ -38,7 +47,7 @@ public class FuiouPayService extends BasePayService { /** * B2C/B2B支付 */ - public static final String URL_FuiouSmpGate = "smpGate.do"; + public static final String URL_FuiouSmpGate = "smpGate.do"; /** * B2C/B2B支付(跨境支付) */ @@ -63,39 +72,46 @@ public class FuiouPayService extends BasePayService { /** * 获取对应的请求地址 + * * @return 请求地址 */ @Override - public String getReqUrl(TransactionType transactionType){ + public String getReqUrl(TransactionType transactionType) { return payConfigStorage.isTest() ? DEV_URL_FUIOU_BASE_DOMAIN : URL_FUIOU_BASE_DOMAIN; } + /** * 获取对应的请求地址 + * * @return 请求地址 */ - public String getReqUrl(){ + public String getReqUrl() { return getReqUrl(null); } /** * 构造函数,初始化时候使用 + * * @param payConfigStorage 支付账户配置信息 - * @param configStorage 网络代理配置 + * @param configStorage 网络代理配置 */ - public FuiouPayService (FuiouPayConfigStorage payConfigStorage, HttpConfigStorage configStorage) { + public FuiouPayService(FuiouPayConfigStorage payConfigStorage, HttpConfigStorage configStorage) { super(payConfigStorage, configStorage); } + /** * 构造函数,初始化时候使用 + * * @param payConfigStorage 支付账户配置信息 */ - public FuiouPayService (FuiouPayConfigStorage payConfigStorage) { + public FuiouPayService(FuiouPayConfigStorage payConfigStorage) { super(payConfigStorage); } /** * 回调校验 + * * @param params 回调回来的参数集 * @return 返回检验结果 0000 成功 其他失败 */ @@ -108,7 +124,8 @@ public class FuiouPayService extends BasePayService { try { //返回参数校验 和 重新请求订单检查数据是否合法 return (signVerify(params, (String) params.remove("md5")) && verifySource((String) params.get("order_id"))); - } catch (PayErrorException e) { + } + catch (PayErrorException e) { e.printStackTrace(); } return false; @@ -117,11 +134,10 @@ public class FuiouPayService extends BasePayService { /** * 回调签名校验 * - * @param params 参数集 - * @param responseSign 响应的签名串 + * @param params 参数集 + * @param responseSign 响应的签名串 * @return 校验结果 */ - @Override public boolean signVerify(Map params, String responseSign) { String sign = createSign(SignUtils.parameters2MD5Str(params, "|"), payConfigStorage.getInputCharset()); @@ -135,14 +151,13 @@ public class FuiouPayService extends BasePayService { * @param orderId 业务id, 数据的真实性. * @return 返回校验结果 */ - @Override public boolean verifySource(String orderId) { LinkedHashMap params = new LinkedHashMap(3); params.put("mchnt_cd", payConfigStorage.getPid()); params.put("order_id", orderId); params.put("md5", createSign(SignUtils.parameters2MD5Str(params, "|"), payConfigStorage.getInputCharset())); JSONObject resultJson = getHttpRequestTemplate().postForObject(getReqUrl() + URL_FuiouSmpAQueryGate + "?" + UriVariables.getMapToParameters(params), null, JSONObject.class); - if (null == resultJson){ + if (null == resultJson) { return false; } return "0000".equals(resultJson.getJSONObject("plain").getString("order_pay_code")); @@ -151,12 +166,13 @@ public class FuiouPayService extends BasePayService { /** * 将支付请求参数加密成md5 + * * @param order 支付订单 * @return 返回支付请求参数集合 */ @Override public Map orderInfo(PayOrder order) { - if (null == order.getTransactionType()){ + if (null == order.getTransactionType()) { order.setTransactionType(FuiouTransactionType.B2C); } @@ -168,6 +184,7 @@ public class FuiouPayService extends BasePayService { /** * 按序添加请求参数 + * * @param order 支付订单 * @return 返回支付请求参数集合 */ @@ -189,9 +206,10 @@ public class FuiouPayService extends BasePayService { //商户接受的支付结果后台通知地址 //非必填 parameters.put("back_notify_url", StringUtils.isBlank(payConfigStorage.getNotifyUrl()) ? "" : payConfigStorage.getNotifyUrl()); - if (null != order.getExpirationTime()){ + if (null != order.getExpirationTime()) { parameters.put("order_valid_time", DateUtils.minutesRemaining(order.getExpirationTime()) + "m"); - }else { + } + else { //超时时间 1m-15天,m:分钟、h:小时、d天、1c当天有效, parameters.put("order_valid_time", "30m"); } @@ -210,6 +228,7 @@ public class FuiouPayService extends BasePayService { /** * 对内容进行加密 + * * @param content 需要加密的内容 * @param characterEncoding 字符编码 * @return 加密后的字符串 @@ -221,45 +240,48 @@ public class FuiouPayService extends BasePayService { /** * 将请求参数或者请求流转化为 Map + * * @param parameterMap 请求参数 * @param is 请求流 * @return 返回参数集合 */ @Override public Map getParameter2Map(Map parameterMap, InputStream is) { - Map params = conversion(parameterMap, new LinkedHashMap(), "mchnt_cd"); - conversion(parameterMap, params, "order_id"); - conversion(parameterMap, params, "order_date"); - conversion(parameterMap, params, "order_amt"); - conversion(parameterMap, params, "order_st"); - conversion(parameterMap, params, "order_pay_code"); - conversion(parameterMap, params, "order_pay_error"); - conversion(parameterMap, params, "resv1"); - conversion(parameterMap, params, "fy_ssn"); - conversion(parameterMap, params, "md5"); + Map params = conversion(parameterMap, new LinkedHashMap(), "mchnt_cd"); + conversion(parameterMap, params, "order_id"); + conversion(parameterMap, params, "order_date"); + conversion(parameterMap, params, "order_amt"); + conversion(parameterMap, params, "order_st"); + conversion(parameterMap, params, "order_pay_code"); + conversion(parameterMap, params, "order_pay_error"); + conversion(parameterMap, params, "resv1"); + conversion(parameterMap, params, "fy_ssn"); + conversion(parameterMap, params, "md5"); return params; } /** - * 将parameterMap对应的key存放至params + * 将parameterMap对应的key存放至params + * * @param parameterMap 请求参数 - * @param params 转化的对象 - * @param key 需要取值的key + * @param params 转化的对象 + * @param key 需要取值的key * @return params */ - public Map conversion(Map parameterMap, Map params ,String key){ + public Map conversion(Map parameterMap, Map params, String key) { String[] values = parameterMap.get(key); String valueStr = ""; - for (int i = 0,len = values.length; i < len; i++) { - valueStr += (i == len - 1) ? values[i] : values[i] + ","; + for (int i = 0, len = values.length; i < len; i++) { + valueStr += (i == len - 1) ? values[i] : values[i] + ","; } params.put(key, valueStr); return params; } /** - * 获取输出消息,用户返回给支付端 - * @param code 返回代码 + * 获取输出消息,用户返回给支付端 + * + * @param code 返回代码 * @param message 返回信息 * @return 消息实体 */ @@ -268,9 +290,11 @@ public class FuiouPayService extends BasePayService { public PayOutMessage getPayOutMessage(String code, String message) { return PayOutMessage.TEXT().content(code.toLowerCase()).build(); } + /** * 获取成功输出消息,用户返回给支付端 * 主要用于拦截器中返回 + * * @param payMessage 支付回调消息 * @return 返回输出消息 */ @@ -281,6 +305,7 @@ public class FuiouPayService extends BasePayService { /** * 发送支付请求(form表单) + * * @param orderInfo 发起支付的订单信息 * @param method 请求方式 "post" "get", * @return form表单提交的html字符串 @@ -288,22 +313,24 @@ public class FuiouPayService extends BasePayService { @Override public String buildRequest(Map orderInfo, MethodType method) { - return getFormString(orderInfo, method,getReqUrl() + URL_FuiouSmpGate ); + return getFormString(orderInfo, method, getReqUrl() + URL_FuiouSmpGate); } /** * 获取输出二维码,用户返回给支付端, * 暂未实现或无此功能 + * * @param order 发起支付的订单信息 * @return 空 */ @Override - public String getQrPay (PayOrder order) { + public String getQrPay(PayOrder order) { throw new UnsupportedOperationException(); } /** * 暂未实现或无此功能 + * * @param order 发起支付的订单信息 * @return 不支持的操作异常 */ @@ -314,20 +341,21 @@ public class FuiouPayService extends BasePayService { /** * 根据参数形成form表单 - * @param param 参数 + * + * @param param 参数 * @param method 请求方式 get_post - * @param url 支付请求url地址 + * @param url 支付请求url地址 * @return form表单html代码 */ - private String getFormString(Map param, MethodType method,String url) { + private String getFormString(Map param, MethodType method, String url) { StringBuffer formHtml = new StringBuffer(); formHtml.append(""); - formHtml.append( "提交到富友交易系统"); - formHtml.append( ""); - formHtml.append( ""); - formHtml.append( "
"); + formHtml.append("提交到富友交易系统"); + formHtml.append(""); + formHtml.append(""); + formHtml.append(""); for (Map.Entry entry : param.entrySet()) { Object o = entry.getValue(); @@ -341,6 +369,7 @@ public class FuiouPayService extends BasePayService { /** * 交易查询接口 + * * @param tradeNo 支付平台订单号 * @param outTradeNo 商户单号 * @return 空 @@ -357,9 +386,9 @@ public class FuiouPayService extends BasePayService { } - /** * 交易关闭接口 + * * @param tradeNo 支付平台订单号 * @param outTradeNo 商户单号 * @return 空 @@ -370,13 +399,10 @@ public class FuiouPayService extends BasePayService { } - - - /** * 申请退款接口 * - * @param refundOrder 退款订单信息 + * @param refundOrder 退款订单信息 * @return 退款返回结果集 */ @Override @@ -413,6 +439,7 @@ public class FuiouPayService extends BasePayService { /** * 下载对账单 + * * @param billDate 账单时间:日账单格式为yyyy-MM-dd,月账单格式为yyyy-MM。 * @param billType 账单类型,商户通过接口或商户经开放平台授权后其所属服务商通过接口可以获取以下账单类型:trade、signcustomer;trade指商户基于支付宝交易收单的业务账单;signcustomer是指基于商户支付宝余额收入及支出等资金变动的帐务账单; * @return 空 @@ -422,19 +449,5 @@ public class FuiouPayService extends BasePayService { return Collections.emptyMap(); } - /** - * @param tradeNoOrBillDate 支付平台订单号或者账单类型, 具体请 - * 类型为{@link String }或者 {@link Date },类型须强制限制,类型不对应则抛出异常{@link PayErrorException} - * @param outTradeNoBillType 商户单号或者 账单类型 - * @param transactionType 交易类型 - * - * @return 返回支付方对应接口的结果 - */ - @Override - public Map secondaryInterface(Object tradeNoOrBillDate, String outTradeNoBillType, TransactionType transactionType) { - return Collections.emptyMap(); - } - - } diff --git a/pay-java-payoneer/src/main/java/com/egzosn/pay/payoneer/api/PayoneerConfigStorage.java b/pay-java-payoneer/src/main/java/com/egzosn/pay/payoneer/api/PayoneerConfigStorage.java index a598c74..c93ec95 100644 --- a/pay-java-payoneer/src/main/java/com/egzosn/pay/payoneer/api/PayoneerConfigStorage.java +++ b/pay-java-payoneer/src/main/java/com/egzosn/pay/payoneer/api/PayoneerConfigStorage.java @@ -1,10 +1,12 @@ package com.egzosn.pay.payoneer.api; + import com.egzosn.pay.common.api.BasePayConfigStorage; /** * Payoneer P卡 支付 配置 + * * @author Actinia - * @author egan + * @author egan *
  * email hayesfu@qq.com
  * date 2018-01-19
@@ -16,27 +18,38 @@ public class PayoneerConfigStorage extends BasePayConfigStorage {
      */
     private String programId;
     /**
-     *  PayoneerPay 用户名
+     * PayoneerPay 用户名
      */
     private String userName;
 
     /**
-     *  应用id
+     * 应用id
+     *
      * @return 空
      */
     @Override
+    @Deprecated
     public String getAppid() {
         return null;
     }
 
+    /**
+     * 应用id
+     * 纠正名称
+     *
+     * @return 应用id
+     */
+    @Override
+    public String getAppId() {
+        return null;
+    }
 
 
     /**
      * 合作商唯一标识
-     *
      */
     @Override
-    public String getPid () {
+    public String getPid() {
         return programId;
     }
 
@@ -46,7 +59,8 @@ public class PayoneerConfigStorage extends BasePayConfigStorage {
     }
 
     /**
-     *  获取商户Id
+     * 获取商户Id
+     *
      * @return 商户Id
      */
     public String getProgramId() {
@@ -54,7 +68,8 @@ public class PayoneerConfigStorage extends BasePayConfigStorage {
     }
 
     /**
-     *  设置商户Id
+     * 设置商户Id
+     *
      * @param programId 商户Id
      */
     public void setProgramId(String programId) {
@@ -62,10 +77,11 @@ public class PayoneerConfigStorage extends BasePayConfigStorage {
     }
 
     /**
-     *  PayoneerPay 用户名
+     * PayoneerPay 用户名
+     *
      * @param userName 用户名
      */
-    public void setUserName(String userName){
+    public void setUserName(String userName) {
         this.userName = userName;
     }
 
@@ -74,16 +90,18 @@ public class PayoneerConfigStorage extends BasePayConfigStorage {
     }
 
     /**
-     *  设置PayoneerPay API password
+     * 设置PayoneerPay API password
+     *
      * @param apiPassword api密钥
      */
-    public void setApiPassword(String apiPassword){
+    public void setApiPassword(String apiPassword) {
         setKeyPrivate(apiPassword);
     }
+
     /**
-     *  获取 PayoneerPay API password
+     * 获取 PayoneerPay API password
      */
-    public String getApiPassword(){
-       return getKeyPrivate();
+    public String getApiPassword() {
+        return getKeyPrivate();
     }
 }
diff --git a/pay-java-payoneer/src/main/java/com/egzosn/pay/payoneer/api/PayoneerPayService.java b/pay-java-payoneer/src/main/java/com/egzosn/pay/payoneer/api/PayoneerPayService.java
index 617d79a..2b4645c 100644
--- a/pay-java-payoneer/src/main/java/com/egzosn/pay/payoneer/api/PayoneerPayService.java
+++ b/pay-java-payoneer/src/main/java/com/egzosn/pay/payoneer/api/PayoneerPayService.java
@@ -1,9 +1,31 @@
 package com.egzosn.pay.payoneer.api;
 
+import java.math.BigDecimal;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.http.Header;
+import org.apache.http.entity.ContentType;
+import org.apache.http.message.BasicHeader;
+
 import com.alibaba.fastjson.JSON;
 import com.alibaba.fastjson.JSONObject;
 import com.egzosn.pay.common.api.BasePayService;
-import com.egzosn.pay.common.bean.*;
+import com.egzosn.pay.common.bean.BaseRefundResult;
+import com.egzosn.pay.common.bean.CurType;
+import com.egzosn.pay.common.bean.DefaultCurType;
+import com.egzosn.pay.common.bean.MethodType;
+import com.egzosn.pay.common.bean.PayMessage;
+import com.egzosn.pay.common.bean.PayOrder;
+import com.egzosn.pay.common.bean.PayOutMessage;
+import com.egzosn.pay.common.bean.RefundOrder;
+import com.egzosn.pay.common.bean.RefundResult;
+import com.egzosn.pay.common.bean.TransactionType;
+import com.egzosn.pay.common.bean.TransferOrder;
 import com.egzosn.pay.common.bean.outbuilder.PayTextOutMessage;
 import com.egzosn.pay.common.bean.result.PayException;
 import com.egzosn.pay.common.exception.PayErrorException;
@@ -14,19 +36,13 @@ import com.egzosn.pay.common.http.UriVariables;
 import com.egzosn.pay.common.util.DateUtils;
 import com.egzosn.pay.common.util.Util;
 import com.egzosn.pay.payoneer.bean.PayoneerTransactionType;
-import org.apache.http.Header;
-import org.apache.http.entity.ContentType;
-import org.apache.http.message.BasicHeader;
-
-import java.math.BigDecimal;
-import java.util.*;
 
 /**
  * payoneer业务逻辑
  *
  * @author Actinia
  * @author egan
- *         
+ * 
  *         email: egzosn@gmail.com
  *         email: hayesfu@qq.com
  *         create 2018-01-19
@@ -61,20 +77,21 @@ public class PayoneerPayService extends BasePayService im
 
     /**
      * 获取授权请求头
+     *
      * @return 授权请求头
      */
-    private HttpHeader authHeader(){
+    private HttpHeader authHeader() {
 
         List
headers = new ArrayList<>(); - headers.add(new BasicHeader("Authorization", "Basic " + authorizationString(getPayConfigStorage().getSeller(), getPayConfigStorage().getKeyPrivate()))); + headers.add(new BasicHeader("Authorization", "Basic " + authorizationString(getPayConfigStorage().getSeller(), getPayConfigStorage().getKeyPrivate()))); return new HttpHeader(headers); } + /** * 获取授权页面 * * @param payeeId 收款id - * * @return 返回请求结果 */ @Override @@ -97,7 +114,6 @@ public class PayoneerPayService extends BasePayService im * 授权状态 * * @param payeeId 用户id - * * @return 返回是否认证 true 已认证 */ @Override @@ -110,7 +126,6 @@ public class PayoneerPayService extends BasePayService im * 获取授权用户信息 * * @param payeeId 用户id - * * @return 获取授权用户信息,包含用户状态,注册时间,联系人信息,地址信息等等 */ @Override @@ -123,7 +138,6 @@ public class PayoneerPayService extends BasePayService im * 回调校验 * * @param params 回调回来的参数集 - * * @return 签名校验 true通过 */ @Override @@ -137,28 +151,14 @@ public class PayoneerPayService extends BasePayService im return false; } - /** - * 签名校验 - * - * @param params 参数集 - * @param sign 签名原文 - * - * @return 签名校验 true通过 - */ - @Override - public boolean signVerify(Map params, String sign) { - return true; - } /** * 支付宝需要,微信是否也需要再次校验来源,进行订单查询 * 校验数据来源 * * @param outTradeNo 业务id, 数据的真实性. - * * @return true通过 */ - @Override public boolean verifySource(String outTradeNo) { Map data = query(null, outTradeNo); return "DONE".equals(data.get("status")); @@ -168,7 +168,6 @@ public class PayoneerPayService extends BasePayService im * 返回创建的订单信息 * * @param order 支付订单 - * * @return 订单信息 * @see PayOrder 支付订单信息 */ @@ -192,7 +191,6 @@ public class PayoneerPayService extends BasePayService im * * @param content 需要签名的内容 * @param characterEncoding 字符编码 - * * @return 签名 */ @Override @@ -206,7 +204,6 @@ public class PayoneerPayService extends BasePayService im * * @param code 状态 * @param message 消息 - * * @return 返回输出消息 */ @Override @@ -219,7 +216,6 @@ public class PayoneerPayService extends BasePayService im * 主要用于拦截器中返回 * * @param payMessage 支付回调消息 - * * @return 返回输出消息 */ @Override @@ -232,7 +228,6 @@ public class PayoneerPayService extends BasePayService im * * @param orderInfo 发起支付的订单信息 * @param method 请求方式 "post" "get", - * * @return 获取输出消息,用户返回给支付端, 针对于web端 * @see MethodType 请求类型 */ @@ -245,7 +240,6 @@ public class PayoneerPayService extends BasePayService im * 获取输出二维码,用户返回给支付端, * * @param order 发起支付的订单信息 - * * @return 返回图片信息,支付时需要的 */ @Override @@ -257,7 +251,6 @@ public class PayoneerPayService extends BasePayService im * 刷卡付,pos主动扫码付款(条码付) * * @param order 发起支付的订单信息 - * * @return 返回支付结果 */ @Override @@ -271,7 +264,7 @@ public class PayoneerPayService extends BasePayService im if (response != null) { return response; } - throw new PayErrorException(new PayException("fail", "Payoneer申请收款失败,原因:未有返回值" )); + throw new PayErrorException(new PayException("fail", "Payoneer申请收款失败,原因:未有返回值")); } /** @@ -279,7 +272,6 @@ public class PayoneerPayService extends BasePayService im * * @param tradeNo 支付平台订单号 * @param outTradeNo 商户单号 - * * @return 返回查询回来的结果集,支付方原值返回 */ @Override @@ -293,7 +285,6 @@ public class PayoneerPayService extends BasePayService im * * @param tradeNo 支付平台订单号 * @param outTradeNo 商户单号 - * * @return 返回支付方交易关闭后的结果 */ @Override @@ -314,13 +305,10 @@ public class PayoneerPayService extends BasePayService im } - - /** * 申请退款接口 * * @param refundOrder 退款订单信息 - * * @return 返回支付方申请退款后的结果 */ @Override @@ -390,7 +378,6 @@ public class PayoneerPayService extends BasePayService im * * @param billDate 账单时间:日账单格式为yyyy-MM-dd,月账单格式为yyyy-MM。 * @param billType 账单类型,商户通过接口或商户经开放平台授权后其所属服务商通过接口可以获取以下账单类型:trade、signcustomer;trade指商户基于支付宝交易收单的业务账单;signcustomer是指基于商户支付宝余额收入及支出等资金变动的帐务账单; - * * @return 返回支付方下载对账单的结果 */ @Override @@ -404,18 +391,17 @@ public class PayoneerPayService extends BasePayService im * 类型为{@link String }或者 {@link Date },类型须强制限制,类型不对应则抛出异常{@link PayErrorException} * @param outTradeNoBillType 商户单号或者 账单类型 * @param transactionType 交易类型 - * * @return 返回支付方对应接口的结果 */ - @Override public Map secondaryInterface(Object tradeNoOrBillDate, String outTradeNoBillType, TransactionType transactionType) { MethodType methodType = null; if (transactionType == PayoneerTransactionType.CHARGE_CANCEL) { // 退款 methodType = MethodType.POST; - }else { + } + else { methodType = MethodType.GET; } - JSONObject result = getHttpRequestTemplate().doExecute(UriVariables.getUri(getReqUrl(transactionType), outTradeNoBillType), authHeader() ,JSONObject.class, methodType); + JSONObject result = getHttpRequestTemplate().doExecute(UriVariables.getUri(getReqUrl(transactionType), outTradeNoBillType), authHeader(), JSONObject.class, methodType); return result; } @@ -423,7 +409,6 @@ public class PayoneerPayService extends BasePayService im * 转账 * * @param order 转账订单 - * * @return 对应的转账结果 */ @Override @@ -449,7 +434,6 @@ public class PayoneerPayService extends BasePayService im * * @param outNo 商户转账订单号 * @param tradeNo 支付平台转账订单号 - * * @return 对应的转账订单 */ @Override diff --git a/pay-java-paypal/src/main/java/com/egzosn/pay/paypal/api/PayPalConfigStorage.java b/pay-java-paypal/src/main/java/com/egzosn/pay/paypal/api/PayPalConfigStorage.java index 3b29d41..7c267dc 100644 --- a/pay-java-paypal/src/main/java/com/egzosn/pay/paypal/api/PayPalConfigStorage.java +++ b/pay-java-paypal/src/main/java/com/egzosn/pay/paypal/api/PayPalConfigStorage.java @@ -8,37 +8,58 @@ import com.egzosn.pay.common.api.BasePayConfigStorage; * 贝宝支付配置存储 * * @author egan - *

- * email egzosn@gmail.com - * date 2018-4-8 22:11:42 + *

+ * email egzosn@gmail.com + * date 2018-4-8 22:11:42 */ public class PayPalConfigStorage extends BasePayConfigStorage { - private String clientID; + private String clientId; @Override + @Deprecated public String getAppid() { - return clientID; + return clientId; + } + + /** + * 应用id + * 纠正名称 + * + * @return 应用id + */ + @Override + public String getAppId() { + return clientId; } @Override public String getPid() { - return clientID; + return clientId; } @Override public String getSeller() { - return clientID; + return clientId; } public String getClientID() { - return clientID; + return clientId; } - public void setClientID(String clientID) { - this.clientID = clientID; + public void setClientID(String clientId) { + this.clientId = clientId; } + public String getClientId() { + return clientId; + } + + public void setClientId(String clientId) { + this.clientId = clientId; + } + + public String getClientSecret() { return getKeyPrivate(); } @@ -50,6 +71,10 @@ public class PayPalConfigStorage extends BasePayConfigStorage { /** * 设置取消页面的url + *

+     * 注意:这里不是异步回调的通知
+     * IPN 地址设置的路径:https://developer.paypal.com/developer/ipnSimulator/
+     * 
* * @param cancelUrl 取消页面的url */ @@ -59,6 +84,11 @@ public class PayPalConfigStorage extends BasePayConfigStorage { /** * 获取取消页面的url + *
+     * 注意:这里不是异步回调的通知
+     * IPN 地址设置的路径:https://developer.paypal.com/developer/ipnSimulator/
+     * 
+ * * @return 取消页面的url */ public String getCancelUrl() { diff --git a/pay-java-paypal/src/main/java/com/egzosn/pay/paypal/api/PayPalPayService.java b/pay-java-paypal/src/main/java/com/egzosn/pay/paypal/api/PayPalPayService.java index b1447ed..e80c8a0 100644 --- a/pay-java-paypal/src/main/java/com/egzosn/pay/paypal/api/PayPalPayService.java +++ b/pay-java-paypal/src/main/java/com/egzosn/pay/paypal/api/PayPalPayService.java @@ -1,10 +1,34 @@ package com.egzosn.pay.paypal.api; +import java.io.UnsupportedEncodingException; +import java.math.BigDecimal; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Date; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.UUID; +import java.util.concurrent.locks.Lock; + +import org.apache.http.Header; +import org.apache.http.entity.ContentType; +import org.apache.http.message.BasicHeader; + import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONObject; import com.egzosn.pay.common.api.BasePayService; -import com.egzosn.pay.common.bean.*; +import com.egzosn.pay.common.bean.BaseRefundResult; +import com.egzosn.pay.common.bean.CurType; +import com.egzosn.pay.common.bean.DefaultCurType; +import com.egzosn.pay.common.bean.MethodType; +import com.egzosn.pay.common.bean.PayMessage; +import com.egzosn.pay.common.bean.PayOrder; +import com.egzosn.pay.common.bean.PayOutMessage; +import com.egzosn.pay.common.bean.RefundOrder; +import com.egzosn.pay.common.bean.RefundResult; +import com.egzosn.pay.common.bean.TransactionType; import com.egzosn.pay.common.bean.result.PayException; import com.egzosn.pay.common.exception.PayErrorException; import com.egzosn.pay.common.http.HttpHeader; @@ -12,24 +36,22 @@ import com.egzosn.pay.common.http.HttpStringEntity; import com.egzosn.pay.common.util.Util; import com.egzosn.pay.common.util.str.StringUtils; import com.egzosn.pay.paypal.bean.PayPalTransactionType; -import com.egzosn.pay.paypal.bean.order.*; -import org.apache.http.Header; -import org.apache.http.entity.ContentType; -import org.apache.http.message.BasicHeader; - -import java.io.UnsupportedEncodingException; -import java.math.BigDecimal; -import java.util.*; -import java.util.concurrent.locks.Lock; +import com.egzosn.pay.paypal.bean.order.Amount; +import com.egzosn.pay.paypal.bean.order.Links; +import com.egzosn.pay.paypal.bean.order.Payer; +import com.egzosn.pay.paypal.bean.order.Payment; +import com.egzosn.pay.paypal.bean.order.RedirectUrls; +import com.egzosn.pay.paypal.bean.order.Transaction; /** * 贝宝支付配置存储 - * @author egan * + * @author egan + *

* email egzosn@gmail.com * date 2018-4-8 ‏‎22:15:09 */ -public class PayPalPayService extends BasePayService{ +public class PayPalPayService extends BasePayService { /** * 沙箱环境 @@ -42,10 +64,11 @@ public class PayPalPayService extends BasePayService{ /** * 获取对应的请求地址 + * * @return 请求地址 */ @Override - public String getReqUrl(TransactionType transactionType){ + public String getReqUrl(TransactionType transactionType) { return (payConfigStorage.isTest() ? SANDBOX_REQ_URL : REQ_URL) + transactionType.getMethod(); } @@ -55,21 +78,23 @@ public class PayPalPayService extends BasePayService{ } - /** * 获取请求token + * * @return 授权令牌 */ - public String getAccessToken() { + public String getAccessToken() { try { return getAccessToken(false); - } catch (PayErrorException e) { + } + catch (PayErrorException e) { throw e; } } /** - * 获取授权令牌 + * 获取授权令牌 + * * @param forceRefresh 是否重新获取, true重新获取 * @return 新的授权令牌 * @throws PayErrorException 支付异常 @@ -84,21 +109,23 @@ public class PayPalPayService extends BasePayService{ } if (payConfigStorage.isAccessTokenExpired()) { - Map header = new HashMap<>(); - header.put("Authorization", "Basic " + authorizationString(getPayConfigStorage().getAppid(), getPayConfigStorage().getKeyPrivate())); - header.put("Accept", "application/json"); - header.put("Content-Type", "application/x-www-form-urlencoded"); - try { - HttpStringEntity entity = new HttpStringEntity("grant_type=client_credentials", header); - JSONObject resp = getHttpRequestTemplate().postForObject(getReqUrl(PayPalTransactionType.AUTHORIZE), entity, JSONObject.class); - payConfigStorage.updateAccessToken(String.format("%s %s", resp.getString("token_type" ), resp.getString("access_token" )), resp.getIntValue("expires_in" )); - - } catch (UnsupportedEncodingException e) { - throw new PayErrorException(new PayException("failure", e.getMessage())); - } + Map header = new HashMap<>(); + header.put("Authorization", "Basic " + authorizationString(getPayConfigStorage().getAppid(), getPayConfigStorage().getKeyPrivate())); + header.put("Accept", "application/json"); + header.put("Content-Type", "application/x-www-form-urlencoded"); + try { + HttpStringEntity entity = new HttpStringEntity("grant_type=client_credentials", header); + JSONObject resp = getHttpRequestTemplate().postForObject(getReqUrl(PayPalTransactionType.AUTHORIZE), entity, JSONObject.class); + payConfigStorage.updateAccessToken(String.format("%s %s", resp.getString("token_type"), resp.getString("access_token")), resp.getIntValue("expires_in")); + + } + catch (UnsupportedEncodingException e) { + throw new PayErrorException(new PayException("failure", e.getMessage())); + } return payConfigStorage.getAccessToken(); } - } finally { + } + finally { lock.unlock(); } return payConfigStorage.getAccessToken(); @@ -108,28 +135,19 @@ public class PayPalPayService extends BasePayService{ @Override public boolean verify(Map params) { - HttpStringEntity httpEntity = new HttpStringEntity("{\"payer_id\":\""+(String)params.get("PayerID")+"\"}", ContentType.APPLICATION_JSON); + HttpStringEntity httpEntity = new HttpStringEntity("{\"payer_id\":\"" + (String) params.get("PayerID") + "\"}", ContentType.APPLICATION_JSON); httpEntity.setHeaders(authHeader()); JSONObject resp = getHttpRequestTemplate().postForObject(getReqUrl(PayPalTransactionType.EXECUTE), httpEntity, JSONObject.class, (String) params.get("paymentId")); - return "approved".equals(resp.getString("state")); - - } + return "approved".equals(resp.getString("state")); - @Override - public boolean signVerify(Map params, String sign) { - return true; - } - - @Override - public boolean verifySource(String id) { - return true; } /** * 获取授权请求头 + * * @return 授权请求头 */ - private HttpHeader authHeader(){ + private HttpHeader authHeader() { List

headers = new ArrayList<>(); headers.add(new BasicHeader("Authorization", getAccessToken())); @@ -137,6 +155,7 @@ public class PayPalPayService extends BasePayService{ return new HttpHeader(headers); } + /** * 返回创建的订单信息 * @@ -146,21 +165,22 @@ public class PayPalPayService extends BasePayService{ */ @Override public Map orderInfo(PayOrder order) { - if (null == order.getTransactionType()){ + if (null == order.getTransactionType()) { order.setTransactionType(PayPalTransactionType.sale); } Amount amount = new Amount(); - if (null == order.getCurType()){ + if (null == order.getCurType()) { order.setCurType(DefaultCurType.USD); } amount.setCurrency(order.getCurType().getType()); amount.setTotal(Util.conversionAmount(order.getPrice()).toString()); Transaction transaction = new Transaction(); - if (!StringUtils.isEmpty(order.getSubject())){ + if (!StringUtils.isEmpty(order.getSubject())) { transaction.setDescription(order.getSubject()); - }else { + } + else { transaction.setDescription(order.getBody()); } transaction.setAmount(amount); @@ -181,11 +201,11 @@ public class PayPalPayService extends BasePayService{ //发起付款后的页面转跳地址 redirectUrls.setReturnUrl(payConfigStorage.getReturnUrl()); payment.setRedirectUrls(redirectUrls); - HttpStringEntity entity = new HttpStringEntity(JSON.toJSONString(payment), ContentType.APPLICATION_JSON); + HttpStringEntity entity = new HttpStringEntity(JSON.toJSONString(payment), ContentType.APPLICATION_JSON); entity.setHeaders(authHeader()); JSONObject resp = getHttpRequestTemplate().postForObject(getReqUrl(order.getTransactionType()), entity, JSONObject.class); - if ("created".equals(resp.getString("state")) && StringUtils.isNotEmpty(resp.getString("id"))){ - order.setOutTradeNo(resp.getString("id")); + if ("created".equals(resp.getString("state")) && StringUtils.isNotEmpty(resp.getString("id"))) { + order.setTradeNo(resp.getString("id")); } return preOrderHandler(resp, order); } @@ -202,15 +222,15 @@ public class PayPalPayService extends BasePayService{ @Override public String buildRequest(Map orderInfo, MethodType method) { - if (orderInfo instanceof JSONObject){ + if (orderInfo instanceof JSONObject) { Payment payment = ((JSONObject) orderInfo).toJavaObject(Payment.class); - for(Links links : payment.getLinks()){ - if(links.getRel().equals("approval_url")){ - return String.format("",links.getHref() ); + for (Links links : payment.getLinks()) { + if (links.getRel().equals("approval_url")) { + return String.format("", links.getHref()); } } } - return "" ; + return ""; } @Override @@ -222,6 +242,7 @@ public class PayPalPayService extends BasePayService{ public Map microPay(PayOrder order) { return null; } + /** * 交易查询接口 * @@ -241,20 +262,19 @@ public class PayPalPayService extends BasePayService{ } - /** * 申请退款接口 * - * @param refundOrder 退款订单信息 + * @param refundOrder 退款订单信息 * @return 返回支付方申请退款后的结果 */ @Override public RefundResult refund(RefundOrder refundOrder) { - JSONObject request = new JSONObject(); + JSONObject request = new JSONObject(); - if (null != refundOrder.getRefundAmount() && BigDecimal.ZERO.compareTo( refundOrder.getRefundAmount()) == -1){ + if (null != refundOrder.getRefundAmount() && BigDecimal.ZERO.compareTo(refundOrder.getRefundAmount()) == -1) { Amount amount = new Amount(); - if(null == refundOrder.getCurType()){ + if (null == refundOrder.getCurType()) { refundOrder.setCurType(DefaultCurType.USD); } @@ -266,7 +286,7 @@ public class PayPalPayService extends BasePayService{ HttpStringEntity httpEntity = new HttpStringEntity(request.toJSONString(), ContentType.APPLICATION_JSON); httpEntity.setHeaders(authHeader()); - JSONObject resp = getHttpRequestTemplate().postForObject(getReqUrl(PayPalTransactionType.REFUND), httpEntity, JSONObject.class, refundOrder.getTradeNo()); + JSONObject resp = getHttpRequestTemplate().postForObject(getReqUrl(PayPalTransactionType.REFUND), httpEntity, JSONObject.class, refundOrder.getTradeNo()); return new BaseRefundResult(resp) { @Override public String getCode() { @@ -332,10 +352,5 @@ public class PayPalPayService extends BasePayService{ return Collections.emptyMap(); } - @Override - public Map secondaryInterface(Object tradeNoOrBillDate, String outTradeNoBillType, TransactionType transactionType) { - return Collections.emptyMap(); - } - } diff --git a/pay-java-union/src/main/java/com/egzosn/pay/union/api/UnionPayConfigStorage.java b/pay-java-union/src/main/java/com/egzosn/pay/union/api/UnionPayConfigStorage.java index 5497b07..5c2f2e6 100644 --- a/pay-java-union/src/main/java/com/egzosn/pay/union/api/UnionPayConfigStorage.java +++ b/pay-java-union/src/main/java/com/egzosn/pay/union/api/UnionPayConfigStorage.java @@ -1,16 +1,16 @@ package com.egzosn.pay.union.api; -import com.egzosn.pay.common.api.BasePayConfigStorage; -import com.egzosn.pay.common.bean.CertStoreType; - import java.io.IOException; import java.io.InputStream; +import com.egzosn.pay.common.api.BasePayConfigStorage; +import com.egzosn.pay.common.bean.CertStoreType; + /** * @author Actinia - *

- *

+ * 

+ *

  *         email hayesfu@qq.com
  *           create 2017 2017/11/4 0004
  *         
@@ -64,17 +64,18 @@ public class UnionPayConfigStorage extends BasePayConfigStorage { * 设置私钥证书 * * @param certificate 私钥证书地址 或者证书内容字符串 - * 私钥证书密码 {@link #setKeyPrivateCertPwd(String)} + * 私钥证书密码 {@link #setKeyPrivateCertPwd(String)} */ public void setKeyPrivateCert(String certificate) { super.setKeyPrivate(certificate); this.keyPrivateCert = certificate; } + /** * 设置私钥证书 * * @param keyPrivateCert 私钥证书信息流 - * 私钥证书密码 {@link #setKeyPrivateCertPwd(String)} + * 私钥证书密码 {@link #setKeyPrivateCertPwd(String)} */ public void setKeyPrivateCert(InputStream keyPrivateCert) { this.keyPrivateCert = keyPrivateCert; @@ -92,6 +93,7 @@ public class UnionPayConfigStorage extends BasePayConfigStorage { public void setAcpMiddleCert(String acpMiddleCert) { this.acpMiddleCert = acpMiddleCert; } + /** * 设置中级证书 * @@ -109,6 +111,7 @@ public class UnionPayConfigStorage extends BasePayConfigStorage { public void setAcpRootCert(String acpRootCert) { this.acpRootCert = acpRootCert; } + /** * 设置根证书 * @@ -125,6 +128,7 @@ public class UnionPayConfigStorage extends BasePayConfigStorage { public String getAcpRootCert() { return (String) acpRootCert; } + public InputStream getAcpMiddleCertInputStream() throws IOException { return certStoreType.getInputStream(acpMiddleCert); } @@ -135,6 +139,7 @@ public class UnionPayConfigStorage extends BasePayConfigStorage { /** * 获取私钥证书密码 + * * @return 私钥证书密码 */ public String getKeyPrivateCertPwd() { @@ -144,11 +149,23 @@ public class UnionPayConfigStorage extends BasePayConfigStorage { public void setKeyPrivateCertPwd(String keyPrivateCertPwd) { this.keyPrivateCertPwd = keyPrivateCertPwd; } + @Override public String getAppid() { return null; } + /** + * 应用id + * 纠正名称 + * + * @return 应用id + */ + @Override + public String getAppId() { + return null; + } + /** * @return 合作者id * @see #getPid() @@ -214,6 +231,7 @@ public class UnionPayConfigStorage extends BasePayConfigStorage { /** * 证书存储类型 + * * @return 证书存储类型 */ public CertStoreType getCertStoreType() { diff --git a/pay-java-union/src/main/java/com/egzosn/pay/union/api/UnionPayService.java b/pay-java-union/src/main/java/com/egzosn/pay/union/api/UnionPayService.java index 6e576fb..5a68c8e 100644 --- a/pay-java-union/src/main/java/com/egzosn/pay/union/api/UnionPayService.java +++ b/pay-java-union/src/main/java/com/egzosn/pay/union/api/UnionPayService.java @@ -1,8 +1,37 @@ package com.egzosn.pay.union.api; +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.math.BigDecimal; +import java.security.GeneralSecurityException; +import java.security.cert.CertPathBuilder; +import java.security.cert.CertStore; +import java.security.cert.CertificateExpiredException; +import java.security.cert.CertificateFactory; +import java.security.cert.CollectionCertStoreParameters; +import java.security.cert.PKIXBuilderParameters; +import java.security.cert.TrustAnchor; +import java.security.cert.X509CertSelector; +import java.security.cert.X509Certificate; +import java.sql.Timestamp; +import java.text.DateFormat; +import java.text.SimpleDateFormat; +import java.util.Collections; +import java.util.Date; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; +import java.util.TreeMap; + import com.alibaba.fastjson.JSONObject; import com.egzosn.pay.common.api.BasePayService; -import com.egzosn.pay.common.bean.*; +import com.egzosn.pay.common.bean.MethodType; +import com.egzosn.pay.common.bean.PayMessage; +import com.egzosn.pay.common.bean.PayOrder; +import com.egzosn.pay.common.bean.PayOutMessage; +import com.egzosn.pay.common.bean.RefundOrder; +import com.egzosn.pay.common.bean.TransactionType; import com.egzosn.pay.common.bean.outbuilder.PayTextOutMessage; import com.egzosn.pay.common.bean.result.PayException; import com.egzosn.pay.common.exception.PayErrorException; @@ -20,19 +49,6 @@ import com.egzosn.pay.union.bean.UnionPayMessage; import com.egzosn.pay.union.bean.UnionRefundResult; import com.egzosn.pay.union.bean.UnionTransactionType; -import java.io.ByteArrayInputStream; -import java.io.IOException; -import java.io.InputStream; -import java.math.BigDecimal; -import java.security.GeneralSecurityException; -import java.security.InvalidAlgorithmParameterException; -import java.security.NoSuchAlgorithmException; -import java.security.cert.*; -import java.sql.Timestamp; -import java.text.DateFormat; -import java.text.SimpleDateFormat; -import java.util.*; - /** * @author Actinia *
@@ -95,7 +111,8 @@ public class UnionPayService extends BasePayService {
             certDescriptor.initPrivateSignCert(payConfigStorage.getKeyPrivateCertInputStream(), payConfigStorage.getKeyPrivateCertPwd(), "PKCS12");
             certDescriptor.initPublicCert(payConfigStorage.getAcpMiddleCertInputStream());
             certDescriptor.initRootCert(payConfigStorage.getAcpRootCertInputStream());
-        } catch (IOException e) {
+        }
+        catch (IOException e) {
             LOG.error(e);
         }
 
@@ -192,7 +209,6 @@ public class UnionPayService extends BasePayService {
      * @param sign   签名原文
      * @return 签名校验 true通过
      */
-    @Override
     public boolean signVerify(Map params, String sign) {
         SignUtils signUtils = SignUtils.valueOf(payConfigStorage.getSignType());
 
@@ -214,17 +230,6 @@ public class UnionPayService extends BasePayService {
         }
     }
 
-    /**
-     * 支付宝需要,微信是否也需要再次校验来源,进行订单查询
-     * 校验数据来源
-     *
-     * @param id 业务id, 数据的真实性.
-     * @return true通过
-     */
-    @Override
-    public boolean verifySource(String id) {
-        return false;
-    }
 
     /**
      * 订单超时时间。
@@ -371,11 +376,14 @@ public class UnionPayService extends BasePayService {
             /*PKIXCertPathBuilderResult result = (PKIXCertPathBuilderResult)*/
             builder.build(pkixParams);
             return cert;
-        } catch (java.security.cert.CertPathBuilderException e) {
+        }
+        catch (java.security.cert.CertPathBuilderException e) {
             LOG.error("verify certificate chain fail.", e);
-        } catch (CertificateExpiredException e) {
+        }
+        catch (CertificateExpiredException e) {
             LOG.error(e);
-        } catch (GeneralSecurityException e) {
+        }
+        catch (GeneralSecurityException e) {
             LOG.error(e);
         }
         return null;
@@ -403,7 +411,8 @@ public class UnionPayService extends BasePayService {
 
         if (null == order.getTransactionType()) {
             order.setTransactionType(UnionTransactionType.WEB);
-        } else if (UnionTransactionType.WEB != order.getTransactionType() && UnionTransactionType.WAP != order.getTransactionType() && UnionTransactionType.B2B != order.getTransactionType()) {
+        }
+        else if (UnionTransactionType.WEB != order.getTransactionType() && UnionTransactionType.WAP != order.getTransactionType() && UnionTransactionType.B2B != order.getTransactionType()) {
             throw new PayErrorException(new PayException("-1", "错误的交易类型:" + order.getTransactionType()));
         }
 
@@ -456,7 +465,8 @@ public class UnionPayService extends BasePayService {
             CertificateFactory cf = CertificateFactory.getInstance("X.509");
             InputStream tIn = new ByteArrayInputStream(x509CertString.getBytes("ISO-8859-1"));
             x509Cert = (X509Certificate) cf.generateCertificate(tIn);
-        } catch (Exception e) {
+        }
+        catch (Exception e) {
             throw new PayErrorException(new PayException("证书加载失败", "gen certificate error:" + e.getLocalizedMessage()));
         }
         return x509Cert;
@@ -675,19 +685,6 @@ public class UnionPayService extends BasePayService {
     }
 
 
-    /**
-     * @param tradeNoOrBillDate  支付平台订单号或者账单类型, 具体请
-     *                           类型为{@link String }或者 {@link Date },类型须强制限制,类型不对应则抛出异常{@link PayErrorException}
-     * @param outTradeNoBillType 商户单号或者 账单类型
-     * @param transactionType    交易类型
-     * @return 返回支付方对应接口的结果
-     */
-    @Override
-    public Map secondaryInterface(Object tradeNoOrBillDate, String outTradeNoBillType, TransactionType transactionType) {
-        return Collections.emptyMap();
-    }
-
-
     /**
      * 创建消息
      *
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 be789e9..8888db6 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
@@ -26,6 +26,17 @@ public class WxYouDianPayConfigStorage extends BasePayConfigStorage {
         return null;
     }
 
+    /**
+     * 应用id
+     * 纠正名称
+     *
+     * @return 应用id
+     */
+    @Override
+    public String getAppId() {
+        return null;
+    }
+
 
     @Override
     public String getPid() {
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 82f6c8a..2dcdfff 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
@@ -1,9 +1,26 @@
 package com.egzosn.pay.wx.youdian.api;
 
+import java.io.InputStream;
+import java.math.BigDecimal;
+import java.util.Collections;
+import java.util.Date;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.TreeMap;
+import java.util.concurrent.locks.Lock;
+
 import com.alibaba.fastjson.JSON;
 import com.alibaba.fastjson.JSONObject;
 import com.egzosn.pay.common.api.BasePayService;
-import com.egzosn.pay.common.bean.*;
+import com.egzosn.pay.common.bean.BaseRefundResult;
+import com.egzosn.pay.common.bean.CurType;
+import com.egzosn.pay.common.bean.MethodType;
+import com.egzosn.pay.common.bean.PayMessage;
+import com.egzosn.pay.common.bean.PayOrder;
+import com.egzosn.pay.common.bean.PayOutMessage;
+import com.egzosn.pay.common.bean.RefundOrder;
+import com.egzosn.pay.common.bean.RefundResult;
+import com.egzosn.pay.common.bean.TransactionType;
 import com.egzosn.pay.common.bean.result.PayError;
 import com.egzosn.pay.common.exception.PayErrorException;
 import com.egzosn.pay.common.http.HttpConfigStorage;
@@ -14,15 +31,11 @@ import com.egzosn.pay.wx.youdian.bean.WxYoudianPayMessage;
 import com.egzosn.pay.wx.youdian.bean.YdPayError;
 import com.egzosn.pay.wx.youdian.bean.YoudianTransactionType;
 
-import java.io.InputStream;
-import java.math.BigDecimal;
-import java.util.*;
-import java.util.concurrent.locks.Lock;
-
 /**
- *  友店支付服务
- * @author  egan
+ * 友店支付服务
  *
+ * @author egan
+ * 

* email egzosn@gmail.com * date 2017/01/12 22:58 */ @@ -33,18 +46,21 @@ public class WxYouDianPayService extends BasePayService data = new TreeMap<>(); - data.put("username", payConfigStorage.getSeller()); - data.put("password", payConfigStorage.getKeyPrivate()); - String apbNonce = SignUtils.randomStr(); + public JSONObject login() throws PayErrorException { + TreeMap data = new TreeMap<>(); + data.put("username", payConfigStorage.getSeller()); + data.put("password", payConfigStorage.getKeyPrivate()); + String apbNonce = SignUtils.randomStr(); // 1、确定请求主体为用户登录,即需要传登录的用户名username和密码password并且要生成唯一的随机数命名为apb_nonce,长度为32位 // 2、将所有的参数集进行key排序 // 3、将排序后的数组从起始位置拼接成字符串如:password=XXXXXXXusername=XXXXX // 4、将拼接出来的字符串连接上apb_nonce的值即AAAAAAAAAA。再连接 password=XXXXXXXusername=XXXXXAAAAAAAAAA - String sign = createSign(SignUtils.parameterText(data, "") + apbNonce, payConfigStorage.getInputCharset()); - String queryParam = SignUtils.parameterText(data) + "&apb_nonce=" + apbNonce + "&sign=" + sign; - - JSONObject json = execute(getReqUrl(YoudianTransactionType.LOGIN) + "?" + queryParam, MethodType.GET, null); - payConfigStorage.updateAccessToken(json.getString("access_token"), json.getLongValue("viptime")); - return json; - } - + String sign = createSign(SignUtils.parameterText(data, "") + apbNonce, payConfigStorage.getInputCharset()); + String queryParam = SignUtils.parameterText(data) + "&apb_nonce=" + apbNonce + "&sign=" + sign; + JSONObject json = execute(getReqUrl(YoudianTransactionType.LOGIN) + "?" + queryParam, MethodType.GET, null); + payConfigStorage.updateAccessToken(json.getString("access_token"), json.getLongValue("viptime")); + return json; + } /** @@ -115,15 +132,18 @@ public class WxYouDianPayService extends BasePayService params) { - if (!"SUCCESS".equals(params.get("return_code"))){ + if (!"SUCCESS".equals(params.get("return_code"))) { 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) { + return signVerify(params, (String) params.get("sign")) && verifySource((String) params.get("out_trade_no")); + } + catch (PayErrorException e) { LOG.error(e.getMessage()); } return false; @@ -132,32 +152,32 @@ public class WxYouDianPayService extends BasePayService params, String sign) { return SignUtils.valueOf(payConfigStorage.getSignType()).verify(params, sign, "&key=" + payConfigStorage.getKeyPublic(), payConfigStorage.getInputCharset()); } - /** * 验证链接来源是否有效 * 校验数据来源 - * @param id id 商户订单号(扫码收款返回的order_sn) + * + * @param id id 商户订单号(扫码收款返回的order_sn) * @return true通过 */ - @Override public boolean verifySource(String id) { try { - JSONObject jsonObject = (JSONObject)query(id, null); + JSONObject jsonObject = (JSONObject) query(id, null); return 0 == jsonObject.getIntValue("errorcode"); - }catch (PayErrorException e){ - if (Integer.parseInt(e.getPayError().getErrorCode()) >= 400){ + } + catch (PayErrorException e) { + if (Integer.parseInt(e.getPayError().getErrorCode()) >= 400) { throw e; } return false; @@ -166,46 +186,47 @@ public class WxYouDianPayService extends BasePayService data = new TreeMap<>(); - data.put("access_token", getAccessToken()); + data.put("access_token", getAccessToken()); data.put("paymoney", Util.conversionAmount(order.getPrice()).toString()); data.putAll(order.getAttrs()); - data = preOrderHandler(data, order); + data = preOrderHandler(data, order); String apbNonce = SignUtils.randomStr(); String sign = createSign(SignUtils.parameterText(data, "") + apbNonce, payConfigStorage.getInputCharset()); data.put("PayMoney", data.remove("paymoney")); - String params = SignUtils.parameterText(data) + "&apb_nonce=" + apbNonce + "&sign=" + sign; + String params = SignUtils.parameterText(data) + "&apb_nonce=" + apbNonce + "&sign=" + sign; try { - JSONObject json = execute(getReqUrl(order.getTransactionType())+ "?" + params, MethodType.GET, null); + JSONObject json = execute(getReqUrl(order.getTransactionType()) + "?" + params, MethodType.GET, null); //友店比较特殊,需要在下完预订单后,自己存储 order_sn 对应 微信官方文档 out_trade_no - order.setOutTradeNo(json.getString("order_sn")); + order.setTradeNo(json.getString("order_sn")); return json; - } catch (PayErrorException e) { - throw e; + } + catch (PayErrorException e) { + throw e; } } - /** * 签名 - * @param content 需要签名的内容 - * @param characterEncoding 字符编码 * - * 1、确定请求主体为用户登录,即需要传登录的用户名username和密码password并且要生成唯一的随机数命名为apb_nonce,长度为32位 - * 2、将所有的参数集进行key排序 - * 3、将排序后的数组从起始位置拼接成字符串如:password=XXXXXXXusername=XXXXX - * 4、将拼接出来的字符串连接上apb_nonce的值即AAAAAAAAAA。再连接 password=XXXXXXXusername=XXXXXAAAAAAAAAA + * @param content 需要签名的内容 + * @param characterEncoding 字符编码 + *

+ * 1、确定请求主体为用户登录,即需要传登录的用户名username和密码password并且要生成唯一的随机数命名为apb_nonce,长度为32位 + * 2、将所有的参数集进行key排序 + * 3、将排序后的数组从起始位置拼接成字符串如:password=XXXXXXXusername=XXXXX + * 4、将拼接出来的字符串连接上apb_nonce的值即AAAAAAAAAA。再连接 password=XXXXXXXusername=XXXXXAAAAAAAAAA * @return 签名结果 */ @Override public String createSign(String content, String characterEncoding) { - return SignUtils.valueOf(payConfigStorage.getSignType().toUpperCase()).createSign(content, "&source=http://life.51youdian.com", characterEncoding); + return SignUtils.valueOf(payConfigStorage.getSignType().toUpperCase()).createSign(content, "&source=http://life.51youdian.com", characterEncoding); } /** @@ -270,7 +291,7 @@ public class WxYouDianPayService extends BasePayService getParameter2Map(Map parameterMap, InputStream is) { Map params = new TreeMap(); - for (Iterator iter = parameterMap.keySet().iterator(); iter.hasNext();) { + for (Iterator iter = parameterMap.keySet().iterator(); iter.hasNext(); ) { String name = (String) iter.next(); String[] values = parameterMap.get(name); String valueStr = ""; @@ -295,7 +316,6 @@ public class WxYouDianPayService extends BasePayService query(String tradeNo, String outTradeNo) { String apbNonce = SignUtils.randomStr(); TreeMap data = new TreeMap<>(); - data.put("access_token", payConfigStorage.getAccessToken()); + data.put("access_token", payConfigStorage.getAccessToken()); - if (StringUtils.isEmpty(tradeNo)){ + if (StringUtils.isEmpty(tradeNo)) { data.put("order_sn", outTradeNo); - }else { + } + else { data.put("order_sn", tradeNo); } String sign = createSign(SignUtils.parameterText(data, "") + apbNonce, payConfigStorage.getInputCharset()); - String queryParam = SignUtils.parameterText(data) + "&apb_nonce=" + apbNonce + "&sign=" + sign; - JSONObject jsonObject = execute(getReqUrl(YoudianTransactionType.NATIVE_STATUS) + "?" + queryParam, MethodType.GET, null); + String queryParam = SignUtils.parameterText(data) + "&apb_nonce=" + apbNonce + "&sign=" + sign; + JSONObject jsonObject = execute(getReqUrl(YoudianTransactionType.NATIVE_STATUS) + "?" + queryParam, MethodType.GET, null); return jsonObject; } @@ -384,8 +408,6 @@ public class WxYouDianPayService extends BasePayService data = new TreeMap<>(); - data.put("access_token", payConfigStorage.getAccessToken()); + data.put("access_token", payConfigStorage.getAccessToken()); - if (StringUtils.isEmpty(refundOrder.getOutTradeNo())){ + if (StringUtils.isEmpty(refundOrder.getOutTradeNo())) { data.put("order_sn", refundOrder.getOutTradeNo()); - }else { + } + else { data.put("order_sn", refundOrder.getTradeNo()); } //支付类型刷卡为3扫码为4 data.put("type", "4"); data.put("refund_fee", refundOrder.getRefundAmount().setScale(2, BigDecimal.ROUND_HALF_UP).toString()); String sign = createSign(SignUtils.parameterText(data, "") + apbNonce, payConfigStorage.getInputCharset()); - String queryParam = SignUtils.parameterText(data) + "&apb_nonce=" + apbNonce + "&sign=" + sign; - JSONObject jsonObject = execute(getReqUrl(YoudianTransactionType.REFUND) + "?" + queryParam, MethodType.GET, null); - return new BaseRefundResult() { + String queryParam = SignUtils.parameterText(data) + "&apb_nonce=" + apbNonce + "&sign=" + sign; + JSONObject jsonObject = execute(getReqUrl(YoudianTransactionType.REFUND) + "?" + queryParam, MethodType.GET, null); + return new BaseRefundResult(jsonObject) { @Override public String getCode() { return getAttrString("errorcode"); @@ -471,26 +494,11 @@ public class WxYouDianPayService extends BasePayService downloadbill(Date billDate, String billType) { + public Map downloadbill(Date billDate, String billType) { return Collections.emptyMap(); } - /** - * @param tradeNoOrBillDate 支付平台订单号或者账单类型, 具体请 - * 类型为{@link String }或者 {@link Date },类型须强制限制,类型不对应则抛出异常{@link PayErrorException} - * @param outTradeNoBillType 商户单号或者 账单类型 - * @param transactionType 交易类型 - * - * @return 返回支付方对应接口的结果 - */ - @Override - public Map secondaryInterface(Object tradeNoOrBillDate, String outTradeNoBillType, TransactionType transactionType) { - return Collections.emptyMap(); - } - - - public WxYouDianPayService(WxYouDianPayConfigStorage payConfigStorage) { super(payConfigStorage); } @@ -501,11 +509,12 @@ public class WxYouDianPayService extends BasePayService * email egzosn@gmail.com @@ -17,34 +18,32 @@ public class WxPayConfigStorage extends BasePayConfigStorage { /** * 微信分配的公众账号ID */ - private String appid ; + private String appId; /** * 微信分配的子商户公众账号ID */ - private String subAppid ; + private String subAppId; /** - * 微信支付分配的商户号 合作者id + * 微信支付分配的商户号 合作者id */ private String mchId; /** - * 微信支付分配的子商户号,开发者模式下必填 合作者id + * 微信支付分配的子商户号,开发者模式下必填 合作者id */ private String subMchId; - - - + @Deprecated @Override public String getAppid() { - return appid; - } - - public void setAppid(String appid) { - this.appid = appid; + return appId; } + @Deprecated + public void setAppid(String appId) { + this.appId = appId; + } /** @@ -56,8 +55,6 @@ public class WxPayConfigStorage extends BasePayConfigStorage { } - - @Override public String getSeller() { return null; @@ -73,7 +70,8 @@ public class WxPayConfigStorage extends BasePayConfigStorage { } /** - * 为商户平台设置的密钥key + * 为商户平台设置的密钥key + * * @return 微信密钥 */ public String getSecretKey() { @@ -81,17 +79,50 @@ public class WxPayConfigStorage extends BasePayConfigStorage { } public void setSecretKey(String secretKey) { - setKeyPrivate(secretKey); + setKeyPrivate(secretKey); } + public void setAppId(String appId) { + this.appId = appId; + } + + /** + * 应用id + * 纠正名称 + * + * @return 应用id + */ + @Override + public String getAppId() { + return appId; + } + + public String getSubAppId() { + return subAppId; + } + + public void setSubAppId(String subAppId) { + this.subAppId = subAppId; + } + + /** + * 应用id + * 纠正名称 + * + * @return 应用id + * @see #getSubAppId() + */ + @Deprecated public String getSubAppid() { - return subAppid; + return subAppId; } + @Deprecated public void setSubAppid(String subAppid) { - this.subAppid = subAppid; + this.subAppId = subAppid; } + public String getSubMchId() { return subMchId; } 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 84d3e4c..200279f 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 @@ -74,7 +74,7 @@ import com.egzosn.pay.wx.bean.WxTransferType; * date 2016-5-18 14:09:01 *

*/ -public class WxPayService extends BasePayService implements WxRedPackService,WxBillService { +public class WxPayService extends BasePayService implements WxRedPackService, WxBillService { /** @@ -132,7 +132,7 @@ public class WxPayService extends BasePayService implements @Override public boolean verify(Map params) { - if (null == params.get(SIGN) || !(SUCCESS.equals(params.get(RETURN_CODE)) && SUCCESS.equals(params.get(RESULT_CODE))) ) { + if (null == params.get(SIGN) || !(SUCCESS.equals(params.get(RETURN_CODE)) && SUCCESS.equals(params.get(RESULT_CODE)))) { if (LOG.isErrorEnabled()) { LOG.error(String.format("微信支付异常:return_code=%s,参数集=%s", params.get(RETURN_CODE), params)); } @@ -140,26 +140,15 @@ public class WxPayService extends BasePayService implements } try { - return signVerify(params, (String) params.get(SIGN)) && verifySource((String) params.get(OUT_TRADE_NO)); - } catch (PayErrorException e) { + return signVerify(params, (String) params.get(SIGN)); + } + catch (PayErrorException e) { LOG.error(e); } return false; } - /** - * 微信是否也需要再次校验来源,进行订单查询 - * - * @param id 商户单号 - * @return true通过 - */ - @Override - public boolean verifySource(String id) { - return true; - } - - /** * 根据反馈回来的信息,生成签名结果 * @@ -167,7 +156,6 @@ public class WxPayService extends BasePayService implements * @param sign 比对的签名结果 * @return 生成的签名结果 */ - @Override public boolean signVerify(Map params, String sign) { return signVerify(params, sign, payConfigStorage.isTest()); } @@ -190,11 +178,11 @@ public class WxPayService extends BasePayService implements private Map getPublicParameters() { Map parameters = new TreeMap(); - parameters.put(APPID, payConfigStorage.getAppid()); + parameters.put(APPID, payConfigStorage.getAppId()); parameters.put(MCH_ID, payConfigStorage.getMchId()); //判断如果是服务商模式信息则加入 setParameters(parameters, "sub_mch_id", payConfigStorage.getSubMchId()); - setParameters(parameters, "sub_appid", payConfigStorage.getSubAppid()); + setParameters(parameters, "sub_appid", payConfigStorage.getSubAppId()); parameters.put(NONCE_STR, SignUtils.randomStr()); return parameters; @@ -289,7 +277,8 @@ public class WxPayService extends BasePayService implements params.put("timeStamp", System.currentTimeMillis() / 1000); params.put("nonceStr", result.get(NONCE_STR)); params.put("package", "prepay_id=" + result.get("prepay_id")); - } else if (WxTransactionType.APP == order.getTransactionType()) { + } + else if (WxTransactionType.APP == order.getTransactionType()) { params.put("partnerid", payConfigStorage.getPid()); params.put(APPID, payConfigStorage.getAppid()); params.put("prepayid", result.get("prepay_id")); @@ -391,7 +380,8 @@ public class WxPayService extends BasePayService implements TreeMap map = new TreeMap(); try { return XML.inputStream2Map(is, map); - } catch (IOException e) { + } + catch (IOException e) { throw new PayErrorException(new PayException("IOException", e.getMessage())); } @@ -419,7 +409,7 @@ public class WxPayService extends BasePayService implements */ @Override public PayOutMessage successPayOutMessage(PayMessage payMessage) { - return PayOutMessage.XML().code("Success").content("成功").build(); + return PayOutMessage.XML().code("SUCCESS").content("成功").build(); } @@ -471,7 +461,8 @@ public class WxPayService extends BasePayService implements if (null == order.getTransactionType()) { order.setTransactionType(WxTransactionType.MICROPAY); - } else if (WxTransactionType.MICROPAY != order.getTransactionType() && WxTransactionType.FACEPAY != order.getTransactionType()) { + } + else if (WxTransactionType.MICROPAY != order.getTransactionType() && WxTransactionType.FACEPAY != order.getTransactionType()) { throw new PayErrorException(new PayException("-1", "错误的交易类型:" + order.getTransactionType())); } return orderInfo(order); @@ -575,7 +566,7 @@ public class WxPayService extends BasePayService implements */ @Override public Map downloadbill(Date billDate, String billType) { - Map parameters = getDownloadBillParam(billDate, billType,false); + Map parameters = getDownloadBillParam(billDate, billType, false); //设置签名 setSign(parameters); String respStr = requestTemplate.postForObject(getReqUrl(WxTransactionType.DOWNLOADBILL), XML.getMap2Xml(parameters), String.class); @@ -595,25 +586,26 @@ public class WxPayService extends BasePayService implements * * @param billDate 账单类型,商户通过接口或商户经开放平台授权后其所属服务商通过接口可以获取以下账单类型:trade、signcustomer;trade指商户基于支付宝交易收单的业务账单;signcustomer是指基于商户支付宝余额收入及支出等资金变动的帐务账单; * @param billType 账单时间:日账单格式为yyyy-MM-dd,月账单格式为yyyy-MM。 - * @param path 账单返回格式 账单存储的基础路径,按月切割 + * @param path 账单返回格式 账单存储的基础路径,按月切割 * @return 返回支付方下载对账单的结果 */ @Override public Map downloadbill(Date billDate, String billType, String path) { - Map parameters = getDownloadBillParam(billDate, billType,true); - //设置签名 + Map parameters = getDownloadBillParam(billDate, billType, true); + //设置签名 setSign(parameters); InputStream inputStream = requestTemplate.postForObject(getReqUrl(WxTransactionType.DOWNLOADBILL), XML.getMap2Xml(parameters), InputStream.class); try { //解压流 inputStream = uncompress(inputStream); - writeToLocal(path+DateUtils.formatDate(new Date(), DateUtils.YYYYMM)+"/"+DateUtils.formatDate(new Date(), DateUtils.YYYYMMDDHHMMSS)+".txt", inputStream); + writeToLocal(path + DateUtils.formatDate(new Date(), DateUtils.YYYYMM) + "/" + DateUtils.formatDate(new Date(), DateUtils.YYYYMMDDHHMMSS) + ".txt", inputStream); Map ret = new HashMap(3); ret.put(RETURN_CODE, SUCCESS); ret.put(RETURN_MSG_CODE, "ok"); ret.put("data", path); return ret; - } catch (Exception e) { + } + catch (Exception e) { e.printStackTrace(); Map ret = new HashMap(3); ret.put(RETURN_CODE, FAIL); @@ -627,8 +619,8 @@ public class WxPayService extends BasePayService implements * GZIP解压缩 * * @param input 输入流账单 - * @return 解压后输入流 - * @throws IOException IOException + * @return 解压后输入流 + * @throws IOException IOException */ public static InputStream uncompress(InputStream input) throws IOException { ByteArrayOutputStream out = new ByteArrayOutputStream(); @@ -644,6 +636,7 @@ public class WxPayService extends BasePayService implements /** * 将InputStream写入本地文件 + * * @param destination 写入本地目录 * @param inputStream 输入流 * @throws IOException IOException @@ -651,45 +644,46 @@ public class WxPayService extends BasePayService implements private void writeToLocal(String destination, InputStream inputStream) throws IOException { - // 判断字节大小 - if (inputStream.available() != 0) { - System.out.println("结果大小:" + inputStream.available()); - File file = new File(destination); - if (!file.getParentFile().exists()) { - boolean result = file.getParentFile().mkdirs(); - if (!result) { - System.out.println("创建失败"); - } - } - OutputStream out = new FileOutputStream(file); - int size = 0; - int len = 0; - byte[] buf = new byte[1024]; - while ((size = inputStream.read(buf)) != -1) { - len += size; - out.write(buf, 0, size); + // 判断字节大小 + if (inputStream.available() != 0) { + System.out.println("结果大小:" + inputStream.available()); + File file = new File(destination); + if (!file.getParentFile().exists()) { + boolean result = file.getParentFile().mkdirs(); + if (!result) { + System.out.println("创建失败"); } - System.out.println("最终写入字节数大小:" + len); - inputStream.close(); - out.close(); } + OutputStream out = new FileOutputStream(file); + int size = 0; + int len = 0; + byte[] buf = new byte[1024]; + while ((size = inputStream.read(buf)) != -1) { + len += size; + out.write(buf, 0, size); + } + System.out.println("最终写入字节数大小:" + len); + inputStream.close(); + out.close(); + } } /** * 下载账单公共参数 + * * @param billDate 账单类型,商户通过接口或商户经开放平台授权后其所属服务商通过接口可以获取以下账单类型:trade、signcustomer;trade指商户基于支付宝交易收单的业务账单;signcustomer是指基于商户支付宝余额收入及支出等资金变动的帐务账单; * @param billType 账单时间:日账单格式为yyyy-MM-dd,月账单格式为yyyy-MM。 - * @param tarType 账单返回格式 默认返回流false ,gzip 时候true + * @param tarType 账单返回格式 默认返回流false ,gzip 时候true * @return */ - private Map getDownloadBillParam(Date billDate, String billType,boolean tarType) { + private Map getDownloadBillParam(Date billDate, String billType, boolean tarType) { //获取公共参数 Map parameters = getPublicParameters(); parameters.put("bill_type", billType); //目前只支持日账单 parameters.put("bill_date", DateUtils.formatDate(billDate, DateUtils.YYYYMMDD)); - if(tarType){ + if (tarType) { parameters.put("tar_type", "GZIP"); } return parameters; @@ -702,7 +696,6 @@ public class WxPayService extends BasePayService implements * @param transactionType 交易类型 * @return 返回支付方对应接口的结果 */ - @Override public Map secondaryInterface(Object transactionIdOrBillDate, String outTradeNoBillType, TransactionType transactionType) { if (transactionType == WxTransactionType.REFUND) { @@ -735,12 +728,12 @@ public class WxPayService extends BasePayService implements * @param order 转账订单 *
      *
-     *              注意事项:
-     *              ◆ 当返回错误码为“SYSTEMERROR”时,请不要更换商户订单号,一定要使用原商户订单号重试,否则可能造成重复支付等资金风险。
-     *              ◆ XML具有可扩展性,因此返回参数可能会有新增,而且顺序可能不完全遵循此文档规范,如果在解析回包的时候发生错误,请商户务必不要换单重试,请商户联系客服确认付款情况。如果有新回包字段,会更新到此API文档中。
-     *              ◆ 因为错误代码字段err_code的值后续可能会增加,所以商户如果遇到回包返回新的错误码,请商户务必不要换单重试,请商户联系客服确认付款情况。如果有新的错误码,会更新到此API文档中。
-     *              ◆ 错误代码描述字段err_code_des只供人工定位问题时做参考,系统实现时请不要依赖这个字段来做自动化处理。
-     *              
+ * 注意事项: + * ◆ 当返回错误码为“SYSTEMERROR”时,请不要更换商户订单号,一定要使用原商户订单号重试,否则可能造成重复支付等资金风险。 + * ◆ XML具有可扩展性,因此返回参数可能会有新增,而且顺序可能不完全遵循此文档规范,如果在解析回包的时候发生错误,请商户务必不要换单重试,请商户联系客服确认付款情况。如果有新回包字段,会更新到此API文档中。 + * ◆ 因为错误代码字段err_code的值后续可能会增加,所以商户如果遇到回包返回新的错误码,请商户务必不要换单重试,请商户联系客服确认付款情况。如果有新的错误码,会更新到此API文档中。 + * ◆ 错误代码描述字段err_code_des只供人工定位问题时做参考,系统实现时请不要依赖这个字段来做自动化处理。 + *
* @return 对应的转账结果 */ @Override @@ -757,7 +750,8 @@ public class WxPayService extends BasePayService implements if (null != order.getTransferType() && TRANSFERS == order.getTransferType()) { transfers(parameters, order); parameters.put("mchid", payConfigStorage.getPid()); - } else { + } + else { parameters.put(MCH_ID, payConfigStorage.getPid()); order.setTransferType(WxTransferType.PAY_BANK); payBank(parameters, order); @@ -842,7 +836,8 @@ public class WxPayService extends BasePayService implements public String keyPublic(String content) { try { return RSA2.encrypt(content, payConfigStorage.getKeyPublic(), CIPHER_ALGORITHM, payConfigStorage.getInputCharset()); - } catch (GeneralSecurityException | IOException e) { + } + catch (GeneralSecurityException | IOException e) { throw new PayErrorException(new WxPayError(FAILURE, e.getLocalizedMessage())); } } @@ -873,7 +868,8 @@ public class WxPayService extends BasePayService implements //现金红包,小程序红包默认传1.裂变红包取传入值,且需要大于3 parameters.put("total_num", Math.max(redpackOrder.getTotalNum(), 3)); parameters.put("amt_type", "ALL_RAND"); - } else if (WxSendredpackType.SENDMINIPROGRAMHB == redpackOrder.getTransferType()) { + } + else if (WxSendredpackType.SENDMINIPROGRAMHB == redpackOrder.getTransferType()) { parameters.put("notify_way", "MINI_PROGRAM_JSAPI"); } diff --git a/pay-java-yiji/src/main/java/com/egzosn/pay/yiji/api/YiJiPayConfigStorage.java b/pay-java-yiji/src/main/java/com/egzosn/pay/yiji/api/YiJiPayConfigStorage.java index 492ac1f..8484c27 100644 --- a/pay-java-yiji/src/main/java/com/egzosn/pay/yiji/api/YiJiPayConfigStorage.java +++ b/pay-java-yiji/src/main/java/com/egzosn/pay/yiji/api/YiJiPayConfigStorage.java @@ -4,7 +4,8 @@ import com.egzosn.pay.common.api.BasePayConfigStorage; /** * 易极付配置存储 - * @author egan + * + * @author egan * *
  * email egzosn@gmail.com
@@ -15,14 +16,14 @@ public class YiJiPayConfigStorage extends BasePayConfigStorage {
 
 
     /**
-     *  	易极付分配的商户号 合作者id
+     * 易极付分配的商户号 合作者id
      */
     private String partnerId;
 
     /**
      * 卖家id
      */
-    private String  sellerUserId;
+    private String sellerUserId;
 
     public String getPartnerId() {
         return partnerId;
@@ -37,6 +38,17 @@ public class YiJiPayConfigStorage extends BasePayConfigStorage {
         return null;
     }
 
+    /**
+     * 应用id
+     * 纠正名称
+     *
+     * @return 应用id
+     */
+    @Override
+    public String getAppId() {
+        return null;
+    }
+
 
     /**
      * 合作商唯一标识
@@ -47,8 +59,6 @@ public class YiJiPayConfigStorage extends BasePayConfigStorage {
     }
 
 
-
-
     @Override
     public String getSeller() {
         return sellerUserId;
@@ -63,7 +73,8 @@ public class YiJiPayConfigStorage extends BasePayConfigStorage {
     }
 
     /**
-     *  为商户平台设置的密钥key
+     * 为商户平台设置的密钥key
+     *
      * @return 密钥
      */
     public String getSecretKey() {
@@ -71,7 +82,7 @@ public class YiJiPayConfigStorage extends BasePayConfigStorage {
     }
 
     public void setSecretKey(String secretKey) {
-         setKeyPrivate(secretKey);
+        setKeyPrivate(secretKey);
     }
 
 
diff --git a/pay-java-yiji/src/main/java/com/egzosn/pay/yiji/api/YiJiPayService.java b/pay-java-yiji/src/main/java/com/egzosn/pay/yiji/api/YiJiPayService.java
index ccc553a..1ef9daa 100644
--- a/pay-java-yiji/src/main/java/com/egzosn/pay/yiji/api/YiJiPayService.java
+++ b/pay-java-yiji/src/main/java/com/egzosn/pay/yiji/api/YiJiPayService.java
@@ -1,9 +1,24 @@
 package com.egzosn.pay.yiji.api;
 
+import java.math.BigDecimal;
+import java.util.Collections;
+import java.util.Date;
+import java.util.Map;
+import java.util.TreeMap;
+
 import com.alibaba.fastjson.JSONObject;
 import com.egzosn.pay.common.api.BasePayService;
-import com.egzosn.pay.common.bean.*;
-import com.egzosn.pay.common.exception.PayErrorException;
+import com.egzosn.pay.common.bean.BaseRefundResult;
+import com.egzosn.pay.common.bean.CurType;
+import com.egzosn.pay.common.bean.DefaultCurType;
+import com.egzosn.pay.common.bean.MethodType;
+import com.egzosn.pay.common.bean.PayMessage;
+import com.egzosn.pay.common.bean.PayOrder;
+import com.egzosn.pay.common.bean.PayOutMessage;
+import com.egzosn.pay.common.bean.RefundOrder;
+import com.egzosn.pay.common.bean.RefundResult;
+import com.egzosn.pay.common.bean.TransactionType;
+import com.egzosn.pay.common.bean.TransferOrder;
 import com.egzosn.pay.common.http.HttpConfigStorage;
 import com.egzosn.pay.common.util.DateUtils;
 import com.egzosn.pay.common.util.Util;
@@ -11,20 +26,14 @@ import com.egzosn.pay.common.util.sign.SignUtils;
 import com.egzosn.pay.common.util.str.StringUtils;
 import com.egzosn.pay.yiji.bean.YiJiTransactionType;
 
-import java.math.BigDecimal;
-import java.util.Collections;
-import java.util.Date;
-import java.util.Map;
-import java.util.TreeMap;
-
 
 /**
  * 易极付支付服务
  *
  * @author egan
- *         

- * email egzosn@gmail.com - * * date 2019/04/15 22:51 + *

+ * email egzosn@gmail.com + * * date 2019/04/15 22:51 */ public class YiJiPayService extends BasePayService { @@ -54,11 +63,13 @@ public class YiJiPayService extends BasePayService { */ @Override public String getReqUrl(TransactionType transactionType) { - if (payConfigStorage.isTest()){ + if (payConfigStorage.isTest()) { return DEV_REQ_URL; - }else if (/*YiJiTransactionType.corderRemittanceSynOrder == transactionType ||*/ YiJiTransactionType.applyRemittranceWithSynOrder == transactionType){ + } + else if (/*YiJiTransactionType.corderRemittanceSynOrder == transactionType ||*/ YiJiTransactionType.applyRemittranceWithSynOrder == transactionType) { return HTTPS_GLOBAL_REQ_URL; - }else { + } + else { return HTTPS_REQ_URL; } } @@ -72,7 +83,6 @@ public class YiJiPayService extends BasePayService { } - /** * 回调校验 * @@ -98,25 +108,12 @@ public class YiJiPayService extends BasePayService { * @param sign 比对的签名结果 * @return 生成的签名结果 */ - @Override public boolean signVerify(Map params, String sign) { return SignUtils.valueOf(payConfigStorage.getSignType()).verify(params, sign, payConfigStorage.getKeyPublic(), payConfigStorage.getInputCharset()); } - /** - * 校验数据来源 - * - * @param id 业务id, 数据的真实性. - * @return true通过 - */ - @Override - public boolean verifySource(String id) { - return true; - } - - /** * 生成并设置签名 * @@ -160,18 +157,18 @@ public class YiJiPayService extends BasePayService { orderInfo.put("orderNo", order.getOutTradeNo()); orderInfo.put("outOrderNo", order.getOutTradeNo()); - if (StringUtils.isNotEmpty(payConfigStorage.getSeller())){ + if (StringUtils.isNotEmpty(payConfigStorage.getSeller())) { orderInfo.put("sellerUserId", payConfigStorage.getSeller()); } - ((YiJiTransactionType)order.getTransactionType()).setAttribute(orderInfo, order); + ((YiJiTransactionType) order.getTransactionType()).setAttribute(orderInfo, order); orderInfo.put("tradeAmount", Util.conversionAmount(order.getPrice())); //商品条款信息 商品名称 orderInfo.put("goodsClauses", String.format("[{'name':'%s'}]", order.getBody())); //交易名称 orderInfo.put("tradeName", order.getSubject()); - if (null != order.getCurType()){ + if (null != order.getCurType()) { orderInfo.put("currency", order.getCurType()); } orderInfo.putAll(order.getAttrs()); @@ -292,8 +289,6 @@ public class YiJiPayService extends BasePayService { } - - /** * 申请退款接口 * @@ -384,20 +379,6 @@ public class YiJiPayService extends BasePayService { } - /** - * @param tradeNoOrBillDate 支付平台订单号或者账单类型, 具体请 - * 类型为{@link String }或者 {@link Date },类型须强制限制,类型不对应则抛出异常{@link PayErrorException} - * @param outTradeNoBillType 商户单号或者 账单类型 - * @param transactionType 交易类型 - * @return 返回支付方对应接口的结果 - */ - @Override - public Map secondaryInterface(Object tradeNoOrBillDate, String outTradeNoBillType, TransactionType transactionType) { - - - return Collections.emptyMap(); - } - /** * 转账 这里外部进行调用{@link #buildRequest(Map, MethodType)} * @@ -409,10 +390,10 @@ public class YiJiPayService extends BasePayService { Map data = getPublicParameters(YiJiTransactionType.applyRemittranceWithSynOrder); data.put("remittranceBatchNo", order.getBatchNo()); data.put("outOrderNo", order.getOutNo()); - data.put("payAmount", Util.conversionAmount(order.getAmount()) ); + data.put("payAmount", Util.conversionAmount(order.getAmount())); data.put("payCurrency", order.getCurType().getType()); data.put("withdrawCurrency", DefaultCurType.CNY.getType()); - data.put("payMemo",order.getRemark()); + data.put("payMemo", order.getRemark()); data.put("toCountryCode", order.getCountryCode().getCode()); data.put("tradeUseCode", "326"); data.put("payeeName", order.getPayeeName()); -- Gitee From b14bdf0d980e5547a3fb39f2b1c017a8691bcb83 Mon Sep 17 00:00:00 2001 From: egzosn Date: Sun, 17 Jan 2021 22:38:42 +0800 Subject: [PATCH 173/299] paypal v2 --- .../controller/PayPalV2PayController.java | 147 ++++++ .../v2/api/PayPalOutMessageBuilder.java | 26 ++ .../pay/paypal/v2/api/PayPalPayService.java | 437 ++++++++++++++++++ .../pay/paypal/v2/bean/PayPalOrder.java | 189 ++++++++ .../paypal/v2/bean/PayPalRefundResult.java | 119 +++++ .../paypal/v2/bean/PayPalTransactionType.java | 74 +++ .../paypal/v2/bean/order/AddressPortable.java | 220 +++++++++ .../v2/bean/order/ApplicationContext.java | 147 ++++++ .../pay/paypal/v2/bean/order/Money.java | 37 ++ .../egzosn/pay/paypal/v2/bean/order/Name.java | 174 +++++++ .../paypal/v2/bean/order/OrderRequest.java | 43 ++ .../v2/bean/order/PurchaseUnitRequest.java | 141 ++++++ .../paypal/v2/bean/order/ShippingDetail.java | 59 +++ 13 files changed, 1813 insertions(+) create mode 100644 pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/PayPalV2PayController.java create mode 100644 pay-java-paypal/src/main/java/com/egzosn/pay/paypal/v2/api/PayPalOutMessageBuilder.java create mode 100644 pay-java-paypal/src/main/java/com/egzosn/pay/paypal/v2/api/PayPalPayService.java create mode 100644 pay-java-paypal/src/main/java/com/egzosn/pay/paypal/v2/bean/PayPalOrder.java create mode 100644 pay-java-paypal/src/main/java/com/egzosn/pay/paypal/v2/bean/PayPalRefundResult.java create mode 100644 pay-java-paypal/src/main/java/com/egzosn/pay/paypal/v2/bean/PayPalTransactionType.java create mode 100644 pay-java-paypal/src/main/java/com/egzosn/pay/paypal/v2/bean/order/AddressPortable.java create mode 100644 pay-java-paypal/src/main/java/com/egzosn/pay/paypal/v2/bean/order/ApplicationContext.java create mode 100644 pay-java-paypal/src/main/java/com/egzosn/pay/paypal/v2/bean/order/Money.java create mode 100644 pay-java-paypal/src/main/java/com/egzosn/pay/paypal/v2/bean/order/Name.java create mode 100644 pay-java-paypal/src/main/java/com/egzosn/pay/paypal/v2/bean/order/OrderRequest.java create mode 100644 pay-java-paypal/src/main/java/com/egzosn/pay/paypal/v2/bean/order/PurchaseUnitRequest.java create mode 100644 pay-java-paypal/src/main/java/com/egzosn/pay/paypal/v2/bean/order/ShippingDetail.java diff --git a/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/PayPalV2PayController.java b/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/PayPalV2PayController.java new file mode 100644 index 0000000..125c215 --- /dev/null +++ b/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/PayPalV2PayController.java @@ -0,0 +1,147 @@ +package com.egzosn.pay.demo.controller; + + +import java.io.IOException; +import java.io.InputStream; +import java.math.BigDecimal; + +import javax.annotation.PostConstruct; +import javax.servlet.http.HttpServletRequest; + +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import com.egzosn.pay.common.api.PayService; +import com.egzosn.pay.common.bean.DefaultCurType; +import com.egzosn.pay.common.bean.RefundOrder; +import com.egzosn.pay.common.bean.RefundResult; +import com.egzosn.pay.paypal.api.PayPalConfigStorage; +import com.egzosn.pay.paypal.v2.api.PayPalPayService; +import com.egzosn.pay.paypal.v2.bean.PayPalOrder; +import com.egzosn.pay.paypal.v2.bean.order.AddressPortable; +import com.egzosn.pay.paypal.v2.bean.order.Name; +import com.egzosn.pay.paypal.v2.bean.order.ShippingDetail; + +/** + * 发起支付入口 + * + * @author: egan + * email egzosn@gmail.com + * date 2018/05/06 10:30 + */ +@RestController +@RequestMapping("payPalV2") +public class PayPalV2PayController { + + + private PayService service = null; + + @PostConstruct + public void init() { + PayPalConfigStorage storage = new PayPalConfigStorage(); + storage.setClientID("AZDS0IhUZvJTO99unlvSDMfbZIP-p-UecYXZdJoweha9LFuqKXKcQIGZgfVaX6oGiAOJAUuJD7JwyTl1"); + storage.setClientSecret("EK2YaOrw3oLSDWIRzvb9BWGTjiPPhY1fFUu5ylhUsGYLc_h_dlpJ0hr_LDEkbO9MyKP2P83YcywbPaem"); + storage.setTest(true); + //发起付款后的页面转跳地址 + storage.setReturnUrl("http://www.egzosn.com/payPal/payBack.json"); + // 注意:这里不是异步回调的通知 IPN 地址设置的路径:https://developer.paypal.com/developer/ipnSimulator/ + //取消按钮转跳地址, + storage.setCancelUrl("http://www.egzosn.com/pay/cancel"); + service = new PayPalPayService(storage); + } + + + /** + * 跳到支付页面 + * 针对实时支付,即时付款 + * + * @param price 金额 + * @return 跳到支付页面 + */ + @RequestMapping(value = "toPay.html", produces = "text/html;charset=UTF-8") + public String toPay(BigDecimal price) { + //及时收款 + PayPalOrder order = new PayPalOrder(); + order.setBrandName("该标签将覆盖PayPal网站上PayPal帐户中的公司名称,非必填"); + order.setDescription("订单说明"); + order.setInvoiceId("非必填 API调用者为该订单提供的外部发票号码。出现在付款人的交易历史记录和付款人收到的电子邮件中。"); + order.setCustomId("非必填 api调用中没发现有任何用处 API调用者提供的外部ID。用于协调客户端交易与PayPal交易。出现在交易和结算报告中,但付款人不可见"); + order.setPrice(price); + order.setShippingDetail(new ShippingDetail() + .name(new Name().fullName("RATTA")) + .addressPortable(new AddressPortable() + .addressLine1("梅陇镇") + .addressLine2("集心路168号") + .adminArea2("闵行区") + .adminArea1("上海市") + .postalCode("20000") + .countryCode("CN"))); + String toPayHtml = service.toPay(order); + + //某些支付下单时无法设置单号,通过下单后返回对应单号,如 paypal,友店。 + String tradeNo = order.getTradeNo(); + System.out.println("支付订单号:" + tradeNo + " 这里可以进行回存"); + + return toPayHtml; + } + + /** + * 申请退款接口 + * + * @return 返回支付方申请退款后的结果 + */ + @RequestMapping("refund") + public RefundResult refund() { + // TODO 这里需要 refundAmount, curType, description, tradeNo + RefundOrder order = new RefundOrder(); + order.setCurType(DefaultCurType.USD); + order.setDescription(" description "); + order.setTradeNo("paypal 平台的单号, 支付下单返回的单号"); + order.setRefundAmount(BigDecimal.valueOf(0.01)); + return service.refund(order); + } + + + /** + * 注意:这里不是异步回调的通知 IPN 地址设置的路径:https://developer.paypal.com/developer/ipnSimulator/ + * PayPal确认付款调用的接口 + * 用户确认付款后,paypal调用的这个方法执行付款 + * + * @param request 请求 + * @return 付款成功信息 + * @throws IOException IOException + */ + @GetMapping(value = "payBackBefore.json") + public String payBackBefore(HttpServletRequest request) throws IOException { + try (InputStream is = request.getInputStream()) { + if (service.verify(service.getParameter2Map(request.getParameterMap(), is))) { + // TODO 这里进行成功后的订单业务处理 + // TODO 返回成功付款页面,这个到时候再做一个漂亮的页面显示,并使用前后端分离的模式 + return service.successPayOutMessage(null).toMessage(); + } + } + return "failure"; + } + + /* */ + + /** + * 支付回调地址 + * 注意:这里不是异步回调的通知 IPN 地址设置的路径:https://developer.paypal.com/developer/ipnSimulator/ + * + * @param request 请求 + * @return 结果 + * @throws IOException IOException + * 业务处理在对应的PayMessageHandler里面处理,在哪里设置PayMessageHandler,详情查看{@link PayService#setPayMessageHandler(com.egzosn.pay.common.api.PayMessageHandler)} + *

+ * 如果未设置 {@link com.egzosn.pay.common.api.PayMessageHandler} 那么会使用默认的 {@link com.egzosn.pay.common.api.DefaultPayMessageHandler} + */ + @RequestMapping(value = "payBack.json") + public String payBack(HttpServletRequest request) throws IOException { + //业务处理在对应的PayMessageHandler里面处理,在哪里设置PayMessageHandler,详情查看com.egzosn.pay.common.api.PayService.setPayMessageHandler() + return service.payBack(request.getParameterMap(), request.getInputStream()).toMessage(); + } + + +} diff --git a/pay-java-paypal/src/main/java/com/egzosn/pay/paypal/v2/api/PayPalOutMessageBuilder.java b/pay-java-paypal/src/main/java/com/egzosn/pay/paypal/v2/api/PayPalOutMessageBuilder.java new file mode 100644 index 0000000..71e19c4 --- /dev/null +++ b/pay-java-paypal/src/main/java/com/egzosn/pay/paypal/v2/api/PayPalOutMessageBuilder.java @@ -0,0 +1,26 @@ +package com.egzosn.pay.paypal.v2.api; + +import java.util.Map; + +import com.egzosn.pay.common.bean.outbuilder.TextBuilder; + +/** + * @author Egan + *

+ * email egzosn@gmail.com
+ * date 2021/1/17
+ * 
+ */ +public class PayPalOutMessageBuilder extends TextBuilder { + + + public PayPalOutMessageBuilder(Map message) { + StringBuilder out = new StringBuilder(); + for (Map.Entry entry : message.entrySet()) { + out.append(entry.getKey()).append('=').append(entry.getValue()).append("
"); + } + super.content(out.toString()); + } + + +} diff --git a/pay-java-paypal/src/main/java/com/egzosn/pay/paypal/v2/api/PayPalPayService.java b/pay-java-paypal/src/main/java/com/egzosn/pay/paypal/v2/api/PayPalPayService.java new file mode 100644 index 0000000..a6c72ed --- /dev/null +++ b/pay-java-paypal/src/main/java/com/egzosn/pay/paypal/v2/api/PayPalPayService.java @@ -0,0 +1,437 @@ +package com.egzosn.pay.paypal.v2.api; + + +import java.io.InputStream; +import java.io.UnsupportedEncodingException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Date; +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.UUID; +import java.util.concurrent.locks.Lock; + +import org.apache.http.Header; +import org.apache.http.entity.ContentType; +import org.apache.http.message.BasicHeader; + +import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.JSONArray; +import com.alibaba.fastjson.JSONObject; +import com.egzosn.pay.common.api.BasePayService; +import com.egzosn.pay.common.bean.CurType; +import com.egzosn.pay.common.bean.DefaultCurType; +import com.egzosn.pay.common.bean.MethodType; +import com.egzosn.pay.common.bean.PayMessage; +import com.egzosn.pay.common.bean.PayOrder; +import com.egzosn.pay.common.bean.PayOutMessage; +import com.egzosn.pay.common.bean.RefundOrder; +import com.egzosn.pay.common.bean.RefundResult; +import com.egzosn.pay.common.bean.TransactionType; +import com.egzosn.pay.common.bean.result.PayException; +import com.egzosn.pay.common.exception.PayErrorException; +import com.egzosn.pay.common.http.HttpHeader; +import com.egzosn.pay.common.http.HttpStringEntity; +import com.egzosn.pay.common.http.UriVariables; +import com.egzosn.pay.common.util.Util; +import com.egzosn.pay.common.util.str.StringUtils; +import com.egzosn.pay.paypal.api.PayPalConfigStorage; +import com.egzosn.pay.paypal.v2.bean.PayPalRefundResult; +import com.egzosn.pay.paypal.v2.bean.PayPalTransactionType; +import com.egzosn.pay.paypal.v2.bean.order.ApplicationContext; +import com.egzosn.pay.paypal.v2.bean.order.Money; +import com.egzosn.pay.paypal.v2.bean.order.OrderRequest; +import com.egzosn.pay.paypal.v2.bean.order.PurchaseUnitRequest; +import com.egzosn.pay.paypal.v2.bean.order.ShippingDetail; + + +/** + * 贝宝支付配置存储 + * + * @author egan + *

+ * email egzosn@gmail.com + * date 2021-1-16 ‏‎22:15:09 + */ +public class PayPalPayService extends BasePayService { + + /** + * 沙箱环境 + */ + private static final String SANDBOX_REQ_URL = "https://api.sandbox.paypal.com/"; + /** + * 正式测试环境 + */ + private static final String REQ_URL = "https://api.paypal.com/"; + + private static final String NOTIFY_VALIDATE_URL = "https://ipnpb.paypal.com/cgi-bin/webscr?cmd=_notify-validate&"; + private static final String SANDBOX_NOTIFY_VALIDATE_URL = "https://ipnpb.sandbox.paypal.com/cgi-bin/webscr?cmd=_notify-validate&"; + + /** + * 获取对应的请求地址 + * + * @return 请求地址 + */ + @Override + public String getReqUrl(TransactionType transactionType) { + return (payConfigStorage.isTest() ? SANDBOX_REQ_URL : REQ_URL) + transactionType.getMethod(); + } + + + /** + * 获取通知校验对应的请求地址 + * + * @param params 回调参数 + * @return 请求地址 + */ + public String getNotifyReqUrl(Map params) { + return (payConfigStorage.isTest() ? SANDBOX_NOTIFY_VALIDATE_URL : NOTIFY_VALIDATE_URL) + UriVariables.getMapToParameters(params); + } + + + public PayPalPayService(PayPalConfigStorage payConfigStorage) { + super(payConfigStorage); + } + + + /** + * 获取请求token + * + * @return 授权令牌 + */ + public String getAccessToken() { + return getAccessToken(false); + } + + /** + * 获取授权令牌 + * + * @param forceRefresh 是否重新获取, true重新获取 + * @return 新的授权令牌 + * @throws PayErrorException 支付异常 + */ + public String getAccessToken(boolean forceRefresh) throws PayErrorException { + Lock lock = payConfigStorage.getAccessTokenLock(); + try { + lock.lock(); + if (forceRefresh) { + payConfigStorage.expireAccessToken(); + } + if (payConfigStorage.isAccessTokenExpired()) { + Map header = new HashMap<>(); + header.put("Authorization", "Basic " + authorizationString(getPayConfigStorage().getAppid(), getPayConfigStorage().getKeyPrivate())); + header.put("Accept", "application/json"); + header.put("Content-Type", "application/x-www-form-urlencoded"); + try { + HttpStringEntity entity = new HttpStringEntity("grant_type=client_credentials", header); + JSONObject resp = getHttpRequestTemplate().postForObject(getReqUrl(PayPalTransactionType.AUTHORIZE), entity, JSONObject.class); + payConfigStorage.updateAccessToken(String.format("%s %s", resp.getString("token_type"), resp.getString("access_token")), resp.getIntValue("expires_in")); + + } + catch (UnsupportedEncodingException e) { + throw new PayErrorException(new PayException("failure", e.getMessage())); + } + return payConfigStorage.getAccessToken(); + } + } + finally { + lock.unlock(); + } + return payConfigStorage.getAccessToken(); + } + + + /** + * IPN 地址设置的路径:https://developer.paypal.com/developer/ipnSimulator/ + * 1.Check that the payment_status is Completed. + * 2.If the payment_status is Completed, check the txn_id against the previous PayPal transaction that you processed to ensure the IPN message is not a duplicate. + * 3.Check that the receiver_email is an email address registered in your PayPal account. + * 4.Check that the price (carried in mc_gross) and the currency (carried in mc_currency) are correct for the item (carried in item_name or item_number). + * + * @param params 回调回来的参数集 + * @return + */ + @Override + public boolean verify(Map params) { + Object paymentStatus = params.get("payment_status"); + if (!"Completed".equals(paymentStatus)) { + LOG.warn("状态未完成:" + paymentStatus); + return false; + } + String resp = getHttpRequestTemplate().getForObject(getNotifyReqUrl(params), authHeader(), String.class); + return "VERIFIED".equals(resp); + + } + + + /** + * 获取授权请求头 + * + * @return 授权请求头 + */ + private HttpHeader authHeader() { + + List

headers = new ArrayList<>(); + headers.add(new BasicHeader("Authorization", getAccessToken())); + headers.add(new BasicHeader("PayPal-Request-Id", UUID.randomUUID().toString())); + + return new HttpHeader(headers); + } + + + /** + * 页面转跳支付, 返回对应页面重定向信息 + * + * @param order 订单信息 + * @return 对应页面重定向信息 + */ + @Override + public String toPay(PayOrder order) { + order.setTransactionType(PayPalTransactionType.CHECKOUT); + return super.toPay(order); + } + + private ApplicationContext initUrl(ApplicationContext applicationContext, PayOrder order) { + String cancelUrl = (String) order.getAttr("cancelUrl"); + if (StringUtils.isEmpty(cancelUrl)) { + cancelUrl = payConfigStorage.getCancelUrl(); + } + + String returnUrl = (String) order.getAttr("returnUrl"); + if (StringUtils.isEmpty(returnUrl)) { + returnUrl = payConfigStorage.getReturnUrl(); + } + applicationContext + .cancelUrl(cancelUrl) + .returnUrl(returnUrl); + + + return applicationContext; + } + + private ApplicationContext createApplicationContext(PayOrder order) { + ApplicationContext applicationContext = new ApplicationContext(); + initUrl(applicationContext, order); + String brandName = (String) order.getAttr("brandName"); + if (StringUtils.isEmpty(brandName)) { + applicationContext.setBrandName(brandName); + } + + String landingPage = (String) order.getAttr("landingPage"); + if (StringUtils.isEmpty(landingPage)) { + applicationContext.setLandingPage(landingPage); + } + + String shippingPreference = (String) order.getAttr("shippingPreference"); + if (StringUtils.isEmpty(shippingPreference)) { + applicationContext.setShippingPreference(shippingPreference); + } + + String userAction = (String) order.getAttr("userAction"); + if (StringUtils.isEmpty(userAction)) { + applicationContext.setUserAction(userAction); + } + + + return applicationContext; + } + + /** + * 返回创建的订单信息 + * + * @param order 支付订单 + * @return 订单信息 + * @see PayOrder 支付订单信息 + */ + @Override + public Map orderInfo(PayOrder order) { + if (null == order.getTransactionType()) { + order.setTransactionType(PayPalTransactionType.CHECKOUT); + } + OrderRequest orderRequest = new OrderRequest(); + orderRequest.setCheckoutPaymentIntent("CAPTURE"); + + orderRequest.setApplicationContext(createApplicationContext(order)); + + List purchaseUnitRequests = new ArrayList(); + CurType curType = order.getCurType(); + if (null == curType) { + curType = DefaultCurType.USD; + } + PurchaseUnitRequest purchaseUnitRequest = new PurchaseUnitRequest() + .description(order.getSubject()) + .invoiceId((String) order.getAttr("invoiceId")) + .customId(order.getOutTradeNo()) + .money(new Money() + .currencyCode(curType.getType()) + .value(Util.conversionAmount(order.getPrice()).toString())); + + Object shippingDetail = order.getAttr("shippingDetail"); + if (shippingDetail instanceof ShippingDetail) { + purchaseUnitRequest.setShippingDetail((ShippingDetail) shippingDetail); + } + else { + ShippingDetail shippingDetail1 = JSON.parseObject(JSON.toJSONString(shippingDetail), ShippingDetail.class); + purchaseUnitRequest.setShippingDetail(shippingDetail1); + } + + purchaseUnitRequests.add(purchaseUnitRequest); + orderRequest.setPurchaseUnits(purchaseUnitRequests); + + HttpStringEntity entity = new HttpStringEntity(JSON.toJSONString(orderRequest), ContentType.APPLICATION_JSON); + HttpHeader header = authHeader(); + header.addHeader(new BasicHeader("prefer", "return=representation")); + entity.setHeaders(header); + JSONObject resp = getHttpRequestTemplate().postForObject(getReqUrl(order.getTransactionType()), entity, JSONObject.class); + if ("created".equals(resp.getString("state")) && StringUtils.isNotEmpty(resp.getString("id"))) { + order.setTradeNo(resp.getString("id")); + } + return preOrderHandler(resp, order); + } + + @Override + public PayOutMessage getPayOutMessage(String code, String message) { + String out = "The response from IPN was: " + code + ""; + return PayOutMessage.TEXT().content(out).build(); + } + + @Override + public PayOutMessage successPayOutMessage(PayMessage payMessage) { + Map message = payMessage.getPayMessage(); + return new PayPalOutMessageBuilder(message).build(); + } + + @Override + public String buildRequest(Map orderInfo, MethodType method) { + if (orderInfo instanceof JSONObject) { + JSONObject resp = (JSONObject) orderInfo; + JSONArray links = resp.getJSONArray("links"); + for (int i = 0; i < links.size(); i++) { + JSONObject link = links.getJSONObject(i); + if ("approve".equals(link.getString("rel"))) { + return String.format("", link.getString("href")); + } + } + } + return ""; + } + + @Override + public String getQrPay(PayOrder order) { + return null; + } + + @Override + public Map microPay(PayOrder order) { + return null; + } + + /** + * 交易查询接口 + * + * @param tradeNo 支付平台订单号 + * @param outTradeNo 商户单号 + * @return 返回查询回来的结果集,支付方原值返回 + */ + @Override + public Map query(String tradeNo, String outTradeNo) { + JSONObject resp = getHttpRequestTemplate().getForObject(getReqUrl(PayPalTransactionType.ORDERS_GET), authHeader(), JSONObject.class, tradeNo); + return resp; + } + + @Override + public Map close(String tradeNo, String outTradeNo) { + return null; + } + + + /** + * 申请退款接口 + * + * @param refundOrder 退款订单信息 + * @return 返回支付方申请退款后的结果 + */ + @Override + public RefundResult refund(RefundOrder refundOrder) { + + JSONObject ordersCaptureInfo = getHttpRequestTemplate().getForObject(getReqUrl(PayPalTransactionType.ORDERS_CAPTURE), authHeader(), JSONObject.class, refundOrder.getTradeNo()); + if (!"COMPLETED".equals(ordersCaptureInfo.getString("status"))) { + return new PayPalRefundResult(ordersCaptureInfo, refundOrder.getTradeNo()); + } + + String captureId = ordersCaptureInfo.getJSONArray("purchaseUnits").getJSONObject(0).getJSONObject("payments").getJSONArray("captures").getJSONObject(0).getString("id"); + JSONObject request = new JSONObject(); + Money amount = new Money(); + if (null == refundOrder.getCurType()) { + refundOrder.setCurType(DefaultCurType.USD); + } + amount.setCurrencyCode(refundOrder.getCurType().getType()); + amount.value(Util.conversionAmount(refundOrder.getRefundAmount()).toString()); + request.put("amount", amount); + request.put("note_to_payer", refundOrder.getDescription()); + request.put("invoiceId", refundOrder.getOutTradeNo()); + + + HttpStringEntity httpEntity = new HttpStringEntity(request.toJSONString(), ContentType.APPLICATION_JSON); + httpEntity.setHeaders(authHeader()); + JSONObject resp = getHttpRequestTemplate().postForObject(getReqUrl(PayPalTransactionType.REFUND), httpEntity, JSONObject.class, captureId); + PayPalRefundResult payPalRefundResult = new PayPalRefundResult(resp, refundOrder.getTradeNo()); + refundOrder.setRefundNo(payPalRefundResult.getRefundNo()); + return payPalRefundResult; + } + + /** + * 查询退款 + * + * @param refundOrder 退款订单单号信息 + * @return 返回支付方查询退款后的结果 + */ + @Override + public Map refundquery(RefundOrder refundOrder) { + JSONObject resp = getHttpRequestTemplate().getForObject(getReqUrl(PayPalTransactionType.REFUND_GET), authHeader(), JSONObject.class, refundOrder.getRefundNo()); + return resp; + } + + @Override + public Map downloadbill(Date billDate, String billType) { + return Collections.emptyMap(); + } + + + /** + * 将请求参数或者请求流转化为 Map + * + * @param parameterMap 请求参数 + * @param is 请求流 + * @return 获得回调的请求参数 + */ + @Override + public Map getParameter2Map(Map parameterMap, InputStream is) { + + Map params = new LinkedHashMap<>(); + for (Map.Entry entry : parameterMap.entrySet()) { + String name = entry.getKey(); + String[] values = entry.getValue(); + String valueStr = ""; + for (int i = 0, len = values.length; i < len; i++) { + valueStr += (i == len - 1) ? values[i] : values[i] + ","; + } + if (StringUtils.isNotEmpty(payConfigStorage.getInputCharset()) && !valueStr.matches("\\w+")) { + try { + if (valueStr.equals(new String(valueStr.getBytes("iso8859-1"), "iso8859-1"))) { + valueStr = new String(valueStr.getBytes("iso8859-1"), payConfigStorage.getInputCharset()); + } + } + catch (UnsupportedEncodingException e) { + LOG.error(e); + } + } + params.put(name, valueStr); + } + return params; + } + + +} diff --git a/pay-java-paypal/src/main/java/com/egzosn/pay/paypal/v2/bean/PayPalOrder.java b/pay-java-paypal/src/main/java/com/egzosn/pay/paypal/v2/bean/PayPalOrder.java new file mode 100644 index 0000000..24093e1 --- /dev/null +++ b/pay-java-paypal/src/main/java/com/egzosn/pay/paypal/v2/bean/PayPalOrder.java @@ -0,0 +1,189 @@ +package com.egzosn.pay.paypal.v2.bean; + +import com.egzosn.pay.common.bean.CurType; +import com.egzosn.pay.common.bean.DefaultCurType; +import com.egzosn.pay.common.bean.PayOrder; +import com.egzosn.pay.paypal.v2.bean.order.ShippingDetail; + +/** + * PayPal付款订单 + * + * @author Egan + *
+ * email egzosn@gmail.com
+ * date 2021/1/12
+ * 
+ */ +public class PayPalOrder extends PayOrder { + + /** + * 该标签将覆盖PayPal网站上PayPal帐户中的公司名称 + */ + private String brandName; + /** + * 支付成功之后回调的页面 + */ + private String returnUrl; + + /** + * 取消支付的页面 + *
+     * 注意:这里不是异步回调的通知
+     * IPN 地址设置的路径:https://developer.paypal.com/developer/ipnSimulator/
+     * 
+ */ + private String cancelUrl; + /** + * LOGIN。当客户单击PayPal Checkout时,客户将被重定向到页面以登录PayPal并批准付款。 + * BILLING。当客户单击PayPal Checkout时,客户将被重定向到一个页面,以输入信用卡或借记卡以及完成购买所需的其他相关账单信息 + * NO_PREFERENCE。当客户单击“ PayPal Checkout”时,将根据其先前的交互方式将其重定向到页面以登录PayPal并批准付款,或重定向至页面以输入信用卡或借记卡以及完成购买所需的其他相关账单信息使用PayPal。 + * 默认值:NO_PREFERENCE + */ + private String landingPage = "NO_PREFERENCE"; + + /** + * GET_FROM_FILE。使用贝宝网站上客户提供的送货地址。 + * NO_SHIPPING。从PayPal网站编辑送货地址。推荐用于数字商品 + * SET_PROVIDED_ADDRESS。使用商家提供的地址。客户无法在PayPal网站上更改此地址 + */ + private String shippingPreference = "NO_SHIPPING"; + /** + * CONTINUE。将客户重定向到PayPal付款页面后,将出现“ 继续”按钮。当结帐流程启动时最终金额未知时,请使用此选项,并且您想将客户重定向到商家页面而不处理付款。 + * PAY_NOW。将客户重定向到PayPal付款页面后,出现“ 立即付款”按钮。当启动结帐时知道最终金额并且您要在客户单击“ 立即付款”时立即处理付款时,请使用此选项。 + */ + private String userAction = "CONTINUE"; + + private ShippingDetail shippingDetail; + /** + * API调用者为购买单元提供的外部ID。当您必须通过“补丁”更新订单时,需要多个购买单位。如果忽略该值,且订单只包含一个购买单元,PayPal将该值设置为' default '。 + */ + private String referenceId; + /** + * API调用者为该订单提供的外部发票号码。出现在付款人的交易历史记录和付款人收到的电子邮件中 + */ + private String invoiceId; + + + /** + * API调用者提供的外部ID。用于协调客户端交易与PayPal交易。出现在交易和结算报告中,但付款人不可见。 + * + * @return 外部ID + */ + public String getCustomId() { + return super.getOutTradeNo(); + } + + /** + * /** + * API调用者提供的外部ID。用于协调客户端交易与PayPal交易。出现在交易和结算报告中,但付款人不可见。 + * + * @param customId 外部ID + */ + public void setCustomId(String customId) { + super.setOutTradeNo(customId); + } + + + public String getDescription() { + return super.getSubject(); + } + + public void setDescription(String description) { + super.setSubject(description); + } + + + public String getCurrencyCode() { + CurType curType = super.getCurType(); + if (null == curType) { + curType = DefaultCurType.USD; + } + return curType.getType(); + } + + public void setCurrencyCode(CurType currencyCode) { + super.setCurType(currencyCode); + } + + public String getBrandName() { + return brandName; + } + + public void setBrandName(String brandName) { + super.addAttr("brandName", brandName); + this.brandName = brandName; + } + + public String getReturnUrl() { + return returnUrl; + } + + public void setReturnUrl(String returnUrl) { + super.addAttr("returnUrl", returnUrl); + this.returnUrl = returnUrl; + } + + public String getCancelUrl() { + return cancelUrl; + } + + public void setCancelUrl(String cancelUrl) { + super.addAttr("cancelUrl", cancelUrl); + this.cancelUrl = cancelUrl; + } + + public String getLandingPage() { + return landingPage; + } + + public void setLandingPage(String landingPage) { + super.addAttr("landingPage", landingPage); + this.landingPage = landingPage; + } + + public String getShippingPreference() { + return shippingPreference; + } + + public void setShippingPreference(String shippingPreference) { + super.addAttr("shippingPreference", shippingPreference); + this.shippingPreference = shippingPreference; + } + + public String getUserAction() { + + return userAction; + } + + public void setUserAction(String userAction) { + super.addAttr("userAction", userAction); + this.userAction = userAction; + } + + public ShippingDetail getShippingDetail() { + return shippingDetail; + } + + public void setShippingDetail(ShippingDetail shippingDetail) { + super.addAttr("shippingDetail", shippingDetail); + this.shippingDetail = shippingDetail; + } + + public String getReferenceId() { + return referenceId; + } + + public void setReferenceId(String referenceId) { + super.addAttr("referenceId", referenceId); + this.referenceId = referenceId; + } + + public String getInvoiceId() { + return invoiceId; + } + + public void setInvoiceId(String invoiceId) { + super.addAttr("invoiceId", referenceId); + this.invoiceId = invoiceId; + } +} diff --git a/pay-java-paypal/src/main/java/com/egzosn/pay/paypal/v2/bean/PayPalRefundResult.java b/pay-java-paypal/src/main/java/com/egzosn/pay/paypal/v2/bean/PayPalRefundResult.java new file mode 100644 index 0000000..551b8ca --- /dev/null +++ b/pay-java-paypal/src/main/java/com/egzosn/pay/paypal/v2/bean/PayPalRefundResult.java @@ -0,0 +1,119 @@ +package com.egzosn.pay.paypal.v2.bean; + +import java.math.BigDecimal; +import java.util.Map; + +import com.egzosn.pay.common.bean.BaseRefundResult; +import com.egzosn.pay.common.bean.CurType; + +/** + * @author Egan + *
+ * email egzosn@gmail.com
+ * date 2021/1/16
+ * 
+ */ +public class PayPalRefundResult extends BaseRefundResult { + + /** + * 支付平台订单号,交易号 + */ + private String tradeNo; + + public PayPalRefundResult(Map attrs, String tradeNo) { + super(attrs); + this.tradeNo = tradeNo; + } + + /** + * 获取退款请求结果状态码 + * + * @return 状态码 + */ + @Override + public String getCode() { + return getAttrString("state"); + } + + /** + * 获取退款请求结果状态提示信息 + * + * @return 提示信息 + */ + @Override + public String getMsg() { + return null; + } + + /** + * 返回业务结果状态码 + * + * @return 业务结果状态码 + */ + @Override + public String getResultCode() { + return getAttrString("state"); + } + + /** + * 返回业务结果状态提示信息 + * + * @return 业务结果状态提示信息 + */ + @Override + public String getResultMsg() { + return null; + } + + /** + * 退款金额 + * + * @return 退款金额 + */ + @Override + public BigDecimal getRefundFee() { + return null; + } + + /** + * 退款币种信息 + * + * @return 币种信息 + */ + @Override + public CurType getRefundCurrency() { + return null; + } + + /** + * 支付平台交易号 + * 发起支付时 支付平台(如支付宝)返回的交易订单号 + * + * @return 支付平台交易号 + */ + @Override + public String getTradeNo() { + return tradeNo; + } + + /** + * 支付订单号 + * 发起支付时,用户系统的订单号 + * + * @return 支付订单号 + */ + @Override + public String getOutTradeNo() { + return null; + } + + /** + * 商户退款单号 + * + * @return 商户退款单号 + */ + @Override + public String getRefundNo() { + return getAttrString("id"); + } +} diff --git a/pay-java-paypal/src/main/java/com/egzosn/pay/paypal/v2/bean/PayPalTransactionType.java b/pay-java-paypal/src/main/java/com/egzosn/pay/paypal/v2/bean/PayPalTransactionType.java new file mode 100644 index 0000000..30e8aa6 --- /dev/null +++ b/pay-java-paypal/src/main/java/com/egzosn/pay/paypal/v2/bean/PayPalTransactionType.java @@ -0,0 +1,74 @@ +package com.egzosn.pay.paypal.v2.bean; + +import com.egzosn.pay.common.bean.TransactionType; + +/** + * 贝宝交易类型 + *
+ * 说明交易类型主要用于支付接口调用参数所需
+ *
+ *
+ *
+ * 
+ * + * @author egan + *

+ * email egzosn@gmail.com + * date 2018/04/28 11:10 + */ +public enum PayPalTransactionType implements TransactionType { + /** + * 获取token + */ + AUTHORIZE("v1/oauth2/token"), + /** + * 付款 网页支付 + */ + CHECKOUT("v2/checkout/orders"), + /** + * 获取订单信息 + */ + ORDERS_GET("/v2/checkout/orders/{order_id}"), + /** + * 获取订单信息 + */ + ORDERS_CAPTURE("/v2/checkout/orders/{order_id}/capture"), + /** + * 获取订单信息 + */ + CAPTURE("/v2/payments/captures/{capture_id}"), + /** + * 退款 + */ + REFUND("/v2/payments/captures/{capture_id}/refund"), + + /** + * 退款查询 + */ + REFUND_GET("/v2/payments/refunds/{refund_id}"), + + ; + + + private String method; + + private PayPalTransactionType(String method) { + this.method = method; + } + + @Override + public String getType() { + return this.name(); + } + + /** + * 获取接口名称 + * + * @return 接口名称 + */ + @Override + public String getMethod() { + return this.method; + } + +} diff --git a/pay-java-paypal/src/main/java/com/egzosn/pay/paypal/v2/bean/order/AddressPortable.java b/pay-java-paypal/src/main/java/com/egzosn/pay/paypal/v2/bean/order/AddressPortable.java new file mode 100644 index 0000000..3abf421 --- /dev/null +++ b/pay-java-paypal/src/main/java/com/egzosn/pay/paypal/v2/bean/order/AddressPortable.java @@ -0,0 +1,220 @@ +package com.egzosn.pay.paypal.v2.bean.order; + +import com.alibaba.fastjson.annotation.JSONField; + +/** + * The portable international postal address. Maps to [AddressValidationMetadata](https://github.com/googlei18n/libaddressinput/wiki/AddressValidationMetadata) and HTML 5.1 [Autofilling form controls: the autocomplete attribute](https://www.w3.org/TR/html51/sec-forms.html#autofilling-form-controls-the-autocomplete-attribute). + */ +public class AddressPortable { + + public AddressPortable() { + } + + /** + * The first line of the address. For example, number or street. For example, `173 Drury Lane`. Required for data entry and compliance and risk checks. Must contain the full address. + */ + @JSONField(name = "address_line_1") + private String addressLine1; + + public String addressLine1() { + return addressLine1; + } + + public AddressPortable addressLine1(String addressLine1) { + this.addressLine1 = addressLine1; + return this; + } + + /** + * The second line of the address. For example, suite or apartment number. + */ + @JSONField(name = "address_line_2") + private String addressLine2; + + public String addressLine2() { + return addressLine2; + } + + public AddressPortable addressLine2(String addressLine2) { + this.addressLine2 = addressLine2; + return this; + } + + /** + * The third line of the address, if needed. For example, a street complement for Brazil, direction text, such as `next to Walmart`, or a landmark in an Indian address. + */ + @JSONField(name = "address_line_3") + private String addressLine3; + + public String addressLine3() { + return addressLine3; + } + + public AddressPortable addressLine3(String addressLine3) { + this.addressLine3 = addressLine3; + return this; + } + + /** + * The highest level sub-division in a country, which is usually a province, state, or ISO-3166-2 subdivision. Format for postal delivery. For example, `CA` and not `California`. Value, by country, is:

  • UK. A county.
  • US. A state.
  • Canada. A province.
  • Japan. A prefecture.
  • Switzerland. A kanton.
+ */ + @JSONField(name = "admin_area_1") + private String adminArea1; + + public String adminArea1() { + return adminArea1; + } + + public AddressPortable adminArea1(String adminArea1) { + this.adminArea1 = adminArea1; + return this; + } + + /** + * A city, town, or village. Smaller than `admin_area_level_1`. + */ + @JSONField(name = "admin_area_2") + private String adminArea2; + + public String adminArea2() { + return adminArea2; + } + + public AddressPortable adminArea2(String adminArea2) { + this.adminArea2 = adminArea2; + return this; + } + + /** + * A sub-locality, suburb, neighborhood, or district. Smaller than `admin_area_level_2`. Value is:
  • Brazil. Suburb, bairro, or neighborhood.
  • India. Sub-locality or district. Street name information is not always available but a sub-locality or district can be a very small area.
+ */ + @JSONField(name = "admin_area_3") + private String adminArea3; + + public String adminArea3() { + return adminArea3; + } + + public AddressPortable adminArea3(String adminArea3) { + this.adminArea3 = adminArea3; + return this; + } + + /** + * The neighborhood, ward, or district. Smaller than `admin_area_level_3` or `sub_locality`. Value is:
  • The postal sorting code for Guernsey and many French territories, such as French Guiana.
  • The fine-grained administrative levels in China.
+ */ + @JSONField(name = "admin_area_4") + private String adminArea4; + + public String adminArea4() { + return adminArea4; + } + + public AddressPortable adminArea4(String adminArea4) { + this.adminArea4 = adminArea4; + return this; + } + + /** + * REQUIRED + * The [two-character ISO 3166-1 code](/docs/integration/direct/rest/country-codes/) that identifies the country or region.
Note: The country code for Great Britain is GB and not UK as used in the top-level domain names for that country. Use the `C2` country code for China worldwide for comparable uncontrolled price (CUP) method, bank card, and cross-border transactions.
+ */ + @JSONField(name = "country_code") + private String countryCode; + + public String countryCode() { + return countryCode; + } + + public AddressPortable countryCode(String countryCode) { + this.countryCode = countryCode; + return this; + } + + /** + * The postal code, which is the zip code or equivalent. Typically required for countries with a postal code or an equivalent. See [postal code](https://en.wikipedia.org/wiki/Postal_code). + */ + @JSONField(name = "postal_code") + private String postalCode; + + public String postalCode() { + return postalCode; + } + + public AddressPortable postalCode(String postalCode) { + this.postalCode = postalCode; + return this; + } + + public String getAddressLine1() { + return addressLine1; + } + + public void setAddressLine1(String addressLine1) { + this.addressLine1 = addressLine1; + } + + public String getAddressLine2() { + return addressLine2; + } + + public void setAddressLine2(String addressLine2) { + this.addressLine2 = addressLine2; + } + + public String getAddressLine3() { + return addressLine3; + } + + public void setAddressLine3(String addressLine3) { + this.addressLine3 = addressLine3; + } + + public String getAdminArea1() { + return adminArea1; + } + + public void setAdminArea1(String adminArea1) { + this.adminArea1 = adminArea1; + } + + public String getAdminArea2() { + return adminArea2; + } + + public void setAdminArea2(String adminArea2) { + this.adminArea2 = adminArea2; + } + + public String getAdminArea3() { + return adminArea3; + } + + public void setAdminArea3(String adminArea3) { + this.adminArea3 = adminArea3; + } + + public String getAdminArea4() { + return adminArea4; + } + + public void setAdminArea4(String adminArea4) { + this.adminArea4 = adminArea4; + } + + public String getCountryCode() { + return countryCode; + } + + public void setCountryCode(String countryCode) { + this.countryCode = countryCode; + } + + public String getPostalCode() { + return postalCode; + } + + public void setPostalCode(String postalCode) { + this.postalCode = postalCode; + } +} diff --git a/pay-java-paypal/src/main/java/com/egzosn/pay/paypal/v2/bean/order/ApplicationContext.java b/pay-java-paypal/src/main/java/com/egzosn/pay/paypal/v2/bean/order/ApplicationContext.java new file mode 100644 index 0000000..53118d7 --- /dev/null +++ b/pay-java-paypal/src/main/java/com/egzosn/pay/paypal/v2/bean/order/ApplicationContext.java @@ -0,0 +1,147 @@ +package com.egzosn.pay.paypal.v2.bean.order; + + +import com.alibaba.fastjson.annotation.JSONField; + +public class ApplicationContext { + + /** + * 该标签将覆盖PayPal网站上PayPal帐户中的公司名称 + */ + @JSONField(name = "brand_name") + private String brandName; + + @JSONField(name = "cancel_url") + private String cancelUrl; + /** + * LOGIN。当客户单击PayPal Checkout时,客户将被重定向到页面以登录PayPal并批准付款。 + * BILLING。当客户单击PayPal Checkout时,客户将被重定向到一个页面,以输入信用卡或借记卡以及完成购买所需的其他相关账单信息 + * NO_PREFERENCE。当客户单击“ PayPal Checkout”时,将根据其先前的交互方式将其重定向到页面以登录PayPal并批准付款,或重定向至页面以输入信用卡或借记卡以及完成购买所需的其他相关账单信息使用PayPal。 + * 默认值:NO_PREFERENCE + */ + @JSONField(name = "landing_page") + private String landingPage = "NO_PREFERENCE"; + + + @JSONField(name = "return_url") + private String returnUrl; + /** + * GET_FROM_FILE。使用贝宝网站上客户提供的送货地址。 + * NO_SHIPPING。从PayPal网站编辑送货地址。推荐用于数字商品 + * SET_PROVIDED_ADDRESS。使用商家提供的地址。客户无法在PayPal网站上更改此地址 + */ + @JSONField(name = "shipping_preference") + private String shippingPreference = "NO_SHIPPING"; + /** + * CONTINUE。将客户重定向到PayPal付款页面后,将出现“ 继续”按钮。当结帐流程启动时最终金额未知时,请使用此选项,并且您想将客户重定向到商家页面而不处理付款。 + * PAY_NOW。将客户重定向到PayPal付款页面后,出现“ 立即付款”按钮。当启动结帐时知道最终金额并且您要在客户单击“ 立即付款”时立即处理付款时,请使用此选项。 + */ + @JSONField(name = "user_action") + private String userAction = "CONTINUE"; + + public ApplicationContext() { + } + + + public String brandName() { + return brandName; + } + + public ApplicationContext brandName(String brandName) { + this.brandName = brandName; + return this; + } + + public String cancelUrl() { + return this.cancelUrl; + } + + public ApplicationContext cancelUrl(String cancelUrl) { + this.cancelUrl = cancelUrl; + return this; + } + + public String landingPage() { + return this.landingPage; + } + + public ApplicationContext landingPage(String landingPage) { + this.landingPage = landingPage; + return this; + } + + public String returnUrl() { + return this.returnUrl; + } + + public ApplicationContext returnUrl(String returnUrl) { + this.returnUrl = returnUrl; + return this; + } + + public String shippingPreference() { + return this.shippingPreference; + } + + public ApplicationContext shippingPreference(String shippingPreference) { + this.shippingPreference = shippingPreference; + return this; + } + + public String userAction() { + return this.userAction; + } + + public ApplicationContext userAction(String userAction) { + this.userAction = userAction; + return this; + } + + public String getBrandName() { + return brandName; + } + + public void setBrandName(String brandName) { + this.brandName = brandName; + } + + public String getCancelUrl() { + return cancelUrl; + } + + public void setCancelUrl(String cancelUrl) { + this.cancelUrl = cancelUrl; + } + + public String getLandingPage() { + return landingPage; + } + + public void setLandingPage(String landingPage) { + this.landingPage = landingPage; + } + + public String getReturnUrl() { + return returnUrl; + } + + public void setReturnUrl(String returnUrl) { + this.returnUrl = returnUrl; + } + + public String getShippingPreference() { + return shippingPreference; + } + + public void setShippingPreference(String shippingPreference) { + this.shippingPreference = shippingPreference; + } + + public String getUserAction() { + return userAction; + } + + public void setUserAction(String userAction) { + this.userAction = userAction; + } +} diff --git a/pay-java-paypal/src/main/java/com/egzosn/pay/paypal/v2/bean/order/Money.java b/pay-java-paypal/src/main/java/com/egzosn/pay/paypal/v2/bean/order/Money.java new file mode 100644 index 0000000..1ed4274 --- /dev/null +++ b/pay-java-paypal/src/main/java/com/egzosn/pay/paypal/v2/bean/order/Money.java @@ -0,0 +1,37 @@ +package com.egzosn.pay.paypal.v2.bean.order; + +import com.alibaba.fastjson.annotation.JSONField; + +public class Money { + @JSONField(name = "currency_code") + private String currencyCode; + @JSONField(name = "value") + private String value; + + public String getCurrencyCode() { + return currencyCode; + } + + public void setCurrencyCode(String currencyCode) { + this.currencyCode = currencyCode; + } + + public String getValue() { + return value; + } + + public void setValue(String value) { + this.value = value; + } + + public Money currencyCode(String currencyCode) { + this.currencyCode = currencyCode; + return this; + } + + + public Money value(String value) { + this.value = value; + return this; + } +} \ No newline at end of file diff --git a/pay-java-paypal/src/main/java/com/egzosn/pay/paypal/v2/bean/order/Name.java b/pay-java-paypal/src/main/java/com/egzosn/pay/paypal/v2/bean/order/Name.java new file mode 100644 index 0000000..f86d543 --- /dev/null +++ b/pay-java-paypal/src/main/java/com/egzosn/pay/paypal/v2/bean/order/Name.java @@ -0,0 +1,174 @@ +package com.egzosn.pay.paypal.v2.bean.order; + +import com.alibaba.fastjson.annotation.JSONField; + +/** + * The name of the party. + */ +public class Name { + + // Required default constructor + public Name() { + } + + /** + * DEPRECATED. The party's alternate name. Can be a business name, nickname, or any other name that cannot be split into first, last name. Required when the party is a business. + */ + @JSONField(name = "alternate_full_name") + private String alternateFullName; + + public String alternateFullName() { + return alternateFullName; + } + + public Name alternateFullName(String alternateFullName) { + this.alternateFullName = alternateFullName; + return this; + } + + /** + * When the party is a person, the party's full name. + */ + @JSONField(name = "full_name") + private String fullName; + + public String fullName() { + return fullName; + } + + public Name fullName(String fullName) { + this.fullName = fullName; + return this; + } + + /** + * When the party is a person, the party's given, or first, name. + */ + @JSONField(name = "given_name") + private String givenName; + + public String givenName() { + return givenName; + } + + public Name givenName(String givenName) { + this.givenName = givenName; + return this; + } + + /** + * When the party is a person, the party's middle name. Use also to store multiple middle names including the patronymic, or father's, middle name. + */ + @JSONField(name = "middle_name") + private String middleName; + + public String middleName() { + return middleName; + } + + public Name middleName(String middleName) { + this.middleName = middleName; + return this; + } + + /** + * The prefix, or title, to the party's name. + */ + @JSONField(name = "prefix") + private String prefix; + + public String prefix() { + return prefix; + } + + public Name prefix(String prefix) { + this.prefix = prefix; + return this; + } + + /** + * The suffix for the party's name. + */ + @JSONField(name = "suffix") + private String suffix; + + public String suffix() { + return suffix; + } + + public Name suffix(String suffix) { + this.suffix = suffix; + return this; + } + + /** + * When the party is a person, the party's surname or family name. Also known as the last name. Required when the party is a person. Use also to store multiple surnames including the matronymic, or mother's, surname. + */ + @JSONField(name = "surname") + private String surname; + + public String surname() { + return surname; + } + + public Name surname(String surname) { + this.surname = surname; + return this; + } + + public String getAlternateFullName() { + return alternateFullName; + } + + public void setAlternateFullName(String alternateFullName) { + this.alternateFullName = alternateFullName; + } + + public String getFullName() { + return fullName; + } + + public void setFullName(String fullName) { + this.fullName = fullName; + } + + public String getGivenName() { + return givenName; + } + + public void setGivenName(String givenName) { + this.givenName = givenName; + } + + public String getMiddleName() { + return middleName; + } + + public void setMiddleName(String middleName) { + this.middleName = middleName; + } + + public String getPrefix() { + return prefix; + } + + public void setPrefix(String prefix) { + this.prefix = prefix; + } + + public String getSuffix() { + return suffix; + } + + public void setSuffix(String suffix) { + this.suffix = suffix; + } + + public String getSurname() { + return surname; + } + + public void setSurname(String surname) { + this.surname = surname; + } +} diff --git a/pay-java-paypal/src/main/java/com/egzosn/pay/paypal/v2/bean/order/OrderRequest.java b/pay-java-paypal/src/main/java/com/egzosn/pay/paypal/v2/bean/order/OrderRequest.java new file mode 100644 index 0000000..7b75492 --- /dev/null +++ b/pay-java-paypal/src/main/java/com/egzosn/pay/paypal/v2/bean/order/OrderRequest.java @@ -0,0 +1,43 @@ +package com.egzosn.pay.paypal.v2.bean.order; + +import java.util.List; + +import com.alibaba.fastjson.annotation.JSONField; + +public class OrderRequest { + @JSONField(name = "application_context") + private ApplicationContext applicationContext; + @JSONField(name = "intent") + private String checkoutPaymentIntent; + + @JSONField(name = + "purchase_units" + ) + private List purchaseUnits; + + + public ApplicationContext getApplicationContext() { + return applicationContext; + } + + public void setApplicationContext(ApplicationContext applicationContext) { + this.applicationContext = applicationContext; + } + + public String getCheckoutPaymentIntent() { + return checkoutPaymentIntent; + } + + public void setCheckoutPaymentIntent(String checkoutPaymentIntent) { + this.checkoutPaymentIntent = checkoutPaymentIntent; + } + + + public List getPurchaseUnits() { + return purchaseUnits; + } + + public void setPurchaseUnits(List purchaseUnits) { + this.purchaseUnits = purchaseUnits; + } +} \ No newline at end of file diff --git a/pay-java-paypal/src/main/java/com/egzosn/pay/paypal/v2/bean/order/PurchaseUnitRequest.java b/pay-java-paypal/src/main/java/com/egzosn/pay/paypal/v2/bean/order/PurchaseUnitRequest.java new file mode 100644 index 0000000..591e884 --- /dev/null +++ b/pay-java-paypal/src/main/java/com/egzosn/pay/paypal/v2/bean/order/PurchaseUnitRequest.java @@ -0,0 +1,141 @@ +package com.egzosn.pay.paypal.v2.bean.order; + +import com.alibaba.fastjson.annotation.JSONField; + +public class PurchaseUnitRequest { + @JSONField(name = "amount") + private Money money; + @JSONField(name = "custom_id") + private String customId; + @JSONField(name = "description") + private String description; + @JSONField(name = "invoice_id") + private String invoiceId; + @JSONField(name = "reference_id") + private String referenceId; + + @JSONField(name = "soft_descriptor") + private String softDescriptor; + /** + * The shipping details. + */ + @JSONField(name = "shipping") + private ShippingDetail shippingDetail; + + public Money money() { + return this.money; + } + + public PurchaseUnitRequest money(Money money) { + this.money = money; + return this; + } + + public String customId() { + return this.customId; + } + + public PurchaseUnitRequest customId(String customId) { + this.customId = customId; + return this; + } + + public String description() { + return this.description; + } + + public PurchaseUnitRequest description(String description) { + this.description = description; + return this; + } + + public String invoiceId() { + return this.invoiceId; + } + + public PurchaseUnitRequest invoiceId(String invoiceId) { + this.invoiceId = invoiceId; + return this; + } + + + public String referenceId() { + return this.referenceId; + } + + public PurchaseUnitRequest referenceId(String referenceId) { + this.referenceId = referenceId; + return this; + } + + + public String softDescriptor() { + return this.softDescriptor; + } + + public PurchaseUnitRequest softDescriptor(String softDescriptor) { + this.softDescriptor = softDescriptor; + return this; + } + + public PurchaseUnitRequest shippingDetail(ShippingDetail shippingDetail) { + this.shippingDetail = shippingDetail; + return this; + } + + public Money getMoney() { + return money; + } + + public void setMoney(Money money) { + this.money = money; + } + + public String getCustomId() { + return customId; + } + + public void setCustomId(String customId) { + this.customId = customId; + } + + public String getDescription() { + return description; + } + + public void setDescription(String description) { + this.description = description; + } + + public String getInvoiceId() { + return invoiceId; + } + + public void setInvoiceId(String invoiceId) { + this.invoiceId = invoiceId; + } + + public String getReferenceId() { + return referenceId; + } + + public void setReferenceId(String referenceId) { + this.referenceId = referenceId; + } + + public String getSoftDescriptor() { + return softDescriptor; + } + + public void setSoftDescriptor(String softDescriptor) { + this.softDescriptor = softDescriptor; + } + + public ShippingDetail getShippingDetail() { + return shippingDetail; + } + + public void setShippingDetail(ShippingDetail shippingDetail) { + this.shippingDetail = shippingDetail; + } +} \ No newline at end of file diff --git a/pay-java-paypal/src/main/java/com/egzosn/pay/paypal/v2/bean/order/ShippingDetail.java b/pay-java-paypal/src/main/java/com/egzosn/pay/paypal/v2/bean/order/ShippingDetail.java new file mode 100644 index 0000000..6561fc1 --- /dev/null +++ b/pay-java-paypal/src/main/java/com/egzosn/pay/paypal/v2/bean/order/ShippingDetail.java @@ -0,0 +1,59 @@ +package com.egzosn.pay.paypal.v2.bean.order; + +import com.alibaba.fastjson.annotation.JSONField; + +/** + * The shipping details. + */ +public class ShippingDetail { + + // Required default constructor + public ShippingDetail() { + } + + /** + * The portable international postal address. Maps to [AddressValidationMetadata](https://github.com/googlei18n/libaddressinput/wiki/AddressValidationMetadata) and HTML 5.1 [Autofilling form controls: the autocomplete attribute](https://www.w3.org/TR/html51/sec-forms.html#autofilling-form-controls-the-autocomplete-attribute). + */ + @JSONField(name = "address") + private AddressPortable addressPortable; + + public AddressPortable addressPortable() { + return addressPortable; + } + + public ShippingDetail addressPortable(AddressPortable addressPortable) { + this.addressPortable = addressPortable; + return this; + } + + /** + * The name of the party. + */ + @JSONField(name = "name") + private Name name; + + public Name name() { + return name; + } + + public ShippingDetail name(Name name) { + this.name = name; + return this; + } + + public AddressPortable getAddressPortable() { + return addressPortable; + } + + public void setAddressPortable(AddressPortable addressPortable) { + this.addressPortable = addressPortable; + } + + public Name getName() { + return name; + } + + public void setName(Name name) { + this.name = name; + } +} -- Gitee From 5d46be5829eccbbaff57d4d1e56978fffa05a0d0 Mon Sep 17 00:00:00 2001 From: egzosn Date: Sun, 17 Jan 2021 23:23:43 +0800 Subject: [PATCH 174/299] paypal v2 --- .../controller/PayPalV2PayController.java | 20 +++++++++++++++++-- .../pay/paypal/v2/api/PayPalPayService.java | 7 +++++-- 2 files changed, 23 insertions(+), 4 deletions(-) diff --git a/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/PayPalV2PayController.java b/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/PayPalV2PayController.java index 125c215..95abfd1 100644 --- a/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/PayPalV2PayController.java +++ b/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/PayPalV2PayController.java @@ -4,6 +4,7 @@ package com.egzosn.pay.demo.controller; import java.io.IOException; import java.io.InputStream; import java.math.BigDecimal; +import java.util.Map; import javax.annotation.PostConstruct; import javax.servlet.http.HttpServletRequest; @@ -99,10 +100,22 @@ public class PayPalV2PayController { order.setDescription(" description "); order.setTradeNo("paypal 平台的单号, 支付下单返回的单号"); order.setRefundAmount(BigDecimal.valueOf(0.01)); - return service.refund(order); + RefundResult refundResult = service.refund(order); + System.out.println("退款成功之后返回退款单号:" + refundResult.getRefundNo()); + return refundResult; } - + /** + * 查询退款 + * + * @return 返回支付方查询退款后的结果 + */ + @RequestMapping("refundquery") + public Map refundquery() { + RefundOrder order = new RefundOrder(); + order.setRefundNo("退款成功之后返回的退款单号"); + return service.refundquery(order); + } /** * 注意:这里不是异步回调的通知 IPN 地址设置的路径:https://developer.paypal.com/developer/ipnSimulator/ * PayPal确认付款调用的接口 @@ -115,6 +128,7 @@ public class PayPalV2PayController { @GetMapping(value = "payBackBefore.json") public String payBackBefore(HttpServletRequest request) throws IOException { try (InputStream is = request.getInputStream()) { + // 参数解析与校验 https://developer.paypal.com/docs/api-basics/notifications/ipn/IPNIntro/#id08CKFJ00JYK if (service.verify(service.getParameter2Map(request.getParameterMap(), is))) { // TODO 这里进行成功后的订单业务处理 // TODO 返回成功付款页面,这个到时候再做一个漂亮的页面显示,并使用前后端分离的模式 @@ -129,6 +143,7 @@ public class PayPalV2PayController { /** * 支付回调地址 * 注意:这里不是异步回调的通知 IPN 地址设置的路径:https://developer.paypal.com/developer/ipnSimulator/ + * 参数解析与校验 https://developer.paypal.com/docs/api-basics/notifications/ipn/IPNIntro/#id08CKFJ00JYK * * @param request 请求 * @return 结果 @@ -140,6 +155,7 @@ public class PayPalV2PayController { @RequestMapping(value = "payBack.json") public String payBack(HttpServletRequest request) throws IOException { //业务处理在对应的PayMessageHandler里面处理,在哪里设置PayMessageHandler,详情查看com.egzosn.pay.common.api.PayService.setPayMessageHandler() + // 参数解析与校验 https://developer.paypal.com/docs/api-basics/notifications/ipn/IPNIntro/#id08CKFJ00JYK return service.payBack(request.getParameterMap(), request.getInputStream()).toMessage(); } diff --git a/pay-java-paypal/src/main/java/com/egzosn/pay/paypal/v2/api/PayPalPayService.java b/pay-java-paypal/src/main/java/com/egzosn/pay/paypal/v2/api/PayPalPayService.java index a6c72ed..a35aa73 100644 --- a/pay-java-paypal/src/main/java/com/egzosn/pay/paypal/v2/api/PayPalPayService.java +++ b/pay-java-paypal/src/main/java/com/egzosn/pay/paypal/v2/api/PayPalPayService.java @@ -145,6 +145,7 @@ public class PayPalPayService extends BasePayService { /** * IPN 地址设置的路径:https://developer.paypal.com/developer/ipnSimulator/ + * 参数解析与校验 https://developer.paypal.com/docs/api-basics/notifications/ipn/IPNIntro/#id08CKFJ00JYK * 1.Check that the payment_status is Completed. * 2.If the payment_status is Completed, check the txn_id against the previous PayPal transaction that you processed to ensure the IPN message is not a duplicate. * 3.Check that the receiver_email is an email address registered in your PayPal account. @@ -240,7 +241,7 @@ public class PayPalPayService extends BasePayService { /** * 返回创建的订单信息 - * + * 订单信息与接口地址 https://developer.paypal.com/docs/api/orders/v2 * @param order 支付订单 * @return 订单信息 * @see PayOrder 支付订单信息 @@ -350,6 +351,8 @@ public class PayPalPayService extends BasePayService { /** * 申请退款接口 * + * 1.需要通过支付单号获取captureId 详情: https://developer.paypal.com/docs/api/payments/v2/#captures + * 2.通过captureId发起退款 详情: https://developer.paypal.com/docs/api/payments/v2/#captures_refund * @param refundOrder 退款订单信息 * @return 返回支付方申请退款后的结果 */ @@ -384,7 +387,7 @@ public class PayPalPayService extends BasePayService { /** * 查询退款 - * + * 通过退款id获取退款信息 详情:https://developer.paypal.com/docs/api/payments/v2/#refunds * @param refundOrder 退款订单单号信息 * @return 返回支付方查询退款后的结果 */ -- Gitee From 6055833c6cc4d5472d75ccbfef813e9ca04e7066 Mon Sep 17 00:00:00 2001 From: wangshirui Date: Wed, 27 Jan 2021 14:27:20 +0800 Subject: [PATCH 175/299] =?UTF-8?q?[pay-java-wx]=20Update:=20jsapi?= =?UTF-8?q?=E6=94=AF=E4=BB=98=E5=8F=82=E6=95=B0=E6=9B=B4=E6=96=B0=201.=20t?= =?UTF-8?q?imestamp=E9=9C=80=E8=A6=81=E6=98=AF=E5=AD=97=E7=AC=A6=E4=B8=B2?= =?UTF-8?q?=202.=20=E7=AD=BE=E5=90=8D=E5=AD=97=E6=AE=B5=E5=90=8D=E4=B8=BAp?= =?UTF-8?q?aySign;=20ref:=20https://pay.weixin.qq.com/wiki/doc/api/jsapi.p?= =?UTF-8?q?hp=3Fchapter=3D7=5F7&index=3D6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/main/java/com/egzosn/pay/wx/api/WxPayService.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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 200279f..e407211 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 @@ -274,7 +274,7 @@ public class WxPayService extends BasePayService implements if (WxTransactionType.JSAPI == order.getTransactionType()) { params.put("signType", payConfigStorage.getSignType()); params.put("appId", payConfigStorage.getAppid()); - params.put("timeStamp", System.currentTimeMillis() / 1000); + params.put("timeStamp", System.currentTimeMillis() / 1000 + ""); params.put("nonceStr", result.get(NONCE_STR)); params.put("package", "prepay_id=" + result.get("prepay_id")); } @@ -287,7 +287,7 @@ public class WxPayService extends BasePayService implements params.put("package", "Sign=WXPay"); } String paySign = createSign(SignUtils.parameterText(params), payConfigStorage.getInputCharset()); - params.put(SIGN, paySign); + params.put(WxTransactionType.JSAPI.equals(order.getTransactionType()) ? "paySign" : SIGN, paySign); return params; } throw new PayErrorException(new WxPayError(result.getString(RETURN_CODE), result.getString(RETURN_MSG_CODE), "Invalid sign value")); -- Gitee From a63eefd78d1251d7ef5099751959bf618c49fef4 Mon Sep 17 00:00:00 2001 From: egzosn Date: Mon, 22 Feb 2021 21:53:36 +0800 Subject: [PATCH 176/299] paypal v2 --- .../pay/paypal/v2/api/PayPalPayService.java | 119 ++++++++++++++++-- .../paypal/v2/api/PayPalPayServiceInf.java | 103 +++++++++++++++ .../paypal/v2/bean/PayPalTransactionType.java | 6 +- 3 files changed, 212 insertions(+), 16 deletions(-) create mode 100644 pay-java-paypal/src/main/java/com/egzosn/pay/paypal/v2/api/PayPalPayServiceInf.java diff --git a/pay-java-paypal/src/main/java/com/egzosn/pay/paypal/v2/api/PayPalPayService.java b/pay-java-paypal/src/main/java/com/egzosn/pay/paypal/v2/api/PayPalPayService.java index a35aa73..1521049 100644 --- a/pay-java-paypal/src/main/java/com/egzosn/pay/paypal/v2/api/PayPalPayService.java +++ b/pay-java-paypal/src/main/java/com/egzosn/pay/paypal/v2/api/PayPalPayService.java @@ -55,7 +55,7 @@ import com.egzosn.pay.paypal.v2.bean.order.ShippingDetail; * email egzosn@gmail.com * date 2021-1-16 ‏‎22:15:09 */ -public class PayPalPayService extends BasePayService { +public class PayPalPayService extends BasePayService implements PayPalPayServiceInf { /** * 沙箱环境 @@ -242,6 +242,7 @@ public class PayPalPayService extends BasePayService { /** * 返回创建的订单信息 * 订单信息与接口地址 https://developer.paypal.com/docs/api/orders/v2 + * * @param order 支付订单 * @return 订单信息 * @see PayOrder 支付订单信息 @@ -338,8 +339,7 @@ public class PayPalPayService extends BasePayService { */ @Override public Map query(String tradeNo, String outTradeNo) { - JSONObject resp = getHttpRequestTemplate().getForObject(getReqUrl(PayPalTransactionType.ORDERS_GET), authHeader(), JSONObject.class, tradeNo); - return resp; + return getHttpRequestTemplate().getForObject(getReqUrl(PayPalTransactionType.ORDERS_GET), authHeader(), JSONObject.class, tradeNo); } @Override @@ -347,24 +347,115 @@ public class PayPalPayService extends BasePayService { return null; } + /** + * 注意:最好在付款成功之后回调时进行调用 + * 确认订单并返回确认后订单信息 + * 注意:此方法一个订单只能调用一次, 建议在支付回调时进行调用 + * 这里主要用来获取captureId使用,后续退款,查订单等等使用,用来替换下单返回的id + * 详情: https://developer.paypal.com/docs/api/orders/v2/#orders_capture + * + * @param tradeNo paypal下单成功之后返回的订单号 + * @return 确认后订单信息 + * 获取captureId + */ + @Override + public Map ordersCapture(String tradeNo) { + JSONObject ordersCaptureInfo = getHttpRequestTemplate().getForObject(getReqUrl(PayPalTransactionType.ORDERS_CAPTURE), authHeader(), JSONObject.class, tradeNo); +// String captureId = ordersCaptureInfo.getJSONArray("purchaseUnits").getJSONObject(0).getJSONObject("payments").getJSONArray("captures").getJSONObject(0).getString("id"); + return ordersCaptureInfo; + } + + /** + * 确认订单之后获取订单信息 + * 详情: https://developer.paypal.com/docs/api/payments/v2/#captures_get + * + * @param captureId 确认付款订单之后生成的id + * @return 确认付款订单详情 + *
+     *     {
+     *   "id": "2GG279541U471931P",
+     *   "status": "COMPLETED",
+     *   "status_details": {},
+     *   "amount": {
+     *     "total": "10.99",
+     *     "currency": "USD"
+     *   },
+     *   "final_capture": true,
+     *   "seller_protection": {
+     *     "status": "ELIGIBLE",
+     *     "dispute_categories": [
+     *       "ITEM_NOT_RECEIVED",
+     *       "UNAUTHORIZED_TRANSACTION"
+     *     ]
+     *   },
+     *   "seller_receivable_breakdown": {
+     *     "gross_amount": {
+     *       "total": "10.99",
+     *       "currency": "USD"
+     *     },
+     *     "paypal_fee": {
+     *       "value": "0.33",
+     *       "currency": "USD"
+     *     },
+     *     "net_amount": {
+     *       "value": "10.66",
+     *       "currency": "USD"
+     *     },
+     *     "receivable_amount": {
+     *       "currency_code": "CNY",
+     *       "value": "59.26"
+     *     },
+     *     "paypal_fee_in_receivable_currency": {
+     *       "currency_code": "CNY",
+     *       "value": "1.13"
+     *     },
+     *     "exchange_rate": {
+     *       "source_currency": "USD",
+     *       "target_currency": "CNY",
+     *       "value": "5.9483297432325"
+     *     }
+     *   },
+     *   "invoice_id": "INVOICE-123",
+     *   "create_time": "2017-09-11T23:24:01Z",
+     *   "update_time": "2017-09-11T23:24:01Z",
+     *   "links": [
+     *     {
+     *       "href": "https://api-m.paypal.com/v2/payments/captures/2GG279541U471931P",
+     *       "rel": "self",
+     *       "method": "GET"
+     *     },
+     *     {
+     *       "href": "https://api-m.paypal.com/v2/payments/captures/2GG279541U471931P/refund",
+     *       "rel": "refund",
+     *       "method": "POST"
+     *     },
+     *     {
+     *       "href": "https://api-m.paypal.com/v2/payments/authorizations/0VF52814937998046",
+     *       "rel": "up",
+     *       "method": "GET"
+     *     }
+     *   ]
+     * }
+     *
+     * 
+ * + */ + @Override + public Map getCapture(String captureId) { + JSONObject ordersCaptureInfo = getHttpRequestTemplate().getForObject(getReqUrl(PayPalTransactionType.GET_CAPTURE), authHeader(), JSONObject.class, captureId); + return ordersCaptureInfo; + } /** * 申请退款接口 + * 通过captureId发起退款 详情: https://developer.paypal.com/docs/api/payments/v2/#captures_refund + * captureId 详情{@link #ordersCapture(String)} * - * 1.需要通过支付单号获取captureId 详情: https://developer.paypal.com/docs/api/payments/v2/#captures - * 2.通过captureId发起退款 详情: https://developer.paypal.com/docs/api/payments/v2/#captures_refund * @param refundOrder 退款订单信息 * @return 返回支付方申请退款后的结果 */ @Override public RefundResult refund(RefundOrder refundOrder) { - - JSONObject ordersCaptureInfo = getHttpRequestTemplate().getForObject(getReqUrl(PayPalTransactionType.ORDERS_CAPTURE), authHeader(), JSONObject.class, refundOrder.getTradeNo()); - if (!"COMPLETED".equals(ordersCaptureInfo.getString("status"))) { - return new PayPalRefundResult(ordersCaptureInfo, refundOrder.getTradeNo()); - } - - String captureId = ordersCaptureInfo.getJSONArray("purchaseUnits").getJSONObject(0).getJSONObject("payments").getJSONArray("captures").getJSONObject(0).getString("id"); JSONObject request = new JSONObject(); Money amount = new Money(); if (null == refundOrder.getCurType()) { @@ -376,9 +467,10 @@ public class PayPalPayService extends BasePayService { request.put("note_to_payer", refundOrder.getDescription()); request.put("invoiceId", refundOrder.getOutTradeNo()); - HttpStringEntity httpEntity = new HttpStringEntity(request.toJSONString(), ContentType.APPLICATION_JSON); httpEntity.setHeaders(authHeader()); + //TODO: 这里TradeNo为{@link #ordersCapture} 确认订单之后的captureId + String captureId = refundOrder.getTradeNo(); JSONObject resp = getHttpRequestTemplate().postForObject(getReqUrl(PayPalTransactionType.REFUND), httpEntity, JSONObject.class, captureId); PayPalRefundResult payPalRefundResult = new PayPalRefundResult(resp, refundOrder.getTradeNo()); refundOrder.setRefundNo(payPalRefundResult.getRefundNo()); @@ -388,6 +480,7 @@ public class PayPalPayService extends BasePayService { /** * 查询退款 * 通过退款id获取退款信息 详情:https://developer.paypal.com/docs/api/payments/v2/#refunds + * * @param refundOrder 退款订单单号信息 * @return 返回支付方查询退款后的结果 */ diff --git a/pay-java-paypal/src/main/java/com/egzosn/pay/paypal/v2/api/PayPalPayServiceInf.java b/pay-java-paypal/src/main/java/com/egzosn/pay/paypal/v2/api/PayPalPayServiceInf.java new file mode 100644 index 0000000..01435ab --- /dev/null +++ b/pay-java-paypal/src/main/java/com/egzosn/pay/paypal/v2/api/PayPalPayServiceInf.java @@ -0,0 +1,103 @@ +package com.egzosn.pay.paypal.v2.api; + +import java.util.Map; + +/** + * PayPal 支付接口 + * + * @author Egan + *
+ * email egzosn@gmail.com
+ * date 2021/2/20
+ * 
+ */ +public interface PayPalPayServiceInf { + + /** + * 注意:最好在付款成功之后回调时进行调用 + * 确认付款订单并返回确认后订单信息 + * 注意:此方法一个订单只能调用一次, 建议在支付回调时进行调用 + * 这里主要用来获取captureId使用,后续退款,查订单等等使用,用来替换下单返回的id + * 详情: https://developer.paypal.com/docs/api/orders/v2/#orders_capture + * + * @param tradeNo paypal下单成功之后返回的订单号 + * @return 确认付款后订单信息 + */ + Map ordersCapture(String tradeNo); + + /** + * 确认订单之后获取订单信息 + * 详情: https://developer.paypal.com/docs/api/payments/v2/#captures_get + * + * @param captureId 确认付款订单之后生成的id + * @return 确认付款订单详情 + *
+     *     {
+     *   "id": "2GG279541U471931P",
+     *   "status": "COMPLETED",
+     *   "status_details": {},
+     *   "amount": {
+     *     "total": "10.99",
+     *     "currency": "USD"
+     *   },
+     *   "final_capture": true,
+     *   "seller_protection": {
+     *     "status": "ELIGIBLE",
+     *     "dispute_categories": [
+     *       "ITEM_NOT_RECEIVED",
+     *       "UNAUTHORIZED_TRANSACTION"
+     *     ]
+     *   },
+     *   "seller_receivable_breakdown": {
+     *     "gross_amount": {
+     *       "total": "10.99",
+     *       "currency": "USD"
+     *     },
+     *     "paypal_fee": {
+     *       "value": "0.33",
+     *       "currency": "USD"
+     *     },
+     *     "net_amount": {
+     *       "value": "10.66",
+     *       "currency": "USD"
+     *     },
+     *     "receivable_amount": {
+     *       "currency_code": "CNY",
+     *       "value": "59.26"
+     *     },
+     *     "paypal_fee_in_receivable_currency": {
+     *       "currency_code": "CNY",
+     *       "value": "1.13"
+     *     },
+     *     "exchange_rate": {
+     *       "source_currency": "USD",
+     *       "target_currency": "CNY",
+     *       "value": "5.9483297432325"
+     *     }
+     *   },
+     *   "invoice_id": "INVOICE-123",
+     *   "create_time": "2017-09-11T23:24:01Z",
+     *   "update_time": "2017-09-11T23:24:01Z",
+     *   "links": [
+     *     {
+     *       "href": "https://api-m.paypal.com/v2/payments/captures/2GG279541U471931P",
+     *       "rel": "self",
+     *       "method": "GET"
+     *     },
+     *     {
+     *       "href": "https://api-m.paypal.com/v2/payments/captures/2GG279541U471931P/refund",
+     *       "rel": "refund",
+     *       "method": "POST"
+     *     },
+     *     {
+     *       "href": "https://api-m.paypal.com/v2/payments/authorizations/0VF52814937998046",
+     *       "rel": "up",
+     *       "method": "GET"
+     *     }
+     *   ]
+     * }
+     *
+     * 
+ */ + Map getCapture(String captureId); +} diff --git a/pay-java-paypal/src/main/java/com/egzosn/pay/paypal/v2/bean/PayPalTransactionType.java b/pay-java-paypal/src/main/java/com/egzosn/pay/paypal/v2/bean/PayPalTransactionType.java index 30e8aa6..2cde37d 100644 --- a/pay-java-paypal/src/main/java/com/egzosn/pay/paypal/v2/bean/PayPalTransactionType.java +++ b/pay-java-paypal/src/main/java/com/egzosn/pay/paypal/v2/bean/PayPalTransactionType.java @@ -30,13 +30,13 @@ public enum PayPalTransactionType implements TransactionType { */ ORDERS_GET("/v2/checkout/orders/{order_id}"), /** - * 获取订单信息 + * 确认订单并返回确认后订单信息 */ ORDERS_CAPTURE("/v2/checkout/orders/{order_id}/capture"), /** - * 获取订单信息 + * 获取确认后订单信息 */ - CAPTURE("/v2/payments/captures/{capture_id}"), + GET_CAPTURE("/v2/payments/captures/{capture_id}"), /** * 退款 */ -- Gitee From db4c051007e85c717a56121ee7d36f6c95726723 Mon Sep 17 00:00:00 2001 From: egzosn Date: Tue, 23 Feb 2021 01:39:09 +0800 Subject: [PATCH 177/299] =?UTF-8?q?=E8=B4=A6=E5=8D=95=E7=B1=BB=E5=9E=8B?= =?UTF-8?q?=E5=AE=9A=E4=B9=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/egzosn/pay/common/api/PayService.java | 13 +++++++- .../com/egzosn/pay/common/bean/BillType.java | 33 +++++++++++++++++++ .../com/egzosn/pay/common/util/DateUtils.java | 1 + 3 files changed, 46 insertions(+), 1 deletion(-) create mode 100644 pay-java-common/src/main/java/com/egzosn/pay/common/bean/BillType.java diff --git a/pay-java-common/src/main/java/com/egzosn/pay/common/api/PayService.java b/pay-java-common/src/main/java/com/egzosn/pay/common/api/PayService.java index 5fce5a9..fa8df25 100644 --- a/pay-java-common/src/main/java/com/egzosn/pay/common/api/PayService.java +++ b/pay-java-common/src/main/java/com/egzosn/pay/common/api/PayService.java @@ -5,6 +5,7 @@ import java.io.InputStream; import java.util.Date; import java.util.Map; +import com.egzosn.pay.common.bean.BillType; import com.egzosn.pay.common.bean.MethodType; import com.egzosn.pay.common.bean.PayMessage; import com.egzosn.pay.common.bean.PayOrder; @@ -275,11 +276,21 @@ public interface PayService { * 下载对账单 * * @param billDate 账单时间:日账单格式为yyyy-MM-dd,月账单格式为yyyy-MM。 - * @param billType 账单类型,商户通过接口或商户经开放平台授权后其所属服务商通过接口可以获取以下账单类型:trade、signcustomer;trade指商户基于支付宝交易收单的业务账单;signcustomer是指基于商户支付宝余额收入及支出等资金变动的帐务账单; + * @param billType 账单类型 * @return 返回支付方下载对账单的结果 */ + @Deprecated Map downloadbill(Date billDate, String billType); + /** + * 下载对账单 + * + * @param billDate 账单时间:日账单格式为yyyy-MM-dd,月账单格式为yyyy-MM。 + * @param billType 账单类型 + * @return 返回支付方下载对账单的结果 + */ + Map downloadBill(Date billDate, BillType billType); + /** * 下载对账单 * diff --git a/pay-java-common/src/main/java/com/egzosn/pay/common/bean/BillType.java b/pay-java-common/src/main/java/com/egzosn/pay/common/bean/BillType.java new file mode 100644 index 0000000..793463a --- /dev/null +++ b/pay-java-common/src/main/java/com/egzosn/pay/common/bean/BillType.java @@ -0,0 +1,33 @@ +package com.egzosn.pay.common.bean; + +/** + * 账单类型 + * @author Egan + * @email egzosn@gmail.com + * @date 2021/2/22 + */ +public interface BillType { + /** + * 获取类型名称 + * @return 类型 + */ + String getType(); + + /** + * 获取类型对应的日期格式化表达式 + * @return 日期格式化表达式 + */ + String getDatePattern(); + + /** + * 获取压缩类型 + * @return 压缩类型 + */ + String getTarType(); + + /** + * 自定义属性 + * @return 自定义属性 + */ + String getCustom(); +} diff --git a/pay-java-common/src/main/java/com/egzosn/pay/common/util/DateUtils.java b/pay-java-common/src/main/java/com/egzosn/pay/common/util/DateUtils.java index 63c2d64..fa6b4e6 100644 --- a/pay-java-common/src/main/java/com/egzosn/pay/common/util/DateUtils.java +++ b/pay-java-common/src/main/java/com/egzosn/pay/common/util/DateUtils.java @@ -60,6 +60,7 @@ public final class DateUtils { public static final String YYYYMMDDHHMMSS = "yyyyMMddHHmmss"; public static final String MMDD = "MMdd"; public static final String YYYYMM = "yyyyMM"; + public static final String YYYY_MM = "yyyy-MM"; public static String formatDate(Date date, String pattern) { -- Gitee From b3540dc41fcbf4c9acfd5e5615aa47deca2590d6 Mon Sep 17 00:00:00 2001 From: egzosn Date: Tue, 23 Feb 2021 01:39:35 +0800 Subject: [PATCH 178/299] =?UTF-8?q?=E8=B4=A6=E5=8D=95=E7=B1=BB=E5=9E=8B?= =?UTF-8?q?=E5=AE=9E=E7=8E=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/egzosn/pay/ali/api/AliPayService.java | 33 +++-- .../egzosn/pay/ali/bean/AliPayBillType.java | 89 +++++++++++++ .../egzosn/pay/baidu/api/BaiduPayService.java | 38 ++++-- .../egzosn/pay/baidu/bean/BaiduBillType.java | 101 +++++++++++++++ .../egzosn/pay/fuiou/api/FuiouPayService.java | 14 +++ .../pay/payoneer/api/PayoneerPayService.java | 14 ++- .../pay/paypal/api/PayPalPayService.java | 8 ++ .../pay/paypal/v2/api/PayPalPayService.java | 7 ++ .../egzosn/pay/union/api/UnionPayService.java | 33 +++++ .../pay/union/bean/UnionPayBillType.java | 12 ++ .../wx/youdian/api/WxYouDianPayService.java | 9 ++ .../com/egzosn/pay/wx/api/WxBillService.java | 5 + .../java/com/egzosn/pay/wx/api/WxConst.java | 1 + .../com/egzosn/pay/wx/api/WxPayService.java | 71 +++++++++-- .../com/egzosn/pay/wx/bean/WxPayBillType.java | 119 ++++++++++++++++++ .../egzosn/pay/yiji/api/YiJiPayService.java | 15 +++ 16 files changed, 538 insertions(+), 31 deletions(-) create mode 100644 pay-java-ali/src/main/java/com/egzosn/pay/ali/bean/AliPayBillType.java create mode 100644 pay-java-baidu/src/main/java/com/egzosn/pay/baidu/bean/BaiduBillType.java create mode 100644 pay-java-union/src/main/java/com/egzosn/pay/union/bean/UnionPayBillType.java create mode 100644 pay-java-wx/src/main/java/com/egzosn/pay/wx/bean/WxPayBillType.java 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 4e15880..1fb432b 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 @@ -1,6 +1,7 @@ package com.egzosn.pay.ali.api; import java.util.Date; +import java.util.HashMap; import java.util.LinkedHashMap; import java.util.Map; import java.util.TreeMap; @@ -19,6 +20,7 @@ import static com.egzosn.pay.ali.bean.AliPayConst.SUCCESS_CODE; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONObject; +import com.egzosn.pay.ali.bean.AliPayBillType; import com.egzosn.pay.ali.bean.AliPayConst; import com.egzosn.pay.ali.bean.AliPayMessage; import com.egzosn.pay.ali.bean.AliRefundResult; @@ -27,6 +29,7 @@ import com.egzosn.pay.ali.bean.AliTransferType; import com.egzosn.pay.ali.bean.CertEnvironment; import com.egzosn.pay.ali.bean.OrderSettle; import com.egzosn.pay.common.api.BasePayService; +import com.egzosn.pay.common.bean.BillType; import com.egzosn.pay.common.bean.MethodType; import com.egzosn.pay.common.bean.Order; import com.egzosn.pay.common.bean.PayMessage; @@ -560,26 +563,42 @@ public class AliPayService extends BasePayService { /** * 目前只支持日账单 * - * @param billDate 账单类型,商户通过接口或商户经开放平台授权后其所属服务商通过接口可以获取以下账单类型:trade、signcustomer;trade指商户基于支付宝交易收单的业务账单;signcustomer是指基于商户支付宝余额收入及支出等资金变动的帐务账单; - * @param billType 账单时间:日账单格式为yyyy-MM-dd,月账单格式为yyyy-MM。 + * @param billDate 账单时间:日账单格式为yyyy-MM-dd,月账单格式为yyyy-MM。 + * @param billType 账单类型,商户通过接口或商户经开放平台授权后其所属服务商通过接口可以获取以下账单类型:trade、signcustomer;trade指商户基于支付宝交易收单的业务账单;signcustomer是指基于商户支付宝余额收入及支出等资金变动的帐务账单; * @return 返回支付方下载对账单的结果 */ + @Deprecated @Override public Map downloadbill(Date billDate, String billType) { + + return this.downloadBill(billDate, "trade".equals(billType) ? AliPayBillType.TRADE_DAY : AliPayBillType.SIGNCUSTOMER_DAY); + } + + /** + * 下载对账单 + * + * @param billDate 账单时间:日账单格式为yyyy-MM-dd,月账单格式为yyyy-MM。 + * @param billType 账单类型,商户通过接口或商户经开放平台授权后其所属服务商通过接口可以获取以下账单类型:trade、signcustomer;trade指商户基于支付宝交易收单的业务账单;signcustomer是指基于商户支付宝余额收入及支出等资金变动的帐务账单; + * @return 返回支付方下载对账单的结果 + */ + public Map downloadBill(Date billDate, BillType billType) { //获取公共参数 Map parameters = getPublicParameters(AliTransactionType.DOWNLOADBILL); Map bizContent = new TreeMap<>(); - bizContent.put("bill_type", billType); + bizContent.put("bill_type", billType.getType()); //目前只支持日账单 - bizContent.put("bill_date", DateUtils.formatDay(billDate)); + bizContent.put("bill_date", DateUtils.formatDate(billDate, billType.getDatePattern())); //设置请求参数的集合 - parameters.put(BIZ_CONTENT, JSON.toJSONString(bizContent)); + final String bizContentStr = JSON.toJSONString(bizContent); + parameters.put(BIZ_CONTENT, bizContentStr); //设置签名 setSign(parameters); - return requestTemplate.getForObject(getReqUrl() + "?" + UriVariables.getMapToParameters(parameters), JSONObject.class); - } + Map bizContentMap = new HashMap(1); + parameters.put(BIZ_CONTENT, bizContentStr); + return requestTemplate.postForObject(getReqUrl() + "?" + UriVariables.getMapToParameters(parameters), bizContentMap, JSONObject.class); + } /** * @param tradeNoOrBillDate 支付平台订单号或者账单类型, 具体请 diff --git a/pay-java-ali/src/main/java/com/egzosn/pay/ali/bean/AliPayBillType.java b/pay-java-ali/src/main/java/com/egzosn/pay/ali/bean/AliPayBillType.java new file mode 100644 index 0000000..a91cea6 --- /dev/null +++ b/pay-java-ali/src/main/java/com/egzosn/pay/ali/bean/AliPayBillType.java @@ -0,0 +1,89 @@ +package com.egzosn.pay.ali.bean; + +import com.egzosn.pay.common.bean.BillType; +import com.egzosn.pay.common.util.DateUtils; + +/** + * 支付宝账单类型 + * @author Egan + *
+ * email egzosn@gmail.com
+ * date 2021/2/22
+ * 
+ */ +public enum AliPayBillType implements BillType { + /** + * 商户基于支付宝交易收单的业务账单;每日账单 + */ + TRADE_DAY("trade", DateUtils.YYYY_MM_DD), + /** + * 商户基于支付宝交易收单的业务账单;每月账单 + */ + TRADE_MONTH("trade", DateUtils.YYYY_MM), + /** + * 基于商户支付宝余额收入及支出等资金变动的帐务账单;每日账单 + */ + SIGNCUSTOMER_DAY("signcustomer", DateUtils.YYYY_MM_DD), + /** + * 基于商户支付宝余额收入及支出等资金变动的帐务账单;每月账单 + */ + SIGNCUSTOMER_MONTH("signcustomer", DateUtils.YYYY_MM), + + ; + + /** + * 账单类型 + */ + private String type; + /** + * 日期格式化表达式 + */ + private String datePattern; + + AliPayBillType(String type, String datePattern) { + this.type = type; + this.datePattern = datePattern; + } + + /** + * 获取类型名称 + * + * @return 类型 + */ + @Override + public String getType() { + return type; + } + + /** + * 获取类型对应的日期格式化表达式 + * + * @return 日期格式化表达式 + */ + @Override + public String getDatePattern() { + return datePattern; + } + + /** + * 获取压缩类型 + * + * @return 压缩类型 + */ + @Override + public String getTarType() { + return null; + } + + /** + * 自定义属性 + * + * @return 自定义属性 + */ + @Override + public String getCustom() { + return null; + } + + +} diff --git a/pay-java-baidu/src/main/java/com/egzosn/pay/baidu/api/BaiduPayService.java b/pay-java-baidu/src/main/java/com/egzosn/pay/baidu/api/BaiduPayService.java index 8c2095b..b94f00f 100644 --- a/pay-java-baidu/src/main/java/com/egzosn/pay/baidu/api/BaiduPayService.java +++ b/pay-java-baidu/src/main/java/com/egzosn/pay/baidu/api/BaiduPayService.java @@ -7,12 +7,14 @@ import java.util.Map; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONObject; +import com.egzosn.pay.baidu.bean.BaiduBillType; import com.egzosn.pay.baidu.bean.BaiduPayOrder; import com.egzosn.pay.baidu.bean.BaiduTransactionType; import com.egzosn.pay.baidu.bean.type.AuditStatus; import com.egzosn.pay.baidu.util.Asserts; import com.egzosn.pay.common.api.BasePayService; import com.egzosn.pay.common.bean.BaseRefundResult; +import com.egzosn.pay.common.bean.BillType; import com.egzosn.pay.common.bean.CurType; import com.egzosn.pay.common.bean.MethodType; import com.egzosn.pay.common.bean.PayMessage; @@ -402,34 +404,48 @@ public class BaiduPayService extends BasePayService { } /** - * 下载资金账单 + * 下载订单对账单 * * @param billDate 账单时间:日账单格式为yyyy-MM-dd * @param accessToken 用户token * @return 对账单 */ + @Deprecated @Override public Map downloadbill(Date billDate, String accessToken) { + return downloadBill(billDate, new BaiduBillType(accessToken, BaiduTransactionType.DOWNLOAD_ORDER_BILL.name())); + } + + /** + * 下载对账单 + * + * @param billDate 账单时间:日账单格式为yyyy-MM-dd,月账单格式为yyyy-MM。 + * @param billType 账单类型 {@link BaiduBillType} + * @return 返回支付方下载对账单的结果 + */ + public Map downloadBill(Date billDate, BillType billType) { Map parameters = new HashMap<>(); - parameters.put("access_token", accessToken); - parameters.put("billTime", DateUtils.formatDay(billDate)); - return requestTemplate.getForObject(String.format("%s?%s", getReqUrl(BaiduTransactionType.DOWNLOAD_BILL), + parameters.put("access_token", billType.getCustom()); + parameters.put("billTime", DateUtils.formatDate(billDate, billType.getDatePattern())); + final String type = billType.getType(); + BaiduTransactionType transactionType = BaiduTransactionType.DOWNLOAD_ORDER_BILL; + if (BaiduTransactionType.DOWNLOAD_BILL.name().equals(type)) { + transactionType = BaiduTransactionType.DOWNLOAD_BILL; + } + return requestTemplate.getForObject(String.format("%s?%s", getReqUrl(transactionType), UriVariables.getMapToParameters(parameters)), JSONObject.class); } /** - * 下载订单对账单 + * 下载资金账单 * * @param billDate 账单时间:日账单格式为yyyy-MM-dd * @param accessToken 用户token * @return 账单结果 */ - public Map downloadOrderBill(Date billDate, String accessToken) { - Map parameters = new HashMap<>(); - parameters.put("access_token", accessToken); - parameters.put("billTime", DateUtils.formatDay(billDate)); - return requestTemplate.getForObject(String.format("%s?%s", getReqUrl(BaiduTransactionType.DOWNLOAD_ORDER_BILL), - UriVariables.getMapToParameters(parameters)), JSONObject.class); + @Deprecated + public Map downloadMoneyBill(Date billDate, String accessToken) { + return downloadBill(billDate, new BaiduBillType(accessToken, BaiduTransactionType.DOWNLOAD_BILL.name())); } /** diff --git a/pay-java-baidu/src/main/java/com/egzosn/pay/baidu/bean/BaiduBillType.java b/pay-java-baidu/src/main/java/com/egzosn/pay/baidu/bean/BaiduBillType.java new file mode 100644 index 0000000..f3c5387 --- /dev/null +++ b/pay-java-baidu/src/main/java/com/egzosn/pay/baidu/bean/BaiduBillType.java @@ -0,0 +1,101 @@ +package com.egzosn.pay.baidu.bean; + +import com.egzosn.pay.common.bean.BillType; +import com.egzosn.pay.common.util.DateUtils; +import com.egzosn.pay.common.util.str.StringUtils; + +/** + * 百度 + * @author Egan + *
+ * email egzosn@gmail.com
+ * date 2021/2/22
+ * 
+ */ +public class BaiduBillType implements BillType { + /** + * 用户accessToken + */ + private String accessToken; + /** + + * 值为DOWNLOAD_ORDER_BILL与DOWNLOAD_BILL + * com.egzosn.pay.baidu.bean.BaiduTransactionType#DOWNLOAD_ORDER_BILL + * com.egzosn.pay.baidu.bean.BaiduTransactionType#DOWNLOAD_BILL + */ + private String type; + + private String datePattern; + + public String getAccessToken() { + return accessToken; + } + + public void setAccessToken(String accessToken) { + this.accessToken = accessToken; + } + + public void setType(String type) { + this.type = type; + } + + /** + * 获取类型名称 + * + * @return 类型 + */ + @Override + public String getType() { + return type; + } + + /** + * 获取类型对应的日期格式化表达式 + * + * @return 日期格式化表达式 + */ + @Override + public String getDatePattern() { + if (StringUtils.isEmpty(datePattern)){ + datePattern = DateUtils.YYYY_MM_DD; + } + return datePattern; + } + + public void setDatePattern(String datePattern) { + this.datePattern = datePattern; + } + + /** + * 获取压缩类型 + * + * @return 压缩类型 + */ + @Override + public String getTarType() { + return null; + } + + + /** + * 自定义属性 + * + * @return 自定义属性 + */ + @Override + public String getCustom() { + return accessToken; + } + + public BaiduBillType() { + } + + public BaiduBillType(String accessToken) { + this.accessToken = accessToken; + } + + public BaiduBillType(String accessToken, String type) { + this.accessToken = accessToken; + this.type = type; + } +} diff --git a/pay-java-fuiou/src/main/java/com/egzosn/pay/fuiou/api/FuiouPayService.java b/pay-java-fuiou/src/main/java/com/egzosn/pay/fuiou/api/FuiouPayService.java index 418af4b..c56638c 100644 --- a/pay-java-fuiou/src/main/java/com/egzosn/pay/fuiou/api/FuiouPayService.java +++ b/pay-java-fuiou/src/main/java/com/egzosn/pay/fuiou/api/FuiouPayService.java @@ -9,6 +9,7 @@ import java.util.Map; import com.alibaba.fastjson.JSONObject; import com.egzosn.pay.common.api.BasePayService; +import com.egzosn.pay.common.bean.BillType; import com.egzosn.pay.common.bean.MethodType; import com.egzosn.pay.common.bean.PayMessage; import com.egzosn.pay.common.bean.PayOrder; @@ -444,10 +445,23 @@ public class FuiouPayService extends BasePayService { * @param billType 账单类型,商户通过接口或商户经开放平台授权后其所属服务商通过接口可以获取以下账单类型:trade、signcustomer;trade指商户基于支付宝交易收单的业务账单;signcustomer是指基于商户支付宝余额收入及支出等资金变动的帐务账单; * @return 空 */ + @Deprecated @Override public Map downloadbill(Date billDate, String billType) { return Collections.emptyMap(); } + /** + * 下载对账单 + * + * @param billDate 账单时间:日账单格式为yyyy-MM-dd,月账单格式为yyyy-MM。 + * @param billType 账单类型,商户通过接口或商户经开放平台授权后其所属服务商通过接口可以获取以下账单类型:trade、signcustomer;trade指商户基于支付宝交易收单的业务账单;signcustomer是指基于商户支付宝余额收入及支出等资金变动的帐务账单; + * @return 空 + */ + @Override + public Map downloadBill(Date billDate, BillType billType) { + return Collections.emptyMap(); + } + } diff --git a/pay-java-payoneer/src/main/java/com/egzosn/pay/payoneer/api/PayoneerPayService.java b/pay-java-payoneer/src/main/java/com/egzosn/pay/payoneer/api/PayoneerPayService.java index 2b4645c..e706314 100644 --- a/pay-java-payoneer/src/main/java/com/egzosn/pay/payoneer/api/PayoneerPayService.java +++ b/pay-java-payoneer/src/main/java/com/egzosn/pay/payoneer/api/PayoneerPayService.java @@ -16,6 +16,7 @@ import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONObject; import com.egzosn.pay.common.api.BasePayService; import com.egzosn.pay.common.bean.BaseRefundResult; +import com.egzosn.pay.common.bean.BillType; import com.egzosn.pay.common.bean.CurType; import com.egzosn.pay.common.bean.DefaultCurType; import com.egzosn.pay.common.bean.MethodType; @@ -380,9 +381,20 @@ public class PayoneerPayService extends BasePayService im * @param billType 账单类型,商户通过接口或商户经开放平台授权后其所属服务商通过接口可以获取以下账单类型:trade、signcustomer;trade指商户基于支付宝交易收单的业务账单;signcustomer是指基于商户支付宝余额收入及支出等资金变动的帐务账单; * @return 返回支付方下载对账单的结果 */ + @Deprecated @Override public Map downloadbill(Date billDate, String billType) { - + return Collections.emptyMap(); + } + /** + * 下载对账单 + * + * @param billDate 账单时间:日账单格式为yyyy-MM-dd,月账单格式为yyyy-MM。 + * @param billType 账单类型,商户通过接口或商户经开放平台授权后其所属服务商通过接口可以获取以下账单类型:trade、signcustomer;trade指商户基于支付宝交易收单的业务账单;signcustomer是指基于商户支付宝余额收入及支出等资金变动的帐务账单; + * @return 空 + */ + @Override + public Map downloadBill(Date billDate, BillType billType) { return Collections.emptyMap(); } diff --git a/pay-java-paypal/src/main/java/com/egzosn/pay/paypal/api/PayPalPayService.java b/pay-java-paypal/src/main/java/com/egzosn/pay/paypal/api/PayPalPayService.java index e80c8a0..4a8d212 100644 --- a/pay-java-paypal/src/main/java/com/egzosn/pay/paypal/api/PayPalPayService.java +++ b/pay-java-paypal/src/main/java/com/egzosn/pay/paypal/api/PayPalPayService.java @@ -20,6 +20,7 @@ import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONObject; import com.egzosn.pay.common.api.BasePayService; import com.egzosn.pay.common.bean.BaseRefundResult; +import com.egzosn.pay.common.bean.BillType; import com.egzosn.pay.common.bean.CurType; import com.egzosn.pay.common.bean.DefaultCurType; import com.egzosn.pay.common.bean.MethodType; @@ -347,10 +348,17 @@ public class PayPalPayService extends BasePayService { return resp; } + @Deprecated @Override public Map downloadbill(Date billDate, String billType) { return Collections.emptyMap(); } + @Override + public Map downloadBill(Date billDate, BillType billType) { + return Collections.emptyMap(); + } + + } diff --git a/pay-java-paypal/src/main/java/com/egzosn/pay/paypal/v2/api/PayPalPayService.java b/pay-java-paypal/src/main/java/com/egzosn/pay/paypal/v2/api/PayPalPayService.java index 1521049..acf3d07 100644 --- a/pay-java-paypal/src/main/java/com/egzosn/pay/paypal/v2/api/PayPalPayService.java +++ b/pay-java-paypal/src/main/java/com/egzosn/pay/paypal/v2/api/PayPalPayService.java @@ -21,6 +21,7 @@ import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONArray; import com.alibaba.fastjson.JSONObject; import com.egzosn.pay.common.api.BasePayService; +import com.egzosn.pay.common.bean.BillType; import com.egzosn.pay.common.bean.CurType; import com.egzosn.pay.common.bean.DefaultCurType; import com.egzosn.pay.common.bean.MethodType; @@ -490,12 +491,18 @@ public class PayPalPayService extends BasePayService implem return resp; } + @Deprecated @Override public Map downloadbill(Date billDate, String billType) { return Collections.emptyMap(); } + public Map downloadBill(Date billDate, BillType billType) { + return Collections.emptyMap(); + } + + /** * 将请求参数或者请求流转化为 Map * diff --git a/pay-java-union/src/main/java/com/egzosn/pay/union/api/UnionPayService.java b/pay-java-union/src/main/java/com/egzosn/pay/union/api/UnionPayService.java index 5a68c8e..f6a146e 100644 --- a/pay-java-union/src/main/java/com/egzosn/pay/union/api/UnionPayService.java +++ b/pay-java-union/src/main/java/com/egzosn/pay/union/api/UnionPayService.java @@ -26,6 +26,7 @@ import java.util.TreeMap; import com.alibaba.fastjson.JSONObject; import com.egzosn.pay.common.api.BasePayService; +import com.egzosn.pay.common.bean.BillType; import com.egzosn.pay.common.bean.MethodType; import com.egzosn.pay.common.bean.PayMessage; import com.egzosn.pay.common.bean.PayOrder; @@ -661,6 +662,7 @@ public class UnionPayService extends BasePayService { * @param billType 账单类型 * @return 返回fileContent 请自行将数据落地 */ + @Deprecated @Override public Map downloadbill(Date billDate, String billType) { Map params = this.getCommonParam(); @@ -684,6 +686,37 @@ public class UnionPayService extends BasePayService { throw new PayErrorException(new PayException("failure", "验证签名失败", response.toString())); } + /** + * 下载对账单 + * + * @param billDate 账单时间 + * @param billType 账单类型 + * @return 返回fileContent 请自行将数据落地 + */ + @Override + public Map downloadBill(Date billDate, BillType billType) { + + Map params = this.getCommonParam(); + UnionTransactionType.FILE_TRANSFER.convertMap(params); + + params.put(SDKConstants.param_settleDate, DateUtils.formatDate(billDate, DateUtils.MMDD)); + params.put(SDKConstants.param_fileType, billType.getTarType()); + params.remove(SDKConstants.param_backUrl); + params.remove(SDKConstants.param_currencyCode); + this.setSign(params); + String responseStr = getHttpRequestTemplate().postForObject(this.getFileTransUrl(), params, String.class); + JSONObject response = UriVariables.getParametersToMap(responseStr); + if (this.verify(response)) { + if (SDKConstants.OK_RESP_CODE.equals(response.get(SDKConstants.param_respCode))) { + return response; + + } + throw new PayErrorException(new PayException(response.get(SDKConstants.param_respCode).toString(), response.get(SDKConstants.param_respMsg).toString(), response.toString())); + + } + throw new PayErrorException(new PayException("failure", "验证签名失败", response.toString())); + } + /** * 创建消息 diff --git a/pay-java-union/src/main/java/com/egzosn/pay/union/bean/UnionPayBillType.java b/pay-java-union/src/main/java/com/egzosn/pay/union/bean/UnionPayBillType.java new file mode 100644 index 0000000..ef03102 --- /dev/null +++ b/pay-java-union/src/main/java/com/egzosn/pay/union/bean/UnionPayBillType.java @@ -0,0 +1,12 @@ +package com.egzosn.pay.union.bean; + +/** + * 银联账单类型 + * @author Egan + *
+ * email egzosn@gmail.com
+ * date 2021/2/23
+ * 
+ */ +public enum UnionPayBillType { +} 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 2dcdfff..347e657 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 @@ -13,6 +13,7 @@ import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONObject; import com.egzosn.pay.common.api.BasePayService; import com.egzosn.pay.common.bean.BaseRefundResult; +import com.egzosn.pay.common.bean.BillType; import com.egzosn.pay.common.bean.CurType; import com.egzosn.pay.common.bean.MethodType; import com.egzosn.pay.common.bean.PayMessage; @@ -493,12 +494,20 @@ public class WxYouDianPayService extends BasePayService downloadbill(Date billDate, String billType) { return Collections.emptyMap(); } + + @Override + public Map downloadBill(Date billDate, BillType billType) { + return Collections.emptyMap(); + } + + public WxYouDianPayService(WxYouDianPayConfigStorage payConfigStorage) { super(payConfigStorage); } diff --git a/pay-java-wx/src/main/java/com/egzosn/pay/wx/api/WxBillService.java b/pay-java-wx/src/main/java/com/egzosn/pay/wx/api/WxBillService.java index eeed364..06d9f05 100644 --- a/pay-java-wx/src/main/java/com/egzosn/pay/wx/api/WxBillService.java +++ b/pay-java-wx/src/main/java/com/egzosn/pay/wx/api/WxBillService.java @@ -3,13 +3,18 @@ package com.egzosn.pay.wx.api; import java.util.Date; import java.util.Map; +import com.egzosn.pay.common.bean.BillType; + /** * 账单接口 + * * @author: faymanwang * email: 1057438332@qq.com * time: 2020/7/31 11:21 */ public interface WxBillService { + @Deprecated Map downloadbill(Date billDate, String billType, String path); + } diff --git a/pay-java-wx/src/main/java/com/egzosn/pay/wx/api/WxConst.java b/pay-java-wx/src/main/java/com/egzosn/pay/wx/api/WxConst.java index ac656b4..52ea2e9 100644 --- a/pay-java-wx/src/main/java/com/egzosn/pay/wx/api/WxConst.java +++ b/pay-java-wx/src/main/java/com/egzosn/pay/wx/api/WxConst.java @@ -32,6 +32,7 @@ public interface WxConst { String MCH_ID = "mch_id"; String NONCE_STR = "nonce_str"; String OUT_TRADE_NO = "out_trade_no"; + String GZIP = "GZIP"; } 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 e407211..8274cba 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 @@ -38,6 +38,7 @@ import static com.egzosn.pay.wx.bean.WxTransferType.TRANSFERS; import com.alibaba.fastjson.JSONObject; import com.egzosn.pay.common.api.BasePayService; +import com.egzosn.pay.common.bean.BillType; import com.egzosn.pay.common.bean.MethodType; import com.egzosn.pay.common.bean.PayMessage; import com.egzosn.pay.common.bean.PayOrder; @@ -58,6 +59,7 @@ import com.egzosn.pay.common.util.sign.SignUtils; import com.egzosn.pay.common.util.sign.encrypt.RSA2; import com.egzosn.pay.common.util.str.StringUtils; import com.egzosn.pay.wx.bean.RedpackOrder; +import com.egzosn.pay.wx.bean.WxPayBillType; import com.egzosn.pay.wx.bean.WxPayError; import com.egzosn.pay.wx.bean.WxPayMessage; import com.egzosn.pay.wx.bean.WxRefundResult; @@ -560,10 +562,15 @@ public class WxPayService extends BasePayService implements /** * 目前只支持日账单 * - * @param billDate 账单类型,商户通过接口或商户经开放平台授权后其所属服务商通过接口可以获取以下账单类型:trade、signcustomer;trade指商户基于支付宝交易收单的业务账单;signcustomer是指基于商户支付宝余额收入及支出等资金变动的帐务账单; - * @param billType 账单时间:日账单格式为yyyy-MM-dd,月账单格式为yyyy-MM。 + * @param billDate 下载对账单的日期,格式:20140603 + * @param billType 账单类型 + * ALL(默认值),返回当日所有订单信息(不含充值退款订单) + * SUCCESS,返回当日成功支付的订单(不含充值退款订单) + * REFUND,返回当日退款订单(不含充值退款订单) + * RECHARGE_REFUND,返回当日充值退款订单 * @return 返回支付方下载对账单的结果 */ + @Deprecated @Override public Map downloadbill(Date billDate, String billType) { Map parameters = getDownloadBillParam(billDate, billType, false); @@ -581,6 +588,43 @@ public class WxPayService extends BasePayService implements return ret; } + /** + * 目前只支持日账单 + * + * @param billDate 下载对账单的日期,格式:20140603 + * @param billType 账单类型 + * ALL(默认值),返回当日所有订单信息(不含充值退款订单) + * SUCCESS,返回当日成功支付的订单(不含充值退款订单) + * REFUND,返回当日退款订单(不含充值退款订单) + * RECHARGE_REFUND,返回当日充值退款订单 + * @return 返回支付方下载对账单的结果, 如果【账单类型】为gzip的话则返回值中key为data值为gzip的输入流 + */ + public Map downloadBill(Date billDate, BillType billType) { + //获取公共参数 + Map parameters = getPublicParameters(); + parameters.put("bill_type", billType); + //目前只支持日账单 + parameters.put("bill_date", DateUtils.formatDate(billDate, DateUtils.YYYYMMDD)); + setParameters(parameters, "tar_type", billType.getTarType()); + //设置签名 + setSign(parameters); + Map ret = new HashMap(3); + ret.put(RETURN_CODE, SUCCESS); + ret.put(RETURN_MSG_CODE, "ok"); + if (StringUtils.isEmpty(billType.getTarType())) { + String respStr = requestTemplate.postForObject(getReqUrl(WxTransactionType.DOWNLOADBILL), XML.getMap2Xml(parameters), String.class); + if (respStr.indexOf("<") == 0) { + return XML.toJSONObject(respStr); + } + ret.put("data", respStr); + return ret; + } + InputStream respStream = requestTemplate.postForObject(getReqUrl(WxTransactionType.DOWNLOADBILL), XML.getMap2Xml(parameters), InputStream.class); + ret.put("data", respStream); + return ret; + } + + /** * 目前只支持日账单,增加账单返回格式 * @@ -589,6 +633,7 @@ public class WxPayService extends BasePayService implements * @param path 账单返回格式 账单存储的基础路径,按月切割 * @return 返回支付方下载对账单的结果 */ + @Deprecated @Override public Map downloadbill(Date billDate, String billType, String path) { Map parameters = getDownloadBillParam(billDate, billType, true); @@ -622,6 +667,7 @@ public class WxPayService extends BasePayService implements * @return 解压后输入流 * @throws IOException IOException */ + @Deprecated public static InputStream uncompress(InputStream input) throws IOException { ByteArrayOutputStream out = new ByteArrayOutputStream(); GZIPInputStream ungzip = new GZIPInputStream(input); @@ -641,6 +687,7 @@ public class WxPayService extends BasePayService implements * @param inputStream 输入流 * @throws IOException IOException */ + @Deprecated private void writeToLocal(String destination, InputStream inputStream) throws IOException { @@ -651,7 +698,7 @@ public class WxPayService extends BasePayService implements if (!file.getParentFile().exists()) { boolean result = file.getParentFile().mkdirs(); if (!result) { - System.out.println("创建失败"); + LOG.warn("创建失败"); } } OutputStream out = new FileOutputStream(file); @@ -662,7 +709,7 @@ public class WxPayService extends BasePayService implements len += size; out.write(buf, 0, size); } - System.out.println("最终写入字节数大小:" + len); + LOG.info("最终写入字节数大小:" + len); inputStream.close(); out.close(); } @@ -677,6 +724,7 @@ public class WxPayService extends BasePayService implements * @param tarType 账单返回格式 默认返回流false ,gzip 时候true * @return */ + @Deprecated private Map getDownloadBillParam(Date billDate, String billType, boolean tarType) { //获取公共参数 Map parameters = getPublicParameters(); @@ -689,7 +737,6 @@ public class WxPayService extends BasePayService implements return parameters; } - /** * @param transactionIdOrBillDate 支付平台订单号或者账单日期, 具体请 类型为{@link String }或者 {@link Date },类型须强制限制,类型不对应则抛出异常{@link PayErrorException} * @param outTradeNoBillType 商户单号或者 账单类型 @@ -704,7 +751,7 @@ public class WxPayService extends BasePayService implements if (transactionType == WxTransactionType.DOWNLOADBILL) { if (transactionIdOrBillDate instanceof Date) { - return downloadbill((Date) transactionIdOrBillDate, outTradeNoBillType); + return downloadBill((Date) transactionIdOrBillDate, WxPayBillType.forType(outTradeNoBillType)); } throw new PayErrorException(new PayException(FAILURE, "非法类型异常:" + transactionIdOrBillDate.getClass())); } @@ -728,12 +775,12 @@ public class WxPayService extends BasePayService implements * @param order 转账订单 *
      *
-     *                           注意事项:
-     *                           ◆ 当返回错误码为“SYSTEMERROR”时,请不要更换商户订单号,一定要使用原商户订单号重试,否则可能造成重复支付等资金风险。
-     *                           ◆ XML具有可扩展性,因此返回参数可能会有新增,而且顺序可能不完全遵循此文档规范,如果在解析回包的时候发生错误,请商户务必不要换单重试,请商户联系客服确认付款情况。如果有新回包字段,会更新到此API文档中。
-     *                           ◆ 因为错误代码字段err_code的值后续可能会增加,所以商户如果遇到回包返回新的错误码,请商户务必不要换单重试,请商户联系客服确认付款情况。如果有新的错误码,会更新到此API文档中。
-     *                           ◆ 错误代码描述字段err_code_des只供人工定位问题时做参考,系统实现时请不要依赖这个字段来做自动化处理。
-     *                           
+ * 注意事项: + * ◆ 当返回错误码为“SYSTEMERROR”时,请不要更换商户订单号,一定要使用原商户订单号重试,否则可能造成重复支付等资金风险。 + * ◆ XML具有可扩展性,因此返回参数可能会有新增,而且顺序可能不完全遵循此文档规范,如果在解析回包的时候发生错误,请商户务必不要换单重试,请商户联系客服确认付款情况。如果有新回包字段,会更新到此API文档中。 + * ◆ 因为错误代码字段err_code的值后续可能会增加,所以商户如果遇到回包返回新的错误码,请商户务必不要换单重试,请商户联系客服确认付款情况。如果有新的错误码,会更新到此API文档中。 + * ◆ 错误代码描述字段err_code_des只供人工定位问题时做参考,系统实现时请不要依赖这个字段来做自动化处理。 + *
* @return 对应的转账结果 */ @Override diff --git a/pay-java-wx/src/main/java/com/egzosn/pay/wx/bean/WxPayBillType.java b/pay-java-wx/src/main/java/com/egzosn/pay/wx/bean/WxPayBillType.java new file mode 100644 index 0000000..1bdd111 --- /dev/null +++ b/pay-java-wx/src/main/java/com/egzosn/pay/wx/bean/WxPayBillType.java @@ -0,0 +1,119 @@ +package com.egzosn.pay.wx.bean; + +import com.egzosn.pay.common.bean.BillType; +import com.egzosn.pay.common.util.str.StringUtils; +import com.egzosn.pay.wx.api.WxConst; + +/** + * 支付宝账单类型 + * + * @author Egan + *
+ * email egzosn@gmail.com
+ * date 2021/2/22
+ * 
+ */ +public enum WxPayBillType implements BillType { + /** + * 商户基于支付宝交易收单的业务账单;每日账单 + */ + ALL("ALL"), + /** + * 商户基于支付宝交易收单的业务账单;每日账单 + */ + ALL_GZIP("ALL", WxConst.GZIP), + /** + * 商户基于支付宝交易收单的业务账单;每月账单 + */ + SUCCESS(WxConst.SUCCESS), + /** + * 商户基于支付宝交易收单的业务账单;每月账单 + */ + SUCCESS_GZIP(WxConst.SUCCESS, WxConst.GZIP), + /** + * 基于商户支付宝余额收入及支出等资金变动的帐务账单;每日账单 + */ + REFUND("REFUND"), + /** + * 基于商户支付宝余额收入及支出等资金变动的帐务账单;每日账单 + */ + REFUND_GZIP("REFUND", WxConst.GZIP), + /** + * 基于商户支付宝余额收入及支出等资金变动的帐务账单;每月账单 + */ + RECHARGE_REFUND("RECHARGE_REFUND"), + /** + * 基于商户支付宝余额收入及支出等资金变动的帐务账单;每月账单 + */ + RECHARGE_REFUND_GZIP("RECHARGE_REFUND", WxConst.GZIP); + + /** + * 账单类型 + */ + private String type; + /** + * 日期格式化表达式 + */ + private String tarType; + + WxPayBillType(String type) { + this.type = type; + } + + + WxPayBillType(String type, String tarType) { + this.type = type; + this.tarType = tarType; + } + + /** + * 获取类型名称 + * + * @return 类型 + */ + @Override + public String getType() { + return type; + } + + /** + * 获取类型对应的日期格式化表达式 + * + * @return 日期格式化表达式 + */ + @Override + public String getDatePattern() { + return null; + } + + /** + * 获取压缩类型 + * + * @return 压缩类型 + */ + @Override + public String getTarType() { + return tarType; + } + + + /** + * 自定义属性 + * + * @return 自定义属性 + */ + @Override + public String getCustom() { + return null; + } + + public static WxPayBillType forType(String type){ + for (WxPayBillType wxPayBillType : WxPayBillType.values()){ + if (wxPayBillType.getType().equals(type) && StringUtils.isEmpty(wxPayBillType.getTarType())){ + return wxPayBillType; + } + } + return WxPayBillType.ALL; + } + +} diff --git a/pay-java-yiji/src/main/java/com/egzosn/pay/yiji/api/YiJiPayService.java b/pay-java-yiji/src/main/java/com/egzosn/pay/yiji/api/YiJiPayService.java index 1ef9daa..5d42fbd 100644 --- a/pay-java-yiji/src/main/java/com/egzosn/pay/yiji/api/YiJiPayService.java +++ b/pay-java-yiji/src/main/java/com/egzosn/pay/yiji/api/YiJiPayService.java @@ -9,6 +9,7 @@ import java.util.TreeMap; import com.alibaba.fastjson.JSONObject; import com.egzosn.pay.common.api.BasePayService; import com.egzosn.pay.common.bean.BaseRefundResult; +import com.egzosn.pay.common.bean.BillType; import com.egzosn.pay.common.bean.CurType; import com.egzosn.pay.common.bean.DefaultCurType; import com.egzosn.pay.common.bean.MethodType; @@ -372,12 +373,26 @@ public class YiJiPayService extends BasePayService { * @param billType 账单时间:日账单格式为yyyy-MM-dd,月账单格式为yyyy-MM。 * @return 返回支付方下载对账单的结果 */ + @Deprecated @Override public Map downloadbill(Date billDate, String billType) { return Collections.emptyMap(); } + /** + * 目前只支持日账单 + * + * @param billDate 账单类型,商户通过接口或商户经开放平台授权后其所属服务商通过接口可以获取以下账单类型:trade、signcustomer;trade指商户基于易极付交易收单的业务账单;signcustomer是指基于商户易极付余额收入及支出等资金变动的帐务账单; + * @param billType 账单时间:日账单格式为yyyy-MM-dd,月账单格式为yyyy-MM。 + * @return 返回支付方下载对账单的结果 + */ + @Override + public Map downloadBill(Date billDate, BillType billType) { + + return Collections.emptyMap(); + } + /** * 转账 这里外部进行调用{@link #buildRequest(Map, MethodType)} -- Gitee From 513b182c13fc9658605b36b1581ed50afd2ade56 Mon Sep 17 00:00:00 2001 From: egzosn Date: Tue, 23 Feb 2021 01:54:55 +0800 Subject: [PATCH 179/299] =?UTF-8?q?=E8=B4=A6=E5=8D=95=E7=B1=BB=E5=9E=8B?= =?UTF-8?q?=E5=AE=9E=E7=8E=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/egzosn/pay/common/bean/BillType.java | 6 +- .../egzosn/pay/union/api/UnionPayService.java | 27 ++------ .../pay/union/bean/UnionPayBillType.java | 61 ++++++++++++++++++- .../com/egzosn/pay/wx/api/WxPayService.java | 17 +++--- .../com/egzosn/pay/wx/bean/WxPayBillType.java | 12 ++-- 5 files changed, 85 insertions(+), 38 deletions(-) diff --git a/pay-java-common/src/main/java/com/egzosn/pay/common/bean/BillType.java b/pay-java-common/src/main/java/com/egzosn/pay/common/bean/BillType.java index 793463a..bf65b8f 100644 --- a/pay-java-common/src/main/java/com/egzosn/pay/common/bean/BillType.java +++ b/pay-java-common/src/main/java/com/egzosn/pay/common/bean/BillType.java @@ -20,10 +20,10 @@ public interface BillType { String getDatePattern(); /** - * 获取压缩类型 - * @return 压缩类型 + * 获取文件类型 + * @return 文件类型 */ - String getTarType(); + String getFileType(); /** * 自定义属性 diff --git a/pay-java-union/src/main/java/com/egzosn/pay/union/api/UnionPayService.java b/pay-java-union/src/main/java/com/egzosn/pay/union/api/UnionPayService.java index f6a146e..37c3af6 100644 --- a/pay-java-union/src/main/java/com/egzosn/pay/union/api/UnionPayService.java +++ b/pay-java-union/src/main/java/com/egzosn/pay/union/api/UnionPayService.java @@ -46,6 +46,7 @@ import com.egzosn.pay.common.util.sign.encrypt.RSA; import com.egzosn.pay.common.util.sign.encrypt.RSA2; import com.egzosn.pay.common.util.str.StringUtils; import com.egzosn.pay.union.bean.SDKConstants; +import com.egzosn.pay.union.bean.UnionPayBillType; import com.egzosn.pay.union.bean.UnionPayMessage; import com.egzosn.pay.union.bean.UnionRefundResult; import com.egzosn.pay.union.bean.UnionTransactionType; @@ -659,31 +660,13 @@ public class UnionPayService extends BasePayService { * 下载对账单 * * @param billDate 账单时间 - * @param billType 账单类型 + * @param fileType 文件类型 文件类型,一般商户填写00即可 * @return 返回fileContent 请自行将数据落地 */ @Deprecated @Override - public Map downloadbill(Date billDate, String billType) { - Map params = this.getCommonParam(); - UnionTransactionType.FILE_TRANSFER.convertMap(params); - - params.put(SDKConstants.param_settleDate, DateUtils.formatDate(billDate, DateUtils.MMDD)); - params.put(SDKConstants.param_fileType, billType); - params.remove(SDKConstants.param_backUrl); - params.remove(SDKConstants.param_currencyCode); - this.setSign(params); - String responseStr = getHttpRequestTemplate().postForObject(this.getFileTransUrl(), params, String.class); - JSONObject response = UriVariables.getParametersToMap(responseStr); - if (this.verify(response)) { - if (SDKConstants.OK_RESP_CODE.equals(response.get(SDKConstants.param_respCode))) { - return response; - - } - throw new PayErrorException(new PayException(response.get(SDKConstants.param_respCode).toString(), response.get(SDKConstants.param_respMsg).toString(), response.toString())); - - } - throw new PayErrorException(new PayException("failure", "验证签名失败", response.toString())); + public Map downloadbill(Date billDate, String fileType) { + return downloadBill(billDate, new UnionPayBillType(fileType)); } /** @@ -700,7 +683,7 @@ public class UnionPayService extends BasePayService { UnionTransactionType.FILE_TRANSFER.convertMap(params); params.put(SDKConstants.param_settleDate, DateUtils.formatDate(billDate, DateUtils.MMDD)); - params.put(SDKConstants.param_fileType, billType.getTarType()); + params.put(SDKConstants.param_fileType, billType.getFileType()); params.remove(SDKConstants.param_backUrl); params.remove(SDKConstants.param_currencyCode); this.setSign(params); diff --git a/pay-java-union/src/main/java/com/egzosn/pay/union/bean/UnionPayBillType.java b/pay-java-union/src/main/java/com/egzosn/pay/union/bean/UnionPayBillType.java index ef03102..c833685 100644 --- a/pay-java-union/src/main/java/com/egzosn/pay/union/bean/UnionPayBillType.java +++ b/pay-java-union/src/main/java/com/egzosn/pay/union/bean/UnionPayBillType.java @@ -1,12 +1,71 @@ package com.egzosn.pay.union.bean; +import com.egzosn.pay.common.bean.BillType; +import com.egzosn.pay.common.util.str.StringUtils; + /** * 银联账单类型 + * * @author Egan *
  * email egzosn@gmail.com
  * date 2021/2/23
  * 
*/ -public enum UnionPayBillType { +public class UnionPayBillType implements BillType { + + private String fileType = "00"; + + /** + * 获取类型名称 + * + * @return 类型 + */ + @Override + public String getType() { + return null; + } + + /** + * 获取类型对应的日期格式化表达式 + * + * @return 日期格式化表达式 + */ + @Override + public String getDatePattern() { + return null; + } + + /** + * 获取文件类型 + * + * @return 文件类型 + */ + @Override + public String getFileType() { + return fileType; + } + + public void setFileType(String fileType) { + this.fileType = fileType; + } + + /** + * 自定义属性 + * + * @return 自定义属性 + */ + @Override + public String getCustom() { + return null; + } + + public UnionPayBillType() { + } + + public UnionPayBillType(String fileType) { + if (StringUtils.isNotEmpty(fileType)) { + this.fileType = fileType; + } + } } 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 8274cba..3e4ddc5 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 @@ -605,13 +605,14 @@ public class WxPayService extends BasePayService implements parameters.put("bill_type", billType); //目前只支持日账单 parameters.put("bill_date", DateUtils.formatDate(billDate, DateUtils.YYYYMMDD)); - setParameters(parameters, "tar_type", billType.getTarType()); + String fileType = billType.getFileType(); + setParameters(parameters, "tar_type", fileType); //设置签名 setSign(parameters); Map ret = new HashMap(3); ret.put(RETURN_CODE, SUCCESS); ret.put(RETURN_MSG_CODE, "ok"); - if (StringUtils.isEmpty(billType.getTarType())) { + if (StringUtils.isEmpty(fileType)) { String respStr = requestTemplate.postForObject(getReqUrl(WxTransactionType.DOWNLOADBILL), XML.getMap2Xml(parameters), String.class); if (respStr.indexOf("<") == 0) { return XML.toJSONObject(respStr); @@ -775,12 +776,12 @@ public class WxPayService extends BasePayService implements * @param order 转账订单 *
      *
-     *                                        注意事项:
-     *                                        ◆ 当返回错误码为“SYSTEMERROR”时,请不要更换商户订单号,一定要使用原商户订单号重试,否则可能造成重复支付等资金风险。
-     *                                        ◆ XML具有可扩展性,因此返回参数可能会有新增,而且顺序可能不完全遵循此文档规范,如果在解析回包的时候发生错误,请商户务必不要换单重试,请商户联系客服确认付款情况。如果有新回包字段,会更新到此API文档中。
-     *                                        ◆ 因为错误代码字段err_code的值后续可能会增加,所以商户如果遇到回包返回新的错误码,请商户务必不要换单重试,请商户联系客服确认付款情况。如果有新的错误码,会更新到此API文档中。
-     *                                        ◆ 错误代码描述字段err_code_des只供人工定位问题时做参考,系统实现时请不要依赖这个字段来做自动化处理。
-     *                                        
+ * 注意事项: + * ◆ 当返回错误码为“SYSTEMERROR”时,请不要更换商户订单号,一定要使用原商户订单号重试,否则可能造成重复支付等资金风险。 + * ◆ XML具有可扩展性,因此返回参数可能会有新增,而且顺序可能不完全遵循此文档规范,如果在解析回包的时候发生错误,请商户务必不要换单重试,请商户联系客服确认付款情况。如果有新回包字段,会更新到此API文档中。 + * ◆ 因为错误代码字段err_code的值后续可能会增加,所以商户如果遇到回包返回新的错误码,请商户务必不要换单重试,请商户联系客服确认付款情况。如果有新的错误码,会更新到此API文档中。 + * ◆ 错误代码描述字段err_code_des只供人工定位问题时做参考,系统实现时请不要依赖这个字段来做自动化处理。 + *
* @return 对应的转账结果 */ @Override diff --git a/pay-java-wx/src/main/java/com/egzosn/pay/wx/bean/WxPayBillType.java b/pay-java-wx/src/main/java/com/egzosn/pay/wx/bean/WxPayBillType.java index 1bdd111..b4fabfb 100644 --- a/pay-java-wx/src/main/java/com/egzosn/pay/wx/bean/WxPayBillType.java +++ b/pay-java-wx/src/main/java/com/egzosn/pay/wx/bean/WxPayBillType.java @@ -87,16 +87,20 @@ public enum WxPayBillType implements BillType { } /** - * 获取压缩类型 + * 获取文件类型 * - * @return 压缩类型 + * @return 文件类型 */ @Override - public String getTarType() { + public String getFileType() { return tarType; } + + + + /** * 自定义属性 * @@ -109,7 +113,7 @@ public enum WxPayBillType implements BillType { public static WxPayBillType forType(String type){ for (WxPayBillType wxPayBillType : WxPayBillType.values()){ - if (wxPayBillType.getType().equals(type) && StringUtils.isEmpty(wxPayBillType.getTarType())){ + if (wxPayBillType.getType().equals(type) && StringUtils.isEmpty(wxPayBillType.getFileType())){ return wxPayBillType; } } -- Gitee From 1b1c7531e1e11802111f07ef32aa63d09658a4bc Mon Sep 17 00:00:00 2001 From: egzosn Date: Mon, 22 Feb 2021 23:04:52 +0800 Subject: [PATCH 180/299] =?UTF-8?q?=E4=BB=A3=E7=A0=81=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../egzosn/pay/common/api/BasePayService.java | 2 +- .../egzosn/pay/common/http/UriVariables.java | 54 +- .../com/egzosn/pay/common/util/DateUtils.java | 20 +- .../pay/common/util/MatrixToImageWriter.java | 217 +++---- .../java/com/egzosn/pay/common/util/XML.java | 105 ++-- .../pay/common/util/sign/SecureUtil.java | 71 +-- .../pay/common/util/sign/SignUtils.java | 154 +++-- .../pay/common/util/sign/encrypt/RSA.java | 575 ++++++++++-------- .../pay/common/util/sign/encrypt/sm3/SM3.java | 6 +- .../util/sign/encrypt/sm3/SM3Digest.java | 71 +-- .../egzosn/pay/fuiou/api/FuiouPayService.java | 10 +- 11 files changed, 706 insertions(+), 579 deletions(-) diff --git a/pay-java-common/src/main/java/com/egzosn/pay/common/api/BasePayService.java b/pay-java-common/src/main/java/com/egzosn/pay/common/api/BasePayService.java index 38bc4be..43aa78e 100644 --- a/pay-java-common/src/main/java/com/egzosn/pay/common/api/BasePayService.java +++ b/pay-java-common/src/main/java/com/egzosn/pay/common/api/BasePayService.java @@ -150,7 +150,7 @@ public abstract class BasePayService implements Pay */ @Override public String toPay(O order) { - Map orderInfo = orderInfo(order); + Map orderInfo = orderInfo(order); return buildRequest(orderInfo, MethodType.POST); } diff --git a/pay-java-common/src/main/java/com/egzosn/pay/common/http/UriVariables.java b/pay-java-common/src/main/java/com/egzosn/pay/common/http/UriVariables.java index e7b6ae4..b8bb0c0 100644 --- a/pay-java-common/src/main/java/com/egzosn/pay/common/http/UriVariables.java +++ b/pay-java-common/src/main/java/com/egzosn/pay/common/http/UriVariables.java @@ -5,6 +5,9 @@ import java.net.URLEncoder; import java.util.List; import java.util.Map; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + import com.alibaba.fastjson.JSONObject; import com.egzosn.pay.common.bean.result.PayException; import com.egzosn.pay.common.exception.PayErrorException; @@ -12,13 +15,17 @@ import com.egzosn.pay.common.exception.PayErrorException; /** * URL表达式处理器 * - * @author: egan + * @author egan *
  * email egzosn@gmail.com
  * date 2017/3/5 10:07
  * 
*/ -public class UriVariables { +public final class UriVariables { + private static final Log LOG = LogFactory.getLog(UriVariables.class); + + private UriVariables() { + } /** * 依次匹配 @@ -96,26 +103,21 @@ public class UriVariables { 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 + ","; + 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; } - builder.append(entry.getKey()).append("=").append(URLEncoder.encode(valueStr, "utf-8")).append("&"); - - continue; + String value = os[i].toString().trim(); + valueStr += (i == len - 1) ? value : value + ","; } - builder.append(entry.getKey()).append("=").append(URLEncoder.encode(entry.getValue().toString(), "utf-8")).append("&"); - } - catch (UnsupportedEncodingException e) { - e.printStackTrace(); + builder.append(entry.getKey()).append("=").append(urlEncoder(valueStr)).append("&"); + continue; } + builder.append(entry.getKey()).append("=").append(urlEncoder(entry.getValue().toString())).append("&"); + } if (builder.length() > 1) { builder.deleteCharAt(builder.length() - 1); @@ -202,5 +204,19 @@ public class UriVariables { } } + public static String urlEncoder(String str) { + return urlEncoder(str, "utf-8"); + } + + public static String urlEncoder(String str, String enc) { + try { + return URLEncoder.encode(str, enc); + } + catch (UnsupportedEncodingException e) { + LOG.error(e); + } + return str; + } + } diff --git a/pay-java-common/src/main/java/com/egzosn/pay/common/util/DateUtils.java b/pay-java-common/src/main/java/com/egzosn/pay/common/util/DateUtils.java index fa6b4e6..5cd5111 100644 --- a/pay-java-common/src/main/java/com/egzosn/pay/common/util/DateUtils.java +++ b/pay-java-common/src/main/java/com/egzosn/pay/common/util/DateUtils.java @@ -1,13 +1,16 @@ package com.egzosn.pay.common.util; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; -import org.apache.http.util.Args; - import java.lang.ref.SoftReference; import java.text.ParseException; import java.text.SimpleDateFormat; -import java.util.*; +import java.util.Date; +import java.util.HashMap; +import java.util.Map; +import java.util.TimeZone; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.apache.http.util.Args; /** * 日期转换运算工具 @@ -19,11 +22,11 @@ import java.util.*; *
*/ public final class DateUtils { + private static final Log LOG = LogFactory.getLog(DateUtils.class); + private DateUtils() { } - private static final Log LOG = LogFactory.getLog(DateUtils.class); - static final class DateFormatHolder { private static final ThreadLocal>> THREADLOCAL_FORMATS = new ThreadLocal>>(); @@ -76,7 +79,8 @@ public final class DateUtils { SimpleDateFormat formatFor = DateFormatHolder.formatFor(pattern); try { return formatFor.parse(date); - } catch (ParseException e) { + } + catch (ParseException e) { LOG.error(e); } return null; diff --git a/pay-java-common/src/main/java/com/egzosn/pay/common/util/MatrixToImageWriter.java b/pay-java-common/src/main/java/com/egzosn/pay/common/util/MatrixToImageWriter.java index fc32049..57b8e75 100644 --- a/pay-java-common/src/main/java/com/egzosn/pay/common/util/MatrixToImageWriter.java +++ b/pay-java-common/src/main/java/com/egzosn/pay/common/util/MatrixToImageWriter.java @@ -1,11 +1,5 @@ package com.egzosn.pay.common.util; -import com.google.zxing.BarcodeFormat; -import com.google.zxing.EncodeHintType; -import com.google.zxing.MultiFormatWriter; -import com.google.zxing.common.BitMatrix; - -import javax.imageio.ImageIO; import java.awt.image.BufferedImage; import java.io.File; import java.io.IOException; @@ -13,10 +7,18 @@ import java.io.OutputStream; import java.util.HashMap; import java.util.Map; +import javax.imageio.ImageIO; + +import com.google.zxing.BarcodeFormat; +import com.google.zxing.EncodeHintType; +import com.google.zxing.MultiFormatWriter; +import com.google.zxing.common.BitMatrix; + /** * 二维码生成工具 - * @author egan + * + * @author egan *
  * email egzosn@gmail.com
  * date  2017/2/7 10:35
@@ -25,104 +27,111 @@ import java.util.Map;
 public class MatrixToImageWriter {
 
 
-	   private static final int BLACK = 0xFF000000;
-	   private static final int WHITE = 0xFFFFFFFF;
-	 
-	   private MatrixToImageWriter() {}
+    private static final int BLACK = 0xFF000000;
+    private static final int WHITE = 0xFFFFFFFF;
+
+    private MatrixToImageWriter() {
+    }
+
+    /**
+     * 根据二维矩阵的碎片 生成对应的二维码图像缓冲
+     *
+     * @param matrix 二维矩阵的碎片 包含 宽高 行,字节
+     * @return 二维码图像缓冲
+     * @see com.google.zxing.common.BitMatrix
+     */
+    public static BufferedImage toBufferedImage(BitMatrix matrix) {
+        int width = matrix.getWidth();
+        int height = matrix.getHeight();
+        BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
+        for (int x = 0; x < width; x++) {
+            for (int y = 0; y < height; y++) {
+                image.setRGB(x, y, matrix.get(x, y) ? BLACK : WHITE);
+            }
+        }
+        return image;
+    }
+
+
+    /**
+     * 二维码生成文件
+     *
+     * @param matrix 二维矩阵的碎片 包含 宽高 行,字节
+     * @param format 格式
+     * @param file   保持的文件地址
+     * @throws IOException 文件保存异常
+     */
+    public static void writeToFile(BitMatrix matrix, String format, File file)
+            throws IOException {
+        BufferedImage image = toBufferedImage(matrix);
+        if (!ImageIO.write(image, format, file)) {
+            throw new IOException("Could not write an image of format " + format + " to " + file);
+        }
+    }
+
+
+    /**
+     * 二维码生成流
+     *
+     * @param matrix 二维矩阵的碎片 包含 宽高 行,字节
+     * @param format 格式
+     * @param stream 保持的文件输出流
+     * @throws IOException 文件保存异常
+     */
+    public static void writeToStream(BitMatrix matrix, String format, OutputStream stream)
+            throws IOException {
+        BufferedImage image = toBufferedImage(matrix);
+        if (!ImageIO.write(image, format, stream)) {
+            throw new IOException("Could not write an image of format " + format);
+        }
+    }
+
 
     /**
-	 * 根据二维矩阵的碎片 生成对应的二维码图像缓冲
-	 * @param matrix  二维矩阵的碎片 包含 宽高 行,字节
-	 * @see com.google.zxing.common.BitMatrix
-	 * @return 二维码图像缓冲
+     * 二维码信息写成JPG文件
+     *
+     * @param content 二维码信息
+     * @param fileUrl 文件地址
      */
-	   public static BufferedImage toBufferedImage(BitMatrix matrix) {
-	     int width = matrix.getWidth();
-	     int height = matrix.getHeight();
-	     BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
-	     for (int x = 0; x < width; x++) {
-	       for (int y = 0; y < height; y++) {
-	         image.setRGB(x, y, matrix.get(x, y) ? BLACK : WHITE);
-	       }
-	     }
-	     return image;
-	   }
-
-
-	/**
-	 * 二维码生成文件
-	 * @param matrix 二维矩阵的碎片 包含 宽高 行,字节
-	 * @param format 格式
-	 * @param file 保持的文件地址
-	 * @throws IOException 文件保存异常
-	 */
-	   public static void writeToFile(BitMatrix matrix, String format, File file)
-	       throws IOException {
-	     BufferedImage image = toBufferedImage(matrix);
-	     if (!ImageIO.write(image, format, file)) {
-	       throw new IOException("Could not write an image of format " + format + " to " + file);
-	     }
-	   }
-
-
-	/**
-	 * 二维码生成流
-	 * @param matrix 二维矩阵的碎片 包含 宽高 行,字节
-	 * @param format 格式
-	 * @param stream 保持的文件输出流
-	 * @throws IOException 文件保存异常
-	 */
-	   public static void writeToStream(BitMatrix matrix, String format, OutputStream stream)
-	       throws IOException {
-	     BufferedImage image = toBufferedImage(matrix);
-	     if (!ImageIO.write(image, format, stream)) {
-	       throw new IOException("Could not write an image of format " + format);
-	     }
-	   }
-	   
-	   
-	   /**
-	    * 二维码信息写成JPG文件
-	    * @param content  二维码信息
-	    * @param fileUrl 文件地址
-	    */
-	  public static void writeInfoToJpgFile(String content, String fileUrl){
-		    MultiFormatWriter multiFormatWriter = new MultiFormatWriter();
-		    Map hints = new HashMap();
-		    hints.put(EncodeHintType.CHARACTER_SET, "UTF-8");
-		    try {
-				BitMatrix bitMatrix = multiFormatWriter.encode(content,
-						BarcodeFormat.QR_CODE, 250, 250, hints);
-				File file1 = new File(fileUrl);
-				MatrixToImageWriter.writeToFile(bitMatrix, "jpg", file1);
-			} catch (Exception e) {
-				e.printStackTrace();
-			}	 
-	   }
-	  
-	  
-	  /**
-	   * 二维码信息写成JPG BufferedImage
-	   * @param content 二维码信息
-	   * @return JPG BufferedImage
-	   */
-	  public static BufferedImage writeInfoToJpgBuff(String content){
-		    BufferedImage re=null;
-		  
-		    MultiFormatWriter multiFormatWriter = new MultiFormatWriter();
-		    Map hints = new HashMap();
-		    hints.put(EncodeHintType.CHARACTER_SET, "UTF-8");
-		    try {
-				BitMatrix bitMatrix = multiFormatWriter.encode(content,
-						BarcodeFormat.QR_CODE, 250, 250, hints);
-				re=MatrixToImageWriter.toBufferedImage(bitMatrix);
-			} catch (Exception e) {
-				e.printStackTrace();
-			}	 
-		    
-		  return re;
-	  }
-	 
-	   
-	   
+    public static void writeInfoToJpgFile(String content, String fileUrl) {
+        MultiFormatWriter multiFormatWriter = new MultiFormatWriter();
+        Map hints = new HashMap();
+        hints.put(EncodeHintType.CHARACTER_SET, "UTF-8");
+        try {
+            BitMatrix bitMatrix = multiFormatWriter.encode(content,
+                    BarcodeFormat.QR_CODE, 250, 250, hints);
+            File file1 = new File(fileUrl);
+            MatrixToImageWriter.writeToFile(bitMatrix, "jpg", file1);
+        }
+        catch (Exception e) {
+            e.printStackTrace();
+        }
+    }
+
+
+    /**
+     * 二维码信息写成JPG BufferedImage
+     *
+     * @param content 二维码信息
+     * @return JPG BufferedImage
+     */
+    public static BufferedImage writeInfoToJpgBuff(String content) {
+        BufferedImage re = null;
+
+        MultiFormatWriter multiFormatWriter = new MultiFormatWriter();
+        Map hints = new HashMap();
+        hints.put(EncodeHintType.CHARACTER_SET, "UTF-8");
+        try {
+            BitMatrix bitMatrix = multiFormatWriter.encode(content,
+                    BarcodeFormat.QR_CODE, 250, 250, hints);
+            re = MatrixToImageWriter.toBufferedImage(bitMatrix);
+        }
+        catch (Exception e) {
+            e.printStackTrace();
+        }
+
+        return re;
+    }
+
+
 }
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 1ee67ec..675bdfd 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
@@ -1,16 +1,13 @@
 package com.egzosn.pay.common.util;
 
 
-import com.alibaba.fastjson.JSON;
-import com.alibaba.fastjson.JSONArray;
-import com.alibaba.fastjson.JSONObject;
-import com.egzosn.pay.common.bean.result.PayException;
-import com.egzosn.pay.common.exception.PayErrorException;
-import com.egzosn.pay.common.util.str.StringUtils;
-import org.w3c.dom.Document;
-import org.w3c.dom.Element;
-import org.w3c.dom.Node;
-import org.w3c.dom.NodeList;
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.StringWriter;
+import java.nio.charset.Charset;
+import java.util.List;
+import java.util.Map;
 
 import javax.xml.XMLConstants;
 import javax.xml.parsers.DocumentBuilder;
@@ -22,20 +19,25 @@ import javax.xml.transform.TransformerException;
 import javax.xml.transform.TransformerFactory;
 import javax.xml.transform.dom.DOMSource;
 import javax.xml.transform.stream.StreamResult;
-import java.io.ByteArrayInputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.StringWriter;
-import java.nio.charset.Charset;
-import java.util.List;
-import java.util.Map;
+
+import org.w3c.dom.Document;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+import org.xml.sax.SAXException;
+
+import com.alibaba.fastjson.JSON;
+import com.alibaba.fastjson.JSONArray;
+import com.alibaba.fastjson.JSONObject;
+import com.egzosn.pay.common.bean.result.PayException;
+import com.egzosn.pay.common.exception.PayErrorException;
+import com.egzosn.pay.common.util.str.StringUtils;
 
 
 /**
  * XML工具
  *
  * @author egan
- *         
+ * 
  *         email egzosn@gmail.com
  *         date 2016-6-2 19:45:06
  *         
@@ -57,7 +59,8 @@ public class XML { try { return (JSONObject) inputStream2Map(in, null); - } catch (IOException e) { + } + catch (IOException e) { throw new PayErrorException(new PayException("IOException", e.getMessage())); } @@ -76,6 +79,7 @@ public class XML { } + /** * 解析xml并转化为Json值 * @@ -90,6 +94,7 @@ public class XML { } return toJSONObject(content.getBytes(charset)); } + /** * 解析xml并转化为Json值 * @@ -103,7 +108,8 @@ public class XML { } try (InputStream in = new ByteArrayInputStream(content)) { return (JSONObject) inputStream2Map(in, null); - } catch (IOException e) { + } + catch (IOException e) { throw new PayErrorException(new PayException("IOException", e.getMessage())); } @@ -113,8 +119,8 @@ public class XML { * 解析xml并转化为Json值 * * @param content json字符串 - * @param clazz 需要转化的类 - * @param 返回对应类型 + * @param clazz 需要转化的类 + * @param 返回对应类型 * @return Json值 */ public static T toBean(String content, Class clazz) { @@ -124,7 +130,8 @@ public class XML { } try (InputStream in = new ByteArrayInputStream(content.getBytes("UTF-8"))) { return inputStream2Bean(in, clazz); - } catch (IOException e) { + } + catch (IOException e) { throw new PayErrorException(new PayException("IOException", e.getMessage())); } @@ -154,7 +161,8 @@ public class XML { JSONArray array = new JSONArray(); array.add(json); json = array; - } else { + } + else { j.put(node.getNodeName(), getChildren(nodeList)); } } @@ -164,7 +172,8 @@ public class XML { c.put(node.getNodeName(), getChildren(nodeList)); ((JSONArray) json).add(c); } - } else if (node.getNodeType() == Node.ELEMENT_NODE ) { + } + else if (node.getNodeType() == Node.ELEMENT_NODE) { if (null == json) { json = new JSONObject(); } @@ -224,22 +233,27 @@ public class XML { Node node = children.item(idx); NodeList nodeList = node.getChildNodes(); int length = nodeList.getLength(); - if (node.getNodeType() == Node.ELEMENT_NODE && (length >1 || length==1 && nodeList.item(0).hasChildNodes())) { + if (node.getNodeType() == Node.ELEMENT_NODE && (length > 1 || length == 1 && nodeList.item(0).hasChildNodes())) { m.put(node.getNodeName(), getChildren(nodeList)); - } else if (node.getNodeType() == Node.ELEMENT_NODE ) { + } + else if (node.getNodeType() == Node.ELEMENT_NODE) { m.put(node.getNodeName(), node.getTextContent()); } } - } catch (Exception e) { + } + catch (ParserConfigurationException e) { throw new PayErrorException(new PayException("XML failure", "XML解析失败\n" + e.getMessage())); - } finally { + } + catch (SAXException e) { + throw new PayErrorException(new PayException("XML failure", "XML解析失败\n" + e.getMessage())); + } + finally { in.close(); } return m; } - /** * 将Map转换为XML格式的字符串 * @@ -254,16 +268,17 @@ public class XML { /** * 将Map转换为XML格式的字符串 * - * @param data Map类型数据 + * @param data Map类型数据 * @param rootElementName 最外层节点名称 - * @param encoding 字符编码 + * @param encoding 字符编码 * @return XML格式的字符串 */ public static String getMap2Xml(Map data, String rootElementName, String encoding) { Document document = null; try { document = newDocument(); - } catch (ParserConfigurationException e) { + } + catch (ParserConfigurationException e) { throw new PayErrorException(new PayException("ParserConfigurationException", e.getLocalizedMessage())); } org.w3c.dom.Element root = document.createElement(rootElementName); @@ -292,18 +307,18 @@ public class XML { transformer.transform(source, result); String output = writer.getBuffer().toString(); return output; - } catch (TransformerException e) { - e.printStackTrace(); + } + catch (TransformerException e) { + throw new PayErrorException(new PayException("XML failure", "XML生成失败\n" + e.getMessage())); } - return ""; } /** * 将Map转换为XML格式的字符串 * - * @param data Map类型数据 + * @param data Map类型数据 * @param document 文档 * @param element 节点 */ @@ -333,17 +348,19 @@ public class XML { } } - private static void object2Xml(Object value, Document document, org.w3c.dom.Element element){ + private static void object2Xml(Object value, Document document, org.w3c.dom.Element element) { - if (value instanceof Map){ - map2Xml((Map)value, document, element); - }else if (value instanceof List){ - List vs = (List)value; - for (Object v : vs ){ + if (value instanceof Map) { + map2Xml((Map) value, document, element); + } + else if (value instanceof List) { + List vs = (List) value; + for (Object v : vs) { object2Xml(v, document, element); } // map2Xml((Map)value, document, element); - }else { + } + else { value = value.toString().trim(); element.appendChild(document.createTextNode(value.toString())); } diff --git a/pay-java-common/src/main/java/com/egzosn/pay/common/util/sign/SecureUtil.java b/pay-java-common/src/main/java/com/egzosn/pay/common/util/sign/SecureUtil.java index 63cbc1b..22839a4 100644 --- a/pay-java-common/src/main/java/com/egzosn/pay/common/util/sign/SecureUtil.java +++ b/pay-java-common/src/main/java/com/egzosn/pay/common/util/sign/SecureUtil.java @@ -1,19 +1,21 @@ package com.egzosn.pay.common.util.sign; -import com.egzosn.pay.common.bean.result.PayException; -import com.egzosn.pay.common.exception.PayErrorException; -import com.egzosn.pay.common.util.sign.encrypt.sm3.SM3Digest; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; - import java.io.UnsupportedEncodingException; import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; import java.security.PublicKey; import java.security.Signature; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import com.egzosn.pay.common.bean.result.PayException; +import com.egzosn.pay.common.exception.PayErrorException; +import com.egzosn.pay.common.util.sign.encrypt.sm3.SM3Digest; + public class SecureUtil { - //日志 - protected static final Log log = LogFactory.getLog(SecureUtil.class); + + private static final Log LOG = LogFactory.getLog(SecureUtil.class); /** * 算法常量: SHA1 */ @@ -34,19 +36,20 @@ public class SecureUtil { /** * 获取摘要 * - * @param data 待计算的数据 + * @param data 待计算的数据 * @param algorithm 算法名 * @return 计算结果 */ - private static byte[] digestByData (byte[] data,String algorithm) { + private static byte[] digestByData(byte[] data, String algorithm) { MessageDigest md = null; try { md = MessageDigest.getInstance(algorithm); md.reset(); md.update(data); return md.digest(); - } catch (Exception e) { - e.printStackTrace(); + } + catch (NoSuchAlgorithmException e) { + LOG.error(e); return null; } } @@ -58,7 +61,7 @@ public class SecureUtil { * @param encoding 编码 * @return 计算结果 */ - public static byte[] sha1X16 (String data, String encoding) { + public static byte[] sha1X16(String data, String encoding) { try { byte[] bytes = digestByData(data.getBytes(encoding), ALGORITHM_SHA1); StringBuilder sha1StrBuff = new StringBuilder(); @@ -66,41 +69,42 @@ public class SecureUtil { if (Integer.toHexString(0xFF & bytes[i]).length() == 1) { sha1StrBuff.append("0").append( Integer.toHexString(0xFF & bytes[i])); - } else { + } + else { sha1StrBuff.append(Integer.toHexString(0xFF & bytes[i])); } } return sha1StrBuff.toString().getBytes(encoding); - } catch (UnsupportedEncodingException e) { - e.printStackTrace(); + } + catch (UnsupportedEncodingException e) { + LOG.error(e); return null; } } - /** * sha256计算后进行16进制转换 * - * @param data - * 待计算的数据 - * @param encoding - * 编码 + * @param data 待计算的数据 + * @param encoding 编码 * @return 计算结果 */ public static String sha256X16Str(String data, String encoding) { - byte[] bytes =null; + byte[] bytes = null; try { bytes = digestByData(data.getBytes(encoding), ALGORITHM_SHA1); - } catch (UnsupportedEncodingException e) { - throw new PayErrorException(new PayException("error", e.getLocalizedMessage())); + } + catch (UnsupportedEncodingException e) { + throw new PayErrorException(new PayException("error", e.getLocalizedMessage())); } StringBuilder sha256StrBuff = new StringBuilder(); for (int i = 0; i < bytes.length; i++) { if (Integer.toHexString(0xFF & bytes[i]).length() == 1) { sha256StrBuff.append("0").append( Integer.toHexString(0xFF & bytes[i])); - } else { + } + else { sha256StrBuff.append(Integer.toHexString(0xFF & bytes[i])); } } @@ -110,8 +114,7 @@ public class SecureUtil { /** * SM3计算. * - * @param data - * 待计算的数据 + * @param data 待计算的数据 * @return 计算结果 */ private static byte[] sm3(byte[] data) { @@ -126,25 +129,25 @@ public class SecureUtil { /** * sm3计算后进行16进制转换 * - * @param data - * 待计算的数据 - * @param encoding - * 编码 + * @param data 待计算的数据 + * @param encoding 编码 * @return 计算结果 */ public static String sm3X16Str(String data, String encoding) { byte[] bytes = new byte[new SM3Digest().getDigestSize()]; try { bytes = SecureUtil.sm3(data.getBytes(encoding)); - } catch (UnsupportedEncodingException e) { - e.printStackTrace(); + } + catch (UnsupportedEncodingException e) { + LOG.error(e); } StringBuilder sm3StrBuff = new StringBuilder(); for (int i = 0; i < bytes.length; i++) { if (Integer.toHexString(0xFF & bytes[i]).length() == 1) { sm3StrBuff.append("0").append( Integer.toHexString(0xFF & bytes[i])); - } else { + } + else { sm3StrBuff.append(Integer.toHexString(0xFF & bytes[i])); } } 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 74ade5f..07957ea 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 @@ -1,23 +1,35 @@ package com.egzosn.pay.common.util.sign; +import java.io.UnsupportedEncodingException; +import java.security.InvalidKeyException; +import java.security.NoSuchAlgorithmException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.SortedMap; +import java.util.UUID; + +import javax.crypto.Mac; +import javax.crypto.spec.SecretKeySpec; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.apache.http.message.BasicNameValuePair; + import com.egzosn.pay.common.bean.SignType; import com.egzosn.pay.common.bean.result.PayException; import com.egzosn.pay.common.exception.PayErrorException; import com.egzosn.pay.common.util.str.StringUtils; -import org.apache.http.message.BasicNameValuePair; - -import javax.crypto.Mac; -import javax.crypto.spec.SecretKeySpec; -import java.io.UnsupportedEncodingException; -import java.security.InvalidKeyException; -import java.security.NoSuchAlgorithmException; -import java.util.*; /** * 签名 工具 * - * @author: egan + * @author egan *
  * email egzosn@gmail.com
  * date 2016/11/9 17:45
@@ -51,7 +63,7 @@ public enum SignUtils implements SignType {
         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);
         }
-    },HMACSHA256{
+    }, HMACSHA256 {
         @Override
         public String getName() {
             return "HMAC-SHA256";
@@ -79,12 +91,15 @@ public enum SignUtils implements SignType {
                     sb.append(Integer.toHexString((item & 0xFF) | 0x100).substring(1, 3));
                 }
                 return sb.toString().toUpperCase();
-            } catch (NoSuchAlgorithmException e) {
-                e.printStackTrace();
-            } catch (InvalidKeyException e) {
-                e.printStackTrace();
-            } catch (UnsupportedEncodingException e) {
-                e.printStackTrace();
+            }
+            catch (NoSuchAlgorithmException e) {
+                LOG.error(e);
+            }
+            catch (InvalidKeyException e) {
+                LOG.error(e);
+            }
+            catch (UnsupportedEncodingException e) {
+                LOG.error(e);
             }
 
             throw new PayErrorException(new PayException("fail", "HMACSHA256 签名异常"));
@@ -162,10 +177,11 @@ public enum SignUtils implements SignType {
             return com.egzosn.pay.common.util.sign.encrypt.RSA2.verify(text, sign, publicKey, characterEncoding);
         }
     };
+    private static final Log LOG = LogFactory.getLog(SignUtils.class);
 
     /**
-     *
      * 把数组所有元素排序,并按照“参数=参数值”的模式用“@param separator”字符拼接成字符串
+     *
      * @param parameters 参数
      * @return 去掉空值与签名参数后的新签名,拼接后字符串
      */
@@ -175,10 +191,10 @@ public enum SignUtils implements SignType {
     }
 
     /**
-     *
      * 把数组所有元素排序,并按照“参数=参数值”的模式用“@param separator”字符拼接成字符串
+     *
      * @param parameters 参数
-     * @param separator 分隔符
+     * @param separator  分隔符
      * @return 去掉空值与签名参数后的新签名,拼接后字符串
      */
     public static String parameterText(Map parameters, String separator) {
@@ -186,42 +202,47 @@ public enum SignUtils implements SignType {
     }
 
     /**
-     *
      * 把数组所有元素排序,并按照“参数=参数值”的模式用“@param separator”字符拼接成字符串
+     *
      * @param parameters 参数
-     * @param separator 分隔符
-     * @param ignoreKey 需要忽略添加的key
+     * @param separator  分隔符
+     * @param ignoreKey  需要忽略添加的key
      * @return 去掉空值与签名参数后的新签名,拼接后字符串
      */
-    public static String parameterText(Map parameters, String separator, String... ignoreKey) {
+    public static String parameterText(Map parameters, String separator, String... ignoreKey) {
         return parameterText(parameters, separator, true, ignoreKey);
     }
-    
+
     /**
-     *
      * 把数组所有元素排序,并按照“参数=参数值”的模式用“@param separator”字符拼接成字符串
-     * @param parameters 参数
-     * @param separator 分隔符
+     *
+     * @param parameters      参数
+     * @param separator       分隔符
      * @param ignoreNullValue 需要忽略NULL值
-     * @param ignoreKey 需要忽略添加的key
+     * @param ignoreKey       需要忽略添加的key
      * @return 去掉空值与签名参数后的新签名,拼接后字符串
      */
-    public static String parameterText(Map parameters, String separator, boolean ignoreNullValue, String... ignoreKey ) {
-        if(parameters == null){
+    public static String parameterText(Map parameters, String separator, boolean ignoreNullValue, String... ignoreKey) {
+        if (parameters == null) {
             return "";
         }
-        StringBuffer sb = new StringBuffer();
-        if (null != ignoreKey){
+
+        if (null != ignoreKey) {
             Arrays.sort(ignoreKey);
         }
+        StringBuffer sb = new StringBuffer();
         // TODO 2016/11/11 10:14 author: egan 已经排序好处理
         if (parameters instanceof SortedMap) {
-            for (Map.Entry entry : (Set>)parameters.entrySet()) {
+            for (Map.Entry entry :  parameters.entrySet()) {
                 Object v = entry.getValue();
-                if (null == v || "".equals(v.toString().trim()) || (null != ignoreKey && Arrays.binarySearch(ignoreKey, entry.getKey() ) >= 0)) {
+                if (null == v) {
+                    continue;
+                }
+                String valStr = v.toString().trim();
+                if ("".equals(valStr) || (null != ignoreKey && Arrays.binarySearch(ignoreKey, entry.getKey()) >= 0)) {
                     continue;
                 }
-                sb.append(entry.getKey() ).append("=").append( v.toString().trim()).append(separator);
+                sb.append(entry.getKey()).append("=").append(valStr).append(separator);
             }
             if (sb.length() > 0 && !"".equals(separator)) {
                 sb.deleteCharAt(sb.length() - 1);
@@ -230,7 +251,13 @@ public enum SignUtils implements SignType {
 
         }
 
+        return sortMapParameterText(parameters, separator, ignoreNullValue, ignoreKey);
+
+    }
 
+
+    private static String sortMapParameterText(Map parameters, String separator, boolean ignoreNullValue, String... ignoreKey) {
+        StringBuffer sb = new StringBuffer();
         // TODO 2016/11/11 10:14 author: egan 未排序须处理
         List keys = new ArrayList(parameters.keySet());
         //排序
@@ -246,16 +273,19 @@ public enum SignUtils implements SignType {
 
                 for (int i = 0; i < values.length; i++) {
                     String value = values[i].trim();
-                    if ("".equals(value)){ continue;}
-                    valueStr += (i == values.length - 1) ?  value :  value + ",";
+                    if ("".equals(value)) {
+                        continue;
+                    }
+                    valueStr += (i == values.length - 1) ? value : value + ",";
                 }
-            } else {
+            }
+            else {
                 valueStr = o.toString();
             }
-            if (null == valueStr || "".equals(valueStr.toString().trim()) || (null != ignoreKey && Arrays.binarySearch(ignoreKey, k ) >= 0)) {
+            if (StringUtils.isBlank(valueStr) || (null != ignoreKey && Arrays.binarySearch(ignoreKey, k) >= 0)) {
                 continue;
             }
-            sb.append(k ).append("=").append( valueStr).append(separator);
+            sb.append(k).append("=").append(valueStr).append(separator);
         }
         if (sb.length() > 0) {
             sb.deleteCharAt(sb.length() - 1);
@@ -266,35 +296,38 @@ public enum SignUtils implements SignType {
     /**
      * 将参数集合(事前做好排序)按分割符号拼凑字符串并加密为MD5
      * example: mchnt_cd+"|"  +order_id+"|"+order_amt+"|"+order_pay_type+"|"+page_notify_url+"|"+back_notify_url+"|"+order_valid_time+"|"+iss_ins_cd+"|"+goods_name+"|"+"+goods_display_url+"|"+rem+"|"+ver+"|"+mchnt_key
+     *
      * @param parameters 参数集合
-     * @param separator 分隔符
+     * @param separator  分隔符
      * @return 参数排序好的值
      */
-    public static String  parameters2MD5Str(Object parameters, String separator){
+    public static String parameters2Md5Str(Object parameters, String separator) {
         StringBuffer sb = new StringBuffer();
 
         if (parameters instanceof LinkedHashMap) {
-            Set  keys = (Set) ((LinkedHashMap)parameters).keySet();
-            for(String key : keys){
-                String val = ((LinkedHashMap)parameters).get(key).toString();
+            Set keys = (Set) ((LinkedHashMap) parameters).keySet();
+            for (String key : keys) {
+                String val = ((LinkedHashMap) parameters).get(key).toString();
                 sb.append(val).append(separator);
 
             }
-        }else if(parameters instanceof List){
-            for(BasicNameValuePair bnv :((List)parameters) ){
-                    sb.append(bnv.getValue()).append(separator);
+        }
+        else if (parameters instanceof List) {
+            for (BasicNameValuePair bnv : ((List) parameters)) {
+                sb.append(bnv.getValue()).append(separator);
             }
         }
 
-        return StringUtils.isBlank(sb.toString())?"":sb.deleteCharAt(sb.length() - 1).toString();
+        return StringUtils.isBlank(sb.toString()) ? "" : sb.deleteCharAt(sb.length() - 1).toString();
     }
 
 
     /**
      * 获取随机字符串
+     *
      * @return 随机字符串
      */
-    public static String randomStr(){
+    public static String randomStr() {
         return UUID.randomUUID().toString().replace("-", "");
     }
 
@@ -306,24 +339,26 @@ public enum SignUtils implements SignType {
     /**
      * 签名
      *
-     * @param parameters 需要进行排序签名的参数
-     * @param key 密钥
+     * @param parameters        需要进行排序签名的参数
+     * @param key               密钥
      * @param characterEncoding 编码格式
      * @return 签名值
      */
-    public  String sign(Map parameters, String key, String characterEncoding) {
+    public String sign(Map parameters, String key, String characterEncoding) {
 
         return createSign(parameterText(parameters, "&"), key, characterEncoding);
     }
+
     /**
      * 签名
-     * @param parameters 需要进行排序签名的参数
-     * @param key 密钥
-     * @param separator 分隔符  默认 &
+     *
+     * @param parameters        需要进行排序签名的参数
+     * @param key               密钥
+     * @param separator         分隔符  默认 &
      * @param characterEncoding 编码格式
      * @return 签名值
      */
-    public  String sign(Map parameters, String key, String separator, String characterEncoding) {
+    public String sign(Map parameters, String key, String separator, String characterEncoding) {
 
         return createSign(parameterText(parameters, separator), key, characterEncoding);
 
@@ -333,17 +368,16 @@ public enum SignUtils implements SignType {
     /**
      * 签名字符串
      *
-     * @param params              需要签名的字符串
+     * @param params            需要签名的字符串
      * @param sign              签名结果
      * @param key               密钥
      * @param characterEncoding 编码格式
      * @return 签名结果
      */
-    public  boolean verify(Map params, String sign, String key, String characterEncoding){
+    public boolean verify(Map params, String sign, String key, String characterEncoding) {
         //判断是否一样
         return this.verify(parameterText(params), sign, key, characterEncoding);
     }
 
 
-
 }
diff --git a/pay-java-common/src/main/java/com/egzosn/pay/common/util/sign/encrypt/RSA.java b/pay-java-common/src/main/java/com/egzosn/pay/common/util/sign/encrypt/RSA.java
index d75d1c8..e1c0490 100644
--- a/pay-java-common/src/main/java/com/egzosn/pay/common/util/sign/encrypt/RSA.java
+++ b/pay-java-common/src/main/java/com/egzosn/pay/common/util/sign/encrypt/RSA.java
@@ -1,8 +1,13 @@
 
 package com.egzosn.pay.common.util.sign.encrypt;
 
-import javax.crypto.Cipher;
-import java.io.*;
+import java.io.BufferedReader;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.UnsupportedEncodingException;
 import java.security.GeneralSecurityException;
 import java.security.KeyFactory;
 import java.security.PrivateKey;
@@ -10,288 +15,324 @@ import java.security.PublicKey;
 import java.security.spec.PKCS8EncodedKeySpec;
 import java.security.spec.X509EncodedKeySpec;
 
+import javax.crypto.Cipher;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
 /**
  * RSA
+ *
  * @author egan
  * 
  * email egzosn@gmail.com
- *
+ *
*/ -public class RSA{ - - private static final String ALGORITHM = "RSA"; - - - private static final String SIGN_ALGORITHMS = "SHA1WithRSA"; - - - /** - * RSA签名 - * @param content 待签名数据 - * @param privateKey 私钥 - * @param signAlgorithms 签名算法 - * @param characterEncoding 编码格式 - * @return 签名值 - */ - public static String sign(String content, String privateKey, String signAlgorithms, String characterEncoding) { - try { - PKCS8EncodedKeySpec priPKCS8 = new PKCS8EncodedKeySpec(Base64.decode(privateKey)); - KeyFactory keyf = KeyFactory.getInstance(ALGORITHM); - PrivateKey priKey = keyf.generatePrivate(priPKCS8); - - java.security.Signature signature = java.security.Signature.getInstance(signAlgorithms); - - signature.initSign(priKey); - signature.update(content.getBytes(characterEncoding)); - - byte[] signed = signature.sign(); - - return Base64.encode(signed); - } catch (Exception e) { - e.printStackTrace(); - } - - return null; - } - - - - /** - * RSA签名 - * @param content 待签名数据 - * @param privateKey 私钥 - * @param signAlgorithms 签名算法 - * @param characterEncoding 编码格式 - * @return 签名值 - */ - public static String sign(String content, PrivateKey privateKey, String signAlgorithms, String characterEncoding) { - try { - java.security.Signature signature = java.security.Signature.getInstance(signAlgorithms); - signature.initSign(privateKey); - signature.update(content.getBytes(characterEncoding)); - byte[] signed = signature.sign(); - return Base64.encode(signed); - } catch (Exception e) { - e.printStackTrace(); - } - - return null; - } - - - /** - * RSA签名 - * @param content 待签名数据 - * @param privateKey 私钥 - * @param characterEncoding 编码格式 - * @return 签名值 - */ - public static String sign(String content, String privateKey ,String characterEncoding){ +public class RSA { + private static final Log LOG = LogFactory.getLog(RSA.class); + private static final String ALGORITHM = "RSA"; + + + private static final String SIGN_ALGORITHMS = "SHA1WithRSA"; + + + /** + * RSA签名 + * + * @param content 待签名数据 + * @param privateKey 私钥 + * @param signAlgorithms 签名算法 + * @param characterEncoding 编码格式 + * @return 签名值 + */ + public static String sign(String content, String privateKey, String signAlgorithms, String characterEncoding) { + try { + PKCS8EncodedKeySpec priPKCS8 = new PKCS8EncodedKeySpec(Base64.decode(privateKey)); + KeyFactory keyf = KeyFactory.getInstance(ALGORITHM); + PrivateKey priKey = keyf.generatePrivate(priPKCS8); + + java.security.Signature signature = java.security.Signature.getInstance(signAlgorithms); + + signature.initSign(priKey); + signature.update(content.getBytes(characterEncoding)); + + byte[] signed = signature.sign(); + + return Base64.encode(signed); + } + catch (GeneralSecurityException e) { + LOG.error(e); + } + catch (UnsupportedEncodingException e) { + LOG.error(e); + } + + return null; + } + + + /** + * RSA签名 + * + * @param content 待签名数据 + * @param privateKey 私钥 + * @param signAlgorithms 签名算法 + * @param characterEncoding 编码格式 + * @return 签名值 + */ + public static String sign(String content, PrivateKey privateKey, String signAlgorithms, String characterEncoding) { + try { + java.security.Signature signature = java.security.Signature.getInstance(signAlgorithms); + signature.initSign(privateKey); + signature.update(content.getBytes(characterEncoding)); + byte[] signed = signature.sign(); + return Base64.encode(signed); + } + catch (GeneralSecurityException e) { + LOG.error(e); + } + catch (UnsupportedEncodingException e) { + LOG.error(e); + } + + return null; + } + + + /** + * RSA签名 + * + * @param content 待签名数据 + * @param privateKey 私钥 + * @param characterEncoding 编码格式 + * @return 签名值 + */ + public static String sign(String content, String privateKey, String characterEncoding) { return sign(content, privateKey, SIGN_ALGORITHMS, characterEncoding); } - /** - * RSA签名 - * @param content 待签名数据 - * @param privateKey 私钥 - * @param characterEncoding 编码格式 - * @return 签名值 - */ - public static String sign(String content, PrivateKey privateKey ,String characterEncoding){ + /** + * RSA签名 + * + * @param content 待签名数据 + * @param privateKey 私钥 + * @param characterEncoding 编码格式 + * @return 签名值 + */ + public static String sign(String content, PrivateKey privateKey, String characterEncoding) { return sign(content, privateKey, SIGN_ALGORITHMS, characterEncoding); } - /** - * RSA验签名检查 - * @param content 待签名数据 - * @param sign 签名值 - * @param publicKey 公钥 - * @param signAlgorithms 签名算法 - * @param characterEncoding 编码格式 - * @return 布尔值 - */ - public static boolean verify(String content, String sign, String publicKey, String signAlgorithms, String characterEncoding){ - try { - PublicKey pubKey = getPublicKey(publicKey, ALGORITHM); - java.security.Signature signature = java.security.Signature.getInstance(signAlgorithms); - signature.initVerify(pubKey); - signature.update(content.getBytes(characterEncoding) ); - return signature.verify(Base64.decode(sign) ); - } catch (Exception e) { - e.printStackTrace(); - } - return false; - } - - /** - * RSA验签名检查 - * @param content 待签名数据 - * @param sign 签名值 - * @param publicKey 公钥 - * @param signAlgorithms 签名算法 - * @param characterEncoding 编码格式 - * @return 布尔值 - */ - public static boolean verify(String content, String sign, PublicKey publicKey, String signAlgorithms, String characterEncoding){ - try { - java.security.Signature signature = java.security.Signature.getInstance(signAlgorithms); - signature.initVerify(publicKey); - signature.update(content.getBytes(characterEncoding) ); - return signature.verify(Base64.decode(sign) ); - } catch (Exception e) { - e.printStackTrace(); - } - return false; - } - /** - * RSA验签名检查 - * @param content 待签名数据 - * @param sign 签名值 - * @param publicKey 公钥 - * @param characterEncoding 编码格式 - * @return 布尔值 - */ - public static boolean verify(String content, String sign, String publicKey, String characterEncoding){ - - return verify(content, sign, publicKey, SIGN_ALGORITHMS, characterEncoding); - } - - - /** - * RSA验签名检查 - * @param content 待签名数据 - * @param sign 签名值 - * @param publicKey 公钥 - * @param characterEncoding 编码格式 - * @return 布尔值 - */ - public static boolean verify(String content, String sign, PublicKey publicKey, String characterEncoding){ - return verify(content, sign, publicKey, SIGN_ALGORITHMS, characterEncoding); - } - - /** - * 解密 - * @param content 密文 - * @param privateKey 商户私钥 - * @param characterEncoding 编码格式 - * @return 解密后的字符串 - * @throws GeneralSecurityException 解密异常 - * @throws IOException IOException - */ - public static String decrypt(String content, String privateKey, String characterEncoding) throws GeneralSecurityException, IOException { + /** + * RSA验签名检查 + * + * @param content 待签名数据 + * @param sign 签名值 + * @param publicKey 公钥 + * @param signAlgorithms 签名算法 + * @param characterEncoding 编码格式 + * @return 布尔值 + */ + public static boolean verify(String content, String sign, String publicKey, String signAlgorithms, String characterEncoding) { + try { + PublicKey pubKey = getPublicKey(publicKey, ALGORITHM); + java.security.Signature signature = java.security.Signature.getInstance(signAlgorithms); + signature.initVerify(pubKey); + signature.update(content.getBytes(characterEncoding)); + return signature.verify(Base64.decode(sign)); + } + catch (GeneralSecurityException e) { + LOG.error(e); + } + catch (IOException e) { + LOG.error(e); + } + return false; + } + + /** + * RSA验签名检查 + * + * @param content 待签名数据 + * @param sign 签名值 + * @param publicKey 公钥 + * @param signAlgorithms 签名算法 + * @param characterEncoding 编码格式 + * @return 布尔值 + */ + public static boolean verify(String content, String sign, PublicKey publicKey, String signAlgorithms, String characterEncoding) { + try { + java.security.Signature signature = java.security.Signature.getInstance(signAlgorithms); + signature.initVerify(publicKey); + signature.update(content.getBytes(characterEncoding)); + return signature.verify(Base64.decode(sign)); + } + catch (GeneralSecurityException e) { + LOG.error(e); + } + catch (IOException e) { + LOG.error(e); + } + return false; + } + + /** + * RSA验签名检查 + * + * @param content 待签名数据 + * @param sign 签名值 + * @param publicKey 公钥 + * @param characterEncoding 编码格式 + * @return 布尔值 + */ + public static boolean verify(String content, String sign, String publicKey, String characterEncoding) { + + return verify(content, sign, publicKey, SIGN_ALGORITHMS, characterEncoding); + } + + + /** + * RSA验签名检查 + * + * @param content 待签名数据 + * @param sign 签名值 + * @param publicKey 公钥 + * @param characterEncoding 编码格式 + * @return 布尔值 + */ + public static boolean verify(String content, String sign, PublicKey publicKey, String characterEncoding) { + return verify(content, sign, publicKey, SIGN_ALGORITHMS, characterEncoding); + } + + /** + * 解密 + * + * @param content 密文 + * @param privateKey 商户私钥 + * @param characterEncoding 编码格式 + * @return 解密后的字符串 + * @throws GeneralSecurityException 解密异常 + * @throws IOException IOException + */ + public static String decrypt(String content, String privateKey, String characterEncoding) throws GeneralSecurityException, IOException { PrivateKey prikey = getPrivateKey(privateKey); Cipher cipher = Cipher.getInstance(ALGORITHM); cipher.init(Cipher.DECRYPT_MODE, prikey); - try(InputStream ins = new ByteArrayInputStream(Base64.decode(content)); ByteArrayOutputStream writer = new ByteArrayOutputStream();) { - - //rsa解密的字节大小最多是128,将需要解密的内容,按128位拆开解密 - byte[] buf = new byte[128]; - int bufl; - while ((bufl = ins.read(buf)) != -1) { - byte[] block = null; - - if (buf.length == bufl) { - block = buf; - } else { - block = new byte[bufl]; - - for (int i = 0; i < bufl; i++) { - block[i] = buf[i]; - } - } - writer.write(cipher.doFinal(block)); - } - - return new String(writer.toByteArray(), characterEncoding); - } + try (InputStream ins = new ByteArrayInputStream(Base64.decode(content)); ByteArrayOutputStream writer = new ByteArrayOutputStream();) { + + //rsa解密的字节大小最多是128,将需要解密的内容,按128位拆开解密 + byte[] buf = new byte[128]; + int bufl; + while ((bufl = ins.read(buf)) != -1) { + byte[] block = null; + + if (buf.length == bufl) { + block = buf; + } + else { + block = new byte[bufl]; + + for (int i = 0; i < bufl; i++) { + block[i] = buf[i]; + } + } + writer.write(cipher.doFinal(block)); + } + + return new String(writer.toByteArray(), characterEncoding); + } + } + + + /** + * 得到私钥 + * + * @param key 密钥字符串(经过base64编码) + * @return 私钥 + * @throws GeneralSecurityException 加密异常 + */ + public static PrivateKey getPrivateKey(String key) throws GeneralSecurityException { + + byte[] keyBytes; + keyBytes = Base64.decode(key); + PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(keyBytes); + KeyFactory keyFactory = KeyFactory.getInstance(ALGORITHM); + PrivateKey privateKey = keyFactory.generatePrivate(keySpec); + return privateKey; + } + + /** + * 得到公钥 + * + * @param key 密钥字符串(经过base64编码) + * @param signAlgorithms 密钥类型 + * @return 公钥 + * @throws GeneralSecurityException 加密异常 + * @throws IOException 加密异常 + */ + public static PublicKey getPublicKey(String key, String signAlgorithms) throws GeneralSecurityException, IOException { + try (ByteArrayInputStream is = new ByteArrayInputStream(key.getBytes("ISO8859-1"))) { + return getPublicKey(is, signAlgorithms); + } } - /** - * 得到私钥 - * @param key 密钥字符串(经过base64编码) - * @throws GeneralSecurityException 加密异常 - * @return 私钥 - */ - public static PrivateKey getPrivateKey(String key) throws GeneralSecurityException { - - byte[] keyBytes; - keyBytes = Base64.decode(key); - PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(keyBytes); - KeyFactory keyFactory = KeyFactory.getInstance(ALGORITHM); - PrivateKey privateKey = keyFactory.generatePrivate(keySpec); - return privateKey; - } - - /** - * 得到公钥 - * @param key 密钥字符串(经过base64编码) - * @param signAlgorithms 密钥类型 - * @throws GeneralSecurityException 加密异常 - * @throws IOException 加密异常 - * @return 公钥 - */ - public static PublicKey getPublicKey(String key, String signAlgorithms) throws GeneralSecurityException, IOException { - try (ByteArrayInputStream is = new ByteArrayInputStream(key.getBytes("ISO8859-1"))){ - return getPublicKey(is, signAlgorithms); - } - } - - - /** - * 得到公钥 - * @param key 密钥字符串(经过base64编码) - * @throws GeneralSecurityException 加密异常 - * @throws IOException 加密异常 - * @return 公钥 - */ - public static PublicKey getPublicKey(String key) throws GeneralSecurityException, IOException { - - return getPublicKey(key, ALGORITHM); - } - - public static PublicKey getPublicKey(InputStream inputStream, String keyAlgorithm) throws IOException, GeneralSecurityException { - try (BufferedReader br = new BufferedReader(new InputStreamReader(inputStream));) { - StringBuilder sb = new StringBuilder(); - String readLine = null; - while ((readLine = br.readLine()) != null) { - if (readLine.charAt(0) == '-') { - continue; - } - sb.append(readLine); - sb.append('\r'); - } - X509EncodedKeySpec pubX509 = new X509EncodedKeySpec(Base64.decode(sb.toString())); - KeyFactory keyFactory = KeyFactory.getInstance(keyAlgorithm); - PublicKey publicKey = keyFactory.generatePublic(pubX509); - return publicKey; - } - } - - public static byte[] encrypt(byte[] plainBytes, PublicKey publicKey, int keyLength, int reserveSize, String cipherAlgorithm) throws IOException, GeneralSecurityException { - int keyByteSize = keyLength / 8; - int encryptBlockSize = keyByteSize - reserveSize; - int nBlock = plainBytes.length / encryptBlockSize; - if ((plainBytes.length % encryptBlockSize) != 0) { - nBlock += 1; - } - try (ByteArrayOutputStream outbuf = new ByteArrayOutputStream(nBlock * keyByteSize)) { - Cipher cipher = Cipher.getInstance(cipherAlgorithm); - cipher.init(Cipher.ENCRYPT_MODE, publicKey); - for (int offset = 0; offset < plainBytes.length; offset += encryptBlockSize) { - int inputLen = plainBytes.length - offset; - if (inputLen > encryptBlockSize) { - inputLen = encryptBlockSize; - } - byte[] encryptedBlock = cipher.doFinal(plainBytes, offset, inputLen); - outbuf.write(encryptedBlock); - } - outbuf.flush(); - return outbuf.toByteArray(); - } - } - public static String encrypt(String content, String publicKey, String cipherAlgorithm, String characterEncoding ) throws IOException, GeneralSecurityException { - return Base64.encode(RSA.encrypt(content.getBytes(characterEncoding), RSA.getPublicKey(publicKey),1024, 11, cipherAlgorithm)); - } + /** + * 得到公钥 + * + * @param key 密钥字符串(经过base64编码) + * @return 公钥 + * @throws GeneralSecurityException 加密异常 + * @throws IOException 加密异常 + */ + public static PublicKey getPublicKey(String key) throws GeneralSecurityException, IOException { + + return getPublicKey(key, ALGORITHM); + } + + public static PublicKey getPublicKey(InputStream inputStream, String keyAlgorithm) throws IOException, GeneralSecurityException { + try (BufferedReader br = new BufferedReader(new InputStreamReader(inputStream));) { + StringBuilder sb = new StringBuilder(); + String readLine = null; + while ((readLine = br.readLine()) != null) { + if (readLine.charAt(0) == '-') { + continue; + } + sb.append(readLine); + sb.append('\r'); + } + X509EncodedKeySpec pubX509 = new X509EncodedKeySpec(Base64.decode(sb.toString())); + KeyFactory keyFactory = KeyFactory.getInstance(keyAlgorithm); + PublicKey publicKey = keyFactory.generatePublic(pubX509); + return publicKey; + } + } + + public static byte[] encrypt(byte[] plainBytes, PublicKey publicKey, int keyLength, int reserveSize, String cipherAlgorithm) throws IOException, GeneralSecurityException { + int keyByteSize = keyLength / 8; + int encryptBlockSize = keyByteSize - reserveSize; + int nBlock = plainBytes.length / encryptBlockSize; + if ((plainBytes.length % encryptBlockSize) != 0) { + nBlock += 1; + } + try (ByteArrayOutputStream outbuf = new ByteArrayOutputStream(nBlock * keyByteSize)) { + Cipher cipher = Cipher.getInstance(cipherAlgorithm); + cipher.init(Cipher.ENCRYPT_MODE, publicKey); + for (int offset = 0; offset < plainBytes.length; offset += encryptBlockSize) { + int inputLen = plainBytes.length - offset; + if (inputLen > encryptBlockSize) { + inputLen = encryptBlockSize; + } + byte[] encryptedBlock = cipher.doFinal(plainBytes, offset, inputLen); + outbuf.write(encryptedBlock); + } + outbuf.flush(); + return outbuf.toByteArray(); + } + } + + public static String encrypt(String content, String publicKey, String cipherAlgorithm, String characterEncoding) throws IOException, GeneralSecurityException { + return Base64.encode(RSA.encrypt(content.getBytes(characterEncoding), RSA.getPublicKey(publicKey), 1024, 11, cipherAlgorithm)); + } } diff --git a/pay-java-common/src/main/java/com/egzosn/pay/common/util/sign/encrypt/sm3/SM3.java b/pay-java-common/src/main/java/com/egzosn/pay/common/util/sign/encrypt/sm3/SM3.java index 09f319b..c0ded5b 100644 --- a/pay-java-common/src/main/java/com/egzosn/pay/common/util/sign/encrypt/sm3/SM3.java +++ b/pay-java-common/src/main/java/com/egzosn/pay/common/util/sign/encrypt/sm3/SM3.java @@ -137,7 +137,8 @@ public class SM3 { private static int FFj(int X, int Y, int Z, int j) { if (j >= 0 && j <= 15) { return FF1j(X, Y, Z); - } else { + } + else { return FF2j(X, Y, Z); } } @@ -145,7 +146,8 @@ public class SM3 { private static int GGj(int X, int Y, int Z, int j) { if (j >= 0 && j <= 15) { return GG1j(X, Y, Z); - } else { + } + else { return GG2j(X, Y, Z); } } diff --git a/pay-java-common/src/main/java/com/egzosn/pay/common/util/sign/encrypt/sm3/SM3Digest.java b/pay-java-common/src/main/java/com/egzosn/pay/common/util/sign/encrypt/sm3/SM3Digest.java index cf62b24..6b3a792 100644 --- a/pay-java-common/src/main/java/com/egzosn/pay/common/util/sign/encrypt/sm3/SM3Digest.java +++ b/pay-java-common/src/main/java/com/egzosn/pay/common/util/sign/encrypt/sm3/SM3Digest.java @@ -1,22 +1,34 @@ package com.egzosn.pay.common.util.sign.encrypt.sm3; public class SM3Digest { - /** SM3值的长度 */ + /** + * SM3值的长度 + */ private static final int BYTE_LENGTH = 32; - /** SM3分组长度 */ + /** + * SM3分组长度 + */ private static final int BLOCK_LENGTH = 64; - /** 缓冲区长度 */ + /** + * 缓冲区长度 + */ private static final int BUFFER_LENGTH = BLOCK_LENGTH * 1; - /** 缓冲区 */ + /** + * 缓冲区 + */ private byte[] xBuf = new byte[BUFFER_LENGTH]; - /** 缓冲区偏移量 */ + /** + * 缓冲区偏移量 + */ private int xBufOff; - /** 初始向量 */ + /** + * 初始向量 + */ private byte[] V = SM3.iv.clone(); private int cntBlock = 0; @@ -25,7 +37,7 @@ public class SM3Digest { } - public SM3Digest(SM3Digest t){ + public SM3Digest(SM3Digest t) { System.arraycopy(t.xBuf, 0, this.xBuf, 0, t.xBuf.length); this.xBufOff = t.xBufOff; System.arraycopy(t.V, 0, this.V, 0, t.V.length); @@ -34,12 +46,11 @@ public class SM3Digest { /** * SM3结果输出 * - * @param out 保存SM3结构的缓冲区 + * @param out 保存SM3结构的缓冲区 * @param outOff 缓冲区偏移量 * @return 字节长度 */ - public int doFinal(byte[] out, int outOff) - { + public int doFinal(byte[] out, int outOff) { byte[] tmp = doFinal(); System.arraycopy(tmp, 0, out, 0, tmp.length); return BYTE_LENGTH; @@ -48,8 +59,7 @@ public class SM3Digest { /** * 重置 */ - public void reset() - { + public void reset() { xBufOff = 0; cntBlock = 0; V = SM3.iv.clone(); @@ -58,23 +68,20 @@ public class SM3Digest { /** * 明文输入 * - * @param in 明文输入缓冲区 + * @param in 明文输入缓冲区 * @param inOff 缓冲区偏移量 - * @param len 明文长度 + * @param len 明文长度 */ - public void update(byte[] in, int inOff, int len) - { + public void update(byte[] in, int inOff, int len) { int partLen = BUFFER_LENGTH - xBufOff; int inputLen = len; int dPos = inOff; - if (partLen < inputLen) - { + if (partLen < inputLen) { System.arraycopy(in, dPos, xBuf, xBufOff, partLen); inputLen -= partLen; dPos += partLen; doUpdate(); - while (inputLen > BUFFER_LENGTH) - { + while (inputLen > BUFFER_LENGTH) { System.arraycopy(in, dPos, xBuf, 0, BUFFER_LENGTH); inputLen -= BUFFER_LENGTH; dPos += BUFFER_LENGTH; @@ -89,11 +96,9 @@ public class SM3Digest { /** * 更新 */ - private void doUpdate() - { + private void doUpdate() { byte[] B = new byte[BLOCK_LENGTH]; - for (int i = 0; i < BUFFER_LENGTH; i += BLOCK_LENGTH) - { + for (int i = 0; i < BUFFER_LENGTH; i += BLOCK_LENGTH) { System.arraycopy(xBuf, i, B, 0, B.length); doHash(B); } @@ -102,37 +107,33 @@ public class SM3Digest { /** * 转16进制 + * * @param B 字节数组 */ - private void doHash(byte[] B) - { + private void doHash(byte[] B) { byte[] tmp = SM3.CF(V, B); System.arraycopy(tmp, 0, V, 0, V.length); cntBlock++; } - private byte[] doFinal() - { + private byte[] doFinal() { byte[] B = new byte[BLOCK_LENGTH]; byte[] buffer = new byte[xBufOff]; System.arraycopy(xBuf, 0, buffer, 0, buffer.length); byte[] tmp = SM3.padding(buffer, cntBlock); - for (int i = 0; i < tmp.length; i += BLOCK_LENGTH) - { + for (int i = 0; i < tmp.length; i += BLOCK_LENGTH) { System.arraycopy(tmp, i, B, 0, B.length); doHash(B); } return V; } - public void update(byte in) - { - byte[] buffer = new byte[] { in }; + public void update(byte in) { + byte[] buffer = new byte[]{in}; update(buffer, 0, 1); } - public int getDigestSize() - { + public int getDigestSize() { return BYTE_LENGTH; } diff --git a/pay-java-fuiou/src/main/java/com/egzosn/pay/fuiou/api/FuiouPayService.java b/pay-java-fuiou/src/main/java/com/egzosn/pay/fuiou/api/FuiouPayService.java index c56638c..8bae0c2 100644 --- a/pay-java-fuiou/src/main/java/com/egzosn/pay/fuiou/api/FuiouPayService.java +++ b/pay-java-fuiou/src/main/java/com/egzosn/pay/fuiou/api/FuiouPayService.java @@ -141,7 +141,7 @@ public class FuiouPayService extends BasePayService { */ public boolean signVerify(Map params, String responseSign) { - String sign = createSign(SignUtils.parameters2MD5Str(params, "|"), payConfigStorage.getInputCharset()); + String sign = createSign(SignUtils.parameters2Md5Str(params, "|"), payConfigStorage.getInputCharset()); return responseSign.equals(sign); } @@ -156,7 +156,7 @@ public class FuiouPayService extends BasePayService { LinkedHashMap params = new LinkedHashMap(3); params.put("mchnt_cd", payConfigStorage.getPid()); params.put("order_id", orderId); - params.put("md5", createSign(SignUtils.parameters2MD5Str(params, "|"), payConfigStorage.getInputCharset())); + params.put("md5", createSign(SignUtils.parameters2Md5Str(params, "|"), payConfigStorage.getInputCharset())); JSONObject resultJson = getHttpRequestTemplate().postForObject(getReqUrl() + URL_FuiouSmpAQueryGate + "?" + UriVariables.getMapToParameters(params), null, JSONObject.class); if (null == resultJson) { return false; @@ -178,7 +178,7 @@ public class FuiouPayService extends BasePayService { } Map parameters = getOrderInfo(order); - String sign = createSign(SignUtils.parameters2MD5Str(parameters, "|"), payConfigStorage.getInputCharset()); + String sign = createSign(SignUtils.parameters2Md5Str(parameters, "|"), payConfigStorage.getInputCharset()); parameters.put("md5", sign); return parameters; } @@ -381,7 +381,7 @@ public class FuiouPayService extends BasePayService { LinkedHashMap params = new LinkedHashMap<>(); params.put("mchnt_cd", payConfigStorage.getPid()); params.put("order_id", outTradeNo); - params.put("md5", createSign(SignUtils.parameters2MD5Str(params, "|"), payConfigStorage.getInputCharset())); + params.put("md5", createSign(SignUtils.parameters2Md5Str(params, "|"), payConfigStorage.getInputCharset())); JSONObject resultJson = getHttpRequestTemplate().postForObject(getReqUrl() + URL_FuiouSmpAQueryGate + "?" + UriVariables.getMapToParameters(params), null, JSONObject.class); return resultJson; } @@ -420,7 +420,7 @@ public class FuiouPayService extends BasePayService { //备注 params.put("rem", ""); params.putAll(refundOrder.getAttrs()); - params.put("md5", createSign(SignUtils.parameters2MD5Str(params, "|"), payConfigStorage.getInputCharset())); + params.put("md5", createSign(SignUtils.parameters2Md5Str(params, "|"), payConfigStorage.getInputCharset())); JSONObject resultJson = getHttpRequestTemplate().postForObject(getReqUrl() + URL_FuiouSmpRefundGate, params, JSONObject.class); return FuiouRefundResult.create(resultJson); } -- Gitee From 817471d27ef30d6add0393239212d20e43ded295 Mon Sep 17 00:00:00 2001 From: egzosn Date: Mon, 22 Feb 2021 23:05:05 +0800 Subject: [PATCH 181/299] =?UTF-8?q?=E5=B0=8F=E7=A8=8B=E5=BA=8F=E5=8F=91?= =?UTF-8?q?=E7=BA=A2=E5=8C=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/egzosn/pay/wx/api/WxPayService.java | 33 ++++++++++++++----- 1 file changed, 24 insertions(+), 9 deletions(-) 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 3e4ddc5..b0016a1 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 @@ -47,11 +47,13 @@ import com.egzosn.pay.common.bean.RefundOrder; import com.egzosn.pay.common.bean.SignType; import com.egzosn.pay.common.bean.TransactionType; import com.egzosn.pay.common.bean.TransferOrder; +import com.egzosn.pay.common.bean.TransferType; import com.egzosn.pay.common.bean.result.PayException; import com.egzosn.pay.common.exception.PayErrorException; import com.egzosn.pay.common.http.ClientHttpRequest; import com.egzosn.pay.common.http.HttpConfigStorage; import com.egzosn.pay.common.http.HttpStringEntity; +import com.egzosn.pay.common.http.UriVariables; import com.egzosn.pay.common.util.DateUtils; import com.egzosn.pay.common.util.Util; import com.egzosn.pay.common.util.XML; @@ -776,12 +778,12 @@ public class WxPayService extends BasePayService implements * @param order 转账订单 *
      *
-     *                                                     注意事项:
-     *                                                     ◆ 当返回错误码为“SYSTEMERROR”时,请不要更换商户订单号,一定要使用原商户订单号重试,否则可能造成重复支付等资金风险。
-     *                                                     ◆ XML具有可扩展性,因此返回参数可能会有新增,而且顺序可能不完全遵循此文档规范,如果在解析回包的时候发生错误,请商户务必不要换单重试,请商户联系客服确认付款情况。如果有新回包字段,会更新到此API文档中。
-     *                                                     ◆ 因为错误代码字段err_code的值后续可能会增加,所以商户如果遇到回包返回新的错误码,请商户务必不要换单重试,请商户联系客服确认付款情况。如果有新的错误码,会更新到此API文档中。
-     *                                                     ◆ 错误代码描述字段err_code_des只供人工定位问题时做参考,系统实现时请不要依赖这个字段来做自动化处理。
-     *                                                     
+ * 注意事项: + * ◆ 当返回错误码为“SYSTEMERROR”时,请不要更换商户订单号,一定要使用原商户订单号重试,否则可能造成重复支付等资金风险。 + * ◆ XML具有可扩展性,因此返回参数可能会有新增,而且顺序可能不完全遵循此文档规范,如果在解析回包的时候发生错误,请商户务必不要换单重试,请商户联系客服确认付款情况。如果有新回包字段,会更新到此API文档中。 + * ◆ 因为错误代码字段err_code的值后续可能会增加,所以商户如果遇到回包返回新的错误码,请商户务必不要换单重试,请商户联系客服确认付款情况。如果有新的错误码,会更新到此API文档中。 + * ◆ 错误代码描述字段err_code_des只供人工定位问题时做参考,系统实现时请不要依赖这个字段来做自动化处理。 + *
* @return 对应的转账结果 */ @Override @@ -912,17 +914,30 @@ public class WxPayService extends BasePayService implements public Map sendredpack(RedpackOrder redpackOrder) { Map parameters = new TreeMap(); redPackParam(redpackOrder, parameters); - if (WxSendredpackType.SENDGROUPREDPACK == redpackOrder.getTransferType()) { + final TransferType transferType = redpackOrder.getTransferType(); + if (WxSendredpackType.SENDGROUPREDPACK == transferType) { //现金红包,小程序红包默认传1.裂变红包取传入值,且需要大于3 parameters.put("total_num", Math.max(redpackOrder.getTotalNum(), 3)); parameters.put("amt_type", "ALL_RAND"); } - else if (WxSendredpackType.SENDMINIPROGRAMHB == redpackOrder.getTransferType()) { + else if (WxSendredpackType.SENDMINIPROGRAMHB == transferType) { parameters.put("notify_way", "MINI_PROGRAM_JSAPI"); } parameters.put(SIGN, createSign(SignUtils.parameterText(parameters, "&", SIGN), payConfigStorage.getInputCharset())); - return requestTemplate.postForObject(getReqUrl(redpackOrder.getTransferType()), XML.getMap2Xml(parameters), JSONObject.class); + final JSONObject resp = requestTemplate.postForObject(getReqUrl(redpackOrder.getTransferType()), XML.getMap2Xml(parameters), JSONObject.class); + if (WxSendredpackType.SENDMINIPROGRAMHB != transferType || FAIL.equals(resp.getString(RESULT_CODE))) { + return resp; + } + Map params = new TreeMap(); + params.put("appId", payConfigStorage.getAppId()); + params.put("timeStamp", System.currentTimeMillis() / 1000 + ""); + params.put("nonceStr", parameters.get(NONCE_STR)); + params.put("package", UriVariables.urlEncoder(resp.getString("package"))); + String paySign = createSign(SignUtils.parameterText(params), payConfigStorage.getInputCharset()); + params.put("signType", payConfigStorage.getSignType()); + params.put("paySign", paySign); + return params; } -- Gitee From 0f53fb4527f14e1379d5da539ac16aea84bfeeb6 Mon Sep 17 00:00:00 2001 From: egzosn Date: Mon, 22 Feb 2021 23:08:17 +0800 Subject: [PATCH 182/299] =?UTF-8?q?=E8=B4=A6=E5=8D=95=E7=B1=BB=E5=9E=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/egzosn/pay/ali/bean/AliPayBillType.java | 7 ++++--- .../com/egzosn/pay/baidu/bean/BaiduBillType.java | 15 ++++++++------- 2 files changed, 12 insertions(+), 10 deletions(-) diff --git a/pay-java-ali/src/main/java/com/egzosn/pay/ali/bean/AliPayBillType.java b/pay-java-ali/src/main/java/com/egzosn/pay/ali/bean/AliPayBillType.java index a91cea6..297398f 100644 --- a/pay-java-ali/src/main/java/com/egzosn/pay/ali/bean/AliPayBillType.java +++ b/pay-java-ali/src/main/java/com/egzosn/pay/ali/bean/AliPayBillType.java @@ -66,15 +66,16 @@ public enum AliPayBillType implements BillType { } /** - * 获取压缩类型 + * 获取文件类型 * - * @return 压缩类型 + * @return 文件类型 */ @Override - public String getTarType() { + public String getFileType() { return null; } + /** * 自定义属性 * diff --git a/pay-java-baidu/src/main/java/com/egzosn/pay/baidu/bean/BaiduBillType.java b/pay-java-baidu/src/main/java/com/egzosn/pay/baidu/bean/BaiduBillType.java index f3c5387..ded156b 100644 --- a/pay-java-baidu/src/main/java/com/egzosn/pay/baidu/bean/BaiduBillType.java +++ b/pay-java-baidu/src/main/java/com/egzosn/pay/baidu/bean/BaiduBillType.java @@ -62,20 +62,21 @@ public class BaiduBillType implements BillType { return datePattern; } - public void setDatePattern(String datePattern) { - this.datePattern = datePattern; - } - /** - * 获取压缩类型 + * 获取文件类型 * - * @return 压缩类型 + * @return 文件类型 */ @Override - public String getTarType() { + public String getFileType() { return null; } + public void setDatePattern(String datePattern) { + this.datePattern = datePattern; + } + + /** * 自定义属性 -- Gitee From 2413e091b6deceb51d73cd8d76d3440318437e32 Mon Sep 17 00:00:00 2001 From: egzosn Date: Tue, 23 Feb 2021 23:07:05 +0800 Subject: [PATCH 183/299] 2.14.0 --- README.md | 2 +- pay-java-ali/pom.xml | 2 +- pay-java-baidu/pom.xml | 2 +- pay-java-common/pom.xml | 2 +- pay-java-demo/pom.xml | 2 +- pay-java-fuiou/pom.xml | 2 +- pay-java-payoneer/pom.xml | 2 +- pay-java-paypal/pom.xml | 2 +- pay-java-union/pom.xml | 2 +- pay-java-wx-youdian/pom.xml | 2 +- pay-java-wx/pom.xml | 2 +- pay-java-yiji/pom.xml | 2 +- .../main/java/com/egzosn/pay/yiji/api/YiJiPayService.java | 3 --- pom.xml | 6 +++--- 14 files changed, 15 insertions(+), 18 deletions(-) diff --git a/README.md b/README.md index 8675b94..b906de9 100644 --- a/README.md +++ b/README.md @@ -21,7 +21,7 @@ com.egzosn {module-name} - 2.13.3-b1 + 2.14.0 ``` diff --git a/pay-java-ali/pom.xml b/pay-java-ali/pom.xml index f31a779..c56343e 100644 --- a/pay-java-ali/pom.xml +++ b/pay-java-ali/pom.xml @@ -5,7 +5,7 @@ pay-java-parent com.egzosn - 2.13.4-SNAPSHOT + 2.14.0 4.0.0 pay-java-ali diff --git a/pay-java-baidu/pom.xml b/pay-java-baidu/pom.xml index 228a3d0..db0db4f 100644 --- a/pay-java-baidu/pom.xml +++ b/pay-java-baidu/pom.xml @@ -5,7 +5,7 @@ pay-java-parent com.egzosn - 2.13.4-SNAPSHOT + 2.14.0 4.0.0 pay-java-baidu diff --git a/pay-java-common/pom.xml b/pay-java-common/pom.xml index e0ad8d1..f29abf3 100644 --- a/pay-java-common/pom.xml +++ b/pay-java-common/pom.xml @@ -5,7 +5,7 @@ pay-java-parent com.egzosn - 2.13.4-SNAPSHOT + 2.14.0 4.0.0 jar diff --git a/pay-java-demo/pom.xml b/pay-java-demo/pom.xml index 7c0de2a..53690b5 100644 --- a/pay-java-demo/pom.xml +++ b/pay-java-demo/pom.xml @@ -5,7 +5,7 @@ pay-java-parent com.egzosn - 2.13.4-SNAPSHOT + 2.14.0 4.0.0 war diff --git a/pay-java-fuiou/pom.xml b/pay-java-fuiou/pom.xml index 6a834c7..beb641d 100644 --- a/pay-java-fuiou/pom.xml +++ b/pay-java-fuiou/pom.xml @@ -5,7 +5,7 @@ pay-java-parent com.egzosn - 2.13.4-SNAPSHOT + 2.14.0 4.0.0 pay-java-fuiou diff --git a/pay-java-payoneer/pom.xml b/pay-java-payoneer/pom.xml index 9df1ded..fcf63bf 100644 --- a/pay-java-payoneer/pom.xml +++ b/pay-java-payoneer/pom.xml @@ -5,7 +5,7 @@ pay-java-parent com.egzosn - 2.13.4-SNAPSHOT + 2.14.0 4.0.0 pay-java-payoneer diff --git a/pay-java-paypal/pom.xml b/pay-java-paypal/pom.xml index 0abbb06..5926fa1 100644 --- a/pay-java-paypal/pom.xml +++ b/pay-java-paypal/pom.xml @@ -5,7 +5,7 @@ pay-java-parent com.egzosn - 2.13.4-SNAPSHOT + 2.14.0 4.0.0 diff --git a/pay-java-union/pom.xml b/pay-java-union/pom.xml index 6c539c1..7c412a0 100644 --- a/pay-java-union/pom.xml +++ b/pay-java-union/pom.xml @@ -5,7 +5,7 @@ pay-java-parent com.egzosn - 2.13.4-SNAPSHOT + 2.14.0 4.0.0 diff --git a/pay-java-wx-youdian/pom.xml b/pay-java-wx-youdian/pom.xml index 2081227..2aee8c8 100644 --- a/pay-java-wx-youdian/pom.xml +++ b/pay-java-wx-youdian/pom.xml @@ -5,7 +5,7 @@ pay-java-parent com.egzosn - 2.13.4-SNAPSHOT + 2.14.0 4.0.0 pay-java-wx-youdian diff --git a/pay-java-wx/pom.xml b/pay-java-wx/pom.xml index dfe0e5d..7aed12a 100644 --- a/pay-java-wx/pom.xml +++ b/pay-java-wx/pom.xml @@ -5,7 +5,7 @@ pay-java-parent com.egzosn - 2.13.4-SNAPSHOT + 2.14.0 4.0.0 pay-java-wx diff --git a/pay-java-yiji/pom.xml b/pay-java-yiji/pom.xml index 0a3ac4e..5d0199a 100644 --- a/pay-java-yiji/pom.xml +++ b/pay-java-yiji/pom.xml @@ -5,7 +5,7 @@ pay-java-parent com.egzosn - 2.13.4-SNAPSHOT + 2.14.0 4.0.0 diff --git a/pay-java-yiji/src/main/java/com/egzosn/pay/yiji/api/YiJiPayService.java b/pay-java-yiji/src/main/java/com/egzosn/pay/yiji/api/YiJiPayService.java index 5d42fbd..ba19707 100644 --- a/pay-java-yiji/src/main/java/com/egzosn/pay/yiji/api/YiJiPayService.java +++ b/pay-java-yiji/src/main/java/com/egzosn/pay/yiji/api/YiJiPayService.java @@ -53,9 +53,6 @@ public class YiJiPayService extends BasePayService { public static final String SIGN = "sign"; - public static final String SUCCESS_CODE = "10000"; - - public static final String CODE = "code"; /** * 获取对应的请求地址 diff --git a/pom.xml b/pom.xml index fc3322b..3f51c77 100644 --- a/pom.xml +++ b/pom.xml @@ -7,7 +7,7 @@ com.egzosn pay-java-parent pom - 2.13.4-SNAPSHOT + 2.14.0 Pay Java - Parent Pay Java Parent @@ -58,13 +58,13 @@ pay-java-paypal pay-java-yiji pay-java-baidu - pay-java-demo + - 2.13.4-SNAPSHOT + 2.14.0 4.5.4 1.2.17 1.2.73 -- Gitee From 5186ce9f3c379a752daa65be7efab621218c8df0 Mon Sep 17 00:00:00 2001 From: egzosn Date: Tue, 23 Feb 2021 23:07:21 +0800 Subject: [PATCH 184/299] 2.14.0 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 3f51c77..02e8d5e 100644 --- a/pom.xml +++ b/pom.xml @@ -58,7 +58,7 @@ pay-java-paypal pay-java-yiji pay-java-baidu - + pay-java-demo -- Gitee From 1b1db0f32101826157d8c32b0d3f55dc769355d1 Mon Sep 17 00:00:00 2001 From: egzosn Date: Wed, 24 Feb 2021 01:56:23 +0800 Subject: [PATCH 185/299] 2.14.1 --- README.md | 2 +- pay-java-ali/pom.xml | 2 +- pay-java-baidu/pom.xml | 2 +- pay-java-common/pom.xml | 2 +- pay-java-demo/pom.xml | 2 +- pay-java-fuiou/pom.xml | 2 +- pay-java-payoneer/pom.xml | 2 +- pay-java-paypal/pom.xml | 2 +- pay-java-union/pom.xml | 2 +- pay-java-wx-youdian/pom.xml | 2 +- pay-java-wx/pom.xml | 2 +- pay-java-yiji/pom.xml | 2 +- pom.xml | 4 ++-- 13 files changed, 14 insertions(+), 14 deletions(-) diff --git a/README.md b/README.md index b906de9..a3807e7 100644 --- a/README.md +++ b/README.md @@ -21,7 +21,7 @@ com.egzosn {module-name} - 2.14.0 + 2.14.1 ``` diff --git a/pay-java-ali/pom.xml b/pay-java-ali/pom.xml index c56343e..a791d6f 100644 --- a/pay-java-ali/pom.xml +++ b/pay-java-ali/pom.xml @@ -5,7 +5,7 @@ pay-java-parent com.egzosn - 2.14.0 + 2.14.1 4.0.0 pay-java-ali diff --git a/pay-java-baidu/pom.xml b/pay-java-baidu/pom.xml index db0db4f..d835c66 100644 --- a/pay-java-baidu/pom.xml +++ b/pay-java-baidu/pom.xml @@ -5,7 +5,7 @@ pay-java-parent com.egzosn - 2.14.0 + 2.14.1 4.0.0 pay-java-baidu diff --git a/pay-java-common/pom.xml b/pay-java-common/pom.xml index f29abf3..1082296 100644 --- a/pay-java-common/pom.xml +++ b/pay-java-common/pom.xml @@ -5,7 +5,7 @@ pay-java-parent com.egzosn - 2.14.0 + 2.14.1 4.0.0 jar diff --git a/pay-java-demo/pom.xml b/pay-java-demo/pom.xml index 53690b5..160cca1 100644 --- a/pay-java-demo/pom.xml +++ b/pay-java-demo/pom.xml @@ -5,7 +5,7 @@ pay-java-parent com.egzosn - 2.14.0 + 2.14.1 4.0.0 war diff --git a/pay-java-fuiou/pom.xml b/pay-java-fuiou/pom.xml index beb641d..183e19a 100644 --- a/pay-java-fuiou/pom.xml +++ b/pay-java-fuiou/pom.xml @@ -5,7 +5,7 @@ pay-java-parent com.egzosn - 2.14.0 + 2.14.1 4.0.0 pay-java-fuiou diff --git a/pay-java-payoneer/pom.xml b/pay-java-payoneer/pom.xml index fcf63bf..ebff04b 100644 --- a/pay-java-payoneer/pom.xml +++ b/pay-java-payoneer/pom.xml @@ -5,7 +5,7 @@ pay-java-parent com.egzosn - 2.14.0 + 2.14.1 4.0.0 pay-java-payoneer diff --git a/pay-java-paypal/pom.xml b/pay-java-paypal/pom.xml index 5926fa1..887ff3e 100644 --- a/pay-java-paypal/pom.xml +++ b/pay-java-paypal/pom.xml @@ -5,7 +5,7 @@ pay-java-parent com.egzosn - 2.14.0 + 2.14.1 4.0.0 diff --git a/pay-java-union/pom.xml b/pay-java-union/pom.xml index 7c412a0..d52eb4d 100644 --- a/pay-java-union/pom.xml +++ b/pay-java-union/pom.xml @@ -5,7 +5,7 @@ pay-java-parent com.egzosn - 2.14.0 + 2.14.1 4.0.0 diff --git a/pay-java-wx-youdian/pom.xml b/pay-java-wx-youdian/pom.xml index 2aee8c8..80500ae 100644 --- a/pay-java-wx-youdian/pom.xml +++ b/pay-java-wx-youdian/pom.xml @@ -5,7 +5,7 @@ pay-java-parent com.egzosn - 2.14.0 + 2.14.1 4.0.0 pay-java-wx-youdian diff --git a/pay-java-wx/pom.xml b/pay-java-wx/pom.xml index 7aed12a..f68a0b6 100644 --- a/pay-java-wx/pom.xml +++ b/pay-java-wx/pom.xml @@ -5,7 +5,7 @@ pay-java-parent com.egzosn - 2.14.0 + 2.14.1 4.0.0 pay-java-wx diff --git a/pay-java-yiji/pom.xml b/pay-java-yiji/pom.xml index 5d0199a..0411dfd 100644 --- a/pay-java-yiji/pom.xml +++ b/pay-java-yiji/pom.xml @@ -5,7 +5,7 @@ pay-java-parent com.egzosn - 2.14.0 + 2.14.1 4.0.0 diff --git a/pom.xml b/pom.xml index 02e8d5e..3874de6 100644 --- a/pom.xml +++ b/pom.xml @@ -7,7 +7,7 @@ com.egzosn pay-java-parent pom - 2.14.0 + 2.14.1 Pay Java - Parent Pay Java Parent @@ -64,7 +64,7 @@ - 2.14.0 + 2.14.1 4.5.4 1.2.17 1.2.73 -- Gitee From 3e98301f6d171349b909135a22a9810f995936b6 Mon Sep 17 00:00:00 2001 From: egzosn Date: Wed, 24 Feb 2021 22:49:09 +0800 Subject: [PATCH 186/299] 2.14.1 --- .../com/egzosn/pay/ali/api/AliPayService.java | 2 +- .../pay/ali/utils/AntCertificationUtil.java | 1 + pay-java-ali/src/test/java/PayTest.java | 2 +- .../egzosn/pay/baidu/api/BaiduPayService.java | 2 +- .../pay/baidu/bean/BaiduRefundOrder.java | 1 + .../pay/baidu/api/BaiduPayServiceTest.java | 2 +- .../pay/common/api/BasePayConfigStorage.java | 2 +- .../egzosn/pay/common/api/BasePayService.java | 2 +- .../com/egzosn/pay/common/api/Callback.java | 2 +- .../com/egzosn/pay/common/bean/BillType.java | 6 ++++-- .../egzosn/pay/common/bean/MethodType.java | 2 +- .../com/egzosn/pay/common/bean/SignType.java | 2 +- .../common/bean/outbuilder/JsonBuilder.java | 2 +- .../pay/common/bean/result/PayException.java | 2 +- .../pay/common/http/ClientHttpRequest.java | 2 +- .../pay/common/http/HttpConfigStorage.java | 2 +- .../pay/common/http/HttpRequestTemplate.java | 2 +- .../java/com/egzosn/pay/common/util/XML.java | 11 +++++----- .../pay/demo/controller/AliPayController.java | 5 +++-- .../demo/controller/FuiouPayController.java | 2 +- .../pay/demo/controller/PayController.java | 2 +- .../demo/controller/PayPalPayController.java | 2 +- .../controller/PayPalV2PayController.java | 2 +- .../demo/controller/UnionPayController.java | 2 +- .../pay/demo/controller/WxPayController.java | 4 ++-- .../pay/demo/dao/ApyAccountRepository.java | 10 ++++----- .../egzosn/pay/demo/entity/ApyAccount.java | 2 +- .../com/egzosn/pay/demo/entity/PayType.java | 6 +++--- .../egzosn/pay/demo/request/QueryOrder.java | 2 +- .../pay/demo/service/ApyAccountService.java | 2 +- .../egzosn/pay/demo/service/PayResponse.java | 2 +- .../interceptor/AliPayMessageInterceptor.java | 2 +- .../YoudianPayMessageInterceptor.java | 2 +- .../src/main/resources/apy_account.sql | 2 +- .../payoneer/api/PayoneerConfigStorage.java | 1 + .../pay/paypal/api/PayPalPayService.java | 2 +- .../egzosn/pay/paypal/bean/order/Error.java | 4 +--- .../egzosn/pay/paypal/bean/order/Links.java | 4 +--- .../pay/paypal/v2/api/PayPalPayService.java | 4 ++-- .../pay/union/api/UnionPayConfigStorage.java | 1 - .../pay/wx/youdian/bean/YdPayError.java | 2 +- .../com/egzosn/pay/wx/api/WxBillService.java | 2 +- .../com/egzosn/pay/wx/api/WxPayService.java | 12 +++++------ .../egzosn/pay/wx/bean/WxTransactionType.java | 21 ++++++++++++------- pay-java-wx/src/test/java/PayTest.java | 2 +- pom.xml | 2 ++ 46 files changed, 80 insertions(+), 73 deletions(-) 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 1fb432b..6e6792f 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 @@ -301,7 +301,7 @@ public class AliPayService extends BasePayService { */ private Map getPublicParameters(TransactionType transactionType) { Map orderInfo = new TreeMap<>(); - orderInfo.put("app_id", payConfigStorage.getAppid()); + orderInfo.put("app_id", payConfigStorage.getAppId()); orderInfo.put("method", transactionType.getMethod()); orderInfo.put("charset", payConfigStorage.getInputCharset()); orderInfo.put("timestamp", DateUtils.format(new Date())); diff --git a/pay-java-ali/src/main/java/com/egzosn/pay/ali/utils/AntCertificationUtil.java b/pay-java-ali/src/main/java/com/egzosn/pay/ali/utils/AntCertificationUtil.java index 7dae2ac..e225431 100644 --- a/pay-java-ali/src/main/java/com/egzosn/pay/ali/utils/AntCertificationUtil.java +++ b/pay-java-ali/src/main/java/com/egzosn/pay/ali/utils/AntCertificationUtil.java @@ -54,6 +54,7 @@ public class AntCertificationUtil { * * @param certContent 需要验证的目标证书或者证书链 * @param rootCertContent 可信根证书列表 + * @return 是否校验成功 */ public static boolean isTrusted(String certContent, String rootCertContent) { X509Certificate[] certificates; diff --git a/pay-java-ali/src/test/java/PayTest.java b/pay-java-ali/src/test/java/PayTest.java index c098259..b22ba22 100644 --- a/pay-java-ali/src/test/java/PayTest.java +++ b/pay-java-ali/src/test/java/PayTest.java @@ -50,7 +50,7 @@ public class PayTest { AliPayConfigStorage aliPayConfigStorage = new AliPayConfigStorage(); aliPayConfigStorage.setPid("合作者id"); - aliPayConfigStorage.setAppid("应用id"); + aliPayConfigStorage.setAppId("应用id"); //普通公钥方式与证书公钥方式为两者取其一的方式 keyPublic(aliPayConfigStorage); // certKeyPublic(aliPayConfigStorage); diff --git a/pay-java-baidu/src/main/java/com/egzosn/pay/baidu/api/BaiduPayService.java b/pay-java-baidu/src/main/java/com/egzosn/pay/baidu/api/BaiduPayService.java index b94f00f..a1e3267 100644 --- a/pay-java-baidu/src/main/java/com/egzosn/pay/baidu/api/BaiduPayService.java +++ b/pay-java-baidu/src/main/java/com/egzosn/pay/baidu/api/BaiduPayService.java @@ -112,7 +112,7 @@ public class BaiduPayService extends BasePayService { String appKey = payConfigStorage.getAppKey(); Map result = new HashMap<>(); result.put(APP_KEY, appKey); - result.put(APP_ID, payConfigStorage.getAppid()); + result.put(APP_ID, payConfigStorage.getAppId()); return result; } diff --git a/pay-java-baidu/src/main/java/com/egzosn/pay/baidu/bean/BaiduRefundOrder.java b/pay-java-baidu/src/main/java/com/egzosn/pay/baidu/bean/BaiduRefundOrder.java index 91d6510..50974eb 100644 --- a/pay-java-baidu/src/main/java/com/egzosn/pay/baidu/bean/BaiduRefundOrder.java +++ b/pay-java-baidu/src/main/java/com/egzosn/pay/baidu/bean/BaiduRefundOrder.java @@ -54,6 +54,7 @@ public class BaiduRefundOrder extends RefundOrder { /** * 业务方退款批次id,退款业务流水唯一编号,发起部分退款时必传 + * @param bizRefundBatchId 业务方退款批次id */ public void setBizRefundBatchId(String bizRefundBatchId) { setRefundNo(bizRefundBatchId); diff --git a/pay-java-baidu/src/test/java/com/egzosn/pay/baidu/api/BaiduPayServiceTest.java b/pay-java-baidu/src/test/java/com/egzosn/pay/baidu/api/BaiduPayServiceTest.java index 6f9b5c2..9cb7434 100644 --- a/pay-java-baidu/src/test/java/com/egzosn/pay/baidu/api/BaiduPayServiceTest.java +++ b/pay-java-baidu/src/test/java/com/egzosn/pay/baidu/api/BaiduPayServiceTest.java @@ -13,7 +13,7 @@ public class BaiduPayServiceTest { @Test public void orderInfo() { BaiduPayConfigStorage configStorage = new BaiduPayConfigStorage(); - configStorage.setAppid("APP ID"); + configStorage.setAppId("APP ID"); configStorage.setAppKey("APP KEY"); configStorage.setDealId("DEAL ID"); configStorage.setKeyPublic("KEY PUBLIC"); diff --git a/pay-java-common/src/main/java/com/egzosn/pay/common/api/BasePayConfigStorage.java b/pay-java-common/src/main/java/com/egzosn/pay/common/api/BasePayConfigStorage.java index b8b2b17..f813936 100644 --- a/pay-java-common/src/main/java/com/egzosn/pay/common/api/BasePayConfigStorage.java +++ b/pay-java-common/src/main/java/com/egzosn/pay/common/api/BasePayConfigStorage.java @@ -7,7 +7,7 @@ import java.util.concurrent.locks.Lock; /** * 支付基础配置存储 * - * @author: egan + * @author egan *
  *     email egzosn@gmail.com
  *     date 2017/3/5 20:33
diff --git a/pay-java-common/src/main/java/com/egzosn/pay/common/api/BasePayService.java b/pay-java-common/src/main/java/com/egzosn/pay/common/api/BasePayService.java
index 43aa78e..38e00a9 100644
--- a/pay-java-common/src/main/java/com/egzosn/pay/common/api/BasePayService.java
+++ b/pay-java-common/src/main/java/com/egzosn/pay/common/api/BasePayService.java
@@ -31,7 +31,7 @@ import com.egzosn.pay.common.util.str.StringUtils;
 /**
  * 支付基础服务
  *
- * @author: egan
+ * @author egan
  * 
  *      email egzosn@gmail.com
  *      date 2017/3/5 20:36
diff --git a/pay-java-common/src/main/java/com/egzosn/pay/common/api/Callback.java b/pay-java-common/src/main/java/com/egzosn/pay/common/api/Callback.java
index dbb537b..4328942 100644
--- a/pay-java-common/src/main/java/com/egzosn/pay/common/api/Callback.java
+++ b/pay-java-common/src/main/java/com/egzosn/pay/common/api/Callback.java
@@ -21,7 +21,7 @@ import java.util.Map;
 
 /**
  * 回调,可用于类型转换
- * @author: egan
+ * @author egan
  * 
  *     email egzosn@gmail.com
  *     date 2017/3/7 18:55
diff --git a/pay-java-common/src/main/java/com/egzosn/pay/common/bean/BillType.java b/pay-java-common/src/main/java/com/egzosn/pay/common/bean/BillType.java
index bf65b8f..a85bb11 100644
--- a/pay-java-common/src/main/java/com/egzosn/pay/common/bean/BillType.java
+++ b/pay-java-common/src/main/java/com/egzosn/pay/common/bean/BillType.java
@@ -3,8 +3,10 @@ package com.egzosn.pay.common.bean;
 /**
  * 账单类型
  * @author Egan
- * @email egzosn@gmail.com
- * @date 2021/2/22
+ * 
+ * email egzosn@gmail.com
+ * date 2021/2/22
+ * 
*/ public interface BillType { /** 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 dd9f31b..72d9c9b 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 @@ -18,7 +18,7 @@ package com.egzosn.pay.common.bean; /** - * @author: egan + * @author egan *
  *     email egzosn@gmail.com
  *     date 2017/2/7 9:52
diff --git a/pay-java-common/src/main/java/com/egzosn/pay/common/bean/SignType.java b/pay-java-common/src/main/java/com/egzosn/pay/common/bean/SignType.java
index 11230e8..4f9c159 100644
--- a/pay-java-common/src/main/java/com/egzosn/pay/common/bean/SignType.java
+++ b/pay-java-common/src/main/java/com/egzosn/pay/common/bean/SignType.java
@@ -6,7 +6,7 @@ import java.util.*;
 /**
  * 签名类型
  *
- * @author: egan
+ * @author egan
  * 
  * email egzosn@gmail.com
  * date 2019/12/08 13:30
diff --git a/pay-java-common/src/main/java/com/egzosn/pay/common/bean/outbuilder/JsonBuilder.java b/pay-java-common/src/main/java/com/egzosn/pay/common/bean/outbuilder/JsonBuilder.java
index 726a2ed..179865e 100644
--- a/pay-java-common/src/main/java/com/egzosn/pay/common/bean/outbuilder/JsonBuilder.java
+++ b/pay-java-common/src/main/java/com/egzosn/pay/common/bean/outbuilder/JsonBuilder.java
@@ -5,7 +5,7 @@ import com.alibaba.fastjson.JSONObject;
 import com.egzosn.pay.common.bean.PayOutMessage;
 
 /**
- * @author: egan
+ * @author egan
  *  
  *      email egzosn@gmail.com
  *      date 2017/1/13 14:30
diff --git a/pay-java-common/src/main/java/com/egzosn/pay/common/bean/result/PayException.java b/pay-java-common/src/main/java/com/egzosn/pay/common/bean/result/PayException.java
index e54abc6..2e738ac 100644
--- a/pay-java-common/src/main/java/com/egzosn/pay/common/bean/result/PayException.java
+++ b/pay-java-common/src/main/java/com/egzosn/pay/common/bean/result/PayException.java
@@ -19,7 +19,7 @@ package com.egzosn.pay.common.bean.result;
 
 /**
  * 支付异常
- * @author: egan
+ * @author egan
  *  
  *      email egzosn@gmail.com
  *      date 2017/3/7 12:32
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 f58b25f..8b6fcce 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
@@ -39,7 +39,7 @@ import com.egzosn.pay.common.util.str.StringUtils;
 /**
  * 一个HTTP请求的客户端
  *
- * @author: egan
+ * @author egan
  * 
  * email egzosn@gmail.com
  * date 2017/3/4 17:56
diff --git a/pay-java-common/src/main/java/com/egzosn/pay/common/http/HttpConfigStorage.java b/pay-java-common/src/main/java/com/egzosn/pay/common/http/HttpConfigStorage.java
index 274b9d9..f5cfc70 100644
--- a/pay-java-common/src/main/java/com/egzosn/pay/common/http/HttpConfigStorage.java
+++ b/pay-java-common/src/main/java/com/egzosn/pay/common/http/HttpConfigStorage.java
@@ -7,7 +7,7 @@ import java.io.*;
 
 /**
  * HTTP 配置
- * @author: egan
+ * @author egan
  *  
  * email egzosn@gmail.com
  * date 2017/3/3 20:48
diff --git a/pay-java-common/src/main/java/com/egzosn/pay/common/http/HttpRequestTemplate.java b/pay-java-common/src/main/java/com/egzosn/pay/common/http/HttpRequestTemplate.java
index ceaf45b..ae4ff73 100644
--- a/pay-java-common/src/main/java/com/egzosn/pay/common/http/HttpRequestTemplate.java
+++ b/pay-java-common/src/main/java/com/egzosn/pay/common/http/HttpRequestTemplate.java
@@ -36,7 +36,7 @@ import java.util.Map;
 
 /**
  * http请求工具
- * @author: egan
+ * @author egan
  *  
  * email egzosn@gmail.com 
* date 2017/3/3 21:33 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 675bdfd..5ebf06c 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 @@ -125,7 +125,7 @@ public class XML { */ public static T toBean(String content, Class clazz) { - if (null == content || "".equals(content)) { + if (StringUtils.isEmpty(content)) { return null; } try (InputStream in = new ByteArrayInputStream(content.getBytes("UTF-8"))) { @@ -207,9 +207,8 @@ public class XML { * @param clazz 需要转化的类 * @param 类型 * @return 对应的对象 - * @throws IOException xml io转化异常 */ - public static T inputStream2Bean(InputStream in, Class clazz) throws IOException { + public static T inputStream2Bean(InputStream in, Class clazz) { JSON json = toJSONObject(in); return json.toJavaObject(clazz); } @@ -220,7 +219,7 @@ public class XML { * @return 整理完成的参数集 * @throws IOException xml io转化异常 */ - public static Map inputStream2Map(InputStream in, Map m) throws IOException { + public static Map inputStream2Map(InputStream in, Map m) throws IOException { if (null == m) { m = new JSONObject(); } @@ -351,10 +350,10 @@ public class XML { private static void object2Xml(Object value, Document document, org.w3c.dom.Element element) { if (value instanceof Map) { - map2Xml((Map) value, document, element); + map2Xml((Map) value, document, element); } else if (value instanceof List) { - List vs = (List) value; + List vs = (List) value; for (Object v : vs) { object2Xml(v, document, element); } diff --git a/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/AliPayController.java b/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/AliPayController.java index 77a25b9..acd961b 100644 --- a/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/AliPayController.java +++ b/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/AliPayController.java @@ -39,7 +39,7 @@ import com.egzosn.pay.demo.service.interceptor.AliPayMessageInterceptor; /** * 发起支付入口 * - * @author: egan + * @author egan * email egzosn@gmail.com * date 2016/11/18 0:25 */ @@ -81,7 +81,8 @@ public class AliPayController { public void init() { AliPayConfigStorage aliPayConfigStorage = new AliPayConfigStorage(); aliPayConfigStorage.setPid("2088102169916436"); - aliPayConfigStorage.setAppid("2016080400165436"); + aliPayConfigStorage.setAppId("2016080400165436"); +// aliPayConfigStorage.setAppAuthToken("ISV代商户代用,指定appAuthToken"); //普通公钥方式与证书公钥方式为两者取其一的方式 keyPublic(aliPayConfigStorage); aliPayConfigStorage.setKeyPrivate("MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBAKroe/8h5vC4L6T+B2WdXiVwGsMvUKgb2XsKix6VY3m2wcf6tyzpNRDCNykbIwGtaeo7FshN+qZxdXHLiIam9goYncBit/8ojfLGy2gLxO/PXfzGxYGs0KsDZ+ryVPPmE34ZZ8jiJpR0ygzCFl8pN3QJPJRGTJn5+FTT9EF/9zyZAgMBAAECgYAktngcYC35u7cQXDk+jMVyiVhWYU2ULxdSpPspgLGzrZyG1saOcTIi/XVX8Spd6+B6nmLQeF/FbU3rOeuD8U2clzul2Z2YMbJ0FYay9oVZFfp5gTEFpFRTVfzqUaZQBIjJe/xHL9kQVqc5xHlE/LVA27/Kx3dbC35Y7B4EVBDYAQJBAOhsX8ZreWLKPhXiXHTyLmNKhOHJc+0tFH7Ktise/0rNspojU7o9prOatKpNylp9v6kux7migcMRdVUWWiVe+4ECQQC8PqsuEz7B0yqirQchRg1DbHjh64bw9Kj82EN1/NzOUd53tP9tg+SO97EzsibK1F7tOcuwqsa7n2aY48mQ+y0ZAkBndA2xcRcnvOOjtAz5VO8G7R12rse181HjGfG6AeMadbKg30aeaGCyIxN1loiSfNR5xsPJwibGIBg81mUrqzqBAkB+K6rkaPXJR9XtzvdWb/N3235yPkDlw7Z4MiOVM3RzvR/VMDV7m8lXoeDde2zQyeMOMYy6ztwA6WgE1bhGOnQRAkEAouUBv1sVdSBlsexX15qphOmAevzYrpufKgJIRLFWQxroXMS7FTesj+f+FmGrpPCxIde1dqJ8lqYLTyJmbzMPYw=="); diff --git a/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/FuiouPayController.java b/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/FuiouPayController.java index e3e79a0..046001e 100644 --- a/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/FuiouPayController.java +++ b/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/FuiouPayController.java @@ -20,7 +20,7 @@ import java.util.UUID; /** * 发起支付入口 * - * @author: egan + * @author egan * email egzosn@gmail.com * date 2016/11/18 0:25 */ 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 7f1b916..0789efb 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 @@ -46,7 +46,7 @@ import com.egzosn.pay.wx.bean.WxTransactionType; /** * 发起支付入口 * - * @author: egan + * @author egan * email egzosn@gmail.com * date 2016/11/18 0:25 */ diff --git a/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/PayPalPayController.java b/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/PayPalPayController.java index a9b6a08..934b8b1 100644 --- a/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/PayPalPayController.java +++ b/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/PayPalPayController.java @@ -26,7 +26,7 @@ import com.egzosn.pay.paypal.bean.PayPalTransactionType; /** * 发起支付入口 * - * @author: egan + * @author egan * email egzosn@gmail.com * date 2018/05/06 10:30 */ diff --git a/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/PayPalV2PayController.java b/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/PayPalV2PayController.java index 95abfd1..94917e7 100644 --- a/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/PayPalV2PayController.java +++ b/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/PayPalV2PayController.java @@ -27,7 +27,7 @@ import com.egzosn.pay.paypal.v2.bean.order.ShippingDetail; /** * 发起支付入口 * - * @author: egan + * @author egan * email egzosn@gmail.com * date 2018/05/06 10:30 */ diff --git a/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/UnionPayController.java b/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/UnionPayController.java index 15a6b40..0b9493f 100644 --- a/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/UnionPayController.java +++ b/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/UnionPayController.java @@ -32,7 +32,7 @@ import static com.egzosn.pay.union.bean.UnionTransactionType.WEB; /** * 银联相关 * - * @author: egan + * @author egan * email egzosn@gmail.com * date 2016/11/18 0:25 */ diff --git a/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/WxPayController.java b/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/WxPayController.java index 1269ebc..4b0b241 100644 --- a/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/WxPayController.java +++ b/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/WxPayController.java @@ -24,7 +24,7 @@ import java.util.UUID; /** * 发起支付入口 * - * @author: egan + * @author egan * email egzosn@gmail.com * date 2016/11/18 0:25 */ @@ -42,7 +42,7 @@ public class WxPayController { @PostConstruct public void init() { WxPayConfigStorage wxPayConfigStorage = new WxPayConfigStorage(); - wxPayConfigStorage.setAppid("公众账号ID"); + wxPayConfigStorage.setAppId("公众账号ID"); wxPayConfigStorage.setMchId("合作者id(商户号)"); //以下两个参数在 服务商版模式中必填-------- 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 58cf5e2..b659dd7 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 @@ -13,7 +13,7 @@ import com.egzosn.pay.demo.entity.PayType; /** * 账户 * - * @author: egan + * @author egan * email egzosn@gmail.com * date 2016/11/18 1:21 */ @@ -29,7 +29,7 @@ public class ApyAccountRepository { ApyAccount apyAccount1 = new ApyAccount(); apyAccount1.setPayId(1); apyAccount1.setPartner("2088102169916436"); - apyAccount1.setAppid("2016080400165436"); + apyAccount1.setAppId("2016080400165436"); // TODO 2017/2/9 16:20 author: egan sign_type只有单一key时public_key与private_key相等,比如sign_type=MD5的情况 apyAccount1.setPublicKey("MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDIgHnOn7LLILlKETd6BFRJ0GqgS2Y3mn1wMQmyh9zEyWlz5p1zrahRahbXAfCfSqshSNfqOmAQzSHRVjCqjsAw1jyqrXaPdKBmr90DIpIxmIyKXv4GGAkPyJ/6FTFY99uhpiq0qadD/uSzQsefWo0aTvP/65zi3eof7TcZ32oWpwIDAQAB"); apyAccount1.setPrivateKey("MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBAKroe/8h5vC4L6T+B2WdXiVwGsMvUKgb2XsKix6VY3m2wcf6tyzpNRDCNykbIwGtaeo7FshN+qZxdXHLiIam9goYncBit/8ojfLGy2gLxO/PXfzGxYGs0KsDZ+ryVPPmE34ZZ8jiJpR0ygzCFl8pN3QJPJRGTJn5+FTT9EF/9zyZAgMBAAECgYAktngcYC35u7cQXDk+jMVyiVhWYU2ULxdSpPspgLGzrZyG1saOcTIi/XVX8Spd6+B6nmLQeF/FbU3rOeuD8U2clzul2Z2YMbJ0FYay9oVZFfp5gTEFpFRTVfzqUaZQBIjJe/xHL9kQVqc5xHlE/LVA27/Kx3dbC35Y7B4EVBDYAQJBAOhsX8ZreWLKPhXiXHTyLmNKhOHJc+0tFH7Ktise/0rNspojU7o9prOatKpNylp9v6kux7migcMRdVUWWiVe+4ECQQC8PqsuEz7B0yqirQchRg1DbHjh64bw9Kj82EN1/NzOUd53tP9tg+SO97EzsibK1F7tOcuwqsa7n2aY48mQ+y0ZAkBndA2xcRcnvOOjtAz5VO8G7R12rse181HjGfG6AeMadbKg30aeaGCyIxN1loiSfNR5xsPJwibGIBg81mUrqzqBAkB+K6rkaPXJR9XtzvdWb/N3235yPkDlw7Z4MiOVM3RzvR/VMDV7m8lXoeDde2zQyeMOMYy6ztwA6WgE1bhGOnQRAkEAouUBv1sVdSBlsexX15qphOmAevzYrpufKgJIRLFWQxroXMS7FTesj+f+FmGrpPCxIde1dqJ8lqYLTyJmbzMPYw=="); @@ -47,7 +47,7 @@ public class ApyAccountRepository { ApyAccount apyAccount2 = new ApyAccount(); apyAccount2.setPayId(2); apyAccount2.setPartner("1469188802"); - apyAccount2.setAppid("wx3344f4aed352deae"); + apyAccount2.setAppId("wx3344f4aed352deae"); // TODO 2017/2/9 16:20 author: egan sign_type只有单一key时public_key与private_key相等,比如sign_type=MD5的情况 apyAccount2.setPublicKey("991ded080***************f7fc61095"); apyAccount2.setPrivateKey("991ded080***************f7fc61095"); @@ -65,7 +65,7 @@ public class ApyAccountRepository { ApyAccount apyAccount3 = new ApyAccount(); apyAccount3.setPayId(3); apyAccount3.setPartner("12****601"); - apyAccount3.setAppid("wxa39*****ba9e9"); + apyAccount3.setAppId("wxa39*****ba9e9"); apyAccount3.setPublicKey("48gf0i************h9eiut9"); apyAccount3.setPrivateKey("48gf0i************h9eiut9"); apyAccount3.setNotifyUrl("http://pay.egan.in/payBack3.json"); @@ -106,7 +106,7 @@ public class ApyAccountRepository { ApyAccount apyAccount6 = new ApyAccount(); apyAccount6.setPayId(6); - apyAccount6.setAppid("1AZ7HTcvrEAxYbzYx_iDZAi06GdqbjhqqQzFgPBFLxm2VUMzwlmiNUBk_y_5QNP4zWKblTuM6ZBAmxScd");//Program ID + apyAccount6.setAppId("1AZ7HTcvrEAxYbzYx_iDZAi06GdqbjhqqQzFgPBFLxm2VUMzwlmiNUBk_y_5QNP4zWKblTuM6ZBAmxScd");//Program ID apyAccount6.setPrivateKey("1EBMIjAag6NiRdXZxteTv0amEsmKN345xJv3bN7f_HRXSqcRJlW7PXhYXjI9sk5I4nKYOHgeqzhXCXKFo");//API password apyAccount6.setInputCharset("UTF-8"); apyAccount6.setPayType(PayType.payPal); diff --git a/pay-java-demo/src/main/java/com/egzosn/pay/demo/entity/ApyAccount.java b/pay-java-demo/src/main/java/com/egzosn/pay/demo/entity/ApyAccount.java index ccc202b..236bdfa 100644 --- a/pay-java-demo/src/main/java/com/egzosn/pay/demo/entity/ApyAccount.java +++ b/pay-java-demo/src/main/java/com/egzosn/pay/demo/entity/ApyAccount.java @@ -7,7 +7,7 @@ package com.egzosn.pay.demo.entity; /** * 支付账户 * - * @author: egan + * @author egan * email egzosn@gmail.com * date 2016/11/18 0:36 */ 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 7437aa4..27fd489 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 @@ -51,7 +51,7 @@ public enum PayType implements BasePayType { //配置的附加参数的使用 configStorage.setAttach(apyAccount.getPayId()); configStorage.setPid(apyAccount.getPartner()); - configStorage.setAppid(apyAccount.getAppid()); + configStorage.setAppId(apyAccount.getAppId()); configStorage.setKeyPublic(apyAccount.getPublicKey()); configStorage.setKeyPrivate(apyAccount.getPrivateKey()); configStorage.setNotifyUrl(apyAccount.getNotifyUrl()); @@ -84,7 +84,7 @@ public enum PayType implements BasePayType { public PayService getPayService(ApyAccount apyAccount) { WxPayConfigStorage wxPayConfigStorage = new WxPayConfigStorage(); wxPayConfigStorage.setMchId(apyAccount.getPartner()); - wxPayConfigStorage.setAppid(apyAccount.getAppid()); + wxPayConfigStorage.setAppId(apyAccount.getAppId()); //转账公钥,转账时必填 wxPayConfigStorage.setKeyPublic(apyAccount.getPublicKey()); wxPayConfigStorage.setSecretKey(apyAccount.getPrivateKey()); @@ -239,7 +239,7 @@ public enum PayType implements BasePayType { PayPalConfigStorage storage = new PayPalConfigStorage(); //配置的附加参数的使用 storage.setAttach(apyAccount.getPayId()); - storage.setClientID(apyAccount.getAppid()); + storage.setClientID(apyAccount.getAppId()); storage.setClientSecret(apyAccount.getPrivateKey()); storage.setTest(true); //发起付款后的页面转跳地址 diff --git a/pay-java-demo/src/main/java/com/egzosn/pay/demo/request/QueryOrder.java b/pay-java-demo/src/main/java/com/egzosn/pay/demo/request/QueryOrder.java index 38d814d..5e8579d 100644 --- a/pay-java-demo/src/main/java/com/egzosn/pay/demo/request/QueryOrder.java +++ b/pay-java-demo/src/main/java/com/egzosn/pay/demo/request/QueryOrder.java @@ -5,7 +5,7 @@ import java.util.Date; /** * 订单辅助接口 - * @author: egan + * @author egan * email egzosn@gmail.com * date 2017/3/12 14:50 */ 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 a55bde1..ea3b407 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 @@ -10,7 +10,7 @@ import java.util.HashMap; import java.util.Map; /** - * @author: egan + * @author egan * email egzosn@gmail.com * date 2016/11/18 1:11 */ 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 1be58b9..c085fe5 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 @@ -23,7 +23,7 @@ import com.egzosn.pay.demo.service.interceptor.YoudianPayMessageInterceptor; /** * 支付响应对象 * - * @author: egan + * @author egan * email egzosn@gmail.com * date 2016/11/18 0:34 */ diff --git a/pay-java-demo/src/main/java/com/egzosn/pay/demo/service/interceptor/AliPayMessageInterceptor.java b/pay-java-demo/src/main/java/com/egzosn/pay/demo/service/interceptor/AliPayMessageInterceptor.java index 3d35c88..849dec1 100644 --- a/pay-java-demo/src/main/java/com/egzosn/pay/demo/service/interceptor/AliPayMessageInterceptor.java +++ b/pay-java-demo/src/main/java/com/egzosn/pay/demo/service/interceptor/AliPayMessageInterceptor.java @@ -13,7 +13,7 @@ import java.util.Map; /** * 支付宝回调信息拦截器 - * @author: egan + * @author egan * email egzosn@gmail.com * date 2017/1/18 19:28 */ diff --git a/pay-java-demo/src/main/java/com/egzosn/pay/demo/service/interceptor/YoudianPayMessageInterceptor.java b/pay-java-demo/src/main/java/com/egzosn/pay/demo/service/interceptor/YoudianPayMessageInterceptor.java index efb8a07..02b4e05 100644 --- a/pay-java-demo/src/main/java/com/egzosn/pay/demo/service/interceptor/YoudianPayMessageInterceptor.java +++ b/pay-java-demo/src/main/java/com/egzosn/pay/demo/service/interceptor/YoudianPayMessageInterceptor.java @@ -12,7 +12,7 @@ import java.util.Map; /** * 回调信息拦截器 - * @author: egan + * @author egan * email egzosn@gmail.com * date 2017/1/18 19:28 */ diff --git a/pay-java-demo/src/main/resources/apy_account.sql b/pay-java-demo/src/main/resources/apy_account.sql index 2180cf1..c248a8e 100644 --- a/pay-java-demo/src/main/resources/apy_account.sql +++ b/pay-java-demo/src/main/resources/apy_account.sql @@ -6,7 +6,7 @@ DROP TABLE IF EXISTS `pay_account`; CREATE TABLE `pay_account` ( `pay_id` INT(11) NOT NULL AUTO_INCREMENT COMMENT '支付账号id', `partner` VARCHAR(32) DEFAULT NULL COMMENT '支付合作id,商户id,差不多是支付平台的账号或id', - `appid` VARCHAR(32) DEFAULT NULL COMMENT '应用id', + `app_Id` VARCHAR(32) DEFAULT NULL COMMENT '应用id', `public_key` VARCHAR(1204) DEFAULT NULL COMMENT '支付平台公钥(签名校验使用),sign_type只有单一key时public_key与private_key相等,比如sign_type=MD5(友店支付除外)的情况', `private_key` VARCHAR(2048) DEFAULT NULL COMMENT '应用私钥(生成签名)', `notify_url` VARCHAR(1024) DEFAULT NULL COMMENT '异步回调地址', diff --git a/pay-java-payoneer/src/main/java/com/egzosn/pay/payoneer/api/PayoneerConfigStorage.java b/pay-java-payoneer/src/main/java/com/egzosn/pay/payoneer/api/PayoneerConfigStorage.java index c93ec95..979eea2 100644 --- a/pay-java-payoneer/src/main/java/com/egzosn/pay/payoneer/api/PayoneerConfigStorage.java +++ b/pay-java-payoneer/src/main/java/com/egzosn/pay/payoneer/api/PayoneerConfigStorage.java @@ -100,6 +100,7 @@ public class PayoneerConfigStorage extends BasePayConfigStorage { /** * 获取 PayoneerPay API password + * @return PayoneerPay API password */ public String getApiPassword() { return getKeyPrivate(); diff --git a/pay-java-paypal/src/main/java/com/egzosn/pay/paypal/api/PayPalPayService.java b/pay-java-paypal/src/main/java/com/egzosn/pay/paypal/api/PayPalPayService.java index 4a8d212..ae0e236 100644 --- a/pay-java-paypal/src/main/java/com/egzosn/pay/paypal/api/PayPalPayService.java +++ b/pay-java-paypal/src/main/java/com/egzosn/pay/paypal/api/PayPalPayService.java @@ -111,7 +111,7 @@ public class PayPalPayService extends BasePayService { if (payConfigStorage.isAccessTokenExpired()) { Map header = new HashMap<>(); - header.put("Authorization", "Basic " + authorizationString(getPayConfigStorage().getAppid(), getPayConfigStorage().getKeyPrivate())); + header.put("Authorization", "Basic " + authorizationString(getPayConfigStorage().getAppId(), getPayConfigStorage().getKeyPrivate())); header.put("Accept", "application/json"); header.put("Content-Type", "application/x-www-form-urlencoded"); try { diff --git a/pay-java-paypal/src/main/java/com/egzosn/pay/paypal/bean/order/Error.java b/pay-java-paypal/src/main/java/com/egzosn/pay/paypal/bean/order/Error.java index 8543046..1207b88 100644 --- a/pay-java-paypal/src/main/java/com/egzosn/pay/paypal/bean/order/Error.java +++ b/pay-java-paypal/src/main/java/com/egzosn/pay/paypal/bean/order/Error.java @@ -40,9 +40,7 @@ public class Error { public Error() { } - /** - * Parameterized Constructor - */ + public Error(String name, String message, String informationLink, String debugId) { this.name = name; this.message = message; diff --git a/pay-java-paypal/src/main/java/com/egzosn/pay/paypal/bean/order/Links.java b/pay-java-paypal/src/main/java/com/egzosn/pay/paypal/bean/order/Links.java index 55d6665..ebb7376 100644 --- a/pay-java-paypal/src/main/java/com/egzosn/pay/paypal/bean/order/Links.java +++ b/pay-java-paypal/src/main/java/com/egzosn/pay/paypal/bean/order/Links.java @@ -23,9 +23,7 @@ public class Links { public Links() { } - /** - * Parameterized Constructor - */ + public Links(String href, String rel) { this.href = href; this.rel = rel; diff --git a/pay-java-paypal/src/main/java/com/egzosn/pay/paypal/v2/api/PayPalPayService.java b/pay-java-paypal/src/main/java/com/egzosn/pay/paypal/v2/api/PayPalPayService.java index acf3d07..10289dd 100644 --- a/pay-java-paypal/src/main/java/com/egzosn/pay/paypal/v2/api/PayPalPayService.java +++ b/pay-java-paypal/src/main/java/com/egzosn/pay/paypal/v2/api/PayPalPayService.java @@ -122,7 +122,7 @@ public class PayPalPayService extends BasePayService implem } if (payConfigStorage.isAccessTokenExpired()) { Map header = new HashMap<>(); - header.put("Authorization", "Basic " + authorizationString(getPayConfigStorage().getAppid(), getPayConfigStorage().getKeyPrivate())); + header.put("Authorization", "Basic " + authorizationString(getPayConfigStorage().getAppId(), getPayConfigStorage().getKeyPrivate())); header.put("Accept", "application/json"); header.put("Content-Type", "application/x-www-form-urlencoded"); try { @@ -153,7 +153,7 @@ public class PayPalPayService extends BasePayService implem * 4.Check that the price (carried in mc_gross) and the currency (carried in mc_currency) are correct for the item (carried in item_name or item_number). * * @param params 回调回来的参数集 - * @return + * @return 是否成功 true成功 */ @Override public boolean verify(Map params) { diff --git a/pay-java-union/src/main/java/com/egzosn/pay/union/api/UnionPayConfigStorage.java b/pay-java-union/src/main/java/com/egzosn/pay/union/api/UnionPayConfigStorage.java index 5c2f2e6..010ed7d 100644 --- a/pay-java-union/src/main/java/com/egzosn/pay/union/api/UnionPayConfigStorage.java +++ b/pay-java-union/src/main/java/com/egzosn/pay/union/api/UnionPayConfigStorage.java @@ -9,7 +9,6 @@ import com.egzosn.pay.common.bean.CertStoreType; /** * @author Actinia - *

*

  *         email hayesfu@qq.com
  *           create 2017 2017/11/4 0004
diff --git a/pay-java-wx-youdian/src/main/java/com/egzosn/pay/wx/youdian/bean/YdPayError.java b/pay-java-wx-youdian/src/main/java/com/egzosn/pay/wx/youdian/bean/YdPayError.java
index b782a50..9aed247 100644
--- a/pay-java-wx-youdian/src/main/java/com/egzosn/pay/wx/youdian/bean/YdPayError.java
+++ b/pay-java-wx-youdian/src/main/java/com/egzosn/pay/wx/youdian/bean/YdPayError.java
@@ -20,7 +20,7 @@ package com.egzosn.pay.wx.youdian.bean;
 import com.egzosn.pay.common.bean.result.PayError;
 
 /**
- * @author: egan
+ * @author egan
  *
  * email egzosn@gmail.com
  * date 2017/3/6 19:41
diff --git a/pay-java-wx/src/main/java/com/egzosn/pay/wx/api/WxBillService.java b/pay-java-wx/src/main/java/com/egzosn/pay/wx/api/WxBillService.java
index 06d9f05..09232df 100644
--- a/pay-java-wx/src/main/java/com/egzosn/pay/wx/api/WxBillService.java
+++ b/pay-java-wx/src/main/java/com/egzosn/pay/wx/api/WxBillService.java
@@ -8,7 +8,7 @@ import com.egzosn.pay.common.bean.BillType;
 /**
  * 账单接口
  *
- * @author: faymanwang
+ * @author faymanwang
  * email: 1057438332@qq.com
  * time: 2020/7/31 11:21
  */
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 b0016a1..f955647 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
@@ -277,14 +277,14 @@ public class WxPayService extends BasePayService implements
 
             if (WxTransactionType.JSAPI == order.getTransactionType()) {
                 params.put("signType", payConfigStorage.getSignType());
-                params.put("appId", payConfigStorage.getAppid());
+                params.put("appId", payConfigStorage.getAppId());
                 params.put("timeStamp", System.currentTimeMillis() / 1000 + "");
                 params.put("nonceStr", result.get(NONCE_STR));
                 params.put("package", "prepay_id=" + result.get("prepay_id"));
             }
             else if (WxTransactionType.APP == order.getTransactionType()) {
                 params.put("partnerid", payConfigStorage.getPid());
-                params.put(APPID, payConfigStorage.getAppid());
+                params.put(APPID, payConfigStorage.getAppId());
                 params.put("prepayid", result.get("prepay_id"));
                 params.put("timestamp", System.currentTimeMillis() / 1000);
                 params.put("noncestr", result.get(NONCE_STR));
@@ -824,7 +824,7 @@ public class WxPayService extends BasePayService implements
      */
     public Map transfers(Map parameters, TransferOrder order) {
         //转账到余额, 申请商户号的appid或商户号绑定的appid
-        parameters.put("mch_appid", payConfigStorage.getAppid());
+        parameters.put("mch_appid", payConfigStorage.getAppId());
         parameters.put("openid", order.getPayeeAccount());
         parameters.put("spbill_create_ip", StringUtils.isEmpty(order.getIp()) ? "192.168.1.150" : order.getIp());
         //默认不校验真实姓名
@@ -908,7 +908,7 @@ public class WxPayService extends BasePayService implements
      *
      * @param redpackOrder 红包实体
      * @return 返回发红包实体后的结果
-     * @author: faymanwang 1057438332@qq.com
+     * @author faymanwang 1057438332@qq.com
      */
     @Override
     public Map sendredpack(RedpackOrder redpackOrder) {
@@ -948,7 +948,7 @@ public class WxPayService extends BasePayService implements
      *
      * @param mchBillno 商户发放红包的商户订单号
      * @return 返回查询结果
-     * @author: faymanwang 1057438332@qq.com
+     * @author faymanwang 1057438332@qq.com
      */
     @Override
     public Map gethbinfo(String mchBillno) {
@@ -968,7 +968,7 @@ public class WxPayService extends BasePayService implements
     private void redPackParam(RedpackOrder redpackOrder, Map parameters) {
         parameters.put(NONCE_STR, SignUtils.randomStr());
         parameters.put(MCH_ID, payConfigStorage.getPid());
-        parameters.put("wxappid", payConfigStorage.getAppid());
+        parameters.put("wxappid", payConfigStorage.getAppId());
         parameters.put("send_name", redpackOrder.getSendName());
         parameters.put("re_openid", redpackOrder.getReOpenid());
         parameters.put("mch_billno", redpackOrder.getMchBillno());
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 740f9b5..a049754 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
@@ -9,12 +9,13 @@ import java.util.Map;
 
 /**
  * 微信交易类型
- * @author egan
  *
+ * @author egan
+ * 

* email egzosn@gmail.com * date 2016/10/19 22:58 */ -public enum WxTransactionType implements TransactionType { +public enum WxTransactionType implements TransactionType { /** * 公众号支付 */ @@ -50,7 +51,7 @@ public enum WxTransactionType implements TransactionType { /** * 刷脸支付 */ - FACEPAY("pay/facepay"){ + FACEPAY("pay/facepay") { @Override public void setAttribute(Map parameters, PayOrder order) { parameters.put("openid", order.getOpenid()); @@ -60,11 +61,11 @@ public enum WxTransactionType implements TransactionType { /** * H5支付 */ - MWEB("pay/unifiedorder"){ + MWEB("pay/unifiedorder") { @Override public void setAttribute(Map parameters, PayOrder order) { //H5支付专用 - LinkedHashMap value = new LinkedHashMap(); + LinkedHashMap value = new LinkedHashMap(6); value.put("type", "Wap"); //WAP网站URL地址 value.put("wap_url", order.getWapUrl()); @@ -74,6 +75,7 @@ public enum WxTransactionType implements TransactionType { sceneInfo.put("h5_info", value); parameters.put("scene_info", sceneInfo.toJSONString()); } + /** * 是否直接返回 * @@ -87,13 +89,14 @@ public enum WxTransactionType implements TransactionType { /** * 刷卡付 */ - MICROPAY("pay/micropay"){ + MICROPAY("pay/micropay") { @Override public void setAttribute(Map parameters, PayOrder order) { parameters.put("auth_code", order.getAuthCode()); parameters.remove("notify_url"); parameters.remove("trade_type"); } + /** * 是否直接返回 * @@ -146,6 +149,7 @@ public enum WxTransactionType implements TransactionType { public String getType() { return this.name(); } + @Override public String getMethod() { return this.method; @@ -153,13 +157,14 @@ public enum WxTransactionType implements TransactionType { /** * 是否直接返回 + * * @return 是否直接返回 */ - public boolean isReturn(){ + public boolean isReturn() { return false; } - public void setAttribute(Map parameters, PayOrder order){ + public void setAttribute(Map parameters, PayOrder order) { } } diff --git a/pay-java-wx/src/test/java/PayTest.java b/pay-java-wx/src/test/java/PayTest.java index 4588157..8137df0 100644 --- a/pay-java-wx/src/test/java/PayTest.java +++ b/pay-java-wx/src/test/java/PayTest.java @@ -26,7 +26,7 @@ public class PayTest { public static void main(String[] args) { WxPayConfigStorage wxPayConfigStorage = new WxPayConfigStorage(); - wxPayConfigStorage.setAppid("公众账号ID"); + wxPayConfigStorage.setAppId("公众账号ID"); wxPayConfigStorage.setMchId("合作者id(商户号)"); //以下两个参数在 服务商版模式中必填-------- diff --git a/pom.xml b/pom.xml index 3874de6..8c7479b 100644 --- a/pom.xml +++ b/pom.xml @@ -185,6 +185,7 @@ attach-javadocs + install jar @@ -198,6 +199,7 @@ attach-sources + install jar-no-fork -- Gitee From 66cceb06f593067c8fa01891510ba4e77da450c4 Mon Sep 17 00:00:00 2001 From: egzosn Date: Wed, 24 Feb 2021 22:55:20 +0800 Subject: [PATCH 187/299] 2.14.1 --- pay-java-ali/README.md | 6 ++++-- pay-java-demo/README.md | 4 ++-- pay-java-wx/README.md | 2 +- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/pay-java-ali/README.md b/pay-java-ali/README.md index 0f27875..166131d 100644 --- a/pay-java-ali/README.md +++ b/pay-java-ali/README.md @@ -9,7 +9,8 @@ AliPayConfigStorage aliPayConfigStorage = new AliPayConfigStorage(); aliPayConfigStorage.setPid("合作者id"); - aliPayConfigStorage.setAppid("应用id"); + aliPayConfigStorage.setAppId("应用id"); +// aliPayConfigStorage.setAppAuthToken("ISV代商户代用,指定appAuthToken"); aliPayConfigStorage.setKeyPublic("支付宝公钥"); aliPayConfigStorage.setKeyPrivate("应用私钥"); aliPayConfigStorage.setNotifyUrl("异步回调地址"); @@ -27,7 +28,8 @@ AliPayConfigStorage aliPayConfigStorage = new AliPayConfigStorage(); aliPayConfigStorage.setPid("合作者id"); - aliPayConfigStorage.setAppid("应用id"); + aliPayConfigStorage.setAppId("应用id"); +// aliPayConfigStorage.setAppAuthToken("ISV代商户代用,指定appAuthToken"); aliPayConfigStorage.setKeyPrivate("应用私钥"); //设置为证书方式 aliPayConfigStorage.setCertSign(true); diff --git a/pay-java-demo/README.md b/pay-java-demo/README.md index d6b9a7e..869d81c 100644 --- a/pay-java-demo/README.md +++ b/pay-java-demo/README.md @@ -22,7 +22,7 @@ public enum PayType implements BasePayType { public PayService getPayService(ApyAccount apyAccount) { AliPayConfigStorage aliPayConfigStorage = new AliPayConfigStorage(); aliPayConfigStorage.setPid(apyAccount.getPartner()); - aliPayConfigStorage.setAppid(apyAccount.getAppid()); + aliPayConfigStorage.setAppId(apyAccount.getAppid()); aliPayConfigStorage.setKeyPublic(apyAccount.getPublicKey()); aliPayConfigStorage.setKeyPrivate(apyAccount.getPrivateKey()); aliPayConfigStorage.setNotifyUrl(apyAccount.getNotifyUrl()); @@ -49,7 +49,7 @@ public enum PayType implements BasePayType { wxPayConfigStorage.setMchId(apyAccount.getPartner()); wxPayConfigStorage.setAppSecret(apyAccount.getPublicKey()); wxPayConfigStorage.setKeyPublic(apyAccount.getPublicKey()); - wxPayConfigStorage.setAppid(apyAccount.getAppid()); + wxPayConfigStorage.setAppId(apyAccount.getAppid()); wxPayConfigStorage.setKeyPrivate(apyAccount.getPrivateKey()); wxPayConfigStorage.setNotifyUrl(apyAccount.getNotifyUrl()); wxPayConfigStorage.setSignType(apyAccount.getSignType()); diff --git a/pay-java-wx/README.md b/pay-java-wx/README.md index da29557..6012fe6 100644 --- a/pay-java-wx/README.md +++ b/pay-java-wx/README.md @@ -8,7 +8,7 @@ WxPayConfigStorage wxPayConfigStorage = new WxPayConfigStorage(); wxPayConfigStorage.setMchId("合作者id(商户号)"); - wxPayConfigStorage.setAppid("应用id"); + wxPayConfigStorage.setAppId("应用id"); wxPayConfigStorage.setKeyPublic("转账公钥,转账时必填"); wxPayConfigStorage.setSecretKey("密钥"); wxPayConfigStorage.setNotifyUrl("异步回调地址"); -- Gitee From f517b8488ed67380455da8a86191d6ba2e2eb5c5 Mon Sep 17 00:00:00 2001 From: egzosn Date: Wed, 24 Feb 2021 22:59:42 +0800 Subject: [PATCH 188/299] 2.14.1 --- .../java/com/egzosn/pay/demo/entity/ApyAccount.java | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/pay-java-demo/src/main/java/com/egzosn/pay/demo/entity/ApyAccount.java b/pay-java-demo/src/main/java/com/egzosn/pay/demo/entity/ApyAccount.java index 236bdfa..8aec899 100644 --- a/pay-java-demo/src/main/java/com/egzosn/pay/demo/entity/ApyAccount.java +++ b/pay-java-demo/src/main/java/com/egzosn/pay/demo/entity/ApyAccount.java @@ -24,7 +24,7 @@ public class ApyAccount { private String partner; // 应用id // @Column(name = "appid") - private String appid; + private String appId; // 支付平台公钥(签名校验使用),sign_type只有单一key时public_key与private_key相等,比如sign_type=MD5的情况 private String publicKey; // 应用私钥(生成签名) @@ -77,12 +77,12 @@ public class ApyAccount { this.partner = partner; } - public String getAppid() { - return appid; + public String getAppId() { + return appId; } - public void setAppid(String appid) { - this.appid = appid; + public void setAppId(String appId) { + this.appId = appId; } public String getPublicKey() { @@ -178,7 +178,7 @@ public class ApyAccount { return "ApyAccount{" + "payId=" + payId + ", partner='" + partner + '\'' + - ", appid='" + appid + '\'' + + ", appId='" + appId + '\'' + ", publicKey='" + publicKey + '\'' + ", privateKey='" + privateKey + '\'' + ", notifyUrl='" + notifyUrl + '\'' + -- Gitee From 860a805c555cac1184154c827f30ab0180fb172d Mon Sep 17 00:00:00 2001 From: egzosn Date: Wed, 24 Feb 2021 22:00:39 +0800 Subject: [PATCH 189/299] =?UTF-8?q?2.14.2=20=E5=BC=80=E5=A7=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pay-java-ali/pom.xml | 2 +- pay-java-baidu/pom.xml | 2 +- pay-java-common/pom.xml | 2 +- pay-java-demo/pom.xml | 2 +- pay-java-fuiou/pom.xml | 2 +- pay-java-payoneer/pom.xml | 2 +- pay-java-paypal/pom.xml | 2 +- pay-java-union/pom.xml | 2 +- pay-java-wx-youdian/pom.xml | 2 +- pay-java-wx/pom.xml | 2 +- pay-java-yiji/pom.xml | 2 +- pom.xml | 4 ++-- 12 files changed, 13 insertions(+), 13 deletions(-) diff --git a/pay-java-ali/pom.xml b/pay-java-ali/pom.xml index a791d6f..7e4683a 100644 --- a/pay-java-ali/pom.xml +++ b/pay-java-ali/pom.xml @@ -5,7 +5,7 @@ pay-java-parent com.egzosn - 2.14.1 + 2.14.2-SNAPSHOT 4.0.0 pay-java-ali diff --git a/pay-java-baidu/pom.xml b/pay-java-baidu/pom.xml index d835c66..8861985 100644 --- a/pay-java-baidu/pom.xml +++ b/pay-java-baidu/pom.xml @@ -5,7 +5,7 @@ pay-java-parent com.egzosn - 2.14.1 + 2.14.2-SNAPSHOT 4.0.0 pay-java-baidu diff --git a/pay-java-common/pom.xml b/pay-java-common/pom.xml index 1082296..143d887 100644 --- a/pay-java-common/pom.xml +++ b/pay-java-common/pom.xml @@ -5,7 +5,7 @@ pay-java-parent com.egzosn - 2.14.1 + 2.14.2-SNAPSHOT 4.0.0 jar diff --git a/pay-java-demo/pom.xml b/pay-java-demo/pom.xml index 160cca1..78628fe 100644 --- a/pay-java-demo/pom.xml +++ b/pay-java-demo/pom.xml @@ -5,7 +5,7 @@ pay-java-parent com.egzosn - 2.14.1 + 2.14.2-SNAPSHOT 4.0.0 war diff --git a/pay-java-fuiou/pom.xml b/pay-java-fuiou/pom.xml index 183e19a..4ba3acd 100644 --- a/pay-java-fuiou/pom.xml +++ b/pay-java-fuiou/pom.xml @@ -5,7 +5,7 @@ pay-java-parent com.egzosn - 2.14.1 + 2.14.2-SNAPSHOT 4.0.0 pay-java-fuiou diff --git a/pay-java-payoneer/pom.xml b/pay-java-payoneer/pom.xml index ebff04b..a8e8334 100644 --- a/pay-java-payoneer/pom.xml +++ b/pay-java-payoneer/pom.xml @@ -5,7 +5,7 @@ pay-java-parent com.egzosn - 2.14.1 + 2.14.2-SNAPSHOT 4.0.0 pay-java-payoneer diff --git a/pay-java-paypal/pom.xml b/pay-java-paypal/pom.xml index 887ff3e..ce4d4a7 100644 --- a/pay-java-paypal/pom.xml +++ b/pay-java-paypal/pom.xml @@ -5,7 +5,7 @@ pay-java-parent com.egzosn - 2.14.1 + 2.14.2-SNAPSHOT 4.0.0 diff --git a/pay-java-union/pom.xml b/pay-java-union/pom.xml index d52eb4d..c1ca7f9 100644 --- a/pay-java-union/pom.xml +++ b/pay-java-union/pom.xml @@ -5,7 +5,7 @@ pay-java-parent com.egzosn - 2.14.1 + 2.14.2-SNAPSHOT 4.0.0 diff --git a/pay-java-wx-youdian/pom.xml b/pay-java-wx-youdian/pom.xml index 80500ae..e88c55c 100644 --- a/pay-java-wx-youdian/pom.xml +++ b/pay-java-wx-youdian/pom.xml @@ -5,7 +5,7 @@ pay-java-parent com.egzosn - 2.14.1 + 2.14.2-SNAPSHOT 4.0.0 pay-java-wx-youdian diff --git a/pay-java-wx/pom.xml b/pay-java-wx/pom.xml index f68a0b6..b0d3669 100644 --- a/pay-java-wx/pom.xml +++ b/pay-java-wx/pom.xml @@ -5,7 +5,7 @@ pay-java-parent com.egzosn - 2.14.1 + 2.14.2-SNAPSHOT 4.0.0 pay-java-wx diff --git a/pay-java-yiji/pom.xml b/pay-java-yiji/pom.xml index 0411dfd..2458fcf 100644 --- a/pay-java-yiji/pom.xml +++ b/pay-java-yiji/pom.xml @@ -5,7 +5,7 @@ pay-java-parent com.egzosn - 2.14.1 + 2.14.2-SNAPSHOT 4.0.0 diff --git a/pom.xml b/pom.xml index 8c7479b..018c8bf 100644 --- a/pom.xml +++ b/pom.xml @@ -7,7 +7,7 @@ com.egzosn pay-java-parent pom - 2.14.1 + 2.14.2-SNAPSHOT Pay Java - Parent Pay Java Parent @@ -64,7 +64,7 @@ - 2.14.1 + 2.14.2-SNAPSHOT 4.5.4 1.2.17 1.2.73 -- Gitee From e87e62c241b36ac33725097415f2e8f2e114a4c0 Mon Sep 17 00:00:00 2001 From: egzosn Date: Wed, 24 Feb 2021 22:02:09 +0800 Subject: [PATCH 190/299] =?UTF-8?q?https://github.com/egzosn/pay-java-pare?= =?UTF-8?q?nt/issues/57=20=E6=97=A9=E6=9C=9FQUICK=5FWAP=5FWAY=E8=99=BD?= =?UTF-8?q?=E7=84=B6=E4=B9=9F=E5=8F=AF=E4=BB=A5=E8=BF=9B=E8=A1=8C=E6=94=AF?= =?UTF-8?q?=E4=BB=98=EF=BC=8C=E6=A0=B9=E6=8D=AE=E6=9C=80=E6=96=B0=E6=96=87?= =?UTF-8?q?=E6=A1=A3=E8=B0=83=E6=95=B4=E4=B8=BAQUICK=5FWAP=5FPAY?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/main/java/com/egzosn/pay/ali/api/AliPayService.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 6e6792f..02d3608 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 @@ -246,7 +246,7 @@ public class AliPayService extends BasePayService { break; case WAP: bizContent.put(PASSBACK_PARAMS, order.getAddition()); - bizContent.put(PRODUCT_CODE, "QUICK_WAP_WAY"); + bizContent.put(PRODUCT_CODE, "QUICK_WAP_PAY"); setReturnUrl(orderInfo, order); break; case APP: -- Gitee From ec11b7af56c8652988eddb471091fd2f3acc6c83 Mon Sep 17 00:00:00 2001 From: egzosn Date: Wed, 24 Feb 2021 22:02:31 +0800 Subject: [PATCH 191/299] =?UTF-8?q?=E5=BE=AE=E4=BF=A1=E9=80=80=E6=AC=BE?= =?UTF-8?q?=E8=87=AA=E5=AE=9A=E4=B9=89=E5=9B=9E=E8=B0=83=E9=80=9A=E7=9F=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/egzosn/pay/wx/api/WxPayService.java | 24 ++++++++++++------- 1 file changed, 15 insertions(+), 9 deletions(-) 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 f955647..c1baeca 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 @@ -40,6 +40,7 @@ import com.alibaba.fastjson.JSONObject; import com.egzosn.pay.common.api.BasePayService; import com.egzosn.pay.common.bean.BillType; import com.egzosn.pay.common.bean.MethodType; +import com.egzosn.pay.common.bean.Order; import com.egzosn.pay.common.bean.PayMessage; import com.egzosn.pay.common.bean.PayOrder; import com.egzosn.pay.common.bean.PayOutMessage; @@ -214,8 +215,7 @@ public class WxPayService extends BasePayService implements // 总金额单位为分 parameters.put("total_fee", Util.conversionCentAmount(order.getPrice())); setParameters(parameters, "attach", order.getAddition()); - parameters.put("notify_url", payConfigStorage.getNotifyUrl()); - setParameters(parameters, "notify_url", order); + initNotifyUrl(parameters, order); parameters.put("trade_type", order.getTransactionType().getType()); if (null != order.getExpirationTime()) { parameters.put("time_start", DateUtils.formatDate(new Date(), DateUtils.YYYYMMDDHHMMSS)); @@ -512,6 +512,12 @@ public class WxPayService extends BasePayService implements } + private Map initNotifyUrl(Map parameters, Order order) { + setParameters(parameters, "notify_url", payConfigStorage.getNotifyUrl()); + setParameters(parameters, "notify_url", order); + return parameters; + } + /** * 申请退款接口 * @@ -528,7 +534,7 @@ public class WxPayService extends BasePayService implements setParameters(parameters, "out_refund_no", refundOrder.getRefundNo()); parameters.put("total_fee", Util.conversionCentAmount(refundOrder.getTotalAmount())); parameters.put("refund_fee", Util.conversionCentAmount(refundOrder.getRefundAmount())); - setParameters(parameters, "notify_url", payConfigStorage.getNotifyUrl()); + initNotifyUrl(parameters, refundOrder); if (null != refundOrder.getCurType()) { parameters.put("refund_fee_type", refundOrder.getCurType().getType()); } @@ -778,12 +784,12 @@ public class WxPayService extends BasePayService implements * @param order 转账订单 *

      *
-     *                           注意事项:
-     *                           ◆ 当返回错误码为“SYSTEMERROR”时,请不要更换商户订单号,一定要使用原商户订单号重试,否则可能造成重复支付等资金风险。
-     *                           ◆ XML具有可扩展性,因此返回参数可能会有新增,而且顺序可能不完全遵循此文档规范,如果在解析回包的时候发生错误,请商户务必不要换单重试,请商户联系客服确认付款情况。如果有新回包字段,会更新到此API文档中。
-     *                           ◆ 因为错误代码字段err_code的值后续可能会增加,所以商户如果遇到回包返回新的错误码,请商户务必不要换单重试,请商户联系客服确认付款情况。如果有新的错误码,会更新到此API文档中。
-     *                           ◆ 错误代码描述字段err_code_des只供人工定位问题时做参考,系统实现时请不要依赖这个字段来做自动化处理。
-     *                           
+ * 注意事项: + * ◆ 当返回错误码为“SYSTEMERROR”时,请不要更换商户订单号,一定要使用原商户订单号重试,否则可能造成重复支付等资金风险。 + * ◆ XML具有可扩展性,因此返回参数可能会有新增,而且顺序可能不完全遵循此文档规范,如果在解析回包的时候发生错误,请商户务必不要换单重试,请商户联系客服确认付款情况。如果有新回包字段,会更新到此API文档中。 + * ◆ 因为错误代码字段err_code的值后续可能会增加,所以商户如果遇到回包返回新的错误码,请商户务必不要换单重试,请商户联系客服确认付款情况。如果有新的错误码,会更新到此API文档中。 + * ◆ 错误代码描述字段err_code_des只供人工定位问题时做参考,系统实现时请不要依赖这个字段来做自动化处理。 + *
* @return 对应的转账结果 */ @Override -- Gitee From 88ef6146f6ddac868baf336b91fc38bfaac15214 Mon Sep 17 00:00:00 2001 From: egzosn Date: Wed, 24 Feb 2021 22:15:07 +0800 Subject: [PATCH 192/299] =?UTF-8?q?https://github.com/egzosn/pay-java-pare?= =?UTF-8?q?nt/issues/66=20=E6=94=AF=E4=BB=98=E5=AE=9D=E5=85=AC=E9=92=A5?= =?UTF-8?q?=E8=AF=81=E4=B9=A6=20verify=20=E6=8A=A5=E9=94=99=E9=9C=80?= =?UTF-8?q?=E8=A6=81=E5=BF=BD=E7=95=A5alipay=5Fcert=5Fsn?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/main/java/com/egzosn/pay/ali/api/AliPayService.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) 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 02d3608..da0de8f 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 @@ -6,6 +6,7 @@ import java.util.LinkedHashMap; import java.util.Map; import java.util.TreeMap; +import static com.egzosn.pay.ali.bean.AliPayConst.ALIPAY_CERT_SN_FIELD; import static com.egzosn.pay.ali.bean.AliPayConst.APP_AUTH_TOKEN; import static com.egzosn.pay.ali.bean.AliPayConst.BIZ_CONTENT; import static com.egzosn.pay.ali.bean.AliPayConst.CODE; @@ -130,7 +131,7 @@ public class AliPayService extends BasePayService { if (params instanceof JSONObject) { for (Map.Entry entry : params.entrySet()) { - if (SIGN.equals(entry.getKey())) { + if (SIGN.equals(entry.getKey()) || ALIPAY_CERT_SN_FIELD.equals(entry.getKey())) { continue; } TreeMap response = new TreeMap((Map) entry.getValue()); @@ -164,7 +165,7 @@ public class AliPayService extends BasePayService { * @return 支付宝公钥证书序列号 */ public String getAliPayCertSN(java.util.Map respMap) { - return (String) respMap.get(AliPayConst.ALIPAY_CERT_SN_FIELD); + return (String) respMap.get(ALIPAY_CERT_SN_FIELD); } /** -- Gitee From f4067a546ec2ba96075ea54a8973be619d970db7 Mon Sep 17 00:00:00 2001 From: egzosn Date: Wed, 24 Feb 2021 22:20:17 +0800 Subject: [PATCH 193/299] =?UTF-8?q?https://github.com/egzosn/pay-java-pare?= =?UTF-8?q?nt/issues/66=20=E6=94=AF=E4=BB=98=E5=AE=9D=E5=85=AC=E9=92=A5?= =?UTF-8?q?=E8=AF=81=E4=B9=A6=20verify=20=E6=8A=A5=E9=94=99=E9=9C=80?= =?UTF-8?q?=E8=A6=81=E5=BF=BD=E7=95=A5alipay=5Fcert=5Fsn?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/egzosn/pay/ali/api/AliPayService.java | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) 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 da0de8f..dd6df3d 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 @@ -151,7 +151,7 @@ public class AliPayService extends BasePayService { * @param params 响应参数 * @return 公钥信息 */ - private String getKeyPublic(Map params) { + protected String getKeyPublic(Map params) { if (!payConfigStorage.isCertSign()) { return payConfigStorage.getKeyPublic(); } @@ -174,7 +174,7 @@ public class AliPayService extends BasePayService { * @param id 业务id, 数据的真实性. * @return true通过 */ - public boolean verifySource(String id) { + protected boolean verifySource(String id) { return true; } @@ -185,7 +185,7 @@ public class AliPayService extends BasePayService { * @param parameters 请求参数 * @return 请求参数 */ - private Map setSign(Map parameters) { + protected Map setSign(Map parameters) { parameters.put("sign_type", payConfigStorage.getSignType()); String sign = createSign(SignUtils.parameterText(parameters, "&", SIGN), payConfigStorage.getInputCharset()); @@ -225,7 +225,7 @@ public class AliPayService extends BasePayService { * @return 返回支付宝预下单信息 * @see PayOrder 支付订单信息 */ - private Map getOrder(PayOrder order) { + protected Map getOrder(PayOrder order) { Map orderInfo = getPublicParameters(order.getTransactionType()); @@ -300,7 +300,7 @@ public class AliPayService extends BasePayService { * @param transactionType 交易类型 * @return 放回公共请求参数 */ - private Map getPublicParameters(TransactionType transactionType) { + protected Map getPublicParameters(TransactionType transactionType) { Map orderInfo = new TreeMap<>(); orderInfo.put("app_id", payConfigStorage.getAppId()); orderInfo.put("method", transactionType.getMethod()); @@ -316,7 +316,7 @@ public class AliPayService extends BasePayService { * * @param orderInfo 订单信息 */ - private void loadCertSn(Map orderInfo) { + protected void loadCertSn(Map orderInfo) { if (payConfigStorage.isCertSign()) { final CertEnvironment certEnvironment = payConfigStorage.getCertEnvironment(); setParameters(orderInfo, "app_cert_sn", certEnvironment.getMerchantCertSN()); @@ -492,7 +492,7 @@ public class AliPayService extends BasePayService { * @param attrs 订单属性 * @return 参数 */ - private void setAppAuthToken(Map parameters, Map attrs) { + protected void setAppAuthToken(Map parameters, Map attrs) { setAppAuthToken(parameters); setParameters(parameters, APP_AUTH_TOKEN, (String) attrs.remove(APP_AUTH_TOKEN)); } @@ -503,7 +503,7 @@ public class AliPayService extends BasePayService { * @param parameters 参数 * @return 参数 */ - private void setAppAuthToken(Map parameters) { + protected void setAppAuthToken(Map parameters) { setParameters(parameters, APP_AUTH_TOKEN, payConfigStorage.getAppAuthToken()); } -- Gitee From 05cd058be7c01a484b5e9de0d261b1acb6dd1c3e Mon Sep 17 00:00:00 2001 From: egzosn Date: Sat, 13 Mar 2021 15:45:39 +0800 Subject: [PATCH 194/299] =?UTF-8?q?=E5=BE=AE=E4=BF=A1=E8=BD=AC=E8=B4=A6?= =?UTF-8?q?=E5=88=B0=E9=9B=B6=E9=92=B1=E6=9F=A5=E8=AF=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/main/java/com/egzosn/pay/wx/api/WxPayService.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) 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 c1baeca..f854859 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 @@ -876,14 +876,16 @@ public class WxPayService extends BasePayService implements parameters.put(MCH_ID, payConfigStorage.getPid()); parameters.put("partner_trade_no", outNo); parameters.put(NONCE_STR, SignUtils.randomStr()); - parameters.put(SIGN, createSign(SignUtils.parameterText(parameters, "&", SIGN), payConfigStorage.getInputCharset())); if (StringUtils.isEmpty(wxTransferType)) { throw new PayErrorException(new WxPayError(FAILURE, "微信转账类型 #transferQuery(String outNo, String wxTransferType) 必填,详情com.egzosn.pay.wx.bean.WxTransferType")); } //如果类型为余额方式 if (TRANSFERS.getType().equals(wxTransferType) || GETTRANSFERINFO.getType().equals(wxTransferType)) { + parameters.put(APPID, payConfigStorage.getAppId()); + parameters.put(SIGN, createSign(SignUtils.parameterText(parameters, "&", SIGN), payConfigStorage.getInputCharset())); return getHttpRequestTemplate().postForObject(getReqUrl(GETTRANSFERINFO), XML.getMap2Xml(parameters), JSONObject.class); } + parameters.put(SIGN, createSign(SignUtils.parameterText(parameters, "&", SIGN), payConfigStorage.getInputCharset())); //默认查询银行卡的记录 return getHttpRequestTemplate().postForObject(getReqUrl(QUERY_BANK), XML.getMap2Xml(parameters), JSONObject.class); } -- Gitee From 91c586a4abc29ce8db46122a0740cdf1c2928679 Mon Sep 17 00:00:00 2001 From: egzosn Date: Thu, 18 Mar 2021 22:14:39 +0800 Subject: [PATCH 195/299] =?UTF-8?q?=E6=94=AF=E4=BB=98=E5=AE=9D=E9=80=80?= =?UTF-8?q?=E6=AC=BE=E5=BA=8F=E5=88=97=E5=8C=96=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../main/java/com/egzosn/pay/ali/bean/AliRefundResult.java | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/pay-java-ali/src/main/java/com/egzosn/pay/ali/bean/AliRefundResult.java b/pay-java-ali/src/main/java/com/egzosn/pay/ali/bean/AliRefundResult.java index 6fbf661..65c6cbc 100644 --- a/pay-java-ali/src/main/java/com/egzosn/pay/ali/bean/AliRefundResult.java +++ b/pay-java-ali/src/main/java/com/egzosn/pay/ali/bean/AliRefundResult.java @@ -2,6 +2,7 @@ package com.egzosn.pay.ali.bean; import java.math.BigDecimal; import java.util.Date; +import java.util.List; import java.util.Map; import com.alibaba.fastjson.JSONObject; @@ -92,7 +93,7 @@ public class AliRefundResult extends BaseRefundResult { * 只有在签约中指定需要返回资金明细,或者入参的query_options中指定时才返回该字段信息。 */ @JSONField(name = "refund_detail_item_list") - private TradeFundBill refundDetailItemList; + private List refundDetailItemList; /** * 交易在支付时候的门店名称 */ @@ -306,11 +307,11 @@ public class AliRefundResult extends BaseRefundResult { this.gmtRefundPay = gmtRefundPay; } - public TradeFundBill getRefundDetailItemList() { + public List getRefundDetailItemList() { return refundDetailItemList; } - public void setRefundDetailItemList(TradeFundBill refundDetailItemList) { + public void setRefundDetailItemList(List refundDetailItemList) { this.refundDetailItemList = refundDetailItemList; } -- Gitee From dfe5dc9f8e45c79091a22f6028bbc297f8c2879f Mon Sep 17 00:00:00 2001 From: egzosn Date: Thu, 18 Mar 2021 22:16:09 +0800 Subject: [PATCH 196/299] =?UTF-8?q?=E6=B5=8B=E8=AF=95=E4=B8=80=E4=B8=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 1 - 1 file changed, 1 deletion(-) diff --git a/.gitignore b/.gitignore index 770ee41..ac55e55 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,5 @@ # Compiled class file *.class - # Log file *.log -- Gitee From 1d6189d63abb80342c5f131fe540a0761ec77c09 Mon Sep 17 00:00:00 2001 From: Actinia-517 <412605202@qq.com> Date: Thu, 18 Mar 2021 22:22:26 +0800 Subject: [PATCH 197/299] =?UTF-8?q?=E6=94=AF=E4=BB=98=E5=AE=9D=E9=80=80?= =?UTF-8?q?=E6=AC=BE=E5=BA=8F=E5=88=97=E5=8C=96=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index ac55e55..770ee41 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,6 @@ # Compiled class file *.class + # Log file *.log -- Gitee From a0df7d71f2407ef9b66d1785c7633df592e14d18 Mon Sep 17 00:00:00 2001 From: egzosn Date: Tue, 23 Mar 2021 12:20:17 +0800 Subject: [PATCH 198/299] =?UTF-8?q?=E6=B3=A8=E9=87=8A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/main/java/com/egzosn/pay/ali/api/AliPayService.java | 2 -- 1 file changed, 2 deletions(-) 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 dd6df3d..cd81c74 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 @@ -490,7 +490,6 @@ public class AliPayService extends BasePayService { * * @param parameters 参数 * @param attrs 订单属性 - * @return 参数 */ protected void setAppAuthToken(Map parameters, Map attrs) { setAppAuthToken(parameters); @@ -501,7 +500,6 @@ public class AliPayService extends BasePayService { * 设置支付宝授权Token * * @param parameters 参数 - * @return 参数 */ protected void setAppAuthToken(Map parameters) { setParameters(parameters, APP_AUTH_TOKEN, payConfigStorage.getAppAuthToken()); -- Gitee From cc9dcd7fc2219191dbb7d35b2ac897defdc081dd Mon Sep 17 00:00:00 2001 From: egzosn Date: Wed, 24 Mar 2021 12:31:38 +0800 Subject: [PATCH 199/299] =?UTF-8?q?=E6=9B=BF=E6=8D=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pay-java-demo/src/main/webapp/gzh.png | Bin 131545 -> 37332 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/pay-java-demo/src/main/webapp/gzh.png b/pay-java-demo/src/main/webapp/gzh.png index bb9a069e24afa708eabced9fcc2b43bd3481c989..e607cb9ba904d1ac409680f6d8e7c282a0607ff8 100644 GIT binary patch literal 37332 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 literal 131545 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 -- Gitee From 74155a47138b9f2439bd6993f49496236378b568 Mon Sep 17 00:00:00 2001 From: egzosn Date: Wed, 24 Mar 2021 12:40:35 +0800 Subject: [PATCH 200/299] =?UTF-8?q?=E6=9B=BF=E6=8D=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index a3807e7..d1f0393 100644 --- a/README.md +++ b/README.md @@ -21,7 +21,7 @@ com.egzosn {module-name} - 2.14.1 + 2.14.1-b ``` -- Gitee From 66a9966e6ca1a13ad51e5b24ffd3f09d580fc393 Mon Sep 17 00:00:00 2001 From: Menjoe <1030009014@qq.com> Date: Mon, 12 Apr 2021 22:04:20 +0800 Subject: [PATCH 201/299] =?UTF-8?q?=E6=8E=A5=E9=80=9A=E7=99=BE=E5=BA=A6?= =?UTF-8?q?=E6=94=AF=E4=BB=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../pay/baidu/api/BaiduPayConfigStorage.java | 39 ++--- .../egzosn/pay/baidu/api/BaiduPayService.java | 138 ++++++++++++++++-- .../pay/baidu/api/BaiduPayServiceTest.java | 2 +- 3 files changed, 149 insertions(+), 30 deletions(-) diff --git a/pay-java-baidu/src/main/java/com/egzosn/pay/baidu/api/BaiduPayConfigStorage.java b/pay-java-baidu/src/main/java/com/egzosn/pay/baidu/api/BaiduPayConfigStorage.java index 7786b80..61ee1cd 100644 --- a/pay-java-baidu/src/main/java/com/egzosn/pay/baidu/api/BaiduPayConfigStorage.java +++ b/pay-java-baidu/src/main/java/com/egzosn/pay/baidu/api/BaiduPayConfigStorage.java @@ -3,13 +3,23 @@ package com.egzosn.pay.baidu.api; import com.egzosn.pay.common.api.BasePayConfigStorage; public class BaiduPayConfigStorage extends BasePayConfigStorage { - private String appId; + + private String appid; + private String dealId; + /** + * 支付平台公钥(签名校验使用) + */ + private String keyPublic; @Override - @Deprecated public String getAppid() { - return this.appId; + return this.appid; + } + + @Override + public String getAppId() { + return this.appid; } @Override @@ -17,9 +27,10 @@ public class BaiduPayConfigStorage extends BasePayConfigStorage { return getDealId(); } + //使用json序列化的时候会报错,所以不要直接抛出异常 @Override public String getSeller() { - throw new UnsupportedOperationException("不支持"); + return getDealId(); } public String getDealId() { @@ -31,33 +42,25 @@ public class BaiduPayConfigStorage extends BasePayConfigStorage { } public String getAppKey() { - return this.getKeyPrivate(); + return this.appid; } public void setAppKey(String appKey) { - setKeyPrivate(appKey); + this.setAppid(appKey); } @Override public String getKeyPublic() { - return super.getKeyPrivate(); + return keyPublic; } @Override public void setKeyPublic(String keyPublic) { - super.setKeyPublic(keyPublic); - } - - public void setAppid(String appId) { - this.appId = appId; + this.keyPublic = keyPublic; } - @Override - public String getAppId() { - return appId; + public void setAppid(String appid) { + this.appid = appid; } - public void setAppId(String appId) { - this.appId = appId; - } } diff --git a/pay-java-baidu/src/main/java/com/egzosn/pay/baidu/api/BaiduPayService.java b/pay-java-baidu/src/main/java/com/egzosn/pay/baidu/api/BaiduPayService.java index a1e3267..e234257 100644 --- a/pay-java-baidu/src/main/java/com/egzosn/pay/baidu/api/BaiduPayService.java +++ b/pay-java-baidu/src/main/java/com/egzosn/pay/baidu/api/BaiduPayService.java @@ -1,9 +1,13 @@ package com.egzosn.pay.baidu.api; +import java.io.UnsupportedEncodingException; import java.math.BigDecimal; -import java.util.Date; -import java.util.HashMap; -import java.util.Map; +import java.net.URLEncoder; +import java.security.*; +import java.security.spec.InvalidKeySpecException; +import java.security.spec.PKCS8EncodedKeySpec; +import java.security.spec.X509EncodedKeySpec; +import java.util.*; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONObject; @@ -51,6 +55,13 @@ public class BaiduPayService extends BasePayService { public static final String RESPONSE_STATUS = "status"; + private static final String CHARSET = "UTF-8"; + private static final String SIGN_ALGORITHMS = "SHA1WithRSA"; + private static final String SIGN_TYPE_RSA = "RSA"; + private static final String SIGN_KEY = "rsaSign"; + public static final String PAY_MONEY = "payMoney"; + + public BaiduPayService(BaiduPayConfigStorage payConfigStorage) { super(payConfigStorage); } @@ -68,12 +79,103 @@ public class BaiduPayService extends BasePayService { */ @Override public boolean verify(Map params) { - if (!RESPONSE_SUCCESS.equals(params.get(RESPONSE_STATUS))) { + if (!RESPONSE_SUCCESS.equals(params.get(RESPONSE_STATUS)) && !RESPONSE_SUCCESS.toString().equals(params.get(RESPONSE_STATUS))) { return false; } - return signVerify(params, String.valueOf(params.get(RSA_SIGN))); + LOG.info("开始验证回调签名参数:" + params); + try { + boolean checkSign = this.checkReturnSign(params, payConfigStorage.getKeyPublic(), (String) params.get(RSA_SIGN)); + return checkSign; + } catch (Exception e) { + LOG.info("验签失败", e); + } + return false; + } + + public boolean checkReturnSign(Map params, String publicKey, String rsaSign) { + try { + String content = signContent(params); + Signature signature = Signature.getInstance(SIGN_ALGORITHMS); + signature.initVerify(this.getPublicKeyX509(publicKey)); + signature.update(content.getBytes(CHARSET)); + boolean verify = signature.verify(Base64.getDecoder().decode(rsaSign.getBytes(CHARSET))); + LOG.info("使用公钥进行验签: " + verify); + return verify; + } catch (Exception e) { + LOG.info("使用公钥进行验签出错, 返回false", e); + } + return false; + } + + + /** + * 将公钥字符串进行Base64 decode之后,生成X509标准公钥 + * + * @param publicKey 公钥原始字符串 + * + * @return X509标准公钥 + * + * @throws InvalidKeySpecException + * @throws NoSuchAlgorithmException + */ + private static PublicKey getPublicKeyX509(String publicKey) throws InvalidKeySpecException, + NoSuchAlgorithmException, UnsupportedEncodingException { + if (StringUtils.isEmpty(publicKey)) { + return null; + } + KeyFactory keyFactory = KeyFactory.getInstance(SIGN_TYPE_RSA); + byte[] decodedKey = Base64.getDecoder().decode(publicKey.getBytes(CHARSET)); + return keyFactory.generatePublic(new X509EncodedKeySpec(decodedKey)); } + /** + * 对输入参数进行key过滤排序和字符串拼接 + * + * @param params 待签名参数集合 + * + * @return 待签名内容 + * + * @throws UnsupportedEncodingException + */ + private String signContent(Map params) throws UnsupportedEncodingException { + Map sortedParams = new TreeMap<>(Comparator.naturalOrder()); + for (Map.Entry entry : params.entrySet()) { + String key = entry.getKey(); + if (legalKey(key)) { + String value = + entry.getValue() == null ? null : URLEncoder.encode(entry.getValue().toString(), CHARSET); + sortedParams.put(key, value); + } + } + + StringBuilder builder = new StringBuilder(); + if (sortedParams != null && sortedParams.size() > 1) { + for (Map.Entry entry : sortedParams.entrySet()) { + if (StringUtils.equals(entry.getKey(), RSA_SIGN)) continue; + builder.append(entry.getKey()); + builder.append("="); + builder.append(entry.getValue()); + builder.append("&"); + } + builder.deleteCharAt(builder.length() - 1); + } + LOG.info("验签字符串:\n" + builder); + return builder.toString(); + } + + /** + * 有效的待签名参数key值 + * 非空、且非签名字段 + * + * @param key 待签名参数key值 + * + * @return true | false + */ + private static boolean legalKey(String key) { + return StringUtils.isNotBlank(key) && !SIGN_KEY.equalsIgnoreCase(key); + } + + /** * 验证签名 * @@ -97,8 +199,9 @@ public class BaiduPayService extends BasePayService { */ @Override public Map orderInfo(PayOrder order) { - Map params = getUseOrderInfoParams(order); - String rsaSign = getRsaSign(params, RSA_SIGN); + LOG.info("百度支付配置:" + JSON.toJSONString(payConfigStorage)); + Map params = this.getUseOrderInfoParams(order); + String rsaSign = this.getRsaSign(params, RSA_SIGN); params.put(RSA_SIGN, rsaSign); return params; } @@ -128,12 +231,15 @@ public class BaiduPayService extends BasePayService { String appKey = payConfigStorage.getAppKey(); String dealId = payConfigStorage.getDealId(); result.put(APP_KEY, appKey); - result.put(TP_ORDER_ID, payOrder.getTradeNo()); result.put(DEAL_ID, dealId); + result.put(TOTAL_AMOUNT, String.valueOf(Util.conversionCentAmount(order.getPrice()))); + result.put(TP_ORDER_ID, payOrder.getOutTradeNo()); + result.put(DEAL_TITLE, payOrder.getSubject()); result.put(SIGN_FIELDS_RANGE, payOrder.getSignFieldsRange()); result.put(BIZ_INFO, JSON.toJSONString(payOrder.getBizInfo())); - result.put(TOTAL_AMOUNT, String.valueOf(Util.conversionAmount(order.getPrice()))); + + LOG.info("百度支付 getUseOrderInfoParams:" + JSON.toJSONString(result)); return result; } @@ -489,7 +595,17 @@ public class BaiduPayService extends BasePayService { * @return 签名结果 */ private String getRsaSign(Map params, String... ignoreKeys) { - String waitSignVal = SignUtils.parameterText(params, "&", false, ignoreKeys); - return SignUtils.valueOf(payConfigStorage.getSignType()).createSign(waitSignVal, payConfigStorage.getKeyPrivate(), payConfigStorage.getInputCharset()); + Map result = new HashMap<>(); + String appKey = payConfigStorage.getAppKey(); + String dealId = payConfigStorage.getDealId(); + result.put(APP_KEY, appKey); + result.put(DEAL_ID, dealId); + result.put(TOTAL_AMOUNT, params.get(TOTAL_AMOUNT)); + result.put(TP_ORDER_ID, params.get(TP_ORDER_ID)); + + LOG.info("百度支付签名参数:" + JSON.toJSONString(result)); + + String waitSignVal = SignUtils.parameterText(result, "&", false, ignoreKeys); + return SignUtils.RSA.createSign(waitSignVal, payConfigStorage.getKeyPrivate(), payConfigStorage.getInputCharset()); } } diff --git a/pay-java-baidu/src/test/java/com/egzosn/pay/baidu/api/BaiduPayServiceTest.java b/pay-java-baidu/src/test/java/com/egzosn/pay/baidu/api/BaiduPayServiceTest.java index 9cb7434..6f9b5c2 100644 --- a/pay-java-baidu/src/test/java/com/egzosn/pay/baidu/api/BaiduPayServiceTest.java +++ b/pay-java-baidu/src/test/java/com/egzosn/pay/baidu/api/BaiduPayServiceTest.java @@ -13,7 +13,7 @@ public class BaiduPayServiceTest { @Test public void orderInfo() { BaiduPayConfigStorage configStorage = new BaiduPayConfigStorage(); - configStorage.setAppId("APP ID"); + configStorage.setAppid("APP ID"); configStorage.setAppKey("APP KEY"); configStorage.setDealId("DEAL ID"); configStorage.setKeyPublic("KEY PUBLIC"); -- Gitee From 0f24b22a325ffa2bb51f74f967f6625db9f532a7 Mon Sep 17 00:00:00 2001 From: "menjoe.zhou@islide.cc" Date: Wed, 14 Apr 2021 18:34:58 +0800 Subject: [PATCH 202/299] =?UTF-8?q?1.=20=E6=8E=92=E9=99=A41.8=E7=89=B9?= =?UTF-8?q?=E6=80=A7=202.=20=E6=9B=BF=E6=8D=A2base64=E5=B7=A5=E5=85=B7?= =?UTF-8?q?=E7=B1=BB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/egzosn/pay/baidu/api/BaiduPayService.java | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/pay-java-baidu/src/main/java/com/egzosn/pay/baidu/api/BaiduPayService.java b/pay-java-baidu/src/main/java/com/egzosn/pay/baidu/api/BaiduPayService.java index e234257..8275398 100644 --- a/pay-java-baidu/src/main/java/com/egzosn/pay/baidu/api/BaiduPayService.java +++ b/pay-java-baidu/src/main/java/com/egzosn/pay/baidu/api/BaiduPayService.java @@ -5,9 +5,9 @@ import java.math.BigDecimal; import java.net.URLEncoder; import java.security.*; import java.security.spec.InvalidKeySpecException; -import java.security.spec.PKCS8EncodedKeySpec; import java.security.spec.X509EncodedKeySpec; import java.util.*; +import com.egzosn.pay.common.util.sign.encrypt.Base64; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONObject; @@ -98,7 +98,7 @@ public class BaiduPayService extends BasePayService { Signature signature = Signature.getInstance(SIGN_ALGORITHMS); signature.initVerify(this.getPublicKeyX509(publicKey)); signature.update(content.getBytes(CHARSET)); - boolean verify = signature.verify(Base64.getDecoder().decode(rsaSign.getBytes(CHARSET))); + boolean verify = signature.verify(Base64.decode(rsaSign)); LOG.info("使用公钥进行验签: " + verify); return verify; } catch (Exception e) { @@ -119,12 +119,12 @@ public class BaiduPayService extends BasePayService { * @throws NoSuchAlgorithmException */ private static PublicKey getPublicKeyX509(String publicKey) throws InvalidKeySpecException, - NoSuchAlgorithmException, UnsupportedEncodingException { + NoSuchAlgorithmException { if (StringUtils.isEmpty(publicKey)) { return null; } KeyFactory keyFactory = KeyFactory.getInstance(SIGN_TYPE_RSA); - byte[] decodedKey = Base64.getDecoder().decode(publicKey.getBytes(CHARSET)); + byte[] decodedKey = Base64.decode(publicKey); return keyFactory.generatePublic(new X509EncodedKeySpec(decodedKey)); } @@ -138,7 +138,12 @@ public class BaiduPayService extends BasePayService { * @throws UnsupportedEncodingException */ private String signContent(Map params) throws UnsupportedEncodingException { - Map sortedParams = new TreeMap<>(Comparator.naturalOrder()); + Map sortedParams = new TreeMap<>(new Comparator() { + @Override + public int compare(String o1, String o2) { + return o1.compareTo(o2); + } + }); for (Map.Entry entry : params.entrySet()) { String key = entry.getKey(); if (legalKey(key)) { -- Gitee From dfc55e271aafe3dd6827c97f6bc786c639143712 Mon Sep 17 00:00:00 2001 From: "menjoe.zhou@islide.cc" Date: Thu, 15 Apr 2021 14:17:12 +0800 Subject: [PATCH 203/299] =?UTF-8?q?=E5=8E=BB=E9=99=A4=E4=B8=8D=E5=BF=85?= =?UTF-8?q?=E8=A6=81=E5=B1=9E=E6=80=A7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/main/java/com/egzosn/pay/baidu/api/BaiduPayService.java | 1 - 1 file changed, 1 deletion(-) diff --git a/pay-java-baidu/src/main/java/com/egzosn/pay/baidu/api/BaiduPayService.java b/pay-java-baidu/src/main/java/com/egzosn/pay/baidu/api/BaiduPayService.java index 8275398..3845a11 100644 --- a/pay-java-baidu/src/main/java/com/egzosn/pay/baidu/api/BaiduPayService.java +++ b/pay-java-baidu/src/main/java/com/egzosn/pay/baidu/api/BaiduPayService.java @@ -59,7 +59,6 @@ public class BaiduPayService extends BasePayService { private static final String SIGN_ALGORITHMS = "SHA1WithRSA"; private static final String SIGN_TYPE_RSA = "RSA"; private static final String SIGN_KEY = "rsaSign"; - public static final String PAY_MONEY = "payMoney"; public BaiduPayService(BaiduPayConfigStorage payConfigStorage) { -- Gitee From 3c56125a3575b21af47b8193f2dc33c8c84a8cdd Mon Sep 17 00:00:00 2001 From: egzosn Date: Tue, 27 Apr 2021 22:35:05 +0800 Subject: [PATCH 204/299] =?UTF-8?q?=E5=B0=86=E6=94=AF=E4=BB=98=E5=AE=9Ddem?= =?UTF-8?q?o=E5=85=AC=E9=92=A5=E8=AF=81=E4=B9=A6=E6=A1=88=E4=BE=8B?= =?UTF-8?q?=EF=BC=8C=E7=94=B1=E7=BB=9D=E5=AF=B9=E8=B7=AF=E5=BE=84=E8=AF=81?= =?UTF-8?q?=E4=B9=A6=E5=AD=98=E5=82=A8=E6=96=B9=E5=BC=8F=E4=BF=AE=E6=94=B9?= =?UTF-8?q?=E4=B8=BAclassPath=E6=96=B9=E5=BC=8F=EF=BC=8C=E7=94=A8=E4=BA=8E?= =?UTF-8?q?=E7=9B=B4=E6=8E=A5=E8=BF=9B=E8=A1=8C=E6=B5=8B=E8=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 3 ++- pay-java-demo/pom.xml | 2 +- .../pay/demo/controller/AliPayController.java | 16 ++++++++-------- pom.xml | 5 +++++ 4 files changed, 16 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index d1f0393..70468d6 100644 --- a/README.md +++ b/README.md @@ -57,7 +57,8 @@ android 例子 [pay-java-android](https://gitee.com/egzosn/pay-java-android) ## 交流 很希望更多志同道合友友一起扩展新的的支付接口。 -这里感谢[ouyangxiangshao](https://github.com/ouyangxiangshao),[ZhuangXiong](https://github.com/ZhuangXiong) 与[Actinian](http://git.oschina.net/Actinia517) 所提交的安卓例子或者分支 +开发者 +[ouyangxiangshao](https://github.com/ouyangxiangshao)、[ZhuangXiong](https://github.com/ZhuangXiong) 、[Actinian](http://gitee.com/Actinia517) 、[Menjoe](https://gitee.com/menjoe-z) 也感谢各大友友同学帮忙进行接口测试 diff --git a/pay-java-demo/pom.xml b/pay-java-demo/pom.xml index 78628fe..c83932b 100644 --- a/pay-java-demo/pom.xml +++ b/pay-java-demo/pom.xml @@ -86,7 +86,7 @@ com.fasterxml.jackson.core jackson-databind - 2.11.2 + 2.9.10.6 diff --git a/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/AliPayController.java b/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/AliPayController.java index acd961b..0933696 100644 --- a/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/AliPayController.java +++ b/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/AliPayController.java @@ -71,10 +71,10 @@ public class AliPayController { //设置为证书方式 aliPayConfigStorage.setCertSign(true); //设置证书存储方式,这里为路径 - aliPayConfigStorage.setCertStoreType(CertStoreType.PATH); - aliPayConfigStorage.setMerchantCert("E:\\pay-java-parent\\pay-java-demo\\src\\main\\resources\\ali\\alipayCertPublicKey_RSA2.crt"); - aliPayConfigStorage.setAliPayCert("E:\\pay-java-parent\\pay-java-demo\\src\\main\\resources\\ali\\alipayCertPublicKey_RSA2.crt"); - aliPayConfigStorage.setAliPayRootCert("E:\\pay-java-parent\\pay-java-demo\\src\\main\\resources\\ali\\alipayRootCert.crt"); + aliPayConfigStorage.setCertStoreType(CertStoreType.CLASS_PATH); + aliPayConfigStorage.setMerchantCert("ali/appCertPublicKey_2016080400165436.crt"); + aliPayConfigStorage.setAliPayCert("ali/alipayCertPublicKey_RSA2.crt"); + aliPayConfigStorage.setAliPayRootCert("ali/alipayRootCert.crt"); } @PostConstruct @@ -84,10 +84,10 @@ public class AliPayController { aliPayConfigStorage.setAppId("2016080400165436"); // aliPayConfigStorage.setAppAuthToken("ISV代商户代用,指定appAuthToken"); //普通公钥方式与证书公钥方式为两者取其一的方式 - keyPublic(aliPayConfigStorage); - aliPayConfigStorage.setKeyPrivate("MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBAKroe/8h5vC4L6T+B2WdXiVwGsMvUKgb2XsKix6VY3m2wcf6tyzpNRDCNykbIwGtaeo7FshN+qZxdXHLiIam9goYncBit/8ojfLGy2gLxO/PXfzGxYGs0KsDZ+ryVPPmE34ZZ8jiJpR0ygzCFl8pN3QJPJRGTJn5+FTT9EF/9zyZAgMBAAECgYAktngcYC35u7cQXDk+jMVyiVhWYU2ULxdSpPspgLGzrZyG1saOcTIi/XVX8Spd6+B6nmLQeF/FbU3rOeuD8U2clzul2Z2YMbJ0FYay9oVZFfp5gTEFpFRTVfzqUaZQBIjJe/xHL9kQVqc5xHlE/LVA27/Kx3dbC35Y7B4EVBDYAQJBAOhsX8ZreWLKPhXiXHTyLmNKhOHJc+0tFH7Ktise/0rNspojU7o9prOatKpNylp9v6kux7migcMRdVUWWiVe+4ECQQC8PqsuEz7B0yqirQchRg1DbHjh64bw9Kj82EN1/NzOUd53tP9tg+SO97EzsibK1F7tOcuwqsa7n2aY48mQ+y0ZAkBndA2xcRcnvOOjtAz5VO8G7R12rse181HjGfG6AeMadbKg30aeaGCyIxN1loiSfNR5xsPJwibGIBg81mUrqzqBAkB+K6rkaPXJR9XtzvdWb/N3235yPkDlw7Z4MiOVM3RzvR/VMDV7m8lXoeDde2zQyeMOMYy6ztwA6WgE1bhGOnQRAkEAouUBv1sVdSBlsexX15qphOmAevzYrpufKgJIRLFWQxroXMS7FTesj+f+FmGrpPCxIde1dqJ8lqYLTyJmbzMPYw=="); -// certKeyPublic(aliPayConfigStorage); -// aliPayConfigStorage.setKeyPrivate("MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQCw7MD2Cwv/jnXssFjXnGx3JlGF57gJa2aYbJRV8MnNiPVpX4Ha+8ZjnQDhvkrWH4hHmzcujOr213HqloMpUSYBzCPiXGVRUUvdimejcHHTod7nI4g6nztzzfey/TXNDHmp7vY3pOIcjB0Zn0pkNAz2tKAFkqb4raHOqTB0QA0zD24Cn+26J2UJyYRcgeH0GtSQuUrm7yaGsuKakh+qtgWF6R71n5PMGOTQ5LH3i0WVHfCBkNGgJC6yC96HR4D7cosoyKD0+lp8UB/NVUWl7Tt/KLOgFUwh0GKSYFfv56O/VBV2+xqCGE4PlZESfVuOqz5vjjxzw3xDAUJrV8hSX/AJAgMBAAECggEBAKE0d3U4B4yo/2XUIH8EdgfykCFUSum6RFbpyBauORHfksyaSzV+ZvtomN8XhhSn0oJ8OMFfgM+86nz2+zdwSxMkMCYWTfLUAi4v59KRqAVO3kz4oS3Y3FDeAK3D7XuRvGFL7GgzAhtEx1cLPrsiehVn6s5pG15GxsIIgq/JlL1J88wn1zENLrVHmD6z/JpXvfb/RS1yR+5lyoohp4g0Ph9jJ3bCyUbRpK0QkPEzgAuWL0K2ITCL7PYHNAplI8d2xHHOLF9Qdjyx+ZrQ/RxtqzfyWzhqjsmp2qlgNCxWlt3woS9UhDB+nRvjEoWTJmIOszAMYuj8wGlX+3Ui3ALOdQECgYEA25EqnFPFinUnzgNvB6NYmh5STmZun6s4bUOLqwefKtEvrOtRwTu7sB7NIf37fizG3/MJUWHxiLy2/3ub4d2JxdDNBtJoEqnp6QB12qglCNa4CajdjtJa1dR81F9QvytsqEkmPYXFPPyviB0FcSIDAGMb3IbwvIfzBPY9WY8dJnECgYEAzkg3yKEFBZ8BU0WQ+3hyfKUoAhBEnxouxRSTBcXxwstJRiqaGTVe5aoJGQI+0xS7Z6q07XDtN2t97s6DnRLWbljsX6B64itzNhXRyzjdD3iZDU/KSw7khjhXf8XOZaj9eXmACDiUnkEn1xsM8bLiRGqB8y5f3aMY/RpuACGXnxkCgYEAx/zwT9Vpr1RIfjfYcJ+Su0X0994K0roUukj0tUJK8qf4gcsQ+y1aJe/YLib1ZBaKyj7G9O5+HmqtUAUZld/AdoJZzOXmz2EeYhD+R7wxh1xz4rCBpW3qOKvDS3jJxmZaIOoHv6/RWFxb0WGFrGcrTrX3EaWDLmWxr4pNlP5qsbECgYATllntrBR8/ycyEAX/SuWcHlaZM5BAh0zvm8+GGdCmDYWMqxjs0duL9URd4o+ynWJaKqR5c2KjA4r2tRdcP+Cqo7j2L5fbiAKtnQ7JvEGJaYsm72+nBuf+MrVkRZUepBhFg5r7rNu31zoAO+pTvQetNWvXeozRz93ckrjlPEtYaQKBgQDFwbV92rlRMLjZzlY+o0knoeJBjPQmPdiBTpGNimdy9L4c2Ure7affjcUiYhkKqrK5k5SScJTATgyQ7JF346FdtUtZ/6Kkj1RwJmmprPrDa9CATLoTle7g9OVd4sHT2ITHZMzPaF3ILvzcwJ70AD1xcxCQb+/7sDPmw7Mc8gOA7Q=="); +// keyPublic(aliPayConfigStorage); +// aliPayConfigStorage.setKeyPrivate("MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBAKroe/8h5vC4L6T+B2WdXiVwGsMvUKgb2XsKix6VY3m2wcf6tyzpNRDCNykbIwGtaeo7FshN+qZxdXHLiIam9goYncBit/8ojfLGy2gLxO/PXfzGxYGs0KsDZ+ryVPPmE34ZZ8jiJpR0ygzCFl8pN3QJPJRGTJn5+FTT9EF/9zyZAgMBAAECgYAktngcYC35u7cQXDk+jMVyiVhWYU2ULxdSpPspgLGzrZyG1saOcTIi/XVX8Spd6+B6nmLQeF/FbU3rOeuD8U2clzul2Z2YMbJ0FYay9oVZFfp5gTEFpFRTVfzqUaZQBIjJe/xHL9kQVqc5xHlE/LVA27/Kx3dbC35Y7B4EVBDYAQJBAOhsX8ZreWLKPhXiXHTyLmNKhOHJc+0tFH7Ktise/0rNspojU7o9prOatKpNylp9v6kux7migcMRdVUWWiVe+4ECQQC8PqsuEz7B0yqirQchRg1DbHjh64bw9Kj82EN1/NzOUd53tP9tg+SO97EzsibK1F7tOcuwqsa7n2aY48mQ+y0ZAkBndA2xcRcnvOOjtAz5VO8G7R12rse181HjGfG6AeMadbKg30aeaGCyIxN1loiSfNR5xsPJwibGIBg81mUrqzqBAkB+K6rkaPXJR9XtzvdWb/N3235yPkDlw7Z4MiOVM3RzvR/VMDV7m8lXoeDde2zQyeMOMYy6ztwA6WgE1bhGOnQRAkEAouUBv1sVdSBlsexX15qphOmAevzYrpufKgJIRLFWQxroXMS7FTesj+f+FmGrpPCxIde1dqJ8lqYLTyJmbzMPYw=="); + certKeyPublic(aliPayConfigStorage); + aliPayConfigStorage.setKeyPrivate("MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQCw7MD2Cwv/jnXssFjXnGx3JlGF57gJa2aYbJRV8MnNiPVpX4Ha+8ZjnQDhvkrWH4hHmzcujOr213HqloMpUSYBzCPiXGVRUUvdimejcHHTod7nI4g6nztzzfey/TXNDHmp7vY3pOIcjB0Zn0pkNAz2tKAFkqb4raHOqTB0QA0zD24Cn+26J2UJyYRcgeH0GtSQuUrm7yaGsuKakh+qtgWF6R71n5PMGOTQ5LH3i0WVHfCBkNGgJC6yC96HR4D7cosoyKD0+lp8UB/NVUWl7Tt/KLOgFUwh0GKSYFfv56O/VBV2+xqCGE4PlZESfVuOqz5vjjxzw3xDAUJrV8hSX/AJAgMBAAECggEBAKE0d3U4B4yo/2XUIH8EdgfykCFUSum6RFbpyBauORHfksyaSzV+ZvtomN8XhhSn0oJ8OMFfgM+86nz2+zdwSxMkMCYWTfLUAi4v59KRqAVO3kz4oS3Y3FDeAK3D7XuRvGFL7GgzAhtEx1cLPrsiehVn6s5pG15GxsIIgq/JlL1J88wn1zENLrVHmD6z/JpXvfb/RS1yR+5lyoohp4g0Ph9jJ3bCyUbRpK0QkPEzgAuWL0K2ITCL7PYHNAplI8d2xHHOLF9Qdjyx+ZrQ/RxtqzfyWzhqjsmp2qlgNCxWlt3woS9UhDB+nRvjEoWTJmIOszAMYuj8wGlX+3Ui3ALOdQECgYEA25EqnFPFinUnzgNvB6NYmh5STmZun6s4bUOLqwefKtEvrOtRwTu7sB7NIf37fizG3/MJUWHxiLy2/3ub4d2JxdDNBtJoEqnp6QB12qglCNa4CajdjtJa1dR81F9QvytsqEkmPYXFPPyviB0FcSIDAGMb3IbwvIfzBPY9WY8dJnECgYEAzkg3yKEFBZ8BU0WQ+3hyfKUoAhBEnxouxRSTBcXxwstJRiqaGTVe5aoJGQI+0xS7Z6q07XDtN2t97s6DnRLWbljsX6B64itzNhXRyzjdD3iZDU/KSw7khjhXf8XOZaj9eXmACDiUnkEn1xsM8bLiRGqB8y5f3aMY/RpuACGXnxkCgYEAx/zwT9Vpr1RIfjfYcJ+Su0X0994K0roUukj0tUJK8qf4gcsQ+y1aJe/YLib1ZBaKyj7G9O5+HmqtUAUZld/AdoJZzOXmz2EeYhD+R7wxh1xz4rCBpW3qOKvDS3jJxmZaIOoHv6/RWFxb0WGFrGcrTrX3EaWDLmWxr4pNlP5qsbECgYATllntrBR8/ycyEAX/SuWcHlaZM5BAh0zvm8+GGdCmDYWMqxjs0duL9URd4o+ynWJaKqR5c2KjA4r2tRdcP+Cqo7j2L5fbiAKtnQ7JvEGJaYsm72+nBuf+MrVkRZUepBhFg5r7rNu31zoAO+pTvQetNWvXeozRz93ckrjlPEtYaQKBgQDFwbV92rlRMLjZzlY+o0knoeJBjPQmPdiBTpGNimdy9L4c2Ure7affjcUiYhkKqrK5k5SScJTATgyQ7JF346FdtUtZ/6Kkj1RwJmmprPrDa9CATLoTle7g9OVd4sHT2ITHZMzPaF3ILvzcwJ70AD1xcxCQb+/7sDPmw7Mc8gOA7Q=="); aliPayConfigStorage.setNotifyUrl("http://pay.egzosn.com/payBack.json"); aliPayConfigStorage.setReturnUrl("http://pay.egzosn.com/payBack.html"); aliPayConfigStorage.setSignType(SignUtils.RSA2.name()); diff --git a/pom.xml b/pom.xml index 018c8bf..193a8d6 100644 --- a/pom.xml +++ b/pom.xml @@ -39,6 +39,11 @@ hocgin@gmail.com https://github.com/hocgin + + Menjoe + 1030009014@qq.com + https://gitee.com/menjoe-z + scm:git:https://github.com/egzosn/pay-java-parent.git -- Gitee From 0fb2b8dba93807977db74ef7db7878d33bd67525 Mon Sep 17 00:00:00 2001 From: egzosn Date: Sun, 30 May 2021 20:37:04 +0800 Subject: [PATCH 205/299] =?UTF-8?q?=E9=93=B6=E8=81=94app=E6=94=AF=E4=BB=98?= =?UTF-8?q?=E9=94=99=E8=AF=AF=E7=9A=84=E8=AF=B7=E6=B1=82=E8=BF=9E=E6=8E=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/egzosn/pay/demo/entity/PayType.java | 41 +++++++++---------- .../egzosn/pay/union/api/UnionPayService.java | 13 +++--- 2 files changed, 27 insertions(+), 27 deletions(-) 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 27fd489..dbfa77f 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 @@ -7,7 +7,7 @@ import com.egzosn.pay.common.api.PayService; import com.egzosn.pay.common.bean.BasePayType; import com.egzosn.pay.common.bean.CertStoreType; import com.egzosn.pay.common.bean.TransactionType; -import com.egzosn.pay.common.http.HttpConfigStorage; +import com.egzosn.pay.common.util.sign.SignUtils; import com.egzosn.pay.demo.service.handler.WxPayMessageHandler; import com.egzosn.pay.fuiou.api.FuiouPayConfigStorage; import com.egzosn.pay.fuiou.api.FuiouPayService; @@ -47,27 +47,24 @@ public enum PayType implements BasePayType { */ @Override public PayService getPayService(ApyAccount apyAccount) { - AliPayConfigStorage configStorage = new AliPayConfigStorage(); - //配置的附加参数的使用 - configStorage.setAttach(apyAccount.getPayId()); - configStorage.setPid(apyAccount.getPartner()); - configStorage.setAppId(apyAccount.getAppId()); - configStorage.setKeyPublic(apyAccount.getPublicKey()); - configStorage.setKeyPrivate(apyAccount.getPrivateKey()); - configStorage.setNotifyUrl(apyAccount.getNotifyUrl()); - configStorage.setReturnUrl(apyAccount.getReturnUrl()); - configStorage.setSignType(apyAccount.getSignType()); - configStorage.setSeller(apyAccount.getSeller()); - configStorage.setPayType(apyAccount.getPayType().toString()); - configStorage.setInputCharset(apyAccount.getInputCharset()); - configStorage.setTest(apyAccount.isTest()); - //请求连接池配置 - HttpConfigStorage httpConfigStorage = new HttpConfigStorage(); - //最大连接数 - httpConfigStorage.setMaxTotal(20); - //默认的每个路由的最大连接数 - httpConfigStorage.setDefaultMaxPerRoute(10); - return new AliPayService(configStorage, httpConfigStorage); + AliPayConfigStorage aliPayConfigStorage = new AliPayConfigStorage(); + aliPayConfigStorage.setPid("2088102169916436"); + aliPayConfigStorage.setAppId("2016080400165436"); + aliPayConfigStorage.setCertSign(true); + //设置证书存储方式,这里为路径 + aliPayConfigStorage.setCertStoreType(CertStoreType.CLASS_PATH); + aliPayConfigStorage.setMerchantCert("ali/appCertPublicKey_2016080400165436.crt"); + aliPayConfigStorage.setAliPayCert("ali/alipayCertPublicKey_RSA2.crt"); + aliPayConfigStorage.setAliPayRootCert("ali/alipayRootCert.crt"); + aliPayConfigStorage.setKeyPrivate("MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQCw7MD2Cwv/jnXssFjXnGx3JlGF57gJa2aYbJRV8MnNiPVpX4Ha+8ZjnQDhvkrWH4hHmzcujOr213HqloMpUSYBzCPiXGVRUUvdimejcHHTod7nI4g6nztzzfey/TXNDHmp7vY3pOIcjB0Zn0pkNAz2tKAFkqb4raHOqTB0QA0zD24Cn+26J2UJyYRcgeH0GtSQuUrm7yaGsuKakh+qtgWF6R71n5PMGOTQ5LH3i0WVHfCBkNGgJC6yC96HR4D7cosoyKD0+lp8UB/NVUWl7Tt/KLOgFUwh0GKSYFfv56O/VBV2+xqCGE4PlZESfVuOqz5vjjxzw3xDAUJrV8hSX/AJAgMBAAECggEBAKE0d3U4B4yo/2XUIH8EdgfykCFUSum6RFbpyBauORHfksyaSzV+ZvtomN8XhhSn0oJ8OMFfgM+86nz2+zdwSxMkMCYWTfLUAi4v59KRqAVO3kz4oS3Y3FDeAK3D7XuRvGFL7GgzAhtEx1cLPrsiehVn6s5pG15GxsIIgq/JlL1J88wn1zENLrVHmD6z/JpXvfb/RS1yR+5lyoohp4g0Ph9jJ3bCyUbRpK0QkPEzgAuWL0K2ITCL7PYHNAplI8d2xHHOLF9Qdjyx+ZrQ/RxtqzfyWzhqjsmp2qlgNCxWlt3woS9UhDB+nRvjEoWTJmIOszAMYuj8wGlX+3Ui3ALOdQECgYEA25EqnFPFinUnzgNvB6NYmh5STmZun6s4bUOLqwefKtEvrOtRwTu7sB7NIf37fizG3/MJUWHxiLy2/3ub4d2JxdDNBtJoEqnp6QB12qglCNa4CajdjtJa1dR81F9QvytsqEkmPYXFPPyviB0FcSIDAGMb3IbwvIfzBPY9WY8dJnECgYEAzkg3yKEFBZ8BU0WQ+3hyfKUoAhBEnxouxRSTBcXxwstJRiqaGTVe5aoJGQI+0xS7Z6q07XDtN2t97s6DnRLWbljsX6B64itzNhXRyzjdD3iZDU/KSw7khjhXf8XOZaj9eXmACDiUnkEn1xsM8bLiRGqB8y5f3aMY/RpuACGXnxkCgYEAx/zwT9Vpr1RIfjfYcJ+Su0X0994K0roUukj0tUJK8qf4gcsQ+y1aJe/YLib1ZBaKyj7G9O5+HmqtUAUZld/AdoJZzOXmz2EeYhD+R7wxh1xz4rCBpW3qOKvDS3jJxmZaIOoHv6/RWFxb0WGFrGcrTrX3EaWDLmWxr4pNlP5qsbECgYATllntrBR8/ycyEAX/SuWcHlaZM5BAh0zvm8+GGdCmDYWMqxjs0duL9URd4o+ynWJaKqR5c2KjA4r2tRdcP+Cqo7j2L5fbiAKtnQ7JvEGJaYsm72+nBuf+MrVkRZUepBhFg5r7rNu31zoAO+pTvQetNWvXeozRz93ckrjlPEtYaQKBgQDFwbV92rlRMLjZzlY+o0knoeJBjPQmPdiBTpGNimdy9L4c2Ure7affjcUiYhkKqrK5k5SScJTATgyQ7JF346FdtUtZ/6Kkj1RwJmmprPrDa9CATLoTle7g9OVd4sHT2ITHZMzPaF3ILvzcwJ70AD1xcxCQb+/7sDPmw7Mc8gOA7Q=="); + aliPayConfigStorage.setNotifyUrl("http://pay.egzosn.com/payBack.json"); + aliPayConfigStorage.setReturnUrl("http://pay.egzosn.com/payBack.html"); + aliPayConfigStorage.setSignType(SignUtils.RSA2.name()); + aliPayConfigStorage.setSeller("2088102169916436"); + aliPayConfigStorage.setInputCharset("utf-8"); + //是否为测试账号,沙箱环境 + aliPayConfigStorage.setTest(true); + return new AliPayService(aliPayConfigStorage); } @Override diff --git a/pay-java-union/src/main/java/com/egzosn/pay/union/api/UnionPayService.java b/pay-java-union/src/main/java/com/egzosn/pay/union/api/UnionPayService.java index 37c3af6..d82fd78 100644 --- a/pay-java-union/src/main/java/com/egzosn/pay/union/api/UnionPayService.java +++ b/pay-java-union/src/main/java/com/egzosn/pay/union/api/UnionPayService.java @@ -149,6 +149,9 @@ public class UnionPayService extends BasePayService { public String getBackTransUrl() { return String.format(BACK_TRANS_URL, getReqUrl()); } + public String getAppTransUrl() { + return String.format(APP_TRANS_URL, getReqUrl()); + } public String getSingleQueryUrl() { return String.format(SINGLE_QUERY_URL, getReqUrl()); @@ -398,9 +401,9 @@ public class UnionPayService extends BasePayService { * @return 返回支付结果 */ - public JSONObject postOrder(PayOrder order) { + public JSONObject postOrder(PayOrder order, String url) { Map params = orderInfo(order); - String responseStr = getHttpRequestTemplate().postForObject(this.getBackTransUrl(), params, String.class); + String responseStr = getHttpRequestTemplate().postForObject(url, params, String.class); JSONObject response = UriVariables.getParametersToMap(responseStr); if (response.isEmpty()) { throw new PayErrorException(new PayException("failure", "响应内容有误!", responseStr)); @@ -430,7 +433,7 @@ public class UnionPayService extends BasePayService { @Override public String getQrPay(PayOrder order) { order.setTransactionType(UnionTransactionType.APPLY_QR_CODE); - JSONObject response = postOrder(order); + JSONObject response = postOrder(order, getBackTransUrl()); if (this.verify(response)) { if (SDKConstants.OK_RESP_CODE.equals(response.get(SDKConstants.param_respCode))) { //成功 @@ -450,7 +453,7 @@ public class UnionPayService extends BasePayService { @Override public Map microPay(PayOrder order) { order.setTransactionType(UnionTransactionType.CONSUME); - JSONObject response = postOrder(order); + JSONObject response = postOrder(order, getBackTransUrl()); return response; } @@ -539,7 +542,7 @@ public class UnionPayService extends BasePayService { if (null == order.getTransactionType()) { order.setTransactionType(UnionTransactionType.APP); } - JSONObject response = postOrder(order); + JSONObject response = postOrder(order, getAppTransUrl()); if (this.verify(response)) { if (SDKConstants.OK_RESP_CODE.equals(response.get(SDKConstants.param_respCode))) { // //成功,获取tn号 -- Gitee From d40efb21c52da23ff240bbaaf59dd6b4ee5ab36e Mon Sep 17 00:00:00 2001 From: egzosn Date: Sun, 30 May 2021 20:51:16 +0800 Subject: [PATCH 206/299] =?UTF-8?q?=E5=88=87=E6=8D=A2=E6=97=A5=E5=BF=97?= =?UTF-8?q?=E7=BB=84=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../pay/ali/utils/AntCertificationUtil.java | 6 ++--- pay-java-common/pom.xml | 5 +++-- .../egzosn/pay/common/api/BasePayService.java | 11 +++++----- .../common/api/DefaultPayMessageHandler.java | 11 +++++----- .../pay/common/api/PayMessageRouter.java | 6 ++--- .../pay/common/http/ClientHttpRequest.java | 6 ++--- .../pay/common/http/HttpRequestTemplate.java | 12 +++++----- .../egzosn/pay/common/http/UriVariables.java | 8 +++---- .../com/egzosn/pay/common/util/DateUtils.java | 8 +++---- .../pay/common/util/LogExceptionHandler.java | 8 +++---- .../pay/common/util/sign/CertDescriptor.java | 6 ++--- .../pay/common/util/sign/SecureUtil.java | 12 +++++----- .../pay/common/util/sign/SignUtils.java | 12 +++++----- .../pay/common/util/sign/encrypt/RSA.java | 22 +++++++++---------- .../pay/paypal/v2/api/PayPalPayService.java | 2 +- .../egzosn/pay/union/api/UnionPayService.java | 6 ++--- .../com/egzosn/pay/wx/api/WxPayService.java | 2 +- 17 files changed, 73 insertions(+), 70 deletions(-) diff --git a/pay-java-ali/src/main/java/com/egzosn/pay/ali/utils/AntCertificationUtil.java b/pay-java-ali/src/main/java/com/egzosn/pay/ali/utils/AntCertificationUtil.java index e225431..b3057cd 100644 --- a/pay-java-ali/src/main/java/com/egzosn/pay/ali/utils/AntCertificationUtil.java +++ b/pay-java-ali/src/main/java/com/egzosn/pay/ali/utils/AntCertificationUtil.java @@ -24,8 +24,8 @@ import java.util.HashMap; import java.util.List; import java.util.Map; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.bouncycastle.jce.provider.BouncyCastleProvider; import com.egzosn.pay.common.bean.result.PayException; @@ -44,7 +44,7 @@ import com.egzosn.pay.common.util.str.StringUtils; * */ public class AntCertificationUtil { - private static final Log LOGGER = LogFactory.getLog(AntCertificationUtil.class); + private static final Logger LOGGER = LoggerFactory.getLogger(AntCertificationUtil.class); static { Security.removeProvider("SunEC"); Security.addProvider(new BouncyCastleProvider()); diff --git a/pay-java-common/pom.xml b/pay-java-common/pom.xml index 143d887..fb24df8 100644 --- a/pay-java-common/pom.xml +++ b/pay-java-common/pom.xml @@ -28,8 +28,9 @@ - log4j - log4j + org.slf4j + slf4j-api + 1.7.30 diff --git a/pay-java-common/src/main/java/com/egzosn/pay/common/api/BasePayService.java b/pay-java-common/src/main/java/com/egzosn/pay/common/api/BasePayService.java index 38e00a9..4e0a2aa 100644 --- a/pay-java-common/src/main/java/com/egzosn/pay/common/api/BasePayService.java +++ b/pay-java-common/src/main/java/com/egzosn/pay/common/api/BasePayService.java @@ -11,8 +11,9 @@ import java.util.List; import java.util.Map; import java.util.TreeMap; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import com.alibaba.fastjson.JSON; import com.egzosn.pay.common.bean.MethodType; @@ -38,7 +39,7 @@ import com.egzosn.pay.common.util.str.StringUtils; * */ public abstract class BasePayService implements PayService { - protected final Log LOG = LogFactory.getLog(getClass()); + protected final Logger LOG = LoggerFactory.getLogger(getClass()); protected PC payConfigStorage; protected HttpRequestTemplate requestTemplate; @@ -112,7 +113,7 @@ public abstract class BasePayService implements Pay base64ClientID = com.egzosn.pay.common.util.sign.encrypt.Base64.encode(String.format("%s:%s", user, password).getBytes("UTF-8")); } catch (UnsupportedEncodingException e) { - LOG.error(e); + LOG.error("", e); } return base64ClientID; @@ -202,7 +203,7 @@ public abstract class BasePayService implements Pay } } catch (UnsupportedEncodingException e) { - LOG.error(e); + LOG.error("", e); } } params.put(name, valueStr); diff --git a/pay-java-common/src/main/java/com/egzosn/pay/common/api/DefaultPayMessageHandler.java b/pay-java-common/src/main/java/com/egzosn/pay/common/api/DefaultPayMessageHandler.java index 496b7a3..0f136f5 100644 --- a/pay-java-common/src/main/java/com/egzosn/pay/common/api/DefaultPayMessageHandler.java +++ b/pay-java-common/src/main/java/com/egzosn/pay/common/api/DefaultPayMessageHandler.java @@ -1,13 +1,14 @@ package com.egzosn.pay.common.api; +import java.util.Map; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + import com.alibaba.fastjson.JSON; import com.egzosn.pay.common.bean.PayMessage; import com.egzosn.pay.common.bean.PayOutMessage; import com.egzosn.pay.common.exception.PayErrorException; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; - -import java.util.Map; /** * 默认处理支付回调消息的处理器接口 @@ -21,7 +22,7 @@ import java.util.Map; */ public class DefaultPayMessageHandler implements PayMessageHandler { - protected final Log LOG = LogFactory.getLog(DefaultPayMessageHandler.class); + protected final Logger LOG = LoggerFactory.getLogger(DefaultPayMessageHandler.class); /** * @param payMessage 支付消息 * @param context 上下文,如果handler或interceptor之间有信息要传递,可以用这个 diff --git a/pay-java-common/src/main/java/com/egzosn/pay/common/api/PayMessageRouter.java b/pay-java-common/src/main/java/com/egzosn/pay/common/api/PayMessageRouter.java index 2fe8825..22817b7 100644 --- a/pay-java-common/src/main/java/com/egzosn/pay/common/api/PayMessageRouter.java +++ b/pay-java-common/src/main/java/com/egzosn/pay/common/api/PayMessageRouter.java @@ -8,8 +8,8 @@ import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import com.egzosn.pay.common.bean.PayMessage; import com.egzosn.pay.common.bean.PayOutMessage; @@ -44,8 +44,8 @@ import com.egzosn.pay.common.util.LogExceptionHandler; * @author egan */ public class PayMessageRouter { + protected final Logger LOG = LoggerFactory.getLogger(PayMessageRouter.class); - protected final Log LOG = LogFactory.getLog(PayMessageRouter.class); /** * 异步线程大小 */ 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 8b6fcce..49681ed 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 @@ -9,8 +9,8 @@ import java.net.URI; import java.nio.charset.Charset; import java.util.Map; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.apache.http.Consts; import org.apache.http.Header; import org.apache.http.HttpEntity; @@ -46,7 +46,7 @@ import com.egzosn.pay.common.util.str.StringUtils; * */ public class ClientHttpRequest extends HttpEntityEnclosingRequestBase implements org.apache.http.client.ResponseHandler { - protected static final Log LOG = LogFactory.getLog(ClientHttpRequest.class); + protected static final Logger LOG = LoggerFactory.getLogger(ClientHttpRequest.class); public static final ContentType APPLICATION_FORM_URLENCODED_UTF_8 = ContentType.create("application/x-www-form-urlencoded", Consts.UTF_8); public static final ContentType APPLICATION_XML_UTF_8 = ContentType.create("application/xml", Consts.UTF_8); diff --git a/pay-java-common/src/main/java/com/egzosn/pay/common/http/HttpRequestTemplate.java b/pay-java-common/src/main/java/com/egzosn/pay/common/http/HttpRequestTemplate.java index ae4ff73..b7b1ed3 100644 --- a/pay-java-common/src/main/java/com/egzosn/pay/common/http/HttpRequestTemplate.java +++ b/pay-java-common/src/main/java/com/egzosn/pay/common/http/HttpRequestTemplate.java @@ -4,8 +4,8 @@ import com.egzosn.pay.common.bean.MethodType; import com.egzosn.pay.common.bean.result.PayException; import com.egzosn.pay.common.exception.PayErrorException; import com.egzosn.pay.common.util.str.StringUtils; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.apache.http.HttpHost; import org.apache.http.auth.AuthScope; import org.apache.http.auth.UsernamePasswordCredentials; @@ -44,7 +44,7 @@ import java.util.Map; */ public class HttpRequestTemplate { - protected static final Log LOG = LogFactory.getLog(HttpRequestTemplate.class); + protected static final Logger LOG = LoggerFactory.getLogger(HttpRequestTemplate.class); protected CloseableHttpClient httpClient; @@ -123,7 +123,7 @@ public class HttpRequestTemplate { try { return sslsf = new SSLConnectionSocketFactory(SSLContext.getDefault()); } catch (NoSuchAlgorithmException e) { - LOG.error(e); + LOG.error("", e); } } @@ -149,9 +149,9 @@ public class HttpRequestTemplate { return sslsf; } catch (IOException e) { - LOG.error(e); + LOG.error("", e); } catch (GeneralSecurityException e) { - LOG.error(e); + LOG.error("", e); } return null; diff --git a/pay-java-common/src/main/java/com/egzosn/pay/common/http/UriVariables.java b/pay-java-common/src/main/java/com/egzosn/pay/common/http/UriVariables.java index b8bb0c0..8f59338 100644 --- a/pay-java-common/src/main/java/com/egzosn/pay/common/http/UriVariables.java +++ b/pay-java-common/src/main/java/com/egzosn/pay/common/http/UriVariables.java @@ -5,8 +5,8 @@ import java.net.URLEncoder; import java.util.List; import java.util.Map; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import com.alibaba.fastjson.JSONObject; import com.egzosn.pay.common.bean.result.PayException; @@ -22,7 +22,7 @@ import com.egzosn.pay.common.exception.PayErrorException; * */ public final class UriVariables { - private static final Log LOG = LogFactory.getLog(UriVariables.class); + private static final Logger LOG = LoggerFactory.getLogger(UriVariables.class); private UriVariables() { } @@ -213,7 +213,7 @@ public final class UriVariables { return URLEncoder.encode(str, enc); } catch (UnsupportedEncodingException e) { - LOG.error(e); + LOG.error("", e); } return str; } diff --git a/pay-java-common/src/main/java/com/egzosn/pay/common/util/DateUtils.java b/pay-java-common/src/main/java/com/egzosn/pay/common/util/DateUtils.java index 5cd5111..daa6747 100644 --- a/pay-java-common/src/main/java/com/egzosn/pay/common/util/DateUtils.java +++ b/pay-java-common/src/main/java/com/egzosn/pay/common/util/DateUtils.java @@ -8,8 +8,8 @@ import java.util.HashMap; import java.util.Map; import java.util.TimeZone; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.apache.http.util.Args; /** @@ -22,7 +22,7 @@ import org.apache.http.util.Args; * */ public final class DateUtils { - private static final Log LOG = LogFactory.getLog(DateUtils.class); + private static final Logger LOG = LoggerFactory.getLogger(DateUtils.class); private DateUtils() { } @@ -81,7 +81,7 @@ public final class DateUtils { return formatFor.parse(date); } catch (ParseException e) { - LOG.error(e); + LOG.error("", e); } return null; } diff --git a/pay-java-common/src/main/java/com/egzosn/pay/common/util/LogExceptionHandler.java b/pay-java-common/src/main/java/com/egzosn/pay/common/util/LogExceptionHandler.java index 35550c5..01a1c46 100644 --- a/pay-java-common/src/main/java/com/egzosn/pay/common/util/LogExceptionHandler.java +++ b/pay-java-common/src/main/java/com/egzosn/pay/common/util/LogExceptionHandler.java @@ -2,8 +2,8 @@ package com.egzosn.pay.common.util; import com.egzosn.pay.common.api.PayErrorExceptionHandler; import com.egzosn.pay.common.exception.PayErrorException; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; @@ -20,12 +20,12 @@ import org.apache.commons.logging.LogFactory; */ public class LogExceptionHandler implements PayErrorExceptionHandler { - protected final Log log = LogFactory.getLog(PayErrorExceptionHandler.class); + protected final Logger LOGGER = LoggerFactory.getLogger(PayErrorExceptionHandler.class); @Override public void handle(PayErrorException e) { - log.error("Error happens", e); + LOGGER.error("Error happens", e); } diff --git a/pay-java-common/src/main/java/com/egzosn/pay/common/util/sign/CertDescriptor.java b/pay-java-common/src/main/java/com/egzosn/pay/common/util/sign/CertDescriptor.java index 3577a11..2419571 100644 --- a/pay-java-common/src/main/java/com/egzosn/pay/common/util/sign/CertDescriptor.java +++ b/pay-java-common/src/main/java/com/egzosn/pay/common/util/sign/CertDescriptor.java @@ -15,8 +15,8 @@ package com.egzosn.pay.common.util.sign; import com.egzosn.pay.common.util.str.StringUtils; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import java.io.FileInputStream; import java.io.FileNotFoundException; @@ -35,7 +35,7 @@ import java.util.Enumeration; * 声明:以下代码只是为了方便接入方测试而提供的样例代码,商户可以根据自己需要,按照技术文档编写。该代码仅供参考,不提供编码,性能,规范性等方面的保障 */ public class CertDescriptor { - protected static final Log LOG = LogFactory.getLog(CertDescriptor.class); + protected static final Logger LOG = LoggerFactory.getLogger(CertDescriptor.class); /** * 证书容器,存储对商户请求报文签名私钥证书. */ diff --git a/pay-java-common/src/main/java/com/egzosn/pay/common/util/sign/SecureUtil.java b/pay-java-common/src/main/java/com/egzosn/pay/common/util/sign/SecureUtil.java index 22839a4..24093c3 100644 --- a/pay-java-common/src/main/java/com/egzosn/pay/common/util/sign/SecureUtil.java +++ b/pay-java-common/src/main/java/com/egzosn/pay/common/util/sign/SecureUtil.java @@ -6,8 +6,8 @@ import java.security.NoSuchAlgorithmException; import java.security.PublicKey; import java.security.Signature; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import com.egzosn.pay.common.bean.result.PayException; import com.egzosn.pay.common.exception.PayErrorException; @@ -15,7 +15,7 @@ import com.egzosn.pay.common.util.sign.encrypt.sm3.SM3Digest; public class SecureUtil { - private static final Log LOG = LogFactory.getLog(SecureUtil.class); + private static final Logger LOG = LoggerFactory.getLogger(SecureUtil.class); /** * 算法常量: SHA1 */ @@ -49,7 +49,7 @@ public class SecureUtil { return md.digest(); } catch (NoSuchAlgorithmException e) { - LOG.error(e); + LOG.error("", e); return null; } } @@ -77,7 +77,7 @@ public class SecureUtil { return sha1StrBuff.toString().getBytes(encoding); } catch (UnsupportedEncodingException e) { - LOG.error(e); + LOG.error("", e); return null; } } @@ -139,7 +139,7 @@ public class SecureUtil { bytes = SecureUtil.sm3(data.getBytes(encoding)); } catch (UnsupportedEncodingException e) { - LOG.error(e); + LOG.error("", e); } StringBuilder sm3StrBuff = new StringBuilder(); for (int i = 0; i < bytes.length; i++) { 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 07957ea..4c3f106 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 @@ -17,8 +17,8 @@ import java.util.UUID; import javax.crypto.Mac; import javax.crypto.spec.SecretKeySpec; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.apache.http.message.BasicNameValuePair; import com.egzosn.pay.common.bean.SignType; @@ -93,13 +93,13 @@ public enum SignUtils implements SignType { return sb.toString().toUpperCase(); } catch (NoSuchAlgorithmException e) { - LOG.error(e); + LOG.error("", e); } catch (InvalidKeyException e) { - LOG.error(e); + LOG.error("", e); } catch (UnsupportedEncodingException e) { - LOG.error(e); + LOG.error("", e); } throw new PayErrorException(new PayException("fail", "HMACSHA256 签名异常")); @@ -177,7 +177,7 @@ public enum SignUtils implements SignType { return com.egzosn.pay.common.util.sign.encrypt.RSA2.verify(text, sign, publicKey, characterEncoding); } }; - private static final Log LOG = LogFactory.getLog(SignUtils.class); + private static final Logger LOG = LoggerFactory.getLogger(SignUtils.class); /** * 把数组所有元素排序,并按照“参数=参数值”的模式用“@param separator”字符拼接成字符串 diff --git a/pay-java-common/src/main/java/com/egzosn/pay/common/util/sign/encrypt/RSA.java b/pay-java-common/src/main/java/com/egzosn/pay/common/util/sign/encrypt/RSA.java index e1c0490..ec060ac 100644 --- a/pay-java-common/src/main/java/com/egzosn/pay/common/util/sign/encrypt/RSA.java +++ b/pay-java-common/src/main/java/com/egzosn/pay/common/util/sign/encrypt/RSA.java @@ -17,8 +17,8 @@ import java.security.spec.X509EncodedKeySpec; import javax.crypto.Cipher; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; /** * RSA @@ -29,7 +29,7 @@ import org.apache.commons.logging.LogFactory; * */ public class RSA { - private static final Log LOG = LogFactory.getLog(RSA.class); + private static final Logger LOG = LoggerFactory.getLogger(RSA.class); private static final String ALGORITHM = "RSA"; @@ -61,10 +61,10 @@ public class RSA { return Base64.encode(signed); } catch (GeneralSecurityException e) { - LOG.error(e); + LOG.error("", e); } catch (UnsupportedEncodingException e) { - LOG.error(e); + LOG.error("", e); } return null; @@ -89,10 +89,10 @@ public class RSA { return Base64.encode(signed); } catch (GeneralSecurityException e) { - LOG.error(e); + LOG.error("", e); } catch (UnsupportedEncodingException e) { - LOG.error(e); + LOG.error("", e); } return null; @@ -142,10 +142,10 @@ public class RSA { return signature.verify(Base64.decode(sign)); } catch (GeneralSecurityException e) { - LOG.error(e); + LOG.error("", e); } catch (IOException e) { - LOG.error(e); + LOG.error("", e); } return false; } @@ -168,10 +168,10 @@ public class RSA { return signature.verify(Base64.decode(sign)); } catch (GeneralSecurityException e) { - LOG.error(e); + LOG.error("", e); } catch (IOException e) { - LOG.error(e); + LOG.error("", e); } return false; } diff --git a/pay-java-paypal/src/main/java/com/egzosn/pay/paypal/v2/api/PayPalPayService.java b/pay-java-paypal/src/main/java/com/egzosn/pay/paypal/v2/api/PayPalPayService.java index 10289dd..3fd521f 100644 --- a/pay-java-paypal/src/main/java/com/egzosn/pay/paypal/v2/api/PayPalPayService.java +++ b/pay-java-paypal/src/main/java/com/egzosn/pay/paypal/v2/api/PayPalPayService.java @@ -528,7 +528,7 @@ public class PayPalPayService extends BasePayService implem } } catch (UnsupportedEncodingException e) { - LOG.error(e); + LOG.error("", e); } } params.put(name, valueStr); diff --git a/pay-java-union/src/main/java/com/egzosn/pay/union/api/UnionPayService.java b/pay-java-union/src/main/java/com/egzosn/pay/union/api/UnionPayService.java index d82fd78..6fde8a7 100644 --- a/pay-java-union/src/main/java/com/egzosn/pay/union/api/UnionPayService.java +++ b/pay-java-union/src/main/java/com/egzosn/pay/union/api/UnionPayService.java @@ -115,7 +115,7 @@ public class UnionPayService extends BasePayService { certDescriptor.initRootCert(payConfigStorage.getAcpRootCertInputStream()); } catch (IOException e) { - LOG.error(e); + LOG.error("", e); } @@ -386,10 +386,10 @@ public class UnionPayService extends BasePayService { LOG.error("verify certificate chain fail.", e); } catch (CertificateExpiredException e) { - LOG.error(e); + LOG.error("", e); } catch (GeneralSecurityException e) { - LOG.error(e); + LOG.error("", e); } 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 f854859..fb4c039 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 @@ -148,7 +148,7 @@ public class WxPayService extends BasePayService implements return signVerify(params, (String) params.get(SIGN)); } catch (PayErrorException e) { - LOG.error(e); + LOG.error("", e); } return false; } -- Gitee From 4d1ad05fa8d6dd2d24c271aafda86c20628da4b1 Mon Sep 17 00:00:00 2001 From: egan Date: Sun, 1 Aug 2021 23:33:02 +0800 Subject: [PATCH 207/299] =?UTF-8?q?=E7=A7=BB=E9=99=A4=E8=BF=87=E6=97=B6?= =?UTF-8?q?=E6=96=B9=E6=B3=95=EF=BC=8C=E5=B7=A5=E5=85=B7=E7=B1=BB=E8=BF=81?= =?UTF-8?q?=E7=A7=BB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/egzosn/pay/ali/api/AliPayService.java | 12 ++-- .../egzosn/pay/baidu/api/BaiduPayService.java | 31 ++++----- .../com/egzosn/pay/baidu/package-info.java | 1 - .../egzosn/pay/common/api/BasePayService.java | 19 +++--- .../com/egzosn/pay/common/api/PayService.java | 15 +---- .../common/exception/PayErrorException.java | 5 ++ .../pay/demo/controller/PayController.java | 6 +- .../demo/controller/UnionPayController.java | 11 ++-- .../pay/demo/dao/ApyAccountRepository.java | 2 +- .../egzosn/pay/fuiou/api/FuiouPayService.java | 24 ++----- .../pay/payoneer/api/PayoneerPayService.java | 13 +--- .../pay/paypal/api/PayPalPayService.java | 5 -- .../pay/paypal/v2/api/PayPalPayService.java | 8 --- .../egzosn/pay/union/api/UnionPayService.java | 29 ++++----- pay-java-union/src/test/java/PayTest.java | 2 +- .../wx/youdian/api/WxYouDianPayService.java | 36 +++++------ pay-java-wx/README.md | 2 +- .../com/egzosn/pay/wx/api/WxPayService.java | 63 ++++++++----------- .../egzosn/pay/yiji/api/YiJiPayService.java | 17 +---- 19 files changed, 113 insertions(+), 188 deletions(-) delete mode 100644 pay-java-baidu/src/main/java/com/egzosn/pay/baidu/package-info.java 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 cd81c74..1e94971 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 @@ -46,6 +46,7 @@ import com.egzosn.pay.common.http.HttpConfigStorage; import com.egzosn.pay.common.http.UriVariables; import com.egzosn.pay.common.util.DateUtils; import com.egzosn.pay.common.util.Util; +import com.egzosn.pay.common.util.sign.SignTextUtils; import com.egzosn.pay.common.util.sign.SignUtils; import com.egzosn.pay.common.util.str.StringUtils; @@ -94,7 +95,6 @@ public class AliPayService extends BasePayService { public AliPayService(AliPayConfigStorage payConfigStorage, HttpConfigStorage configStorage) { super(payConfigStorage, configStorage); - payConfigStorage.loadCertEnvironment(); } public AliPayService(AliPayConfigStorage payConfigStorage) { @@ -187,7 +187,7 @@ public class AliPayService extends BasePayService { */ protected Map setSign(Map parameters) { parameters.put("sign_type", payConfigStorage.getSignType()); - String sign = createSign(SignUtils.parameterText(parameters, "&", SIGN), payConfigStorage.getInputCharset()); + String sign = createSign(SignTextUtils.parameterText(parameters, "&", SIGN), payConfigStorage.getInputCharset()); parameters.put(SIGN, sign); return parameters; @@ -566,9 +566,7 @@ public class AliPayService extends BasePayService { * @param billType 账单类型,商户通过接口或商户经开放平台授权后其所属服务商通过接口可以获取以下账单类型:trade、signcustomer;trade指商户基于支付宝交易收单的业务账单;signcustomer是指基于商户支付宝余额收入及支出等资金变动的帐务账单; * @return 返回支付方下载对账单的结果 */ - @Deprecated - @Override - public Map downloadbill(Date billDate, String billType) { + public Map downloadBill(Date billDate, String billType) { return this.downloadBill(billDate, "trade".equals(billType) ? AliPayBillType.TRADE_DAY : AliPayBillType.SIGNCUSTOMER_DAY); } @@ -614,7 +612,7 @@ public class AliPayService extends BasePayService { if (transactionType == AliTransactionType.DOWNLOADBILL) { if (tradeNoOrBillDate instanceof Date) { - return downloadbill((Date) tradeNoOrBillDate, outTradeNoBillType); + return downloadBill((Date) tradeNoOrBillDate, outTradeNoBillType); } throw new PayErrorException(new PayException("failure", "非法类型异常:" + tradeNoOrBillDate.getClass())); } @@ -627,7 +625,7 @@ public class AliPayService extends BasePayService { //设置签名 setSign(parameters); - return requestTemplate.getForObject(getReqUrl() + "?" + UriVariables.getMapToParameters(parameters), JSONObject.class); + return requestTemplate.getForObject(getReqUrl(transactionType) + "?" + UriVariables.getMapToParameters(parameters), JSONObject.class); } /** diff --git a/pay-java-baidu/src/main/java/com/egzosn/pay/baidu/api/BaiduPayService.java b/pay-java-baidu/src/main/java/com/egzosn/pay/baidu/api/BaiduPayService.java index 3845a11..7d0fcde 100644 --- a/pay-java-baidu/src/main/java/com/egzosn/pay/baidu/api/BaiduPayService.java +++ b/pay-java-baidu/src/main/java/com/egzosn/pay/baidu/api/BaiduPayService.java @@ -3,11 +3,17 @@ package com.egzosn.pay.baidu.api; import java.io.UnsupportedEncodingException; import java.math.BigDecimal; import java.net.URLEncoder; -import java.security.*; +import java.security.KeyFactory; +import java.security.NoSuchAlgorithmException; +import java.security.PublicKey; +import java.security.Signature; import java.security.spec.InvalidKeySpecException; import java.security.spec.X509EncodedKeySpec; -import java.util.*; -import com.egzosn.pay.common.util.sign.encrypt.Base64; +import java.util.Comparator; +import java.util.Date; +import java.util.HashMap; +import java.util.Map; +import java.util.TreeMap; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONObject; @@ -30,7 +36,9 @@ import com.egzosn.pay.common.http.HttpConfigStorage; import com.egzosn.pay.common.http.UriVariables; import com.egzosn.pay.common.util.DateUtils; import com.egzosn.pay.common.util.Util; +import com.egzosn.pay.common.util.sign.SignTextUtils; import com.egzosn.pay.common.util.sign.SignUtils; +import com.egzosn.pay.common.util.sign.encrypt.Base64; import com.egzosn.pay.common.util.str.StringUtils; @@ -85,7 +93,8 @@ public class BaiduPayService extends BasePayService { try { boolean checkSign = this.checkReturnSign(params, payConfigStorage.getKeyPublic(), (String) params.get(RSA_SIGN)); return checkSign; - } catch (Exception e) { + } + catch (Exception e) { LOG.info("验签失败", e); } return false; @@ -100,7 +109,8 @@ public class BaiduPayService extends BasePayService { boolean verify = signature.verify(Base64.decode(rsaSign)); LOG.info("使用公钥进行验签: " + verify); return verify; - } catch (Exception e) { + } + catch (Exception e) { LOG.info("使用公钥进行验签出错, 返回false", e); } return false; @@ -111,9 +121,7 @@ public class BaiduPayService extends BasePayService { * 将公钥字符串进行Base64 decode之后,生成X509标准公钥 * * @param publicKey 公钥原始字符串 - * * @return X509标准公钥 - * * @throws InvalidKeySpecException * @throws NoSuchAlgorithmException */ @@ -131,9 +139,7 @@ public class BaiduPayService extends BasePayService { * 对输入参数进行key过滤排序和字符串拼接 * * @param params 待签名参数集合 - * * @return 待签名内容 - * * @throws UnsupportedEncodingException */ private String signContent(Map params) throws UnsupportedEncodingException { @@ -172,7 +178,6 @@ public class BaiduPayService extends BasePayService { * 非空、且非签名字段 * * @param key 待签名参数key值 - * * @return true | false */ private static boolean legalKey(String key) { @@ -520,9 +525,7 @@ public class BaiduPayService extends BasePayService { * @param accessToken 用户token * @return 对账单 */ - @Deprecated - @Override - public Map downloadbill(Date billDate, String accessToken) { + public Map downloadBill(Date billDate, String accessToken) { return downloadBill(billDate, new BaiduBillType(accessToken, BaiduTransactionType.DOWNLOAD_ORDER_BILL.name())); } @@ -609,7 +612,7 @@ public class BaiduPayService extends BasePayService { LOG.info("百度支付签名参数:" + JSON.toJSONString(result)); - String waitSignVal = SignUtils.parameterText(result, "&", false, ignoreKeys); + String waitSignVal = SignTextUtils.parameterText(result, "&", false, ignoreKeys); return SignUtils.RSA.createSign(waitSignVal, payConfigStorage.getKeyPrivate(), payConfigStorage.getInputCharset()); } } diff --git a/pay-java-baidu/src/main/java/com/egzosn/pay/baidu/package-info.java b/pay-java-baidu/src/main/java/com/egzosn/pay/baidu/package-info.java deleted file mode 100644 index e9370e2..0000000 --- a/pay-java-baidu/src/main/java/com/egzosn/pay/baidu/package-info.java +++ /dev/null @@ -1 +0,0 @@ -package com.egzosn.pay.baidu; \ No newline at end of file diff --git a/pay-java-common/src/main/java/com/egzosn/pay/common/api/BasePayService.java b/pay-java-common/src/main/java/com/egzosn/pay/common/api/BasePayService.java index 4e0a2aa..187cbed 100644 --- a/pay-java-common/src/main/java/com/egzosn/pay/common/api/BasePayService.java +++ b/pay-java-common/src/main/java/com/egzosn/pay/common/api/BasePayService.java @@ -11,11 +11,11 @@ import java.util.List; import java.util.Map; import java.util.TreeMap; - import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.alibaba.fastjson.JSON; +import com.egzosn.pay.common.bean.BillType; import com.egzosn.pay.common.bean.MethodType; import com.egzosn.pay.common.bean.Order; import com.egzosn.pay.common.bean.PayMessage; @@ -252,7 +252,7 @@ public abstract class BasePayService implements Pay */ @Override public T cancel(String tradeNo, String outTradeNo, Callback callback) { - return callback.perform(close(tradeNo, outTradeNo)); + return callback.perform(cancel(tradeNo, outTradeNo)); } /** @@ -264,7 +264,7 @@ public abstract class BasePayService implements Pay */ @Override public Map cancel(String tradeNo, String outTradeNo) { - return Collections.EMPTY_MAP; + return Collections.emptyMap(); } @@ -297,19 +297,16 @@ public abstract class BasePayService implements Pay } /** - * 目前只支持日账单 + * 下载对账单 * - * @param billDate 账单时间:具体请查看对应支付平台 - * @param billType 账单类型,具体请查看对应支付平台 - * @param callback 处理器 - * @param 返回类型 + * @param billDate 账单时间:日账单格式为yyyy-MM-dd,月账单格式为yyyy-MM。 + * @param billType 账单类型 内部自动转化 {@link BillType} * @return 返回支付方下载对账单的结果 */ @Override - public T downloadbill(Date billDate, String billType, Callback callback) { - return callback.perform(downloadbill(billDate, billType)); + public Map downloadBill(Date billDate, String billType){ + return Collections.emptyMap(); } - /** * 转账 * diff --git a/pay-java-common/src/main/java/com/egzosn/pay/common/api/PayService.java b/pay-java-common/src/main/java/com/egzosn/pay/common/api/PayService.java index fa8df25..bf5256f 100644 --- a/pay-java-common/src/main/java/com/egzosn/pay/common/api/PayService.java +++ b/pay-java-common/src/main/java/com/egzosn/pay/common/api/PayService.java @@ -276,11 +276,10 @@ public interface PayService { * 下载对账单 * * @param billDate 账单时间:日账单格式为yyyy-MM-dd,月账单格式为yyyy-MM。 - * @param billType 账单类型 + * @param billType 账单类型 内部自动转化 {@link BillType} * @return 返回支付方下载对账单的结果 */ - @Deprecated - Map downloadbill(Date billDate, String billType); + Map downloadBill(Date billDate, String billType); /** * 下载对账单 @@ -291,16 +290,6 @@ public interface PayService { */ Map downloadBill(Date billDate, BillType billType); - /** - * 下载对账单 - * - * @param billDate 账单时间:具体请查看对应支付平台 - * @param billType 账单类型,具体请查看对应支付平台 - * @param callback 处理器 - * @param 返回类型 - * @return 返回支付方下载对账单的结果 - */ - T downloadbill(Date billDate, String billType, Callback callback); /** diff --git a/pay-java-common/src/main/java/com/egzosn/pay/common/exception/PayErrorException.java b/pay-java-common/src/main/java/com/egzosn/pay/common/exception/PayErrorException.java index 3b077b4..6362978 100644 --- a/pay-java-common/src/main/java/com/egzosn/pay/common/exception/PayErrorException.java +++ b/pay-java-common/src/main/java/com/egzosn/pay/common/exception/PayErrorException.java @@ -18,6 +18,11 @@ public class PayErrorException extends RuntimeException { this.error = error; } + public PayErrorException(PayError error, Throwable throwable) { + super(error.getString(), throwable); + this.error = error; + } + public PayError getPayError() { return error; 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 0789efb..d6aa4d0 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 @@ -460,11 +460,11 @@ public class PayController { * @param order 订单的请求体 * @return 返回支付方下载对账单的结果 */ - @RequestMapping("downloadbill") - public Object downloadbill(QueryOrder order) { + @RequestMapping("downloadBill") + public Object downloadBill(QueryOrder order) { PayResponse payResponse = service.getPayResponse(order.getPayId()); - return payResponse.getService().downloadbill(order.getBillDate(), order.getBillType()); + return payResponse.getService().downloadBill(order.getBillDate(), order.getBillType()); } diff --git a/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/UnionPayController.java b/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/UnionPayController.java index 0b9493f..5cb3135 100644 --- a/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/UnionPayController.java +++ b/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/UnionPayController.java @@ -8,6 +8,7 @@ import com.egzosn.pay.common.bean.MethodType; import com.egzosn.pay.common.bean.PayOrder; import com.egzosn.pay.common.bean.RefundOrder; import com.egzosn.pay.common.http.HttpConfigStorage; +import com.egzosn.pay.common.util.sign.SignTextUtils; import com.egzosn.pay.common.util.sign.SignUtils; import com.egzosn.pay.demo.request.QueryOrder; import com.egzosn.pay.union.api.UnionPayConfigStorage; @@ -130,7 +131,7 @@ public class UnionPayController { public Map app() { Map data = new HashMap<>(); data.put("code", 0); - PayOrder order = new PayOrder("订单title", "摘要", BigDecimal.valueOf(0.01), SignUtils.randomStr()); + PayOrder order = new PayOrder("订单title", "摘要", BigDecimal.valueOf(0.01), SignTextUtils.randomStr()); //App支付 order.setTransactionType(UnionTransactionType.APP); @@ -178,7 +179,7 @@ public class UnionPayController { public Map microPay(BigDecimal price, String authCode) { //获取对应的支付账户操作工具(可根据账户id) //条码付 - PayOrder order = new PayOrder("egan order", "egan order", null == price ? BigDecimal.valueOf(0.01) : price, SignUtils.randomStr(), UnionTransactionType.CONSUME); + PayOrder order = new PayOrder("egan order", "egan order", null == price ? BigDecimal.valueOf(0.01) : price, SignTextUtils.randomStr(), UnionTransactionType.CONSUME); //设置授权码,条码等 order.setAuthCode(authCode); //支付结果 @@ -275,9 +276,9 @@ public class UnionPayController { * @param order 订单的请求体 * @return 返回支付方下载对账单的结果 */ - @RequestMapping("downloadbill") - public Object downloadbill(QueryOrder order) { - return service.downloadbill(order.getBillDate(), order.getBillType()); + @RequestMapping("downloadBill") + public Object downloadBill(QueryOrder order) { + return service.downloadBill(order.getBillDate(), order.getBillType()); } 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 b659dd7..067a180 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 @@ -47,7 +47,7 @@ public class ApyAccountRepository { ApyAccount apyAccount2 = new ApyAccount(); apyAccount2.setPayId(2); apyAccount2.setPartner("1469188802"); - apyAccount2.setAppId("wx3344f4aed352deae"); + apyAccount2.setAppId("wx3344f4aed352de09"); // TODO 2017/2/9 16:20 author: egan sign_type只有单一key时public_key与private_key相等,比如sign_type=MD5的情况 apyAccount2.setPublicKey("991ded080***************f7fc61095"); apyAccount2.setPrivateKey("991ded080***************f7fc61095"); diff --git a/pay-java-fuiou/src/main/java/com/egzosn/pay/fuiou/api/FuiouPayService.java b/pay-java-fuiou/src/main/java/com/egzosn/pay/fuiou/api/FuiouPayService.java index 8bae0c2..bb16674 100644 --- a/pay-java-fuiou/src/main/java/com/egzosn/pay/fuiou/api/FuiouPayService.java +++ b/pay-java-fuiou/src/main/java/com/egzosn/pay/fuiou/api/FuiouPayService.java @@ -22,6 +22,7 @@ import com.egzosn.pay.common.http.HttpConfigStorage; import com.egzosn.pay.common.http.UriVariables; import com.egzosn.pay.common.util.DateUtils; import com.egzosn.pay.common.util.Util; +import com.egzosn.pay.common.util.sign.SignTextUtils; import com.egzosn.pay.common.util.sign.SignUtils; import com.egzosn.pay.common.util.str.StringUtils; import com.egzosn.pay.fuiou.bean.FuiouRefundResult; @@ -141,7 +142,7 @@ public class FuiouPayService extends BasePayService { */ public boolean signVerify(Map params, String responseSign) { - String sign = createSign(SignUtils.parameters2Md5Str(params, "|"), payConfigStorage.getInputCharset()); + String sign = createSign(SignTextUtils.parameters2Md5Str(params, "|"), payConfigStorage.getInputCharset()); return responseSign.equals(sign); } @@ -156,7 +157,7 @@ public class FuiouPayService extends BasePayService { LinkedHashMap params = new LinkedHashMap(3); params.put("mchnt_cd", payConfigStorage.getPid()); params.put("order_id", orderId); - params.put("md5", createSign(SignUtils.parameters2Md5Str(params, "|"), payConfigStorage.getInputCharset())); + params.put("md5", createSign(SignTextUtils.parameters2Md5Str(params, "|"), payConfigStorage.getInputCharset())); JSONObject resultJson = getHttpRequestTemplate().postForObject(getReqUrl() + URL_FuiouSmpAQueryGate + "?" + UriVariables.getMapToParameters(params), null, JSONObject.class); if (null == resultJson) { return false; @@ -178,7 +179,7 @@ public class FuiouPayService extends BasePayService { } Map parameters = getOrderInfo(order); - String sign = createSign(SignUtils.parameters2Md5Str(parameters, "|"), payConfigStorage.getInputCharset()); + String sign = createSign(SignTextUtils.parameters2Md5Str(parameters, "|"), payConfigStorage.getInputCharset()); parameters.put("md5", sign); return parameters; } @@ -381,7 +382,7 @@ public class FuiouPayService extends BasePayService { LinkedHashMap params = new LinkedHashMap<>(); params.put("mchnt_cd", payConfigStorage.getPid()); params.put("order_id", outTradeNo); - params.put("md5", createSign(SignUtils.parameters2Md5Str(params, "|"), payConfigStorage.getInputCharset())); + params.put("md5", createSign(SignTextUtils.parameters2Md5Str(params, "|"), payConfigStorage.getInputCharset())); JSONObject resultJson = getHttpRequestTemplate().postForObject(getReqUrl() + URL_FuiouSmpAQueryGate + "?" + UriVariables.getMapToParameters(params), null, JSONObject.class); return resultJson; } @@ -420,7 +421,7 @@ public class FuiouPayService extends BasePayService { //备注 params.put("rem", ""); params.putAll(refundOrder.getAttrs()); - params.put("md5", createSign(SignUtils.parameters2Md5Str(params, "|"), payConfigStorage.getInputCharset())); + params.put("md5", createSign(SignTextUtils.parameters2Md5Str(params, "|"), payConfigStorage.getInputCharset())); JSONObject resultJson = getHttpRequestTemplate().postForObject(getReqUrl() + URL_FuiouSmpRefundGate, params, JSONObject.class); return FuiouRefundResult.create(resultJson); } @@ -438,19 +439,6 @@ public class FuiouPayService extends BasePayService { } - /** - * 下载对账单 - * - * @param billDate 账单时间:日账单格式为yyyy-MM-dd,月账单格式为yyyy-MM。 - * @param billType 账单类型,商户通过接口或商户经开放平台授权后其所属服务商通过接口可以获取以下账单类型:trade、signcustomer;trade指商户基于支付宝交易收单的业务账单;signcustomer是指基于商户支付宝余额收入及支出等资金变动的帐务账单; - * @return 空 - */ - @Deprecated - @Override - public Map downloadbill(Date billDate, String billType) { - return Collections.emptyMap(); - } - /** * 下载对账单 * diff --git a/pay-java-payoneer/src/main/java/com/egzosn/pay/payoneer/api/PayoneerPayService.java b/pay-java-payoneer/src/main/java/com/egzosn/pay/payoneer/api/PayoneerPayService.java index e706314..5561b40 100644 --- a/pay-java-payoneer/src/main/java/com/egzosn/pay/payoneer/api/PayoneerPayService.java +++ b/pay-java-payoneer/src/main/java/com/egzosn/pay/payoneer/api/PayoneerPayService.java @@ -374,18 +374,7 @@ public class PayoneerPayService extends BasePayService im return Collections.emptyMap(); } - /** - * 下载对账单 - * - * @param billDate 账单时间:日账单格式为yyyy-MM-dd,月账单格式为yyyy-MM。 - * @param billType 账单类型,商户通过接口或商户经开放平台授权后其所属服务商通过接口可以获取以下账单类型:trade、signcustomer;trade指商户基于支付宝交易收单的业务账单;signcustomer是指基于商户支付宝余额收入及支出等资金变动的帐务账单; - * @return 返回支付方下载对账单的结果 - */ - @Deprecated - @Override - public Map downloadbill(Date billDate, String billType) { - return Collections.emptyMap(); - } + /** * 下载对账单 * diff --git a/pay-java-paypal/src/main/java/com/egzosn/pay/paypal/api/PayPalPayService.java b/pay-java-paypal/src/main/java/com/egzosn/pay/paypal/api/PayPalPayService.java index ae0e236..9da31e2 100644 --- a/pay-java-paypal/src/main/java/com/egzosn/pay/paypal/api/PayPalPayService.java +++ b/pay-java-paypal/src/main/java/com/egzosn/pay/paypal/api/PayPalPayService.java @@ -348,11 +348,6 @@ public class PayPalPayService extends BasePayService { return resp; } - @Deprecated - @Override - public Map downloadbill(Date billDate, String billType) { - return Collections.emptyMap(); - } @Override diff --git a/pay-java-paypal/src/main/java/com/egzosn/pay/paypal/v2/api/PayPalPayService.java b/pay-java-paypal/src/main/java/com/egzosn/pay/paypal/v2/api/PayPalPayService.java index 3fd521f..898b35c 100644 --- a/pay-java-paypal/src/main/java/com/egzosn/pay/paypal/v2/api/PayPalPayService.java +++ b/pay-java-paypal/src/main/java/com/egzosn/pay/paypal/v2/api/PayPalPayService.java @@ -439,7 +439,6 @@ public class PayPalPayService extends BasePayService implem * } * * - * */ @Override public Map getCapture(String captureId) { @@ -491,13 +490,6 @@ public class PayPalPayService extends BasePayService implem return resp; } - @Deprecated - @Override - public Map downloadbill(Date billDate, String billType) { - return Collections.emptyMap(); - } - - public Map downloadBill(Date billDate, BillType billType) { return Collections.emptyMap(); } diff --git a/pay-java-union/src/main/java/com/egzosn/pay/union/api/UnionPayService.java b/pay-java-union/src/main/java/com/egzosn/pay/union/api/UnionPayService.java index 6fde8a7..1c10645 100644 --- a/pay-java-union/src/main/java/com/egzosn/pay/union/api/UnionPayService.java +++ b/pay-java-union/src/main/java/com/egzosn/pay/union/api/UnionPayService.java @@ -41,6 +41,7 @@ import com.egzosn.pay.common.http.UriVariables; import com.egzosn.pay.common.util.DateUtils; import com.egzosn.pay.common.util.Util; import com.egzosn.pay.common.util.sign.CertDescriptor; +import com.egzosn.pay.common.util.sign.SignTextUtils; import com.egzosn.pay.common.util.sign.SignUtils; import com.egzosn.pay.common.util.sign.encrypt.RSA; import com.egzosn.pay.common.util.sign.encrypt.RSA2; @@ -80,7 +81,7 @@ public class UnionPayService extends BasePayService { /** * 证书解释器 */ - private CertDescriptor certDescriptor; + private volatile CertDescriptor certDescriptor; /** * 构造函数 @@ -103,12 +104,13 @@ public class UnionPayService extends BasePayService { */ @Override public UnionPayService setPayConfigStorage(UnionPayConfigStorage payConfigStorage) { - super.setPayConfigStorage(payConfigStorage); - if (!payConfigStorage.isCertSign() || null != certDescriptor) { + this.payConfigStorage = payConfigStorage; + if (!payConfigStorage.isCertSign()) { return this; } - - certDescriptor = new CertDescriptor(); + if (null == certDescriptor) { + certDescriptor = new CertDescriptor(); + } try { certDescriptor.initPrivateSignCert(payConfigStorage.getKeyPrivateCertInputStream(), payConfigStorage.getKeyPrivateCertPwd(), "PKCS12"); certDescriptor.initPublicCert(payConfigStorage.getAcpMiddleCertInputStream()); @@ -149,6 +151,7 @@ public class UnionPayService extends BasePayService { public String getBackTransUrl() { return String.format(BACK_TRANS_URL, getReqUrl()); } + public String getAppTransUrl() { return String.format(APP_TRANS_URL, getReqUrl()); } @@ -217,7 +220,7 @@ public class UnionPayService extends BasePayService { public boolean signVerify(Map params, String sign) { SignUtils signUtils = SignUtils.valueOf(payConfigStorage.getSignType()); - String data = SignUtils.parameterText(params, "&", "signature"); + String data = SignTextUtils.parameterText(params, "&", "signature"); switch (signUtils) { case RSA: data = SignUtils.SHA1.createSign(data, "", payConfigStorage.getInputCharset()); @@ -322,20 +325,20 @@ public class UnionPayService extends BasePayService { case RSA: parameters.put(SDKConstants.param_signMethod, SDKConstants.SIGNMETHOD_RSA); parameters.put(SDKConstants.param_certId, certDescriptor.getSignCertId()); - signStr = SignUtils.SHA1.createSign(SignUtils.parameterText(parameters, "&", "signature"), "", payConfigStorage.getInputCharset()); + signStr = SignUtils.SHA1.createSign(SignTextUtils.parameterText(parameters, "&", "signature"), "", payConfigStorage.getInputCharset()); parameters.put(SDKConstants.param_signature, RSA.sign(signStr, certDescriptor.getSignCertPrivateKey(payConfigStorage.getKeyPrivateCertPwd()), payConfigStorage.getInputCharset())); break; case RSA2: parameters.put(SDKConstants.param_signMethod, SDKConstants.SIGNMETHOD_RSA); parameters.put(SDKConstants.param_certId, certDescriptor.getSignCertId()); - signStr = SignUtils.SHA256.createSign(SignUtils.parameterText(parameters, "&", "signature"), "", payConfigStorage.getInputCharset()); + signStr = SignUtils.SHA256.createSign(SignTextUtils.parameterText(parameters, "&", "signature"), "", payConfigStorage.getInputCharset()); parameters.put(SDKConstants.param_signature, RSA2.sign(signStr, certDescriptor.getSignCertPrivateKey(payConfigStorage.getKeyPrivateCertPwd()), payConfigStorage.getInputCharset())); break; case SHA1: case SHA256: case SM3: String key = payConfigStorage.getKeyPrivate(); - signStr = SignUtils.parameterText(parameters, "&", "signature"); + signStr = SignTextUtils.parameterText(parameters, "&", "signature"); key = signUtils.createSign(key, "", payConfigStorage.getInputCharset()) + "&"; parameters.put(SDKConstants.param_signature, signUtils.createSign(signStr, key, payConfigStorage.getInputCharset())); break; @@ -401,7 +404,7 @@ public class UnionPayService extends BasePayService { * @return 返回支付结果 */ - public JSONObject postOrder(PayOrder order, String url) { + public JSONObject postOrder(PayOrder order, String url) { Map params = orderInfo(order); String responseStr = getHttpRequestTemplate().postForObject(url, params, String.class); JSONObject response = UriVariables.getParametersToMap(responseStr); @@ -666,10 +669,8 @@ public class UnionPayService extends BasePayService { * @param fileType 文件类型 文件类型,一般商户填写00即可 * @return 返回fileContent 请自行将数据落地 */ - @Deprecated - @Override - public Map downloadbill(Date billDate, String fileType) { - return downloadBill(billDate, new UnionPayBillType(fileType)); + public Map downloadBill(Date billDate, String fileType) { + return downloadBill(billDate, new UnionPayBillType(fileType)); } /** diff --git a/pay-java-union/src/test/java/PayTest.java b/pay-java-union/src/test/java/PayTest.java index 659ea4c..93bda12 100644 --- a/pay-java-union/src/test/java/PayTest.java +++ b/pay-java-union/src/test/java/PayTest.java @@ -98,7 +98,7 @@ public class PayTest { /*-----------文件传输类接口:后台获取对账文件交易,只有同步应答 ------------------------------*/ - Map fileConten = service.downloadbill(new Date(), "文件类型,一般商户填写00即可"); /*-----------退货交易:后台资金类交易,有同步应答和后台通知应答------------------------------*/ + Map fileConten = service.downloadBill(new Date(), "文件类型,一般商户填写00即可"); /*-----------退货交易:后台资金类交易,有同步应答和后台通知应答------------------------------*/ /*-----------回调处理-------------------*/ 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 347e657..5139f48 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 @@ -26,6 +26,7 @@ import com.egzosn.pay.common.bean.result.PayError; import com.egzosn.pay.common.exception.PayErrorException; import com.egzosn.pay.common.http.HttpConfigStorage; import com.egzosn.pay.common.util.Util; +import com.egzosn.pay.common.util.sign.SignTextUtils; import com.egzosn.pay.common.util.sign.SignUtils; import com.egzosn.pay.common.util.str.StringUtils; import com.egzosn.pay.wx.youdian.bean.WxYoudianPayMessage; @@ -80,7 +81,7 @@ public class WxYouDianPayService extends BasePayService builder = new TreeMap<>(); builder.put("return_code", code.toUpperCase()); builder.put("return_msg", message); - builder.put("nonce_str", SignUtils.randomStr()); + builder.put("nonce_str", SignTextUtils.randomStr()); String sgin = SignUtils.valueOf(payConfigStorage.getSignType()).sign(builder, "&key=" + payConfigStorage.getKeyPrivate(), payConfigStorage.getInputCharset()); return PayOutMessage.TEXT().content("{\"return_code\":\"" + builder.get("return_code") + "\",\"return_msg\":\"" + builder.get("return_msg") + "\",\"nonce_str\":\"" + builder.get("nonce_str") + "\",\"sign\":\"" + sgin + "\"}").build(); } @@ -386,7 +387,7 @@ public class WxYouDianPayService extends BasePayService query(String tradeNo, String outTradeNo) { - String apbNonce = SignUtils.randomStr(); + String apbNonce = SignTextUtils.randomStr(); TreeMap data = new TreeMap<>(); data.put("access_token", payConfigStorage.getAccessToken()); @@ -396,8 +397,8 @@ public class WxYouDianPayService extends BasePayService data = new TreeMap<>(); data.put("access_token", payConfigStorage.getAccessToken()); @@ -430,8 +431,8 @@ public class WxYouDianPayService extends BasePayService downloadbill(Date billDate, String billType) { - return Collections.emptyMap(); - } - - @Override public Map downloadBill(Date billDate, BillType billType) { diff --git a/pay-java-wx/README.md b/pay-java-wx/README.md index 6012fe6..2689e11 100644 --- a/pay-java-wx/README.md +++ b/pay-java-wx/README.md @@ -217,7 +217,7 @@ #### 交易关闭接口 ```java - Map result = service..close("微信单号", "我方系统单号"); + Map result = service.close("微信单号", "我方系统单号"); ``` 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 fb4c039..2042622 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 @@ -58,6 +58,7 @@ import com.egzosn.pay.common.http.UriVariables; import com.egzosn.pay.common.util.DateUtils; import com.egzosn.pay.common.util.Util; import com.egzosn.pay.common.util.XML; +import com.egzosn.pay.common.util.sign.SignTextUtils; import com.egzosn.pay.common.util.sign.SignUtils; import com.egzosn.pay.common.util.sign.encrypt.RSA2; import com.egzosn.pay.common.util.str.StringUtils; @@ -171,7 +172,7 @@ public class WxPayService extends BasePayService implements if (isTest) { keyPrivate = getKeyPrivate(); } - String content = SignUtils.parameterText(params, "&", SIGN, "appId") + "&key=" + (signUtils == SignUtils.MD5 ? "" : keyPrivate); + String content = SignTextUtils.parameterText(params, "&", SIGN, "appId") + "&key=" + (signUtils == SignUtils.MD5 ? "" : keyPrivate); return signUtils.verify(content, sign, keyPrivate, payConfigStorage.getInputCharset()); } @@ -188,7 +189,7 @@ public class WxPayService extends BasePayService implements //判断如果是服务商模式信息则加入 setParameters(parameters, "sub_mch_id", payConfigStorage.getSubMchId()); setParameters(parameters, "sub_appid", payConfigStorage.getSubAppId()); - parameters.put(NONCE_STR, SignUtils.randomStr()); + parameters.put(NONCE_STR, SignTextUtils.randomStr()); return parameters; @@ -290,7 +291,7 @@ public class WxPayService extends BasePayService implements params.put("noncestr", result.get(NONCE_STR)); params.put("package", "Sign=WXPay"); } - String paySign = createSign(SignUtils.parameterText(params), payConfigStorage.getInputCharset()); + String paySign = createSign(SignTextUtils.parameterText(params), payConfigStorage.getInputCharset()); params.put(WxTransactionType.JSAPI.equals(order.getTransactionType()) ? "paySign" : SIGN, paySign); return params; } @@ -311,7 +312,7 @@ public class WxPayService extends BasePayService implements signTypeStr = SignUtils.HMACSHA256.getName(); } parameters.put("sign_type", signTypeStr); - String sign = createSign(SignUtils.parameterText(parameters, "&", SIGN, "appId"), payConfigStorage.getInputCharset()); + String sign = createSign(SignTextUtils.parameterText(parameters, "&", SIGN, "appId"), payConfigStorage.getInputCharset()); parameters.put(SIGN, sign); return parameters; } @@ -328,9 +329,9 @@ public class WxPayService extends BasePayService implements } SortedMap parameters = new TreeMap(); parameters.put(MCH_ID, payConfigStorage.getMchId()); - parameters.put(NONCE_STR, SignUtils.randomStr()); + parameters.put(NONCE_STR, SignTextUtils.randomStr()); - String sign = createSign(SignUtils.parameterText(parameters, "&", SIGN, "appId"), payConfigStorage.getInputCharset(), false); + String sign = createSign(SignTextUtils.parameterText(parameters, "&", SIGN, "appId"), payConfigStorage.getInputCharset(), false); parameters.put(SIGN, sign); HttpStringEntity entity = new HttpStringEntity(XML.getMap2Xml(parameters), ClientHttpRequest.APPLICATION_XML_UTF_8); @@ -578,22 +579,8 @@ public class WxPayService extends BasePayService implements * RECHARGE_REFUND,返回当日充值退款订单 * @return 返回支付方下载对账单的结果 */ - @Deprecated - @Override - public Map downloadbill(Date billDate, String billType) { - Map parameters = getDownloadBillParam(billDate, billType, false); - //设置签名 - setSign(parameters); - String respStr = requestTemplate.postForObject(getReqUrl(WxTransactionType.DOWNLOADBILL), XML.getMap2Xml(parameters), String.class); - if (respStr.indexOf("<") == 0) { - return XML.toJSONObject(respStr); - } - - Map ret = new HashMap(3); - ret.put(RETURN_CODE, SUCCESS); - ret.put(RETURN_MSG_CODE, "ok"); - ret.put("data", respStr); - return ret; + public Map downloadBill(Date billDate, String billType) { + return downloadBill(billDate, WxPayBillType.valueOf(billType)); } /** @@ -784,12 +771,12 @@ public class WxPayService extends BasePayService implements * @param order 转账订单 *

      *
-     *                                        注意事项:
-     *                                        ◆ 当返回错误码为“SYSTEMERROR”时,请不要更换商户订单号,一定要使用原商户订单号重试,否则可能造成重复支付等资金风险。
-     *                                        ◆ XML具有可扩展性,因此返回参数可能会有新增,而且顺序可能不完全遵循此文档规范,如果在解析回包的时候发生错误,请商户务必不要换单重试,请商户联系客服确认付款情况。如果有新回包字段,会更新到此API文档中。
-     *                                        ◆ 因为错误代码字段err_code的值后续可能会增加,所以商户如果遇到回包返回新的错误码,请商户务必不要换单重试,请商户联系客服确认付款情况。如果有新的错误码,会更新到此API文档中。
-     *                                        ◆ 错误代码描述字段err_code_des只供人工定位问题时做参考,系统实现时请不要依赖这个字段来做自动化处理。
-     *                                        
+ * 注意事项: + * ◆ 当返回错误码为“SYSTEMERROR”时,请不要更换商户订单号,一定要使用原商户订单号重试,否则可能造成重复支付等资金风险。 + * ◆ XML具有可扩展性,因此返回参数可能会有新增,而且顺序可能不完全遵循此文档规范,如果在解析回包的时候发生错误,请商户务必不要换单重试,请商户联系客服确认付款情况。如果有新回包字段,会更新到此API文档中。 + * ◆ 因为错误代码字段err_code的值后续可能会增加,所以商户如果遇到回包返回新的错误码,请商户务必不要换单重试,请商户联系客服确认付款情况。如果有新的错误码,会更新到此API文档中。 + * ◆ 错误代码描述字段err_code_des只供人工定位问题时做参考,系统实现时请不要依赖这个字段来做自动化处理。 + * * @return 对应的转账结果 */ @Override @@ -802,7 +789,7 @@ public class WxPayService extends BasePayService implements if (!StringUtils.isEmpty(order.getRemark())) { parameters.put("desc", order.getRemark()); } - parameters.put(NONCE_STR, SignUtils.randomStr()); + parameters.put(NONCE_STR, SignTextUtils.randomStr()); if (null != order.getTransferType() && TRANSFERS == order.getTransferType()) { transfers(parameters, order); parameters.put("mchid", payConfigStorage.getPid()); @@ -812,7 +799,7 @@ public class WxPayService extends BasePayService implements order.setTransferType(WxTransferType.PAY_BANK); payBank(parameters, order); } - parameters.put(SIGN, createSign(SignUtils.parameterText(parameters, "&", SIGN), payConfigStorage.getInputCharset())); + parameters.put(SIGN, createSign(SignTextUtils.parameterText(parameters, "&", SIGN), payConfigStorage.getInputCharset())); return getHttpRequestTemplate().postForObject(getReqUrl(order.getTransferType()), XML.getMap2Xml(parameters), JSONObject.class); } @@ -875,17 +862,17 @@ public class WxPayService extends BasePayService implements Map parameters = new TreeMap(); parameters.put(MCH_ID, payConfigStorage.getPid()); parameters.put("partner_trade_no", outNo); - parameters.put(NONCE_STR, SignUtils.randomStr()); + parameters.put(NONCE_STR, SignTextUtils.randomStr()); if (StringUtils.isEmpty(wxTransferType)) { throw new PayErrorException(new WxPayError(FAILURE, "微信转账类型 #transferQuery(String outNo, String wxTransferType) 必填,详情com.egzosn.pay.wx.bean.WxTransferType")); } //如果类型为余额方式 if (TRANSFERS.getType().equals(wxTransferType) || GETTRANSFERINFO.getType().equals(wxTransferType)) { - parameters.put(APPID, payConfigStorage.getAppId()); - parameters.put(SIGN, createSign(SignUtils.parameterText(parameters, "&", SIGN), payConfigStorage.getInputCharset())); + parameters.put(APPID, payConfigStorage.getAppId()); + parameters.put(SIGN, createSign(SignTextUtils.parameterText(parameters, "&", SIGN), payConfigStorage.getInputCharset())); return getHttpRequestTemplate().postForObject(getReqUrl(GETTRANSFERINFO), XML.getMap2Xml(parameters), JSONObject.class); } - parameters.put(SIGN, createSign(SignUtils.parameterText(parameters, "&", SIGN), payConfigStorage.getInputCharset())); + parameters.put(SIGN, createSign(SignTextUtils.parameterText(parameters, "&", SIGN), payConfigStorage.getInputCharset())); //默认查询银行卡的记录 return getHttpRequestTemplate().postForObject(getReqUrl(QUERY_BANK), XML.getMap2Xml(parameters), JSONObject.class); } @@ -932,7 +919,7 @@ public class WxPayService extends BasePayService implements parameters.put("notify_way", "MINI_PROGRAM_JSAPI"); } - parameters.put(SIGN, createSign(SignUtils.parameterText(parameters, "&", SIGN), payConfigStorage.getInputCharset())); + parameters.put(SIGN, createSign(SignTextUtils.parameterText(parameters, "&", SIGN), payConfigStorage.getInputCharset())); final JSONObject resp = requestTemplate.postForObject(getReqUrl(redpackOrder.getTransferType()), XML.getMap2Xml(parameters), JSONObject.class); if (WxSendredpackType.SENDMINIPROGRAMHB != transferType || FAIL.equals(resp.getString(RESULT_CODE))) { return resp; @@ -942,7 +929,7 @@ public class WxPayService extends BasePayService implements params.put("timeStamp", System.currentTimeMillis() / 1000 + ""); params.put("nonceStr", parameters.get(NONCE_STR)); params.put("package", UriVariables.urlEncoder(resp.getString("package"))); - String paySign = createSign(SignUtils.parameterText(params), payConfigStorage.getInputCharset()); + String paySign = createSign(SignTextUtils.parameterText(params), payConfigStorage.getInputCharset()); params.put("signType", payConfigStorage.getSignType()); params.put("paySign", paySign); return params; @@ -963,7 +950,7 @@ public class WxPayService extends BasePayService implements Map parameters = this.getPublicParameters(); parameters.put("mch_billno", mchBillno); parameters.put("bill_type", "MCHT"); - parameters.put(SIGN, createSign(SignUtils.parameterText(parameters, "&", SIGN), payConfigStorage.getInputCharset())); + parameters.put(SIGN, createSign(SignTextUtils.parameterText(parameters, "&", SIGN), payConfigStorage.getInputCharset())); return requestTemplate.postForObject(getReqUrl(WxSendredpackType.GETHBINFO), XML.getMap2Xml(parameters), JSONObject.class); } @@ -974,7 +961,7 @@ public class WxPayService extends BasePayService implements * @param parameters 接收参数 */ private void redPackParam(RedpackOrder redpackOrder, Map parameters) { - parameters.put(NONCE_STR, SignUtils.randomStr()); + parameters.put(NONCE_STR, SignTextUtils.randomStr()); parameters.put(MCH_ID, payConfigStorage.getPid()); parameters.put("wxappid", payConfigStorage.getAppId()); parameters.put("send_name", redpackOrder.getSendName()); diff --git a/pay-java-yiji/src/main/java/com/egzosn/pay/yiji/api/YiJiPayService.java b/pay-java-yiji/src/main/java/com/egzosn/pay/yiji/api/YiJiPayService.java index ba19707..d530673 100644 --- a/pay-java-yiji/src/main/java/com/egzosn/pay/yiji/api/YiJiPayService.java +++ b/pay-java-yiji/src/main/java/com/egzosn/pay/yiji/api/YiJiPayService.java @@ -23,6 +23,7 @@ import com.egzosn.pay.common.bean.TransferOrder; import com.egzosn.pay.common.http.HttpConfigStorage; import com.egzosn.pay.common.util.DateUtils; import com.egzosn.pay.common.util.Util; +import com.egzosn.pay.common.util.sign.SignTextUtils; import com.egzosn.pay.common.util.sign.SignUtils; import com.egzosn.pay.common.util.str.StringUtils; import com.egzosn.pay.yiji.bean.YiJiTransactionType; @@ -120,7 +121,7 @@ public class YiJiPayService extends BasePayService { */ private Map setSign(Map parameters) { parameters.put("signType", payConfigStorage.getSignType()); - String sign = createSign(SignUtils.parameterText(parameters, "&", SIGN), payConfigStorage.getInputCharset()); + String sign = createSign(SignTextUtils.parameterText(parameters, "&", SIGN), payConfigStorage.getInputCharset()); parameters.put(SIGN, sign); return parameters; @@ -363,22 +364,8 @@ public class YiJiPayService extends BasePayService { } - /** - * 目前只支持日账单 - * - * @param billDate 账单类型,商户通过接口或商户经开放平台授权后其所属服务商通过接口可以获取以下账单类型:trade、signcustomer;trade指商户基于易极付交易收单的业务账单;signcustomer是指基于商户易极付余额收入及支出等资金变动的帐务账单; - * @param billType 账单时间:日账单格式为yyyy-MM-dd,月账单格式为yyyy-MM。 - * @return 返回支付方下载对账单的结果 - */ - @Deprecated - @Override - public Map downloadbill(Date billDate, String billType) { - - return Collections.emptyMap(); - } /** - * 目前只支持日账单 * * @param billDate 账单类型,商户通过接口或商户经开放平台授权后其所属服务商通过接口可以获取以下账单类型:trade、signcustomer;trade指商户基于易极付交易收单的业务账单;signcustomer是指基于商户易极付余额收入及支出等资金变动的帐务账单; * @param billType 账单时间:日账单格式为yyyy-MM-dd,月账单格式为yyyy-MM。 -- Gitee From 4bc739d88453659ba2c5e119791c1e544cb623f6 Mon Sep 17 00:00:00 2001 From: egan Date: Sun, 1 Aug 2021 23:33:51 +0800 Subject: [PATCH 208/299] =?UTF-8?q?=E8=AF=B7=E6=B1=82=E5=B7=A5=E5=85=B7?= =?UTF-8?q?=E6=96=B0=E5=A2=9E=E5=93=8D=E5=BA=94=E5=AE=9E=E4=BD=93?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../pay/common/http/HttpRequestTemplate.java | 243 ++++++++++++------ .../pay/common/http/HttpStringEntity.java | 36 +-- .../pay/common/http/ResponseEntity.java | 33 +++ .../egzosn/pay/common/http/UriVariables.java | 27 ++ 4 files changed, 244 insertions(+), 95 deletions(-) create mode 100644 pay-java-common/src/main/java/com/egzosn/pay/common/http/ResponseEntity.java diff --git a/pay-java-common/src/main/java/com/egzosn/pay/common/http/HttpRequestTemplate.java b/pay-java-common/src/main/java/com/egzosn/pay/common/http/HttpRequestTemplate.java index b7b1ed3..218a26e 100644 --- a/pay-java-common/src/main/java/com/egzosn/pay/common/http/HttpRequestTemplate.java +++ b/pay-java-common/src/main/java/com/egzosn/pay/common/http/HttpRequestTemplate.java @@ -1,11 +1,17 @@ package com.egzosn.pay.common.http; -import com.egzosn.pay.common.bean.MethodType; -import com.egzosn.pay.common.bean.result.PayException; -import com.egzosn.pay.common.exception.PayErrorException; -import com.egzosn.pay.common.util.str.StringUtils; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; +import java.io.IOException; +import java.io.InputStream; +import java.net.URI; +import java.security.GeneralSecurityException; +import java.security.KeyStore; +import java.security.NoSuchAlgorithmException; +import java.util.Map; + +import javax.net.ssl.KeyManagerFactory; +import javax.net.ssl.SSLContext; + +import org.apache.http.Header; import org.apache.http.HttpHost; import org.apache.http.auth.AuthScope; import org.apache.http.auth.UsernamePasswordCredentials; @@ -23,24 +29,22 @@ import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClients; import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; import org.apache.http.ssl.SSLContexts; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; -import javax.net.ssl.KeyManagerFactory; -import javax.net.ssl.SSLContext; -import java.io.IOException; -import java.io.InputStream; -import java.net.URI; -import java.security.GeneralSecurityException; -import java.security.KeyStore; -import java.security.NoSuchAlgorithmException; -import java.util.Map; +import com.egzosn.pay.common.bean.MethodType; +import com.egzosn.pay.common.bean.result.PayException; +import com.egzosn.pay.common.exception.PayErrorException; +import com.egzosn.pay.common.util.str.StringUtils; /** * http请求工具 + * * @author egan - * + * * email egzosn@gmail.com
* date 2017/3/3 21:33 - *
+ *
*/ public class HttpRequestTemplate { @@ -55,8 +59,10 @@ public class HttpRequestTemplate { protected HttpConfigStorage configStorage; private SSLConnectionSocketFactory sslsf; + /** - * 获取代理带代理地址的 HttpHost + * 获取代理带代理地址的 HttpHost + * * @return 获取代理带代理地址的 HttpHost */ public HttpHost getHttpProxy() { @@ -98,7 +104,8 @@ public class HttpRequestTemplate { } /** - * 初始化 + * 初始化 + * * @param configStorage 请求配置 */ public HttpRequestTemplate(HttpConfigStorage configStorage) { @@ -111,24 +118,26 @@ public class HttpRequestTemplate { /** - * 创建ssl配置 + * 创建ssl配置 + * * @param configStorage 请求配置 * @return SSLConnectionSocketFactory Layered socket factory for TLS/SSL connections. */ - public SSLConnectionSocketFactory createSSL( HttpConfigStorage configStorage){ - if (null != sslsf){ + public SSLConnectionSocketFactory createSSL(HttpConfigStorage configStorage) { + if (null != sslsf) { return sslsf; } - if (null == configStorage.getKeystore()){ + if (null == configStorage.getKeystore()) { try { return sslsf = new SSLConnectionSocketFactory(SSLContext.getDefault()); - } catch (NoSuchAlgorithmException e) { + } + catch (NoSuchAlgorithmException e) { LOG.error("", e); } } //读取本机存放的PKCS12证书文件 - try(InputStream instream = configStorage.getKeystoreInputStream()){ + try (InputStream instream = configStorage.getKeystoreInputStream()) { //指定读取证书格式为PKCS12 KeyStore keyStore = KeyStore.getInstance("PKCS12"); @@ -148,9 +157,11 @@ public class HttpRequestTemplate { new DefaultHostnameVerifier()); return sslsf; - } catch (IOException e) { + } + catch (IOException e) { LOG.error("", e); - } catch (GeneralSecurityException e) { + } + catch (GeneralSecurityException e) { LOG.error("", e); } return null; @@ -159,10 +170,11 @@ public class HttpRequestTemplate { /** * 创建凭据提供程序 + * * @param configStorage 请求配置 * @return 凭据提供程序 */ - public CredentialsProvider createCredentialsProvider(HttpConfigStorage configStorage){ + public CredentialsProvider createCredentialsProvider(HttpConfigStorage configStorage) { if (StringUtils.isBlank(configStorage.getAuthUsername())) { @@ -181,20 +193,21 @@ public class HttpRequestTemplate { /** * 初始化连接池 + * * @param configStorage 配置 * @return 连接池对象 */ - public PoolingHttpClientConnectionManager connectionManager(HttpConfigStorage configStorage){ - if (null != connectionManager){ + public PoolingHttpClientConnectionManager connectionManager(HttpConfigStorage configStorage) { + if (null != connectionManager) { return connectionManager; } - if (0 == configStorage.getMaxTotal() || 0 == configStorage.getDefaultMaxPerRoute()){ + if (0 == configStorage.getMaxTotal() || 0 == configStorage.getDefaultMaxPerRoute()) { return null; } if (LOG.isInfoEnabled()) { LOG.info(String.format("Initialize the PoolingHttpClientConnectionManager -- maxTotal:%s, defaultMaxPerRoute:%s", configStorage.getMaxTotal(), configStorage.getDefaultMaxPerRoute())); } - Registry socketFactoryRegistry = RegistryBuilder. create() + Registry socketFactoryRegistry = RegistryBuilder.create() .register("https", createSSL(configStorage)) .register("http", new PlainConnectionSocketFactory()) .build(); @@ -216,26 +229,25 @@ public class HttpRequestTemplate { if (null != configStorage && StringUtils.isNotBlank(configStorage.getHttpProxyHost())) { //http代理地址设置 - httpProxy = new HttpHost(configStorage.getHttpProxyHost(),configStorage.getHttpProxyPort());; + httpProxy = new HttpHost(configStorage.getHttpProxyHost(), configStorage.getHttpProxyPort()); + ; } return this; } - - /** - * * post - * @param uri 请求地址 - * @param request 请求参数 + * + * @param uri 请求地址 + * @param request 请求参数 * @param responseType 为响应类(需要自己依据响应格式来确定) * @param uriVariables 地址通配符对应的值 - * @param 响应类型 + * @param 响应类型 * @return 类型对象 */ - public T postForObject(String uri, Object request, Class responseType, Object... uriVariables){ + public T postForObject(String uri, Object request, Class responseType, Object... uriVariables) { return doExecute(URI.create(UriVariables.getUri(uri, uriVariables)), request, responseType, MethodType.POST); } @@ -243,7 +255,7 @@ public class HttpRequestTemplate { return doExecute(URI.create(UriVariables.getUri(uri, uriVariables)), request, responseType, MethodType.POST); } - public T postForObject(URI uri, Object request, Class responseType){ + public T postForObject(URI uri, Object request, Class responseType) { return doExecute(uri, request, responseType, MethodType.POST); } @@ -255,14 +267,13 @@ public class HttpRequestTemplate { * @param responseType 响应类型 * @param uriVariables 用于匹配表达式 * @param 响应类型 - * * @return 类型对象 - *

+ * * * getForObject("http://egan.in/pay/{id}/f/{type}", String.class, "1", "APP") * */ - public T getForObject(String uri, Class responseType, Object... uriVariables){ + public T getForObject(String uri, Class responseType, Object... uriVariables) { return doExecute(URI.create(UriVariables.getUri(uri, uriVariables)), null, responseType, MethodType.GET); } @@ -277,101 +288,179 @@ public class HttpRequestTemplate { * @return 类型对象 * * Map<String, String> uriVariables = new HashMap<String, String>();
- * + * * uriVariables.put("id", "1");
- * + * * uriVariables.put("type", "APP");
- * + * * getForObject("http://egan.in/pay/{id}/f/{type}", String.class, uriVariables)
*
*/ - public T getForObject(String uri, Class responseType, Map uriVariables){ + public T getForObject(String uri, Class responseType, Map uriVariables) { return doExecute(URI.create(UriVariables.getUri(uri, uriVariables)), null, responseType, MethodType.GET); } /** * get 请求 - * @param uri 请求地址 - * @param header 请求头 + * + * @param uri 请求地址 + * @param header 请求头 * @param responseType 响应类型 * @param uriVariables 用于匹配表达式 - * @param 响应类型 - * @return 类型对象 + * @param 响应类型 + * @return 类型对象 * * - * getForObject("http://egan.in/pay/{id}/f/{type}", String.class, "1", "APP") + * getForObject("http://egan.in/pay/{id}/f/{type}", String.class, "1", "APP") * */ - public T getForObject(String uri, HttpHeader header, Class responseType, Object... uriVariables){ + public T getForObject(String uri, HttpHeader header, Class responseType, Object... uriVariables) { - return doExecute(URI.create(UriVariables.getUri(uri, uriVariables)), header, responseType, MethodType.GET); + return getForObjectEntity(uri, header, responseType, uriVariables).getBody(); } /** * get 请求 * * @param uri 请求地址 - * @param header 请求头 + * @param header 请求头 * @param responseType 响应类型 * @param uriVariables 用于匹配表达式 - * @param 响应类型 + * @param 响应类型 * @return 类型对象 + * * - * Map<String, String> uriVariables = new HashMap<String, String>();
+ * getForObject("http://egan.in/pay/{id}/f/{type}", String.class, "1", "APP") + *
+ */ + public ResponseEntity getForObjectEntity(String uri, HttpHeader header, Class responseType, Object... uriVariables) { + + return doExecuteEntity(URI.create(UriVariables.getUri(uri, uriVariables)), header, responseType, MethodType.GET); + } + + /** + * get 请求 * + * @param uri 请求地址 + * @param header 请求头 + * @param responseType 响应类型 + * @param uriVariables 用于匹配表达式 + * @param 响应类型 + * @return 类型对象 + * + * Map<String, String> uriVariables = new HashMap<String, String>();
+ * * uriVariables.put("id", "1");
- * + * * uriVariables.put("type", "APP");
+ * + * getForObject("http://egan.in/pay/{id}/f/{type}", String.class, uriVariables)
+ *
+ */ + public T getForObject(String uri, HttpHeader header, Class responseType, Map uriVariables) { + return getForObjectEntity(uri, header, responseType, uriVariables).getBody(); + } + /** + * get 请求 * + * @param uri 请求地址 + * @param header 请求头 + * @param responseType 响应类型 + * @param uriVariables 用于匹配表达式 + * @param 响应类型 + * @return 类型对象 + * + * Map<String, String> uriVariables = new HashMap<String, String>();
+ * + * uriVariables.put("id", "1");
+ * + * uriVariables.put("type", "APP");
+ * * getForObject("http://egan.in/pay/{id}/f/{type}", String.class, uriVariables)
*
*/ - public T getForObject(String uri, HttpHeader header, Class responseType, Map uriVariables){ - return doExecute(URI.create(UriVariables.getUri(uri, uriVariables)), header, responseType, MethodType.GET); + public ResponseEntity getForObjectEntity(String uri, HttpHeader header, Class responseType, Map uriVariables) { + return doExecuteEntity(URI.create(UriVariables.getUri(uri, uriVariables)), header, responseType, MethodType.GET); } /** * http 请求执行 - * @param uri 地址 - * @param request 请求数据 + * + * @param uri 地址 + * @param request 请求数据 * @param responseType 响应类型 - * @param method 请求方法 - * @param 响应类型 + * @param method 请求方法 + * @param 响应类型 * @return 类型对象 */ - public T doExecute(URI uri, Object request, Class responseType, MethodType method){ + public T doExecute(URI uri, Object request, Class responseType, MethodType method) { + return doExecuteEntity(uri, request, responseType, method).getBody(); + + } + + /** + * http 请求执行 + * + * @param uri 地址 + * @param request 请求数据 + * @param responseType 响应类型 + * @param method 请求方法 + * @param 响应类型 + * @return 类型对象 + */ + public ResponseEntity doExecuteEntity(URI uri, Object request, Class responseType, MethodType method) { + if (LOG.isDebugEnabled()) { LOG.debug(String.format("uri:%s, httpMethod:%s ", uri, method.name())); } - ClientHttpRequest httpRequest = new ClientHttpRequest(uri ,method, request, null == configStorage ? null : configStorage.getCharset()); + ClientHttpRequest httpRequest = new ClientHttpRequest(uri, method, request, null == configStorage ? null : configStorage.getCharset()); //判断是否有代理设置 - if (null != httpProxy){ + if (null != httpProxy) { httpRequest.setProxy(httpProxy); } httpRequest.setResponseType(responseType); try (CloseableHttpResponse response = getHttpClient().execute(httpRequest)) { - return httpRequest.handleResponse(response); - }catch (IOException e){ + int statusCode = response.getStatusLine().getStatusCode(); + Header[] allHeaders = response.getAllHeaders(); + T body = httpRequest.handleResponse(response); + return new ResponseEntity<>(statusCode, allHeaders, body); + } + catch (IOException e) { throw new PayErrorException(new PayException("IOException", e.getLocalizedMessage())); - }finally { + } + finally { httpRequest.releaseConnection(); } - } /** * http 请求执行 - * @param uri 地址 - * @param request 请求数据 + * + * @param uri 地址 + * @param request 请求数据 * @param responseType 响应类型 - * @param method 请求方法 - * @param 响应类型 + * @param method 请求方法 + * @param 响应类型 * @return 类型对象 */ - public T doExecute(String uri, Object request, Class responseType, MethodType method){ + public T doExecute(String uri, Object request, Class responseType, MethodType method) { return doExecute(URI.create(uri), request, responseType, method); } + + /** + * http 请求执行 + * + * @param uri 地址 + * @param request 请求数据 + * @param responseType 响应类型 + * @param method 请求方法 + * @param 响应类型 + * @return 类型对象 + */ + public ResponseEntity doExecuteEntity(String uri, Object request, Class responseType, MethodType method) { + return doExecuteEntity(URI.create(uri), request, responseType, method); + } } diff --git a/pay-java-common/src/main/java/com/egzosn/pay/common/http/HttpStringEntity.java b/pay-java-common/src/main/java/com/egzosn/pay/common/http/HttpStringEntity.java index 828c9be..53e5062 100644 --- a/pay-java-common/src/main/java/com/egzosn/pay/common/http/HttpStringEntity.java +++ b/pay-java-common/src/main/java/com/egzosn/pay/common/http/HttpStringEntity.java @@ -1,25 +1,30 @@ package com.egzosn.pay.common.http; +import java.io.UnsupportedEncodingException; +import java.nio.charset.Charset; +import java.nio.charset.UnsupportedCharsetException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Map; + import org.apache.http.Header; import org.apache.http.entity.ContentType; import org.apache.http.entity.StringEntity; import org.apache.http.message.BasicHeader; -import java.io.UnsupportedEncodingException; -import java.nio.charset.Charset; -import java.nio.charset.UnsupportedCharsetException; -import java.util.*; - import static com.egzosn.pay.common.http.UriVariables.getMapToParameters; +import com.egzosn.pay.common.util.str.StringUtils; + /** * 请求实体,包含请求头,内容类型,编码类型等 * * @author egan - *

-*               email egzosn@gmail.com
-*               date 2017/12/20
-*           
+ *
+ *               email egzosn@gmail.com
+ *               date 2017/12/20
+ *           
*/ public class HttpStringEntity extends StringEntity { /** @@ -42,12 +47,13 @@ public class HttpStringEntity extends StringEntity { public void requestIsEmpty(Map request) { - if (null == request || request.isEmpty()){ + if (null == request || request.isEmpty()) { this.isEmpty = true; } } + public void requestIsEmpty(String request) { - if (null == request || request.isEmpty()){ + if (StringUtils.isEmpty(request)) { this.isEmpty = true; } @@ -59,7 +65,6 @@ public class HttpStringEntity extends StringEntity { * * @param request 请求体 * @param headers 请求头 - * * @throws UnsupportedEncodingException 不支持默认的HTTP字符集 */ public HttpStringEntity(Map request, Header... headers) throws UnsupportedEncodingException { @@ -73,7 +78,6 @@ public class HttpStringEntity extends StringEntity { * * @param request 请求体 * @param headers 请求头 - * * @throws UnsupportedEncodingException 不支持默认的HTTP字符集 */ public HttpStringEntity(Map request, Map headers) throws UnsupportedEncodingException { @@ -119,7 +123,6 @@ public class HttpStringEntity extends StringEntity { * 构造器 * * @param request 请求体 - * * @throws UnsupportedEncodingException 不支持默认的HTTP字符集 */ public HttpStringEntity(Map request) throws UnsupportedEncodingException { @@ -132,7 +135,6 @@ public class HttpStringEntity extends StringEntity { * * @param request 请求体 * @param contentType 内容类型 - * * @throws UnsupportedCharsetException 不支持默认的HTTP字符集 */ public HttpStringEntity(String request, ContentType contentType) throws UnsupportedCharsetException { @@ -145,7 +147,6 @@ public class HttpStringEntity extends StringEntity { * * @param request 请求体 * @param charset 字符类型 - * * @throws UnsupportedCharsetException 不支持默认的HTTP字符集 */ public HttpStringEntity(String request, String charset) throws UnsupportedCharsetException { @@ -169,7 +170,6 @@ public class HttpStringEntity extends StringEntity { * * @param request 请求体 * @param headers 请求头 - * * @throws UnsupportedEncodingException 不支持默认的HTTP字符集 */ public HttpStringEntity(String request, Header... headers) throws UnsupportedEncodingException { @@ -185,7 +185,6 @@ public class HttpStringEntity extends StringEntity { * * @param request 请求体 * @param headers 请求头 - * * @throws UnsupportedEncodingException 不支持默认的HTTP字符集 */ public HttpStringEntity(String request, Map headers) throws UnsupportedEncodingException { @@ -237,6 +236,7 @@ public class HttpStringEntity extends StringEntity { addHeader(new BasicHeader(entry.getKey(), entry.getValue())); } } + /** * 设置请求头 * diff --git a/pay-java-common/src/main/java/com/egzosn/pay/common/http/ResponseEntity.java b/pay-java-common/src/main/java/com/egzosn/pay/common/http/ResponseEntity.java new file mode 100644 index 0000000..995bc67 --- /dev/null +++ b/pay-java-common/src/main/java/com/egzosn/pay/common/http/ResponseEntity.java @@ -0,0 +1,33 @@ +package com.egzosn.pay.common.http; + +import org.apache.http.Header; + +/** + * 响应实体 + * @author Egan + * email egzosn@gmail.com + * date 2021/8/1 + */ +public class ResponseEntity { + private final int statusCode; + private final Header[] headers; + private final T body; + + public ResponseEntity(int statusCode, Header[] headers, T body) { + this.statusCode = statusCode; + this.headers = headers; + this.body = body; + } + + public int getStatusCode() { + return statusCode; + } + + public Header[] getHeaders() { + return headers; + } + + public T getBody() { + return body; + } +} diff --git a/pay-java-common/src/main/java/com/egzosn/pay/common/http/UriVariables.java b/pay-java-common/src/main/java/com/egzosn/pay/common/http/UriVariables.java index 8f59338..cff3608 100644 --- a/pay-java-common/src/main/java/com/egzosn/pay/common/http/UriVariables.java +++ b/pay-java-common/src/main/java/com/egzosn/pay/common/http/UriVariables.java @@ -1,6 +1,8 @@ package com.egzosn.pay.common.http; import java.io.UnsupportedEncodingException; +import java.net.URI; +import java.net.URISyntaxException; import java.net.URLEncoder; import java.util.List; import java.util.Map; @@ -11,6 +13,7 @@ import org.slf4j.LoggerFactory; import com.alibaba.fastjson.JSONObject; import com.egzosn.pay.common.bean.result.PayException; import com.egzosn.pay.common.exception.PayErrorException; +import com.egzosn.pay.common.util.str.StringUtils; /** * URL表达式处理器 @@ -23,6 +26,7 @@ import com.egzosn.pay.common.exception.PayErrorException; */ public final class UriVariables { private static final Logger LOG = LoggerFactory.getLogger(UriVariables.class); + public static final String QUESTION = "?"; private UriVariables() { } @@ -218,5 +222,28 @@ public final class UriVariables { return str; } + /** + * 去除域名的标准url + * + * @param url url + * @return 去除域名的标准url + */ + public static String getCanonicalUrl(String url) { + if (StringUtils.isEmpty(url)) { + return url; + } + try { + URI uri = new URI(url); + String path = uri.getPath(); + String encodedQuery = uri.getQuery(); + if (StringUtils.isNotEmpty(encodedQuery)) { + path += QUESTION.concat(encodedQuery); + } + return path; + } + catch (URISyntaxException e) { + throw new PayErrorException(new PayException("failure", "去除域名的标准url失败"), e); + } + } } -- Gitee From bb91d24d7405bd0e88d2fd224e003b4aba345ec3 Mon Sep 17 00:00:00 2001 From: egan Date: Sun, 1 Aug 2021 23:34:15 +0800 Subject: [PATCH 209/299] =?UTF-8?q?=E5=B7=A5=E5=85=B7=E7=B1=BB=E4=BC=98?= =?UTF-8?q?=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/egzosn/pay/common/util/DateUtils.java | 26 ++- .../com/egzosn/pay/common/util/MapGen.java | 36 ++++ .../java/com/egzosn/pay/common/util/Util.java | 11 +- .../pay/common/util/sign/SecureUtil.java | 172 ----------------- .../pay/common/util/sign/SignTextUtils.java | 182 ++++++++++++++++++ .../pay/common/util/sign/SignUtils.java | 178 +---------------- .../common/util/sign/encrypt/HmacSha256.java | 56 ++++++ .../pay/common/util/str/StringUtils.java | 53 ++++- 8 files changed, 350 insertions(+), 364 deletions(-) create mode 100644 pay-java-common/src/main/java/com/egzosn/pay/common/util/MapGen.java delete mode 100644 pay-java-common/src/main/java/com/egzosn/pay/common/util/sign/SecureUtil.java create mode 100644 pay-java-common/src/main/java/com/egzosn/pay/common/util/sign/SignTextUtils.java create mode 100644 pay-java-common/src/main/java/com/egzosn/pay/common/util/sign/encrypt/HmacSha256.java diff --git a/pay-java-common/src/main/java/com/egzosn/pay/common/util/DateUtils.java b/pay-java-common/src/main/java/com/egzosn/pay/common/util/DateUtils.java index daa6747..f5397da 100644 --- a/pay-java-common/src/main/java/com/egzosn/pay/common/util/DateUtils.java +++ b/pay-java-common/src/main/java/com/egzosn/pay/common/util/DateUtils.java @@ -8,9 +8,9 @@ import java.util.HashMap; import java.util.Map; import java.util.TimeZone; +import org.apache.http.util.Args; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.apache.http.util.Args; /** * 日期转换运算工具 @@ -90,15 +90,15 @@ public final class DateUtils { return parseDate(date, YYYY_MM_DD_HH_MM_SS); } - public static final String format(Date date) { + public static String format(Date date) { return formatDate(date, YYYY_MM_DD_HH_MM_SS); } - public static final Date parseDay(String date) { + public static Date parseDay(String date) { return parseDate(date, YYYY_MM_DD); } - public static final String formatDay(Date date) { + public static String formatDay(Date date) { return formatDate(date, YYYY_MM_DD); } @@ -108,8 +108,8 @@ public final class DateUtils { * @param date 结束点日期 * @return 分钟数 */ - public static final long minutesRemaining(Date date) { - return (date.getTime() / 1000 / 60 - System.currentTimeMillis() / 1000 / 60); + public static long minutesRemaining(Date date) { + return (date.getTime() / 1000 / 60 - DateUtils.toEpochSecond() / 60); } /** @@ -118,7 +118,7 @@ public final class DateUtils { * @param date 结束点日期 * @return 小时数 */ - public static final long remainingHours(Date date) { + public static long remainingHours(Date date) { return minutesRemaining(date) / 60; } @@ -128,8 +128,18 @@ public final class DateUtils { * @param date 结束点日期 * @return 天数 */ - public static final long remainingDays(Date date) { + public static long remainingDays(Date date) { return remainingHours(date) / 24; } + /** + * 将此日期时间转换为从epoch开始的秒数 + * + * @return epoch开始的秒数 + */ + public static long toEpochSecond() { + return System.currentTimeMillis() / 1000; + } + + } diff --git a/pay-java-common/src/main/java/com/egzosn/pay/common/util/MapGen.java b/pay-java-common/src/main/java/com/egzosn/pay/common/util/MapGen.java new file mode 100644 index 0000000..45b8a89 --- /dev/null +++ b/pay-java-common/src/main/java/com/egzosn/pay/common/util/MapGen.java @@ -0,0 +1,36 @@ +package com.egzosn.pay.common.util; + +import java.util.Map; + +/** + * Map生成工具 + * + * @author Egan + * email egzosn@gmail.com + * date 2021/8/1 + */ +public class MapGen { + + /** + * 属性 + */ + private Map attr; + + public MapGen(K key, V value) { + keyValue(key, value); + } + + public MapGen keyValue(K key, V value) { + attr.put(key, value); + return this; + } + + + public Map getAttr() { + return attr; + } + + private void setAttr(Map attr) { + this.attr = attr; + } +} diff --git a/pay-java-common/src/main/java/com/egzosn/pay/common/util/Util.java b/pay-java-common/src/main/java/com/egzosn/pay/common/util/Util.java index e8c2b14..7991c3c 100644 --- a/pay-java-common/src/main/java/com/egzosn/pay/common/util/Util.java +++ b/pay-java-common/src/main/java/com/egzosn/pay/common/util/Util.java @@ -571,14 +571,19 @@ public class Util { } + /** + * 一百 + */ + public static final BigDecimal HUNDRED = new BigDecimal(100); + /** * 元转分 * * @param amount 元的金额 * @return 分的金额 */ - public static final int conversionCentAmount(BigDecimal amount) { - return amount.multiply(new BigDecimal(100)).setScale(0, BigDecimal.ROUND_HALF_UP).intValue(); + public static int conversionCentAmount(BigDecimal amount) { + return amount.multiply(HUNDRED).setScale(0, BigDecimal.ROUND_HALF_UP).intValue(); } /** @@ -587,7 +592,7 @@ public class Util { * @param amount 元的金额 * @return 元的金额 两位小数 */ - public static final BigDecimal conversionAmount(BigDecimal amount) { + public static BigDecimal conversionAmount(BigDecimal amount) { return amount.setScale(2, BigDecimal.ROUND_HALF_UP); } diff --git a/pay-java-common/src/main/java/com/egzosn/pay/common/util/sign/SecureUtil.java b/pay-java-common/src/main/java/com/egzosn/pay/common/util/sign/SecureUtil.java deleted file mode 100644 index 24093c3..0000000 --- a/pay-java-common/src/main/java/com/egzosn/pay/common/util/sign/SecureUtil.java +++ /dev/null @@ -1,172 +0,0 @@ -package com.egzosn.pay.common.util.sign; - -import java.io.UnsupportedEncodingException; -import java.security.MessageDigest; -import java.security.NoSuchAlgorithmException; -import java.security.PublicKey; -import java.security.Signature; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import com.egzosn.pay.common.bean.result.PayException; -import com.egzosn.pay.common.exception.PayErrorException; -import com.egzosn.pay.common.util.sign.encrypt.sm3.SM3Digest; - -public class SecureUtil { - - private static final Logger LOG = LoggerFactory.getLogger(SecureUtil.class); - /** - * 算法常量: SHA1 - */ - private static final String ALGORITHM_SHA1 = "SHA-1"; - /** - * 算法常量: SHA256 - */ - private static final String ALGORITHM_SHA256 = "SHA-256"; - /** - * 算法常量:SHA1withRSA - */ - private static final String BC_PROV_ALGORITHM_SHA1RSA = "SHA1withRSA"; - /** - * 算法常量:SHA256withRSA - */ - private static final String BC_PROV_ALGORITHM_SHA256RSA = "SHA256withRSA"; - - /** - * 获取摘要 - * - * @param data 待计算的数据 - * @param algorithm 算法名 - * @return 计算结果 - */ - private static byte[] digestByData(byte[] data, String algorithm) { - MessageDigest md = null; - try { - md = MessageDigest.getInstance(algorithm); - md.reset(); - md.update(data); - return md.digest(); - } - catch (NoSuchAlgorithmException e) { - LOG.error("", e); - return null; - } - } - - /** - * sha1计算后进行16进制转换 - * - * @param data 待计算的数据 - * @param encoding 编码 - * @return 计算结果 - */ - public static byte[] sha1X16(String data, String encoding) { - try { - byte[] bytes = digestByData(data.getBytes(encoding), ALGORITHM_SHA1); - StringBuilder sha1StrBuff = new StringBuilder(); - for (int i = 0; i < bytes.length; i++) { - if (Integer.toHexString(0xFF & bytes[i]).length() == 1) { - sha1StrBuff.append("0").append( - Integer.toHexString(0xFF & bytes[i])); - } - else { - sha1StrBuff.append(Integer.toHexString(0xFF & bytes[i])); - } - } - return sha1StrBuff.toString().getBytes(encoding); - } - catch (UnsupportedEncodingException e) { - LOG.error("", e); - return null; - } - } - - - /** - * sha256计算后进行16进制转换 - * - * @param data 待计算的数据 - * @param encoding 编码 - * @return 计算结果 - */ - public static String sha256X16Str(String data, String encoding) { - byte[] bytes = null; - try { - bytes = digestByData(data.getBytes(encoding), ALGORITHM_SHA1); - } - catch (UnsupportedEncodingException e) { - throw new PayErrorException(new PayException("error", e.getLocalizedMessage())); - } - StringBuilder sha256StrBuff = new StringBuilder(); - for (int i = 0; i < bytes.length; i++) { - if (Integer.toHexString(0xFF & bytes[i]).length() == 1) { - sha256StrBuff.append("0").append( - Integer.toHexString(0xFF & bytes[i])); - } - else { - sha256StrBuff.append(Integer.toHexString(0xFF & bytes[i])); - } - } - return sha256StrBuff.toString(); - } - - /** - * SM3计算. - * - * @param data 待计算的数据 - * @return 计算结果 - */ - private static byte[] sm3(byte[] data) { - - SM3Digest sm3 = new SM3Digest(); - sm3.update(data, 0, data.length); - byte[] result = new byte[sm3.getDigestSize()]; - sm3.doFinal(result, 0); - return result; - } - - /** - * sm3计算后进行16进制转换 - * - * @param data 待计算的数据 - * @param encoding 编码 - * @return 计算结果 - */ - public static String sm3X16Str(String data, String encoding) { - byte[] bytes = new byte[new SM3Digest().getDigestSize()]; - try { - bytes = SecureUtil.sm3(data.getBytes(encoding)); - } - catch (UnsupportedEncodingException e) { - LOG.error("", e); - } - StringBuilder sm3StrBuff = new StringBuilder(); - for (int i = 0; i < bytes.length; i++) { - if (Integer.toHexString(0xFF & bytes[i]).length() == 1) { - sm3StrBuff.append("0").append( - Integer.toHexString(0xFF & bytes[i])); - } - else { - sm3StrBuff.append(Integer.toHexString(0xFF & bytes[i])); - } - } - return sm3StrBuff.toString(); - } - - public static boolean validateSignBySoft256(PublicKey publicKey, byte[] signData, byte[] srcData) throws Exception { - - Signature st = Signature.getInstance(BC_PROV_ALGORITHM_SHA256RSA, "BC"); - st.initVerify(publicKey); - st.update(srcData); - return st.verify(signData); - } - - public static boolean validateSignBySoft(PublicKey publicKey, - byte[] signData, byte[] srcData) throws Exception { - Signature st = Signature.getInstance(BC_PROV_ALGORITHM_SHA1RSA, "BC"); - st.initVerify(publicKey); - st.update(srcData); - return st.verify(signData); - } -} diff --git a/pay-java-common/src/main/java/com/egzosn/pay/common/util/sign/SignTextUtils.java b/pay-java-common/src/main/java/com/egzosn/pay/common/util/sign/SignTextUtils.java new file mode 100644 index 0000000..dfe94dd --- /dev/null +++ b/pay-java-common/src/main/java/com/egzosn/pay/common/util/sign/SignTextUtils.java @@ -0,0 +1,182 @@ +package com.egzosn.pay.common.util.sign; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.SortedMap; + +import org.apache.http.message.BasicNameValuePair; + +import com.egzosn.pay.common.util.str.StringUtils; + +/** + * 签名文本构建工具 + * + * @author Egan + * email egzosn@gmail.com + * date 2021/8/1 + */ +public final class SignTextUtils { + + private SignTextUtils() { + } + + + /** + * 把数组所有元素排序,并按照“参数=参数值”的模式用“@param separator”字符拼接成字符串 + * + * @param parameters 参数 + * @return 去掉空值与签名参数后的新签名,拼接后字符串 + */ + public static String parameterText(Map parameters) { + return parameterText(parameters, "&"); + + } + + /** + * 把数组所有元素排序,并按照“参数=参数值”的模式用“@param separator”字符拼接成字符串 + * + * @param parameters 参数 + * @param separator 分隔符 + * @return 去掉空值与签名参数后的新签名,拼接后字符串 + */ + public static String parameterText(Map parameters, String separator) { + return parameterText(parameters, separator, "signature", "sign", "key", "sign_type"); + } + + /** + * 把数组所有元素排序,并按照“参数=参数值”的模式用“@param separator”字符拼接成字符串 + * + * @param parameters 参数 + * @param separator 分隔符 + * @param ignoreKey 需要忽略添加的key + * @return 去掉空值与签名参数后的新签名,拼接后字符串 + */ + public static String parameterText(Map parameters, String separator, String... ignoreKey) { + return parameterText(parameters, separator, true, ignoreKey); + } + + /** + * 把数组所有元素排序,并按照“参数=参数值”的模式用“@param separator”字符拼接成字符串 + * + * @param parameters 参数 + * @param separator 分隔符 + * @param ignoreNullValue 需要忽略NULL值 + * @param ignoreKey 需要忽略添加的key + * @return 去掉空值与签名参数后的新签名,拼接后字符串 + */ + public static String parameterText(Map parameters, String separator, boolean ignoreNullValue, String... ignoreKey) { + if (parameters == null) { + return ""; + } + + if (null != ignoreKey) { + Arrays.sort(ignoreKey); + } + StringBuffer sb = new StringBuffer(); + // TODO 2016/11/11 10:14 author: egan 已经排序好处理 + if (parameters instanceof SortedMap) { + for (Map.Entry entry : parameters.entrySet()) { + Object v = entry.getValue(); + if (null == v) { + continue; + } + String valStr = v.toString().trim(); + if ("".equals(valStr) || (null != ignoreKey && Arrays.binarySearch(ignoreKey, entry.getKey()) >= 0)) { + continue; + } + sb.append(entry.getKey()).append("=").append(valStr).append(separator); + } + if (sb.length() > 0 && !"".equals(separator)) { + sb.deleteCharAt(sb.length() - 1); + } + return sb.toString(); + + } + + return sortMapParameterText(parameters, separator, ignoreNullValue, ignoreKey); + + } + + + private static String sortMapParameterText(Map parameters, String separator, boolean ignoreNullValue, String... ignoreKey) { + StringBuffer sb = new StringBuffer(); + // 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 (ignoreNullValue && null == o) { + continue; + } + if (o instanceof String[]) { + String[] values = (String[]) o; + + 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 { + valueStr = o.toString(); + } + if (StringUtils.isBlank(valueStr) || (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(); + } + + /** + * 将参数集合(事前做好排序)按分割符号拼凑字符串并加密为MD5 + * example: mchnt_cd+"|" +order_id+"|"+order_amt+"|"+order_pay_type+"|"+page_notify_url+"|"+back_notify_url+"|"+order_valid_time+"|"+iss_ins_cd+"|"+goods_name+"|"+"+goods_display_url+"|"+rem+"|"+ver+"|"+mchnt_key + * + * @param parameters 参数集合 + * @param separator 分隔符 + * @return 参数排序好的值 + */ + public static String parameters2Md5Str(Object parameters, String separator) { + StringBuffer sb = new StringBuffer(); + + if (parameters instanceof LinkedHashMap) { + Set keys = (Set) ((LinkedHashMap) parameters).keySet(); + for (String key : keys) { + String val = ((LinkedHashMap) parameters).get(key).toString(); + sb.append(val).append(separator); + + } + } + else if (parameters instanceof List) { + for (BasicNameValuePair bnv : ((List) parameters)) { + sb.append(bnv.getValue()).append(separator); + } + } + + return StringUtils.isBlank(sb.toString()) ? "" : sb.deleteCharAt(sb.length() - 1).toString(); + } + + + /** + * 获取随机字符串 + * + * @return 随机字符串 + */ + public static String randomStr() { + return StringUtils.randomStr(); + } + + +} 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 4c3f106..0085513 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 @@ -21,9 +21,12 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.apache.http.message.BasicNameValuePair; +import static com.egzosn.pay.common.util.sign.SignTextUtils.parameterText; + import com.egzosn.pay.common.bean.SignType; import com.egzosn.pay.common.bean.result.PayException; import com.egzosn.pay.common.exception.PayErrorException; +import com.egzosn.pay.common.util.sign.encrypt.HmacSha256; import com.egzosn.pay.common.util.str.StringUtils; /** @@ -80,29 +83,7 @@ public enum SignUtils implements SignType { */ @Override public String createSign(String content, String key, String characterEncoding) { - Mac sha256HMAC = null; - try { - sha256HMAC = Mac.getInstance("HmacSHA256"); - SecretKeySpec secretKey = new SecretKeySpec(key.getBytes(characterEncoding), "HmacSHA256"); - sha256HMAC.init(secretKey); - byte[] array = sha256HMAC.doFinal(content.getBytes(characterEncoding)); - StringBuilder sb = new StringBuilder(); - for (byte item : array) { - sb.append(Integer.toHexString((item & 0xFF) | 0x100).substring(1, 3)); - } - return sb.toString().toUpperCase(); - } - catch (NoSuchAlgorithmException e) { - LOG.error("", e); - } - catch (InvalidKeyException e) { - LOG.error("", e); - } - catch (UnsupportedEncodingException e) { - LOG.error("", e); - } - - throw new PayErrorException(new PayException("fail", "HMACSHA256 签名异常")); + return HmacSha256.createSign(content, key, characterEncoding); } /** @@ -177,160 +158,9 @@ public enum SignUtils implements SignType { return com.egzosn.pay.common.util.sign.encrypt.RSA2.verify(text, sign, publicKey, characterEncoding); } }; - private static final Logger LOG = LoggerFactory.getLogger(SignUtils.class); - - /** - * 把数组所有元素排序,并按照“参数=参数值”的模式用“@param separator”字符拼接成字符串 - * - * @param parameters 参数 - * @return 去掉空值与签名参数后的新签名,拼接后字符串 - */ - public static String parameterText(Map parameters) { - return parameterText(parameters, "&"); - - } - - /** - * 把数组所有元素排序,并按照“参数=参数值”的模式用“@param separator”字符拼接成字符串 - * - * @param parameters 参数 - * @param separator 分隔符 - * @return 去掉空值与签名参数后的新签名,拼接后字符串 - */ - public static String parameterText(Map parameters, String separator) { - return parameterText(parameters, separator, "signature", "sign", "key", "sign_type"); - } - - /** - * 把数组所有元素排序,并按照“参数=参数值”的模式用“@param separator”字符拼接成字符串 - * - * @param parameters 参数 - * @param separator 分隔符 - * @param ignoreKey 需要忽略添加的key - * @return 去掉空值与签名参数后的新签名,拼接后字符串 - */ - public static String parameterText(Map parameters, String separator, String... ignoreKey) { - return parameterText(parameters, separator, true, ignoreKey); - } - - /** - * 把数组所有元素排序,并按照“参数=参数值”的模式用“@param separator”字符拼接成字符串 - * - * @param parameters 参数 - * @param separator 分隔符 - * @param ignoreNullValue 需要忽略NULL值 - * @param ignoreKey 需要忽略添加的key - * @return 去掉空值与签名参数后的新签名,拼接后字符串 - */ - public static String parameterText(Map parameters, String separator, boolean ignoreNullValue, String... ignoreKey) { - if (parameters == null) { - return ""; - } - - if (null != ignoreKey) { - Arrays.sort(ignoreKey); - } - StringBuffer sb = new StringBuffer(); - // TODO 2016/11/11 10:14 author: egan 已经排序好处理 - if (parameters instanceof SortedMap) { - for (Map.Entry entry : parameters.entrySet()) { - Object v = entry.getValue(); - if (null == v) { - continue; - } - String valStr = v.toString().trim(); - if ("".equals(valStr) || (null != ignoreKey && Arrays.binarySearch(ignoreKey, entry.getKey()) >= 0)) { - continue; - } - sb.append(entry.getKey()).append("=").append(valStr).append(separator); - } - if (sb.length() > 0 && !"".equals(separator)) { - sb.deleteCharAt(sb.length() - 1); - } - return sb.toString(); - } - - return sortMapParameterText(parameters, separator, ignoreNullValue, ignoreKey); - - } - private static String sortMapParameterText(Map parameters, String separator, boolean ignoreNullValue, String... ignoreKey) { - StringBuffer sb = new StringBuffer(); - // 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 (ignoreNullValue && null == o) { - continue; - } - if (o instanceof String[]) { - String[] values = (String[]) o; - - 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 { - valueStr = o.toString(); - } - if (StringUtils.isBlank(valueStr) || (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(); - } - - /** - * 将参数集合(事前做好排序)按分割符号拼凑字符串并加密为MD5 - * example: mchnt_cd+"|" +order_id+"|"+order_amt+"|"+order_pay_type+"|"+page_notify_url+"|"+back_notify_url+"|"+order_valid_time+"|"+iss_ins_cd+"|"+goods_name+"|"+"+goods_display_url+"|"+rem+"|"+ver+"|"+mchnt_key - * - * @param parameters 参数集合 - * @param separator 分隔符 - * @return 参数排序好的值 - */ - public static String parameters2Md5Str(Object parameters, String separator) { - StringBuffer sb = new StringBuffer(); - - if (parameters instanceof LinkedHashMap) { - Set keys = (Set) ((LinkedHashMap) parameters).keySet(); - for (String key : keys) { - String val = ((LinkedHashMap) parameters).get(key).toString(); - sb.append(val).append(separator); - - } - } - else if (parameters instanceof List) { - for (BasicNameValuePair bnv : ((List) parameters)) { - sb.append(bnv.getValue()).append(separator); - } - } - - return StringUtils.isBlank(sb.toString()) ? "" : sb.deleteCharAt(sb.length() - 1).toString(); - } - - - /** - * 获取随机字符串 - * - * @return 随机字符串 - */ - public static String randomStr() { - return UUID.randomUUID().toString().replace("-", ""); - } - @Override public String getName() { return this.name(); diff --git a/pay-java-common/src/main/java/com/egzosn/pay/common/util/sign/encrypt/HmacSha256.java b/pay-java-common/src/main/java/com/egzosn/pay/common/util/sign/encrypt/HmacSha256.java new file mode 100644 index 0000000..2640915 --- /dev/null +++ b/pay-java-common/src/main/java/com/egzosn/pay/common/util/sign/encrypt/HmacSha256.java @@ -0,0 +1,56 @@ +package com.egzosn.pay.common.util.sign.encrypt; + +import java.io.UnsupportedEncodingException; +import java.security.GeneralSecurityException; + +import javax.crypto.Mac; +import javax.crypto.spec.SecretKeySpec; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.egzosn.pay.common.bean.result.PayException; +import com.egzosn.pay.common.exception.PayErrorException; + +/** + * + * HmacSHA256 + * @author Egan + * email egzosn@gmail.com + * date 2021/8/1 + */ +public class HmacSha256 { + private static final Logger LOG = LoggerFactory.getLogger(HmacSha256.class); + + /** + * 签名 + * + * @param content 需要签名的内容 + * @param key 密钥 + * @param characterEncoding 字符编码 + * + * @return 签名值 + */ + public static String createSign(String content, String key, String characterEncoding) { + Mac sha256HMAC = null; + try { + sha256HMAC = Mac.getInstance("HmacSHA256"); + SecretKeySpec secretKey = new SecretKeySpec(key.getBytes(characterEncoding), "HmacSHA256"); + sha256HMAC.init(secretKey); + byte[] array = sha256HMAC.doFinal(content.getBytes(characterEncoding)); + StringBuilder sb = new StringBuilder(); + for (byte item : array) { + sb.append(Integer.toHexString((item & 0xFF) | 0x100).substring(1, 3)); + } + return sb.toString().toUpperCase(); + } + catch (UnsupportedEncodingException e) { + LOG.error("", e); + } + catch (GeneralSecurityException e) { + LOG.error("", e); + } + + throw new PayErrorException(new PayException("fail", "HMACSHA256 签名异常")); + } +} diff --git a/pay-java-common/src/main/java/com/egzosn/pay/common/util/str/StringUtils.java b/pay-java-common/src/main/java/com/egzosn/pay/common/util/str/StringUtils.java index 79a0636..42ae96f 100644 --- a/pay-java-common/src/main/java/com/egzosn/pay/common/util/str/StringUtils.java +++ b/pay-java-common/src/main/java/com/egzosn/pay/common/util/str/StringUtils.java @@ -1,6 +1,7 @@ package com.egzosn.pay.common.util.str; import java.io.UnsupportedEncodingException; +import java.util.UUID; /** * Created by ZaoSheng on 2016/6/4. @@ -19,7 +20,6 @@ public class StringUtils { * * @param str1 要比较的字符串1 * @param str2 要比较的字符串2 - * * @return 如果两个字符串相同,或者都是null,则返回true */ public static boolean equals(String str1, String str2) { @@ -31,6 +31,7 @@ public class StringUtils { } // Empty checks //----------------------------------------------------------------------- + /** *

Checks if a CharSequence is empty ("") or null.

*
@@ -40,7 +41,8 @@ public class StringUtils {
      * StringUtils.isEmpty("bob")     = false
      * StringUtils.isEmpty("  bob  ") = false
      * 
- * @param cs the CharSequence to check, may be null + * + * @param cs the CharSequence to check, may be null * @return {@code true} if the CharSequence is empty or null */ public static boolean isEmpty(CharSequence cs) { @@ -58,7 +60,7 @@ public class StringUtils { * StringUtils.isNotEmpty(" bob ") = true * * - * @param cs the CharSequence to check, may be null + * @param cs the CharSequence to check, may be null * @return {@code true} if the CharSequence is not empty and not null */ public static boolean isNotEmpty(CharSequence cs) { @@ -75,7 +77,8 @@ public class StringUtils { * StringUtils.isBlank("bob") = false * StringUtils.isBlank(" bob ") = false * - * @param cs the CharSequence to check, may be null + * + * @param cs the CharSequence to check, may be null * @return {@code true} if the CharSequence is null, empty or whitespace * @since 2.0 */ @@ -102,9 +105,10 @@ public class StringUtils { * StringUtils.isNotBlank("bob") = true * StringUtils.isNotBlank(" bob ") = true * - * @param cs the CharSequence to check, may be null + * + * @param cs the CharSequence to check, may be null * @return {@code true} if the CharSequence is - * not empty and not null and not whitespace + * not empty and not null and not whitespace */ public static boolean isNotBlank(CharSequence cs) { return !StringUtils.isBlank(cs); @@ -121,13 +125,16 @@ public class StringUtils { } try { return content.getBytes(charset); - } catch (UnsupportedEncodingException e) { + } + catch (UnsupportedEncodingException e) { throw new RuntimeException("转码过程中出现错误,指定的编码集不对,您目前指定的编码集是:" + charset); } } + /** * 对 subject body 进行 trim 运算, * 以防止在签名是可能造成的签名错误问题 + * * @param str 字符 * @return 去除空格之后的字符 */ @@ -135,4 +142,36 @@ public class StringUtils { return str == null ? null : str.trim(); } + + /** + * 字符串数组拼接为字符串 + * + * @param separator 分隔符 + * @param str 字符数组 + * @return 字符串 + */ + public static String joining(String separator, String... str) { + StringBuilder builder = new StringBuilder(); + for (String s : str) { + if (StringUtils.isEmpty(s)) { + continue; + } + if (builder.length() > 0) { + builder.append(separator); + } + builder.append(s); + } + return builder.toString(); + } + + /** + * 获取随机字符串 + * + * @return 随机字符串 + */ + public static String randomStr() { + return UUID.randomUUID().toString().replace("-", ""); + } + + } -- Gitee From 3062726c9da64983cf5724056857b61076a533fb Mon Sep 17 00:00:00 2001 From: egan Date: Sun, 1 Aug 2021 23:34:44 +0800 Subject: [PATCH 210/299] =?UTF-8?q?=E5=BE=AE=E4=BF=A1V3=E9=83=A8=E5=88=86?= =?UTF-8?q?=E6=8E=A5=E5=8F=A3=E5=AE=9E=E7=8E=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../pay/wx/v3/api/WxPayConfigStorage.java | 225 ++++++ .../egzosn/pay/wx/v3/api/WxPayService.java | 709 ++++++++++++++++++ .../pay/wx/v3/bean/CertEnvironment.java | 62 ++ .../egzosn/pay/wx/v3/bean/WxAccountType.java | 116 +++ .../com/egzosn/pay/wx/v3/bean/WxBillType.java | 113 +++ .../pay/wx/v3/bean/WxTransactionType.java | 156 ++++ .../egzosn/pay/wx/v3/bean/order/Amount.java | 75 ++ .../egzosn/pay/wx/v3/bean/order/Detail.java | 51 ++ .../com/egzosn/pay/wx/v3/bean/order/From.java | 39 + .../pay/wx/v3/bean/order/GoodsDetail.java | 84 +++ .../egzosn/pay/wx/v3/bean/order/H5Info.java | 94 +++ .../pay/wx/v3/bean/order/RefundAmount.java | 113 +++ .../pay/wx/v3/bean/order/SceneInfo.java | 67 ++ .../pay/wx/v3/bean/order/StoreInfo.java | 62 ++ .../wx/v3/bean/response/WxRefundResult.java | 324 ++++++++ .../pay/wx/v3/utils/AntCertificationUtil.java | 66 ++ .../com/egzosn/pay/wx/v3/utils/WxConst.java | 41 + 17 files changed, 2397 insertions(+) create mode 100644 pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/api/WxPayConfigStorage.java create mode 100644 pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/api/WxPayService.java create mode 100644 pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/CertEnvironment.java create mode 100644 pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/WxAccountType.java create mode 100644 pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/WxBillType.java create mode 100644 pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/WxTransactionType.java create mode 100644 pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/order/Amount.java create mode 100644 pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/order/Detail.java create mode 100644 pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/order/From.java create mode 100644 pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/order/GoodsDetail.java create mode 100644 pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/order/H5Info.java create mode 100644 pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/order/RefundAmount.java create mode 100644 pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/order/SceneInfo.java create mode 100644 pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/order/StoreInfo.java create mode 100644 pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/response/WxRefundResult.java create mode 100644 pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/utils/AntCertificationUtil.java create mode 100644 pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/utils/WxConst.java diff --git a/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/api/WxPayConfigStorage.java b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/api/WxPayConfigStorage.java new file mode 100644 index 0000000..e61f963 --- /dev/null +++ b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/api/WxPayConfigStorage.java @@ -0,0 +1,225 @@ +package com.egzosn.pay.wx.v3.api; + +import java.io.IOException; +import java.io.InputStream; + +import com.egzosn.pay.common.api.BasePayConfigStorage; +import com.egzosn.pay.common.bean.CertStoreType; +import com.egzosn.pay.common.bean.result.PayException; +import com.egzosn.pay.common.exception.PayErrorException; +import com.egzosn.pay.wx.v3.bean.CertEnvironment; +import com.egzosn.pay.wx.v3.utils.AntCertificationUtil; +import com.egzosn.pay.wx.v3.utils.WxConst; + +/** + * 微信配置存储 + * + * @author egan + * + *
+ * email egzosn@gmail.com
+ * date 2016-5-18 14:09:01
+ * 
+ */ +public class WxPayConfigStorage extends BasePayConfigStorage { + + + /** + * 微信分配的公众账号ID + */ + private String appId; + /** + * 服务商申请的公众号appid。 + */ + private String spAppId; + /** + * 服务商户号,由微信支付生成并下发 。 + */ + private String spMchId; + /** + * 子商户应用ID, 非必填 + * 子商户申请的公众号appid。 + * 若sub_openid有传的情况下,sub_appid必填,且sub_appid需与sub_openid对应 + * 示例值:wxd678efh567hg6999 + */ + private String subAppId; + /** + * 微信支付分配的商户号 合作者id + */ + private String mchId; + /** + * 微信支付分配的子商户号,开发者模式下必填 合作者id + */ + private String subMchId; + + /** + * 商户API证书 + * 包含商户的商户号、公司名称、公钥信息 + * 详情 https://pay.weixin.qq.com/wiki/doc/apiv3/wechatpay/wechatpay3_1.shtml + */ + private Object apiClientKeyP12; + + /** + * 证书存储类型 + */ + private CertStoreType certStoreType; + + + /** + * 证书信息 + */ + private CertEnvironment certEnvironment; + + @Deprecated + @Override + public String getAppid() { + return appId; + } + + + @Deprecated + public void setAppid(String appId) { + this.appId = appId; + } + + + /** + * 合作商唯一标识 + */ + @Override + public String getPid() { + return mchId; + } + + + @Override + public String getSeller() { + return mchId; + } + + + public String getMchId() { + return mchId; + } + + public void setMchId(String mchId) { + this.mchId = mchId; + } + + /** + * 为商户平台设置的密钥key + * + * @return 微信密钥 + */ + public String getSecretKey() { + return getKeyPrivate(); + } + + public void setSecretKey(String secretKey) { + setKeyPrivate(secretKey); + } + + public void setAppId(String appId) { + this.appId = appId; + } + + /** + * 应用id + * 纠正名称 + * + * @return 应用id + */ + @Override + public String getAppId() { + return appId; + } + + public String getSubAppId() { + return subAppId; + } + + public void setSubAppId(String subAppId) { + this.subAppId = subAppId; + } + + public String getSpAppId() { + return spAppId; + } + + public void setSpAppId(String spAppId) { + this.spAppId = spAppId; + } + + public String getSpMchId() { + return spMchId; + } + + public void setSpMchId(String spMchId) { + this.spMchId = spMchId; + } + + /** + * 应用id + * 纠正名称 + * + * @return 应用id + * @see #getSubAppId() + */ + @Deprecated + public String getSubAppid() { + return subAppId; + } + + @Deprecated + public void setSubAppid(String subAppid) { + this.subAppId = subAppid; + } + + + public String getSubMchId() { + return subMchId; + } + + public void setSubMchId(String subMchId) { + this.subMchId = subMchId; + } + + public Object getApiClientKeyP12() { + return apiClientKeyP12; + } + + public void setApiClientKeyP12(Object apiClientKeyP12) { + this.apiClientKeyP12 = apiClientKeyP12; + } + + public CertStoreType getCertStoreType() { + return certStoreType; + } + + public void setCertStoreType(CertStoreType certStoreType) { + this.certStoreType = certStoreType; + } + + public CertEnvironment getCertEnvironment() { + return certEnvironment; + } + + public void setCertEnvironment(CertEnvironment certEnvironment) { + this.certEnvironment = certEnvironment; + } + + /** + * 初始化证书信息 + */ + public void loadCertEnvironment() { + if (!isCertSign() || null != this.certEnvironment) { + return; + } + try (InputStream apiKeyCert = certStoreType.getInputStream(getApiClientKeyP12())) { + this.certEnvironment = AntCertificationUtil.initCertification(apiKeyCert, WxConst.CERT_ALIAS, getMchId()); + } + catch (IOException e) { + throw new PayErrorException(new PayException("读取证书异常", e.getMessage())); + } + } +} diff --git a/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/api/WxPayService.java b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/api/WxPayService.java new file mode 100644 index 0000000..ba4255a --- /dev/null +++ b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/api/WxPayService.java @@ -0,0 +1,709 @@ +package com.egzosn.pay.wx.v3.api; + +import java.io.IOException; +import java.io.InputStream; +import java.net.URLEncoder; +import java.security.PrivateKey; +import java.util.Date; +import java.util.HashMap; +import java.util.Map; +import java.util.TreeMap; + +import org.apache.http.entity.ContentType; +import org.apache.http.message.BasicHeader; + +import static com.egzosn.pay.wx.api.WxConst.OUT_TRADE_NO; +import static com.egzosn.pay.wx.api.WxConst.RETURN_CODE; +import static com.egzosn.pay.wx.api.WxConst.RETURN_MSG_CODE; +import static com.egzosn.pay.wx.api.WxConst.SANDBOXNEW; +import static com.egzosn.pay.wx.api.WxConst.SUCCESS; + +import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.JSONObject; +import com.alibaba.fastjson.serializer.SerializerFeature; +import com.egzosn.pay.common.api.BasePayService; +import com.egzosn.pay.common.bean.BillType; +import com.egzosn.pay.common.bean.CurType; +import com.egzosn.pay.common.bean.MethodType; +import com.egzosn.pay.common.bean.Order; +import com.egzosn.pay.common.bean.PayMessage; +import com.egzosn.pay.common.bean.PayOrder; +import com.egzosn.pay.common.bean.PayOutMessage; +import com.egzosn.pay.common.bean.RefundOrder; +import com.egzosn.pay.common.bean.TransactionType; +import com.egzosn.pay.common.bean.TransferOrder; +import com.egzosn.pay.common.bean.result.PayException; +import com.egzosn.pay.common.exception.PayErrorException; +import com.egzosn.pay.common.http.HttpConfigStorage; +import com.egzosn.pay.common.http.HttpStringEntity; +import com.egzosn.pay.common.http.ResponseEntity; +import com.egzosn.pay.common.http.UriVariables; +import com.egzosn.pay.common.util.DateUtils; +import com.egzosn.pay.common.util.IOUtils; +import com.egzosn.pay.common.util.MapGen; +import com.egzosn.pay.common.util.Util; +import com.egzosn.pay.common.util.XML; +import com.egzosn.pay.common.util.sign.SignTextUtils; +import com.egzosn.pay.common.util.sign.encrypt.RSA2; +import com.egzosn.pay.common.util.str.StringUtils; +import com.egzosn.pay.wx.api.WxRedPackService; +import com.egzosn.pay.wx.bean.RedpackOrder; +import com.egzosn.pay.wx.bean.WxPayError; +import com.egzosn.pay.wx.bean.WxPayMessage; +import com.egzosn.pay.wx.bean.WxTransferType; +import com.egzosn.pay.wx.v3.bean.WxAccountType; +import com.egzosn.pay.wx.v3.bean.WxBillType; +import com.egzosn.pay.wx.v3.bean.WxTransactionType; +import com.egzosn.pay.wx.v3.bean.order.Amount; +import com.egzosn.pay.wx.v3.bean.order.RefundAmount; +import com.egzosn.pay.wx.v3.bean.response.WxRefundResult; +import com.egzosn.pay.wx.v3.utils.WxConst; + +/** + * 微信支付服务 + * + * @author egan + *
+ * email egzosn@gmail.com
+ * date 2016-5-18 14:09:01
+ * 
+ */ +public class WxPayService extends BasePayService implements WxRedPackService { + + + /** + * api服务地址,默认为国内 + */ + private String apiServerUrl = WxConst.URI; + /** + * 是否为服务商模式, 默认为false + */ + private boolean partner = false; + + /** + * 创建支付服务 + * + * @param payConfigStorage 微信对应的支付配置 + */ + public WxPayService(WxPayConfigStorage payConfigStorage) { + super(payConfigStorage); + } + + /** + * 创建支付服务 + * + * @param payConfigStorage 微信对应的支付配置 + * @param configStorage 微信对应的网络配置,包含代理配置、ssl证书配置 + */ + public WxPayService(WxPayConfigStorage payConfigStorage, HttpConfigStorage configStorage) { + super(payConfigStorage, configStorage); + } + + /** + * 设置支付配置 + * + * @param payConfigStorage 支付配置 + */ + @Override + public BasePayService setPayConfigStorage(WxPayConfigStorage payConfigStorage) { + payConfigStorage.loadCertEnvironment(); + this.payConfigStorage = payConfigStorage; + return this; + } + + /** + * 设置api服务器地址 + * + * @param apiServerUrl api服务器地址 + * @return 自身 + */ + public WxPayService setApiServerUrl(String apiServerUrl) { + this.apiServerUrl = apiServerUrl; + return this; + } + + /** + * 根据交易类型获取url + * + * @param transactionType 交易类型 + * @return 请求url + */ + @Override + public String getReqUrl(TransactionType transactionType) { + String type = transactionType.getType(); + String partnerStr = ""; + if (partner) { + partnerStr = "/partner"; + } + type = type.replace("{partner}", partnerStr); + return apiServerUrl + (payConfigStorage.isTest() ? SANDBOXNEW : "") + type; + } + + /** + * 回调校验 + * + * @param params 回调回来的参数集 + * @return 签名校验 true通过 + */ + @Override + public boolean verify(Map params) { + throw new PayErrorException(new WxPayError("", "等待作者实现")); + + } + + + /** + * 获取公共参数 + * + * @return 公共参数 + */ + private Map getPublicParameters() { + + Map parameters = new TreeMap(); + parameters.put(WxConst.APPID, payConfigStorage.getAppId()); + parameters.put(WxConst.MCH_ID, payConfigStorage.getMchId()); + + return parameters; + + + } + + + /** + * 初始化商户相关信息 + * + * @param parameters 参数信息 + * @return 参数信息 + */ + private Map initPartner(Map parameters) { + if (null == parameters) { + parameters = new HashMap<>(); + } + if (StringUtils.isNotEmpty(payConfigStorage.getSpAppId()) && StringUtils.isNotEmpty(payConfigStorage.getSpMchId())) { + this.partner = true; + parameters.put("sp_appid", payConfigStorage.getSpAppId()); + parameters.put(WxConst.SP_MCH_ID, payConfigStorage.getSpMchId()); + } + setParameters(parameters, "sub_appid", payConfigStorage.getSubAppId()); + initSubMchId(parameters); + + return parameters; + + } + + /** + * 初始化商户相关信息 + * + * @param parameters 参数信息 + * @return 参数信息 + */ + private Map initSubMchId(Map parameters) { + if (null == parameters) { + parameters = new HashMap<>(); + } + if (StringUtils.isNotEmpty(payConfigStorage.getSubMchId())) { + this.partner = true; + parameters.put(WxConst.SUB_MCH_ID, payConfigStorage.getSubMchId()); + } + return parameters; + + } + + + /** + * 加载结算信息 + * + * @param parameters 订单参数 + * @param order 支付订单 + * @return 订单参数 + */ + private Map loadSettleInfo(Map parameters, PayOrder order) { + Object profitSharing = order.getAttr("profit_sharing"); + if (null != profitSharing) { + Map settleInfo = new MapGen("profit_sharing", profitSharing).getAttr(); + parameters.put("settle_info", settleInfo); + } + else { + //结算信息 + setParameters(parameters, "settle_info", order); + } + + return parameters; + + } + + /** + * 发起请求 + * + * @param parameters 支付参数 + * @param transactionType 交易类型 + * @return 响应内容体 + */ + protected JSONObject doExecute(Map parameters, TransactionType transactionType) { + String requestBody = JSON.toJSONString(parameters, SerializerFeature.WriteMapNullValue); + return doExecute(requestBody, transactionType); + } + + + /** + * 发起请求 + * + * @param body 请求内容 + * @param transactionType 交易类型 + * @param uriVariables 用于匹配表达式 + * @return 响应内容体 + */ + protected JSONObject doExecute(String body, TransactionType transactionType, Object... uriVariables) { + String reqUrl = UriVariables.getUri(getReqUrl(transactionType), uriVariables); + MethodType method = MethodType.valueOf(transactionType.getMethod()); + if (MethodType.GET == method && StringUtils.isNotEmpty(body)) { + reqUrl += UriVariables.QUESTION.concat(body); + body = ""; + } + HttpStringEntity entity = initHttpStringEntity(reqUrl, body, transactionType.getMethod()); + ResponseEntity responseEntity = requestTemplate.doExecuteEntity(reqUrl, entity, JSONObject.class, method); + int statusCode = responseEntity.getStatusCode(); + JSONObject responseBody = responseEntity.getBody(); + if (statusCode >= 400) { + throw new PayErrorException(new WxPayError(responseBody.getString(WxConst.CODE), responseBody.getString(WxConst.MESSAGE), responseBody.toJSONString())); + } + return responseBody; + } + + /** + * 发起请求 + * + * @param parameters 支付参数 + * @param order 订单 + * @return 请求响应 + */ + protected JSONObject doExecute(Map parameters, PayOrder order) { + TransactionType transactionType = order.getTransactionType(); + return doExecute(parameters, transactionType); + } + + + /** + * 微信统一下单接口 + * + * @param order 支付订单集 + * @return 下单结果 + */ + public JSONObject unifiedOrder(PayOrder order) { + + //统一下单 + Map parameters = getPublicParameters(); + initPartner(parameters); + // 商品描述 + setParameters(parameters, "description", order.getSubject()); + setParameters(parameters, "description", order.getBody()); + // 订单号 + parameters.put(WxConst.OUT_TRADE_NO, order.getOutTradeNo()); + //交易结束时间 + if (null != order.getExpirationTime()) { + parameters.put("time_expire", DateUtils.formatDate(order.getExpirationTime(), DateUtils.YYYYMMDDHHMMSS)); + } + setParameters(parameters, "attach", order.getAddition()); + initNotifyUrl(parameters, order); + //订单优惠标记 + setParameters(parameters, "goods_tag", order); + parameters.put("amount", Amount.getAmount(order.getPrice(), order.getCurType())); + + //优惠功能 + setParameters(parameters, "detail", order); + //支付场景描述 + setParameters(parameters, WxConst.SCENE_INFO, order); + loadSettleInfo(parameters, order); + + TransactionType transactionType = order.getTransactionType(); + ((WxTransactionType) transactionType).setAttribute(parameters, order); + + return doExecute(parameters, order); + } + + + /** + * 返回创建的订单信息 + * + * @param order 支付订单 + * @return 订单信息 + * @see PayOrder 支付订单信息 + */ + @Override + public Map orderInfo(PayOrder order) { + + ////统一下单 + JSONObject result = unifiedOrder(order); + throw new PayErrorException(new WxPayError("", "等待作者实现")); + + } + + + /** + * 初始化请求实体 + * 这里也做签名处理 + * + * @param body 请求内容体 + * @param method 请求方法 + * @return 请求实体 + */ + private HttpStringEntity initHttpStringEntity(String url, String body, String method) { + String nonceStr = SignTextUtils.randomStr(); + long timestamp = DateUtils.toEpochSecond(); + String canonicalUrl = UriVariables.getCanonicalUrl(url); + //签名信息 + String signText = StringUtils.joining("\n", method, canonicalUrl, String.valueOf(timestamp), nonceStr, body); + String sign = createSign(signText, payConfigStorage.getInputCharset()); + String serialNumber = payConfigStorage.getCertEnvironment().getSerialNumber(); + // 生成token + String token = String.format(WxConst.TOKEN_PATTERN, payConfigStorage.getMchId(), nonceStr, timestamp, serialNumber, sign); + HttpStringEntity entity = new HttpStringEntity(body, ContentType.APPLICATION_JSON); + entity.addHeader(new BasicHeader("Authorization", WxConst.SCHEMA.concat(token))); + entity.addHeader(new BasicHeader("User-Agent", "Pay-Java-Service")); + return entity; + } + + + /** + * 签名 + * + * @param content 需要签名的内容 不包含key + * @param characterEncoding 字符编码 + * @return 签名结果 + */ + @Override + public String createSign(String content, String characterEncoding) { + PrivateKey privateKey = payConfigStorage.getCertEnvironment().getPrivateKey(); + return RSA2.sign(content, privateKey, characterEncoding); + } + + + /** + * 初始化通知URL必须为直接可访问的URL,不允许携带查询串,要求必须为https地址。 + * + * @param parameters 订单参数 + * @param order 订单信息 + * @return 订单参数 + */ + private void initNotifyUrl(Map parameters, Order order) { + setParameters(parameters, WxConst.NOTIFY_URL, payConfigStorage.getNotifyUrl()); + setParameters(parameters, WxConst.NOTIFY_URL, order); + } + + /** + * 获取服务商相关信息 + * + * @return 服务商相关信息 + */ + private String getSpParameters() { + Map attr = initSubMchId(null); + setParameters(attr, WxConst.SP_MCH_ID, payConfigStorage.getSpMchId()); + String parameters = UriVariables.getMapToParameters(attr); + return parameters; + } + + + /** + * 将请求参数或者请求流转化为 Map + * + * @param parameterMap 请求参数 + * @param is 请求流 + * @return 获得回调的请求参数 + */ + @Override + public Map getParameter2Map(Map parameterMap, InputStream is) { + TreeMap map = new TreeMap(); + try { + return XML.inputStream2Map(is, map); + } + catch (IOException e) { + throw new PayErrorException(new PayException("IOException", e.getMessage())); + } + + } + + /** + * 获取输出消息,用户返回给支付端 + * + * @param code 状态 + * @param message 消息 + * @return 返回输出消息 + */ + @Override + public PayOutMessage getPayOutMessage(String code, String message) { + return PayOutMessage.XML().code(code.toUpperCase()).content(message).build(); + } + + + /** + * 获取成功输出消息,用户返回给支付端 + * 主要用于拦截器中返回 + * + * @param payMessage 支付回调消息 + * @return 返回输出消息 + */ + @Override + public PayOutMessage successPayOutMessage(PayMessage payMessage) { + return PayOutMessage.XML().code("SUCCESS").content("成功").build(); + } + + + /** + * 获取输出消息,用户返回给支付端, 针对于web端 + * + * @param orderInfo 发起支付的订单信息 + * @param method 请求方式 "post" "get", + * @return 获取输出消息,用户返回给支付端, 针对于web端 + * @see MethodType 请求类型 + */ + @Override + public String buildRequest(Map orderInfo, MethodType method) { + if (!SUCCESS.equals(orderInfo.get(RETURN_CODE))) { + throw new PayErrorException(new WxPayError((String) orderInfo.get(RETURN_CODE), (String) orderInfo.get(RETURN_MSG_CODE))); + } + if (WxTransactionType.H5.name().equals(orderInfo.get("trade_type"))) { + return String.format("", orderInfo.get("mweb_url"), StringUtils.isEmpty(payConfigStorage.getReturnUrl()) ? "" : "&redirect_url=" + URLEncoder.encode(payConfigStorage.getReturnUrl())); + } + throw new UnsupportedOperationException(); + + } + + /** + * 获取输出二维码信息, + * + * @param order 发起支付的订单信息 + * @return 返回二维码信息,,支付时需要的 + */ + @Override + public String getQrPay(PayOrder order) { + order.setTransactionType(WxTransactionType.NATIVE); + Map orderInfo = orderInfo(order); + + return (String) orderInfo.get("code_url"); + } + + /** + * 刷卡付,pos主动扫码付款 + * + * @param order 发起支付的订单信息 + * @return 返回支付结果 + */ + @Override + public Map microPay(PayOrder order) { + throw new PayErrorException(new PayException("failure", "V3暂时没有提供此功能,请查看V2版本功能")); + } + + /** + * 交易查询接口 + * + * @param transactionId 微信支付平台订单号 + * @param outTradeNo 商户单号 + * @return 返回查询回来的结果集,支付方原值返回 + */ + @Override + public Map query(String transactionId, String outTradeNo) { + + String parameters = getSpParameters(); + WxTransactionType transactionType = WxTransactionType.QUERY_TRANSACTION_ID; + String uriVariable = transactionId; + if (StringUtils.isNotEmpty(outTradeNo)) { + transactionType = WxTransactionType.QUERY_OUT_TRADE_NO; + uriVariable = outTradeNo; + } + return doExecute(parameters, transactionType, uriVariable); + } + + + /** + * 交易关闭接口 + * + * @param transactionId 支付平台订单号 + * @param outTradeNo 商户单号 + * @return 返回支付方交易关闭后的结果 + */ + @Override + public Map close(String transactionId, String outTradeNo) { + String parameters = getSpParameters(); + return doExecute(parameters, WxTransactionType.CLOSE, outTradeNo); + } + + /** + * 申请退款接口 + * + * @param refundOrder 退款订单信息 + * @return 返回支付方申请退款后的结果 + */ + @Override + public WxRefundResult refund(RefundOrder refundOrder) { + //获取公共参数 + Map parameters = initSubMchId(null); + + setParameters(parameters, "transaction_id", refundOrder.getTradeNo()); + setParameters(parameters, OUT_TRADE_NO, refundOrder.getOutTradeNo()); + setParameters(parameters, "out_refund_no", refundOrder.getRefundNo()); + setParameters(parameters, "reason", refundOrder.getDescription()); + setParameters(parameters, "funds_account", refundOrder); + initNotifyUrl(parameters, refundOrder); + RefundAmount refundAmount = new RefundAmount(); + refundAmount.setRefund(Util.conversionCentAmount(refundOrder.getRefundAmount())); + refundAmount.setTotal(Util.conversionCentAmount(refundOrder.getTotalAmount())); + CurType curType = refundOrder.getCurType(); + if (null != curType) { + refundAmount.setCurrency(curType.getType()); + } + parameters.put("amount", refundAmount); + setParameters(parameters, "amount", refundOrder); + return WxRefundResult.create(doExecute(parameters, WxTransactionType.REFUND)); + } + + + /** + * 查询退款 + * + * @param refundOrder 退款订单单号信息 + * @return 返回支付方查询退款后的结果 + */ + @Override + public Map refundquery(RefundOrder refundOrder) { + String parameters = UriVariables.getMapToParameters(initSubMchId(null)); + return doExecute(parameters, WxTransactionType.REFUND_QUERY, refundOrder.getRefundNo()); + } + + /** + * 下载对账单 + * + * @param billDate 账单时间:日账单格式为yyyy-MM-dd,月账单格式为yyyy-MM。 + * @param billType 账单类型 内部自动转化 {@link BillType} + * @return 返回支付方下载对账单的结果 + */ + @Override + public Map downloadBill(Date billDate, String billType) { + BillType wxBillType = WxBillType.forType(billType); + if (null == wxBillType) { + wxBillType = WxAccountType.forType(billType); + } + return downloadBill(billDate, wxBillType); + } + + /** + * 目前只支持 + * 对账单中涉及金额的字段单位为“元”。 + * + * @param billDate 下载对账单的日期,格式:20140603 + * @param billType 账单类型 {@link WxBillType} 与 {@link WxAccountType} + * @return 返回支付方下载对账单的结果, 如果【账单类型】为gzip的话则返回值中key为data值为gzip的输入流 + */ + public Map downloadBill(Date billDate, BillType billType) { + //获取公共参数 + Map parameters = new HashMap<>(5); + + //目前只支持日账单 + parameters.put("bill_date", DateUtils.formatDate(billDate, DateUtils.YYYYMMDD)); + String fileType = billType.getFileType(); + setParameters(parameters, "tar_type", fileType); + if (billType instanceof WxAccountType) { + setParameters(parameters, "account_type", billType.getType()); + } + else { + initSubMchId(parameters).put("bill_type", billType.getType()); + } + String body = UriVariables.getMapToParameters(parameters); + JSONObject result = doExecute(body, WxTransactionType.valueOf(billType.getCustom())); + String downloadUrl = result.getString("download_url"); + MethodType methodType = MethodType.GET; + HttpStringEntity entity = initHttpStringEntity(downloadUrl, "", methodType.name()); + ResponseEntity responseEntity = requestTemplate.doExecuteEntity(downloadUrl, entity, InputStream.class, methodType); + InputStream inputStream = responseEntity.getBody(); + int statusCode = responseEntity.getStatusCode(); + if (statusCode >= 400) { + try { + String errorText = IOUtils.toString(inputStream); + JSONObject json = JSON.parseObject(errorText); + throw new PayErrorException(new WxPayError(statusCode + "", json.getString(WxConst.MESSAGE), errorText)); + } + catch (IOException e) { + throw new PayErrorException(new WxPayError(statusCode + "", "")); + } + } + Map data = new HashMap<>(); + data.put("file", inputStream); + return result; + } + + + /** + * 转账 + * + * @param order 转账订单 + *
+     *
+     *                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                               注意事项:
+     *                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                               ◆ 当返回错误码为“SYSTEMERROR”时,请不要更换商户订单号,一定要使用原商户订单号重试,否则可能造成重复支付等资金风险。
+     *                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                               ◆ XML具有可扩展性,因此返回参数可能会有新增,而且顺序可能不完全遵循此文档规范,如果在解析回包的时候发生错误,请商户务必不要换单重试,请商户联系客服确认付款情况。如果有新回包字段,会更新到此API文档中。
+     *                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                               ◆ 因为错误代码字段err_code的值后续可能会增加,所以商户如果遇到回包返回新的错误码,请商户务必不要换单重试,请商户联系客服确认付款情况。如果有新的错误码,会更新到此API文档中。
+     *                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                               ◆ 错误代码描述字段err_code_des只供人工定位问题时做参考,系统实现时请不要依赖这个字段来做自动化处理。
+     *                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                               
+ * @return 对应的转账结果 + */ + @Override + public Map transfer(TransferOrder order) { + + + throw new PayErrorException(new WxPayError("", "等待作者实现")); + } + + + /** + * 转账查询 + * + * @param outNo 商户转账订单号 + * @param wxTransferType 微信转账类型,.....这里没办法了只能这样写(┬_┬),请见谅 {@link WxTransferType} + *
+ * @return 对应的转账订单 + */ + @Override + public Map transferQuery(String outNo, String wxTransferType) { + throw new PayErrorException(new WxPayError("", "等待作者实现")); + } + + + /** + * 创建消息 + * + * @param message 支付平台返回的消息 + * @return 支付消息对象 + */ + @Override + public PayMessage createMessage(Map message) { + return WxPayMessage.create(message); + } + + /** + * 微信发红包 + * + * @param redpackOrder 红包实体 + * @return 返回发红包实体后的结果 + */ + @Override + public Map sendredpack(RedpackOrder redpackOrder) { + throw new PayErrorException(new WxPayError("", "等待作者实现")); + } + + + /** + * 查询红包记录 + * 用于商户对已发放的红包进行查询红包的具体信息,可支持普通红包和裂变包 + * 查询红包记录API只支持查询30天内的红包订单,30天之前的红包订单请登录商户平台查询。 + * + * @param mchBillno 商户发放红包的商户订单号 + * @return 返回查询结果 + */ + @Override + public Map gethbinfo(String mchBillno) { + throw new PayErrorException(new WxPayError("", "等待作者实现")); + } + +} diff --git a/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/CertEnvironment.java b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/CertEnvironment.java new file mode 100644 index 0000000..7bc13f2 --- /dev/null +++ b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/CertEnvironment.java @@ -0,0 +1,62 @@ +package com.egzosn.pay.wx.v3.bean; + +import java.security.PrivateKey; +import java.security.PublicKey; + +/** + * 证书模式运行时环境 + * + * @author egan + * email egzosn@gmail.com + * date 2021/07/18.20:29 + */ +public class CertEnvironment { + /** + * 存放私钥 + */ + private PrivateKey privateKey; + + /** + * 存放公钥 + */ + private PublicKey publicKey; + + /** + * 公钥序列 + */ + private String serialNumber; + + + public CertEnvironment() { + } + + public CertEnvironment(PrivateKey privateKey, PublicKey publicKey, String serialNumber) { + this.privateKey = privateKey; + this.publicKey = publicKey; + this.serialNumber = serialNumber; + } + + public PrivateKey getPrivateKey() { + return privateKey; + } + + public void setPrivateKey(PrivateKey privateKey) { + this.privateKey = privateKey; + } + + public PublicKey getPublicKey() { + return publicKey; + } + + public void setPublicKey(PublicKey publicKey) { + this.publicKey = publicKey; + } + + public String getSerialNumber() { + return serialNumber; + } + + public void setSerialNumber(String serialNumber) { + this.serialNumber = serialNumber; + } +} \ No newline at end of file diff --git a/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/WxAccountType.java b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/WxAccountType.java new file mode 100644 index 0000000..fc64193 --- /dev/null +++ b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/WxAccountType.java @@ -0,0 +1,116 @@ +package com.egzosn.pay.wx.v3.bean; + +import com.egzosn.pay.common.bean.BillType; +import com.egzosn.pay.common.util.str.StringUtils; +import com.egzosn.pay.wx.api.WxConst; + +/** + * 资金账户类型 + * + * @author Egan + *
+ * email egzosn@gmail.com
+ * date 2021/2/22
+ * 
+ */ +public enum WxAccountType implements BillType { + /** + * 基本账户, 不填则默认是数据流 + */ + BASIC("BASIC"), + /** + * 基本账户 + * 返回格式为.gzip的压缩包账单 + */ + BASIC_GZIP("BASIC", WxConst.GZIP), + /** + * 运营账户 + */ + OPERATION("OPERATION"), + /** + * 运营账户 + * 返回格式为.gzip的压缩包账单 + */ + OPERATION_GZIP("OPERATION", WxConst.GZIP), + /** + * 手续费账户 + */ + FEES("FEES"), + /** + * 手续费账户 + * 返回格式为.gzip的压缩包账单 + */ + FEES_GZIP("FEES", WxConst.GZIP); + + /** + * 账单类型 + */ + private String type; + /** + * 日期格式化表达式 + */ + private String tarType; + + + + WxAccountType(String type) { + this.type = type; + } + + + WxAccountType(String type, String tarType) { + this.type = type; + this.tarType = tarType; + } + + /** + * 获取类型名称 + * + * @return 类型 + */ + @Override + public String getType() { + return type; + } + + /** + * 获取类型对应的日期格式化表达式 + * + * @return 日期格式化表达式 + */ + @Override + public String getDatePattern() { + return null; + } + + /** + * 获取文件类型 + * + * @return 文件类型 + */ + @Override + public String getFileType() { + return tarType; + } + + + /** + * 返回交易类型 + * + * @return 交易类型 + */ + @Override + public String getCustom() { + return WxTransactionType.FUND_FLOW_BILL.name(); + } + + public static WxAccountType forType(String type) { + for (WxAccountType wxPayBillType : WxAccountType.values()) { + if (wxPayBillType.getType().equals(type) && StringUtils.isEmpty(wxPayBillType.getFileType())) { + return wxPayBillType; + } + } + return null; + } + +} diff --git a/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/WxBillType.java b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/WxBillType.java new file mode 100644 index 0000000..6b0cd7b --- /dev/null +++ b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/WxBillType.java @@ -0,0 +1,113 @@ +package com.egzosn.pay.wx.v3.bean; + +import com.egzosn.pay.common.bean.BillType; +import com.egzosn.pay.common.util.str.StringUtils; +import com.egzosn.pay.wx.api.WxConst; + +/** + * 微信账单类型 + * + * @author Egan + *
+ * email egzosn@gmail.com
+ * date 2021/08/01
+ * 
+ */ +public enum WxBillType implements BillType { + /** + * 返回当日所有订单信息(不含充值退款订单) + */ + ALL("ALL"), + /** + * 返回当日所有订单信息(不含充值退款订单) 返回格式为.gzip的压缩包账单 + */ + ALL_GZIP("ALL", WxConst.GZIP), + /** + * 返回当日成功支付的订单(不含充值退款订单) + */ + SUCCESS(WxConst.SUCCESS), + /** + * 返回当日成功支付的订单(不含充值退款订单) + * 返回格式为.gzip的压缩包账单 + */ + SUCCESS_GZIP(WxConst.SUCCESS, WxConst.GZIP), + /** + * 返回当日退款订单(不含充值退款订单) + */ + REFUND("REFUND"), + /** + * 返回当日退款订单(不含充值退款订单) + * 返回格式为.gzip的压缩包账单 + */ + REFUND_GZIP("REFUND", WxConst.GZIP); + + /** + * 账单类型 + */ + private String type; + /** + * 日期格式化表达式 + */ + private String tarType; + + WxBillType(String type) { + this.type = type; + } + + + WxBillType(String type, String tarType) { + this.type = type; + this.tarType = tarType; + } + + /** + * 获取类型名称 + * + * @return 类型 + */ + @Override + public String getType() { + return type; + } + + /** + * 获取类型对应的日期格式化表达式 + * + * @return 日期格式化表达式 + */ + @Override + public String getDatePattern() { + return null; + } + + /** + * 获取文件类型 + * + * @return 文件类型 + */ + @Override + public String getFileType() { + return tarType; + } + + + /** + * 自定义属性 + * + * @return 自定义属性 + */ + @Override + public String getCustom() { + return WxTransactionType.TRADE_BILL.name(); + } + + public static WxBillType forType(String type) { + for (WxBillType wxPayBillType : WxBillType.values()) { + if (wxPayBillType.getType().equals(type) && StringUtils.isEmpty(wxPayBillType.getFileType())) { + return wxPayBillType; + } + } + return null; + } + +} diff --git a/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/WxTransactionType.java b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/WxTransactionType.java new file mode 100644 index 0000000..46ed6a4 --- /dev/null +++ b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/WxTransactionType.java @@ -0,0 +1,156 @@ +package com.egzosn.pay.wx.v3.bean; + +import java.util.Map; + +import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.serializer.SerializerFeature; +import com.egzosn.pay.common.bean.MethodType; +import com.egzosn.pay.common.bean.PayOrder; +import com.egzosn.pay.common.bean.TransactionType; +import com.egzosn.pay.common.util.MapGen; +import com.egzosn.pay.common.util.str.StringUtils; +import com.egzosn.pay.wx.v3.bean.order.H5Info; +import com.egzosn.pay.wx.v3.bean.order.SceneInfo; +import com.egzosn.pay.wx.v3.utils.WxConst; + +/** + * 微信交易类型 + * + * @author egan + *

+ * email egzosn@gmail.com + * date 2016/10/19 22:58 + */ +public enum WxTransactionType implements TransactionType { + /** + * 获取证书. + */ + CERT("certificates", MethodType.GET), + /** + * 公众号支付 + */ + JSAPI("pay{partner}/transactions/jsapi", MethodType.POST) { + @Override + public void setAttribute(Map parameters, PayOrder order) { + String key = parameters.containsKey("sub_mchid") ? "sub_openid" : "openid"; + MapGen mapGen = new MapGen(key, order.getOpenid()); + parameters.put("payer", mapGen.getAttr()); + } + }, + /** + * 二维码支付 + */ + NATIVE("pay{partner}/transactions/native", MethodType.POST, true), + /** + * 移动支付 + */ + APP("pay{partner}/transactions/app", MethodType.POST), + /** + * H5支付 + */ + H5("pay{partner}/transactions/h5", MethodType.POST, true) { + @Override + public void setAttribute(Map parameters, PayOrder order) { + Object sceneInfoObj = parameters.get(WxConst.SCENE_INFO); + SceneInfo sceneInfo = null; + if (null == sceneInfoObj) { + sceneInfo = new SceneInfo(); + } + else if (sceneInfoObj instanceof SceneInfo) { + sceneInfo = (SceneInfo) sceneInfoObj; + } + else { + String jsonString = JSON.toJSONString(sceneInfoObj, SerializerFeature.WriteMapNullValue); + sceneInfo = JSON.parseObject(jsonString, SceneInfo.class); + } + String billCreateIp = order.getSpbillCreateIp(); + if (StringUtils.isNotEmpty(billCreateIp)) { + sceneInfo.setPayerClientIp(billCreateIp); + } + H5Info h5Info = sceneInfo.getH5Info(); + if (null == h5Info) { + sceneInfo.setH5Info(new H5Info(order.getWapName(), order.getWapUrl())); + } + } + + }, + + /** + * 查询订单 + * 兼容V2的方式,通过入参来决定 + */ + QUERY("pay{partner}/transactions/", MethodType.GET), + /** + * 微信支付订单号查询 + */ + QUERY_TRANSACTION_ID("pay{partner}/transactions/id/{transaction_id}", MethodType.GET), + /** + * 商户订单号查询 + */ + QUERY_OUT_TRADE_NO("pay{partner}/transactions/out-trade-no/{out_trade_no}", MethodType.GET), + /** + * 关闭订单 + */ + CLOSE("pay{partner}/transactions/out-trade-no/{out_trade_no}/close", MethodType.POST), + /** + * 申请退款 + */ + REFUND("refund/domestic/refunds", MethodType.POST), + /** + * 查询退款 + */ + REFUND_QUERY("refund/domestic/refunds/{out_refund_no}", MethodType.GET), + /** + * 申请交易账单 + */ + TRADE_BILL("bill/tradebill", MethodType.GET), + /** + * 申请资金账单 + */ + FUND_FLOW_BILL("bill/fundflowbill", MethodType.GET), + + + ; + + WxTransactionType(String type, MethodType method) { + this(type, method, false); + + } + + WxTransactionType(String type, MethodType method, boolean back) { + this.type = type; + this.method = method; + this.back = back; + } + + private String type; + private MethodType method; + /** + * 是否直接返回 + */ + private boolean back; + + + @Override + public String getType() { + return type; + } + + @Override + public String getMethod() { + return this.method.name(); + } + + /** + * 是否直接返回 + * + * @return 是否直接返回 + */ + public boolean isReturn() { + return back; + } + + public void setAttribute(Map parameters, PayOrder order) { + + } +} diff --git a/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/order/Amount.java b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/order/Amount.java new file mode 100644 index 0000000..7c04136 --- /dev/null +++ b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/order/Amount.java @@ -0,0 +1,75 @@ +package com.egzosn.pay.wx.v3.bean.order; + + +import java.math.BigDecimal; + +import com.egzosn.pay.common.bean.CurType; +import com.egzosn.pay.common.util.Util; + +/** + * 订单金额信息 + * @author Egan + * email egzosn@gmail.com + * date 2021/8/1 + */ +public class Amount { + + /** + * 订单总金额,单位为分。 + */ + private Integer total; + /** + * 货币类型 CNY:人民币,境内商户号仅支持人民币。 + * {@link com.egzosn.pay.common.bean.CurType} + */ + private String currency; + + public Amount() { + } + + public Amount(Integer total) { + this.total = total; + } + + public Amount(Integer total, String currency) { + this.total = total; + this.currency = currency; + } + + public Integer getTotal() { + return total; + } + + public void setTotal(Integer total) { + this.total = total; + } + + public String getCurrency() { + return currency; + } + + public void setCurrency(String currency) { + this.currency = currency; + } + /** + * 订单金额信息 + * + * @param order 支付订单 + * @return 订单金额信息 + */ + /** + * 订单金额信息 + * @param total 金额,这里单位为元 + * @param curType 货币类型 + * @return 订单金额信息 + */ + public static Amount getAmount(BigDecimal total, CurType curType ) { + // 总金额单位为分 + Amount amount = new Amount(Util.conversionCentAmount(total)); + if (null != curType) { + amount.setCurrency(curType.getType()); + } + + return amount; + } +} diff --git a/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/order/Detail.java b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/order/Detail.java new file mode 100644 index 0000000..ede9be6 --- /dev/null +++ b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/order/Detail.java @@ -0,0 +1,51 @@ +package com.egzosn.pay.wx.v3.bean.order; + +import java.util.List; + +import com.alibaba.fastjson.annotation.JSONField; + +/** + * 优惠功能 + * @author Egan + * email egzosn@gmail.com + * date 2021/8/1 + */ +public class Detail { + /** + * 订单原价 + */ + @JSONField(name = "cost_price") + private int costPrice; + /** + * 商家小票 + */ + @JSONField(name = "invoice_id") + private String invoiceId; + + @JSONField(name = "goods_detail") + private List goodsDetail; + + public int getCostPrice() { + return costPrice; + } + + public void setCostPrice(int costPrice) { + this.costPrice = costPrice; + } + + public String getInvoiceId() { + return invoiceId; + } + + public void setInvoiceId(String invoiceId) { + this.invoiceId = invoiceId; + } + + public List getGoodsDetail() { + return goodsDetail; + } + + public void setGoodsDetail(List goodsDetail) { + this.goodsDetail = goodsDetail; + } +} diff --git a/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/order/From.java b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/order/From.java new file mode 100644 index 0000000..84812ae --- /dev/null +++ b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/order/From.java @@ -0,0 +1,39 @@ +package com.egzosn.pay.wx.v3.bean.order; + +/** + * 退款出资的账户类型及金额信息 + * @author Egan + * email egzosn@gmail.com + * date 2021/8/1 + */ +public class From { + + /** + * 出资账户类型 + * 下面枚举值多选一。 + * 枚举值: + * AVAILABLE : 可用余额 + * UNAVAILABLE : 不可用余额 + */ + private String account; + /** + * 对应账户出资金额 + */ + private int amount; + + public String getAccount() { + return account; + } + + public void setAccount(String account) { + this.account = account; + } + + public int getAmount() { + return amount; + } + + public void setAmount(int amount) { + this.amount = amount; + } +} diff --git a/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/order/GoodsDetail.java b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/order/GoodsDetail.java new file mode 100644 index 0000000..e1155d0 --- /dev/null +++ b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/order/GoodsDetail.java @@ -0,0 +1,84 @@ +package com.egzosn.pay.wx.v3.bean.order; + +import com.alibaba.fastjson.annotation.JSONField; + +/** + * 单品列表信息 + * @author Egan + * email egzosn@gmail.com + * date 2021/8/1 + */ +public class GoodsDetail { + + + /** + * 商户侧商品编码 + */ + @JSONField(name = "merchant_goods_id") + private String merchantGoodsId; + + /** + * 微信侧商品编码 + */ + @JSONField(name = "wechatpay_goods_id") + private String wechatpayGoodsId ; + + + /** + * 商品名称 + */ + @JSONField(name = "goods_name") + private String goodsName; + /** + * 商品数量 + */ + + private int quantity; + /** + * 商品单价 + * 商品单价,单位为分 + */ + @JSONField(name = "unit_price") + private int unitPrice ; + + + public String getMerchantGoodsId() { + return merchantGoodsId; + } + + public void setMerchantGoodsId(String merchantGoodsId) { + this.merchantGoodsId = merchantGoodsId; + } + + public String getWechatpayGoodsId() { + return wechatpayGoodsId; + } + + public void setWechatpayGoodsId(String wechatpayGoodsId) { + this.wechatpayGoodsId = wechatpayGoodsId; + } + + public String getGoodsName() { + return goodsName; + } + + public void setGoodsName(String goodsName) { + this.goodsName = goodsName; + } + + public int getQuantity() { + return quantity; + } + + public void setQuantity(int quantity) { + this.quantity = quantity; + } + + public int getUnitPrice() { + return unitPrice; + } + + public void setUnitPrice(int unitPrice) { + this.unitPrice = unitPrice; + } +} diff --git a/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/order/H5Info.java b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/order/H5Info.java new file mode 100644 index 0000000..03e5f71 --- /dev/null +++ b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/order/H5Info.java @@ -0,0 +1,94 @@ +package com.egzosn.pay.wx.v3.bean.order; + +import com.alibaba.fastjson.annotation.JSONField; + +/** + * H5场景信息 + * + * @author Egan + * email egzosn@gmail.com + * date 2021/8/1 + */ +public class H5Info { + + /** + * 场景类型 + * 示例值:iOS, Android, Wap + */ + private String type; + /** + * 应用名称 + * 示例值:王者荣耀 + */ + @JSONField(name = "app_name") + private String appName; + /** + * 网站URL + * 示例值:https://pay.qq.com + */ + @JSONField(name = "app_url") + private String appUrl; + /** + * iOS平台BundleID + * 示例值:com.tencent.wzryiOS + */ + @JSONField(name = "bundle_id") + private String bundleId; + /** + * Android平台PackageName + * 示例值:com.tencent.tmgp.sgame + */ + @JSONField(name = "package_name") + private String packageName; + + public H5Info() { + this.type = "Wap"; + } + + + public H5Info(String appName, String appUrl) { + this(); + this.appName = appName; + this.appUrl = appUrl; + } + + public String getType() { + return type; + } + + public void setType(String type) { + this.type = type; + } + + public String getAppName() { + return appName; + } + + public void setAppName(String appName) { + this.appName = appName; + } + + public String getAppUrl() { + return appUrl; + } + + public void setAppUrl(String appUrl) { + this.appUrl = appUrl; + } + + public String getBundleId() { + return bundleId; + } + + public void setBundleId(String bundleId) { + this.bundleId = bundleId; + } + + public String getPackageName() { + return packageName; + } + + public void setPackageName(String packageName) { + this.packageName = packageName; + } +} diff --git a/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/order/RefundAmount.java b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/order/RefundAmount.java new file mode 100644 index 0000000..fbac901 --- /dev/null +++ b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/order/RefundAmount.java @@ -0,0 +1,113 @@ +package com.egzosn.pay.wx.v3.bean.order; + +import java.util.List; + +import com.alibaba.fastjson.annotation.JSONField; + +/** + * 退款金额 + * + * @author Egan + * email egzosn@gmail.com + * date 2021/8/1 + */ +public class RefundAmount extends Amount { + + + /** + * 退款金额,单位分 + */ + private int refund; + + /** + * 退款出资的账户类型及金额信息 + */ + private List from; + + /** + * 用户支付金额,单位分 + */ + @JSONField(name = "payer_total") + private int payerTotal; + /** + * 用户退款金额 + * 退款给用户的金额,不包含所有优惠券金额 + */ + @JSONField(name = "payer_refund") + private int payerRefund; + /** + * 应结退款金额 + * 去掉非充值代金券退款金额后的退款金额,单位为分,退款金额=申请退款金额-非充值代金券退款金额,退款金额<=申请退款金额 + */ + @JSONField(name = "settlement_refund") + private int settlementRefund; + /** + * 应结订单金额 + * 应结订单金额=订单金额-免充值代金券金额,应结订单金额<=订单金额,单位为分 + */ + @JSONField(name = "settlement_total") + private int settlementTotal; + /** + * 优惠退款金额 + * 优惠退款金额<=退款金额,退款金额-代金券或立减优惠退款金额为现金,说明详见代金券或立减优惠,单位为分 + */ + @JSONField(name = "discount_refund") + private int discountRefund; + + + public int getRefund() { + return refund; + } + + public void setRefund(int refund) { + this.refund = refund; + } + + public List getFrom() { + return from; + } + + public void setFrom(List from) { + this.from = from; + } + + public int getPayerTotal() { + return payerTotal; + } + + public void setPayerTotal(int payerTotal) { + this.payerTotal = payerTotal; + } + + public int getPayerRefund() { + return payerRefund; + } + + public void setPayerRefund(int payerRefund) { + this.payerRefund = payerRefund; + } + + public int getSettlementRefund() { + return settlementRefund; + } + + public void setSettlementRefund(int settlementRefund) { + this.settlementRefund = settlementRefund; + } + + public int getSettlementTotal() { + return settlementTotal; + } + + public void setSettlementTotal(int settlementTotal) { + this.settlementTotal = settlementTotal; + } + + public int getDiscountRefund() { + return discountRefund; + } + + public void setDiscountRefund(int discountRefund) { + this.discountRefund = discountRefund; + } +} diff --git a/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/order/SceneInfo.java b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/order/SceneInfo.java new file mode 100644 index 0000000..055cf35 --- /dev/null +++ b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/order/SceneInfo.java @@ -0,0 +1,67 @@ +package com.egzosn.pay.wx.v3.bean.order; + +import com.alibaba.fastjson.annotation.JSONField; + +/** + * @author Egan + * email egzosn@gmail.com + * date 2021/8/1 + */ +public class SceneInfo { + + /** + * 用户的客户端IP,支持IPv4和IPv6两种格式的IP地址。 + */ + @JSONField(name = "payer_client_ip") + private String payerClientIp; + /** + * 商户端设备号 + */ + @JSONField(name = "device_id") + private String deviceId; + + /** + * 商户门店信息 + */ + @JSONField(name = "store_info ") + private StoreInfo storeInfo; + /** + * H5场景信息 + */ + @JSONField(name = "h5_info") + private H5Info h5Info; + + + + public String getPayerClientIp() { + return payerClientIp; + } + + public void setPayerClientIp(String payerClientIp) { + this.payerClientIp = payerClientIp; + } + + public String getDeviceId() { + return deviceId; + } + + public void setDeviceId(String deviceId) { + this.deviceId = deviceId; + } + + public StoreInfo getStoreInfo() { + return storeInfo; + } + + public void setStoreInfo(StoreInfo storeInfo) { + this.storeInfo = storeInfo; + } + + public H5Info getH5Info() { + return h5Info; + } + + public void setH5Info(H5Info h5Info) { + this.h5Info = h5Info; + } +} diff --git a/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/order/StoreInfo.java b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/order/StoreInfo.java new file mode 100644 index 0000000..271d5e6 --- /dev/null +++ b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/order/StoreInfo.java @@ -0,0 +1,62 @@ +package com.egzosn.pay.wx.v3.bean.order; + +import com.alibaba.fastjson.annotation.JSONField; + +/** + * 商户门店信息 + * @author Egan0 + * email egzosn@gmail.com + * date 2021/8/1 + */ +public class StoreInfo { + /** + * 商户侧门店编号 + */ + private String id; + /** + * 商户侧门店名称 + */ + private String name; + /** + * 地区编码,详细请见省市区编号对照表。 + * https://pay.weixin.qq.com/wiki/doc/apiv3/terms_definition/chapter1_1_3.shtml#part-5 + */ + @JSONField(name = "area_code") + private String areaCode; + /** + * 详细的商户门店地址 + */ + private String address; + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getAreaCode() { + return areaCode; + } + + public void setAreaCode(String areaCode) { + this.areaCode = areaCode; + } + + public String getAddress() { + return address; + } + + public void setAddress(String address) { + this.address = address; + } +} diff --git a/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/response/WxRefundResult.java b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/response/WxRefundResult.java new file mode 100644 index 0000000..fc9fb19 --- /dev/null +++ b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/response/WxRefundResult.java @@ -0,0 +1,324 @@ +package com.egzosn.pay.wx.v3.bean.response; + +import java.math.BigDecimal; +import java.util.Map; + +import com.alibaba.fastjson.JSONObject; +import com.alibaba.fastjson.annotation.JSONField; +import com.egzosn.pay.common.bean.BaseRefundResult; +import com.egzosn.pay.common.bean.CurType; +import com.egzosn.pay.common.bean.DefaultCurType; +import com.egzosn.pay.common.util.Util; +import com.egzosn.pay.wx.v3.bean.order.RefundAmount; + +/** + * 微信退款结果 + * + * @author Egan + *

+ * email egzosn@gmail.com
+ * date 2020/8/16 21:29
+ * 
+ */ +public class WxRefundResult extends BaseRefundResult { + + /** + * 500 SYSTEM_ERROR 系统超时 请不要更换商户退款单号,请使用相同参数再次调用API。 + * 403 USER_ACCOUNT_ABNORMAL 退款请求失败 此状态代表退款申请失败,商户可自行处理退款。 + * 403 NOT_ENOUGH 余额不足 此状态代表退款申请失败,商户可根据具体的错误提示做相应的处理。 + * 400 PARAM_ERROR 参数错误 请求参数错误,请重新检查再调用申请退款接口 + * 404 MCH_NOT_EXISTS MCHID不存在 请检查MCHID是否正确 + * 404 RESOURCE_NOT_EXISTS 订单号不存在 请检查你的订单号是否正确且是否已支付,未支付的订单不能发起退款 + * 401 SIGN_ERROR 签名错误 请检查签名参数和方法是否都符合签名算法要求 + * 429 FREQUENCY_LIMITED 频率限制 该笔退款未受理,请降低频率后重试 + * 400 INVALID_REQUEST 请求参数符合参数格式,但不符合业务规则 此状态代表退款申请失败,商户可根据具体的错误提示做相应的处理。 + * 403 NO_AUTH 没有退款权限 此状态代表退款申请失败,请检查是否有该笔订单的退款权限 + */ + private String code; + /** + * 返回信息 + */ + private String message; + /** + * 微信退款单号 + */ + @JSONField(name = "refund_id") + private String refundId; + /** + * 商户退款单号 + */ + @JSONField(name = "out_refund_no") + private String outRefundNo; + /** + * 微信订单号 + */ + @JSONField(name = "transaction_id") + private String transactionId; + /** + * 商户订单号 + */ + @JSONField(name = "out_trade_no") + private String outTradeNo; + + + /** + * 退款渠道 + * 枚举值: + * ORIGINAL:原路退款 + * BALANCE:退回到余额 + * OTHER_BALANCE:原账户异常退到其他余额账户 + * OTHER_BANKCARD:原银行卡异常退到其他银行卡 + * 示例值:ORIGINAL + */ + private String channel; + + /** + * 退款入账账户 + * 取当前退款单的退款入账方,有以下几种情况: + * 1)退回银行卡:{银行名称}{卡类型}{卡尾号} + * 2)退回支付用户零钱:支付用户零钱 + * 3)退还商户:商户基本账户商户结算银行账户 + * 4)退回支付用户零钱通:支付用户零钱通 + * 示例值:招商银行信用卡0403 + */ + @JSONField(name = "user_received_account") + private String userReceivedAccount; + /** + * 退款成功时间 + * 退款成功时间,当退款状态为退款成功时有返回。 + * 示例值:2020-12-01T16:18:12+08:00 + */ + @JSONField(name = "success_time") + private String successTime; + /** + * 退款创建时间 + */ + @JSONField(name = "create_time") + private String createTime; + + /** + * 退款状态 + * 退款到银行发现用户的卡作废或者冻结了,导致原路退款银行卡失败,可前往服务商平台-交易中心,手动处理此笔退款。 + * 枚举值: + * SUCCESS:退款成功 + * CLOSED:退款关闭 + * PROCESSING:退款处理中 + * ABNORMAL:退款异常 + * 示例值:SUCCESS + */ + private String status; + + /** + * 资金账户 + * 退款所使用资金对应的资金账户类型 + * 枚举值: + * UNSETTLED : 未结算资金 + * AVAILABLE : 可用余额 + * UNAVAILABLE : 不可用余额 + * OPERATION : 运营户 + * BASIC : 基本账户(含可用余额和不可用余额) + */ + @JSONField(name = "funds_account") + private String fundsAccount; + + /** + * 金额详细信息 + */ + private RefundAmount amount; + + /** + * 获取退款请求结果状态码 + * + * @return 状态码 + */ + @Override + public String getCode() { + return code; + } + + /** + * 获取退款请求结果状态提示信息 + * + * @return 提示信息 + */ + @Override + public String getMsg() { + return message; + } + + /** + * 返回业务结果状态码 + * + * @return 业务结果状态码 + */ + @Override + public String getResultCode() { + return status; + } + + /** + * 返回业务结果状态提示信息 + * + * @return 业务结果状态提示信息 + */ + @Override + public String getResultMsg() { + return message; + } + + /** + * 退款金额, 金额元 + * + * @return 退款金额 + */ + @Override + public BigDecimal getRefundFee() { + + return new BigDecimal(amount.getRefund()).divide(Util.HUNDRED, 2, BigDecimal.ROUND_HALF_UP); + } + + /** + * 退款币种信息 + * + * @return 币种信息 + */ + @Override + public CurType getRefundCurrency() { + return DefaultCurType.valueOf(amount.getCurrency()); + } + + /** + * 支付平台交易号 + * 发起支付时 支付平台(如支付宝)返回的交易订单号 + * + * @return 支付平台交易号 + */ + @Override + public String getTradeNo() { + return transactionId; + } + + /** + * 支付订单号 + * 发起支付时,用户系统的订单号 + * + * @return 支付订单号 + */ + @Override + public String getOutTradeNo() { + return outTradeNo; + } + + /** + * 商户退款单号 + * + * @return 商户退款单号 + */ + @Override + public String getRefundNo() { + return outRefundNo; + } + + + public String getTransactionId() { + return transactionId; + } + + public void setTransactionId(String transactionId) { + this.transactionId = transactionId; + } + + public void setOutTradeNo(String outTradeNo) { + this.outTradeNo = outTradeNo; + } + + public String getOutRefundNo() { + return outRefundNo; + } + + public void setOutRefundNo(String outRefundNo) { + this.outRefundNo = outRefundNo; + } + + public String getRefundId() { + return refundId; + } + + public void setRefundId(String refundId) { + this.refundId = refundId; + } + + public void setCode(String code) { + this.code = code; + } + + public String getMessage() { + return message; + } + + public void setMessage(String message) { + this.message = message; + } + + public String getChannel() { + return channel; + } + + public void setChannel(String channel) { + this.channel = channel; + } + + public String getUserReceivedAccount() { + return userReceivedAccount; + } + + public void setUserReceivedAccount(String userReceivedAccount) { + this.userReceivedAccount = userReceivedAccount; + } + + public String getSuccessTime() { + return successTime; + } + + public void setSuccessTime(String successTime) { + this.successTime = successTime; + } + + public String getCreateTime() { + return createTime; + } + + public void setCreateTime(String createTime) { + this.createTime = createTime; + } + + public String getStatus() { + return status; + } + + public void setStatus(String status) { + this.status = status; + } + + public String getFundsAccount() { + return fundsAccount; + } + + public void setFundsAccount(String fundsAccount) { + this.fundsAccount = fundsAccount; + } + + public RefundAmount getAmount() { + return amount; + } + + public void setAmount(RefundAmount amount) { + this.amount = amount; + } + + public static final WxRefundResult create(Map result) { + WxRefundResult refundResult = new JSONObject(result).toJavaObject(WxRefundResult.class); + refundResult.setAttrs(result); + return refundResult; + } +} diff --git a/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/utils/AntCertificationUtil.java b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/utils/AntCertificationUtil.java new file mode 100644 index 0000000..1e06084 --- /dev/null +++ b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/utils/AntCertificationUtil.java @@ -0,0 +1,66 @@ +package com.egzosn.pay.wx.v3.utils; + +import java.io.IOException; +import java.io.InputStream; +import java.security.GeneralSecurityException; +import java.security.KeyStore; +import java.security.KeyStoreException; +import java.security.PrivateKey; +import java.security.PublicKey; +import java.security.cert.X509Certificate; + +import com.egzosn.pay.common.exception.PayErrorException; +import com.egzosn.pay.wx.bean.WxPayError; +import com.egzosn.pay.wx.v3.bean.CertEnvironment; + +/** + * 证书文件可信校验 + * + * @author egan + * email egzosn@gmail.com + * date 2021/07/18.20:29 + */ +public final class AntCertificationUtil { + private AntCertificationUtil() { + } + + private static final KeyStore PKCS12_KEY_STORE; + + static { + try { + PKCS12_KEY_STORE = KeyStore.getInstance("PKCS12"); + } + catch (KeyStoreException e) { + throw new PayErrorException(new WxPayError("500", " keystore 初始化失败")); + } + } + + /** + * 获取公私钥. + * + * @param keyCertStream 商户API证书 + * @param keyAlias 证书别名 + * @param keyPass 证书对应的密码 + * @return 证书信息集合 + */ + public static CertEnvironment initCertification(InputStream keyCertStream, String keyAlias, String keyPass) { + + char[] pem = keyPass.toCharArray(); + try { + PKCS12_KEY_STORE.load(keyCertStream, pem); + X509Certificate certificate = (X509Certificate) PKCS12_KEY_STORE.getCertificate(keyAlias); + certificate.checkValidity(); + String serialNumber = certificate.getSerialNumber().toString(16).toUpperCase(); + PublicKey publicKey = certificate.getPublicKey(); + PrivateKey privateKey = (PrivateKey) PKCS12_KEY_STORE.getKey(keyAlias, pem); + return new CertEnvironment(privateKey, publicKey, serialNumber); + } + catch (GeneralSecurityException e) { + throw new PayErrorException(new WxPayError("500", "获取公私钥失败"), e); + } + catch (IOException e) { + throw new PayErrorException(new WxPayError("500", "私钥证书流加载失败"), e); + } + + } +} diff --git a/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/utils/WxConst.java b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/utils/WxConst.java new file mode 100644 index 0000000..fe0d1e0 --- /dev/null +++ b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/utils/WxConst.java @@ -0,0 +1,41 @@ +package com.egzosn.pay.wx.v3.utils; + +/** + * 微信所需常量 + * + * @author Egan + * email egzosn@gmail.com + * date 2021/8/1 + */ +public final class WxConst { + + private WxConst() { + } + + /** + * 微信默认请求地址 + */ + public static final String URI = "https://api.mch.weixin.qq.com/v3/"; + /** + * 证书别名 + */ + public static final String CERT_ALIAS = "Tenpay Certificate"; + /** + * 沙箱 + */ + public static final String SANDBOXNEW = "sandboxnew/"; + public static final String APPID = "appid"; + public static final String MCH_ID = "mch_id"; + public static final String SUB_MCH_ID = "sub_mchid"; + public static final String SP_MCH_ID = "sp_mchid"; + public static final String OUT_TRADE_NO = "out_trade_no"; + public static final String NOTIFY_URL = "notify_url"; + + public static final String TOKEN_PATTERN = "mchid=\"%s\",nonce_str=\"%s\",timestamp=\"%d\",serial_no=\"%s\",signature=\"%s\""; + + public static final String SCHEMA = "WECHATPAY2-SHA256-RSA2048 "; + + public static final String CODE = "code"; + public static final String MESSAGE = "message"; + public static final String SCENE_INFO = "scene_info"; +} -- Gitee From 5ebd70472382995c89d622fc8b98afd27b330360 Mon Sep 17 00:00:00 2001 From: egan Date: Fri, 6 Aug 2021 04:40:29 +0000 Subject: [PATCH 211/299] update README.md. --- README.md | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 70468d6..c7a2071 100644 --- a/README.md +++ b/README.md @@ -69,7 +69,12 @@ android 例子 [pay-java-android](https://gitee.com/egzosn/pay-java-android) E-Mail:egzosn@gmail.com -QQ群:542193977 + **QQ群:** + + +1. pay-java(1群): 542193977(已满) +2. pay-java(2群):766275051 + 微信群: ![微信群](https://egzosn.gitee.io/pay-java-parent/wx.jpg "wx.jpg") -- Gitee From 44c72fa710a6520290e0474b3b685044bac3354d Mon Sep 17 00:00:00 2001 From: egan Date: Sun, 8 Aug 2021 23:04:12 +0800 Subject: [PATCH 212/299] =?UTF-8?q?=E4=BB=A3=E7=A0=81=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../egzosn/pay/ali/bean/CertEnvironment.java | 10 +-- pay-java-ali/src/test/java/PayTest.java | 4 +- .../pay/common/util/sign/CertDescriptor.java | 73 ++++++++++++------- pay-java-demo/README.md | 12 +-- .../pay/demo/controller/AliPayController.java | 4 +- .../pay/demo/controller/WxPayController.java | 4 +- pay-java-fuiou/src/test/java/PayTest.java | 4 +- pay-java-payoneer/test/java/PayTest.java | 4 +- .../src/test/java/PayTest.java | 4 +- 9 files changed, 71 insertions(+), 48 deletions(-) diff --git a/pay-java-ali/src/main/java/com/egzosn/pay/ali/bean/CertEnvironment.java b/pay-java-ali/src/main/java/com/egzosn/pay/ali/bean/CertEnvironment.java index f8b262c..79dcb82 100644 --- a/pay-java-ali/src/main/java/com/egzosn/pay/ali/bean/CertEnvironment.java +++ b/pay-java-ali/src/main/java/com/egzosn/pay/ali/bean/CertEnvironment.java @@ -44,7 +44,7 @@ public class CertEnvironment { /** * 缓存的不同支付宝公钥证书序列号对应的支付宝公钥 */ - private Map cachedAliPayPublicKey = new ConcurrentHashMap(); + private static final Map CACHED_ALI_PAY_PUBLIC_KEY = new ConcurrentHashMap(); /** * 构造证书运行环境 @@ -64,7 +64,7 @@ public class CertEnvironment { String aliPayPublicCertContent = AntCertificationUtil.readFromInputStream(aliPayCert); aliPayPublicKeySN = AntCertificationUtil.getCertSN(aliPayPublicCertContent); - cachedAliPayPublicKey.put(aliPayPublicKeySN, + CACHED_ALI_PAY_PUBLIC_KEY.put(aliPayPublicKeySN, AntCertificationUtil.getCertPublicKey(aliPayPublicCertContent)); } @@ -79,11 +79,11 @@ public class CertEnvironment { public String getAliPayPublicKey(String sn) { //如果没有指定sn,则默认取缓存中的第一个值 if (StringUtils.isEmpty(sn)) { - return cachedAliPayPublicKey.values().iterator().next(); + return CACHED_ALI_PAY_PUBLIC_KEY.values().iterator().next(); } - if (cachedAliPayPublicKey.containsKey(sn)) { - return cachedAliPayPublicKey.get(sn); + if (CACHED_ALI_PAY_PUBLIC_KEY.containsKey(sn)) { + return CACHED_ALI_PAY_PUBLIC_KEY.get(sn); } else { //网关在支付宝公钥证书变更前,一定会确认通知到商户并在商户做出反馈后,才会更新该商户的支付宝公钥证书 //TODO: 后续可以考虑加入自动升级支付宝公钥证书逻辑,注意并发更新冲突问题 diff --git a/pay-java-ali/src/test/java/PayTest.java b/pay-java-ali/src/test/java/PayTest.java index b22ba22..416ec15 100644 --- a/pay-java-ali/src/test/java/PayTest.java +++ b/pay-java-ali/src/test/java/PayTest.java @@ -16,8 +16,8 @@ import java.util.UUID; * * 支付宝测试 * @author egan - * @email egzosn@gmail.com - * @date 2017/8/18 + * email egzosn@gmail.com + * date 2017/8/18 */ public class PayTest { diff --git a/pay-java-common/src/main/java/com/egzosn/pay/common/util/sign/CertDescriptor.java b/pay-java-common/src/main/java/com/egzosn/pay/common/util/sign/CertDescriptor.java index 2419571..947c7e1 100644 --- a/pay-java-common/src/main/java/com/egzosn/pay/common/util/sign/CertDescriptor.java +++ b/pay-java-common/src/main/java/com/egzosn/pay/common/util/sign/CertDescriptor.java @@ -14,20 +14,25 @@ */ package com.egzosn.pay.common.util.sign; -import com.egzosn.pay.common.util.str.StringUtils; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; -import java.security.*; +import java.security.KeyStore; +import java.security.KeyStoreException; +import java.security.NoSuchAlgorithmException; +import java.security.PrivateKey; +import java.security.UnrecoverableKeyException; import java.security.cert.CertificateException; import java.security.cert.CertificateFactory; import java.security.cert.X509Certificate; import java.util.Enumeration; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.egzosn.pay.common.util.str.StringUtils; + /** * acpsdk证书工具类,主要用于对证书的加载和使用 @@ -69,13 +74,16 @@ public class CertDescriptor { if (LOG.isWarnEnabled()) { LOG.warn("[CertId=" + encryptCertTemp.getSerialNumber().toString() + "]"); } - } catch (CertificateException e) { + } + catch (CertificateException e) { LOG.error("InitCert Error", e); - } finally { + } + finally { if (null != certIn) { try { certIn.close(); - } catch (IOException e) { + } + catch (IOException e) { LOG.error(e.toString()); } } @@ -96,7 +104,8 @@ public class CertDescriptor { try { in = new FileInputStream(path); encryptCertTemp = initCert(in); - } catch (FileNotFoundException e) { + } + catch (FileNotFoundException e) { LOG.error("InitCert Error File Not Found", e); } return encryptCertTemp; @@ -115,16 +124,18 @@ public class CertDescriptor { if (aliasenum.hasMoreElements()) { keyAlias = aliasenum.nextElement(); } - PrivateKey privateKey = (PrivateKey) keyStore.getKey(keyAlias, - pwd.toCharArray()); + PrivateKey privateKey = (PrivateKey) keyStore.getKey(keyAlias, pwd.toCharArray()); return privateKey; - } catch (KeyStoreException e) { + } + catch (KeyStoreException e) { LOG.error("getSignCertPrivateKey Error", e); return null; - } catch (UnrecoverableKeyException e) { + } + catch (UnrecoverableKeyException e) { LOG.error("getSignCertPrivateKey Error", e); return null; - } catch (NoSuchAlgorithmException e) { + } + catch (NoSuchAlgorithmException e) { LOG.error("getSignCertPrivateKey Error", e); return null; } @@ -145,7 +156,8 @@ public class CertDescriptor { } X509Certificate cert = (X509Certificate) keyStore.getCertificate(keyAlias); return cert.getSerialNumber().toString(); - } catch (Exception e) { + } + catch (Exception e) { LOG.error("getSignCertId Error", e); return null; } @@ -168,7 +180,8 @@ public class CertDescriptor { if (LOG.isInfoEnabled()) { LOG.info("InitSignCert Successful. CertId=[" + getSignCertId() + "]"); } - } catch (IOException e) { + } + catch (IOException e) { LOG.error("InitSignCert Error", e); } } @@ -231,14 +244,17 @@ public class CertDescriptor { ks.load(fxKeyFile, nPassword); } return ks; - } catch (Exception e) { + } + catch (Exception e) { LOG.error("getKeyInfo Error", e); return null; - } finally { + } + finally { if (null != fxKeyFile) { try { fxKeyFile.close(); - } catch (IOException e) { + } + catch (IOException e) { LOG.error("getKeyInfo Error", e); } } @@ -263,7 +279,8 @@ public class CertDescriptor { X509Certificate cert = (X509Certificate) keyStore .getCertificate(keyAlias); return cert.getSerialNumber().toString(); - } catch (KeyStoreException e) { + } + catch (KeyStoreException e) { LOG.error("getCertIdIdByStore Error", e); return null; } @@ -281,7 +298,8 @@ public class CertDescriptor { if (LOG.isInfoEnabled()) { LOG.info("Load PublicKeyCert Successful"); } - } else if (LOG.isInfoEnabled()) { + } + else if (LOG.isInfoEnabled()) { LOG.info("PublicKeyCert is empty"); } } @@ -297,7 +315,8 @@ public class CertDescriptor { if (LOG.isInfoEnabled()) { LOG.info("Load PublicKeyCert Successful"); } - } else if (LOG.isInfoEnabled()) { + } + else if (LOG.isInfoEnabled()) { LOG.info("PublicKeyCert is empty"); } } @@ -311,14 +330,17 @@ public class CertDescriptor { if (!StringUtils.isEmpty(certPath)) { try { initRootCert(new FileInputStream(certPath)); - } catch (FileNotFoundException e) { + } + catch (FileNotFoundException e) { LOG.info("RootCert is empty"); } - } else if (LOG.isInfoEnabled()) { + } + else if (LOG.isInfoEnabled()) { LOG.info("RootCert is empty"); } } + /** * 加载根证书 * @@ -330,7 +352,8 @@ public class CertDescriptor { if (LOG.isInfoEnabled()) { LOG.info("Load RootCert Successful"); } - } else if (LOG.isInfoEnabled()) { + } + else if (LOG.isInfoEnabled()) { LOG.info("RootCert is empty"); } } diff --git a/pay-java-demo/README.md b/pay-java-demo/README.md index 869d81c..d4f5301 100644 --- a/pay-java-demo/README.md +++ b/pay-java-demo/README.md @@ -7,8 +7,8 @@ /** * 支付类型 * @author egan - * @email egzosn@gmail.com - * @date 2016/11/20 0:30 + * email egzosn@gmail.com + * date 2016/11/20 0:30 */ public enum PayType implements BasePayType { @@ -107,8 +107,8 @@ public enum PayType implements BasePayType { /** * 支付响应对象 * @author: egan - * @email egzosn@gmail.com - * @date 2016/11/18 0:34 + * email egzosn@gmail.com + * date 2016/11/18 0:34 */ public class PayResponse { @Resource @@ -244,8 +244,8 @@ public class PayResponse { /** * 支付宝回调信息拦截器 * @author: egan - * @email egzosn@gmail.com - * @date 2017/1/18 19:28 + * email egzosn@gmail.com + * date 2017/1/18 19:28 */ public class AliPayMessageInterceptor implements PayMessageInterceptor { /** diff --git a/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/AliPayController.java b/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/AliPayController.java index 0933696..867d7c5 100644 --- a/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/AliPayController.java +++ b/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/AliPayController.java @@ -335,8 +335,8 @@ public class AliPayController { * @return 返回支付方下载对账单的结果 */ @RequestMapping("downloadbill") - public Object downloadbill(QueryOrder order) { - return service.downloadbill(order.getBillDate(), order.getBillType()); + public Object downloadBill(QueryOrder order) { + return service.downloadBill(order.getBillDate(), order.getBillType()); } diff --git a/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/WxPayController.java b/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/WxPayController.java index 4b0b241..fc0bef7 100644 --- a/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/WxPayController.java +++ b/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/WxPayController.java @@ -333,8 +333,8 @@ public class WxPayController { * @return 返回支付方下载对账单的结果 */ @RequestMapping("downloadbill") - public Object downloadbill(QueryOrder order) { - return service.downloadbill(order.getBillDate(), order.getBillType()); + public Object downloadBill(QueryOrder order) { + return service.downloadBill(order.getBillDate(), order.getBillType()); } diff --git a/pay-java-fuiou/src/test/java/PayTest.java b/pay-java-fuiou/src/test/java/PayTest.java index 53dfd25..f838a26 100644 --- a/pay-java-fuiou/src/test/java/PayTest.java +++ b/pay-java-fuiou/src/test/java/PayTest.java @@ -16,8 +16,8 @@ import java.util.UUID; * * 富友支付测试 * @author egan - * @email egzosn@gmail.com - * @date 2017/8/18 + * email egzosn@gmail.com + * date 2017/8/18 */ public class PayTest { diff --git a/pay-java-payoneer/test/java/PayTest.java b/pay-java-payoneer/test/java/PayTest.java index b591876..ddf2691 100644 --- a/pay-java-payoneer/test/java/PayTest.java +++ b/pay-java-payoneer/test/java/PayTest.java @@ -27,8 +27,8 @@ import java.util.UUID; * * payoneer支付测试 * @author Actinia - * @email hayesfu@qq.com - * @date 2018/1/18 0018 16:49 + * email hayesfu@qq.com + * date 2018/1/18 0018 16:49 */ public class PayTest { diff --git a/pay-java-wx-youdian/src/test/java/PayTest.java b/pay-java-wx-youdian/src/test/java/PayTest.java index e4d3089..26d342f 100644 --- a/pay-java-wx-youdian/src/test/java/PayTest.java +++ b/pay-java-wx-youdian/src/test/java/PayTest.java @@ -16,8 +16,8 @@ import java.util.UUID; * * 友店微信 * @author egan - * @email egzosn@gmail.com - * @date 2017/8/18 + * email egzosn@gmail.com + * date 2017/8/18 */ public class PayTest { -- Gitee From 18654a399df95818c2789456a0929e9f1bf43230 Mon Sep 17 00:00:00 2001 From: egan Date: Sun, 8 Aug 2021 23:04:34 +0800 Subject: [PATCH 213/299] =?UTF-8?q?=E6=94=AF=E4=BB=98=E9=80=9A=E7=9F=A5?= =?UTF-8?q?=E8=AF=B7=E6=B1=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../egzosn/pay/common/bean/NoticeParams.java | 85 +++++++++++++++++++ .../egzosn/pay/common/bean/NoticeRequest.java | 47 ++++++++++ 2 files changed, 132 insertions(+) create mode 100644 pay-java-common/src/main/java/com/egzosn/pay/common/bean/NoticeParams.java create mode 100644 pay-java-common/src/main/java/com/egzosn/pay/common/bean/NoticeRequest.java diff --git a/pay-java-common/src/main/java/com/egzosn/pay/common/bean/NoticeParams.java b/pay-java-common/src/main/java/com/egzosn/pay/common/bean/NoticeParams.java new file mode 100644 index 0000000..71880b0 --- /dev/null +++ b/pay-java-common/src/main/java/com/egzosn/pay/common/bean/NoticeParams.java @@ -0,0 +1,85 @@ +/* + * Copyright 2017-2021 the original Egan. + * email egzosn@gmail.com + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +package com.egzosn.pay.common.bean; + +import java.util.Collections; +import java.util.Enumeration; +import java.util.List; +import java.util.Map; + +/** + * 通知参数 + * + * @author Egan + * email egzosn@gmail.com + * date 2021/8/8 + */ +public class NoticeParams { + + + /** + * 为了获取request里面传过来的动态参数 + */ + private Map body; + + /** + * 存放请求头信息 + */ + private Map> headers; + + + public NoticeParams(Map body) { + this.body = body; + } + + public NoticeParams(Map body, Map> headers) { + this.body = body; + this.headers = headers; + } + + public String getHeader(String name) { + List value = this.headers.get(name); + return (null == value || value.isEmpty()) ? null : value.get(0); + } + + public Enumeration getHeaders(String name) { + List value = this.headers.get(name); + return (Collections.enumeration(value != null ? value : Collections.emptySet())); + } + + public Enumeration getHeaderNames() { + return Collections.enumeration(this.headers.keySet()); + } + + + public Map getBody() { + return body; + } + + public void setBody(Map body) { + this.body = body; + } + + public Map> getHeaders() { + return headers; + } + + public void setHeaders(Map> headers) { + this.headers = headers; + } +} diff --git a/pay-java-common/src/main/java/com/egzosn/pay/common/bean/NoticeRequest.java b/pay-java-common/src/main/java/com/egzosn/pay/common/bean/NoticeRequest.java new file mode 100644 index 0000000..3ec9af7 --- /dev/null +++ b/pay-java-common/src/main/java/com/egzosn/pay/common/bean/NoticeRequest.java @@ -0,0 +1,47 @@ +package com.egzosn.pay.common.bean; + +import java.io.IOException; +import java.io.InputStream; +import java.util.Enumeration; +import java.util.Map; + +/** + * 通知请求 + * @author Egan + * email egzosn@gmail.com + * date 2021/8/8 + */ +public interface NoticeRequest { + + /** + * 根据请求头名称获取请求头信息 + * @param name 名称 + * @return 请求头值 + */ + String getHeader(String name); + /** + * 根据请求头名称获取请求头信息 + * @param name 名称 + * @return 请求头值 + */ + Enumeration getHeaders(String name); + + /** + * 获取所有的请求头名称 + * @return 请求头名称 + */ + Enumeration getHeaderNames(); + + /** + * 输入流 + * @return 输入流 + * @throws IOException IOException + */ + InputStream getInputStream() throws IOException; + + /** + * 获取所有的请求参数 + * @return 请求参数 + */ + Map getParameterMap(); +} -- Gitee From a5a278611d8840172dc404c643398a882973325a Mon Sep 17 00:00:00 2001 From: egan Date: Sun, 8 Aug 2021 23:05:30 +0800 Subject: [PATCH 214/299] =?UTF-8?q?=E5=BE=AE=E4=BF=A1V3=20=E5=B9=B3?= =?UTF-8?q?=E5=8F=B0=E8=AF=81=E4=B9=A6=E5=8A=A0=E8=BD=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../wx/v3/api/DefaultWxPayAssistService.java | 204 ++++++++++++++++++ .../pay/wx/v3/api/WxPayAssistService.java | 76 +++++++ .../egzosn/pay/wx/v3/api/WxPayService.java | 201 ++++++----------- .../pay/wx/v3/bean/CertEnvironment.java | 4 + .../pay/wx/v3/bean/WxTransactionType.java | 6 +- .../pay/wx/v3/utils/AntCertificationUtil.java | 113 +++++++++- .../com/egzosn/pay/wx/v3/utils/WxConst.java | 6 + pay-java-wx/src/test/java/PayTest.java | 4 +- 8 files changed, 473 insertions(+), 141 deletions(-) create mode 100644 pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/api/DefaultWxPayAssistService.java create mode 100644 pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/api/WxPayAssistService.java diff --git a/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/api/DefaultWxPayAssistService.java b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/api/DefaultWxPayAssistService.java new file mode 100644 index 0000000..994335e --- /dev/null +++ b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/api/DefaultWxPayAssistService.java @@ -0,0 +1,204 @@ +package com.egzosn.pay.wx.v3.api; + +import java.io.ByteArrayInputStream; +import java.nio.charset.StandardCharsets; +import java.security.Signature; +import java.security.cert.Certificate; +import java.util.Map; + +import org.apache.http.HttpEntity; +import org.apache.http.entity.ContentType; +import org.apache.http.message.BasicHeader; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.JSONArray; +import com.alibaba.fastjson.JSONObject; +import com.alibaba.fastjson.serializer.SerializerFeature; +import com.egzosn.pay.common.bean.MethodType; +import com.egzosn.pay.common.bean.PayOrder; +import com.egzosn.pay.common.bean.TransactionType; +import com.egzosn.pay.common.exception.PayErrorException; +import com.egzosn.pay.common.http.HttpRequestTemplate; +import com.egzosn.pay.common.http.HttpStringEntity; +import com.egzosn.pay.common.http.ResponseEntity; +import com.egzosn.pay.common.http.UriVariables; +import com.egzosn.pay.common.util.DateUtils; +import com.egzosn.pay.common.util.sign.SignTextUtils; +import com.egzosn.pay.common.util.str.StringUtils; +import com.egzosn.pay.wx.bean.WxPayError; +import com.egzosn.pay.wx.v3.bean.WxTransactionType; +import com.egzosn.pay.wx.v3.utils.AntCertificationUtil; +import com.egzosn.pay.wx.v3.utils.WxConst; + +/** + * 默认的微信支付辅助服务 + * + * @author Egan + * email egzosn@gmail.com + * date 2021/8/7 + */ +public class DefaultWxPayAssistService implements WxPayAssistService { + protected final Logger LOG = LoggerFactory.getLogger(getClass()); + private WxPayConfigStorage payConfigStorage; + + private HttpRequestTemplate requestTemplate; + + private WxPayService wxPayService; + + + public DefaultWxPayAssistService(WxPayService wxPayService) { + this.wxPayService = wxPayService; + payConfigStorage = wxPayService.getPayConfigStorage(); + requestTemplate = wxPayService.getHttpRequestTemplate(); + + } + + + /** + * 发起请求 + * + * @param parameters 支付参数 + * @param transactionType 交易类型 + * @return 响应内容体 + */ + public JSONObject doExecute(Map parameters, TransactionType transactionType) { + String requestBody = JSON.toJSONString(parameters, SerializerFeature.WriteMapNullValue); + return doExecute(requestBody, transactionType); + } + + + /** + * 发起请求 + * + * @param body 请求内容 + * @param transactionType 交易类型 + * @param uriVariables 用于匹配表达式 + * @return 响应内容体 + */ + public JSONObject doExecute(String body, TransactionType transactionType, Object... uriVariables) { + String reqUrl = UriVariables.getUri(wxPayService.getReqUrl(transactionType), uriVariables); + MethodType method = MethodType.valueOf(transactionType.getMethod()); + if (MethodType.GET == method && StringUtils.isNotEmpty(body)) { + reqUrl += UriVariables.QUESTION.concat(body); + body = ""; + } + HttpEntity entity = buildHttpEntity(reqUrl, body, transactionType.getMethod()); + ResponseEntity responseEntity = requestTemplate.doExecuteEntity(reqUrl, entity, JSONObject.class, method); + int statusCode = responseEntity.getStatusCode(); + JSONObject responseBody = responseEntity.getBody(); + if (statusCode >= 400) { + throw new PayErrorException(new WxPayError(responseBody.getString(WxConst.CODE), responseBody.getString(WxConst.MESSAGE), responseBody.toJSONString())); + } + return responseBody; + } + + /** + * 发起请求 + * + * @param parameters 支付参数 + * @param order 订单 + * @return 请求响应 + */ + public JSONObject doExecute(Map parameters, PayOrder order) { + TransactionType transactionType = order.getTransactionType(); + return doExecute(parameters, transactionType); + } + + + /** + * 构建请求实体 + * 这里也做签名处理 + * + * @param body 请求内容体 + * @param method 请求方法 + * @return 请求实体 + */ + public HttpEntity buildHttpEntity(String url, String body, String method) { + String nonceStr = SignTextUtils.randomStr(); + long timestamp = DateUtils.toEpochSecond(); + String canonicalUrl = UriVariables.getCanonicalUrl(url); + //签名信息 + String signText = StringUtils.joining("\n", method, canonicalUrl, String.valueOf(timestamp), nonceStr, body); + String sign = wxPayService.createSign(signText, payConfigStorage.getInputCharset()); + String serialNumber = payConfigStorage.getCertEnvironment().getSerialNumber(); + // 生成token + String token = String.format(WxConst.TOKEN_PATTERN, payConfigStorage.getMchId(), nonceStr, timestamp, serialNumber, sign); + HttpStringEntity entity = new HttpStringEntity(body, ContentType.APPLICATION_JSON); + entity.addHeader(new BasicHeader("Authorization", WxConst.SCHEMA.concat(token))); + entity.addHeader(new BasicHeader("User-Agent", "Pay-Java-Service")); + return entity; + } + + + /** + * 当缓存中平台证书不存在事进行刷新重新获取平台证书 + * 调用/v3/certificates + */ + @Override + public void refreshCertificate() { + JSONObject responseEntity = doExecute("", WxTransactionType.CERT); + + if (null == responseEntity) { + throw new PayErrorException(new WxPayError(WxConst.FAILURE, "获取证书失败")); + } + JSONArray certificates = responseEntity.getJSONArray("data"); + if (null == certificates) { + return; + } + + for (int i = 0; i < certificates.size(); i++) { + JSONObject certificate = certificates.getJSONObject(i); + JSONObject encryptCertificate = certificate.getJSONObject("encrypt_certificate"); + String associatedData = encryptCertificate.getString("associated_data"); + String nonce = encryptCertificate.getString("nonce"); + String ciphertext = encryptCertificate.getString("ciphertext"); + String publicKey = AntCertificationUtil.decryptToString(associatedData, nonce, ciphertext, payConfigStorage.getSecretKey(), payConfigStorage.getInputCharset()); + ByteArrayInputStream inputStream = new ByteArrayInputStream(publicKey.getBytes(StandardCharsets.UTF_8)); + AntCertificationUtil.loadCertificate(certificate.getString("serial_no"), inputStream); + } + + } + + /** + * 通过证书序列获取平台证书 + * + * @param serialNo 证书序列 + * @return 平台证书 + */ + @Override + public Certificate getCertificate(String serialNo) { + final Certificate certificate = AntCertificationUtil.getCertificate(serialNo); + if (null == certificate){ + refreshCertificate(); + } + + + + return null; + } + + +/* *//** + * 我方对响应验签,和应答签名做比较,使用微信平台证书. + * + * @param params the params + * @return the boolean + *//* + public boolean responseSignVerify(ResponseSignVerifyParams params) { + + String wechatpaySerial = params.getWechatpaySerial(); + if (CERTIFICATE_MAP.isEmpty() || !CERTIFICATE_MAP.containsKey(wechatpaySerial)) { + wechatMetaContainer.getTenantIds().forEach(this::refreshCertificate); + } + Certificate certificate = CERTIFICATE_MAP.get(wechatpaySerial); + + final String signatureStr = createSign(true, params.getWechatpayTimestamp(), params.getWechatpayNonce(), params.getBody()); + Signature signer = Signature.getInstance("SHA256withRSA"); + signer.initVerify(certificate); + signer.update(signatureStr.getBytes(StandardCharsets.UTF_8)); + + return signer.verify(Base64Utils.decodeFromString(params.getWechatpaySignature())); + }*/ +} diff --git a/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/api/WxPayAssistService.java b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/api/WxPayAssistService.java new file mode 100644 index 0000000..8bf20e9 --- /dev/null +++ b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/api/WxPayAssistService.java @@ -0,0 +1,76 @@ +package com.egzosn.pay.wx.v3.api; + +import java.security.cert.Certificate; +import java.util.Map; + +import org.apache.http.HttpEntity; + +import com.alibaba.fastjson.JSONObject; +import com.egzosn.pay.common.bean.PayOrder; +import com.egzosn.pay.common.bean.TransactionType; +import com.egzosn.pay.common.http.HttpStringEntity; + +/** + * 微信支付辅助服务 + * + * @author Egan + * email egzosn@gmail.com + * date 2021/8/7 + */ +public interface WxPayAssistService { + + + /** + * 发起请求 + * + * @param parameters 支付参数 + * @param transactionType 交易类型 + * @return 响应内容体 + */ + JSONObject doExecute(Map parameters, TransactionType transactionType); + + + /** + * 发起请求 + * + * @param body 请求内容 + * @param transactionType 交易类型 + * @param uriVariables 用于匹配表达式 + * @return 响应内容体 + */ + JSONObject doExecute(String body, TransactionType transactionType, Object... uriVariables); + + /** + * 发起请求 + * + * @param parameters 支付参数 + * @param order 订单 + * @return 请求响应 + */ + JSONObject doExecute(Map parameters, PayOrder order); + + /** + * 构建请求实体 + * 这里也做签名处理 + * + * @param body 请求内容体 + * @param method 请求方法 + * @return 请求实体 + */ + HttpEntity buildHttpEntity(String url, String body, String method); + + /** + * 当缓存中平台证书不存在事进行刷新重新获取平台证书 + * 调用/v3/certificates + * + */ + void refreshCertificate(); + + /** + * 通过证书序列获取平台证书 + * @param serialNo 证书序列 + * @return 平台证书 + */ + Certificate getCertificate(String serialNo); + +} diff --git a/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/api/WxPayService.java b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/api/WxPayService.java index ba4255a..7615270 100644 --- a/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/api/WxPayService.java +++ b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/api/WxPayService.java @@ -6,11 +6,11 @@ import java.net.URLEncoder; import java.security.PrivateKey; import java.util.Date; import java.util.HashMap; +import java.util.LinkedHashMap; import java.util.Map; import java.util.TreeMap; -import org.apache.http.entity.ContentType; -import org.apache.http.message.BasicHeader; +import org.apache.http.HttpEntity; import static com.egzosn.pay.wx.api.WxConst.OUT_TRADE_NO; import static com.egzosn.pay.wx.api.WxConst.RETURN_CODE; @@ -20,7 +20,6 @@ import static com.egzosn.pay.wx.api.WxConst.SUCCESS; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONObject; -import com.alibaba.fastjson.serializer.SerializerFeature; import com.egzosn.pay.common.api.BasePayService; import com.egzosn.pay.common.bean.BillType; import com.egzosn.pay.common.bean.CurType; @@ -44,10 +43,9 @@ import com.egzosn.pay.common.util.MapGen; import com.egzosn.pay.common.util.Util; import com.egzosn.pay.common.util.XML; import com.egzosn.pay.common.util.sign.SignTextUtils; +import com.egzosn.pay.common.util.sign.SignUtils; import com.egzosn.pay.common.util.sign.encrypt.RSA2; import com.egzosn.pay.common.util.str.StringUtils; -import com.egzosn.pay.wx.api.WxRedPackService; -import com.egzosn.pay.wx.bean.RedpackOrder; import com.egzosn.pay.wx.bean.WxPayError; import com.egzosn.pay.wx.bean.WxPayMessage; import com.egzosn.pay.wx.bean.WxTransferType; @@ -68,7 +66,7 @@ import com.egzosn.pay.wx.v3.utils.WxConst; * date 2016-5-18 14:09:01 * */ -public class WxPayService extends BasePayService implements WxRedPackService { +public class WxPayService extends BasePayService { /** @@ -80,6 +78,22 @@ public class WxPayService extends BasePayService implements */ private boolean partner = false; + /** + * 辅助api + */ + private volatile WxPayAssistService wxPayAssistService; + + public WxPayAssistService getAssistService() { + if (null == wxPayAssistService) { + wxPayAssistService = new DefaultWxPayAssistService(this); + } + return wxPayAssistService; + } + + public void setAssistService(WxPayAssistService wxPayAssistService) { + this.wxPayAssistService = wxPayAssistService; + } + /** * 创建支付服务 * @@ -108,6 +122,7 @@ public class WxPayService extends BasePayService implements public BasePayService setPayConfigStorage(WxPayConfigStorage payConfigStorage) { payConfigStorage.loadCertEnvironment(); this.payConfigStorage = payConfigStorage; + return this; } @@ -175,7 +190,7 @@ public class WxPayService extends BasePayService implements * @param parameters 参数信息 * @return 参数信息 */ - private Map initPartner(Map parameters) { + private void initPartner(Map parameters) { if (null == parameters) { parameters = new HashMap<>(); } @@ -186,9 +201,6 @@ public class WxPayService extends BasePayService implements } setParameters(parameters, "sub_appid", payConfigStorage.getSubAppId()); initSubMchId(parameters); - - return parameters; - } /** @@ -217,69 +229,17 @@ public class WxPayService extends BasePayService implements * @param order 支付订单 * @return 订单参数 */ - private Map loadSettleInfo(Map parameters, PayOrder order) { + private void loadSettleInfo(Map parameters, PayOrder order) { Object profitSharing = order.getAttr("profit_sharing"); if (null != profitSharing) { Map settleInfo = new MapGen("profit_sharing", profitSharing).getAttr(); parameters.put("settle_info", settleInfo); + return; } - else { - //结算信息 - setParameters(parameters, "settle_info", order); - } - - return parameters; + //结算信息 + setParameters(parameters, "settle_info", order); - } - /** - * 发起请求 - * - * @param parameters 支付参数 - * @param transactionType 交易类型 - * @return 响应内容体 - */ - protected JSONObject doExecute(Map parameters, TransactionType transactionType) { - String requestBody = JSON.toJSONString(parameters, SerializerFeature.WriteMapNullValue); - return doExecute(requestBody, transactionType); - } - - - /** - * 发起请求 - * - * @param body 请求内容 - * @param transactionType 交易类型 - * @param uriVariables 用于匹配表达式 - * @return 响应内容体 - */ - protected JSONObject doExecute(String body, TransactionType transactionType, Object... uriVariables) { - String reqUrl = UriVariables.getUri(getReqUrl(transactionType), uriVariables); - MethodType method = MethodType.valueOf(transactionType.getMethod()); - if (MethodType.GET == method && StringUtils.isNotEmpty(body)) { - reqUrl += UriVariables.QUESTION.concat(body); - body = ""; - } - HttpStringEntity entity = initHttpStringEntity(reqUrl, body, transactionType.getMethod()); - ResponseEntity responseEntity = requestTemplate.doExecuteEntity(reqUrl, entity, JSONObject.class, method); - int statusCode = responseEntity.getStatusCode(); - JSONObject responseBody = responseEntity.getBody(); - if (statusCode >= 400) { - throw new PayErrorException(new WxPayError(responseBody.getString(WxConst.CODE), responseBody.getString(WxConst.MESSAGE), responseBody.toJSONString())); - } - return responseBody; - } - - /** - * 发起请求 - * - * @param parameters 支付参数 - * @param order 订单 - * @return 请求响应 - */ - protected JSONObject doExecute(Map parameters, PayOrder order) { - TransactionType transactionType = order.getTransactionType(); - return doExecute(parameters, transactionType); } @@ -318,7 +278,7 @@ public class WxPayService extends BasePayService implements TransactionType transactionType = order.getTransactionType(); ((WxTransactionType) transactionType).setAttribute(parameters, order); - return doExecute(parameters, order); + return wxPayAssistService.doExecute(parameters, order); } @@ -334,33 +294,37 @@ public class WxPayService extends BasePayService implements ////统一下单 JSONObject result = unifiedOrder(order); - throw new PayErrorException(new WxPayError("", "等待作者实现")); - - } + //如果是扫码支付或者刷卡付无需处理,直接返回 + if (((WxTransactionType) order.getTransactionType()).isReturn()) { + return result; + } + Map params = new LinkedHashMap<>(); + String appId = payConfigStorage.getAppId(); + String timeStamp = String.valueOf(DateUtils.toEpochSecond()); + String randomStr = SignTextUtils.randomStr(); + String prepayId = result.getString("prepay_id"); + if (WxTransactionType.JSAPI == order.getTransactionType()) { + params.put("appId", appId); + params.put("timeStamp", timeStamp); + params.put("nonceStr", randomStr); + prepayId = "prepay_id=" + prepayId; + params.put("package", prepayId); + params.put("signType", SignUtils.RSA.getName()); + } + else if (WxTransactionType.APP == order.getTransactionType()) { + params.put(WxConst.APPID, appId); + params.put("partnerid", payConfigStorage.getMchId()); + params.put("timestamp", timeStamp); + params.put("noncestr", randomStr); + params.put("prepayid", prepayId); + params.put("package", "Sign=WXPay"); + } + String signText = StringUtils.joining("\n", appId, timeStamp, prepayId); + String paySign = createSign(signText, payConfigStorage.getInputCharset()); + params.put(WxTransactionType.JSAPI.equals(order.getTransactionType()) ? "paySign" : "sign", paySign); + return params; - /** - * 初始化请求实体 - * 这里也做签名处理 - * - * @param body 请求内容体 - * @param method 请求方法 - * @return 请求实体 - */ - private HttpStringEntity initHttpStringEntity(String url, String body, String method) { - String nonceStr = SignTextUtils.randomStr(); - long timestamp = DateUtils.toEpochSecond(); - String canonicalUrl = UriVariables.getCanonicalUrl(url); - //签名信息 - String signText = StringUtils.joining("\n", method, canonicalUrl, String.valueOf(timestamp), nonceStr, body); - String sign = createSign(signText, payConfigStorage.getInputCharset()); - String serialNumber = payConfigStorage.getCertEnvironment().getSerialNumber(); - // 生成token - String token = String.format(WxConst.TOKEN_PATTERN, payConfigStorage.getMchId(), nonceStr, timestamp, serialNumber, sign); - HttpStringEntity entity = new HttpStringEntity(body, ContentType.APPLICATION_JSON); - entity.addHeader(new BasicHeader("Authorization", WxConst.SCHEMA.concat(token))); - entity.addHeader(new BasicHeader("User-Agent", "Pay-Java-Service")); - return entity; } @@ -398,8 +362,7 @@ public class WxPayService extends BasePayService implements private String getSpParameters() { Map attr = initSubMchId(null); setParameters(attr, WxConst.SP_MCH_ID, payConfigStorage.getSpMchId()); - String parameters = UriVariables.getMapToParameters(attr); - return parameters; + return UriVariables.getMapToParameters(attr); } @@ -510,7 +473,7 @@ public class WxPayService extends BasePayService implements transactionType = WxTransactionType.QUERY_OUT_TRADE_NO; uriVariable = outTradeNo; } - return doExecute(parameters, transactionType, uriVariable); + return wxPayAssistService.doExecute(parameters, transactionType, uriVariable); } @@ -524,7 +487,7 @@ public class WxPayService extends BasePayService implements @Override public Map close(String transactionId, String outTradeNo) { String parameters = getSpParameters(); - return doExecute(parameters, WxTransactionType.CLOSE, outTradeNo); + return wxPayAssistService.doExecute(parameters, WxTransactionType.CLOSE, outTradeNo); } /** @@ -553,7 +516,7 @@ public class WxPayService extends BasePayService implements } parameters.put("amount", refundAmount); setParameters(parameters, "amount", refundOrder); - return WxRefundResult.create(doExecute(parameters, WxTransactionType.REFUND)); + return WxRefundResult.create(wxPayAssistService.doExecute(parameters, WxTransactionType.REFUND)); } @@ -566,7 +529,7 @@ public class WxPayService extends BasePayService implements @Override public Map refundquery(RefundOrder refundOrder) { String parameters = UriVariables.getMapToParameters(initSubMchId(null)); - return doExecute(parameters, WxTransactionType.REFUND_QUERY, refundOrder.getRefundNo()); + return wxPayAssistService.doExecute(parameters, WxTransactionType.REFUND_QUERY, refundOrder.getRefundNo()); } /** @@ -608,10 +571,10 @@ public class WxPayService extends BasePayService implements initSubMchId(parameters).put("bill_type", billType.getType()); } String body = UriVariables.getMapToParameters(parameters); - JSONObject result = doExecute(body, WxTransactionType.valueOf(billType.getCustom())); + JSONObject result = wxPayAssistService.doExecute(body, WxTransactionType.valueOf(billType.getCustom())); String downloadUrl = result.getString("download_url"); MethodType methodType = MethodType.GET; - HttpStringEntity entity = initHttpStringEntity(downloadUrl, "", methodType.name()); + HttpEntity entity = wxPayAssistService.buildHttpEntity(downloadUrl, "", methodType.name()); ResponseEntity responseEntity = requestTemplate.doExecuteEntity(downloadUrl, entity, InputStream.class, methodType); InputStream inputStream = responseEntity.getBody(); int statusCode = responseEntity.getStatusCode(); @@ -637,18 +600,16 @@ public class WxPayService extends BasePayService implements * @param order 转账订单 *
      *
-     *                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                               注意事项:
-     *                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                               ◆ 当返回错误码为“SYSTEMERROR”时,请不要更换商户订单号,一定要使用原商户订单号重试,否则可能造成重复支付等资金风险。
-     *                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                               ◆ XML具有可扩展性,因此返回参数可能会有新增,而且顺序可能不完全遵循此文档规范,如果在解析回包的时候发生错误,请商户务必不要换单重试,请商户联系客服确认付款情况。如果有新回包字段,会更新到此API文档中。
-     *                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                               ◆ 因为错误代码字段err_code的值后续可能会增加,所以商户如果遇到回包返回新的错误码,请商户务必不要换单重试,请商户联系客服确认付款情况。如果有新的错误码,会更新到此API文档中。
-     *                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                               ◆ 错误代码描述字段err_code_des只供人工定位问题时做参考,系统实现时请不要依赖这个字段来做自动化处理。
-     *                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                               
+ * 注意事项: + * ◆ 当返回错误码为“SYSTEMERROR”时,请不要更换商户订单号,一定要使用原商户订单号重试,否则可能造成重复支付等资金风险。 + * ◆ XML具有可扩展性,因此返回参数可能会有新增,而且顺序可能不完全遵循此文档规范,如果在解析回包的时候发生错误,请商户务必不要换单重试,请商户联系客服确认付款情况。如果有新回包字段,会更新到此API文档中。 + * ◆ 因为错误代码字段err_code的值后续可能会增加,所以商户如果遇到回包返回新的错误码,请商户务必不要换单重试,请商户联系客服确认付款情况。如果有新的错误码,会更新到此API文档中。 + * ◆ 错误代码描述字段err_code_des只供人工定位问题时做参考,系统实现时请不要依赖这个字段来做自动化处理。 + * * @return 对应的转账结果 */ @Override public Map transfer(TransferOrder order) { - - throw new PayErrorException(new WxPayError("", "等待作者实现")); } @@ -681,29 +642,5 @@ public class WxPayService extends BasePayService implements return WxPayMessage.create(message); } - /** - * 微信发红包 - * - * @param redpackOrder 红包实体 - * @return 返回发红包实体后的结果 - */ - @Override - public Map sendredpack(RedpackOrder redpackOrder) { - throw new PayErrorException(new WxPayError("", "等待作者实现")); - } - - - /** - * 查询红包记录 - * 用于商户对已发放的红包进行查询红包的具体信息,可支持普通红包和裂变包 - * 查询红包记录API只支持查询30天内的红包订单,30天之前的红包订单请登录商户平台查询。 - * - * @param mchBillno 商户发放红包的商户订单号 - * @return 返回查询结果 - */ - @Override - public Map gethbinfo(String mchBillno) { - throw new PayErrorException(new WxPayError("", "等待作者实现")); - } } diff --git a/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/CertEnvironment.java b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/CertEnvironment.java index 7bc13f2..eed1c5b 100644 --- a/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/CertEnvironment.java +++ b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/CertEnvironment.java @@ -27,6 +27,10 @@ public class CertEnvironment { private String serialNumber; + + + + public CertEnvironment() { } diff --git a/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/WxTransactionType.java b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/WxTransactionType.java index 46ed6a4..7c94876 100644 --- a/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/WxTransactionType.java +++ b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/WxTransactionType.java @@ -14,7 +14,7 @@ import com.egzosn.pay.wx.v3.bean.order.SceneInfo; import com.egzosn.pay.wx.v3.utils.WxConst; /** - * 微信交易类型 + * 微信V3交易类型 * * @author egan *

@@ -107,9 +107,7 @@ public enum WxTransactionType implements TransactionType { /** * 申请资金账单 */ - FUND_FLOW_BILL("bill/fundflowbill", MethodType.GET), - - + FUND_FLOW_BILL("bill/fundflowbill", MethodType.GET) ; WxTransactionType(String type, MethodType method) { diff --git a/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/utils/AntCertificationUtil.java b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/utils/AntCertificationUtil.java index 1e06084..1660d2c 100644 --- a/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/utils/AntCertificationUtil.java +++ b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/utils/AntCertificationUtil.java @@ -2,14 +2,27 @@ package com.egzosn.pay.wx.v3.utils; import java.io.IOException; import java.io.InputStream; +import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; import java.security.GeneralSecurityException; import java.security.KeyStore; import java.security.KeyStoreException; +import java.security.NoSuchProviderException; import java.security.PrivateKey; import java.security.PublicKey; +import java.security.cert.Certificate; +import java.security.cert.CertificateException; +import java.security.cert.CertificateFactory; import java.security.cert.X509Certificate; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +import javax.crypto.Cipher; +import javax.crypto.spec.GCMParameterSpec; +import javax.crypto.spec.SecretKeySpec; import com.egzosn.pay.common.exception.PayErrorException; +import com.egzosn.pay.common.util.sign.encrypt.Base64; import com.egzosn.pay.wx.bean.WxPayError; import com.egzosn.pay.wx.v3.bean.CertEnvironment; @@ -21,18 +34,63 @@ import com.egzosn.pay.wx.v3.bean.CertEnvironment; * date 2021/07/18.20:29 */ public final class AntCertificationUtil { + + /** + * 微信平台证书容器 key = 序列号 value = 证书对象 + */ + private static final Map CERTIFICATE_MAP = new ConcurrentHashMap<>(); + private AntCertificationUtil() { } private static final KeyStore PKCS12_KEY_STORE; + private static final CertificateFactory CERTIFICATE_FACTORY; + static { try { PKCS12_KEY_STORE = KeyStore.getInstance("PKCS12"); } catch (KeyStoreException e) { - throw new PayErrorException(new WxPayError("500", " keystore 初始化失败")); + throw new PayErrorException(new WxPayError(WxConst.FAILURE, " keystore 初始化失败"), e); } + + try { + CERTIFICATE_FACTORY = CertificateFactory.getInstance("X509", WxConst.BC_PROVIDER); + } + catch (NoSuchProviderException | CertificateException e) { + throw new PayErrorException(new WxPayError(WxConst.FAILURE, " keystore 初始化失败"), e); + } + + } + + + /** + * 装载平台证书 + * + * @param serialNo 证书序列 + * @param certificateStream 证书流 + */ + public static void loadCertificate(String serialNo, InputStream certificateStream) { + try { + Certificate certificate = CERTIFICATE_FACTORY.generateCertificate(certificateStream); + CERTIFICATE_MAP.put(serialNo, certificate); + } + catch (CertificateException e) { + throw new PayErrorException(new WxPayError(WxConst.FAILURE, " 在生成微信v3证书时发生错误,原因是" + e.getMessage()), e); + } + + } + + /** + * 获取平台证书 + * + * @param serialNo 证书序列 + * @return 平台证书 + */ + public static Certificate getCertificate(String serialNo) { + return CERTIFICATE_MAP.get(serialNo); + } /** @@ -56,11 +114,60 @@ public final class AntCertificationUtil { return new CertEnvironment(privateKey, publicKey, serialNumber); } catch (GeneralSecurityException e) { - throw new PayErrorException(new WxPayError("500", "获取公私钥失败"), e); + throw new PayErrorException(new WxPayError(WxConst.FAILURE, "获取公私钥失败"), e); } catch (IOException e) { - throw new PayErrorException(new WxPayError("500", "私钥证书流加载失败"), e); + throw new PayErrorException(new WxPayError(WxConst.FAILURE, "私钥证书流加载失败"), e); } } + + + /** + * 解密响应体. + * + * @param associatedData 相关数据 + * @param nonce 随机串 + * @param cipherText 需要解密的文本 + * @return the string + */ + public static String decryptToString(String associatedData, String nonce, String cipherText, String secretKey, String characterEncoding) { + + try { + Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding", WxConst.BC_PROVIDER); + SecretKeySpec key = new SecretKeySpec(secretKey.getBytes(Charset.forName(characterEncoding)), "AES"); + GCMParameterSpec spec = new GCMParameterSpec(128, nonce.getBytes(Charset.forName(characterEncoding))); + cipher.init(Cipher.DECRYPT_MODE, key, spec); + cipher.updateAAD(associatedData.getBytes(Charset.forName(characterEncoding))); + byte[] bytes = cipher.doFinal(Base64.decode(cipherText)); + return new String(bytes, Charset.forName(characterEncoding)); + } + catch (GeneralSecurityException e) { + throw new PayErrorException(new WxPayError(WxConst.FAILURE, e.getMessage()), e); + } + } + + /** + * 对请求敏感字段进行加密 + * + * @param message the message + * @param certificate the certificate + * @return encrypt message + * @since 1.0.6.RELEASE + */ + public static String encryptToString(String message, Certificate certificate) { + try { + Cipher cipher = Cipher.getInstance("RSA/ECB/OAEPWithSHA-1AndMGF1Padding", WxConst.BC_PROVIDER); + cipher.init(Cipher.ENCRYPT_MODE, certificate.getPublicKey()); + + byte[] data = message.getBytes(StandardCharsets.UTF_8); + byte[] cipherData = cipher.doFinal(data); + return Base64.encode(cipherData); + + } + catch (GeneralSecurityException e) { + throw new PayErrorException(new WxPayError(WxConst.FAILURE, e.getMessage()), e); + } + } + } diff --git a/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/utils/WxConst.java b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/utils/WxConst.java index fe0d1e0..e644019 100644 --- a/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/utils/WxConst.java +++ b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/utils/WxConst.java @@ -20,6 +20,11 @@ public final class WxConst { * 证书别名 */ public static final String CERT_ALIAS = "Tenpay Certificate"; + /** + * 加密算法提供方 - BouncyCastle + */ + public static final String BC_PROVIDER = "BC"; + /** * 沙箱 */ @@ -38,4 +43,5 @@ public final class WxConst { public static final String CODE = "code"; public static final String MESSAGE = "message"; public static final String SCENE_INFO = "scene_info"; + public static final String FAILURE = "failure"; } diff --git a/pay-java-wx/src/test/java/PayTest.java b/pay-java-wx/src/test/java/PayTest.java index 8137df0..4b42a60 100644 --- a/pay-java-wx/src/test/java/PayTest.java +++ b/pay-java-wx/src/test/java/PayTest.java @@ -19,8 +19,8 @@ import java.util.UUID; * * 微信 * @author egan - * @email egzosn@gmail.com - * @date 2017/8/18 + * email egzosn@gmail.com + * date 2017/8/18 */ public class PayTest { -- Gitee From ea3bc29debbc5e8d6ba278200f271db68ea8175a Mon Sep 17 00:00:00 2001 From: egan Date: Mon, 9 Aug 2021 14:06:23 +0000 Subject: [PATCH 215/299] update README.md. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index c7a2071..bad91f0 100644 --- a/README.md +++ b/README.md @@ -67,7 +67,7 @@ android 例子 [pay-java-android](https://gitee.com/egzosn/pay-java-android) 作者公众号(每周输出) ![公众号](https://egzosn.gitee.io/pay-java-parent/gzh.png "gzh.png") -E-Mail:egzosn@gmail.com +E-Mail:egan@egzosn.com **QQ群:** -- Gitee From 7b400692b657f81322a3bd2da7aa129f35caec0a Mon Sep 17 00:00:00 2001 From: egan Date: Sun, 15 Aug 2021 22:43:47 +0800 Subject: [PATCH 216/299] =?UTF-8?q?1.=E6=96=B0=E5=A2=9E=E6=94=AF=E4=BB=98?= =?UTF-8?q?=E9=AA=8C=E7=AD=BE=E6=96=B9=E5=BC=8F=202.=E8=AE=A2=E5=8D=95?= =?UTF-8?q?=E5=8F=82=E6=95=B0=E6=9E=84=E9=80=A0=E7=B1=BB=203.REA=E8=AF=81?= =?UTF-8?q?=E4=B9=A6=E9=AA=8C=E7=AD=BE=E6=96=B0=E5=A2=9ECertificate?= =?UTF-8?q?=E6=94=AF=E6=8C=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../egzosn/pay/common/api/BasePayService.java | 43 +++-- .../com/egzosn/pay/common/api/PayService.java | 23 ++- .../egzosn/pay/common/bean/NoticeParams.java | 13 ++ .../pay/common/bean/OrderParaStructure.java | 35 ++++ .../pay/common/util/sign/encrypt/RSA.java | 21 +- .../pay/common/util/sign/encrypt/RSA2.java | 180 ++++++++++-------- 6 files changed, 216 insertions(+), 99 deletions(-) create mode 100644 pay-java-common/src/main/java/com/egzosn/pay/common/bean/OrderParaStructure.java diff --git a/pay-java-common/src/main/java/com/egzosn/pay/common/api/BasePayService.java b/pay-java-common/src/main/java/com/egzosn/pay/common/api/BasePayService.java index 187cbed..cc7213b 100644 --- a/pay-java-common/src/main/java/com/egzosn/pay/common/api/BasePayService.java +++ b/pay-java-common/src/main/java/com/egzosn/pay/common/api/BasePayService.java @@ -17,7 +17,10 @@ import org.slf4j.LoggerFactory; import com.alibaba.fastjson.JSON; import com.egzosn.pay.common.bean.BillType; import com.egzosn.pay.common.bean.MethodType; +import com.egzosn.pay.common.bean.NoticeParams; +import com.egzosn.pay.common.bean.NoticeRequest; import com.egzosn.pay.common.bean.Order; +import com.egzosn.pay.common.bean.OrderParaStructure; import com.egzosn.pay.common.bean.PayMessage; import com.egzosn.pay.common.bean.PayOrder; import com.egzosn.pay.common.bean.PayOutMessage; @@ -119,6 +122,7 @@ public abstract class BasePayService implements Pay return base64ClientID; } + /** * 创建签名 * @@ -395,6 +399,17 @@ public abstract class BasePayService implements Pay interceptors.add(interceptor); } + /** + * 将请求参数或者请求流转化为 Map + * + * @param request 通知请求 + * @return 获得回调的请求参数 + */ + @Override + public NoticeParams getNoticeParams(NoticeRequest request) { + return null; + } + /** * 将请求参数或者请求流转化为 Map * @@ -469,20 +484,26 @@ public abstract class BasePayService implements Pay return orderInfo; } + /** + * 过时 + * @param parameters 参数map + * @param key key + * @param value 值 + * @return 返回订单参数 + */ + @Deprecated protected Map setParameters(Map parameters, String key, String value) { - if (StringUtils.isNotEmpty(value)) { - parameters.put(key, value); - } - return parameters; + return OrderParaStructure.loadParameters(parameters, key, value); } - + /** + * 过时 + * @param parameters 参数map + * @param key key + * @param order 订单对象 + * @return 返回订单参数 + */ protected Map setParameters(Map parameters, String key, Order order) { - Object attr = order.getAttr(key); - if (null != attr && !"".equals(attr)) { - order.getAttrs().remove(key); - parameters.put(key, attr); - } - return parameters; + return OrderParaStructure.loadParameters(parameters, key, order); } diff --git a/pay-java-common/src/main/java/com/egzosn/pay/common/api/PayService.java b/pay-java-common/src/main/java/com/egzosn/pay/common/api/PayService.java index bf5256f..178d69c 100644 --- a/pay-java-common/src/main/java/com/egzosn/pay/common/api/PayService.java +++ b/pay-java-common/src/main/java/com/egzosn/pay/common/api/PayService.java @@ -7,6 +7,8 @@ import java.util.Map; import com.egzosn.pay.common.bean.BillType; import com.egzosn.pay.common.bean.MethodType; +import com.egzosn.pay.common.bean.NoticeParams; +import com.egzosn.pay.common.bean.NoticeRequest; import com.egzosn.pay.common.bean.PayMessage; import com.egzosn.pay.common.bean.PayOrder; import com.egzosn.pay.common.bean.PayOutMessage; @@ -61,13 +63,21 @@ public interface PayService { /** * 回调校验 - * + * 已过时方法,详情{@link #verify(NoticeParams)} * @param params 回调回来的参数集 * @return 签名校验 true通过 */ - + @Deprecated boolean verify(Map params); + /** + * 回调校验 + * + * @param params 回调回来的参数集 + * @return 签名校验 true通过 + */ + boolean verify(NoticeParams params); + /** * 返回创建的订单信息 @@ -114,8 +124,17 @@ public interface PayService { * @param is 请求流 * @return 获得回调的请求参数 */ + @Deprecated Map getParameter2Map(Map parameterMap, InputStream is); + /** + * 将请求参数或者请求流转化为 Map + * + * @param request 通知请求 + * @return 获得回调的请求参数 + */ + NoticeParams getNoticeParams(NoticeRequest request); + /** * 获取输出消息,用户返回给支付端 * diff --git a/pay-java-common/src/main/java/com/egzosn/pay/common/bean/NoticeParams.java b/pay-java-common/src/main/java/com/egzosn/pay/common/bean/NoticeParams.java index 71880b0..f89ac97 100644 --- a/pay-java-common/src/main/java/com/egzosn/pay/common/bean/NoticeParams.java +++ b/pay-java-common/src/main/java/com/egzosn/pay/common/bean/NoticeParams.java @@ -42,6 +42,11 @@ public class NoticeParams { */ private Map> headers; + /** + * 附加属性 + */ + private Map attr; + public NoticeParams(Map body) { this.body = body; @@ -82,4 +87,12 @@ public class NoticeParams { public void setHeaders(Map> headers) { this.headers = headers; } + + public Map getAttr() { + return attr; + } + + public void setAttr(Map attr) { + this.attr = attr; + } } diff --git a/pay-java-common/src/main/java/com/egzosn/pay/common/bean/OrderParaStructure.java b/pay-java-common/src/main/java/com/egzosn/pay/common/bean/OrderParaStructure.java new file mode 100644 index 0000000..c651b25 --- /dev/null +++ b/pay-java-common/src/main/java/com/egzosn/pay/common/bean/OrderParaStructure.java @@ -0,0 +1,35 @@ +package com.egzosn.pay.common.bean; + +import java.util.Map; + +import com.egzosn.pay.common.util.str.StringUtils; + +/** + * 订单参数构造器 + * @author Egan + *

+ * email egzosn@gmail.com
+ * date 2021/8/16
+ * 
+ */ +public final class OrderParaStructure { + private OrderParaStructure() { + } + + public static Map loadParameters(Map parameters, String key, String value) { + if (StringUtils.isNotEmpty(value)) { + parameters.put(key, value); + } + return parameters; + } + + public static Map loadParameters(Map parameters, String key, Order order) { + Object attr = order.getAttr(key); + if (null != attr && !"".equals(attr)) { + order.getAttrs().remove(key); + parameters.put(key, attr); + } + return parameters; + } + +} diff --git a/pay-java-common/src/main/java/com/egzosn/pay/common/util/sign/encrypt/RSA.java b/pay-java-common/src/main/java/com/egzosn/pay/common/util/sign/encrypt/RSA.java index ec060ac..4911def 100644 --- a/pay-java-common/src/main/java/com/egzosn/pay/common/util/sign/encrypt/RSA.java +++ b/pay-java-common/src/main/java/com/egzosn/pay/common/util/sign/encrypt/RSA.java @@ -12,6 +12,7 @@ import java.security.GeneralSecurityException; import java.security.KeyFactory; import java.security.PrivateKey; import java.security.PublicKey; +import java.security.cert.Certificate; import java.security.spec.PKCS8EncodedKeySpec; import java.security.spec.X509EncodedKeySpec; @@ -136,10 +137,7 @@ public class RSA { public static boolean verify(String content, String sign, String publicKey, String signAlgorithms, String characterEncoding) { try { PublicKey pubKey = getPublicKey(publicKey, ALGORITHM); - java.security.Signature signature = java.security.Signature.getInstance(signAlgorithms); - signature.initVerify(pubKey); - signature.update(content.getBytes(characterEncoding)); - return signature.verify(Base64.decode(sign)); + return verify(content, sign, pubKey, signAlgorithms, characterEncoding); } catch (GeneralSecurityException e) { LOG.error("", e); @@ -176,6 +174,7 @@ public class RSA { return false; } + /** * RSA验签名检查 * @@ -204,6 +203,20 @@ public class RSA { return verify(content, sign, publicKey, SIGN_ALGORITHMS, characterEncoding); } + + /** + * RSA验签名检查 + * + * @param content 待签名数据 + * @param sign 签名值 + * @param publicKey 公钥 + * @param characterEncoding 编码格式 + * @return 布尔值 + */ + public static boolean verify(String content, String sign, Certificate publicKey, String characterEncoding) { + final PublicKey pubKey = publicKey.getPublicKey(); + return verify(content, sign, pubKey, SIGN_ALGORITHMS, characterEncoding); + } /** * 解密 * diff --git a/pay-java-common/src/main/java/com/egzosn/pay/common/util/sign/encrypt/RSA2.java b/pay-java-common/src/main/java/com/egzosn/pay/common/util/sign/encrypt/RSA2.java index 46cae1c..d25251c 100644 --- a/pay-java-common/src/main/java/com/egzosn/pay/common/util/sign/encrypt/RSA2.java +++ b/pay-java-common/src/main/java/com/egzosn/pay/common/util/sign/encrypt/RSA2.java @@ -5,93 +5,109 @@ import java.io.IOException; import java.security.GeneralSecurityException; import java.security.PrivateKey; import java.security.PublicKey; +import java.security.cert.Certificate; public class RSA2 { - private static final String SIGN_SHA256RSA_ALGORITHMS = "SHA256WithRSA"; - - - - public static String sign(String content, String privateKey, String characterEncoding) { - - return RSA.sign(content, privateKey, SIGN_SHA256RSA_ALGORITHMS, characterEncoding); - } - - - - /** - * RSA签名 - * @param content 待签名数据 - * @param privateKey 私钥 - * @param characterEncoding 编码格式 - * @return 签名值 - */ - public static String sign(String content, PrivateKey privateKey ,String characterEncoding){ - return RSA.sign(content, privateKey, SIGN_SHA256RSA_ALGORITHMS, characterEncoding); - } - - /** - * RSA验签名检查 - * @param content 待签名数据 - * @param sign 签名值 - * @param publicKey 公钥 - * @param characterEncoding 编码格式 - * @return 布尔值 - */ - public static boolean verify(String content, String sign, String publicKey, String characterEncoding){ - - return RSA.verify(content, sign, publicKey, SIGN_SHA256RSA_ALGORITHMS, characterEncoding ); - } - - - - /** - * RSA验签名检查 - * @param content 待签名数据 - * @param sign 签名值 - * @param publicKey 公钥 - * @param characterEncoding 编码格式 - * @return 布尔值 - */ - public static boolean verify(String content, String sign, PublicKey publicKey, String characterEncoding){ - return RSA.verify(content, sign, publicKey, SIGN_SHA256RSA_ALGORITHMS, characterEncoding); - } - - /** - * 解密 - * @param content 密文 - * @param privateKey 商户私钥 - * @param characterEncoding 编码格式 - * @return 解密后的字符串 - * @throws GeneralSecurityException 解密异常 - * @throws IOException 解密异常 - */ - public static String decrypt(String content, String privateKey, String characterEncoding) throws GeneralSecurityException, IOException { + private static final String SIGN_SHA256RSA_ALGORITHMS = "SHA256WithRSA"; + + + public static String sign(String content, String privateKey, String characterEncoding) { + + return RSA.sign(content, privateKey, SIGN_SHA256RSA_ALGORITHMS, characterEncoding); + } + + + /** + * RSA签名 + * + * @param content 待签名数据 + * @param privateKey 私钥 + * @param characterEncoding 编码格式 + * @return 签名值 + */ + public static String sign(String content, PrivateKey privateKey, String characterEncoding) { + return RSA.sign(content, privateKey, SIGN_SHA256RSA_ALGORITHMS, characterEncoding); + } + + /** + * RSA验签名检查 + * + * @param content 待签名数据 + * @param sign 签名值 + * @param publicKey 公钥 + * @param characterEncoding 编码格式 + * @return 布尔值 + */ + public static boolean verify(String content, String sign, String publicKey, String characterEncoding) { + + return RSA.verify(content, sign, publicKey, SIGN_SHA256RSA_ALGORITHMS, characterEncoding); + } + + + /** + * RSA验签名检查 + * + * @param content 待签名数据 + * @param sign 签名值 + * @param publicKey 公钥 + * @param characterEncoding 编码格式 + * @return 布尔值 + */ + public static boolean verify(String content, String sign, PublicKey publicKey, String characterEncoding) { + return RSA.verify(content, sign, publicKey, SIGN_SHA256RSA_ALGORITHMS, characterEncoding); + } + + /** + * RSA验签名检查 + * + * @param content 待签名数据 + * @param sign 签名值 + * @param publicKey 公钥 + * @param characterEncoding 编码格式 + * @return 布尔值 + */ + public static boolean verify(String content, String sign, Certificate publicKey, String characterEncoding) { + PublicKey pubKey = publicKey.getPublicKey(); + return verify(content, sign, pubKey, characterEncoding); + } + + /** + * 解密 + * + * @param content 密文 + * @param privateKey 商户私钥 + * @param characterEncoding 编码格式 + * @return 解密后的字符串 + * @throws GeneralSecurityException 解密异常 + * @throws IOException 解密异常 + */ + public static String decrypt(String content, String privateKey, String characterEncoding) throws GeneralSecurityException, IOException { return RSA.decrypt(content, privateKey, characterEncoding); } - /** - * 得到私钥 - * @param key 密钥字符串(经过base64编码) - * @throws GeneralSecurityException 加密异常 - * @return 私钥 - */ - public static PrivateKey getPrivateKey(String key) throws GeneralSecurityException { - return RSA.getPrivateKey(key); - } - - /** - * - * @param content 加密文本 - * @param publicKey 公钥 - * @param cipherAlgorithm 算法 - * @param characterEncoding 编码类型 - * @return 加密后文本 - * @throws GeneralSecurityException 加密异常 - * @throws IOException IOException - */ - public static String encrypt(String content, String publicKey, String cipherAlgorithm, String characterEncoding ) throws GeneralSecurityException, IOException { - return Base64.encode(RSA.encrypt(content.getBytes(characterEncoding), RSA.getPublicKey(publicKey), 2048, 11, cipherAlgorithm)); - } + /** + * 得到私钥 + * + * @param key 密钥字符串(经过base64编码) + * @return 私钥 + * @throws GeneralSecurityException 加密异常 + */ + public static PrivateKey getPrivateKey(String key) throws GeneralSecurityException { + return RSA.getPrivateKey(key); + } + + /** + * @param content 加密文本 + * @param publicKey 公钥 + * @param cipherAlgorithm 算法 + * @param characterEncoding 编码类型 + * @return 加密后文本 + * @throws GeneralSecurityException 加密异常 + * @throws IOException IOException + */ + public static String encrypt(String content, String publicKey, String cipherAlgorithm, String characterEncoding) throws GeneralSecurityException, IOException { + return Base64.encode(RSA.encrypt(content.getBytes(characterEncoding), RSA.getPublicKey(publicKey), 2048, 11, cipherAlgorithm)); + } } -- Gitee From 4bd02fa0c2fced9de4d436f0af6956666d6a96b2 Mon Sep 17 00:00:00 2001 From: egan Date: Sun, 15 Aug 2021 22:45:02 +0800 Subject: [PATCH 217/299] =?UTF-8?q?=E6=96=B0=E5=A2=9E=E6=94=AF=E4=BB=98?= =?UTF-8?q?=E9=AA=8C=E7=AD=BE=E6=96=B9=E5=BC=8F=E6=94=AF=E6=8C=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/egzosn/pay/ali/api/AliPayService.java | 36 ++++++++---- .../egzosn/pay/baidu/api/BaiduPayService.java | 19 ++++++- .../egzosn/pay/common/api/BasePayService.java | 1 + .../egzosn/pay/fuiou/api/FuiouPayService.java | 17 +++++- .../pay/payoneer/api/PayoneerPayService.java | 15 ++++- .../pay/paypal/api/PayPalPayService.java | 9 +++ .../pay/paypal/v2/api/PayPalPayService.java | 11 +++- .../egzosn/pay/union/api/UnionPayService.java | 15 ++++- .../wx/youdian/api/WxYouDianPayService.java | 17 +++++- .../com/egzosn/pay/wx/api/WxPayService.java | 57 ++++++++++++------- 10 files changed, 154 insertions(+), 43 deletions(-) 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 1e94971..5c5568f 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 @@ -32,7 +32,9 @@ import com.egzosn.pay.ali.bean.OrderSettle; import com.egzosn.pay.common.api.BasePayService; import com.egzosn.pay.common.bean.BillType; import com.egzosn.pay.common.bean.MethodType; +import com.egzosn.pay.common.bean.NoticeParams; import com.egzosn.pay.common.bean.Order; +import com.egzosn.pay.common.bean.OrderParaStructure; import com.egzosn.pay.common.bean.PayMessage; import com.egzosn.pay.common.bean.PayOrder; import com.egzosn.pay.common.bean.PayOutMessage; @@ -108,18 +110,30 @@ public class AliPayService extends BasePayService { * @param params 回调回来的参数集 * @return 签名校验 true通过 */ + @Deprecated @Override public boolean verify(Map params) { + return verify(new NoticeParams(params)); + } + /** + * 回调校验 + * + * @param noticeParams 回调回来的参数集 + * @return 签名校验 true通过 + */ + @Override + public boolean verify(NoticeParams noticeParams) { + final Map params = noticeParams.getBody(); if (params.get(SIGN) == null) { LOG.debug("支付宝支付异常:params:" + params); return false; } return signVerify(params, (String) params.get(SIGN)) && verifySource((String) params.get("notify_id")); - } + /** * 根据反馈回来的信息,生成签名结果 * @@ -209,12 +223,12 @@ public class AliPayService extends BasePayService { private void setNotifyUrl(Map orderInfo, PayOrder order) { orderInfo.put(NOTIFY_URL, payConfigStorage.getNotifyUrl()); - setParameters(orderInfo, NOTIFY_URL, order); + OrderParaStructure.loadParameters(orderInfo, NOTIFY_URL, order); } private void setReturnUrl(Map orderInfo, PayOrder order) { orderInfo.put(RETURN_URL, payConfigStorage.getReturnUrl()); - setParameters(orderInfo, RETURN_URL, order); + OrderParaStructure.loadParameters(orderInfo, RETURN_URL, order); } /** @@ -235,7 +249,7 @@ public class AliPayService extends BasePayService { Map bizContent = new TreeMap<>(); bizContent.put("body", order.getBody()); - setParameters(bizContent, "seller_id", payConfigStorage.getSeller()); + OrderParaStructure.loadParameters(bizContent, "seller_id", payConfigStorage.getSeller()); bizContent.put("subject", order.getSubject()); bizContent.put("out_trade_no", order.getOutTradeNo()); bizContent.put("total_amount", Util.conversionAmount(order.getPrice()).toString()); @@ -319,8 +333,8 @@ public class AliPayService extends BasePayService { protected void loadCertSn(Map orderInfo) { if (payConfigStorage.isCertSign()) { final CertEnvironment certEnvironment = payConfigStorage.getCertEnvironment(); - setParameters(orderInfo, "app_cert_sn", certEnvironment.getMerchantCertSN()); - setParameters(orderInfo, "alipay_root_cert_sn", certEnvironment.getRootCertSN()); + OrderParaStructure.loadParameters(orderInfo, "app_cert_sn", certEnvironment.getMerchantCertSN()); + OrderParaStructure.loadParameters(orderInfo, "alipay_root_cert_sn", certEnvironment.getRootCertSN()); } } @@ -493,7 +507,7 @@ public class AliPayService extends BasePayService { */ protected void setAppAuthToken(Map parameters, Map attrs) { setAppAuthToken(parameters); - setParameters(parameters, APP_AUTH_TOKEN, (String) attrs.remove(APP_AUTH_TOKEN)); + OrderParaStructure.loadParameters(parameters, APP_AUTH_TOKEN, (String) attrs.remove(APP_AUTH_TOKEN)); } /** @@ -502,7 +516,7 @@ public class AliPayService extends BasePayService { * @param parameters 参数 */ protected void setAppAuthToken(Map parameters) { - setParameters(parameters, APP_AUTH_TOKEN, payConfigStorage.getAppAuthToken()); + OrderParaStructure.loadParameters(parameters, APP_AUTH_TOKEN, payConfigStorage.getAppAuthToken()); } @@ -645,11 +659,11 @@ public class AliPayService extends BasePayService { bizContent.put("out_biz_no", order.getOutNo()); bizContent.put("trans_amount", order.getAmount()); transferType.setAttr(bizContent, order); - setParameters(bizContent, "order_title", order); - setParameters(bizContent, "original_order_id", order); + OrderParaStructure.loadParameters(bizContent, "order_title", order); + OrderParaStructure.loadParameters(bizContent, "original_order_id", order); setPayeeInfo(bizContent, order); bizContent.put("remark", order.getRemark()); - setParameters(bizContent, "business_params", order); + OrderParaStructure.loadParameters(bizContent, "business_params", order); //设置请求参数的集合 parameters.put(BIZ_CONTENT, JSON.toJSONString(bizContent)); diff --git a/pay-java-baidu/src/main/java/com/egzosn/pay/baidu/api/BaiduPayService.java b/pay-java-baidu/src/main/java/com/egzosn/pay/baidu/api/BaiduPayService.java index 7d0fcde..58487fe 100644 --- a/pay-java-baidu/src/main/java/com/egzosn/pay/baidu/api/BaiduPayService.java +++ b/pay-java-baidu/src/main/java/com/egzosn/pay/baidu/api/BaiduPayService.java @@ -27,6 +27,7 @@ import com.egzosn.pay.common.bean.BaseRefundResult; import com.egzosn.pay.common.bean.BillType; import com.egzosn.pay.common.bean.CurType; import com.egzosn.pay.common.bean.MethodType; +import com.egzosn.pay.common.bean.NoticeParams; import com.egzosn.pay.common.bean.PayMessage; import com.egzosn.pay.common.bean.PayOrder; import com.egzosn.pay.common.bean.PayOutMessage; @@ -84,22 +85,34 @@ public class BaiduPayService extends BasePayService { * @param params 回调回来的参数集 * @return 结果 */ + @Deprecated @Override public boolean verify(Map params) { + + return verify(new NoticeParams(params)); + } + + /** + * 回调校验 + * + * @param noticeParams 回调回来的参数集 + * @return 签名校验 true通过 + */ + @Override + public boolean verify(NoticeParams noticeParams) { + final Map params = noticeParams.getBody(); if (!RESPONSE_SUCCESS.equals(params.get(RESPONSE_STATUS)) && !RESPONSE_SUCCESS.toString().equals(params.get(RESPONSE_STATUS))) { return false; } LOG.info("开始验证回调签名参数:" + params); try { - boolean checkSign = this.checkReturnSign(params, payConfigStorage.getKeyPublic(), (String) params.get(RSA_SIGN)); - return checkSign; + return this.checkReturnSign(params, payConfigStorage.getKeyPublic(), (String) params.get(RSA_SIGN)); } catch (Exception e) { LOG.info("验签失败", e); } return false; } - public boolean checkReturnSign(Map params, String publicKey, String rsaSign) { try { String content = signContent(params); diff --git a/pay-java-common/src/main/java/com/egzosn/pay/common/api/BasePayService.java b/pay-java-common/src/main/java/com/egzosn/pay/common/api/BasePayService.java index cc7213b..00b4b10 100644 --- a/pay-java-common/src/main/java/com/egzosn/pay/common/api/BasePayService.java +++ b/pay-java-common/src/main/java/com/egzosn/pay/common/api/BasePayService.java @@ -502,6 +502,7 @@ public abstract class BasePayService implements Pay * @param order 订单对象 * @return 返回订单参数 */ + @Deprecated protected Map setParameters(Map parameters, String key, Order order) { return OrderParaStructure.loadParameters(parameters, key, order); } diff --git a/pay-java-fuiou/src/main/java/com/egzosn/pay/fuiou/api/FuiouPayService.java b/pay-java-fuiou/src/main/java/com/egzosn/pay/fuiou/api/FuiouPayService.java index bb16674..f3127da 100644 --- a/pay-java-fuiou/src/main/java/com/egzosn/pay/fuiou/api/FuiouPayService.java +++ b/pay-java-fuiou/src/main/java/com/egzosn/pay/fuiou/api/FuiouPayService.java @@ -11,6 +11,7 @@ import com.alibaba.fastjson.JSONObject; import com.egzosn.pay.common.api.BasePayService; import com.egzosn.pay.common.bean.BillType; import com.egzosn.pay.common.bean.MethodType; +import com.egzosn.pay.common.bean.NoticeParams; import com.egzosn.pay.common.bean.PayMessage; import com.egzosn.pay.common.bean.PayOrder; import com.egzosn.pay.common.bean.PayOutMessage; @@ -117,8 +118,21 @@ public class FuiouPayService extends BasePayService { * @param params 回调回来的参数集 * @return 返回检验结果 0000 成功 其他失败 */ + @Deprecated @Override public boolean verify(Map params) { + + return verify(new NoticeParams(params)); + } + /** + * 回调校验 + * + * @param noticeParams 回调回来的参数集 + * @return 签名校验 true通过 + */ + @Override + public boolean verify(NoticeParams noticeParams) { + final Map params = noticeParams.getBody(); if (!"0000".equals(params.get("order_pay_code"))) { LOG.debug(String.format("富友支付异常:order_pay_code=%s,错误原因=%s,参数集=%s", params.get("order_pay_code"), params.get("order_pay_error"), params)); return false; @@ -128,11 +142,10 @@ public class FuiouPayService extends BasePayService { return (signVerify(params, (String) params.remove("md5")) && verifySource((String) params.get("order_id"))); } catch (PayErrorException e) { - e.printStackTrace(); + LOG.error("", e); } return false; } - /** * 回调签名校验 * diff --git a/pay-java-payoneer/src/main/java/com/egzosn/pay/payoneer/api/PayoneerPayService.java b/pay-java-payoneer/src/main/java/com/egzosn/pay/payoneer/api/PayoneerPayService.java index 5561b40..a09043f 100644 --- a/pay-java-payoneer/src/main/java/com/egzosn/pay/payoneer/api/PayoneerPayService.java +++ b/pay-java-payoneer/src/main/java/com/egzosn/pay/payoneer/api/PayoneerPayService.java @@ -20,6 +20,7 @@ import com.egzosn.pay.common.bean.BillType; import com.egzosn.pay.common.bean.CurType; import com.egzosn.pay.common.bean.DefaultCurType; import com.egzosn.pay.common.bean.MethodType; +import com.egzosn.pay.common.bean.NoticeParams; import com.egzosn.pay.common.bean.PayMessage; import com.egzosn.pay.common.bean.PayOrder; import com.egzosn.pay.common.bean.PayOutMessage; @@ -141,8 +142,21 @@ public class PayoneerPayService extends BasePayService im * @param params 回调回来的参数集 * @return 签名校验 true通过 */ + @Deprecated @Override public boolean verify(Map params) { + + return verify(new NoticeParams(params)); + } + /** + * 回调校验 + * + * @param noticeParams 回调回来的参数集 + * @return 签名校验 true通过 + */ + @Override + public boolean verify(NoticeParams noticeParams) { + final Map params = noticeParams.getBody(); if (params != null && 0 == Integer.parseInt(params.get(CODE).toString())) { if (params.containsKey(OUT_TRADE_NO)) { return verifySource((String) params.get(OUT_TRADE_NO)); @@ -152,7 +166,6 @@ public class PayoneerPayService extends BasePayService im return false; } - /** * 支付宝需要,微信是否也需要再次校验来源,进行订单查询 * 校验数据来源 diff --git a/pay-java-paypal/src/main/java/com/egzosn/pay/paypal/api/PayPalPayService.java b/pay-java-paypal/src/main/java/com/egzosn/pay/paypal/api/PayPalPayService.java index 9da31e2..9b1b15f 100644 --- a/pay-java-paypal/src/main/java/com/egzosn/pay/paypal/api/PayPalPayService.java +++ b/pay-java-paypal/src/main/java/com/egzosn/pay/paypal/api/PayPalPayService.java @@ -24,6 +24,7 @@ import com.egzosn.pay.common.bean.BillType; import com.egzosn.pay.common.bean.CurType; import com.egzosn.pay.common.bean.DefaultCurType; import com.egzosn.pay.common.bean.MethodType; +import com.egzosn.pay.common.bean.NoticeParams; import com.egzosn.pay.common.bean.PayMessage; import com.egzosn.pay.common.bean.PayOrder; import com.egzosn.pay.common.bean.PayOutMessage; @@ -133,9 +134,17 @@ public class PayPalPayService extends BasePayService { } + @Deprecated @Override public boolean verify(Map params) { + return verify(new NoticeParams(params)); + + } + + @Override + public boolean verify(NoticeParams noticeParams) { + final Map params = noticeParams.getBody(); HttpStringEntity httpEntity = new HttpStringEntity("{\"payer_id\":\"" + (String) params.get("PayerID") + "\"}", ContentType.APPLICATION_JSON); httpEntity.setHeaders(authHeader()); JSONObject resp = getHttpRequestTemplate().postForObject(getReqUrl(PayPalTransactionType.EXECUTE), httpEntity, JSONObject.class, (String) params.get("paymentId")); diff --git a/pay-java-paypal/src/main/java/com/egzosn/pay/paypal/v2/api/PayPalPayService.java b/pay-java-paypal/src/main/java/com/egzosn/pay/paypal/v2/api/PayPalPayService.java index 898b35c..5e54691 100644 --- a/pay-java-paypal/src/main/java/com/egzosn/pay/paypal/v2/api/PayPalPayService.java +++ b/pay-java-paypal/src/main/java/com/egzosn/pay/paypal/v2/api/PayPalPayService.java @@ -25,6 +25,7 @@ import com.egzosn.pay.common.bean.BillType; import com.egzosn.pay.common.bean.CurType; import com.egzosn.pay.common.bean.DefaultCurType; import com.egzosn.pay.common.bean.MethodType; +import com.egzosn.pay.common.bean.NoticeParams; import com.egzosn.pay.common.bean.PayMessage; import com.egzosn.pay.common.bean.PayOrder; import com.egzosn.pay.common.bean.PayOutMessage; @@ -155,8 +156,16 @@ public class PayPalPayService extends BasePayService implem * @param params 回调回来的参数集 * @return 是否成功 true成功 */ + @Deprecated @Override public boolean verify(Map params) { + return verify(new NoticeParams(params)); + + } + + @Override + public boolean verify(NoticeParams noticeParams) { + final Map params = noticeParams.getBody(); Object paymentStatus = params.get("payment_status"); if (!"Completed".equals(paymentStatus)) { LOG.warn("状态未完成:" + paymentStatus); @@ -166,8 +175,6 @@ public class PayPalPayService extends BasePayService implem return "VERIFIED".equals(resp); } - - /** * 获取授权请求头 * diff --git a/pay-java-union/src/main/java/com/egzosn/pay/union/api/UnionPayService.java b/pay-java-union/src/main/java/com/egzosn/pay/union/api/UnionPayService.java index 1c10645..bfcf361 100644 --- a/pay-java-union/src/main/java/com/egzosn/pay/union/api/UnionPayService.java +++ b/pay-java-union/src/main/java/com/egzosn/pay/union/api/UnionPayService.java @@ -28,6 +28,7 @@ import com.alibaba.fastjson.JSONObject; import com.egzosn.pay.common.api.BasePayService; import com.egzosn.pay.common.bean.BillType; import com.egzosn.pay.common.bean.MethodType; +import com.egzosn.pay.common.bean.NoticeParams; import com.egzosn.pay.common.bean.PayMessage; import com.egzosn.pay.common.bean.PayOrder; import com.egzosn.pay.common.bean.PayOutMessage; @@ -200,16 +201,28 @@ public class UnionPayService extends BasePayService { * @param result 回调回来的参数集 * @return 签名校验 true通过 */ + @Deprecated @Override public boolean verify(Map result) { + + return verify(new NoticeParams(result)); + } + + /** + * 回调校验 + * + * @param noticeParams 回调回来的参数集 + * @return 签名校验 true通过 + */ + public boolean verify(NoticeParams noticeParams) { + final Map result = noticeParams.getBody(); if (null == result || result.get(SDKConstants.param_signature) == null) { LOG.debug("银联支付验签异常:params:" + result); return false; } return this.signVerify(result, (String) result.get(SDKConstants.param_signature)); } - /** * 签名校验 * 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 5139f48..859bcb5 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 @@ -16,6 +16,7 @@ import com.egzosn.pay.common.bean.BaseRefundResult; import com.egzosn.pay.common.bean.BillType; import com.egzosn.pay.common.bean.CurType; import com.egzosn.pay.common.bean.MethodType; +import com.egzosn.pay.common.bean.NoticeParams; import com.egzosn.pay.common.bean.PayMessage; import com.egzosn.pay.common.bean.PayOrder; import com.egzosn.pay.common.bean.PayOutMessage; @@ -132,8 +133,22 @@ public class WxYouDianPayService extends BasePayService params) { + + return verify(new NoticeParams(params)); + } + + /** + * 回调校验 + * + * @param noticeParams 回调回来的参数集 + * @return 签名校验 true通过 + */ + public boolean verify(NoticeParams noticeParams) { + final Map params = noticeParams.getBody(); + if (!"SUCCESS".equals(params.get("return_code"))) { LOG.debug(String.format("友店微信支付异常:return_code=%s,参数集=%s", params.get("return_code"), params)); return false; @@ -151,7 +166,6 @@ public class WxYouDianPayService extends BasePayService downloadBill(Date billDate, BillType billType) { return Collections.emptyMap(); 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 2042622..58e622f 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 @@ -40,7 +40,9 @@ import com.alibaba.fastjson.JSONObject; import com.egzosn.pay.common.api.BasePayService; import com.egzosn.pay.common.bean.BillType; import com.egzosn.pay.common.bean.MethodType; +import com.egzosn.pay.common.bean.NoticeParams; import com.egzosn.pay.common.bean.Order; +import com.egzosn.pay.common.bean.OrderParaStructure; import com.egzosn.pay.common.bean.PayMessage; import com.egzosn.pay.common.bean.PayOrder; import com.egzosn.pay.common.bean.PayOutMessage; @@ -135,9 +137,22 @@ public class WxPayService extends BasePayService implements * @param params 回调回来的参数集 * @return 签名校验 true通过 */ + @Deprecated @Override public boolean verify(Map params) { + return verify(new NoticeParams(params)); + } + + + /** + * 回调校验 + * + * @param noticeParams 回调回来的参数集 + * @return 签名校验 true通过 + */ + public boolean verify(NoticeParams noticeParams) { + final Map params = noticeParams.getBody(); if (null == params.get(SIGN) || !(SUCCESS.equals(params.get(RETURN_CODE)) && SUCCESS.equals(params.get(RESULT_CODE)))) { if (LOG.isErrorEnabled()) { LOG.error(String.format("微信支付异常:return_code=%s,参数集=%s", params.get(RETURN_CODE), params)); @@ -187,8 +202,8 @@ public class WxPayService extends BasePayService implements parameters.put(APPID, payConfigStorage.getAppId()); parameters.put(MCH_ID, payConfigStorage.getMchId()); //判断如果是服务商模式信息则加入 - setParameters(parameters, "sub_mch_id", payConfigStorage.getSubMchId()); - setParameters(parameters, "sub_appid", payConfigStorage.getSubAppId()); + OrderParaStructure.loadParameters(parameters, "sub_mch_id", payConfigStorage.getSubMchId()); + OrderParaStructure.loadParameters(parameters, "sub_appid", payConfigStorage.getSubAppId()); parameters.put(NONCE_STR, SignTextUtils.randomStr()); return parameters; @@ -209,13 +224,13 @@ public class WxPayService extends BasePayService implements // 购买支付信息 parameters.put("body", order.getSubject()); // 购买支付信息 - setParameters(parameters, "detail", order); + OrderParaStructure.loadParameters(parameters, "detail", order); // 订单号 parameters.put(OUT_TRADE_NO, order.getOutTradeNo()); parameters.put("spbill_create_ip", StringUtils.isEmpty(order.getSpbillCreateIp()) ? "192.168.1.150" : order.getSpbillCreateIp()); // 总金额单位为分 parameters.put("total_fee", Util.conversionCentAmount(order.getPrice())); - setParameters(parameters, "attach", order.getAddition()); + OrderParaStructure.loadParameters(parameters, "attach", order.getAddition()); initNotifyUrl(parameters, order); parameters.put("trade_type", order.getTransactionType().getType()); if (null != order.getExpirationTime()) { @@ -229,11 +244,11 @@ public class WxPayService extends BasePayService implements ((WxTransactionType) order.getTransactionType()).setAttribute(parameters, order); //可覆盖参数 -/* setParameters(parameters, "notify_url", order); - setParameters(parameters, "goods_tag", order); - setParameters(parameters, "limit_pay", order); - setParameters(parameters, "receipt", order); - setParameters(parameters, "product_id", order);*/ +/* OrderParaStructure.loadParameters(parameters, "notify_url", order); + OrderParaStructure.loadParameters(parameters, "goods_tag", order); + OrderParaStructure.loadParameters(parameters, "limit_pay", order); + OrderParaStructure.loadParameters(parameters, "receipt", order); + OrderParaStructure.loadParameters(parameters, "product_id", order);*/ parameters.putAll(order.getAttrs()); parameters = preOrderHandler(parameters, order); setSign(parameters); @@ -514,8 +529,8 @@ public class WxPayService extends BasePayService implements private Map initNotifyUrl(Map parameters, Order order) { - setParameters(parameters, "notify_url", payConfigStorage.getNotifyUrl()); - setParameters(parameters, "notify_url", order); + OrderParaStructure.loadParameters(parameters, "notify_url", payConfigStorage.getNotifyUrl()); + OrderParaStructure.loadParameters(parameters, "notify_url", order); return parameters; } @@ -530,16 +545,16 @@ public class WxPayService extends BasePayService implements //获取公共参数 Map parameters = getPublicParameters(); - setParameters(parameters, "transaction_id", refundOrder.getTradeNo()); - setParameters(parameters, OUT_TRADE_NO, refundOrder.getOutTradeNo()); - setParameters(parameters, "out_refund_no", refundOrder.getRefundNo()); + OrderParaStructure.loadParameters(parameters, "transaction_id", refundOrder.getTradeNo()); + OrderParaStructure.loadParameters(parameters, OUT_TRADE_NO, refundOrder.getOutTradeNo()); + OrderParaStructure.loadParameters(parameters, "out_refund_no", refundOrder.getRefundNo()); parameters.put("total_fee", Util.conversionCentAmount(refundOrder.getTotalAmount())); parameters.put("refund_fee", Util.conversionCentAmount(refundOrder.getRefundAmount())); initNotifyUrl(parameters, refundOrder); if (null != refundOrder.getCurType()) { parameters.put("refund_fee_type", refundOrder.getCurType().getType()); } - setParameters(parameters, "refund_desc", refundOrder.getDescription()); + OrderParaStructure.loadParameters(parameters, "refund_desc", refundOrder.getDescription()); //附加参数,这里可进行覆盖前面所有参数 parameters.putAll(refundOrder.getAttrs()); //设置签名 @@ -559,9 +574,9 @@ public class WxPayService extends BasePayService implements //获取公共参数 Map parameters = getPublicParameters(); - setParameters(parameters, "transaction_id", refundOrder.getTradeNo()); - setParameters(parameters, OUT_TRADE_NO, refundOrder.getOutTradeNo()); - setParameters(parameters, "out_refund_no", refundOrder.getRefundNo()); + OrderParaStructure.loadParameters(parameters, "transaction_id", refundOrder.getTradeNo()); + OrderParaStructure.loadParameters(parameters, OUT_TRADE_NO, refundOrder.getOutTradeNo()); + OrderParaStructure.loadParameters(parameters, "out_refund_no", refundOrder.getRefundNo()); //设置签名 setSign(parameters); return requestTemplate.postForObject(getReqUrl(WxTransactionType.REFUNDQUERY), XML.getMap2Xml(parameters), JSONObject.class); @@ -601,7 +616,7 @@ public class WxPayService extends BasePayService implements //目前只支持日账单 parameters.put("bill_date", DateUtils.formatDate(billDate, DateUtils.YYYYMMDD)); String fileType = billType.getFileType(); - setParameters(parameters, "tar_type", fileType); + OrderParaStructure.loadParameters(parameters, "tar_type", fileType); //设置签名 setSign(parameters); Map ret = new HashMap(3); @@ -758,8 +773,8 @@ public class WxPayService extends BasePayService implements //获取公共参数 Map parameters = getPublicParameters(); - setParameters(parameters, OUT_TRADE_NO, outTradeNoBillType); - setParameters(parameters, "transaction_id", (String) transactionIdOrBillDate); + OrderParaStructure.loadParameters(parameters, OUT_TRADE_NO, outTradeNoBillType); + OrderParaStructure.loadParameters(parameters, "transaction_id", (String) transactionIdOrBillDate); //设置签名 setSign(parameters); return requestTemplate.postForObject(getReqUrl(transactionType), XML.getMap2Xml(parameters), JSONObject.class); -- Gitee From e4129afe63a8a616f4d59942a3b156d19f06821b Mon Sep 17 00:00:00 2001 From: egan Date: Sun, 15 Aug 2021 22:45:29 +0800 Subject: [PATCH 218/299] =?UTF-8?q?pay=20java=20web=E6=94=AF=E6=8C=81?= =?UTF-8?q?=E7=BB=84=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pay-java-web-support/pom.xml | 42 ++++++++++ .../web/support/HttpRequestNoticeParams.java | 82 +++++++++++++++++++ pom.xml | 1 + 3 files changed, 125 insertions(+) create mode 100644 pay-java-web-support/pom.xml create mode 100644 pay-java-web-support/src/main/java/com/egzosn/pay/web/support/HttpRequestNoticeParams.java diff --git a/pay-java-web-support/pom.xml b/pay-java-web-support/pom.xml new file mode 100644 index 0000000..a0d1aa0 --- /dev/null +++ b/pay-java-web-support/pom.xml @@ -0,0 +1,42 @@ + + + + pay-java-parent + com.egzosn + 2.14.2-SNAPSHOT + + 4.0.0 + jar + pay-java-web-support + Pay Java Web 相关支持 + + 4.3.4.RELEASE + 4.10 + 4.3.6.Final + + + + + + com.egzosn + pay-java-common + + + javax.servlet + javax.servlet-api + provided + 3.1.0 + + + javax.servlet + jsp-api + 2.0 + provided + + + + + + \ No newline at end of file diff --git a/pay-java-web-support/src/main/java/com/egzosn/pay/web/support/HttpRequestNoticeParams.java b/pay-java-web-support/src/main/java/com/egzosn/pay/web/support/HttpRequestNoticeParams.java new file mode 100644 index 0000000..acf0753 --- /dev/null +++ b/pay-java-web-support/src/main/java/com/egzosn/pay/web/support/HttpRequestNoticeParams.java @@ -0,0 +1,82 @@ +package com.egzosn.pay.web.support; + +import java.io.IOException; +import java.io.InputStream; +import java.util.Enumeration; +import java.util.Map; + +import javax.servlet.http.HttpServletRequest; + +import com.egzosn.pay.common.bean.NoticeRequest; + +/** + * web 相关请求支持 + * + * @author Egan + *
+ * email egzosn@gmail.com
+ * date 2021/8/16
+ * 
+ */ +public class HttpRequestNoticeParams implements NoticeRequest { + + + private final HttpServletRequest httpServletRequest; + + public HttpRequestNoticeParams(HttpServletRequest httpServletRequest) { + this.httpServletRequest = httpServletRequest; + } + + /** + * 根据请求头名称获取请求头信息 + * + * @param name 名称 + * @return 请求头值 + */ + @Override + public String getHeader(String name) { + return httpServletRequest.getHeader(name); + } + + /** + * 根据请求头名称获取请求头信息 + * + * @param name 名称 + * @return 请求头值 + */ + @Override + public Enumeration getHeaders(String name) { + return httpServletRequest.getHeaders(name); + } + + /** + * 获取所有的请求头名称 + * + * @return 请求头名称 + */ + @Override + public Enumeration getHeaderNames() { + return httpServletRequest.getHeaderNames(); + } + + /** + * 输入流 + * + * @return 输入流 + * @throws IOException IOException + */ + @Override + public InputStream getInputStream() throws IOException { + return httpServletRequest.getInputStream(); + } + + /** + * 获取所有的请求参数 + * + * @return 请求参数 + */ + @Override + public Map getParameterMap() { + return httpServletRequest.getParameterMap(); + } +} diff --git a/pom.xml b/pom.xml index 193a8d6..e041e7b 100644 --- a/pom.xml +++ b/pom.xml @@ -54,6 +54,7 @@ pay-java-common + pay-java-web-support pay-java-ali pay-java-wx pay-java-wx-youdian -- Gitee From 44a0c90413a8b7026d5d4416b8cc4a9d88d2f638 Mon Sep 17 00:00:00 2001 From: egan Date: Sun, 15 Aug 2021 23:34:36 +0800 Subject: [PATCH 219/299] =?UTF-8?q?=E5=BE=AE=E4=BF=A1V3=20=E5=9B=9E?= =?UTF-8?q?=E8=B0=83=E9=AA=8C=E7=AD=BE=E5=8A=9F=E8=83=BD=E5=AE=9E=E7=8E=B0?= =?UTF-8?q?=EF=BC=8C=E5=BE=AE=E4=BF=A1V3=E6=94=AF=E4=BB=98=E5=8A=9F?= =?UTF-8?q?=E8=83=BD=E5=9F=BA=E6=9C=AC=E5=AE=9E=E7=8E=B0,=E6=9A=82?= =?UTF-8?q?=E6=9C=AA=E6=B5=8B=E8=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../demo/controller/WxV3PayController.java | 335 ++++++++++++++++++ .../wx/v3/api/DefaultWxPayAssistService.java | 38 +- .../pay/wx/v3/api/WxParameterStructure.java | 131 +++++++ .../pay/wx/v3/api/WxPayConfigStorage.java | 15 + .../egzosn/pay/wx/v3/api/WxPayService.java | 207 ++++------- .../egzosn/pay/wx/v3/bean/order/Amount.java | 7 +- .../pay/wx/v3/utils/AntCertificationUtil.java | 7 +- .../egzosn/pay/yiji/api/YiJiPayService.java | 14 +- 8 files changed, 579 insertions(+), 175 deletions(-) create mode 100644 pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/WxV3PayController.java create mode 100644 pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/api/WxParameterStructure.java diff --git a/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/WxV3PayController.java b/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/WxV3PayController.java new file mode 100644 index 0000000..73791ef --- /dev/null +++ b/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/WxV3PayController.java @@ -0,0 +1,335 @@ + +package com.egzosn.pay.demo.controller; + + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.math.BigDecimal; +import java.util.HashMap; +import java.util.Map; +import java.util.UUID; + +import javax.annotation.PostConstruct; +import javax.imageio.ImageIO; +import javax.servlet.http.HttpServletRequest; + +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import com.egzosn.pay.common.bean.CertStoreType; +import com.egzosn.pay.common.bean.PayOrder; +import com.egzosn.pay.common.bean.RefundOrder; +import com.egzosn.pay.common.bean.TransferOrder; +import com.egzosn.pay.demo.request.QueryOrder; +import com.egzosn.pay.wx.bean.WxBank; +import com.egzosn.pay.wx.bean.WxTransferType; +import com.egzosn.pay.wx.v3.api.WxPayConfigStorage; +import com.egzosn.pay.wx.v3.api.WxPayService; +import com.egzosn.pay.wx.v3.bean.WxTransactionType; +import com.egzosn.pay.wx.v3.bean.response.WxRefundResult; + +/** + * 发起支付入口 + * + * @author egan + * email egzosn@gmail.com + * date 2016/11/18 0:25 + */ +@RestController +@RequestMapping("wxV3") +public class WxV3PayController { + + private WxPayService service = null; + + + + + + public WxV3PayController() { + + + } + + @PostConstruct + public void init() { + + WxPayConfigStorage wxPayConfigStorage = new WxPayConfigStorage(); + wxPayConfigStorage.setAppId("wxc7b993ff15a9f27c"); + wxPayConfigStorage.setMchId("1602947766"); +// wxPayConfigStorage.setKeyPublic("转账公钥,转账时必填"); + wxPayConfigStorage.setSecretKey("9bd8f0e7af4841299d782406b7774f56"); + wxPayConfigStorage.setNotifyUrl("https://pay.egzosn.com"); + wxPayConfigStorage.setReturnUrl("https://pay.egzosn.com"); + wxPayConfigStorage.setInputCharset("utf-8"); + wxPayConfigStorage.setCertSign(true); + wxPayConfigStorage.setApiClientKeyP12("E:\\Documents\\gitee\\pay-java-parent\\pay-java-demo\\src\\main\\resources\\yifenli_mall.p12"); + wxPayConfigStorage.setCertStoreType(CertStoreType.PATH); + service = new WxPayService(wxPayConfigStorage); + //设置回调消息处理 + //TODO {@link com.egzosn.pay.demo.controller.WxPayController#payBack} +// service.setPayMessageHandler(new WxPayMessageHandler(null)); + } + + + /** + * 跳到支付页面 + * 针对实时支付 + * + * @param request 请求 + * @param price 金额 + * @return 跳到支付页面 + */ + @RequestMapping(value = "toPay.html", produces = "text/html;charset=UTF-8") + public String toPay(HttpServletRequest request, BigDecimal price) { + PayOrder order = new PayOrder("订单title", "摘要", null == price ? BigDecimal.valueOf(0.01) : price, UUID.randomUUID().toString().replace("-", ""), WxTransactionType.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("在线充值"); + +// Map orderInfo = service.orderInfo(order); +// return service.buildRequest(orderInfo, MethodType.POST); + return service.toPay(order); + } + + /** + * 公众号支付 + * + * @param openid openid + * @param price 金额 + * @return 返回jsapi所需参数 + */ + @RequestMapping(value = "jsapi") + public Map toPay(String openid, BigDecimal price) { + + PayOrder order = new PayOrder("订单title", "摘要", null == price ? BigDecimal.valueOf(0.01) : price, UUID.randomUUID().toString().replace("-", ""), WxTransactionType.JSAPI); + order.setOpenid(openid); + + Map orderInfo = service.orderInfo(order); + orderInfo.put("code", 0); + + return orderInfo; + } + + + /** + * 获取支付预订单信息 + * + * @return 支付预订单信息 + */ + @RequestMapping("app") + public Map app() { + Map data = new HashMap<>(); + data.put("code", 0); + PayOrder order = new PayOrder("订单title", "摘要", BigDecimal.valueOf(0.01), UUID.randomUUID().toString().replace("-", "")); + //App支付 + order.setTransactionType(WxTransactionType.APP); + data.put("orderInfo", service.app(order)); + return data; + } + + /** + * 获取二维码图像 + * 二维码支付 + * + * @param price 金额 + * @return 二维码图像 + * @throws IOException IOException + */ + @RequestMapping(value = "toQrPay.jpg", produces = "image/jpeg;charset=UTF-8") + public byte[] toWxQrPay(BigDecimal price) throws IOException { + //获取对应的支付账户操作工具(可根据账户id) + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + ImageIO.write(service.genQrPay(new PayOrder("订单title", "摘要", null == price ? BigDecimal.valueOf(0.01) : price, System.currentTimeMillis() + "", WxTransactionType.NATIVE)), "JPEG", baos); + return baos.toByteArray(); + } + + /** + * 获取二维码地址 + * 二维码支付 + * + * @param price 金额 + * @return 二维码图像 + * @throws IOException IOException + */ + @RequestMapping(value = "getQrPay.json") + public String getQrPay(BigDecimal price) throws IOException { + //获取对应的支付账户操作工具(可根据账户id) + return service.getQrPay(new PayOrder("订单title", "摘要", null == price ? BigDecimal.valueOf(0.01) : price, System.currentTimeMillis() + "", WxTransactionType.NATIVE)); + } + + + + /** + * 支付回调地址 方式一 + *

+ * 方式二,{@link #payBack(HttpServletRequest)} 是属于简化方式, 试用与简单的业务场景 + * + * @param request 请求 + * @return 是否成功 + * @throws IOException IOException + * @see #payBack(HttpServletRequest) + */ + @Deprecated + @RequestMapping(value = "payBackBefore.json") + public String payBackBefore(HttpServletRequest request) throws IOException { + + //获取支付方返回的对应参数 + Map params = service.getParameter2Map(request.getParameterMap(), request.getInputStream()); + if (null == params) { + return service.getPayOutMessage("fail", "失败").toMessage(); + } + + //校验 + if (service.verify(params)) { + //这里处理业务逻辑 + //......业务逻辑处理块........ + return service.successPayOutMessage(null).toMessage(); + } + + return service.getPayOutMessage("fail", "失败").toMessage(); + } + + /** + * 支付回调地址 + * + * @param request 请求 + * @return 是否成功 + *

+ * 业务处理在对应的PayMessageHandler里面处理,在哪里设置PayMessageHandler,详情查看{@link com.egzosn.pay.common.api.PayService#setPayMessageHandler(com.egzosn.pay.common.api.PayMessageHandler)} + *

+ * 如果未设置 {@link com.egzosn.pay.common.api.PayMessageHandler} 那么会使用默认的 {@link com.egzosn.pay.common.api.DefaultPayMessageHandler} + * @throws IOException IOException + */ + @RequestMapping(value = "payBack.json") + public String payBack(HttpServletRequest request) throws IOException { + //业务处理在对应的PayMessageHandler里面处理,在哪里设置PayMessageHandler,详情查看com.egzosn.pay.common.api.PayService.setPayMessageHandler() + return service.payBack(request.getParameterMap(), request.getInputStream()).toMessage(); + } + + + /** + * 查询 + * + * @param order 订单的请求体 + * @return 返回查询回来的结果集,支付方原值返回 + */ + @RequestMapping("query") + public Map query(QueryOrder order) { + return service.query(order.getTradeNo(), order.getOutTradeNo()); + } + + + /** + * 交易关闭接口 + * + * @param order 订单的请求体 + * @return 返回支付方交易关闭后的结果 + */ + @RequestMapping("close") + public Map close(QueryOrder order) { + return service.close(order.getTradeNo(), order.getOutTradeNo()); + } + + /** + * 申请退款接口 + * + * @param order 订单的请求体 + * @return 返回支付方申请退款后的结果 + */ + @RequestMapping("refund") + public WxRefundResult refund(RefundOrder order) { + + return service.refund(order); + } + + /** + * 查询退款 + * + * @param order 订单的请求体 + * @return 返回支付方查询退款后的结果 + */ + @RequestMapping("refundquery") + public Map refundquery(RefundOrder order) { + return service.refundquery(order); + } + + /** + * 下载对账单 + * + * @param order 订单的请求体 + * @return 返回支付方下载对账单的结果 + */ + @RequestMapping("downloadbill") + public Object downloadBill(QueryOrder order) { + return service.downloadBill(order.getBillDate(), order.getBillType()); + } + + + /** + * 转账到余额 + * + * @param order 转账订单 + * @return 对应的转账结果 + */ + @RequestMapping("transfer") + public Map transfer(TransferOrder order) { + order.setOutNo("partner_trade_no 商户转账订单号"); + order.setPayeeAccount("用户openid"); + order.setPayeeName("收款用户姓名, 非必填,如果填写将强制验证收款人姓名"); + order.setRemark("转账备注, 非必填"); + order.setAmount(new BigDecimal(10)); + + //转账到余额,这里默认值是转账到银行卡 + order.setTransferType(WxTransferType.TRANSFERS); + + return service.transfer(order); + } + + + /** + * 转账到银行卡 + * + * @param order 转账订单 + * @return 对应的转账结果 + */ + @RequestMapping("transferPayBank") + public Map transferPayBank(TransferOrder order) { + order.setOutNo("partner_trade_no 商户转账订单号"); + //采用标准RSA算法,公钥由微信侧提供,将公钥信息配置在PayConfigStorage#setKeyPublic(String) + order.setPayeeAccount("enc_bank_no 收款方银行卡号"); + order.setPayeeName("收款方用户名"); + order.setBank(WxBank.ABC); + order.setRemark("转账备注, 非必填"); + order.setAmount(new BigDecimal(10)); + //转账到银行卡,这里默认值是转账到银行卡 + order.setTransferType(WxTransferType.PAY_BANK); + + return service.transfer(order); + } + + /** + * 转账查询 + * + * @param outNo 商户转账订单号 + * @param wxTransferType 微信转账类型, + * .....这里没办法了只能这样写(┬_┬),请见谅 + * {@link WxTransferType#QUERY_BANK} + * {@link WxTransferType#GETTRANSFERINFO} + * + *

+ * 企业付款到零钱 + * 商户企业付款到银行卡 + *

+ * @return 对应的转账订单 + */ + @RequestMapping("transferQuery") + public Map transferQuery(String outNo, String wxTransferType) { + //默认查询银行卡的记录 com.egzosn.pay.wx.v3.bean.WxTransferType#QUERY_BANK + return service.transferQuery(outNo, wxTransferType); + } + + +} diff --git a/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/api/DefaultWxPayAssistService.java b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/api/DefaultWxPayAssistService.java index 994335e..2ea5602 100644 --- a/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/api/DefaultWxPayAssistService.java +++ b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/api/DefaultWxPayAssistService.java @@ -5,6 +5,7 @@ import java.nio.charset.StandardCharsets; import java.security.Signature; import java.security.cert.Certificate; import java.util.Map; +import java.util.TreeMap; import org.apache.http.HttpEntity; import org.apache.http.entity.ContentType; @@ -12,11 +13,14 @@ import org.apache.http.message.BasicHeader; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import static org.apache.http.entity.ContentType.APPLICATION_JSON; + import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONArray; import com.alibaba.fastjson.JSONObject; import com.alibaba.fastjson.serializer.SerializerFeature; import com.egzosn.pay.common.bean.MethodType; +import com.egzosn.pay.common.bean.NoticeParams; import com.egzosn.pay.common.bean.PayOrder; import com.egzosn.pay.common.bean.TransactionType; import com.egzosn.pay.common.exception.PayErrorException; @@ -26,6 +30,9 @@ import com.egzosn.pay.common.http.ResponseEntity; import com.egzosn.pay.common.http.UriVariables; import com.egzosn.pay.common.util.DateUtils; import com.egzosn.pay.common.util.sign.SignTextUtils; +import com.egzosn.pay.common.util.sign.encrypt.Base64; +import com.egzosn.pay.common.util.sign.encrypt.RSA; +import com.egzosn.pay.common.util.sign.encrypt.RSA2; import com.egzosn.pay.common.util.str.StringUtils; import com.egzosn.pay.wx.bean.WxPayError; import com.egzosn.pay.wx.v3.bean.WxTransactionType; @@ -127,7 +134,8 @@ public class DefaultWxPayAssistService implements WxPayAssistService { String token = String.format(WxConst.TOKEN_PATTERN, payConfigStorage.getMchId(), nonceStr, timestamp, serialNumber, sign); HttpStringEntity entity = new HttpStringEntity(body, ContentType.APPLICATION_JSON); entity.addHeader(new BasicHeader("Authorization", WxConst.SCHEMA.concat(token))); - entity.addHeader(new BasicHeader("User-Agent", "Pay-Java-Service")); + entity.addHeader(new BasicHeader("User-Agent", "X-Pay-Service")); + entity.addHeader(new BasicHeader("Accept", APPLICATION_JSON.getMimeType())); return entity; } @@ -169,36 +177,16 @@ public class DefaultWxPayAssistService implements WxPayAssistService { */ @Override public Certificate getCertificate(String serialNo) { - final Certificate certificate = AntCertificationUtil.getCertificate(serialNo); - if (null == certificate){ + Certificate certificate = AntCertificationUtil.getCertificate(serialNo); + if (null == certificate) { refreshCertificate(); + certificate = AntCertificationUtil.getCertificate(serialNo); } - - return null; + return certificate; } -/* *//** - * 我方对响应验签,和应答签名做比较,使用微信平台证书. - * - * @param params the params - * @return the boolean - *//* - public boolean responseSignVerify(ResponseSignVerifyParams params) { - - String wechatpaySerial = params.getWechatpaySerial(); - if (CERTIFICATE_MAP.isEmpty() || !CERTIFICATE_MAP.containsKey(wechatpaySerial)) { - wechatMetaContainer.getTenantIds().forEach(this::refreshCertificate); - } - Certificate certificate = CERTIFICATE_MAP.get(wechatpaySerial); - - final String signatureStr = createSign(true, params.getWechatpayTimestamp(), params.getWechatpayNonce(), params.getBody()); - Signature signer = Signature.getInstance("SHA256withRSA"); - signer.initVerify(certificate); - signer.update(signatureStr.getBytes(StandardCharsets.UTF_8)); - return signer.verify(Base64Utils.decodeFromString(params.getWechatpaySignature())); - }*/ } diff --git a/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/api/WxParameterStructure.java b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/api/WxParameterStructure.java new file mode 100644 index 0000000..b6031fb --- /dev/null +++ b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/api/WxParameterStructure.java @@ -0,0 +1,131 @@ +package com.egzosn.pay.wx.v3.api; + +import java.util.HashMap; +import java.util.Map; +import java.util.TreeMap; + + +import com.egzosn.pay.common.bean.Order; +import com.egzosn.pay.common.bean.OrderParaStructure; +import com.egzosn.pay.common.bean.PayOrder; +import com.egzosn.pay.common.http.UriVariables; +import com.egzosn.pay.common.util.MapGen; +import com.egzosn.pay.common.util.str.StringUtils; +import com.egzosn.pay.wx.v3.utils.WxConst; + +/** + * 微信参数构造器 + * @author Egan + *
+ * email egzosn@gmail.com
+ * date 2021/8/16
+ * 
+ */ +public class WxParameterStructure { + private final WxPayConfigStorage payConfigStorage; + + public WxParameterStructure(WxPayConfigStorage payConfigStorage) { + this.payConfigStorage = payConfigStorage; + } + + /** + * 获取公共参数 + * + * @return 公共参数 + */ + public Map getPublicParameters() { + + Map parameters = new TreeMap(); + parameters.put(WxConst.APPID, payConfigStorage.getAppId()); + parameters.put(WxConst.MCH_ID, payConfigStorage.getMchId()); + return parameters; + } + + + + + + + /** + * 加载结算信息 + * + * @param parameters 订单参数 + * @param order 支付订单 + * @return 订单参数 + */ + public void loadSettleInfo(Map parameters, PayOrder order) { + Object profitSharing = order.getAttr("profit_sharing"); + if (null != profitSharing) { + Map settleInfo = new MapGen("profit_sharing", profitSharing).getAttr(); + parameters.put("settle_info", settleInfo); + return; + } + //结算信息 + OrderParaStructure.loadParameters(parameters, "settle_info", order); + + + } + + + + /** + * 初始化通知URL必须为直接可访问的URL,不允许携带查询串,要求必须为https地址。 + * + * @param parameters 订单参数 + * @param order 订单信息 + * @return 订单参数 + */ + public void initNotifyUrl(Map parameters, Order order) { + OrderParaStructure.loadParameters(parameters, WxConst.NOTIFY_URL, payConfigStorage.getNotifyUrl()); + OrderParaStructure.loadParameters(parameters, WxConst.NOTIFY_URL, order); + } + + /** + * 获取服务商相关信息 + * + * @return 服务商相关信息 + */ + public String getSpParameters() { + Map attr = initSubMchId(null); + OrderParaStructure.loadParameters(attr, WxConst.SP_MCH_ID, payConfigStorage.getSpMchId()); + return UriVariables.getMapToParameters(attr); + } + /** + * 初始化商户相关信息 + * + * @param parameters 参数信息 + * @return 参数信息 + */ + public void initPartner(Map parameters) { + if (null == parameters) { + parameters = new HashMap<>(); + } + if (StringUtils.isNotEmpty(payConfigStorage.getSpAppId()) && StringUtils.isNotEmpty(payConfigStorage.getSpMchId())) { + payConfigStorage.setPartner(true); + parameters.put("sp_appid", payConfigStorage.getSpAppId()); + parameters.put(WxConst.SP_MCH_ID, payConfigStorage.getSpMchId()); + } + OrderParaStructure.loadParameters(parameters, "sub_appid", payConfigStorage.getSubAppId()); + initSubMchId(parameters); + } + + + /** + * 初始化商户相关信息 + * + * @param parameters 参数信息 + * @return 参数信息 + */ + public Map initSubMchId(Map parameters) { + if (null == parameters) { + parameters = new HashMap<>(); + } + if (StringUtils.isNotEmpty(payConfigStorage.getSubMchId())) { + payConfigStorage.setPartner(true); + parameters.put(WxConst.SUB_MCH_ID, payConfigStorage.getSubMchId()); + } + return parameters; + + } + +} diff --git a/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/api/WxPayConfigStorage.java b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/api/WxPayConfigStorage.java index e61f963..c765a91 100644 --- a/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/api/WxPayConfigStorage.java +++ b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/api/WxPayConfigStorage.java @@ -70,6 +70,13 @@ public class WxPayConfigStorage extends BasePayConfigStorage { */ private CertEnvironment certEnvironment; + /** + * 是否为服务商模式, 默认为false + */ + private boolean partner = false; + + + @Deprecated @Override public String getAppid() { @@ -222,4 +229,12 @@ public class WxPayConfigStorage extends BasePayConfigStorage { throw new PayErrorException(new PayException("读取证书异常", e.getMessage())); } } + + public boolean isPartner() { + return partner; + } + + public void setPartner(boolean partner) { + this.partner = partner; + } } diff --git a/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/api/WxPayService.java b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/api/WxPayService.java index 7615270..1858643 100644 --- a/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/api/WxPayService.java +++ b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/api/WxPayService.java @@ -4,6 +4,7 @@ import java.io.IOException; import java.io.InputStream; import java.net.URLEncoder; import java.security.PrivateKey; +import java.security.cert.Certificate; import java.util.Date; import java.util.HashMap; import java.util.LinkedHashMap; @@ -17,6 +18,7 @@ import static com.egzosn.pay.wx.api.WxConst.RETURN_CODE; import static com.egzosn.pay.wx.api.WxConst.RETURN_MSG_CODE; import static com.egzosn.pay.wx.api.WxConst.SANDBOXNEW; import static com.egzosn.pay.wx.api.WxConst.SUCCESS; +import static com.egzosn.pay.wx.v3.utils.AntCertificationUtil.getCertificate; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONObject; @@ -24,7 +26,8 @@ import com.egzosn.pay.common.api.BasePayService; import com.egzosn.pay.common.bean.BillType; import com.egzosn.pay.common.bean.CurType; import com.egzosn.pay.common.bean.MethodType; -import com.egzosn.pay.common.bean.Order; +import com.egzosn.pay.common.bean.NoticeParams; +import com.egzosn.pay.common.bean.OrderParaStructure; import com.egzosn.pay.common.bean.PayMessage; import com.egzosn.pay.common.bean.PayOrder; import com.egzosn.pay.common.bean.PayOutMessage; @@ -34,12 +37,10 @@ import com.egzosn.pay.common.bean.TransferOrder; import com.egzosn.pay.common.bean.result.PayException; import com.egzosn.pay.common.exception.PayErrorException; import com.egzosn.pay.common.http.HttpConfigStorage; -import com.egzosn.pay.common.http.HttpStringEntity; import com.egzosn.pay.common.http.ResponseEntity; import com.egzosn.pay.common.http.UriVariables; import com.egzosn.pay.common.util.DateUtils; import com.egzosn.pay.common.util.IOUtils; -import com.egzosn.pay.common.util.MapGen; import com.egzosn.pay.common.util.Util; import com.egzosn.pay.common.util.XML; import com.egzosn.pay.common.util.sign.SignTextUtils; @@ -73,16 +74,20 @@ public class WxPayService extends BasePayService { * api服务地址,默认为国内 */ private String apiServerUrl = WxConst.URI; - /** - * 是否为服务商模式, 默认为false - */ - private boolean partner = false; + /** * 辅助api */ private volatile WxPayAssistService wxPayAssistService; + /** + * 微信参数构造器 + */ + private volatile WxParameterStructure wxParameterStructure; + + + public WxPayAssistService getAssistService() { if (null == wxPayAssistService) { wxPayAssistService = new DefaultWxPayAssistService(this); @@ -122,7 +127,7 @@ public class WxPayService extends BasePayService { public BasePayService setPayConfigStorage(WxPayConfigStorage payConfigStorage) { payConfigStorage.loadCertEnvironment(); this.payConfigStorage = payConfigStorage; - + wxParameterStructure = new WxParameterStructure(payConfigStorage); return this; } @@ -147,13 +152,14 @@ public class WxPayService extends BasePayService { public String getReqUrl(TransactionType transactionType) { String type = transactionType.getType(); String partnerStr = ""; - if (partner) { + if (payConfigStorage.isPartner()) { partnerStr = "/partner"; } type = type.replace("{partner}", partnerStr); return apiServerUrl + (payConfigStorage.isTest() ? SANDBOXNEW : "") + type; } + /** * 回调校验 * @@ -162,84 +168,32 @@ public class WxPayService extends BasePayService { */ @Override public boolean verify(Map params) { - throw new PayErrorException(new WxPayError("", "等待作者实现")); + throw new PayErrorException(new WxPayError("", "微信V3不支持方式")); } - /** - * 获取公共参数 + * 验签,使用微信平台证书. * - * @return 公共参数 + * @param noticeParams 通知参数 + * @return the boolean */ - private Map getPublicParameters() { - - Map parameters = new TreeMap(); - parameters.put(WxConst.APPID, payConfigStorage.getAppId()); - parameters.put(WxConst.MCH_ID, payConfigStorage.getMchId()); + public boolean verify(NoticeParams noticeParams) { - return parameters; - - - } - - - /** - * 初始化商户相关信息 - * - * @param parameters 参数信息 - * @return 参数信息 - */ - private void initPartner(Map parameters) { - if (null == parameters) { - parameters = new HashMap<>(); - } - if (StringUtils.isNotEmpty(payConfigStorage.getSpAppId()) && StringUtils.isNotEmpty(payConfigStorage.getSpMchId())) { - this.partner = true; - parameters.put("sp_appid", payConfigStorage.getSpAppId()); - parameters.put(WxConst.SP_MCH_ID, payConfigStorage.getSpMchId()); - } - setParameters(parameters, "sub_appid", payConfigStorage.getSubAppId()); - initSubMchId(parameters); - } - - /** - * 初始化商户相关信息 - * - * @param parameters 参数信息 - * @return 参数信息 - */ - private Map initSubMchId(Map parameters) { - if (null == parameters) { - parameters = new HashMap<>(); - } - if (StringUtils.isNotEmpty(payConfigStorage.getSubMchId())) { - this.partner = true; - parameters.put(WxConst.SUB_MCH_ID, payConfigStorage.getSubMchId()); - } - return parameters; - - } - - - /** - * 加载结算信息 - * - * @param parameters 订单参数 - * @param order 支付订单 - * @return 订单参数 - */ - private void loadSettleInfo(Map parameters, PayOrder order) { - Object profitSharing = order.getAttr("profit_sharing"); - if (null != profitSharing) { - Map settleInfo = new MapGen("profit_sharing", profitSharing).getAttr(); - parameters.put("settle_info", settleInfo); - return; - } - //结算信息 - setParameters(parameters, "settle_info", order); + //当前使用的微信平台证书序列号 + String serial = noticeParams.getHeader("Wechatpay-Serial"); + //微信服务器的时间戳 + String timestamp = noticeParams.getHeader("Wechatpay-Timestamp"); + //微信服务器提供的随机串 + String nonce = noticeParams.getHeader("Wechatpay-Nonce"); + //微信平台签名 + String signature = noticeParams.getHeader("Wechatpay-Signature"); + Certificate certificate = getCertificate(serial); + //签名信息 + String signText = StringUtils.joining("\n", timestamp, nonce, JSON.toJSONString(noticeParams.getBody())); + return RSA2.verify(signText, signature, certificate, payConfigStorage.getInputCharset()); } @@ -252,33 +206,33 @@ public class WxPayService extends BasePayService { public JSONObject unifiedOrder(PayOrder order) { //统一下单 - Map parameters = getPublicParameters(); - initPartner(parameters); + Map parameters = wxParameterStructure.getPublicParameters(); + wxParameterStructure.initPartner(parameters); // 商品描述 - setParameters(parameters, "description", order.getSubject()); - setParameters(parameters, "description", order.getBody()); + OrderParaStructure.loadParameters(parameters, "description", order.getSubject()); + OrderParaStructure.loadParameters(parameters, "description", order.getBody()); // 订单号 parameters.put(WxConst.OUT_TRADE_NO, order.getOutTradeNo()); //交易结束时间 if (null != order.getExpirationTime()) { parameters.put("time_expire", DateUtils.formatDate(order.getExpirationTime(), DateUtils.YYYYMMDDHHMMSS)); } - setParameters(parameters, "attach", order.getAddition()); - initNotifyUrl(parameters, order); + OrderParaStructure.loadParameters(parameters, "attach", order.getAddition()); + wxParameterStructure.initNotifyUrl(parameters, order); //订单优惠标记 - setParameters(parameters, "goods_tag", order); + OrderParaStructure.loadParameters(parameters, "goods_tag", order); parameters.put("amount", Amount.getAmount(order.getPrice(), order.getCurType())); //优惠功能 - setParameters(parameters, "detail", order); + OrderParaStructure.loadParameters(parameters, "detail", order); //支付场景描述 - setParameters(parameters, WxConst.SCENE_INFO, order); - loadSettleInfo(parameters, order); + OrderParaStructure.loadParameters(parameters, WxConst.SCENE_INFO, order); + wxParameterStructure.loadSettleInfo(parameters, order); TransactionType transactionType = order.getTransactionType(); ((WxTransactionType) transactionType).setAttribute(parameters, order); - return wxPayAssistService.doExecute(parameters, order); + return getAssistService().doExecute(parameters, order); } @@ -341,31 +295,6 @@ public class WxPayService extends BasePayService { return RSA2.sign(content, privateKey, characterEncoding); } - - /** - * 初始化通知URL必须为直接可访问的URL,不允许携带查询串,要求必须为https地址。 - * - * @param parameters 订单参数 - * @param order 订单信息 - * @return 订单参数 - */ - private void initNotifyUrl(Map parameters, Order order) { - setParameters(parameters, WxConst.NOTIFY_URL, payConfigStorage.getNotifyUrl()); - setParameters(parameters, WxConst.NOTIFY_URL, order); - } - - /** - * 获取服务商相关信息 - * - * @return 服务商相关信息 - */ - private String getSpParameters() { - Map attr = initSubMchId(null); - setParameters(attr, WxConst.SP_MCH_ID, payConfigStorage.getSpMchId()); - return UriVariables.getMapToParameters(attr); - } - - /** * 将请求参数或者请求流转化为 Map * @@ -466,14 +395,14 @@ public class WxPayService extends BasePayService { @Override public Map query(String transactionId, String outTradeNo) { - String parameters = getSpParameters(); + String parameters = wxParameterStructure.getSpParameters(); WxTransactionType transactionType = WxTransactionType.QUERY_TRANSACTION_ID; String uriVariable = transactionId; if (StringUtils.isNotEmpty(outTradeNo)) { transactionType = WxTransactionType.QUERY_OUT_TRADE_NO; uriVariable = outTradeNo; } - return wxPayAssistService.doExecute(parameters, transactionType, uriVariable); + return getAssistService().doExecute(parameters, transactionType, uriVariable); } @@ -486,8 +415,8 @@ public class WxPayService extends BasePayService { */ @Override public Map close(String transactionId, String outTradeNo) { - String parameters = getSpParameters(); - return wxPayAssistService.doExecute(parameters, WxTransactionType.CLOSE, outTradeNo); + String parameters = wxParameterStructure.getSpParameters(); + return getAssistService().doExecute(parameters, WxTransactionType.CLOSE, outTradeNo); } /** @@ -499,14 +428,14 @@ public class WxPayService extends BasePayService { @Override public WxRefundResult refund(RefundOrder refundOrder) { //获取公共参数 - Map parameters = initSubMchId(null); - - setParameters(parameters, "transaction_id", refundOrder.getTradeNo()); - setParameters(parameters, OUT_TRADE_NO, refundOrder.getOutTradeNo()); - setParameters(parameters, "out_refund_no", refundOrder.getRefundNo()); - setParameters(parameters, "reason", refundOrder.getDescription()); - setParameters(parameters, "funds_account", refundOrder); - initNotifyUrl(parameters, refundOrder); + Map parameters = wxParameterStructure.initSubMchId(null); + + OrderParaStructure.loadParameters(parameters, "transaction_id", refundOrder.getTradeNo()); + OrderParaStructure.loadParameters(parameters, OUT_TRADE_NO, refundOrder.getOutTradeNo()); + OrderParaStructure.loadParameters(parameters, "out_refund_no", refundOrder.getRefundNo()); + OrderParaStructure.loadParameters(parameters, "reason", refundOrder.getDescription()); + OrderParaStructure.loadParameters(parameters, "funds_account", refundOrder); + wxParameterStructure.initNotifyUrl(parameters, refundOrder); RefundAmount refundAmount = new RefundAmount(); refundAmount.setRefund(Util.conversionCentAmount(refundOrder.getRefundAmount())); refundAmount.setTotal(Util.conversionCentAmount(refundOrder.getTotalAmount())); @@ -515,8 +444,8 @@ public class WxPayService extends BasePayService { refundAmount.setCurrency(curType.getType()); } parameters.put("amount", refundAmount); - setParameters(parameters, "amount", refundOrder); - return WxRefundResult.create(wxPayAssistService.doExecute(parameters, WxTransactionType.REFUND)); + OrderParaStructure.loadParameters(parameters, "amount", refundOrder); + return WxRefundResult.create(getAssistService().doExecute(parameters, WxTransactionType.REFUND)); } @@ -528,8 +457,8 @@ public class WxPayService extends BasePayService { */ @Override public Map refundquery(RefundOrder refundOrder) { - String parameters = UriVariables.getMapToParameters(initSubMchId(null)); - return wxPayAssistService.doExecute(parameters, WxTransactionType.REFUND_QUERY, refundOrder.getRefundNo()); + String parameters = UriVariables.getMapToParameters(wxParameterStructure.initSubMchId(null)); + return getAssistService().doExecute(parameters, WxTransactionType.REFUND_QUERY, refundOrder.getRefundNo()); } /** @@ -563,18 +492,18 @@ public class WxPayService extends BasePayService { //目前只支持日账单 parameters.put("bill_date", DateUtils.formatDate(billDate, DateUtils.YYYYMMDD)); String fileType = billType.getFileType(); - setParameters(parameters, "tar_type", fileType); + OrderParaStructure.loadParameters(parameters, "tar_type", fileType); if (billType instanceof WxAccountType) { - setParameters(parameters, "account_type", billType.getType()); + OrderParaStructure.loadParameters(parameters, "account_type", billType.getType()); } else { - initSubMchId(parameters).put("bill_type", billType.getType()); + wxParameterStructure.initSubMchId(parameters).put("bill_type", billType.getType()); } String body = UriVariables.getMapToParameters(parameters); - JSONObject result = wxPayAssistService.doExecute(body, WxTransactionType.valueOf(billType.getCustom())); + JSONObject result = getAssistService().doExecute(body, WxTransactionType.valueOf(billType.getCustom())); String downloadUrl = result.getString("download_url"); MethodType methodType = MethodType.GET; - HttpEntity entity = wxPayAssistService.buildHttpEntity(downloadUrl, "", methodType.name()); + HttpEntity entity = getAssistService().buildHttpEntity(downloadUrl, "", methodType.name()); ResponseEntity responseEntity = requestTemplate.doExecuteEntity(downloadUrl, entity, InputStream.class, methodType); InputStream inputStream = responseEntity.getBody(); int statusCode = responseEntity.getStatusCode(); @@ -598,14 +527,6 @@ public class WxPayService extends BasePayService { * 转账 * * @param order 转账订单 - *
-     *
-     *                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                 注意事项:
-     *                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                 ◆ 当返回错误码为“SYSTEMERROR”时,请不要更换商户订单号,一定要使用原商户订单号重试,否则可能造成重复支付等资金风险。
-     *                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                 ◆ XML具有可扩展性,因此返回参数可能会有新增,而且顺序可能不完全遵循此文档规范,如果在解析回包的时候发生错误,请商户务必不要换单重试,请商户联系客服确认付款情况。如果有新回包字段,会更新到此API文档中。
-     *                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                 ◆ 因为错误代码字段err_code的值后续可能会增加,所以商户如果遇到回包返回新的错误码,请商户务必不要换单重试,请商户联系客服确认付款情况。如果有新的错误码,会更新到此API文档中。
-     *                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                 ◆ 错误代码描述字段err_code_des只供人工定位问题时做参考,系统实现时请不要依赖这个字段来做自动化处理。
-     *                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                 
* @return 对应的转账结果 */ @Override diff --git a/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/order/Amount.java b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/order/Amount.java index 7c04136..8574451 100644 --- a/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/order/Amount.java +++ b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/order/Amount.java @@ -4,6 +4,7 @@ package com.egzosn.pay.wx.v3.bean.order; import java.math.BigDecimal; import com.egzosn.pay.common.bean.CurType; +import com.egzosn.pay.common.bean.DefaultCurType; import com.egzosn.pay.common.util.Util; /** @@ -66,10 +67,10 @@ public class Amount { public static Amount getAmount(BigDecimal total, CurType curType ) { // 总金额单位为分 Amount amount = new Amount(Util.conversionCentAmount(total)); - if (null != curType) { - amount.setCurrency(curType.getType()); + if (null == curType) { + curType = DefaultCurType.CNY; } - + amount.setCurrency(curType.getType()); return amount; } } diff --git a/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/utils/AntCertificationUtil.java b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/utils/AntCertificationUtil.java index 1660d2c..1807787 100644 --- a/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/utils/AntCertificationUtil.java +++ b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/utils/AntCertificationUtil.java @@ -67,14 +67,15 @@ public final class AntCertificationUtil { /** * 装载平台证书 - * - * @param serialNo 证书序列 + * @param serialNo 证书序列 * @param certificateStream 证书流 + * @return */ - public static void loadCertificate(String serialNo, InputStream certificateStream) { + public static Certificate loadCertificate(String serialNo, InputStream certificateStream) { try { Certificate certificate = CERTIFICATE_FACTORY.generateCertificate(certificateStream); CERTIFICATE_MAP.put(serialNo, certificate); + return certificate; } catch (CertificateException e) { throw new PayErrorException(new WxPayError(WxConst.FAILURE, " 在生成微信v3证书时发生错误,原因是" + e.getMessage()), e); diff --git a/pay-java-yiji/src/main/java/com/egzosn/pay/yiji/api/YiJiPayService.java b/pay-java-yiji/src/main/java/com/egzosn/pay/yiji/api/YiJiPayService.java index d530673..cd3c66c 100644 --- a/pay-java-yiji/src/main/java/com/egzosn/pay/yiji/api/YiJiPayService.java +++ b/pay-java-yiji/src/main/java/com/egzosn/pay/yiji/api/YiJiPayService.java @@ -13,6 +13,7 @@ import com.egzosn.pay.common.bean.BillType; import com.egzosn.pay.common.bean.CurType; import com.egzosn.pay.common.bean.DefaultCurType; import com.egzosn.pay.common.bean.MethodType; +import com.egzosn.pay.common.bean.NoticeParams; import com.egzosn.pay.common.bean.PayMessage; import com.egzosn.pay.common.bean.PayOrder; import com.egzosn.pay.common.bean.PayOutMessage; @@ -91,13 +92,24 @@ public class YiJiPayService extends BasePayService { @Override public boolean verify(Map params) { + return verify(new NoticeParams(params)); + + } + + /** + * 回调校验 + * + * @param noticeParams 回调回来的参数集 + * @return 签名校验 true通过 + */ + public boolean verify(NoticeParams noticeParams){ + final Map params = noticeParams.getBody(); if (params.get(SIGN) == null) { LOG.debug("易极付支付异常:params:" + params); return false; } return signVerify(params, (String) params.get(SIGN)); - } /** -- Gitee From f761e9e2b8cfc8c2b0164128c0e413992b6b2800 Mon Sep 17 00:00:00 2001 From: egan Date: Mon, 16 Aug 2021 22:45:57 +0800 Subject: [PATCH 220/299] =?UTF-8?q?=E5=BE=AE=E4=BF=A1V3=E6=94=AF=E4=BB=98?= =?UTF-8?q?=E5=8A=9F=E8=83=BD=E5=9F=BA=E6=9C=AC=E5=AE=9E=E7=8E=B0,?= =?UTF-8?q?=E5=B9=B6=E5=B7=B2=E6=B5=8B=E8=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../egzosn/pay/common/util/str/StringUtils.java | 6 ++---- .../pay/demo/controller/WxV3PayController.java | 14 +++++++------- .../pay/wx/v3/api/DefaultWxPayAssistService.java | 2 +- .../egzosn/pay/wx/v3/api/WxParameterStructure.java | 3 ++- .../java/com/egzosn/pay/wx/v3/utils/WxConst.java | 2 +- 5 files changed, 13 insertions(+), 14 deletions(-) diff --git a/pay-java-common/src/main/java/com/egzosn/pay/common/util/str/StringUtils.java b/pay-java-common/src/main/java/com/egzosn/pay/common/util/str/StringUtils.java index 42ae96f..79a94e6 100644 --- a/pay-java-common/src/main/java/com/egzosn/pay/common/util/str/StringUtils.java +++ b/pay-java-common/src/main/java/com/egzosn/pay/common/util/str/StringUtils.java @@ -156,10 +156,8 @@ public class StringUtils { if (StringUtils.isEmpty(s)) { continue; } - if (builder.length() > 0) { - builder.append(separator); - } - builder.append(s); + + builder.append(s).append(separator); } return builder.toString(); } diff --git a/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/WxV3PayController.java b/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/WxV3PayController.java index 73791ef..84880c4 100644 --- a/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/WxV3PayController.java +++ b/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/WxV3PayController.java @@ -54,15 +54,15 @@ public class WxV3PayController { public void init() { WxPayConfigStorage wxPayConfigStorage = new WxPayConfigStorage(); - wxPayConfigStorage.setAppId("wxc7b993ff15a9f27c"); - wxPayConfigStorage.setMchId("1602947766"); + wxPayConfigStorage.setAppId("wxc7b993ff15a9f271"); + wxPayConfigStorage.setMchId("1602947765"); // wxPayConfigStorage.setKeyPublic("转账公钥,转账时必填"); - wxPayConfigStorage.setSecretKey("9bd8f0e7af4841299d782406b7774f56"); - wxPayConfigStorage.setNotifyUrl("https://pay.egzosn.com"); - wxPayConfigStorage.setReturnUrl("https://pay.egzosn.com"); + wxPayConfigStorage.setSecretKey("9bd8f0e7af4841299d782406b7774f52"); + wxPayConfigStorage.setNotifyUrl("https://pay.egzosn.com/payback"); + wxPayConfigStorage.setReturnUrl("https://pay.egzosn.com/payback"); wxPayConfigStorage.setInputCharset("utf-8"); wxPayConfigStorage.setCertSign(true); - wxPayConfigStorage.setApiClientKeyP12("E:\\Documents\\gitee\\pay-java-parent\\pay-java-demo\\src\\main\\resources\\yifenli_mall.p12"); + wxPayConfigStorage.setApiClientKeyP12("yifenli_mall.p12"); wxPayConfigStorage.setCertStoreType(CertStoreType.PATH); service = new WxPayService(wxPayConfigStorage); //设置回调消息处理 @@ -142,7 +142,7 @@ public class WxV3PayController { public byte[] toWxQrPay(BigDecimal price) throws IOException { //获取对应的支付账户操作工具(可根据账户id) ByteArrayOutputStream baos = new ByteArrayOutputStream(); - ImageIO.write(service.genQrPay(new PayOrder("订单title", "摘要", null == price ? BigDecimal.valueOf(0.01) : price, System.currentTimeMillis() + "", WxTransactionType.NATIVE)), "JPEG", baos); + ImageIO.write(service.genQrPay(new PayOrder("测试商品", "测试商品", null == price ? BigDecimal.valueOf(0.01) : price, UUID.randomUUID().toString().replace("-", ""), WxTransactionType.NATIVE)), "JPEG", baos); return baos.toByteArray(); } diff --git a/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/api/DefaultWxPayAssistService.java b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/api/DefaultWxPayAssistService.java index 2ea5602..4e8d7e3 100644 --- a/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/api/DefaultWxPayAssistService.java +++ b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/api/DefaultWxPayAssistService.java @@ -134,7 +134,7 @@ public class DefaultWxPayAssistService implements WxPayAssistService { String token = String.format(WxConst.TOKEN_PATTERN, payConfigStorage.getMchId(), nonceStr, timestamp, serialNumber, sign); HttpStringEntity entity = new HttpStringEntity(body, ContentType.APPLICATION_JSON); entity.addHeader(new BasicHeader("Authorization", WxConst.SCHEMA.concat(token))); - entity.addHeader(new BasicHeader("User-Agent", "X-Pay-Service")); + entity.addHeader(new BasicHeader("User-Agent", "Pay-Java-Parent")); entity.addHeader(new BasicHeader("Accept", APPLICATION_JSON.getMimeType())); return entity; } diff --git a/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/api/WxParameterStructure.java b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/api/WxParameterStructure.java index b6031fb..a0bba61 100644 --- a/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/api/WxParameterStructure.java +++ b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/api/WxParameterStructure.java @@ -1,6 +1,7 @@ package com.egzosn.pay.wx.v3.api; import java.util.HashMap; +import java.util.LinkedHashMap; import java.util.Map; import java.util.TreeMap; @@ -35,7 +36,7 @@ public class WxParameterStructure { */ public Map getPublicParameters() { - Map parameters = new TreeMap(); + Map parameters = new LinkedHashMap<>(); parameters.put(WxConst.APPID, payConfigStorage.getAppId()); parameters.put(WxConst.MCH_ID, payConfigStorage.getMchId()); return parameters; diff --git a/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/utils/WxConst.java b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/utils/WxConst.java index e644019..978b790 100644 --- a/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/utils/WxConst.java +++ b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/utils/WxConst.java @@ -30,7 +30,7 @@ public final class WxConst { */ public static final String SANDBOXNEW = "sandboxnew/"; public static final String APPID = "appid"; - public static final String MCH_ID = "mch_id"; + public static final String MCH_ID = "mchid"; public static final String SUB_MCH_ID = "sub_mchid"; public static final String SP_MCH_ID = "sp_mchid"; public static final String OUT_TRADE_NO = "out_trade_no"; -- Gitee From aae4ffe61dcdac764d4d070b037d0e1a6c1a6991 Mon Sep 17 00:00:00 2001 From: egan Date: Wed, 18 Aug 2021 22:49:57 +0800 Subject: [PATCH 221/299] =?UTF-8?q?=E6=96=B0=E5=A2=9E=E5=9B=9E=E8=B0=83?= =?UTF-8?q?=E5=8F=82=E6=95=B0=E5=AF=B9=E8=B1=A1=EF=BC=8C=E5=9B=9E=E8=B0=83?= =?UTF-8?q?=E5=A4=84=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../egzosn/pay/common/api/BasePayService.java | 45 ++++++--- .../com/egzosn/pay/common/api/PayService.java | 14 ++- .../pay/common/bean/DefaultNoticeRequest.java | 95 +++++++++++++++++++ .../egzosn/pay/common/bean/NoticeParams.java | 2 + .../com/egzosn/pay/common/util/MapGen.java | 2 +- .../java/com/egzosn/pay/common/util/Util.java | 28 ++++-- pay-java-demo/pom.xml | 5 + .../pay/demo/controller/AliPayController.java | 31 ++++-- .../demo/controller/FuiouPayController.java | 25 ++++- .../pay/demo/controller/PayController.java | 41 ++++++-- .../demo/controller/PayPalPayController.java | 31 ++++-- .../controller/PayPalV2PayController.java | 36 ++++--- .../demo/controller/UnionPayController.java | 25 ++++- .../pay/demo/controller/WxPayController.java | 27 +++++- .../demo/controller/WxV3PayController.java | 38 ++------ .../egzosn/pay/wx/v3/api/WxPayService.java | 53 +++++++++-- .../com/egzosn/pay/wx/v3/utils/WxConst.java | 4 + pom.xml | 5 + 18 files changed, 396 insertions(+), 111 deletions(-) create mode 100644 pay-java-common/src/main/java/com/egzosn/pay/common/bean/DefaultNoticeRequest.java diff --git a/pay-java-common/src/main/java/com/egzosn/pay/common/api/BasePayService.java b/pay-java-common/src/main/java/com/egzosn/pay/common/api/BasePayService.java index 00b4b10..028fbcf 100644 --- a/pay-java-common/src/main/java/com/egzosn/pay/common/api/BasePayService.java +++ b/pay-java-common/src/main/java/com/egzosn/pay/common/api/BasePayService.java @@ -16,6 +16,7 @@ import org.slf4j.LoggerFactory; import com.alibaba.fastjson.JSON; import com.egzosn.pay.common.bean.BillType; +import com.egzosn.pay.common.bean.DefaultNoticeRequest; import com.egzosn.pay.common.bean.MethodType; import com.egzosn.pay.common.bean.NoticeParams; import com.egzosn.pay.common.bean.NoticeRequest; @@ -56,7 +57,7 @@ public abstract class BasePayService implements Pay /** * 支付消息拦截器 */ - protected List interceptors = new ArrayList(); + protected List> interceptors = new ArrayList>(); ; /** @@ -191,6 +192,17 @@ public abstract class BasePayService implements Pay */ @Override public Map getParameter2Map(Map parameterMap, InputStream is) { + return getNoticeParams(new DefaultNoticeRequest(parameterMap, is)).getBody(); + } + /** + * 将请求参数或者请求流转化为 Map + * + * @param request 通知请求 + * @return 获得回调的请求参数 + */ + @Override + public NoticeParams getNoticeParams(NoticeRequest request) { + final Map parameterMap = request.getParameterMap(); Map params = new TreeMap(); for (Map.Entry entry : parameterMap.entrySet()) { @@ -212,10 +224,9 @@ public abstract class BasePayService implements Pay } params.put(name, valueStr); } - return params; + return new NoticeParams(params); } - /** * 交易查询接口,带处理器 * @@ -399,34 +410,38 @@ public abstract class BasePayService implements Pay interceptors.add(interceptor); } + + /** * 将请求参数或者请求流转化为 Map * - * @param request 通知请求 - * @return 获得回调的请求参数 + * @param parameterMap 请求参数 + * @param is 请求流 + * @return 获得回调响应信息 */ + @Deprecated @Override - public NoticeParams getNoticeParams(NoticeRequest request) { - return null; + public PayOutMessage payBack(Map parameterMap, InputStream is) { + return payBack(new DefaultNoticeRequest(parameterMap, is)); } + /** - * 将请求参数或者请求流转化为 Map + * 回调处理 * - * @param parameterMap 请求参数 - * @param is 请求流 + * @param request 请求参数 * @return 获得回调响应信息 */ @Override - public PayOutMessage payBack(Map parameterMap, InputStream is) { - Map data = getParameter2Map(parameterMap, is); + public PayOutMessage payBack(NoticeRequest request) { + final NoticeParams noticeParams = getNoticeParams(request); if (LOG.isDebugEnabled()) { - LOG.debug("回调响应:" + JSON.toJSONString(data)); + LOG.debug("回调响应:{}" , JSON.toJSONString(noticeParams)); } - if (!verify(data)) { + if (!verify(noticeParams)) { return getPayOutMessage("fail", "失败"); } - PayMessage payMessage = this.createMessage(data); + PayMessage payMessage = this.createMessage(noticeParams.getBody()); Map context = new HashMap(); for (PayMessageInterceptor interceptor : interceptors) { if (!interceptor.intercept(payMessage, context, this)) { diff --git a/pay-java-common/src/main/java/com/egzosn/pay/common/api/PayService.java b/pay-java-common/src/main/java/com/egzosn/pay/common/api/PayService.java index 178d69c..0e0d03f 100644 --- a/pay-java-common/src/main/java/com/egzosn/pay/common/api/PayService.java +++ b/pay-java-common/src/main/java/com/egzosn/pay/common/api/PayService.java @@ -351,13 +351,21 @@ public interface PayService { T transferQuery(String outNo, String tradeNo, Callback callback); /** - * 将请求参数或者请求流转化为 Map + * 回调处理 * * @param parameterMap 请求参数 * @param is 请求流 * @return 获得回调响应信息 + * 过时方法,详情查看 {@link #payBack(NoticeRequest)} */ + @Deprecated PayOutMessage payBack(Map parameterMap, InputStream is); + /** + * 回调处理 + * @param request 请求参数 + * @return 获得回调响应信息 + */ + PayOutMessage payBack(NoticeRequest request); /** * 使用转换过的参数进行回调处理 @@ -371,7 +379,7 @@ public interface PayService { * 设置支付消息处理器,这里用于处理具体的支付业务 * * @param handler 消息处理器 - * 配合{@link com.egzosn.pay.common.api.PayService#payBack(java.util.Map, java.io.InputStream)}进行使用 + * 配合{@link com.egzosn.pay.common.api.PayService#payBack(NoticeRequest)}进行使用 *

* 默认使用{@link com.egzosn.pay.common.api.DefaultPayMessageHandler }进行实现 */ @@ -381,7 +389,7 @@ public interface PayService { * 设置支付消息处理器,这里用于处理具体的支付业务 * * @param interceptor 消息拦截器 - * 配合{@link com.egzosn.pay.common.api.PayService#payBack(java.util.Map, java.io.InputStream)}进行使用 + * 配合{@link com.egzosn.pay.common.api.PayService#payBack(NoticeRequest)}进行使用 *

* 默认使用{@link com.egzosn.pay.common.api.DefaultPayMessageHandler }进行实现 */ diff --git a/pay-java-common/src/main/java/com/egzosn/pay/common/bean/DefaultNoticeRequest.java b/pay-java-common/src/main/java/com/egzosn/pay/common/bean/DefaultNoticeRequest.java new file mode 100644 index 0000000..b5f7819 --- /dev/null +++ b/pay-java-common/src/main/java/com/egzosn/pay/common/bean/DefaultNoticeRequest.java @@ -0,0 +1,95 @@ +package com.egzosn.pay.common.bean; + +import java.io.IOException; +import java.io.InputStream; +import java.util.Collections; +import java.util.Enumeration; +import java.util.List; +import java.util.Map; + +/**` + * + * 默认的通知请求 + * @author Egan + *

+ * email egzosn@gmail.com
+ * date 2021/8/18
+ * 
+ */ +public class DefaultNoticeRequest implements NoticeRequest { + + private Map parameterMap; + private InputStream inputStream; + + private Map> headers; + + public DefaultNoticeRequest(Map parameterMap, InputStream inputStream) { + this.parameterMap = parameterMap; + this.inputStream = inputStream; + } + + public DefaultNoticeRequest(Map parameterMap, InputStream inputStream, Map> headers) { + this.parameterMap = parameterMap; + this.inputStream = inputStream; + this.headers = headers; + } + + public DefaultNoticeRequest(InputStream inputStream, Map> headers) { + this.inputStream = inputStream; + this.headers = headers; + } + + /** + * 根据请求头名称获取请求头信息 + * + * @param name 名称 + * @return 请求头值 + */ + @Override + public String getHeader(String name) { + List value = this.headers.get(name); + return (null == value || value.isEmpty()) ? null : value.get(0); + } + + /** + * 根据请求头名称获取请求头信息 + * + * @param name 名称 + * @return 请求头值 + */ + @Override + public Enumeration getHeaders(String name) { + return Collections.enumeration(this.headers.get(name)); + } + + /** + * 获取所有的请求头名称 + * + * @return 请求头名称 + */ + @Override + public Enumeration getHeaderNames() { + return Collections.enumeration(this.headers.keySet()); + } + + /** + * 输入流 + * + * @return 输入流 + * @throws IOException IOException + */ + @Override + public InputStream getInputStream() throws IOException { + return inputStream; + } + + /** + * 获取所有的请求参数 + * + * @return 请求参数 + */ + @Override + public Map getParameterMap() { + return parameterMap; + } +} diff --git a/pay-java-common/src/main/java/com/egzosn/pay/common/bean/NoticeParams.java b/pay-java-common/src/main/java/com/egzosn/pay/common/bean/NoticeParams.java index f89ac97..7cd494f 100644 --- a/pay-java-common/src/main/java/com/egzosn/pay/common/bean/NoticeParams.java +++ b/pay-java-common/src/main/java/com/egzosn/pay/common/bean/NoticeParams.java @@ -47,6 +47,8 @@ public class NoticeParams { */ private Map attr; + public NoticeParams() { + } public NoticeParams(Map body) { this.body = body; diff --git a/pay-java-common/src/main/java/com/egzosn/pay/common/util/MapGen.java b/pay-java-common/src/main/java/com/egzosn/pay/common/util/MapGen.java index 45b8a89..4953c14 100644 --- a/pay-java-common/src/main/java/com/egzosn/pay/common/util/MapGen.java +++ b/pay-java-common/src/main/java/com/egzosn/pay/common/util/MapGen.java @@ -20,7 +20,7 @@ public class MapGen { keyValue(key, value); } - public MapGen keyValue(K key, V value) { + public MapGen keyValue(K key, V value) { attr.put(key, value); return this; } diff --git a/pay-java-common/src/main/java/com/egzosn/pay/common/util/Util.java b/pay-java-common/src/main/java/com/egzosn/pay/common/util/Util.java index 7991c3c..53b9ae5 100644 --- a/pay-java-common/src/main/java/com/egzosn/pay/common/util/Util.java +++ b/pay-java-common/src/main/java/com/egzosn/pay/common/util/Util.java @@ -2,6 +2,8 @@ package com.egzosn.pay.common.util; import java.math.BigDecimal; import java.math.BigInteger; +import java.util.Collection; +import java.util.Map; public class Util { /** @@ -69,9 +71,11 @@ public class Util { if (n.toByteArray().length == 33) { tmpd = new byte[32]; System.arraycopy(n.toByteArray(), 1, tmpd, 0, 32); - } else if (n.toByteArray().length == 32) { + } + else if (n.toByteArray().length == 32) { tmpd = n.toByteArray(); - } else { + } + else { tmpd = new byte[32]; for (int i = 0; i < 32 - n.toByteArray().length; i++) { tmpd[i] = 0; @@ -348,7 +352,8 @@ public class Util { int algorism = 0; if (c >= '0' && c <= '9') { algorism = c - '0'; - } else { + } + else { algorism = c - 55; } result += Math.pow(16, max - i) * algorism; @@ -493,7 +498,8 @@ public class Util { int i = 0; try { i = Integer.parseInt(s, radix); - } catch (NumberFormatException ex) { + } + catch (NumberFormatException ex) { i = defaultInt; } return i; @@ -510,7 +516,8 @@ public class Util { int i = 0; try { i = Integer.parseInt(s); - } catch (NumberFormatException ex) { + } + catch (NumberFormatException ex) { i = defaultInt; } return i; @@ -574,7 +581,7 @@ public class Util { /** * 一百 */ - public static final BigDecimal HUNDRED = new BigDecimal(100); + public static final BigDecimal HUNDRED = new BigDecimal(100); /** * 元转分 @@ -597,4 +604,13 @@ public class Util { } + public static boolean isEmpty(Map map) { + return null == map || map.isEmpty(); + } + + public static boolean isEmpty(Collection collection) { + return null == collection || collection.isEmpty(); + } + + } \ No newline at end of file diff --git a/pay-java-demo/pom.xml b/pay-java-demo/pom.xml index c83932b..7cf711f 100644 --- a/pay-java-demo/pom.xml +++ b/pay-java-demo/pom.xml @@ -31,6 +31,11 @@ pay-java-wx ${pay.version} + + com.egzosn + pay-java-web-support + ${pay.version} + com.egzosn pay-java-ali diff --git a/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/AliPayController.java b/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/AliPayController.java index 867d7c5..f0a674c 100644 --- a/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/AliPayController.java +++ b/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/AliPayController.java @@ -26,6 +26,7 @@ import com.egzosn.pay.ali.bean.AliTransferOrder; import com.egzosn.pay.ali.bean.AliTransferType; import com.egzosn.pay.ali.bean.OrderSettle; import com.egzosn.pay.common.bean.CertStoreType; +import com.egzosn.pay.common.bean.NoticeParams; import com.egzosn.pay.common.bean.PayOrder; import com.egzosn.pay.common.bean.RefundOrder; import com.egzosn.pay.common.http.HttpConfigStorage; @@ -34,6 +35,7 @@ import com.egzosn.pay.common.util.sign.SignUtils; import com.egzosn.pay.demo.request.QueryOrder; import com.egzosn.pay.demo.service.handler.AliPayMessageHandler; import com.egzosn.pay.demo.service.interceptor.AliPayMessageInterceptor; +import com.egzosn.pay.web.support.HttpRequestNoticeParams; /** @@ -223,13 +225,13 @@ public class AliPayController { public String payBackBefore(HttpServletRequest request) throws IOException { //获取支付方返回的对应参数 - Map params = service.getParameter2Map(request.getParameterMap(), request.getInputStream()); - if (null == params) { + NoticeParams noticeParams = service.getNoticeParams(new HttpRequestNoticeParams(request)); + if (null == noticeParams) { return service.getPayOutMessage("fail", "失败").toMessage(); } //校验 - if (service.verify(params)) { + if (service.verify(noticeParams)) { //这里处理业务逻辑 //......业务逻辑处理块........ return service.successPayOutMessage(null).toMessage(); @@ -242,19 +244,36 @@ public class AliPayController { * 支付回调地址 * * @param request 请求 - * @return 返回对应的响应码 + * @return 是否成功 *

* 业务处理在对应的PayMessageHandler里面处理,在哪里设置PayMessageHandler,详情查看{@link com.egzosn.pay.common.api.PayService#setPayMessageHandler(com.egzosn.pay.common.api.PayMessageHandler)} *

* 如果未设置 {@link com.egzosn.pay.common.api.PayMessageHandler} 那么会使用默认的 {@link com.egzosn.pay.common.api.DefaultPayMessageHandler} * @throws IOException IOException */ - @RequestMapping(value = "payBack.json") - public String payBack(HttpServletRequest request) throws IOException { + @Deprecated + @RequestMapping(value = "payBackOld.json") + public String payBackOld(HttpServletRequest request) throws IOException { //业务处理在对应的PayMessageHandler里面处理,在哪里设置PayMessageHandler,详情查看com.egzosn.pay.common.api.PayService.setPayMessageHandler() return service.payBack(request.getParameterMap(), request.getInputStream()).toMessage(); } + /** + * 支付回调地址 + * + * @param request 请求 + * @return 是否成功 + *

+ * 业务处理在对应的PayMessageHandler里面处理,在哪里设置PayMessageHandler,详情查看{@link com.egzosn.pay.common.api.PayService#setPayMessageHandler(com.egzosn.pay.common.api.PayMessageHandler)} + *

+ * 如果未设置 {@link com.egzosn.pay.common.api.PayMessageHandler} 那么会使用默认的 {@link com.egzosn.pay.common.api.DefaultPayMessageHandler} + * @throws IOException IOException + */ + @RequestMapping(value = "payBack.json") + public String payBack(HttpServletRequest request) { + //业务处理在对应的PayMessageHandler里面处理,在哪里设置PayMessageHandler,详情查看com.egzosn.pay.common.api.PayService.setPayMessageHandler() + return service.payBack(new HttpRequestNoticeParams(request)).toMessage(); + } /** * 查询 diff --git a/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/FuiouPayController.java b/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/FuiouPayController.java index 046001e..0ea09e5 100644 --- a/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/FuiouPayController.java +++ b/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/FuiouPayController.java @@ -7,6 +7,8 @@ import com.egzosn.pay.common.bean.PayOrder; import com.egzosn.pay.fuiou.api.FuiouPayConfigStorage; import com.egzosn.pay.fuiou.api.FuiouPayService; import com.egzosn.pay.fuiou.bean.FuiouTransactionType; +import com.egzosn.pay.web.support.HttpRequestNoticeParams; + import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @@ -112,15 +114,32 @@ public class FuiouPayController { * * @return 是否成功 * - * 业务处理在对应的PayMessageHandler里面处理,在哪里设置PayMessageHandler,详情查看{@link PayService#setPayMessageHandler(com.egzosn.pay.common.api.PayMessageHandler)} + * 业务处理在对应的PayMessageHandler里面处理,在哪里设置PayMessageHandler,详情查看{@link com.egzosn.pay.common.api.PayService#setPayMessageHandler(com.egzosn.pay.common.api.PayMessageHandler)} * * 如果未设置 {@link com.egzosn.pay.common.api.PayMessageHandler} 那么会使用默认的 {@link com.egzosn.pay.common.api.DefaultPayMessageHandler} * @throws IOException IOException */ - @RequestMapping(value = "payBack.json") - public String payBack(HttpServletRequest request) throws IOException { + @Deprecated + @RequestMapping(value = "payBackOld.json") + public String payBackOld(HttpServletRequest request) throws IOException { //业务处理在对应的PayMessageHandler里面处理,在哪里设置PayMessageHandler,详情查看com.egzosn.pay.common.api.PayService.setPayMessageHandler() return service.payBack(request.getParameterMap(), request.getInputStream()).toMessage(); } + /** + * 支付回调地址 + * + * @param request 请求 + * @return 是否成功 + *

+ * 业务处理在对应的PayMessageHandler里面处理,在哪里设置PayMessageHandler,详情查看{@link com.egzosn.pay.common.api.PayService#setPayMessageHandler(com.egzosn.pay.common.api.PayMessageHandler)} + *

+ * 如果未设置 {@link com.egzosn.pay.common.api.PayMessageHandler} 那么会使用默认的 {@link com.egzosn.pay.common.api.DefaultPayMessageHandler} + * @throws IOException IOException + */ + @RequestMapping(value = "payBack.json") + public String payBack(HttpServletRequest request) { + //业务处理在对应的PayMessageHandler里面处理,在哪里设置PayMessageHandler,详情查看com.egzosn.pay.common.api.PayService.setPayMessageHandler() + return service.payBack(new HttpRequestNoticeParams(request)).toMessage(); + } } 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 d6aa4d0..3e23fd6 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 @@ -27,6 +27,7 @@ import com.egzosn.pay.common.api.PayConfigStorage; import com.egzosn.pay.common.api.PayMessageInterceptor; import com.egzosn.pay.common.api.PayService; import com.egzosn.pay.common.bean.MethodType; +import com.egzosn.pay.common.bean.NoticeParams; import com.egzosn.pay.common.bean.PayMessage; import com.egzosn.pay.common.bean.PayOrder; import com.egzosn.pay.common.bean.PayOutMessage; @@ -41,6 +42,7 @@ import com.egzosn.pay.demo.entity.PayType; import com.egzosn.pay.demo.request.QueryOrder; import com.egzosn.pay.demo.service.ApyAccountService; import com.egzosn.pay.demo.service.PayResponse; +import com.egzosn.pay.web.support.HttpRequestNoticeParams; import com.egzosn.pay.wx.bean.WxTransactionType; /** @@ -319,32 +321,52 @@ public class PayController { } + /** + * 支付回调地址 + * 方式三 + * + * @param request 请求 + * @param payId 账户id + * @return 支付是否成功 + * @throws IOException IOException + * 拦截器相关增加, 详情查看{@link com.egzosn.pay.common.api.PayService#addPayMessageInterceptor(PayMessageInterceptor)} + *

+ * 业务处理在对应的PayMessageHandler里面处理,在哪里设置PayMessageHandler,详情查看{@link com.egzosn.pay.common.api.PayService#setPayMessageHandler(com.egzosn.pay.common.api.PayMessageHandler)} + *

+ * 如果未设置 {@link com.egzosn.pay.common.api.PayMessageHandler} 那么会使用默认的 {@link com.egzosn.pay.common.api.DefaultPayMessageHandler} + */ + @RequestMapping(value = "payBack{payId}.json") + public String payBack(HttpServletRequest request, @PathVariable Integer payId) throws IOException { + //业务处理在对应的PayMessageHandler里面处理,在哪里设置PayMessageHandler,详情查看com.egzosn.pay.common.api.PayService.setPayMessageHandler() + PayResponse payResponse = service.getPayResponse(payId); + return payResponse.getService().payBack(new HttpRequestNoticeParams(request)).toMessage(); + } /** * 支付回调地址 方式一 *

- * 方式二,{@link #payBack(HttpServletRequest, Integer)} 是属于简化方式, 试用与简单的业务场景 + * 建议使用 方式三,{@link #payBack(HttpServletRequest, Integer)} 是属于简化方式, 试用与简单的业务场景 * * @param request 请求 * @param payId 账户id * @return 支付是否成功 - * @throws IOException IOException */ @RequestMapping(value = "payBackOne{payId}.json") - public String payBackOne(HttpServletRequest request, @PathVariable Integer payId) throws IOException { + public String payBackOne(HttpServletRequest request, @PathVariable Integer payId) { //根据账户id,获取对应的支付账户操作工具 PayResponse payResponse = service.getPayResponse(payId); PayConfigStorage storage = payResponse.getStorage(); //获取支付方返回的对应参数 - Map params = payResponse.getService().getParameter2Map(request.getParameterMap(), request.getInputStream()); -// Map params = JSONObject.parseObject("{\"bizType\":\"000201\",\"signPubKeyCert\":\"-----BEGIN CERTIFICATE-----\\r\\nMIIEQzCCAyugAwIBAgIFEBJJZVgwDQYJKoZIhvcNAQEFBQAwWDELMAkGA1UEBhMC\\r\\nQ04xMDAuBgNVBAoTJ0NoaW5hIEZpbmFuY2lhbCBDZXJ0aWZpY2F0aW9uIEF1dGhv\\r\\ncml0eTEXMBUGA1UEAxMOQ0ZDQSBURVNUIE9DQTEwHhcNMTcxMTAxMDcyNDA4WhcN\\r\\nMjAxMTAxMDcyNDA4WjB3MQswCQYDVQQGEwJjbjESMBAGA1UEChMJQ0ZDQSBPQ0Ex\\r\\nMQ4wDAYDVQQLEwVDVVBSQTEUMBIGA1UECxMLRW50ZXJwcmlzZXMxLjAsBgNVBAMU\\r\\nJTA0MUBaMjAxNy0xMS0xQDAwMDQwMDAwOlNJR05AMDAwMDAwMDEwggEiMA0GCSqG\\r\\nSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDDIWO6AESrg+34HgbU9mSpgef0sl6avr1d\\r\\nbD/IjjZYM63SoQi3CZHZUyoyzBKodRzowJrwXmd+hCmdcIfavdvfwi6x+ptJNp9d\\r\\nEtpfEAnJk+4quriQFj1dNiv6uP8ARgn07UMhgdYB7D8aA1j77Yk1ROx7+LFeo7rZ\\r\\nDdde2U1opPxjIqOPqiPno78JMXpFn7LiGPXu75bwY2rYIGEEImnypgiYuW1vo9UO\\r\\nG47NMWTnsIdy68FquPSw5FKp5foL825GNX3oJSZui8d2UDkMLBasf06Jz0JKz5AV\\r\\nblaI+s24/iCfo8r+6WaCs8e6BDkaijJkR/bvRCQeQpbX3V8WoTLVAgMBAAGjgfQw\\r\\ngfEwHwYDVR0jBBgwFoAUz3CdYeudfC6498sCQPcJnf4zdIAwSAYDVR0gBEEwPzA9\\r\\nBghggRyG7yoBATAxMC8GCCsGAQUFBwIBFiNodHRwOi8vd3d3LmNmY2EuY29tLmNu\\r\\nL3VzL3VzLTE0Lmh0bTA5BgNVHR8EMjAwMC6gLKAqhihodHRwOi8vdWNybC5jZmNh\\r\\nLmNvbS5jbi9SU0EvY3JsMjQ4NzIuY3JsMAsGA1UdDwQEAwID6DAdBgNVHQ4EFgQU\\r\\nmQQLyuqYjES7qKO+zOkzEbvdFwgwHQYDVR0lBBYwFAYIKwYBBQUHAwIGCCsGAQUF\\r\\nBwMEMA0GCSqGSIb3DQEBBQUAA4IBAQAujhBuOcuxA+VzoUH84uoFt5aaBM3vGlpW\\r\\nKVMz6BUsLbIpp1ho5h+LaMnxMs6jdXXDh/du8X5SKMaIddiLw7ujZy1LibKy2jYi\\r\\nYYfs3tbZ0ffCKQtv78vCgC+IxUUurALY4w58fRLLdu8u8p9jyRFHsQEwSq+W5+bP\\r\\nMTh2w7cDd9h+6KoCN6AMI1Ly7MxRIhCbNBL9bzaxF9B5GK86ARY7ixkuDCEl4XCF\\r\\nJGxeoye9R46NqZ6AA/k97mJun//gmUjStmb9PUXA59fR5suAB5o/5lBySZ8UXkrI\\r\\npp/iLT8vIl1hNgLh0Ghs7DBSx99I+S3VuUzjHNxL6fGRhlix7Rb8\\r\\n-----END CERTIFICATE-----\",\"orderId\":\"20171213224128\",\"signature\":\"l8xBYSoMNzt01DDa9/JYcrQKWxN5tasUgSxf6NNsQK5t+DqMr2G9qhHXnDg5bEzeRyTFP4bM3htX9RTRhXYDy7EEsL46ZD4ib5I6mp2wXx+26zscUcLdJUiddkY5eFvQK4tPC8blw7Y6p858yiVJpHgbOK3cONhS7vwPJtK2jMbkY+GATu3aZ4iygkQc75cG+EW8nJQVwLNh7q9A6A6II18EFxR7XubdlIHXv/InVaS6ux8Wh2nmQlhRRnLtHq1ri7v1QPlu2FzM+kaf7/fn61iGr8zEPj62NzWDXue62LUfb4kTRgdkcJnfJBJl8vjZ/w93UtsnK3zjzJC/Nu+wCw==\",\"txnSubType\":\"01\",\"traceNo\":\"492156\",\"accNo\":\"6221********0000\",\"settleAmt\":\"1000\",\"settleCurrencyCode\":\"156\",\"settleDate\":\"1213\",\"txnType\":\"01\",\"encoding\":\"UTF-8\",\"version\":\"5.1.0\",\"queryId\":\"511712132241284921568\",\"accessType\":\"0\",\"exchangeRate\":\"0\",\"respMsg\":\"success\",\"traceTime\":\"1213224128\",\"txnTime\":\"20171213224128\",\"merId\":\"777290058154626\",\"currencyCode\":\"156\",\"respCode\":\"00\",\"signMethod\":\"01\",\"txnAmt\":\"1000\"}"); + final PayService service = payResponse.getService(); + final NoticeParams noticeParams = service.getNoticeParams(new HttpRequestNoticeParams(request)); - if (null == params) { + if (null == noticeParams) { return payResponse.getService().getPayOutMessage("fail", "失败").toMessage(); } //校验 - if (payResponse.getService().verify(params)) { + if (payResponse.getService().verify(noticeParams)) { + Map params = noticeParams.getBody(); //方式一 或者创建PayMessage的子类,AliPayMessage,WxPayMessage等等 /* PayMessage message = new PayMessage(params, storage.getPayType(), storage.getMsgType().name()); PayOutMessage outMessage = payResponse.getRouter().route(message);*/ @@ -379,8 +401,9 @@ public class PayController { *

* 如果未设置 {@link com.egzosn.pay.common.api.PayMessageHandler} 那么会使用默认的 {@link com.egzosn.pay.common.api.DefaultPayMessageHandler} */ - @RequestMapping(value = "payBack{payId}.json") - public String payBack(HttpServletRequest request, @PathVariable Integer payId) throws IOException { + @Deprecated + @RequestMapping(value = "payBackOld{payId}.json") + public String payBackOld(HttpServletRequest request, @PathVariable Integer payId) throws IOException { //业务处理在对应的PayMessageHandler里面处理,在哪里设置PayMessageHandler,详情查看com.egzosn.pay.common.api.PayService.setPayMessageHandler() PayResponse payResponse = service.getPayResponse(payId); return payResponse.getService().payBack(request.getParameterMap(), request.getInputStream()).toMessage(); diff --git a/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/PayPalPayController.java b/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/PayPalPayController.java index 934b8b1..e40d037 100644 --- a/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/PayPalPayController.java +++ b/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/PayPalPayController.java @@ -22,6 +22,7 @@ import com.egzosn.pay.common.http.HttpConfigStorage; import com.egzosn.pay.paypal.api.PayPalConfigStorage; import com.egzosn.pay.paypal.api.PayPalPayService; import com.egzosn.pay.paypal.bean.PayPalTransactionType; +import com.egzosn.pay.web.support.HttpRequestNoticeParams; /** * 发起支付入口 @@ -129,17 +130,35 @@ public class PayPalPayController { * 支付回调地址 * * @param request 请求 - * @return 结果 + * + * @return 是否成功 + * + * 业务处理在对应的PayMessageHandler里面处理,在哪里设置PayMessageHandler,详情查看{@link com.egzosn.pay.common.api.PayService#setPayMessageHandler(com.egzosn.pay.common.api.PayMessageHandler)} + * + * 如果未设置 {@link com.egzosn.pay.common.api.PayMessageHandler} 那么会使用默认的 {@link com.egzosn.pay.common.api.DefaultPayMessageHandler} * @throws IOException IOException - * 业务处理在对应的PayMessageHandler里面处理,在哪里设置PayMessageHandler,详情查看{@link com.egzosn.pay.common.api.PayService#setPayMessageHandler(com.egzosn.pay.common.api.PayMessageHandler)} - *

- * 如果未设置 {@link com.egzosn.pay.common.api.PayMessageHandler} 那么会使用默认的 {@link com.egzosn.pay.common.api.DefaultPayMessageHandler} */ - @RequestMapping(value = "payBack.json") - public String payBack(HttpServletRequest request) throws IOException { + @RequestMapping(value = "payBackOld.json") + public String payBackOld(HttpServletRequest request) throws IOException { //业务处理在对应的PayMessageHandler里面处理,在哪里设置PayMessageHandler,详情查看com.egzosn.pay.common.api.PayService.setPayMessageHandler() return service.payBack(request.getParameterMap(), request.getInputStream()).toMessage(); } + /** + * 支付回调地址 + * + * @param request 请求 + * @return 是否成功 + *

+ * 业务处理在对应的PayMessageHandler里面处理,在哪里设置PayMessageHandler,详情查看{@link com.egzosn.pay.common.api.PayService#setPayMessageHandler(com.egzosn.pay.common.api.PayMessageHandler)} + *

+ * 如果未设置 {@link com.egzosn.pay.common.api.PayMessageHandler} 那么会使用默认的 {@link com.egzosn.pay.common.api.DefaultPayMessageHandler} + * @throws IOException IOException + */ + @RequestMapping(value = "payBack.json") + public String payBack(HttpServletRequest request) { + //业务处理在对应的PayMessageHandler里面处理,在哪里设置PayMessageHandler,详情查看com.egzosn.pay.common.api.PayService.setPayMessageHandler() + return service.payBack(new HttpRequestNoticeParams(request)).toMessage(); + } } diff --git a/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/PayPalV2PayController.java b/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/PayPalV2PayController.java index 94917e7..0febd38 100644 --- a/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/PayPalV2PayController.java +++ b/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/PayPalV2PayController.java @@ -23,6 +23,7 @@ import com.egzosn.pay.paypal.v2.bean.PayPalOrder; import com.egzosn.pay.paypal.v2.bean.order.AddressPortable; import com.egzosn.pay.paypal.v2.bean.order.Name; import com.egzosn.pay.paypal.v2.bean.order.ShippingDetail; +import com.egzosn.pay.web.support.HttpRequestNoticeParams; /** * 发起支付入口 @@ -138,26 +139,39 @@ public class PayPalV2PayController { return "failure"; } - /* */ - /** * 支付回调地址 - * 注意:这里不是异步回调的通知 IPN 地址设置的路径:https://developer.paypal.com/developer/ipnSimulator/ - * 参数解析与校验 https://developer.paypal.com/docs/api-basics/notifications/ipn/IPNIntro/#id08CKFJ00JYK * * @param request 请求 - * @return 结果 + * + * @return 是否成功 + * + * 业务处理在对应的PayMessageHandler里面处理,在哪里设置PayMessageHandler,详情查看{@link com.egzosn.pay.common.api.PayService#setPayMessageHandler(com.egzosn.pay.common.api.PayMessageHandler)} + * + * 如果未设置 {@link com.egzosn.pay.common.api.PayMessageHandler} 那么会使用默认的 {@link com.egzosn.pay.common.api.DefaultPayMessageHandler} * @throws IOException IOException - * 业务处理在对应的PayMessageHandler里面处理,在哪里设置PayMessageHandler,详情查看{@link PayService#setPayMessageHandler(com.egzosn.pay.common.api.PayMessageHandler)} - *

- * 如果未设置 {@link com.egzosn.pay.common.api.PayMessageHandler} 那么会使用默认的 {@link com.egzosn.pay.common.api.DefaultPayMessageHandler} */ - @RequestMapping(value = "payBack.json") - public String payBack(HttpServletRequest request) throws IOException { + @RequestMapping(value = "payBackOld.json") + public String payBackOld(HttpServletRequest request) throws IOException { //业务处理在对应的PayMessageHandler里面处理,在哪里设置PayMessageHandler,详情查看com.egzosn.pay.common.api.PayService.setPayMessageHandler() - // 参数解析与校验 https://developer.paypal.com/docs/api-basics/notifications/ipn/IPNIntro/#id08CKFJ00JYK return service.payBack(request.getParameterMap(), request.getInputStream()).toMessage(); } + /** + * 支付回调地址 + * + * @param request 请求 + * @return 是否成功 + *

+ * 业务处理在对应的PayMessageHandler里面处理,在哪里设置PayMessageHandler,详情查看{@link com.egzosn.pay.common.api.PayService#setPayMessageHandler(com.egzosn.pay.common.api.PayMessageHandler)} + *

+ * 如果未设置 {@link com.egzosn.pay.common.api.PayMessageHandler} 那么会使用默认的 {@link com.egzosn.pay.common.api.DefaultPayMessageHandler} + * @throws IOException IOException + */ + @RequestMapping(value = "payBack.json") + public String payBack(HttpServletRequest request) { + //业务处理在对应的PayMessageHandler里面处理,在哪里设置PayMessageHandler,详情查看com.egzosn.pay.common.api.PayService.setPayMessageHandler() + return service.payBack(new HttpRequestNoticeParams(request)).toMessage(); + } } diff --git a/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/UnionPayController.java b/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/UnionPayController.java index 5cb3135..297bd06 100644 --- a/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/UnionPayController.java +++ b/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/UnionPayController.java @@ -15,6 +15,8 @@ import com.egzosn.pay.union.api.UnionPayConfigStorage; import com.egzosn.pay.union.api.UnionPayService; import com.egzosn.pay.union.bean.UnionRefundResult; import com.egzosn.pay.union.bean.UnionTransactionType; +import com.egzosn.pay.web.support.HttpRequestNoticeParams; + import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @@ -231,18 +233,33 @@ public class UnionPayController { * * @param request 请求 * - * @return 是否成功 + * @return 是否成功 * - * 业务处理在对应的PayMessageHandler里面处理,在哪里设置PayMessageHandler,详情查看{@link PayService#setPayMessageHandler(com.egzosn.pay.common.api.PayMessageHandler)} + * 业务处理在对应的PayMessageHandler里面处理,在哪里设置PayMessageHandler,详情查看{@link com.egzosn.pay.common.api.PayService#setPayMessageHandler(com.egzosn.pay.common.api.PayMessageHandler)} * * 如果未设置 {@link com.egzosn.pay.common.api.PayMessageHandler} 那么会使用默认的 {@link com.egzosn.pay.common.api.DefaultPayMessageHandler} * @throws IOException IOException + */ + @RequestMapping(value = "payBackOld.json") + public String payBackOld(HttpServletRequest request) throws IOException { + //业务处理在对应的PayMessageHandler里面处理,在哪里设置PayMessageHandler,详情查看com.egzosn.pay.common.api.PayService.setPayMessageHandler() + return service.payBack(request.getParameterMap(), request.getInputStream()).toMessage(); + } + /** + * 支付回调地址 * + * @param request 请求 + * @return 是否成功 + *

+ * 业务处理在对应的PayMessageHandler里面处理,在哪里设置PayMessageHandler,详情查看{@link com.egzosn.pay.common.api.PayService#setPayMessageHandler(com.egzosn.pay.common.api.PayMessageHandler)} + *

+ * 如果未设置 {@link com.egzosn.pay.common.api.PayMessageHandler} 那么会使用默认的 {@link com.egzosn.pay.common.api.DefaultPayMessageHandler} + * @throws IOException IOException */ @RequestMapping(value = "payBack.json") - public String payBack(HttpServletRequest request) throws IOException { + public String payBack(HttpServletRequest request) { //业务处理在对应的PayMessageHandler里面处理,在哪里设置PayMessageHandler,详情查看com.egzosn.pay.common.api.PayService.setPayMessageHandler() - return service.payBack(request.getParameterMap(), request.getInputStream()).toMessage(); + return service.payBack(new HttpRequestNoticeParams(request)).toMessage(); } diff --git a/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/WxPayController.java b/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/WxPayController.java index fc0bef7..3ac4464 100644 --- a/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/WxPayController.java +++ b/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/WxPayController.java @@ -5,6 +5,7 @@ package com.egzosn.pay.demo.controller; import com.egzosn.pay.common.bean.*; import com.egzosn.pay.common.http.HttpConfigStorage; import com.egzosn.pay.demo.request.QueryOrder; +import com.egzosn.pay.web.support.HttpRequestNoticeParams; import com.egzosn.pay.wx.api.WxPayConfigStorage; import com.egzosn.pay.wx.api.WxPayService; import com.egzosn.pay.wx.bean.*; @@ -245,13 +246,13 @@ public class WxPayController { public String payBackBefore(HttpServletRequest request) throws IOException { //获取支付方返回的对应参数 - Map params = service.getParameter2Map(request.getParameterMap(), request.getInputStream()); - if (null == params) { + NoticeParams noticeParams = service.getNoticeParams(new HttpRequestNoticeParams(request)); + if (null == noticeParams) { return service.getPayOutMessage("fail", "失败").toMessage(); } //校验 - if (service.verify(params)) { + if (service.verify(noticeParams)) { //这里处理业务逻辑 //......业务逻辑处理块........ return service.successPayOutMessage(null).toMessage(); @@ -271,11 +272,27 @@ public class WxPayController { * 如果未设置 {@link com.egzosn.pay.common.api.PayMessageHandler} 那么会使用默认的 {@link com.egzosn.pay.common.api.DefaultPayMessageHandler} * @throws IOException IOException */ - @RequestMapping(value = "payBack.json") - public String payBack(HttpServletRequest request) throws IOException { + @RequestMapping(value = "payBackOld.json") + public String payBackOld(HttpServletRequest request) throws IOException { //业务处理在对应的PayMessageHandler里面处理,在哪里设置PayMessageHandler,详情查看com.egzosn.pay.common.api.PayService.setPayMessageHandler() return service.payBack(request.getParameterMap(), request.getInputStream()).toMessage(); } + /** + * 支付回调地址 + * + * @param request 请求 + * @return 是否成功 + *

+ * 业务处理在对应的PayMessageHandler里面处理,在哪里设置PayMessageHandler,详情查看{@link com.egzosn.pay.common.api.PayService#setPayMessageHandler(com.egzosn.pay.common.api.PayMessageHandler)} + *

+ * 如果未设置 {@link com.egzosn.pay.common.api.PayMessageHandler} 那么会使用默认的 {@link com.egzosn.pay.common.api.DefaultPayMessageHandler} + * @throws IOException IOException + */ + @RequestMapping(value = "payBack.json") + public String payBack(HttpServletRequest request) { + //业务处理在对应的PayMessageHandler里面处理,在哪里设置PayMessageHandler,详情查看com.egzosn.pay.common.api.PayService.setPayMessageHandler() + return service.payBack(new HttpRequestNoticeParams(request)).toMessage(); + } /** diff --git a/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/WxV3PayController.java b/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/WxV3PayController.java index 84880c4..080ea19 100644 --- a/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/WxV3PayController.java +++ b/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/WxV3PayController.java @@ -17,10 +17,13 @@ import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import com.egzosn.pay.common.bean.CertStoreType; +import com.egzosn.pay.common.bean.NoticeRequest; import com.egzosn.pay.common.bean.PayOrder; import com.egzosn.pay.common.bean.RefundOrder; import com.egzosn.pay.common.bean.TransferOrder; import com.egzosn.pay.demo.request.QueryOrder; +import com.egzosn.pay.demo.service.handler.WxPayMessageHandler; +import com.egzosn.pay.web.support.HttpRequestNoticeParams; import com.egzosn.pay.wx.bean.WxBank; import com.egzosn.pay.wx.bean.WxTransferType; import com.egzosn.pay.wx.v3.api.WxPayConfigStorage; @@ -67,7 +70,7 @@ public class WxV3PayController { service = new WxPayService(wxPayConfigStorage); //设置回调消息处理 //TODO {@link com.egzosn.pay.demo.controller.WxPayController#payBack} -// service.setPayMessageHandler(new WxPayMessageHandler(null)); + service.setPayMessageHandler(new WxPayMessageHandler(null)); } @@ -162,35 +165,6 @@ public class WxV3PayController { - /** - * 支付回调地址 方式一 - *

- * 方式二,{@link #payBack(HttpServletRequest)} 是属于简化方式, 试用与简单的业务场景 - * - * @param request 请求 - * @return 是否成功 - * @throws IOException IOException - * @see #payBack(HttpServletRequest) - */ - @Deprecated - @RequestMapping(value = "payBackBefore.json") - public String payBackBefore(HttpServletRequest request) throws IOException { - - //获取支付方返回的对应参数 - Map params = service.getParameter2Map(request.getParameterMap(), request.getInputStream()); - if (null == params) { - return service.getPayOutMessage("fail", "失败").toMessage(); - } - - //校验 - if (service.verify(params)) { - //这里处理业务逻辑 - //......业务逻辑处理块........ - return service.successPayOutMessage(null).toMessage(); - } - - return service.getPayOutMessage("fail", "失败").toMessage(); - } /** * 支付回调地址 @@ -204,9 +178,9 @@ public class WxV3PayController { * @throws IOException IOException */ @RequestMapping(value = "payBack.json") - public String payBack(HttpServletRequest request) throws IOException { + public String payBack(HttpServletRequest request) { //业务处理在对应的PayMessageHandler里面处理,在哪里设置PayMessageHandler,详情查看com.egzosn.pay.common.api.PayService.setPayMessageHandler() - return service.payBack(request.getParameterMap(), request.getInputStream()).toMessage(); + return service.payBack(new HttpRequestNoticeParams(request)).toMessage(); } diff --git a/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/api/WxPayService.java b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/api/WxPayService.java index 1858643..a5bd1a3 100644 --- a/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/api/WxPayService.java +++ b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/api/WxPayService.java @@ -5,11 +5,13 @@ import java.io.InputStream; import java.net.URLEncoder; import java.security.PrivateKey; import java.security.cert.Certificate; +import java.util.Collections; import java.util.Date; +import java.util.Enumeration; import java.util.HashMap; import java.util.LinkedHashMap; +import java.util.List; import java.util.Map; -import java.util.TreeMap; import org.apache.http.HttpEntity; @@ -19,6 +21,7 @@ import static com.egzosn.pay.wx.api.WxConst.RETURN_MSG_CODE; import static com.egzosn.pay.wx.api.WxConst.SANDBOXNEW; import static com.egzosn.pay.wx.api.WxConst.SUCCESS; import static com.egzosn.pay.wx.v3.utils.AntCertificationUtil.getCertificate; +import static com.egzosn.pay.wx.v3.utils.WxConst.FAILURE; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONObject; @@ -27,6 +30,7 @@ import com.egzosn.pay.common.bean.BillType; import com.egzosn.pay.common.bean.CurType; import com.egzosn.pay.common.bean.MethodType; import com.egzosn.pay.common.bean.NoticeParams; +import com.egzosn.pay.common.bean.NoticeRequest; import com.egzosn.pay.common.bean.OrderParaStructure; import com.egzosn.pay.common.bean.PayMessage; import com.egzosn.pay.common.bean.PayOrder; @@ -41,8 +45,8 @@ import com.egzosn.pay.common.http.ResponseEntity; import com.egzosn.pay.common.http.UriVariables; import com.egzosn.pay.common.util.DateUtils; import com.egzosn.pay.common.util.IOUtils; +import com.egzosn.pay.common.util.MapGen; import com.egzosn.pay.common.util.Util; -import com.egzosn.pay.common.util.XML; import com.egzosn.pay.common.util.sign.SignTextUtils; import com.egzosn.pay.common.util.sign.SignUtils; import com.egzosn.pay.common.util.sign.encrypt.RSA2; @@ -82,12 +86,11 @@ public class WxPayService extends BasePayService { private volatile WxPayAssistService wxPayAssistService; /** - * 微信参数构造器 + * 微信参数构造器 */ private volatile WxParameterStructure wxParameterStructure; - public WxPayAssistService getAssistService() { if (null == wxPayAssistService) { wxPayAssistService = new DefaultWxPayAssistService(this); @@ -190,8 +193,17 @@ public class WxPayService extends BasePayService { String signature = noticeParams.getHeader("Wechatpay-Signature"); Certificate certificate = getCertificate(serial); + + Map attr = noticeParams.getAttr(); + + if (Util.isEmpty(attr)) { + throw new PayErrorException(new WxPayError(FAILURE, "请勿置空NoticeParams.attr中的数据")); + } + + //这里为微信回调时的请求内容体,原值数据 + String body = (String) attr.get(WxConst.RESP_BODY); //签名信息 - String signText = StringUtils.joining("\n", timestamp, nonce, JSON.toJSONString(noticeParams.getBody())); + String signText = StringUtils.joining("\n", timestamp, nonce, body); return RSA2.verify(signText, signature, certificate, payConfigStorage.getInputCharset()); } @@ -304,14 +316,35 @@ public class WxPayService extends BasePayService { */ @Override public Map getParameter2Map(Map parameterMap, InputStream is) { - TreeMap map = new TreeMap(); - try { - return XML.inputStream2Map(is, map); + throw new PayErrorException(new WxPayError(FAILURE, "微信V3不支持方式")); + + } + + /** + * 将请求参数或者请求流转化为 Map + * + * @param request 通知请求 + * @return 获得回调的请求参数 + */ + @Override + public NoticeParams getNoticeParams(NoticeRequest request) { + NoticeParams noticeParams = new NoticeParams(); + Map> headers = new HashMap<>(); + Enumeration headerNames = request.getHeaderNames(); + while (headerNames.hasMoreElements()) { + String name = headerNames.nextElement(); + headers.put(name, Collections.list(request.getHeaders(name))); + } + noticeParams.setHeaders(headers); + try (InputStream is = request.getInputStream()) { + String body = IOUtils.toString(is); + noticeParams.setAttr(new MapGen(WxConst.RESP_BODY, body).getAttr()); + noticeParams.setBody(JSON.parseObject(body)); } catch (IOException e) { - throw new PayErrorException(new PayException("IOException", e.getMessage())); + LOG.error("获取回调参数异常", e); } - + return super.getNoticeParams(request); } /** diff --git a/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/utils/WxConst.java b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/utils/WxConst.java index 978b790..2055d49 100644 --- a/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/utils/WxConst.java +++ b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/utils/WxConst.java @@ -1,5 +1,7 @@ package com.egzosn.pay.wx.v3.utils; +import com.egzosn.pay.wx.v3.api.WxPayService; + /** * 微信所需常量 * @@ -44,4 +46,6 @@ public final class WxConst { public static final String MESSAGE = "message"; public static final String SCENE_INFO = "scene_info"; public static final String FAILURE = "failure"; + + public static final String RESP_BODY = WxPayService.class.getName() + "$RESP_BODY"; } diff --git a/pom.xml b/pom.xml index e041e7b..44f98e1 100644 --- a/pom.xml +++ b/pom.xml @@ -87,6 +87,11 @@ pay-java-common ${pay.version} + + com.egzosn + pay-java-web-support + ${pay.version} + -- Gitee From b731154e681fee248b5611c7ecfdcd720fd64bba Mon Sep 17 00:00:00 2001 From: egan Date: Wed, 18 Aug 2021 23:03:51 +0800 Subject: [PATCH 222/299] =?UTF-8?q?=E6=96=B0=E5=A2=9E=E5=9B=9E=E8=B0=83?= =?UTF-8?q?=E5=8F=82=E6=95=B0=E5=AF=B9=E8=B1=A1=EF=BC=8C=E5=9B=9E=E8=B0=83?= =?UTF-8?q?=E5=A4=84=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/egzosn/pay/demo/controller/WxV3PayController.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/WxV3PayController.java b/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/WxV3PayController.java index 080ea19..4d65fc2 100644 --- a/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/WxV3PayController.java +++ b/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/WxV3PayController.java @@ -60,12 +60,14 @@ public class WxV3PayController { wxPayConfigStorage.setAppId("wxc7b993ff15a9f271"); wxPayConfigStorage.setMchId("1602947765"); // wxPayConfigStorage.setKeyPublic("转账公钥,转账时必填"); - wxPayConfigStorage.setSecretKey("9bd8f0e7af4841299d782406b7774f52"); + //V3密钥 https://pay.weixin.qq.com/wiki/doc/apiv3/wechatpay/wechatpay3_2.shtml + wxPayConfigStorage.setSecretKey("V3密钥"); wxPayConfigStorage.setNotifyUrl("https://pay.egzosn.com/payback"); wxPayConfigStorage.setReturnUrl("https://pay.egzosn.com/payback"); wxPayConfigStorage.setInputCharset("utf-8"); wxPayConfigStorage.setCertSign(true); - wxPayConfigStorage.setApiClientKeyP12("yifenli_mall.p12"); + //商户API证书 https://pay.weixin.qq.com/wiki/doc/apiv3/wechatpay/wechatpay3_1.shtml + wxPayConfigStorage.setApiClientKeyP12("商户API证书.p12"); wxPayConfigStorage.setCertStoreType(CertStoreType.PATH); service = new WxPayService(wxPayConfigStorage); //设置回调消息处理 -- Gitee From 1b10bbd7879100ba343232f94bc73dbde2f426dd Mon Sep 17 00:00:00 2001 From: egzosn Date: Mon, 4 Oct 2021 18:45:50 +0800 Subject: [PATCH 223/299] =?UTF-8?q?=E5=BE=AE=E4=BF=A1V3=E7=89=88=E6=9C=AC?= =?UTF-8?q?=E6=8E=A5=E5=85=A5=E7=9B=B4=E8=BF=9E=E6=8E=A5=E5=8F=A3=EF=BC=8C?= =?UTF-8?q?=E8=87=AA=E6=B5=8B=E9=80=9A=E8=BF=87?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../egzosn/pay/common/api/BasePayService.java | 8 +- .../com/egzosn/pay/common/util/MapGen.java | 5 + .../pay/common/util/str/StringUtils.java | 2 +- .../demo/controller/WxV3PayController.java | 27 +- .../handler/WxV3PayMessageHandler.java | 29 ++ .../src/main/resources/applicationContext.xml | 4 +- pay-java-web-support/pom.xml | 8 +- .../wx/v3/api/DefaultWxPayAssistService.java | 2 +- .../pay/wx/v3/api/WxPayConfigStorage.java | 35 +- .../egzosn/pay/wx/v3/api/WxPayService.java | 73 ++-- .../pay/wx/v3/bean/WxTransactionType.java | 12 + .../pay/wx/v3/bean/order/SceneInfo.java | 5 +- .../pay/wx/v3/bean/response/Resource.java | 80 +++++ .../wx/v3/bean/response/WxNoticeParams.java | 108 ++++++ .../pay/wx/v3/bean/response/WxPayMessage.java | 311 ++++++++++++++++++ .../pay/wx/v3/bean/response/order/Amount.java | 39 +++ .../v3/bean/response/order/GoodsDetail.java | 81 +++++ .../pay/wx/v3/bean/response/order/Payer.java | 24 ++ .../bean/response/order/PromotionDetail.java | 176 ++++++++++ .../wx/v3/bean/response/order/TradeState.java | 54 +++ pom.xml | 4 +- 21 files changed, 1025 insertions(+), 62 deletions(-) create mode 100644 pay-java-demo/src/main/java/com/egzosn/pay/demo/service/handler/WxV3PayMessageHandler.java create mode 100644 pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/response/Resource.java create mode 100644 pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/response/WxNoticeParams.java create mode 100644 pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/response/WxPayMessage.java create mode 100644 pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/response/order/Amount.java create mode 100644 pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/response/order/GoodsDetail.java create mode 100644 pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/response/order/Payer.java create mode 100644 pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/response/order/PromotionDetail.java create mode 100644 pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/response/order/TradeState.java diff --git a/pay-java-common/src/main/java/com/egzosn/pay/common/api/BasePayService.java b/pay-java-common/src/main/java/com/egzosn/pay/common/api/BasePayService.java index 028fbcf..4e6d1b5 100644 --- a/pay-java-common/src/main/java/com/egzosn/pay/common/api/BasePayService.java +++ b/pay-java-common/src/main/java/com/egzosn/pay/common/api/BasePayService.java @@ -58,7 +58,7 @@ public abstract class BasePayService implements Pay * 支付消息拦截器 */ protected List> interceptors = new ArrayList>(); - ; + /** * 设置支付配置 @@ -101,9 +101,15 @@ public abstract class BasePayService implements Pay public BasePayService(PC payConfigStorage, HttpConfigStorage configStorage) { setPayConfigStorage(payConfigStorage); setRequestTemplateConfigStorage(configStorage); + initAfter(); } + /** + * 初始化之后执行 + */ + protected void initAfter(){ + } /** * Generate a Base64 encoded String from user , password * diff --git a/pay-java-common/src/main/java/com/egzosn/pay/common/util/MapGen.java b/pay-java-common/src/main/java/com/egzosn/pay/common/util/MapGen.java index 4953c14..545dae8 100644 --- a/pay-java-common/src/main/java/com/egzosn/pay/common/util/MapGen.java +++ b/pay-java-common/src/main/java/com/egzosn/pay/common/util/MapGen.java @@ -1,5 +1,7 @@ package com.egzosn.pay.common.util; +import java.util.HashMap; +import java.util.LinkedHashMap; import java.util.Map; /** @@ -21,6 +23,9 @@ public class MapGen { } public MapGen keyValue(K key, V value) { + if (null == attr){ + attr = new LinkedHashMap<>(); + } attr.put(key, value); return this; } diff --git a/pay-java-common/src/main/java/com/egzosn/pay/common/util/str/StringUtils.java b/pay-java-common/src/main/java/com/egzosn/pay/common/util/str/StringUtils.java index 79a94e6..9bba1a2 100644 --- a/pay-java-common/src/main/java/com/egzosn/pay/common/util/str/StringUtils.java +++ b/pay-java-common/src/main/java/com/egzosn/pay/common/util/str/StringUtils.java @@ -153,7 +153,7 @@ public class StringUtils { public static String joining(String separator, String... str) { StringBuilder builder = new StringBuilder(); for (String s : str) { - if (StringUtils.isEmpty(s)) { + if (null == s) { continue; } diff --git a/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/WxV3PayController.java b/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/WxV3PayController.java index 4d65fc2..8adc70e 100644 --- a/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/WxV3PayController.java +++ b/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/WxV3PayController.java @@ -17,12 +17,11 @@ import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import com.egzosn.pay.common.bean.CertStoreType; -import com.egzosn.pay.common.bean.NoticeRequest; import com.egzosn.pay.common.bean.PayOrder; import com.egzosn.pay.common.bean.RefundOrder; import com.egzosn.pay.common.bean.TransferOrder; import com.egzosn.pay.demo.request.QueryOrder; -import com.egzosn.pay.demo.service.handler.WxPayMessageHandler; +import com.egzosn.pay.demo.service.handler.WxV3PayMessageHandler; import com.egzosn.pay.web.support.HttpRequestNoticeParams; import com.egzosn.pay.wx.bean.WxBank; import com.egzosn.pay.wx.bean.WxTransferType; @@ -48,31 +47,27 @@ public class WxV3PayController { - public WxV3PayController() { - - } - - @PostConstruct + @PostConstruct //没有证书的情况下注释掉,避免启动报错 public void init() { - + System.out.println("v3 init"); WxPayConfigStorage wxPayConfigStorage = new WxPayConfigStorage(); - wxPayConfigStorage.setAppId("wxc7b993ff15a9f271"); - wxPayConfigStorage.setMchId("1602947765"); -// wxPayConfigStorage.setKeyPublic("转账公钥,转账时必填"); + wxPayConfigStorage.setAppId("wxc7b993ff15a9f27c"); + wxPayConfigStorage.setMchId("1602947766"); //V3密钥 https://pay.weixin.qq.com/wiki/doc/apiv3/wechatpay/wechatpay3_2.shtml - wxPayConfigStorage.setSecretKey("V3密钥"); - wxPayConfigStorage.setNotifyUrl("https://pay.egzosn.com/payback"); - wxPayConfigStorage.setReturnUrl("https://pay.egzosn.com/payback"); + wxPayConfigStorage.setV3ApiKey("9bd8f0e7af4841299d782406b7774f56"); + wxPayConfigStorage.setNotifyUrl("http://sailinmu.iok.la/wxV3/payBack.json"); + wxPayConfigStorage.setReturnUrl("http://sailinmu.iok.la/wxV3/payBack.json"); wxPayConfigStorage.setInputCharset("utf-8"); + //使用证书时设置为true wxPayConfigStorage.setCertSign(true); //商户API证书 https://pay.weixin.qq.com/wiki/doc/apiv3/wechatpay/wechatpay3_1.shtml - wxPayConfigStorage.setApiClientKeyP12("商户API证书.p12"); + wxPayConfigStorage.setApiClientKeyP12("E:\\Documents\\gitee\\支付\\yifenli_mall.p12"); wxPayConfigStorage.setCertStoreType(CertStoreType.PATH); service = new WxPayService(wxPayConfigStorage); //设置回调消息处理 //TODO {@link com.egzosn.pay.demo.controller.WxPayController#payBack} - service.setPayMessageHandler(new WxPayMessageHandler(null)); + service.setPayMessageHandler(new WxV3PayMessageHandler()); } diff --git a/pay-java-demo/src/main/java/com/egzosn/pay/demo/service/handler/WxV3PayMessageHandler.java b/pay-java-demo/src/main/java/com/egzosn/pay/demo/service/handler/WxV3PayMessageHandler.java new file mode 100644 index 0000000..64e9381 --- /dev/null +++ b/pay-java-demo/src/main/java/com/egzosn/pay/demo/service/handler/WxV3PayMessageHandler.java @@ -0,0 +1,29 @@ +package com.egzosn.pay.demo.service.handler; + +import java.util.Map; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.alibaba.fastjson.JSON; +import com.egzosn.pay.common.api.DefaultPayMessageHandler; +import com.egzosn.pay.common.api.PayMessageHandler; +import com.egzosn.pay.common.api.PayService; +import com.egzosn.pay.common.bean.PayOutMessage; +import com.egzosn.pay.common.exception.PayErrorException; +import com.egzosn.pay.wx.v3.bean.response.WxPayMessage; + +/** + * 微信支付回调处理器 + * Created by ZaoSheng on 2016/6/1. + */ +public class WxV3PayMessageHandler implements PayMessageHandler { + + private final Logger LOG = LoggerFactory.getLogger(DefaultPayMessageHandler.class); + + @Override + public PayOutMessage handle(WxPayMessage payMessage, Map context, PayService payService) throws PayErrorException { + LOG.info("回调支付消息处理器,回调消息:{}", JSON.toJSONString(payMessage)); + return payService.successPayOutMessage(payMessage); + } +} diff --git a/pay-java-demo/src/main/resources/applicationContext.xml b/pay-java-demo/src/main/resources/applicationContext.xml index 1bea38a..3352d53 100644 --- a/pay-java-demo/src/main/resources/applicationContext.xml +++ b/pay-java-demo/src/main/resources/applicationContext.xml @@ -10,7 +10,9 @@ - + + + diff --git a/pay-java-web-support/pom.xml b/pay-java-web-support/pom.xml index a0d1aa0..7389396 100644 --- a/pay-java-web-support/pom.xml +++ b/pay-java-web-support/pom.xml @@ -26,15 +26,9 @@ javax.servlet javax.servlet-api - provided 3.1.0 - - javax.servlet - jsp-api - 2.0 - provided - + diff --git a/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/api/DefaultWxPayAssistService.java b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/api/DefaultWxPayAssistService.java index 4e8d7e3..a6d0e1b 100644 --- a/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/api/DefaultWxPayAssistService.java +++ b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/api/DefaultWxPayAssistService.java @@ -162,7 +162,7 @@ public class DefaultWxPayAssistService implements WxPayAssistService { String associatedData = encryptCertificate.getString("associated_data"); String nonce = encryptCertificate.getString("nonce"); String ciphertext = encryptCertificate.getString("ciphertext"); - String publicKey = AntCertificationUtil.decryptToString(associatedData, nonce, ciphertext, payConfigStorage.getSecretKey(), payConfigStorage.getInputCharset()); + String publicKey = AntCertificationUtil.decryptToString(associatedData, nonce, ciphertext, payConfigStorage.getV3ApiKey(), payConfigStorage.getInputCharset()); ByteArrayInputStream inputStream = new ByteArrayInputStream(publicKey.getBytes(StandardCharsets.UTF_8)); AntCertificationUtil.loadCertificate(certificate.getString("serial_no"), inputStream); } diff --git a/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/api/WxPayConfigStorage.java b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/api/WxPayConfigStorage.java index c765a91..66ccf53 100644 --- a/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/api/WxPayConfigStorage.java +++ b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/api/WxPayConfigStorage.java @@ -51,6 +51,10 @@ public class WxPayConfigStorage extends BasePayConfigStorage { * 微信支付分配的子商户号,开发者模式下必填 合作者id */ private String subMchId; + /** + * V2 Api密钥 + */ + private String apiKey; /** * 商户API证书 @@ -111,19 +115,33 @@ public class WxPayConfigStorage extends BasePayConfigStorage { public void setMchId(String mchId) { this.mchId = mchId; + addAttr("mchId", mchId); } /** * 为商户平台设置的密钥key * - * @return 微信密钥 + * @return 微信v2密钥 */ - public String getSecretKey() { - return getKeyPrivate(); + public String getApiKey() { + return apiKey; } - public void setSecretKey(String secretKey) { - setKeyPrivate(secretKey); + public void setApiKey(String apiKey) { + this.apiKey = apiKey; + addAttr("apiKey", apiKey); + } + + public void setV3ApiKey(String v3ApiKey) { + setKeyPrivate(v3ApiKey); + } + /** + * 为商户平台设置的密钥key + * + * @return 微信v3密钥 + */ + public String getV3ApiKey() { + return getKeyPrivate(); } public void setAppId(String appId) { @@ -147,6 +165,7 @@ public class WxPayConfigStorage extends BasePayConfigStorage { public void setSubAppId(String subAppId) { this.subAppId = subAppId; + addAttr("subAppId", subAppId); } public String getSpAppId() { @@ -155,6 +174,7 @@ public class WxPayConfigStorage extends BasePayConfigStorage { public void setSpAppId(String spAppId) { this.spAppId = spAppId; + addAttr("spAppId", spAppId); } public String getSpMchId() { @@ -163,6 +183,7 @@ public class WxPayConfigStorage extends BasePayConfigStorage { public void setSpMchId(String spMchId) { this.spMchId = spMchId; + addAttr("spMchId", spMchId); } /** @@ -180,6 +201,7 @@ public class WxPayConfigStorage extends BasePayConfigStorage { @Deprecated public void setSubAppid(String subAppid) { this.subAppId = subAppid; + addAttr("subAppId", subAppId); } @@ -189,6 +211,7 @@ public class WxPayConfigStorage extends BasePayConfigStorage { public void setSubMchId(String subMchId) { this.subMchId = subMchId; + addAttr("subMchId", subMchId); } public Object getApiClientKeyP12() { @@ -197,6 +220,7 @@ public class WxPayConfigStorage extends BasePayConfigStorage { public void setApiClientKeyP12(Object apiClientKeyP12) { this.apiClientKeyP12 = apiClientKeyP12; + addAttr("apiClientKeyP12", apiClientKeyP12); } public CertStoreType getCertStoreType() { @@ -205,6 +229,7 @@ public class WxPayConfigStorage extends BasePayConfigStorage { public void setCertStoreType(CertStoreType certStoreType) { this.certStoreType = certStoreType; + addAttr("certStoreType", certStoreType); } public CertEnvironment getCertEnvironment() { diff --git a/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/api/WxPayService.java b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/api/WxPayService.java index a5bd1a3..a72958d 100644 --- a/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/api/WxPayService.java +++ b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/api/WxPayService.java @@ -20,7 +20,6 @@ import static com.egzosn.pay.wx.api.WxConst.RETURN_CODE; import static com.egzosn.pay.wx.api.WxConst.RETURN_MSG_CODE; import static com.egzosn.pay.wx.api.WxConst.SANDBOXNEW; import static com.egzosn.pay.wx.api.WxConst.SUCCESS; -import static com.egzosn.pay.wx.v3.utils.AntCertificationUtil.getCertificate; import static com.egzosn.pay.wx.v3.utils.WxConst.FAILURE; import com.alibaba.fastjson.JSON; @@ -52,14 +51,17 @@ import com.egzosn.pay.common.util.sign.SignUtils; import com.egzosn.pay.common.util.sign.encrypt.RSA2; import com.egzosn.pay.common.util.str.StringUtils; import com.egzosn.pay.wx.bean.WxPayError; -import com.egzosn.pay.wx.bean.WxPayMessage; +import com.egzosn.pay.wx.v3.bean.response.WxPayMessage; import com.egzosn.pay.wx.bean.WxTransferType; import com.egzosn.pay.wx.v3.bean.WxAccountType; import com.egzosn.pay.wx.v3.bean.WxBillType; import com.egzosn.pay.wx.v3.bean.WxTransactionType; import com.egzosn.pay.wx.v3.bean.order.Amount; import com.egzosn.pay.wx.v3.bean.order.RefundAmount; +import com.egzosn.pay.wx.v3.bean.response.Resource; +import com.egzosn.pay.wx.v3.bean.response.WxNoticeParams; import com.egzosn.pay.wx.v3.bean.response.WxRefundResult; +import com.egzosn.pay.wx.v3.utils.AntCertificationUtil; import com.egzosn.pay.wx.v3.utils.WxConst; /** @@ -94,6 +96,8 @@ public class WxPayService extends BasePayService { public WxPayAssistService getAssistService() { if (null == wxPayAssistService) { wxPayAssistService = new DefaultWxPayAssistService(this); + //在这预先进行初始化 + wxPayAssistService.refreshCertificate(); } return wxPayAssistService; } @@ -121,17 +125,26 @@ public class WxPayService extends BasePayService { super(payConfigStorage, configStorage); } + /** - * 设置支付配置 - * - * @param payConfigStorage 支付配置 + * 初始化之后执行 */ @Override - public BasePayService setPayConfigStorage(WxPayConfigStorage payConfigStorage) { - payConfigStorage.loadCertEnvironment(); - this.payConfigStorage = payConfigStorage; - wxParameterStructure = new WxParameterStructure(payConfigStorage); - return this; + protected void initAfter() { + new Thread(() -> { + payConfigStorage.loadCertEnvironment(); + wxParameterStructure = new WxParameterStructure(payConfigStorage); + //在这预先进行初始化 + try { + Thread.sleep(10); + } + catch (InterruptedException e) { + + } + getAssistService(); + }).start(); + + } /** @@ -184,15 +197,15 @@ public class WxPayService extends BasePayService { public boolean verify(NoticeParams noticeParams) { //当前使用的微信平台证书序列号 - String serial = noticeParams.getHeader("Wechatpay-Serial"); + String serial = noticeParams.getHeader("wechatpay-serial"); //微信服务器的时间戳 - String timestamp = noticeParams.getHeader("Wechatpay-Timestamp"); + String timestamp = noticeParams.getHeader("wechatpay-timestamp"); //微信服务器提供的随机串 - String nonce = noticeParams.getHeader("Wechatpay-Nonce"); + String nonce = noticeParams.getHeader("wechatpay-nonce"); //微信平台签名 - String signature = noticeParams.getHeader("Wechatpay-Signature"); + String signature = noticeParams.getHeader("wechatpay-signature"); - Certificate certificate = getCertificate(serial); + Certificate certificate = getAssistService().getCertificate(serial); Map attr = noticeParams.getAttr(); @@ -328,23 +341,29 @@ public class WxPayService extends BasePayService { */ @Override public NoticeParams getNoticeParams(NoticeRequest request) { - NoticeParams noticeParams = new NoticeParams(); - Map> headers = new HashMap<>(); - Enumeration headerNames = request.getHeaderNames(); - while (headerNames.hasMoreElements()) { - String name = headerNames.nextElement(); - headers.put(name, Collections.list(request.getHeaders(name))); - } - noticeParams.setHeaders(headers); + WxNoticeParams noticeParams = null; try (InputStream is = request.getInputStream()) { String body = IOUtils.toString(is); + noticeParams = JSON.parseObject(body, WxNoticeParams.class); noticeParams.setAttr(new MapGen(WxConst.RESP_BODY, body).getAttr()); - noticeParams.setBody(JSON.parseObject(body)); + Resource resource = noticeParams.getResource(); + String associatedData = resource.getAssociatedData(); + String nonce = resource.getNonce(); + String ciphertext = resource.getCiphertext(); + String data = AntCertificationUtil.decryptToString(associatedData, nonce, ciphertext, payConfigStorage.getV3ApiKey(), payConfigStorage.getInputCharset()); + noticeParams.setBody(JSON.parseObject(data)); } catch (IOException e) { LOG.error("获取回调参数异常", e); } - return super.getNoticeParams(request); + Map> headers = new HashMap<>(); + Enumeration headerNames = request.getHeaderNames(); + while (headerNames.hasMoreElements()) { + String name = headerNames.nextElement(); + headers.put(name, Collections.list(request.getHeaders(name))); + } + noticeParams.setHeaders(headers); + return noticeParams; } /** @@ -356,7 +375,7 @@ public class WxPayService extends BasePayService { */ @Override public PayOutMessage getPayOutMessage(String code, String message) { - return PayOutMessage.XML().code(code.toUpperCase()).content(message).build(); + return PayOutMessage.JSON().content("code", code).content("message", message).build(); } @@ -369,7 +388,7 @@ public class WxPayService extends BasePayService { */ @Override public PayOutMessage successPayOutMessage(PayMessage payMessage) { - return PayOutMessage.XML().code("SUCCESS").content("成功").build(); + return getPayOutMessage("SUCCESS", "成功"); } diff --git a/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/WxTransactionType.java b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/WxTransactionType.java index 7c94876..70a6ee8 100644 --- a/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/WxTransactionType.java +++ b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/WxTransactionType.java @@ -73,6 +73,18 @@ public enum WxTransactionType implements TransactionType { } } + }, + /** + * H5支付 + * 兼容 后期会抛弃 + */ + @Deprecated + MWEB("pay{partner}/transactions/h5", MethodType.POST, true) { + @Override + public void setAttribute(Map parameters, PayOrder order) { + H5.setAttribute(parameters, order); + } + }, /** diff --git a/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/order/SceneInfo.java b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/order/SceneInfo.java index 055cf35..952a3c9 100644 --- a/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/order/SceneInfo.java +++ b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/order/SceneInfo.java @@ -3,9 +3,12 @@ package com.egzosn.pay.wx.v3.bean.order; import com.alibaba.fastjson.annotation.JSONField; /** + * 支付场景信息描述 * @author Egan + *

  * email egzosn@gmail.com
  * date 2021/8/1
+ * 
*/ public class SceneInfo { @@ -15,7 +18,7 @@ public class SceneInfo { @JSONField(name = "payer_client_ip") private String payerClientIp; /** - * 商户端设备号 + * 商户端设备号(门店号或收银设备ID)。 */ @JSONField(name = "device_id") private String deviceId; diff --git a/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/response/Resource.java b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/response/Resource.java new file mode 100644 index 0000000..a6910de --- /dev/null +++ b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/response/Resource.java @@ -0,0 +1,80 @@ +package com.egzosn.pay.wx.v3.bean.response; + +import com.alibaba.fastjson.annotation.JSONField; + +/** + * 通知资源数据 + * json格式,见示例 + * @author Egan + *
+ * email egan@egzosn.com
+ * date 2021/10/4
+ * 
+ */ +public class Resource { + + + /** + * 对开启结果数据进行加密的加密算法,目前只支持AEAD_AES_256_GCM。 + */ + private String algorithm; + /** + * Base64编码后的开启/停用结果数据密文。 + */ + private String ciphertext; + /** + * 附加数据。 + */ + @JSONField(name = "associated_data") + private String associatedData; + + /** + * 原始回调类型。 + */ + @JSONField(name = "original_type") + private String originalType; + /** + * 加密使用的随机串。 + */ + private String nonce; + + public String getAlgorithm() { + return algorithm; + } + + public void setAlgorithm(String algorithm) { + this.algorithm = algorithm; + } + + public String getCiphertext() { + return ciphertext; + } + + public void setCiphertext(String ciphertext) { + this.ciphertext = ciphertext; + } + + public String getAssociatedData() { + return associatedData; + } + + public void setAssociatedData(String associatedData) { + this.associatedData = associatedData; + } + + public String getOriginalType() { + return originalType; + } + + public void setOriginalType(String originalType) { + this.originalType = originalType; + } + + public String getNonce() { + return nonce; + } + + public void setNonce(String nonce) { + this.nonce = nonce; + } +} diff --git a/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/response/WxNoticeParams.java b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/response/WxNoticeParams.java new file mode 100644 index 0000000..9c7ab19 --- /dev/null +++ b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/response/WxNoticeParams.java @@ -0,0 +1,108 @@ +package com.egzosn.pay.wx.v3.bean.response; + +import com.alibaba.fastjson.annotation.JSONField; +import com.egzosn.pay.common.bean.NoticeParams; + +/** + * 微信通知参数 + * @author Egan + *
+ * email egzosn@gmail.com
+ * date 2021/10/4
+ *
+ */ +public class WxNoticeParams extends NoticeParams { + + + /** + * 通知的唯一ID + * 示例值:EV-2018022511223320873 + */ + private String id; + + /** + *通知创建的时间,遵循rfc3339标准格式,格式为YYYY-MM-DDTHH:mm:ss+TIMEZONE,YYYY-MM-DD表示年月日,T出现在字符串中,表示time元素的开头,HH:mm:ss表示时分秒,TIMEZONE表示时区(+08:00表示东八区时间,领先UTC 8小时,即北京时间)。例如:2015-05-20T13:29:35+08:00表示,北京时间2015年5月20日13点29分35秒。 + * 示例值:2018-06-08T10:34:56+08:00 + */ + @JSONField(name = "create_time") + private String createTime; + /** + * 通知的类型: + * TRANSACTION.SUCCESS 支付成功通知 + * REFUND.SUCCESS:退款成功通知 + * REFUND.ABNORMAL:退款异常通知 + * REFUND.CLOSED:退款关闭通知 + * 示例值:REFUND.SUCCESS + */ + @JSONField(name = "event_type") + private String eventType; + + + + /** + * 通知的资源数据类型,支付成功通知为encrypt-resource + * 示例值:encrypt-resource + */ + @JSONField(name = "resource_type") + private String resourceType; + + /** + * 通知资源数据 + * json格式,见示例 + */ + private Resource resource; + /** + * 通知简要说明 + * 示例值:退款成功 + * 示例值:支付成功 + */ + private String summary; + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getCreateTime() { + return createTime; + } + + public void setCreateTime(String createTime) { + this.createTime = createTime; + } + + public String getEventType() { + return eventType; + } + + public void setEventType(String eventType) { + this.eventType = eventType; + } + + public String getSummary() { + return summary; + } + + public void setSummary(String summary) { + this.summary = summary; + } + + public String getResourceType() { + return resourceType; + } + + public void setResourceType(String resourceType) { + this.resourceType = resourceType; + } + + public Resource getResource() { + return resource; + } + + public void setResource(Resource resource) { + this.resource = resource; + } +} diff --git a/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/response/WxPayMessage.java b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/response/WxPayMessage.java new file mode 100644 index 0000000..8aa4008 --- /dev/null +++ b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/response/WxPayMessage.java @@ -0,0 +1,311 @@ +package com.egzosn.pay.wx.v3.bean.response; + +import java.math.BigDecimal; +import java.util.Date; +import java.util.List; +import java.util.Map; + +import com.alibaba.fastjson.JSONObject; +import com.alibaba.fastjson.annotation.JSONField; +import com.egzosn.pay.common.bean.PayMessage; +import com.egzosn.pay.wx.v3.bean.WxTransactionType; +import com.egzosn.pay.wx.v3.bean.order.SceneInfo; +import com.egzosn.pay.wx.v3.bean.response.order.Amount; +import com.egzosn.pay.wx.v3.bean.response.order.Payer; +import com.egzosn.pay.wx.v3.bean.response.order.PromotionDetail; +import com.egzosn.pay.wx.v3.bean.response.order.TradeState; + +/** + * 支付回调消息 + * @author Egan + *
+ * email egan@egzosn.com
+ * date 2021/10/4
+ * 
+ */ +public class WxPayMessage extends PayMessage { + + + + + /** + * 直连模式应用ID,服务商模式请解析spAppid + */ + private String appid; + /** + * 直连模式商户号,服务商模式请解析spMchid + */ + private String mchid; + /** + * 服务商模式服务商APPID + */ + @JSONField(name = "sp_appid") + private String spAppid; + /** + * 服务商模式服务商户号 + */ + @JSONField(name = "sp_mchid") + private String spMchid; + /** + * 服务商模式-子商户appid + */ + @JSONField(name = "sub_appid") + private String subAppid; + /** + * 服务商模式-子商户商户id + */ + @JSONField(name = "sub_mchid") + private String subMchid; + + /** + * 商户订单号 + * 商户系统内部订单号,只能是数字、大小写字母_-*且在同一个商户号下唯一。 + * 示例值:1217752501201407033233368018 + */ + @JSONField(name = "out_trade_no") + private String outTradeNo; + + /** + * 微信支付订单号 + * 微信支付系统生成的订单号。 + * 示例值:1217752501201407033233368018 + */ + @JSONField(name = "transaction_id") + private String transactionId; + + /** + * 交易类型,枚举值: + * JSAPI:公众号支付 + * NATIVE:扫码支付 + * APP:APP支付 + * MICROPAY:付款码支付 + * MWEB:H5支付 + * FACEPAY:刷脸支付 + * 示例值:MICROPAY + */ + @JSONField(name = "trade_type") + private WxTransactionType tradeType; + + + /** + * 交易状态,枚举值: + * SUCCESS:支付成功 + * REFUND:转入退款 + * NOTPAY:未支付 + * CLOSED:已关闭 + * REVOKED:已撤销(付款码支付) + * USERPAYING:用户支付中(付款码支付) + * PAYERROR:支付失败(其他原因,如银行返回失败) + */ + @JSONField(name = "trade_state") + private TradeState tradeState; + + + /** + * 交易状态描述 + * 示例值:支付成功 + */ + @JSONField(name = "trade_state_desc") + private String tradeStateDesc; + /** + * 银行类型,采用字符串类型的银行标识。 + * 银行标识请参考 《银行类型对照表》 + * 示例值:CMC + */ + @JSONField(name = "bank_type") + private String bankType; + + /** + * 附加数据,在查询API和支付通知中原样返回,可作为自定义参数使用 + */ + private String attach; + + /** + * 支付完成时间,遵循rfc3339标准格式, + * 格式为YYYY-MM-DDTHH:mm:ss+TIMEZONE,YYYY-MM-DD表示年月日,T出现在字符串中,表示time元素的开头,HH:mm:ss表示时分秒, + * TIMEZONE表示时区(+08:00表示东八区时间,领先UTC 8小时,即北京时间)。 + * 例如:2015-05-20T13:29:35+08:00表示,北京时间2015年5月20日 13点29分35秒。 + * 示例值:2018-06-08T10:34:56+08:00 + */ + @JSONField(name = "success_time", format = "yyyy-MM-dd'T'HH:mm:ssXXX") + private Date successTime; + /** + * 支付者信息 + */ + private Payer payer; + + + /** + * 订单金额 + */ + private Amount amount; + /** + * 支付场景信息描述 + */ + @JSONField(name = "scene_info") + private SceneInfo sceneInfo; + + /** + * 优惠功能,享受优惠时返回该字段。 + */ + @JSONField(name = "promotion_detail") + private List promotionDetail; + + + public String getAppid() { + return appid; + } + + public void setAppid(String appid) { + this.appid = appid; + } + + public String getMchid() { + return mchid; + } + + public void setMchid(String mchid) { + this.mchid = mchid; + } + + public String getSpAppid() { + return spAppid; + } + + public void setSpAppid(String spAppid) { + this.spAppid = spAppid; + } + + public String getSpMchid() { + return spMchid; + } + + public void setSpMchid(String spMchid) { + this.spMchid = spMchid; + } + + public String getSubAppid() { + return subAppid; + } + + public void setSubAppid(String subAppid) { + this.subAppid = subAppid; + } + + public String getSubMchid() { + return subMchid; + } + + public void setSubMchid(String subMchid) { + this.subMchid = subMchid; + } + + public String getOutTradeNo() { + return outTradeNo; + } + + public void setOutTradeNo(String outTradeNo) { + this.outTradeNo = outTradeNo; + } + + public String getTransactionId() { + return transactionId; + } + + public void setTransactionId(String transactionId) { + this.transactionId = transactionId; + } + + public WxTransactionType getTradeType() { + return tradeType; + } + + public void setTradeType(WxTransactionType tradeType) { + this.tradeType = tradeType; + } + + public TradeState getTradeState() { + return tradeState; + } + + public void setTradeState(TradeState tradeState) { + this.tradeState = tradeState; + } + + public String getTradeStateDesc() { + return tradeStateDesc; + } + + public void setTradeStateDesc(String tradeStateDesc) { + this.tradeStateDesc = tradeStateDesc; + } + + public String getBankType() { + return bankType; + } + + public void setBankType(String bankType) { + this.bankType = bankType; + } + + public String getAttach() { + return attach; + } + + public void setAttach(String attach) { + this.attach = attach; + } + + public Date getSuccessTime() { + return successTime; + } + + public void setSuccessTime(Date successTime) { + this.successTime = successTime; + } + + public Payer getPayer() { + return payer; + } + + public void setPayer(Payer payer) { + this.payer = payer; + } + + public Amount getAmount() { + return amount; + } + + public void setAmount(Amount amount) { + this.amount = amount; + } + + public SceneInfo getSceneInfo() { + return sceneInfo; + } + + public void setSceneInfo(SceneInfo sceneInfo) { + this.sceneInfo = sceneInfo; + } + + public List getPromotionDetail() { + return promotionDetail; + } + + public void setPromotionDetail(List promotionDetail) { + this.promotionDetail = promotionDetail; + } + + @Override + public BigDecimal getTotalFee() { + return BigDecimal.valueOf(getAmount().getTotal()); + } + + + public static final WxPayMessage create(Map message) { + WxPayMessage payMessage = new JSONObject(message).toJavaObject(WxPayMessage.class); +// payMessage.setPayType(""); + payMessage.setPayMessage(message); + return payMessage; + } +} diff --git a/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/response/order/Amount.java b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/response/order/Amount.java new file mode 100644 index 0000000..c6cf950 --- /dev/null +++ b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/response/order/Amount.java @@ -0,0 +1,39 @@ +package com.egzosn.pay.wx.v3.bean.response.order; + +import java.math.BigDecimal; + +/** + * 回调中的订单金额信息 + * @author Egan + *
+ * email egan@egzosn.com
+ * date 2021/10/4
+ * 
+ */ +public class Amount extends com.egzosn.pay.wx.v3.bean.order.Amount{ + + /** + * 用户支付金额,单位为分。 + */ + private BigDecimal payerTotal; + /** + * 用户支付币种 + */ + private String payerCurrency; + + public BigDecimal getPayerTotal() { + return payerTotal; + } + + public void setPayerTotal(BigDecimal payerTotal) { + this.payerTotal = payerTotal; + } + + public String getPayerCurrency() { + return payerCurrency; + } + + public void setPayerCurrency(String payerCurrency) { + this.payerCurrency = payerCurrency; + } +} diff --git a/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/response/order/GoodsDetail.java b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/response/order/GoodsDetail.java new file mode 100644 index 0000000..d48675c --- /dev/null +++ b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/response/order/GoodsDetail.java @@ -0,0 +1,81 @@ +package com.egzosn.pay.wx.v3.bean.response.order; + +import com.alibaba.fastjson.annotation.JSONField; + +/** + * 单品列表信息 + * @author Egan + *
+ * email egan@egzosn.com
+ * date 2021/10/4
+ * 
+ */ +public class GoodsDetail { + + /** + * 商品编码 + */ + @JSONField(name = "goods_id") + private String goodsId; + /** + * 商品数量 + */ + @JSONField(name = "quantity") + private Long quantity; + /** + * 商品单价 + */ + @JSONField(name = "unit_price") + private Long unitPrice; + /** + * 商品优惠金额,单位【分】 + */ + @JSONField(name = "discount_amount") + private Long discountAmount; + /** + * 商品备注 + */ + @JSONField(name = "goods_remark") + private String goodsRemark; + + + public String getGoodsId() { + return goodsId; + } + + public void setGoodsId(String goodsId) { + this.goodsId = goodsId; + } + + public Long getQuantity() { + return quantity; + } + + public void setQuantity(Long quantity) { + this.quantity = quantity; + } + + public Long getUnitPrice() { + return unitPrice; + } + + public void setUnitPrice(Long unitPrice) { + this.unitPrice = unitPrice; + } + + public Long getDiscountAmount() { + return discountAmount; + } + + public void setDiscountAmount(Long discountAmount) { + this.discountAmount = discountAmount; + } + + public String getGoodsRemark() { + return goodsRemark; + } + + public void setGoodsRemark(String goodsRemark) { + this.goodsRemark = goodsRemark; + } +} \ No newline at end of file diff --git a/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/response/order/Payer.java b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/response/order/Payer.java new file mode 100644 index 0000000..623b97d --- /dev/null +++ b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/response/order/Payer.java @@ -0,0 +1,24 @@ +package com.egzosn.pay.wx.v3.bean.response.order; + +/** + * 支付者信息 + * @author Egan + *
+ * email egan@egzosn.com
+ * date 2021/10/4
+ * 
+ */ +public class Payer { + /** + * 用户在直连商户appid下的唯一标识。 + */ + private String openid; + + public String getOpenid() { + return openid; + } + + public void setOpenid(String openid) { + this.openid = openid; + } +} \ No newline at end of file diff --git a/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/response/order/PromotionDetail.java b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/response/order/PromotionDetail.java new file mode 100644 index 0000000..697355e --- /dev/null +++ b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/response/order/PromotionDetail.java @@ -0,0 +1,176 @@ +package com.egzosn.pay.wx.v3.bean.response.order; + +import java.util.List; + +import com.alibaba.fastjson.annotation.JSONField; + +/** + * 优惠功能 + * + * @author felord.cn + * @since 1.0.0.RELEASE + */ + +public class PromotionDetail { + /** + * 券ID + */ + @JSONField(name = "coupon_id") + private String couponId; + + /** + * 优惠名称 + */ + private String name; + + /** + * 优惠范围 + *
    + *
  • GLOBAL:全场代金券
  • + *
  • SINGLE:单品优惠
  • + *
+ * 示例值:GLOBAL + */ + private String scope; + + /** + * 优惠类型 + *
    + *
  • CASH:充值
  • + *
  • NOCASH:预充值
  • + *
+ * 示例值:CASH + */ + private String type; + /** + * 优惠券面额,单位【分】 + */ + private Long amount; + + + + /** + * 活动ID + */ + @JSONField(name = "stock_id") + private String stockId; + + /** + * 微信出资,单位为分 + */ + @JSONField(name = "wechatpay_contribute") + private Long wechatpayContribute; + + /** + * 商户出资,单位为分 + */ + @JSONField(name = "merchant_contribute") + private Long merchantContribute; + + /** + * 其他出资,单位为分 + */ + @JSONField(name = "other_contribute") + private Long otherContribute; + + /** + * 优惠币种, + * CNY:人民币,境内商户号仅支持人民币。 + * 示例值:CNY + */ + private String currency; + /** + * 单品列表信息 + */ + @JSONField(name = "goods_detail") + private List goodsDetail; + + + public String getCouponId() { + return couponId; + } + + public void setCouponId(String couponId) { + this.couponId = couponId; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getScope() { + return scope; + } + + public void setScope(String scope) { + this.scope = scope; + } + + public String getType() { + return type; + } + + public void setType(String type) { + this.type = type; + } + + public Long getAmount() { + return amount; + } + + public void setAmount(Long amount) { + this.amount = amount; + } + + public String getStockId() { + return stockId; + } + + public void setStockId(String stockId) { + this.stockId = stockId; + } + + public Long getWechatpayContribute() { + return wechatpayContribute; + } + + public void setWechatpayContribute(Long wechatpayContribute) { + this.wechatpayContribute = wechatpayContribute; + } + + public Long getMerchantContribute() { + return merchantContribute; + } + + public void setMerchantContribute(Long merchantContribute) { + this.merchantContribute = merchantContribute; + } + + public Long getOtherContribute() { + return otherContribute; + } + + public void setOtherContribute(Long otherContribute) { + this.otherContribute = otherContribute; + } + + public String getCurrency() { + return currency; + } + + public void setCurrency(String currency) { + this.currency = currency; + } + + public List getGoodsDetail() { + return goodsDetail; + } + + public void setGoodsDetail(List goodsDetail) { + this.goodsDetail = goodsDetail; + } +} \ No newline at end of file diff --git a/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/response/order/TradeState.java b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/response/order/TradeState.java new file mode 100644 index 0000000..6b6afe6 --- /dev/null +++ b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/response/order/TradeState.java @@ -0,0 +1,54 @@ +package com.egzosn.pay.wx.v3.bean.response.order; + +/** + * 微信侧返回交易状态 + * @author Egan + *
+ * email egan@egzosn.com
+ * date 2021/10/4
+ * 
+ */ +public enum TradeState { + /** + * 支付成功 + * + * + */ + SUCCESS, + /** + * 转入退款 + * + * + */ + REFUND, + /** + * 未支付 + * + * + */ + NOTPAY, + /** + * 已关闭 + * + * + */ + CLOSED, + /** + * 已撤销(付款码支付) + * + */ + REVOKED, + /** + * 用户支付中(付款码支付) + */ + USERPAYING, + /** + * 支付失败(其他原因,如银行返回失败) + */ + PAYERROR, + /** + * 已接收,等待扣款 + * + */ + ACCEPT, +} \ No newline at end of file diff --git a/pom.xml b/pom.xml index 44f98e1..a1d07ce 100644 --- a/pom.xml +++ b/pom.xml @@ -147,8 +147,8 @@ org.apache.maven.plugins maven-compiler-plugin - 1.7 - 1.7 + 1.8 + 1.8 utf-8 -- Gitee From 76a27a0105a1e42445bd32232d29591281f01ed5 Mon Sep 17 00:00:00 2001 From: egzosn Date: Mon, 4 Oct 2021 19:24:54 +0800 Subject: [PATCH 224/299] =?UTF-8?q?=E5=BE=AE=E4=BF=A1V3=E7=89=88=E6=9C=ACH?= =?UTF-8?q?5=E8=B7=B3=E8=BD=AC=E4=BF=AE=E5=A4=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/egzosn/pay/wx/v3/api/WxPayService.java | 16 +++------------- 1 file changed, 3 insertions(+), 13 deletions(-) diff --git a/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/api/WxPayService.java b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/api/WxPayService.java index a72958d..49da334 100644 --- a/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/api/WxPayService.java +++ b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/api/WxPayService.java @@ -2,7 +2,6 @@ package com.egzosn.pay.wx.v3.api; import java.io.IOException; import java.io.InputStream; -import java.net.URLEncoder; import java.security.PrivateKey; import java.security.cert.Certificate; import java.util.Collections; @@ -16,10 +15,7 @@ import java.util.Map; import org.apache.http.HttpEntity; import static com.egzosn.pay.wx.api.WxConst.OUT_TRADE_NO; -import static com.egzosn.pay.wx.api.WxConst.RETURN_CODE; -import static com.egzosn.pay.wx.api.WxConst.RETURN_MSG_CODE; import static com.egzosn.pay.wx.api.WxConst.SANDBOXNEW; -import static com.egzosn.pay.wx.api.WxConst.SUCCESS; import static com.egzosn.pay.wx.v3.utils.WxConst.FAILURE; import com.alibaba.fastjson.JSON; @@ -51,7 +47,6 @@ import com.egzosn.pay.common.util.sign.SignUtils; import com.egzosn.pay.common.util.sign.encrypt.RSA2; import com.egzosn.pay.common.util.str.StringUtils; import com.egzosn.pay.wx.bean.WxPayError; -import com.egzosn.pay.wx.v3.bean.response.WxPayMessage; import com.egzosn.pay.wx.bean.WxTransferType; import com.egzosn.pay.wx.v3.bean.WxAccountType; import com.egzosn.pay.wx.v3.bean.WxBillType; @@ -60,6 +55,7 @@ import com.egzosn.pay.wx.v3.bean.order.Amount; import com.egzosn.pay.wx.v3.bean.order.RefundAmount; import com.egzosn.pay.wx.v3.bean.response.Resource; import com.egzosn.pay.wx.v3.bean.response.WxNoticeParams; +import com.egzosn.pay.wx.v3.bean.response.WxPayMessage; import com.egzosn.pay.wx.v3.bean.response.WxRefundResult; import com.egzosn.pay.wx.v3.utils.AntCertificationUtil; import com.egzosn.pay.wx.v3.utils.WxConst; @@ -402,14 +398,8 @@ public class WxPayService extends BasePayService { */ @Override public String buildRequest(Map orderInfo, MethodType method) { - if (!SUCCESS.equals(orderInfo.get(RETURN_CODE))) { - throw new PayErrorException(new WxPayError((String) orderInfo.get(RETURN_CODE), (String) orderInfo.get(RETURN_MSG_CODE))); - } - if (WxTransactionType.H5.name().equals(orderInfo.get("trade_type"))) { - return String.format("", orderInfo.get("mweb_url"), StringUtils.isEmpty(payConfigStorage.getReturnUrl()) ? "" : "&redirect_url=" + URLEncoder.encode(payConfigStorage.getReturnUrl())); - } - throw new UnsupportedOperationException(); - + String redirectUrl = StringUtils.isEmpty(payConfigStorage.getReturnUrl()) ? "" : "&redirect_url=" + UriVariables.urlEncoder(payConfigStorage.getReturnUrl()); + return String.format("", orderInfo.get("h5_url"), redirectUrl); } /** -- Gitee From d4096194f143b03137f3d76a1b1d883ed0af6d46 Mon Sep 17 00:00:00 2001 From: egzosn Date: Wed, 6 Oct 2021 15:45:41 +0800 Subject: [PATCH 225/299] =?UTF-8?q?=E6=96=B0=E5=A2=9E=E8=AE=A2=E5=8D=95?= =?UTF-8?q?=E5=85=B3=E9=97=AD=E6=96=B9=E6=B3=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/egzosn/pay/ali/api/AliPayService.java | 23 +++-- .../egzosn/pay/baidu/api/BaiduPayService.java | 12 ++- .../com/egzosn/pay/common/api/PayService.java | 10 ++ .../egzosn/pay/common/bean/CloseOrder.java | 92 +++++++++++++++++++ .../pay/common/bean/OrderParaStructure.java | 8 ++ .../com/egzosn/pay/common/bean/PayOrder.java | 2 + .../com/egzosn/pay/common/util/DateUtils.java | 1 + .../egzosn/pay/fuiou/api/FuiouPayService.java | 12 ++- .../pay/payoneer/api/PayoneerPayService.java | 12 ++- .../pay/paypal/api/PayPalPayService.java | 12 ++- .../pay/paypal/v2/api/PayPalPayService.java | 12 ++- .../egzosn/pay/union/api/UnionPayService.java | 12 ++- .../wx/youdian/api/WxYouDianPayService.java | 12 ++- .../com/egzosn/pay/wx/api/WxPayService.java | 12 +++ .../egzosn/pay/yiji/api/YiJiPayService.java | 12 ++- 15 files changed, 226 insertions(+), 18 deletions(-) create mode 100644 pay-java-common/src/main/java/com/egzosn/pay/common/bean/CloseOrder.java 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 5c5568f..1b9d724 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 @@ -31,6 +31,7 @@ import com.egzosn.pay.ali.bean.CertEnvironment; import com.egzosn.pay.ali.bean.OrderSettle; import com.egzosn.pay.common.api.BasePayService; import com.egzosn.pay.common.bean.BillType; +import com.egzosn.pay.common.bean.CloseOrder; import com.egzosn.pay.common.bean.MethodType; import com.egzosn.pay.common.bean.NoticeParams; import com.egzosn.pay.common.bean.Order; @@ -130,7 +131,7 @@ public class AliPayService extends BasePayService { return false; } - return signVerify(params, (String) params.get(SIGN)) && verifySource((String) params.get("notify_id")); + return signVerify(params, (String) params.get(SIGN)); } @@ -182,15 +183,6 @@ public class AliPayService extends BasePayService { return (String) respMap.get(ALIPAY_CERT_SN_FIELD); } - /** - * 校验数据来源 - * - * @param id 业务id, 数据的真实性. - * @return true通过 - */ - protected boolean verifySource(String id) { - return true; - } /** @@ -484,6 +476,17 @@ public class AliPayService extends BasePayService { return secondaryInterface(tradeNo, outTradeNo, AliTransactionType.CLOSE); } + /** + * 交易关闭接口 + * + * @param closeOrder 关闭订单 + * @return 返回支付方交易关闭后的结果 + */ + @Override + public Map close(CloseOrder closeOrder){ + return secondaryInterface(closeOrder.getTradeNo(), closeOrder.getOutTradeNo(), AliTransactionType.CLOSE); + } + /** * 支付交易返回失败或支付系统超时,调用该接口撤销交易。 * 如果此订单用户支付失败,支付宝系统会将此订单关闭;如果用户支付成功,支付宝系统会将此订单资金退还给用户。 diff --git a/pay-java-baidu/src/main/java/com/egzosn/pay/baidu/api/BaiduPayService.java b/pay-java-baidu/src/main/java/com/egzosn/pay/baidu/api/BaiduPayService.java index 58487fe..3b7f3af 100644 --- a/pay-java-baidu/src/main/java/com/egzosn/pay/baidu/api/BaiduPayService.java +++ b/pay-java-baidu/src/main/java/com/egzosn/pay/baidu/api/BaiduPayService.java @@ -25,6 +25,7 @@ import com.egzosn.pay.baidu.util.Asserts; import com.egzosn.pay.common.api.BasePayService; import com.egzosn.pay.common.bean.BaseRefundResult; import com.egzosn.pay.common.bean.BillType; +import com.egzosn.pay.common.bean.CloseOrder; import com.egzosn.pay.common.bean.CurType; import com.egzosn.pay.common.bean.MethodType; import com.egzosn.pay.common.bean.NoticeParams; @@ -438,7 +439,16 @@ public class BaiduPayService extends BasePayService { public Map close(String tradeNo, String outTradeNo) { throw new UnsupportedOperationException("不支持该操作"); } - + /** + * 交易关闭接口 + * + * @param closeOrder 关闭订单 + * @return 返回支付方交易关闭后的结果 + */ + @Override + public Map close(CloseOrder closeOrder){ + throw new UnsupportedOperationException("不支持该操作"); + } /** * 退款 diff --git a/pay-java-common/src/main/java/com/egzosn/pay/common/api/PayService.java b/pay-java-common/src/main/java/com/egzosn/pay/common/api/PayService.java index 0e0d03f..6ee633d 100644 --- a/pay-java-common/src/main/java/com/egzosn/pay/common/api/PayService.java +++ b/pay-java-common/src/main/java/com/egzosn/pay/common/api/PayService.java @@ -6,6 +6,7 @@ import java.util.Date; import java.util.Map; import com.egzosn.pay.common.bean.BillType; +import com.egzosn.pay.common.bean.CloseOrder; import com.egzosn.pay.common.bean.MethodType; import com.egzosn.pay.common.bean.NoticeParams; import com.egzosn.pay.common.bean.NoticeRequest; @@ -218,8 +219,17 @@ public interface PayService { * @param tradeNo 支付平台订单号 * @param outTradeNo 商户单号 * @return 返回支付方交易关闭后的结果 + * @see #close(CloseOrder) */ + @Deprecated Map close(String tradeNo, String outTradeNo); + /** + * 交易关闭接口 + * + * @param closeOrder 关闭订单 + * @return 返回支付方交易关闭后的结果 + */ + Map close(CloseOrder closeOrder); /** diff --git a/pay-java-common/src/main/java/com/egzosn/pay/common/bean/CloseOrder.java b/pay-java-common/src/main/java/com/egzosn/pay/common/bean/CloseOrder.java new file mode 100644 index 0000000..b7e975a --- /dev/null +++ b/pay-java-common/src/main/java/com/egzosn/pay/common/bean/CloseOrder.java @@ -0,0 +1,92 @@ +package com.egzosn.pay.common.bean; + +import java.util.Map; + +/** + * 关闭订单 + * @author Egan + * @email egan@egzosn.com + * @date 2021/10/6 + */ +public class CloseOrder implements Order { + + /** + * 支付平台订单号,交易号 + */ + private String tradeNo; + /** + * 商户单号 + */ + private String outTradeNo; + + + /** + * 订单附加信息,可用于预设未提供的参数,这里会覆盖以上所有的订单信息, + */ + private Map attr; + + public CloseOrder() { + } + + public CloseOrder(String outTradeNo) { + this.outTradeNo = outTradeNo; + } + + + + /** + * 添加订单信息 + * + * @param key key + * @param value 值 + */ + @Override + public void addAttr(String key, Object value) { + attr.put(key, value); + } + + /** + * 获取属性 这里可用做覆盖已设置的信息属性,订单信息在签名前进行覆盖。 + * + * @return 属性 + */ + @Override + public Map getAttrs() { + return attr; + } + + /** + * 获取属性 这里可用做覆盖已设置的订单信息属性,订单信息在签名前进行覆盖。 + * + * @param key 属性名 + * @return 属性 + */ + @Override + public Object getAttr(String key) { + return attr.get(key); + } + + public String getTradeNo() { + return tradeNo; + } + + public void setTradeNo(String tradeNo) { + this.tradeNo = tradeNo; + } + + public String getOutTradeNo() { + return outTradeNo; + } + + public void setOutTradeNo(String outTradeNo) { + this.outTradeNo = outTradeNo; + } + + public Map getAttr() { + return attr; + } + + public void setAttr(Map attr) { + this.attr = attr; + } +} diff --git a/pay-java-common/src/main/java/com/egzosn/pay/common/bean/OrderParaStructure.java b/pay-java-common/src/main/java/com/egzosn/pay/common/bean/OrderParaStructure.java index c651b25..0453306 100644 --- a/pay-java-common/src/main/java/com/egzosn/pay/common/bean/OrderParaStructure.java +++ b/pay-java-common/src/main/java/com/egzosn/pay/common/bean/OrderParaStructure.java @@ -1,11 +1,14 @@ package com.egzosn.pay.common.bean; +import java.util.Date; import java.util.Map; +import com.egzosn.pay.common.util.DateUtils; import com.egzosn.pay.common.util.str.StringUtils; /** * 订单参数构造器 + * * @author Egan *
  * email egzosn@gmail.com
@@ -32,4 +35,9 @@ public final class OrderParaStructure {
         return parameters;
     }
 
+    public static Map loadDateParameters(Map parameters, String key, Order order, String datePattern) {
+        return OrderParaStructure.loadParameters(parameters, key, DateUtils.formatDate((Date) order.getAttr(key), datePattern));
+    }
+
+
 }
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 92995ab..1db5c6e 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
@@ -48,10 +48,12 @@ public class PayOrder implements Order {
     /**
      * 设备信息
      */
+    @Deprecated
     private String deviceInfo;
     /**
      * 支付创建ip
      */
+    @Deprecated
     private String spbillCreateIp;
     /**
      * 付款条码串,人脸凭证,有关支付代码相关的,
diff --git a/pay-java-common/src/main/java/com/egzosn/pay/common/util/DateUtils.java b/pay-java-common/src/main/java/com/egzosn/pay/common/util/DateUtils.java
index f5397da..1691321 100644
--- a/pay-java-common/src/main/java/com/egzosn/pay/common/util/DateUtils.java
+++ b/pay-java-common/src/main/java/com/egzosn/pay/common/util/DateUtils.java
@@ -58,6 +58,7 @@ public final class DateUtils {
     }
 
     public static final String YYYY_MM_DD_HH_MM_SS = "yyyy-MM-dd HH:mm:ss";
+    public static final String YYYY_MM_DD_T_HH_MM_SS = "yyyy-MM-dd'T'HH:mm:ss.SSSXXX";
     public static final String YYYY_MM_DD = "yyyy-MM-dd";
     public static final String YYYYMMDD = "yyyyMMdd";
     public static final String YYYYMMDDHHMMSS = "yyyyMMddHHmmss";
diff --git a/pay-java-fuiou/src/main/java/com/egzosn/pay/fuiou/api/FuiouPayService.java b/pay-java-fuiou/src/main/java/com/egzosn/pay/fuiou/api/FuiouPayService.java
index f3127da..94f6dc8 100644
--- a/pay-java-fuiou/src/main/java/com/egzosn/pay/fuiou/api/FuiouPayService.java
+++ b/pay-java-fuiou/src/main/java/com/egzosn/pay/fuiou/api/FuiouPayService.java
@@ -10,6 +10,7 @@ import java.util.Map;
 import com.alibaba.fastjson.JSONObject;
 import com.egzosn.pay.common.api.BasePayService;
 import com.egzosn.pay.common.bean.BillType;
+import com.egzosn.pay.common.bean.CloseOrder;
 import com.egzosn.pay.common.bean.MethodType;
 import com.egzosn.pay.common.bean.NoticeParams;
 import com.egzosn.pay.common.bean.PayMessage;
@@ -412,7 +413,16 @@ public class FuiouPayService extends BasePayService {
     public Map close(String tradeNo, String outTradeNo) {
         return Collections.EMPTY_MAP;
     }
-
+    /**
+     * 交易关闭接口
+     *
+     * @param closeOrder    关闭订单
+     * @return 返回支付方交易关闭后的结果
+     */
+    @Override
+    public Map close(CloseOrder closeOrder){
+        throw new UnsupportedOperationException("不支持该操作");
+    }
 
     /**
      * 申请退款接口
diff --git a/pay-java-payoneer/src/main/java/com/egzosn/pay/payoneer/api/PayoneerPayService.java b/pay-java-payoneer/src/main/java/com/egzosn/pay/payoneer/api/PayoneerPayService.java
index a09043f..d1572b3 100644
--- a/pay-java-payoneer/src/main/java/com/egzosn/pay/payoneer/api/PayoneerPayService.java
+++ b/pay-java-payoneer/src/main/java/com/egzosn/pay/payoneer/api/PayoneerPayService.java
@@ -17,6 +17,7 @@ import com.alibaba.fastjson.JSONObject;
 import com.egzosn.pay.common.api.BasePayService;
 import com.egzosn.pay.common.bean.BaseRefundResult;
 import com.egzosn.pay.common.bean.BillType;
+import com.egzosn.pay.common.bean.CloseOrder;
 import com.egzosn.pay.common.bean.CurType;
 import com.egzosn.pay.common.bean.DefaultCurType;
 import com.egzosn.pay.common.bean.MethodType;
@@ -305,7 +306,16 @@ public class PayoneerPayService extends BasePayService im
     public Map close(String tradeNo, String outTradeNo) {
         return secondaryInterface(tradeNo, outTradeNo, PayoneerTransactionType.CHARGE_CANCEL);
     }
-
+    /**
+     * 交易关闭接口
+     *
+     * @param closeOrder    关闭订单
+     * @return 返回支付方交易关闭后的结果
+     */
+    @Override
+    public Map close(CloseOrder closeOrder){
+        return secondaryInterface(closeOrder.getTradeNo(), closeOrder.getOutTradeNo(), PayoneerTransactionType.CHARGE_CANCEL);
+    }
     /**
      * 交易交易撤销
      *
diff --git a/pay-java-paypal/src/main/java/com/egzosn/pay/paypal/api/PayPalPayService.java b/pay-java-paypal/src/main/java/com/egzosn/pay/paypal/api/PayPalPayService.java
index 9b1b15f..6d44c1c 100644
--- a/pay-java-paypal/src/main/java/com/egzosn/pay/paypal/api/PayPalPayService.java
+++ b/pay-java-paypal/src/main/java/com/egzosn/pay/paypal/api/PayPalPayService.java
@@ -21,6 +21,7 @@ import com.alibaba.fastjson.JSONObject;
 import com.egzosn.pay.common.api.BasePayService;
 import com.egzosn.pay.common.bean.BaseRefundResult;
 import com.egzosn.pay.common.bean.BillType;
+import com.egzosn.pay.common.bean.CloseOrder;
 import com.egzosn.pay.common.bean.CurType;
 import com.egzosn.pay.common.bean.DefaultCurType;
 import com.egzosn.pay.common.bean.MethodType;
@@ -271,7 +272,16 @@ public class PayPalPayService extends BasePayService {
         return null;
     }
 
-
+    /**
+     * 交易关闭接口
+     *
+     * @param closeOrder    关闭订单
+     * @return 返回支付方交易关闭后的结果
+     */
+    @Override
+    public Map close(CloseOrder closeOrder){
+        throw new UnsupportedOperationException("不支持该操作");
+    }
     /**
      * 申请退款接口
      *
diff --git a/pay-java-paypal/src/main/java/com/egzosn/pay/paypal/v2/api/PayPalPayService.java b/pay-java-paypal/src/main/java/com/egzosn/pay/paypal/v2/api/PayPalPayService.java
index 5e54691..c932770 100644
--- a/pay-java-paypal/src/main/java/com/egzosn/pay/paypal/v2/api/PayPalPayService.java
+++ b/pay-java-paypal/src/main/java/com/egzosn/pay/paypal/v2/api/PayPalPayService.java
@@ -22,6 +22,7 @@ import com.alibaba.fastjson.JSONArray;
 import com.alibaba.fastjson.JSONObject;
 import com.egzosn.pay.common.api.BasePayService;
 import com.egzosn.pay.common.bean.BillType;
+import com.egzosn.pay.common.bean.CloseOrder;
 import com.egzosn.pay.common.bean.CurType;
 import com.egzosn.pay.common.bean.DefaultCurType;
 import com.egzosn.pay.common.bean.MethodType;
@@ -354,7 +355,16 @@ public class PayPalPayService extends BasePayService implem
     public Map close(String tradeNo, String outTradeNo) {
         return null;
     }
-
+    /**
+     * 交易关闭接口
+     *
+     * @param closeOrder    关闭订单
+     * @return 返回支付方交易关闭后的结果
+     */
+    @Override
+    public Map close(CloseOrder closeOrder){
+        throw new UnsupportedOperationException("不支持该操作");
+    }
     /**
      * 注意:最好在付款成功之后回调时进行调用
      * 确认订单并返回确认后订单信息
diff --git a/pay-java-union/src/main/java/com/egzosn/pay/union/api/UnionPayService.java b/pay-java-union/src/main/java/com/egzosn/pay/union/api/UnionPayService.java
index bfcf361..7576302 100644
--- a/pay-java-union/src/main/java/com/egzosn/pay/union/api/UnionPayService.java
+++ b/pay-java-union/src/main/java/com/egzosn/pay/union/api/UnionPayService.java
@@ -27,6 +27,7 @@ import java.util.TreeMap;
 import com.alibaba.fastjson.JSONObject;
 import com.egzosn.pay.common.api.BasePayService;
 import com.egzosn.pay.common.bean.BillType;
+import com.egzosn.pay.common.bean.CloseOrder;
 import com.egzosn.pay.common.bean.MethodType;
 import com.egzosn.pay.common.bean.NoticeParams;
 import com.egzosn.pay.common.bean.PayMessage;
@@ -656,7 +657,16 @@ public class UnionPayService extends BasePayService {
     public Map close(String tradeNo, String outTradeNo) {
         return Collections.emptyMap();
     }
-
+    /**
+     * 交易关闭接口
+     *
+     * @param closeOrder    关闭订单
+     * @return 返回支付方交易关闭后的结果
+     */
+    @Override
+    public Map close(CloseOrder closeOrder){
+        return Collections.emptyMap();
+    }
 
     @Override
     public UnionRefundResult refund(RefundOrder refundOrder) {
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 859bcb5..5eec170 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
@@ -14,6 +14,7 @@ import com.alibaba.fastjson.JSONObject;
 import com.egzosn.pay.common.api.BasePayService;
 import com.egzosn.pay.common.bean.BaseRefundResult;
 import com.egzosn.pay.common.bean.BillType;
+import com.egzosn.pay.common.bean.CloseOrder;
 import com.egzosn.pay.common.bean.CurType;
 import com.egzosn.pay.common.bean.MethodType;
 import com.egzosn.pay.common.bean.NoticeParams;
@@ -422,7 +423,16 @@ public class WxYouDianPayService extends BasePayService close(String tradeNo, String outTradeNo) {
         return Collections.emptyMap();
     }
-
+    /**
+     * 交易关闭接口
+     *
+     * @param closeOrder    关闭订单
+     * @return 返回支付方交易关闭后的结果
+     */
+    @Override
+    public Map close(CloseOrder closeOrder){
+        return Collections.emptyMap();
+    }
 
     /**
      * 申请退款接口
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 58e622f..570feb1 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
@@ -39,6 +39,7 @@ import static com.egzosn.pay.wx.bean.WxTransferType.TRANSFERS;
 import com.alibaba.fastjson.JSONObject;
 import com.egzosn.pay.common.api.BasePayService;
 import com.egzosn.pay.common.bean.BillType;
+import com.egzosn.pay.common.bean.CloseOrder;
 import com.egzosn.pay.common.bean.MethodType;
 import com.egzosn.pay.common.bean.NoticeParams;
 import com.egzosn.pay.common.bean.Order;
@@ -514,6 +515,17 @@ public class WxPayService extends BasePayService implements
 
         return secondaryInterface(transactionId, outTradeNo, WxTransactionType.CLOSE);
     }
+    /**
+     * 交易关闭接口
+     *
+     * @param closeOrder    关闭订单
+     * @return 返回支付方交易关闭后的结果
+     */
+    @Override
+    public Map close(CloseOrder closeOrder){
+        return secondaryInterface(closeOrder.getTradeNo(), closeOrder.getOutTradeNo(), WxTransactionType.CLOSE);
+    }
+
 
     /**
      * 交易交易撤销
diff --git a/pay-java-yiji/src/main/java/com/egzosn/pay/yiji/api/YiJiPayService.java b/pay-java-yiji/src/main/java/com/egzosn/pay/yiji/api/YiJiPayService.java
index cd3c66c..adae363 100644
--- a/pay-java-yiji/src/main/java/com/egzosn/pay/yiji/api/YiJiPayService.java
+++ b/pay-java-yiji/src/main/java/com/egzosn/pay/yiji/api/YiJiPayService.java
@@ -10,6 +10,7 @@ import com.alibaba.fastjson.JSONObject;
 import com.egzosn.pay.common.api.BasePayService;
 import com.egzosn.pay.common.bean.BaseRefundResult;
 import com.egzosn.pay.common.bean.BillType;
+import com.egzosn.pay.common.bean.CloseOrder;
 import com.egzosn.pay.common.bean.CurType;
 import com.egzosn.pay.common.bean.DefaultCurType;
 import com.egzosn.pay.common.bean.MethodType;
@@ -298,7 +299,16 @@ public class YiJiPayService extends BasePayService {
     public Map close(String tradeNo, String outTradeNo) {
         return Collections.emptyMap();
     }
-
+    /**
+     * 交易关闭接口
+     *
+     * @param closeOrder    关闭订单
+     * @return 返回支付方交易关闭后的结果
+     */
+    @Override
+    public Map close(CloseOrder closeOrder){
+        return Collections.emptyMap();
+    }
 
     /**
      * 申请退款接口
-- 
Gitee


From 6885cacfc54f914e5a620bc1243319b1dfebba5e Mon Sep 17 00:00:00 2001
From: egzosn 
Date: Wed, 6 Oct 2021 15:46:21 +0800
Subject: [PATCH 226/299] =?UTF-8?q?=E5=BE=AE=E4=BF=A1V3=E5=AE=9E=E7=8E=B0?=
 =?UTF-8?q?=E5=90=88=E5=8D=95=E6=94=AF=E4=BB=98?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 .../pay/wx/v3/api/WxCombinePayService.java    | 159 +++++++++++++++
 .../egzosn/pay/wx/v3/api/WxPayService.java    |  68 ++++---
 .../pay/wx/v3/bean/WxTransactionType.java     |  62 ++++--
 .../pay/wx/v3/bean/combine/CombineAmount.java |  34 ++++
 .../wx/v3/bean/combine/CombineCloseOrder.java |  30 +++
 .../wx/v3/bean/combine/CombinePayMessage.java | 115 +++++++++++
 .../wx/v3/bean/combine/CombinePayOrder.java   |  92 +++++++++
 .../wx/v3/bean/combine/CombineSubOrder.java   |  42 ++++
 .../pay/wx/v3/bean/order/SettleInfo.java      |  45 +++++
 .../egzosn/pay/wx/v3/bean/order/SubOrder.java | 187 ++++++++++++++++++
 .../pay/wx/v3/bean/response/WxPayMessage.java |  67 ++++++-
 .../pay/wx/v3/bean/response/order/Amount.java |  34 +++-
 .../pay/wx/v3/bean/response/order/Payer.java  |   1 +
 .../wx/v3/bean/response/order/TradeState.java |  19 +-
 .../com/egzosn/pay/wx/v3/utils/WxConst.java   |  10 +-
 15 files changed, 902 insertions(+), 63 deletions(-)
 create mode 100644 pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/api/WxCombinePayService.java
 create mode 100644 pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/combine/CombineAmount.java
 create mode 100644 pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/combine/CombineCloseOrder.java
 create mode 100644 pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/combine/CombinePayMessage.java
 create mode 100644 pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/combine/CombinePayOrder.java
 create mode 100644 pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/combine/CombineSubOrder.java
 create mode 100644 pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/order/SettleInfo.java
 create mode 100644 pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/order/SubOrder.java

diff --git a/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/api/WxCombinePayService.java b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/api/WxCombinePayService.java
new file mode 100644
index 0000000..4c99699
--- /dev/null
+++ b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/api/WxCombinePayService.java
@@ -0,0 +1,159 @@
+package com.egzosn.pay.wx.v3.api;
+
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+import com.alibaba.fastjson.JSON;
+import com.alibaba.fastjson.JSONObject;
+import com.alibaba.fastjson.serializer.SerializerFeature;
+import com.egzosn.pay.common.bean.CloseOrder;
+import com.egzosn.pay.common.bean.Order;
+import com.egzosn.pay.common.bean.OrderParaStructure;
+import com.egzosn.pay.common.bean.PayMessage;
+import com.egzosn.pay.common.bean.PayOrder;
+import com.egzosn.pay.common.bean.result.PayException;
+import com.egzosn.pay.common.exception.PayErrorException;
+import com.egzosn.pay.common.http.HttpConfigStorage;
+import com.egzosn.pay.common.util.DateUtils;
+import com.egzosn.pay.common.util.MapGen;
+import com.egzosn.pay.common.util.str.StringUtils;
+import com.egzosn.pay.wx.v3.bean.WxTransactionType;
+import com.egzosn.pay.wx.v3.bean.combine.CombinePayMessage;
+import com.egzosn.pay.wx.v3.utils.WxConst;
+
+/**
+ * 微信合单支付服务
+ *
+ * @author egan
+ * 
+ * email egzosn@gmail.com
+ * date 2016-5-18 14:09:01
+ * 
+ */ +public class WxCombinePayService extends WxPayService { + + /** + * 创建支付服务 + * + * @param payConfigStorage 微信对应的支付配置 + */ + public WxCombinePayService(WxPayConfigStorage payConfigStorage) { + super(payConfigStorage); + } + + /** + * 创建支付服务 + * + * @param payConfigStorage 微信对应的支付配置 + * @param configStorage 微信对应的网络配置,包含代理配置、ssl证书配置 + */ + public WxCombinePayService(WxPayConfigStorage payConfigStorage, HttpConfigStorage configStorage) { + super(payConfigStorage, configStorage); + } + + + /** + * 获取公共参数 + * + * @return 公共参数 + */ + public Map getPublicParameters() { + Map parameters = new LinkedHashMap<>(); + parameters.put(WxConst.COMBINE_APPID, payConfigStorage.getAppId()); + parameters.put(WxConst.COMBINE_MCH_ID, payConfigStorage.getMchId()); + return parameters; + } + + /** + * 初始化通知URL必须为直接可访问的URL,不允许携带查询串,要求必须为https地址。 + * + * @param parameters 订单参数 + * @param order 订单信息 + * @return 订单参数 + */ + public void initNotifyUrl(Map parameters, Order order) { + OrderParaStructure.loadParameters(parameters, WxConst.NOTIFY_URL, payConfigStorage.getNotifyUrl()); + OrderParaStructure.loadParameters(parameters, WxConst.NOTIFY_URL, order); + } + + /** + * 微信统一下单接口 + * + * @param order 支付订单集 + * @return 下单结果 + */ + public JSONObject unifiedOrder(PayOrder order) { + + //统一下单 + Map parameters = getPublicParameters(); + + // 订单号 + parameters.put(WxConst.COMBINE_OUT_TRADE_NO, order.getOutTradeNo()); + + OrderParaStructure.loadDateParameters(parameters, WxConst.TIME_START, order, DateUtils.YYYY_MM_DD_T_HH_MM_SS); + OrderParaStructure.loadDateParameters(parameters, WxConst.TIME_EXPIRE, order, DateUtils.YYYY_MM_DD_T_HH_MM_SS); + initNotifyUrl(parameters, order); + //支付场景描述 + OrderParaStructure.loadParameters(parameters, WxConst.SCENE_INFO, order); + //子单信息 最多支持子单条数:50 + OrderParaStructure.loadParameters(parameters, WxConst.SUB_ORDERS, order); + //支付者信息 + if (StringUtils.isNotEmpty(order.getOpenid())) { + parameters.put("combine_payer_info", new MapGen<>("openid", order.getOpenid()).getAttr()); + } + + return getAssistService().doExecute(parameters, order); + } + + + /** + * 交易查询接口 + * + * @param transactionId 微信支付平台订单号 + * @param outTradeNo 商户单号 + * @return 返回查询回来的结果集,支付方原值返回 + */ + @Override + public Map query(String transactionId, String outTradeNo) { + return getAssistService().doExecute("", WxTransactionType.COMBINE_TRANSACTION, outTradeNo); + } + + + /** + * 交易关闭接口 + * + * @param transactionId 支付平台订单号 + * @param outTradeNo 商户单号 + * @return 返回支付方交易关闭后的结果 + */ + @Override + public Map close(String transactionId, String outTradeNo) { + throw new PayErrorException(new PayException("failure", "合单关闭必须要有子单")); + } + + /** + * 交易关闭接口 + * + * @param closeOrder 关闭订单 + * @return 返回支付方交易关闭后的结果 + */ + @Override + public Map close(CloseOrder closeOrder) { + Map parameters = new MapGen(WxConst.COMBINE_APPID, payConfigStorage.getAppId()) + .keyValue(WxConst.SUB_ORDERS, closeOrder.getAttr(WxConst.SUB_ORDERS)) + .getAttr(); + String requestBody = JSON.toJSONString(parameters, SerializerFeature.WriteMapNullValue); + return getAssistService().doExecute(requestBody, WxTransactionType.COMBINE_CLOSE, closeOrder.getOutTradeNo()); + } + + /** + * 创建消息 + * + * @param message 支付平台返回的消息 + * @return 支付消息对象 + */ + @Override + public PayMessage createMessage(Map message) { + return CombinePayMessage.create(message); + } +} diff --git a/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/api/WxPayService.java b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/api/WxPayService.java index 49da334..80c6679 100644 --- a/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/api/WxPayService.java +++ b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/api/WxPayService.java @@ -22,6 +22,7 @@ import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONObject; import com.egzosn.pay.common.api.BasePayService; import com.egzosn.pay.common.bean.BillType; +import com.egzosn.pay.common.bean.CloseOrder; import com.egzosn.pay.common.bean.CurType; import com.egzosn.pay.common.bean.MethodType; import com.egzosn.pay.common.bean.NoticeParams; @@ -81,27 +82,13 @@ public class WxPayService extends BasePayService { /** * 辅助api */ - private volatile WxPayAssistService wxPayAssistService; + private volatile WxPayAssistService assistService; /** * 微信参数构造器 */ private volatile WxParameterStructure wxParameterStructure; - - public WxPayAssistService getAssistService() { - if (null == wxPayAssistService) { - wxPayAssistService = new DefaultWxPayAssistService(this); - //在这预先进行初始化 - wxPayAssistService.refreshCertificate(); - } - return wxPayAssistService; - } - - public void setAssistService(WxPayAssistService wxPayAssistService) { - this.wxPayAssistService = wxPayAssistService; - } - /** * 创建支付服务 * @@ -123,25 +110,23 @@ public class WxPayService extends BasePayService { /** - * 初始化之后执行 + * 辅助api + * @return 辅助api */ - @Override - protected void initAfter() { - new Thread(() -> { - payConfigStorage.loadCertEnvironment(); - wxParameterStructure = new WxParameterStructure(payConfigStorage); + public WxPayAssistService getAssistService() { + if (null == assistService) { + assistService = new DefaultWxPayAssistService(this); //在这预先进行初始化 - try { - Thread.sleep(10); - } - catch (InterruptedException e) { + assistService.refreshCertificate(); + } + return assistService; + } - } - getAssistService(); - }).start(); + public void setAssistService(WxPayAssistService assistService) { + this.assistService = assistService; + } - } /** * 设置api服务器地址 @@ -154,6 +139,14 @@ public class WxPayService extends BasePayService { return this; } + public String getApiServerUrl() { + return apiServerUrl; + } + + public WxParameterStructure getWxParameterStructure() { + return wxParameterStructure; + } + /** * 根据交易类型获取url * @@ -323,6 +316,7 @@ public class WxPayService extends BasePayService { * @param is 请求流 * @return 获得回调的请求参数 */ + @Deprecated @Override public Map getParameter2Map(Map parameterMap, InputStream is) { throw new PayErrorException(new WxPayError(FAILURE, "微信V3不支持方式")); @@ -350,7 +344,7 @@ public class WxPayService extends BasePayService { noticeParams.setBody(JSON.parseObject(data)); } catch (IOException e) { - LOG.error("获取回调参数异常", e); + throw new PayErrorException(new WxPayError(FAILURE, "获取回调参数异常"), e); } Map> headers = new HashMap<>(); Enumeration headerNames = request.getHeaderNames(); @@ -457,8 +451,20 @@ public class WxPayService extends BasePayService { */ @Override public Map close(String transactionId, String outTradeNo) { + return close(new CloseOrder(outTradeNo)); + } + + + /** + * 交易关闭接口 + * + * @param closeOrder 关闭订单 + * @return 返回支付方交易关闭后的结果 + */ + @Override + public Map close(CloseOrder closeOrder) { String parameters = wxParameterStructure.getSpParameters(); - return getAssistService().doExecute(parameters, WxTransactionType.CLOSE, outTradeNo); + return getAssistService().doExecute(parameters, WxTransactionType.CLOSE, closeOrder.getOutTradeNo()); } /** diff --git a/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/WxTransactionType.java b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/WxTransactionType.java index 70a6ee8..948d3f0 100644 --- a/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/WxTransactionType.java +++ b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/WxTransactionType.java @@ -25,11 +25,14 @@ public enum WxTransactionType implements TransactionType { /** * 获取证书. */ - CERT("certificates", MethodType.GET), + CERT("/v3/certificates", MethodType.GET), + + //----------------------------------------------------------------- + //以下为直连与服务商支付方式 /** - * 公众号支付 + * 微信公众号支付或者小程序支付 */ - JSAPI("pay{partner}/transactions/jsapi", MethodType.POST) { + JSAPI("/v3/pay{partner}/transactions/jsapi", MethodType.POST) { @Override public void setAttribute(Map parameters, PayOrder order) { String key = parameters.containsKey("sub_mchid") ? "sub_openid" : "openid"; @@ -40,15 +43,15 @@ public enum WxTransactionType implements TransactionType { /** * 二维码支付 */ - NATIVE("pay{partner}/transactions/native", MethodType.POST, true), + NATIVE("/v3/pay{partner}/transactions/native", MethodType.POST, true), /** * 移动支付 */ - APP("pay{partner}/transactions/app", MethodType.POST), + APP("/v3/pay{partner}/transactions/app", MethodType.POST), /** * H5支付 */ - H5("pay{partner}/transactions/h5", MethodType.POST, true) { + H5("/v3/pay{partner}/transactions/h5", MethodType.POST, true) { @Override public void setAttribute(Map parameters, PayOrder order) { Object sceneInfoObj = parameters.get(WxConst.SCENE_INFO); @@ -79,7 +82,7 @@ public enum WxTransactionType implements TransactionType { * 兼容 后期会抛弃 */ @Deprecated - MWEB("pay{partner}/transactions/h5", MethodType.POST, true) { + MWEB("/v3/pay{partner}/transactions/h5", MethodType.POST, true) { @Override public void setAttribute(Map parameters, PayOrder order) { H5.setAttribute(parameters, order); @@ -91,35 +94,64 @@ public enum WxTransactionType implements TransactionType { * 查询订单 * 兼容V2的方式,通过入参来决定 */ - QUERY("pay{partner}/transactions/", MethodType.GET), + QUERY("/v3/pay{partner}/transactions/", MethodType.GET), /** * 微信支付订单号查询 */ - QUERY_TRANSACTION_ID("pay{partner}/transactions/id/{transaction_id}", MethodType.GET), + QUERY_TRANSACTION_ID("/v3/pay{partner}/transactions/id/{transaction_id}", MethodType.GET), /** * 商户订单号查询 */ - QUERY_OUT_TRADE_NO("pay{partner}/transactions/out-trade-no/{out_trade_no}", MethodType.GET), + QUERY_OUT_TRADE_NO("/v3/pay{partner}/transactions/out-trade-no/{out_trade_no}", MethodType.GET), /** * 关闭订单 */ - CLOSE("pay{partner}/transactions/out-trade-no/{out_trade_no}/close", MethodType.POST), + CLOSE("/v3/pay{partner}/transactions/out-trade-no/{out_trade_no}/close", MethodType.POST), /** * 申请退款 */ - REFUND("refund/domestic/refunds", MethodType.POST), + REFUND("/v3/refund/domestic/refunds", MethodType.POST), /** * 查询退款 */ - REFUND_QUERY("refund/domestic/refunds/{out_refund_no}", MethodType.GET), + REFUND_QUERY("/v3/refund/domestic/refunds/{out_refund_no}", MethodType.GET), /** * 申请交易账单 */ - TRADE_BILL("bill/tradebill", MethodType.GET), + TRADE_BILL("/v3/bill/tradebill", MethodType.GET), /** * 申请资金账单 */ - FUND_FLOW_BILL("bill/fundflowbill", MethodType.GET) + FUND_FLOW_BILL("/v3/bill/fundflowbill", MethodType.GET), + + //----------------------------------------------------------------- + //以下为合并支付 + /** + * 合单下单-APP支付API. + */ + COMBINE_APP("/v3/combine-transactions/app", MethodType.POST), + + /** + * 合单下单-微信公众号支付或者小程序支付. + */ + COMBINE_JSAPI("/v3/combine-transactions/jsapi", MethodType.POST), + /** + * 合单下单-H5支付API. + */ + COMBINE_H5("/v3/combine-transactions/h5", MethodType.POST, true), + /** + * 合单下单-Native支付API. + */ + COMBINE_NATIVE("/v3/combine-transactions/native", MethodType.POST, true), + /** + * 合单查询订单API. + */ + COMBINE_TRANSACTION("/v3/combine-transactions/out-trade-no/{combine_out_trade_no}", MethodType.GET), + + /** + * 合单关闭订单API. + */ + COMBINE_CLOSE("/v3/combine-transactions/out-trade-no/{combine_out_trade_no}/close", MethodType.POST), ; WxTransactionType(String type, MethodType method) { diff --git a/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/combine/CombineAmount.java b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/combine/CombineAmount.java new file mode 100644 index 0000000..f3d812b --- /dev/null +++ b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/combine/CombineAmount.java @@ -0,0 +1,34 @@ +package com.egzosn.pay.wx.v3.bean.combine; + +import com.alibaba.fastjson.annotation.JSONField; +import com.egzosn.pay.wx.v3.bean.response.order.Amount; + +/** + * 合单支付订单金额信息. + * + * @author Egan + *
+ * email egan@egzosn.com
+ * date 2021/10/5
+ * 
+ */ +public class CombineAmount extends Amount { + + /** + * 子单金额,单位为分,必填 + * 境外场景下,标价金额要超过商户结算币种的最小单位金额,例如结算币种为美元,则标价金额必须大于1美分 + */ + @JSONField(name = "total_amount") + private Integer totalAmount; + + + public Integer getTotalAmount() { + return totalAmount; + } + + public void setTotalAmount(Integer totalAmount) { + this.totalAmount = totalAmount; + } + + +} \ No newline at end of file diff --git a/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/combine/CombineCloseOrder.java b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/combine/CombineCloseOrder.java new file mode 100644 index 0000000..cef6267 --- /dev/null +++ b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/combine/CombineCloseOrder.java @@ -0,0 +1,30 @@ +package com.egzosn.pay.wx.v3.bean.combine; + +import java.util.List; + +import com.egzosn.pay.common.bean.CloseOrder; +import com.egzosn.pay.wx.v3.utils.WxConst; + +/** + * 微信合单关闭订单 + * @author Egan + * @email egan@egzosn.com + * @date 2021/10/6 + */ +public class CombineCloseOrder extends CloseOrder { + + /** + * 子单信息,必填,最多50单 + */ + private List subOrders; + + + public List getSubOrders() { + return subOrders; + } + + public void setSubOrders(List subOrders) { + this.subOrders = subOrders; + addAttr(WxConst.SUB_ORDERS, subOrders); + } +} diff --git a/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/combine/CombinePayMessage.java b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/combine/CombinePayMessage.java new file mode 100644 index 0000000..cda2d61 --- /dev/null +++ b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/combine/CombinePayMessage.java @@ -0,0 +1,115 @@ +package com.egzosn.pay.wx.v3.bean.combine; + +import java.util.List; +import java.util.Map; + +import com.alibaba.fastjson.JSONObject; +import com.alibaba.fastjson.annotation.JSONField; +import com.egzosn.pay.common.bean.PayMessage; +import com.egzosn.pay.wx.v3.bean.order.SceneInfo; +import com.egzosn.pay.wx.v3.bean.response.order.Payer; +import com.egzosn.pay.wx.v3.utils.WxConst; + +/** + * 合单支付回调消息,兼容退款回调 + * @author Egan + *
+ * email egan@egzosn.com
+ * date 2021/10/4
+ * 
+ */ +public class CombinePayMessage extends PayMessage { + + + /** + * 合单商户appid,即合单发起方的appid + */ + @JSONField(name = WxConst.COMBINE_APPID) + private String combineAppid; + + /** + * 合单商户号. + */ + @JSONField(name = WxConst.COMBINE_MCH_ID) + private String combineMchid; + + /** + * 合单商户订单号. + */ + @JSONField(name = WxConst.COMBINE_OUT_TRADE_NO) + private String combineOutTradeNo; + + + /** + * 支付者信息 + */ + @JSONField(name = "combine_payer_info") + private Payer combinePayerInfo; + + /** + * 场景信息,合单支付回调只返回device_id + */ + @JSONField(name = WxConst.SCENE_INFO) + private SceneInfo sceneInfo; + + /** + * 合单支付回调子订单. + */ + @JSONField(name = WxConst.SUB_ORDERS) + private List subOrders; + + public String getCombineAppid() { + return combineAppid; + } + + public void setCombineAppid(String combineAppid) { + this.combineAppid = combineAppid; + } + + public String getCombineMchid() { + return combineMchid; + } + + public void setCombineMchid(String combineMchid) { + this.combineMchid = combineMchid; + } + + public String getCombineOutTradeNo() { + return combineOutTradeNo; + } + + public void setCombineOutTradeNo(String combineOutTradeNo) { + this.combineOutTradeNo = combineOutTradeNo; + } + + public Payer getCombinePayerInfo() { + return combinePayerInfo; + } + + public void setCombinePayerInfo(Payer combinePayerInfo) { + this.combinePayerInfo = combinePayerInfo; + } + + public SceneInfo getSceneInfo() { + return sceneInfo; + } + + public void setSceneInfo(SceneInfo sceneInfo) { + this.sceneInfo = sceneInfo; + } + + public List getSubOrders() { + return subOrders; + } + + public void setSubOrders(List subOrders) { + this.subOrders = subOrders; + } + + public static final CombinePayMessage create(Map message) { + CombinePayMessage payMessage = new JSONObject(message).toJavaObject(CombinePayMessage.class); +// payMessage.setPayType(""); + payMessage.setPayMessage(message); + return payMessage; + } +} diff --git a/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/combine/CombinePayOrder.java b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/combine/CombinePayOrder.java new file mode 100644 index 0000000..280aef3 --- /dev/null +++ b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/combine/CombinePayOrder.java @@ -0,0 +1,92 @@ +package com.egzosn.pay.wx.v3.bean.combine; + +import java.util.Date; +import java.util.List; + +import com.egzosn.pay.common.bean.PayOrder; +import com.egzosn.pay.wx.v3.bean.order.SceneInfo; +import com.egzosn.pay.wx.v3.bean.order.SubOrder; +import com.egzosn.pay.wx.v3.utils.WxConst; + +/** + * 合并支付订单 + * + * @author Egan + *
+ * email egan@egzosn.com
+ * date 2021/10/5
+ * 
+ */ +public class CombinePayOrder extends PayOrder { + + /** + * 子单信息,必填,最多50单 + */ + private List subOrders; + /** + * 交易起始时间,选填 + */ + private Date timeStart; + + /** + * 交易结束时间,选填 + */ + private Date timeExpire; + + /** + * 支付场景信息描述 + */ + private SceneInfo sceneInfo; + + public List getSubOrders() { + return subOrders; + } + + public void setSubOrders(List subOrders) { + this.subOrders = subOrders; + addAttr(WxConst.SUB_ORDERS, subOrders); + } + + public Date getTimeStart() { + return timeStart; + } + + public void setTimeStart(Date timeStart) { + this.timeStart = timeStart; + addAttr(WxConst.TIME_START, timeStart); + } + + public Date getTimeExpire() { + return timeExpire; + } + + public void setTimeExpire(Date timeExpire) { + this.timeExpire = timeExpire; + addAttr(WxConst.TIME_EXPIRE, timeExpire); + } + + /** + * 合单支付总订单号,要求32个字符内,只能是数字、大小写字母_-|*@ ,且在同一个商户号下唯一 。 + * @return 合单支付总订单号 + */ + public String getCombineOutTradeNo() { + return getOutTradeNo(); + } + + /** + * 合单支付总订单号,要求32个字符内,只能是数字、大小写字母_-|*@ ,且在同一个商户号下唯一 。 + * @param combineOutTradeNo 合单支付总订单号 + */ + public void setCombineOutTradeNo(String combineOutTradeNo) { + setOutTradeNo(combineOutTradeNo); + } + + public SceneInfo getSceneInfo() { + return sceneInfo; + } + + public void setSceneInfo(SceneInfo sceneInfo) { + this.sceneInfo = sceneInfo; + addAttr(WxConst.SCENE_INFO, sceneInfo); + } +} diff --git a/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/combine/CombineSubOrder.java b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/combine/CombineSubOrder.java new file mode 100644 index 0000000..ee66a24 --- /dev/null +++ b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/combine/CombineSubOrder.java @@ -0,0 +1,42 @@ +package com.egzosn.pay.wx.v3.bean.combine; + +import com.alibaba.fastjson.annotation.JSONField; + +/** + * 子单信息,最多50单. + * @author Egan + *
+ * email egan@egzosn.com
+ * date 2021/10/5
+ * 
+ */ +public class CombineSubOrder { + + /** + * 子单发起方商户号,必填,必须与发起方appid有绑定关系。 + */ + private String mchid; + + /** + * 商户系统内部订单号,要求32个字符内,只能是数字、大小写字母_-|*@ ,且在同一个商户号下唯一。 + * 示例值:20150806125346 + */ + @JSONField(name = "out_trade_no") + private String outTradeNo; + + public String getMchid() { + return mchid; + } + + public void setMchid(String mchid) { + this.mchid = mchid; + } + + public String getOutTradeNo() { + return outTradeNo; + } + + public void setOutTradeNo(String outTradeNo) { + this.outTradeNo = outTradeNo; + } +} diff --git a/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/order/SettleInfo.java b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/order/SettleInfo.java new file mode 100644 index 0000000..4ce0a7c --- /dev/null +++ b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/order/SettleInfo.java @@ -0,0 +1,45 @@ +package com.egzosn.pay.wx.v3.bean.order; + +import com.alibaba.fastjson.annotation.JSONField; + +/** + * 结算信息 + * @author Egan + *
+ * email egan@egzosn.com
+ * date 2021/10/5
+ * 
+ */ +public class SettleInfo { + /** + * 是否指定分账,枚举值 + * true:是 + * false:否 + * 示例值:true + */ + @JSONField(name = "profit_sharing") + private Boolean profitSharing; + /** + * 补差金额 + * SettleInfo.profit_sharing为true时,该金额才生效。 + * 注意:单笔订单最高补差金额为5000元 + */ + @JSONField(name = "subsidy_amount") + private Integer subsidyAmount; + + public Boolean getProfitSharing() { + return profitSharing; + } + + public void setProfitSharing(Boolean profitSharing) { + this.profitSharing = profitSharing; + } + + public Integer getSubsidyAmount() { + return subsidyAmount; + } + + public void setSubsidyAmount(Integer subsidyAmount) { + this.subsidyAmount = subsidyAmount; + } +} diff --git a/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/order/SubOrder.java b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/order/SubOrder.java new file mode 100644 index 0000000..0b0010d --- /dev/null +++ b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/order/SubOrder.java @@ -0,0 +1,187 @@ +package com.egzosn.pay.wx.v3.bean.order; + +import java.util.Date; +import java.util.List; + +import com.alibaba.fastjson.annotation.JSONField; +import com.egzosn.pay.wx.v3.bean.WxTransactionType; +import com.egzosn.pay.wx.v3.bean.combine.CombineAmount; +import com.egzosn.pay.wx.v3.bean.combine.CombineSubOrder; +import com.egzosn.pay.wx.v3.bean.response.order.PromotionDetail; +import com.egzosn.pay.wx.v3.bean.response.order.TradeState; + +/** + * 子单信息,最多50单. + * + * @author Egan + *
+ * email egan@egzosn.com
+ * date 2021/10/5
+ * 
+ */ +public class SubOrder extends CombineSubOrder { + /** + * 合单支付订单金额信息,必填。 + */ + private CombineAmount amount; + + + /** + * 商品描述,必填,需传入应用市场上的APP名字-实际商品名称,例如:天天爱消除-游戏充值。 + */ + private String description; + + + /** + * 二级商户商户号,由微信支付生成并下发。 + *

+ * 服务商子商户的商户号,被合单方。 + *

+ * 直连商户不用传二级商户号。 + */ + @JSONField(name = "sub_mchid") + private String subMchid; + + /** + * 结算信息,选填 + */ + @JSONField(name = "settle_info") + private SettleInfo settleInfo; + + + /** + * 银行类型,采用字符串类型的银行标识。 + * 银行标识请参考 《银行类型对照表》 + * 示例值:CMC + */ + @JSONField(name = "bank_type") + private String bankType; + + /** + * 附加数据,在查询API和支付通知中原样返回,可作为自定义参数使用 + */ + private String attach; + + /** + * 支付完成时间|| 退款完成时间,遵循rfc3339标准格式, + * 格式为YYYY-MM-DDTHH:mm:ss+TIMEZONE,YYYY-MM-DD表示年月日,T出现在字符串中,表示time元素的开头,HH:mm:ss表示时分秒, + * TIMEZONE表示时区(+08:00表示东八区时间,领先UTC 8小时,即北京时间)。 + * 例如:2015-05-20T13:29:35+08:00表示,北京时间2015年5月20日 13点29分35秒。 + * 示例值:2018-06-08T10:34:56+08:00 + */ + @JSONField(name = "success_time", format = "yyyy-MM-dd'T'HH:mm:ssXXX") + private Date successTime; + + /** + * 交易状态 + */ + @JSONField(name = "trade_state") + private TradeState tradeState; + + /** + * 交易类型 + */ + @JSONField(name = "trade_type") + private WxTransactionType tradeType; + + /** + * 微信支付侧订单号 + */ + @JSONField(name = "transaction_id") + private String transactionId; + + /** + * 优惠功能,子单有核销优惠券时有返回 + */ + @JSONField(name = "promotion_detail") + private List promotionDetail; + + public CombineAmount getAmount() { + return amount; + } + + public void setAmount(CombineAmount amount) { + this.amount = amount; + } + + public String getDescription() { + return description; + } + + public void setDescription(String description) { + this.description = description; + } + + + public String getSubMchid() { + return subMchid; + } + + public void setSubMchid(String subMchid) { + this.subMchid = subMchid; + } + + public SettleInfo getSettleInfo() { + return settleInfo; + } + + public void setSettleInfo(SettleInfo settleInfo) { + this.settleInfo = settleInfo; + } + + public String getBankType() { + return bankType; + } + + public void setBankType(String bankType) { + this.bankType = bankType; + } + + public String getAttach() { + return attach; + } + + public void setAttach(String attach) { + this.attach = attach; + } + + public Date getSuccessTime() { + return successTime; + } + + public void setSuccessTime(Date successTime) { + this.successTime = successTime; + } + + public TradeState getTradeState() { + return tradeState; + } + + public void setTradeState(TradeState tradeState) { + this.tradeState = tradeState; + } + + public WxTransactionType getTradeType() { + return tradeType; + } + + public void setTradeType(WxTransactionType tradeType) { + this.tradeType = tradeType; + } + + public String getTransactionId() { + return transactionId; + } + + public void setTransactionId(String transactionId) { + this.transactionId = transactionId; + } + + public List getPromotionDetail() { + return promotionDetail; + } + + public void setPromotionDetail(List promotionDetail) { + this.promotionDetail = promotionDetail; + } +} diff --git a/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/response/WxPayMessage.java b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/response/WxPayMessage.java index 8aa4008..f02ffc4 100644 --- a/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/response/WxPayMessage.java +++ b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/response/WxPayMessage.java @@ -16,7 +16,7 @@ import com.egzosn.pay.wx.v3.bean.response.order.PromotionDetail; import com.egzosn.pay.wx.v3.bean.response.order.TradeState; /** - * 支付回调消息 + * 支付回调消息,兼容退款回调 * @author Egan *

  * email egan@egzosn.com
@@ -99,8 +99,36 @@ public class WxPayMessage extends PayMessage {
      */
     @JSONField(name = "trade_state")
     private TradeState tradeState;
-
-
+    /**
+     * 商户退款单号
+     */
+    @JSONField(name = "out_refund_no")
+    private String outRefundNo;
+    /**
+     * 微信退款单号
+     */
+    @JSONField(name = "refund_id")
+    private String refundId;
+    /**
+     * 退款状态,枚举值:
+     * SUCCESS:退款成功
+     * CLOSE:退款关闭
+     * ABNORMAL:退款异常,退款到银行发现用户的卡作废或者冻结了,导致原路退款银行卡失败,可前往【商户平台—>交易中心】,手动处理此笔退款
+     * 示例值:SUCCESS
+     */
+    @JSONField(name = "refund_status")
+    private TradeState refundStatus;
+    /**
+     * 退款入账账户
+     * 取当前退款单的退款入账方。
+     * 1、退回银行卡:{银行名称}{卡类型}{卡尾号}
+     * 2、退回支付用户零钱: 支付用户零钱
+     * 3、退还商户: 商户基本账户、商户结算银行账户
+     * 4、退回支付用户零钱通:支付用户零钱通
+     * 示例值:招商银行信用卡0403
+     */
+    @JSONField(name = "user_received_account")
+    private String userReceivedAccount;
     /**
      * 交易状态描述
      * 示例值:支付成功
@@ -121,7 +149,7 @@ public class WxPayMessage extends PayMessage {
     private String attach;
 
     /**
-     * 支付完成时间,遵循rfc3339标准格式,
+     * 支付完成时间|| 退款完成时间,遵循rfc3339标准格式,
      * 格式为YYYY-MM-DDTHH:mm:ss+TIMEZONE,YYYY-MM-DD表示年月日,T出现在字符串中,表示time元素的开头,HH:mm:ss表示时分秒,
      * TIMEZONE表示时区(+08:00表示东八区时间,领先UTC 8小时,即北京时间)。
      * 例如:2015-05-20T13:29:35+08:00表示,北京时间2015年5月20日 13点29分35秒。
@@ -301,6 +329,37 @@ public class WxPayMessage extends PayMessage {
         return BigDecimal.valueOf(getAmount().getTotal());
     }
 
+    public String getOutRefundNo() {
+        return outRefundNo;
+    }
+
+    public void setOutRefundNo(String outRefundNo) {
+        this.outRefundNo = outRefundNo;
+    }
+
+    public String getRefundId() {
+        return refundId;
+    }
+
+    public void setRefundId(String refundId) {
+        this.refundId = refundId;
+    }
+
+    public TradeState getRefundStatus() {
+        return refundStatus;
+    }
+
+    public void setRefundStatus(TradeState refundStatus) {
+        this.refundStatus = refundStatus;
+    }
+
+    public String getUserReceivedAccount() {
+        return userReceivedAccount;
+    }
+
+    public void setUserReceivedAccount(String userReceivedAccount) {
+        this.userReceivedAccount = userReceivedAccount;
+    }
 
     public static final WxPayMessage create(Map message) {
         WxPayMessage payMessage = new JSONObject(message).toJavaObject(WxPayMessage.class);
diff --git a/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/response/order/Amount.java b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/response/order/Amount.java
index c6cf950..b134a8c 100644
--- a/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/response/order/Amount.java
+++ b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/response/order/Amount.java
@@ -2,25 +2,41 @@ package com.egzosn.pay.wx.v3.bean.response.order;
 
 import java.math.BigDecimal;
 
+import com.alibaba.fastjson.annotation.JSONField;
+
 /**
  * 回调中的订单金额信息
+ *
  * @author Egan
  * 
  * email egan@egzosn.com
  * date 2021/10/4
  * 
*/ -public class Amount extends com.egzosn.pay.wx.v3.bean.order.Amount{ +public class Amount extends com.egzosn.pay.wx.v3.bean.order.Amount { /** * 用户支付金额,单位为分。 */ + @JSONField(name = "payer_total") private BigDecimal payerTotal; /** * 用户支付币种 */ + @JSONField(name = "payer_currency") private String payerCurrency; + /** + * 退款金额,单位为分 + */ + private Integer refund; + + /** + * 退款给用户的金额,单位为分,不包含所有优惠券金额 + */ + @JSONField(name = "payer_refund") + private Integer payerRefund; + public BigDecimal getPayerTotal() { return payerTotal; } @@ -36,4 +52,20 @@ public class Amount extends com.egzosn.pay.wx.v3.bean.order.Amount{ public void setPayerCurrency(String payerCurrency) { this.payerCurrency = payerCurrency; } + + public Integer getRefund() { + return refund; + } + + public void setRefund(Integer refund) { + this.refund = refund; + } + + public Integer getPayerRefund() { + return payerRefund; + } + + public void setPayerRefund(Integer payerRefund) { + this.payerRefund = payerRefund; + } } diff --git a/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/response/order/Payer.java b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/response/order/Payer.java index 623b97d..7f359f1 100644 --- a/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/response/order/Payer.java +++ b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/response/order/Payer.java @@ -11,6 +11,7 @@ package com.egzosn.pay.wx.v3.bean.response.order; public class Payer { /** * 用户在直连商户appid下的唯一标识。 + * 使用合单appid获取的对应用户openid。是用户在商户appid下的唯一标识。 */ private String openid; diff --git a/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/response/order/TradeState.java b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/response/order/TradeState.java index 6b6afe6..9d49a4c 100644 --- a/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/response/order/TradeState.java +++ b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/response/order/TradeState.java @@ -1,7 +1,8 @@ package com.egzosn.pay.wx.v3.bean.response.order; /** - * 微信侧返回交易状态 + * 微信侧返回交易状态,兼容退款状态 + * * @author Egan *
  * email egan@egzosn.com
@@ -11,31 +12,28 @@ package com.egzosn.pay.wx.v3.bean.response.order;
 public enum TradeState {
     /**
      * 支付成功
-     *
-     * 
+     * 退款成功
      */
     SUCCESS,
     /**
      * 转入退款
-     *
-     * 
      */
     REFUND,
     /**
      * 未支付
-     *
-     * 
      */
     NOTPAY,
     /**
      * 已关闭
-     *
-     * 
+     * 退款关闭
      */
     CLOSED,
+    /**
+     * 退款异常.
+     */
+    ABNORMAL,
     /**
      * 已撤销(付款码支付)
-     *
      */
     REVOKED,
     /**
@@ -48,7 +46,6 @@ public enum TradeState {
     PAYERROR,
     /**
      * 已接收,等待扣款
-     *
      */
     ACCEPT,
 }
\ No newline at end of file
diff --git a/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/utils/WxConst.java b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/utils/WxConst.java
index 2055d49..941ff10 100644
--- a/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/utils/WxConst.java
+++ b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/utils/WxConst.java
@@ -17,7 +17,7 @@ public final class WxConst {
     /**
      * 微信默认请求地址
      */
-    public static final String URI = "https://api.mch.weixin.qq.com/v3/";
+    public static final String URI = "https://api.mch.weixin.qq.com";
     /**
      * 证书别名
      */
@@ -31,12 +31,20 @@ public final class WxConst {
      * 沙箱
      */
     public static final String SANDBOXNEW = "sandboxnew/";
+    public static final String COMBINE = "combine_";
     public static final String APPID = "appid";
+
+    public static final String COMBINE_APPID = COMBINE + APPID;
     public static final String MCH_ID = "mchid";
+    public static final String COMBINE_MCH_ID = COMBINE + MCH_ID;
     public static final String SUB_MCH_ID = "sub_mchid";
     public static final String SP_MCH_ID = "sp_mchid";
     public static final String OUT_TRADE_NO = "out_trade_no";
+    public static final String COMBINE_OUT_TRADE_NO = COMBINE + OUT_TRADE_NO;
     public static final String NOTIFY_URL = "notify_url";
+    public static final String TIME_START = "time_start";
+    public static final String TIME_EXPIRE = "time_expire";
+    public static final String SUB_ORDERS = "sub_orders";
 
     public static final String TOKEN_PATTERN = "mchid=\"%s\",nonce_str=\"%s\",timestamp=\"%d\",serial_no=\"%s\",signature=\"%s\"";
 
-- 
Gitee


From 175fa4297712944e287b04fd761463866e1ee462 Mon Sep 17 00:00:00 2001
From: egzosn 
Date: Wed, 6 Oct 2021 16:35:32 +0800
Subject: [PATCH 227/299] =?UTF-8?q?=E5=BE=AE=E4=BF=A1V3=E6=94=AF=E4=BB=98?=
 =?UTF-8?q?=E4=B8=8E=E5=BE=AE=E4=BF=A1V3=E5=90=88=E5=8D=95=E6=94=AF?=
 =?UTF-8?q?=E4=BB=98=E6=A1=88=E4=BE=8B=E5=AE=9E=E7=8E=B0?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 .../controller/WxV3CombinePayController.java  | 307 ++++++++++++++++++
 .../demo/controller/WxV3PayController.java    |  78 +----
 .../handler/WxV3CombinePayMessageHandler.java |  29 ++
 .../pay/wx/v3/bean/combine/CombineAmount.java |   6 +
 .../wx/v3/bean/combine/CombineSubOrder.java   |  17 +
 .../egzosn/pay/wx/v3/bean/order/Amount.java   |   2 +-
 .../egzosn/pay/wx/v3/bean/order/H5Info.java   |   5 +
 .../egzosn/pay/wx/v3/bean/order/SubOrder.java |  17 +-
 8 files changed, 368 insertions(+), 93 deletions(-)
 create mode 100644 pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/WxV3CombinePayController.java
 create mode 100644 pay-java-demo/src/main/java/com/egzosn/pay/demo/service/handler/WxV3CombinePayMessageHandler.java

diff --git a/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/WxV3CombinePayController.java b/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/WxV3CombinePayController.java
new file mode 100644
index 0000000..0bfd3ce
--- /dev/null
+++ b/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/WxV3CombinePayController.java
@@ -0,0 +1,307 @@
+
+package com.egzosn.pay.demo.controller;
+
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+import javax.annotation.PostConstruct;
+import javax.imageio.ImageIO;
+import javax.servlet.http.HttpServletRequest;
+
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import com.egzosn.pay.common.bean.CertStoreType;
+import com.egzosn.pay.common.bean.RefundOrder;
+import com.egzosn.pay.demo.request.QueryOrder;
+import com.egzosn.pay.demo.service.handler.WxV3CombinePayMessageHandler;
+import com.egzosn.pay.web.support.HttpRequestNoticeParams;
+import com.egzosn.pay.wx.v3.api.WxCombinePayService;
+import com.egzosn.pay.wx.v3.api.WxPayConfigStorage;
+import com.egzosn.pay.wx.v3.bean.WxTransactionType;
+import com.egzosn.pay.wx.v3.bean.combine.CombineAmount;
+import com.egzosn.pay.wx.v3.bean.combine.CombineCloseOrder;
+import com.egzosn.pay.wx.v3.bean.combine.CombinePayOrder;
+import com.egzosn.pay.wx.v3.bean.combine.CombineSubOrder;
+import com.egzosn.pay.wx.v3.bean.order.H5Info;
+import com.egzosn.pay.wx.v3.bean.order.SceneInfo;
+import com.egzosn.pay.wx.v3.bean.order.SubOrder;
+import com.egzosn.pay.wx.v3.bean.response.WxRefundResult;
+
+/**
+ * 微信V3合单发起支付入口
+ *
+ * @author egan
+ * email egzosn@gmail.com
+ * date 2016/11/18 0:25
+ */
+@RestController
+@RequestMapping("wxV3combine")
+public class WxV3CombinePayController {
+
+    private WxCombinePayService service = null;
+
+    @PostConstruct  //没有证书的情况下注释掉,避免启动报错
+    public void init() {
+        WxPayConfigStorage wxPayConfigStorage = new WxPayConfigStorage();
+        wxPayConfigStorage.setAppId("wxc7b993ff15a9f27c");
+        wxPayConfigStorage.setMchId("1602947766");
+        //V3密钥 https://pay.weixin.qq.com/wiki/doc/apiv3/wechatpay/wechatpay3_2.shtml
+        wxPayConfigStorage.setV3ApiKey("9bd8f0e7af4841299d782406b7774f56");
+        wxPayConfigStorage.setNotifyUrl("http://sailinmu.iok.la/wxV3/payBack.json");
+        wxPayConfigStorage.setReturnUrl("http://sailinmu.iok.la/wxV3/payBack.json");
+        wxPayConfigStorage.setInputCharset("utf-8");
+        //使用证书时设置为true
+        wxPayConfigStorage.setCertSign(true);
+        //商户API证书 https://pay.weixin.qq.com/wiki/doc/apiv3/wechatpay/wechatpay3_1.shtml
+        wxPayConfigStorage.setApiClientKeyP12("E:\\Documents\\gitee\\支付\\yifenli_mall.p12");
+        wxPayConfigStorage.setCertStoreType(CertStoreType.PATH);
+        service = new WxCombinePayService(wxPayConfigStorage);
+        //设置回调消息处理
+        //TODO {@link com.egzosn.pay.demo.controller.WxPayController#payBack}
+        service.setPayMessageHandler(new WxV3CombinePayMessageHandler());
+    }
+
+
+    /**
+     * 跳到支付页面
+     * 针对实时支付
+     *
+     * @return 跳到支付页面
+     */
+    @RequestMapping(value = "toPay.html", produces = "text/html;charset=UTF-8")
+    public String toPay() {
+        CombinePayOrder order = new CombinePayOrder();
+        SceneInfo sceneInfo = new SceneInfo();
+        sceneInfo.setPayerClientIp("用户终端IP ");
+        sceneInfo.setDeviceId("终端设备号(门店号或收银设备ID) 。为了方便问题定位,H5支付场景下,该字段必填");
+        sceneInfo.setH5Info(new H5Info("场景类型,枚举值:\n" +
+                "iOS:IOS移动应用;\n" +
+                "Android:安卓移动应用;\n" +
+                "Wap:WAP网站应用;"));
+        order.setSceneInfo(sceneInfo);
+        order.setCombineOutTradeNo("合单商户订单号");
+        //子单信息,最多50单.
+        List subOrders = new ArrayList<>();
+        SubOrder subOrder = new SubOrder();
+        subOrder.setMchid("子单商户号");
+        subOrder.setAttach("附加数据,在查询API和支付通知中原样返回,可作为自定义参数使用。 ");
+        //"子单金额,单位为分。 "
+        subOrder.setAmount(new CombineAmount(121));
+        subOrder.setOutTradeNo("子单商户订单号 ");
+        subOrder.setDescription("商品描述");
+        subOrder.setSubMchid("二级商户商户号,由微信支付生成并下发。服务商子商户的商户号,被合单方。直连商户不用传二级商户号。 ");
+        subOrders.add(subOrder);
+        order.setSubOrders(subOrders);
+        order.setTransactionType(WxTransactionType.COMBINE_H5);
+        return service.toPay(order);
+    }
+
+    /**
+     * 公众号支付,小程序
+     *
+     * @return 返回jsapi所需参数
+     */
+    @RequestMapping(value = "jsapi")
+    public Map jsapi() {
+
+        CombinePayOrder order = new CombinePayOrder();
+        order.setTransactionType(WxTransactionType.COMBINE_JSAPI);
+        order.setCombineOutTradeNo("合单商户订单号");
+        order.setOpenid("使用合单appid获取的对应用户openid。是用户在商户appid下的唯一标识。 ");
+        //子单信息,最多50单.
+        List subOrders = new ArrayList<>();
+        SubOrder subOrder = new SubOrder();
+        subOrder.setMchid("子单商户号");
+        subOrder.setAttach("附加数据,在查询API和支付通知中原样返回,可作为自定义参数使用。 ");
+        //"子单金额,单位为分。 "
+        subOrder.setAmount(new CombineAmount(111));
+        subOrder.setOutTradeNo("子单商户订单号 ");
+        subOrder.setDescription("商品描述");
+        subOrder.setSubMchid("二级商户商户号,由微信支付生成并下发。服务商子商户的商户号,被合单方。直连商户不用传二级商户号。 ");
+        subOrders.add(subOrder);
+        order.setSubOrders(subOrders);
+        Map orderInfo = service.orderInfo(order);
+        orderInfo.put("code", 0);
+        return orderInfo;
+    }
+
+
+    /**
+     * 获取支付预订单信息
+     *
+     * @return 支付预订单信息
+     */
+    @RequestMapping("app")
+    public Map app() {
+
+        CombinePayOrder order = new CombinePayOrder();
+        order.setTransactionType(WxTransactionType.COMBINE_APP);
+        order.setCombineOutTradeNo("合单商户订单号");
+        //子单信息,最多50单.
+        List subOrders = new ArrayList<>();
+        SubOrder subOrder = new SubOrder();
+        subOrder.setMchid("子单商户号");
+        subOrder.setAttach("附加数据,在查询API和支付通知中原样返回,可作为自定义参数使用。 ");
+        //"子单金额,单位为分。 "
+        subOrder.setAmount(new CombineAmount(211));
+        subOrder.setOutTradeNo("子单商户订单号 ");
+        subOrder.setDescription("商品描述");
+        subOrder.setSubMchid("二级商户商户号,由微信支付生成并下发。服务商子商户的商户号,被合单方。直连商户不用传二级商户号。 ");
+        subOrders.add(subOrder);
+        order.setSubOrders(subOrders);
+        Map orderInfo = service.orderInfo(order);
+        orderInfo.put("code", 0);
+        return orderInfo;
+    }
+
+    /**
+     * 获取二维码图像
+     * 二维码支付
+     *
+     * @return 二维码图像
+     * @throws IOException IOException
+     */
+    @RequestMapping(value = "toQrPay.jpg", produces = "image/jpeg;charset=UTF-8")
+    public byte[] toWxQrPay() throws IOException {
+        CombinePayOrder order = new CombinePayOrder();
+        order.setTransactionType(WxTransactionType.COMBINE_NATIVE);
+        order.setCombineOutTradeNo("合单商户订单号");
+        //子单信息,最多50单.
+        List subOrders = new ArrayList<>();
+        SubOrder subOrder = new SubOrder();
+        subOrder.setMchid("子单商户号");
+        subOrder.setAttach("附加数据,在查询API和支付通知中原样返回,可作为自定义参数使用。 ");
+        //"子单金额,单位为分。 "
+        subOrder.setAmount(new CombineAmount(131));
+        subOrder.setOutTradeNo("子单商户订单号 ");
+        subOrder.setDescription("商品描述");
+        subOrder.setSubMchid("二级商户商户号,由微信支付生成并下发。服务商子商户的商户号,被合单方。直连商户不用传二级商户号。 ");
+        subOrders.add(subOrder);
+        order.setSubOrders(subOrders);
+
+        //获取对应的支付账户操作工具(可根据账户id)
+        ByteArrayOutputStream baos = new ByteArrayOutputStream();
+        ImageIO.write(service.genQrPay(order), "JPEG", baos);
+        return baos.toByteArray();
+    }
+
+    /**
+     * 获取二维码地址
+     * 二维码支付
+     *
+     * @return 二维码图像
+     * @throws IOException IOException
+     */
+    @RequestMapping(value = "getQrPay.json")
+    public String getQrPay() {
+        CombinePayOrder order = new CombinePayOrder();
+        order.setTransactionType(WxTransactionType.COMBINE_NATIVE);
+        order.setCombineOutTradeNo("合单商户订单号");
+        //子单信息,最多50单.
+        List subOrders = new ArrayList<>();
+        SubOrder subOrder = new SubOrder();
+        subOrder.setMchid("子单商户号");
+        subOrder.setAttach("附加数据,在查询API和支付通知中原样返回,可作为自定义参数使用。1 ");
+        //"子单金额,单位为分。 "
+        subOrder.setAmount(new CombineAmount(115));
+        subOrder.setOutTradeNo("子单商户订单号 ");
+        subOrder.setDescription("商品描述");
+        subOrder.setSubMchid("二级商户商户号,由微信支付生成并下发。服务商子商户的商户号,被合单方。直连商户不用传二级商户号。 ");
+        subOrders.add(subOrder);
+        order.setSubOrders(subOrders);
+        //获取对应的支付账户操作工具(可根据账户id)
+        return service.getQrPay(order);
+    }
+
+
+    /**
+     * 支付回调地址
+     *
+     * @param request 请求
+     * @return 是否成功
+     * 

+ * 业务处理在对应的PayMessageHandler里面处理,在哪里设置PayMessageHandler,详情查看{@link com.egzosn.pay.common.api.PayService#setPayMessageHandler(com.egzosn.pay.common.api.PayMessageHandler)} + *

+ * 如果未设置 {@link com.egzosn.pay.common.api.PayMessageHandler} 那么会使用默认的 {@link com.egzosn.pay.common.api.DefaultPayMessageHandler} + * @throws IOException IOException + */ + @RequestMapping(value = "payBack.json") + public String payBack(HttpServletRequest request) { + //业务处理在对应的PayMessageHandler里面处理,在哪里设置PayMessageHandler,详情查看com.egzosn.pay.common.api.PayService.setPayMessageHandler() + return service.payBack(new HttpRequestNoticeParams(request)).toMessage(); + } + + + /** + * 查询 + * + * @param order 订单的请求体 + * @return 返回查询回来的结果集,支付方原值返回 + */ + @RequestMapping("query") + public Map query(QueryOrder order) { + return service.query(order.getTradeNo(), order.getOutTradeNo()); + } + + + /** + * 交易关闭接口 + * + * @return 返回支付方交易关闭后的结果 + */ + @RequestMapping("close") + public Map close() { + CombineCloseOrder order = new CombineCloseOrder(); + order.setOutTradeNo("合单商户订单号"); + //子单信息,最多50单. + List subOrders = new ArrayList<>(); + CombineSubOrder subOrder = new CombineSubOrder(); + subOrder.setMchid("子单商户号"); + subOrder.setOutTradeNo("子单商户订单号 "); + subOrder.setSubMchid("二级商户商户号,由微信支付生成并下发。服务商子商户的商户号,被合单方。直连商户不用传二级商户号。 "); + subOrders.add(subOrder); + order.setSubOrders(subOrders); + return service.close(order); + } + + /** + * 申请退款接口 + * + * @param order 订单的请求体 + * @return 返回支付方申请退款后的结果 + */ + @RequestMapping("refund") + public WxRefundResult refund(RefundOrder order) { + + return service.refund(order); + } + + /** + * 查询退款 + * + * @param order 订单的请求体 + * @return 返回支付方查询退款后的结果 + */ + @RequestMapping("refundquery") + public Map refundquery(RefundOrder order) { + return service.refundquery(order); + } + + /** + * 下载对账单 + * + * @param order 订单的请求体 + * @return 返回支付方下载对账单的结果 + */ + @RequestMapping("downloadbill") + public Object downloadBill(QueryOrder order) { + return service.downloadBill(order.getBillDate(), order.getBillType()); + } + + +} diff --git a/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/WxV3PayController.java b/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/WxV3PayController.java index 8adc70e..5554c00 100644 --- a/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/WxV3PayController.java +++ b/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/WxV3PayController.java @@ -19,19 +19,16 @@ import org.springframework.web.bind.annotation.RestController; import com.egzosn.pay.common.bean.CertStoreType; import com.egzosn.pay.common.bean.PayOrder; import com.egzosn.pay.common.bean.RefundOrder; -import com.egzosn.pay.common.bean.TransferOrder; import com.egzosn.pay.demo.request.QueryOrder; import com.egzosn.pay.demo.service.handler.WxV3PayMessageHandler; import com.egzosn.pay.web.support.HttpRequestNoticeParams; -import com.egzosn.pay.wx.bean.WxBank; -import com.egzosn.pay.wx.bean.WxTransferType; import com.egzosn.pay.wx.v3.api.WxPayConfigStorage; import com.egzosn.pay.wx.v3.api.WxPayService; import com.egzosn.pay.wx.v3.bean.WxTransactionType; import com.egzosn.pay.wx.v3.bean.response.WxRefundResult; /** - * 发起支付入口 + * 微信V3发起支付入口 * * @author egan * email egzosn@gmail.com @@ -44,13 +41,8 @@ public class WxV3PayController { private WxPayService service = null; - - - - @PostConstruct //没有证书的情况下注释掉,避免启动报错 public void init() { - System.out.println("v3 init"); WxPayConfigStorage wxPayConfigStorage = new WxPayConfigStorage(); wxPayConfigStorage.setAppId("wxc7b993ff15a9f27c"); wxPayConfigStorage.setMchId("1602947766"); @@ -161,8 +153,6 @@ public class WxV3PayController { } - - /** * 支付回调地址 * @@ -175,7 +165,7 @@ public class WxV3PayController { * @throws IOException IOException */ @RequestMapping(value = "payBack.json") - public String payBack(HttpServletRequest request) { + public String payBack(HttpServletRequest request) { //业务处理在对应的PayMessageHandler里面处理,在哪里设置PayMessageHandler,详情查看com.egzosn.pay.common.api.PayService.setPayMessageHandler() return service.payBack(new HttpRequestNoticeParams(request)).toMessage(); } @@ -239,68 +229,4 @@ public class WxV3PayController { } - /** - * 转账到余额 - * - * @param order 转账订单 - * @return 对应的转账结果 - */ - @RequestMapping("transfer") - public Map transfer(TransferOrder order) { - order.setOutNo("partner_trade_no 商户转账订单号"); - order.setPayeeAccount("用户openid"); - order.setPayeeName("收款用户姓名, 非必填,如果填写将强制验证收款人姓名"); - order.setRemark("转账备注, 非必填"); - order.setAmount(new BigDecimal(10)); - - //转账到余额,这里默认值是转账到银行卡 - order.setTransferType(WxTransferType.TRANSFERS); - - return service.transfer(order); - } - - - /** - * 转账到银行卡 - * - * @param order 转账订单 - * @return 对应的转账结果 - */ - @RequestMapping("transferPayBank") - public Map transferPayBank(TransferOrder order) { - order.setOutNo("partner_trade_no 商户转账订单号"); - //采用标准RSA算法,公钥由微信侧提供,将公钥信息配置在PayConfigStorage#setKeyPublic(String) - order.setPayeeAccount("enc_bank_no 收款方银行卡号"); - order.setPayeeName("收款方用户名"); - order.setBank(WxBank.ABC); - order.setRemark("转账备注, 非必填"); - order.setAmount(new BigDecimal(10)); - //转账到银行卡,这里默认值是转账到银行卡 - order.setTransferType(WxTransferType.PAY_BANK); - - return service.transfer(order); - } - - /** - * 转账查询 - * - * @param outNo 商户转账订单号 - * @param wxTransferType 微信转账类型, - * .....这里没办法了只能这样写(┬_┬),请见谅 - * {@link WxTransferType#QUERY_BANK} - * {@link WxTransferType#GETTRANSFERINFO} - * - *

- * 企业付款到零钱 - * 商户企业付款到银行卡 - *

- * @return 对应的转账订单 - */ - @RequestMapping("transferQuery") - public Map transferQuery(String outNo, String wxTransferType) { - //默认查询银行卡的记录 com.egzosn.pay.wx.v3.bean.WxTransferType#QUERY_BANK - return service.transferQuery(outNo, wxTransferType); - } - - } diff --git a/pay-java-demo/src/main/java/com/egzosn/pay/demo/service/handler/WxV3CombinePayMessageHandler.java b/pay-java-demo/src/main/java/com/egzosn/pay/demo/service/handler/WxV3CombinePayMessageHandler.java new file mode 100644 index 0000000..cdbcd0c --- /dev/null +++ b/pay-java-demo/src/main/java/com/egzosn/pay/demo/service/handler/WxV3CombinePayMessageHandler.java @@ -0,0 +1,29 @@ +package com.egzosn.pay.demo.service.handler; + +import java.util.Map; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.alibaba.fastjson.JSON; +import com.egzosn.pay.common.api.DefaultPayMessageHandler; +import com.egzosn.pay.common.api.PayMessageHandler; +import com.egzosn.pay.common.api.PayService; +import com.egzosn.pay.common.bean.PayOutMessage; +import com.egzosn.pay.common.exception.PayErrorException; +import com.egzosn.pay.wx.v3.bean.combine.CombinePayMessage; + +/** + * 微信合单支付回调处理器 + * Created by ZaoSheng on 2016/6/1. + */ +public class WxV3CombinePayMessageHandler implements PayMessageHandler { + + private final Logger LOG = LoggerFactory.getLogger(DefaultPayMessageHandler.class); + + @Override + public PayOutMessage handle(CombinePayMessage payMessage, Map context, PayService payService) throws PayErrorException { + LOG.info("回调支付消息处理器,回调消息:{}", JSON.toJSONString(payMessage)); + return payService.successPayOutMessage(payMessage); + } +} diff --git a/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/combine/CombineAmount.java b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/combine/CombineAmount.java index f3d812b..8a677f6 100644 --- a/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/combine/CombineAmount.java +++ b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/combine/CombineAmount.java @@ -21,6 +21,12 @@ public class CombineAmount extends Amount { @JSONField(name = "total_amount") private Integer totalAmount; + public CombineAmount() { + } + + public CombineAmount(Integer totalAmount) { + this.totalAmount = totalAmount; + } public Integer getTotalAmount() { return totalAmount; diff --git a/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/combine/CombineSubOrder.java b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/combine/CombineSubOrder.java index ee66a24..3fd4f14 100644 --- a/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/combine/CombineSubOrder.java +++ b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/combine/CombineSubOrder.java @@ -16,6 +16,15 @@ public class CombineSubOrder { * 子单发起方商户号,必填,必须与发起方appid有绑定关系。 */ private String mchid; + /** + * 二级商户商户号,由微信支付生成并下发。 + *

+ * 服务商子商户的商户号,被合单方。 + *

+ * 直连商户不用传二级商户号。 + */ + @JSONField(name = "sub_mchid") + private String subMchid; /** * 商户系统内部订单号,要求32个字符内,只能是数字、大小写字母_-|*@ ,且在同一个商户号下唯一。 @@ -32,6 +41,14 @@ public class CombineSubOrder { this.mchid = mchid; } + public String getSubMchid() { + return subMchid; + } + + public void setSubMchid(String subMchid) { + this.subMchid = subMchid; + } + public String getOutTradeNo() { return outTradeNo; } diff --git a/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/order/Amount.java b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/order/Amount.java index 8574451..ae15d18 100644 --- a/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/order/Amount.java +++ b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/order/Amount.java @@ -23,7 +23,7 @@ public class Amount { * 货币类型 CNY:人民币,境内商户号仅支持人民币。 * {@link com.egzosn.pay.common.bean.CurType} */ - private String currency; + private String currency = DefaultCurType.CNY.getType(); public Amount() { } diff --git a/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/order/H5Info.java b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/order/H5Info.java index 03e5f71..5568533 100644 --- a/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/order/H5Info.java +++ b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/order/H5Info.java @@ -41,10 +41,15 @@ public class H5Info { @JSONField(name = "package_name") private String packageName; + + public H5Info() { this.type = "Wap"; } + public H5Info(String type) { + this.type = type; + } public H5Info(String appName, String appUrl) { this(); diff --git a/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/order/SubOrder.java b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/order/SubOrder.java index 0b0010d..5a579d8 100644 --- a/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/order/SubOrder.java +++ b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/order/SubOrder.java @@ -32,15 +32,7 @@ public class SubOrder extends CombineSubOrder { private String description; - /** - * 二级商户商户号,由微信支付生成并下发。 - *

- * 服务商子商户的商户号,被合单方。 - *

- * 直连商户不用传二级商户号。 - */ - @JSONField(name = "sub_mchid") - private String subMchid; + /** * 结算信息,选填 @@ -113,13 +105,6 @@ public class SubOrder extends CombineSubOrder { } - public String getSubMchid() { - return subMchid; - } - - public void setSubMchid(String subMchid) { - this.subMchid = subMchid; - } public SettleInfo getSettleInfo() { return settleInfo; -- Gitee From fa4d2d90078f07e9f44b2ea15b38bd7f3a355ef2 Mon Sep 17 00:00:00 2001 From: egzosn Date: Wed, 6 Oct 2021 16:37:28 +0800 Subject: [PATCH 228/299] =?UTF-8?q?=E5=BE=AE=E4=BF=A1V3=E6=94=AF=E4=BB=98?= =?UTF-8?q?=E4=B8=8E=E5=BE=AE=E4=BF=A1V3=E5=90=88=E5=8D=95=E6=94=AF?= =?UTF-8?q?=E4=BB=98=E6=A1=88=E4=BE=8B=E5=AE=9E=E7=8E=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../pay/demo/controller/WxV3CombinePayController.java | 8 ++++---- .../com/egzosn/pay/demo/controller/WxV3PayController.java | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/WxV3CombinePayController.java b/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/WxV3CombinePayController.java index 0bfd3ce..d9fcbc4 100644 --- a/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/WxV3CombinePayController.java +++ b/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/WxV3CombinePayController.java @@ -48,17 +48,17 @@ public class WxV3CombinePayController { @PostConstruct //没有证书的情况下注释掉,避免启动报错 public void init() { WxPayConfigStorage wxPayConfigStorage = new WxPayConfigStorage(); - wxPayConfigStorage.setAppId("wxc7b993ff15a9f27c"); - wxPayConfigStorage.setMchId("1602947766"); + wxPayConfigStorage.setAppId("wxc7b993ff15a9f26c"); + wxPayConfigStorage.setMchId("1602947765"); //V3密钥 https://pay.weixin.qq.com/wiki/doc/apiv3/wechatpay/wechatpay3_2.shtml - wxPayConfigStorage.setV3ApiKey("9bd8f0e7af4841299d782406b7774f56"); + wxPayConfigStorage.setV3ApiKey("9bd8f0e7af4841299d782406b7774f57"); wxPayConfigStorage.setNotifyUrl("http://sailinmu.iok.la/wxV3/payBack.json"); wxPayConfigStorage.setReturnUrl("http://sailinmu.iok.la/wxV3/payBack.json"); wxPayConfigStorage.setInputCharset("utf-8"); //使用证书时设置为true wxPayConfigStorage.setCertSign(true); //商户API证书 https://pay.weixin.qq.com/wiki/doc/apiv3/wechatpay/wechatpay3_1.shtml - wxPayConfigStorage.setApiClientKeyP12("E:\\Documents\\gitee\\支付\\yifenli_mall.p12"); + wxPayConfigStorage.setApiClientKeyP12("yifenli_mall.p12"); wxPayConfigStorage.setCertStoreType(CertStoreType.PATH); service = new WxCombinePayService(wxPayConfigStorage); //设置回调消息处理 diff --git a/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/WxV3PayController.java b/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/WxV3PayController.java index 5554c00..fbc4f97 100644 --- a/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/WxV3PayController.java +++ b/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/WxV3PayController.java @@ -44,17 +44,17 @@ public class WxV3PayController { @PostConstruct //没有证书的情况下注释掉,避免启动报错 public void init() { WxPayConfigStorage wxPayConfigStorage = new WxPayConfigStorage(); - wxPayConfigStorage.setAppId("wxc7b993ff15a9f27c"); - wxPayConfigStorage.setMchId("1602947766"); + wxPayConfigStorage.setAppId("wxc7b993ff15a9f26c"); + wxPayConfigStorage.setMchId("1602947765"); //V3密钥 https://pay.weixin.qq.com/wiki/doc/apiv3/wechatpay/wechatpay3_2.shtml - wxPayConfigStorage.setV3ApiKey("9bd8f0e7af4841299d782406b7774f56"); + wxPayConfigStorage.setV3ApiKey("9bd8f0e7af4841299d782406b7774f57"); wxPayConfigStorage.setNotifyUrl("http://sailinmu.iok.la/wxV3/payBack.json"); wxPayConfigStorage.setReturnUrl("http://sailinmu.iok.la/wxV3/payBack.json"); wxPayConfigStorage.setInputCharset("utf-8"); //使用证书时设置为true wxPayConfigStorage.setCertSign(true); //商户API证书 https://pay.weixin.qq.com/wiki/doc/apiv3/wechatpay/wechatpay3_1.shtml - wxPayConfigStorage.setApiClientKeyP12("E:\\Documents\\gitee\\支付\\yifenli_mall.p12"); + wxPayConfigStorage.setApiClientKeyP12("yifenli_mall.p12"); wxPayConfigStorage.setCertStoreType(CertStoreType.PATH); service = new WxPayService(wxPayConfigStorage); //设置回调消息处理 -- Gitee From 5d47e3e0fabe38458bb0c2aa8b020f1679048ad4 Mon Sep 17 00:00:00 2001 From: egzosn Date: Wed, 6 Oct 2021 16:54:01 +0800 Subject: [PATCH 229/299] 1.8 --- pay-java-demo/pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pay-java-demo/pom.xml b/pay-java-demo/pom.xml index 7cf711f..a95cbba 100644 --- a/pay-java-demo/pom.xml +++ b/pay-java-demo/pom.xml @@ -103,8 +103,8 @@ org.apache.maven.plugins maven-compiler-plugin - 1.7 - 1.7 + 1.8 + 1.8 UTF-8 -- Gitee From 9c02712ae9ea8fc0dd605d962303485ee3af250d Mon Sep 17 00:00:00 2001 From: egzosn Date: Wed, 6 Oct 2021 17:14:59 +0800 Subject: [PATCH 230/299] 2.14.2 --- README.md | 2 +- pay-java-ali/pom.xml | 2 +- pay-java-baidu/pom.xml | 2 +- pay-java-common/pom.xml | 3 +-- .../com/egzosn/pay/common/bean/CloseOrder.java | 6 ++++-- pay-java-demo/pom.xml | 2 +- pay-java-fuiou/pom.xml | 2 +- pay-java-payoneer/pom.xml | 2 +- pay-java-paypal/pom.xml | 2 +- pay-java-union/pom.xml | 2 +- pay-java-web-support/pom.xml | 2 +- pay-java-wx-youdian/pom.xml | 2 +- pay-java-wx/pom.xml | 2 +- .../wx/v3/api/DefaultWxPayAssistService.java | 2 +- .../pay/wx/v3/api/WxCombinePayService.java | 1 - .../pay/wx/v3/api/WxParameterStructure.java | 3 --- .../pay/wx/v3/api/WxPayAssistService.java | 1 + .../wx/v3/bean/combine/CombineCloseOrder.java | 6 ++++-- .../pay/wx/v3/bean/response/WxPayMessage.java | 2 +- .../bean/response/order/PromotionDetail.java | 6 ++++-- .../pay/wx/v3/utils/AntCertificationUtil.java | 18 ++++++++++-------- pay-java-yiji/pom.xml | 2 +- pom.xml | 10 +++++----- 23 files changed, 43 insertions(+), 39 deletions(-) diff --git a/README.md b/README.md index bad91f0..ff69992 100644 --- a/README.md +++ b/README.md @@ -21,7 +21,7 @@ com.egzosn {module-name} - 2.14.1-b + 2.14.2 ``` diff --git a/pay-java-ali/pom.xml b/pay-java-ali/pom.xml index 7e4683a..7d743ce 100644 --- a/pay-java-ali/pom.xml +++ b/pay-java-ali/pom.xml @@ -5,7 +5,7 @@ pay-java-parent com.egzosn - 2.14.2-SNAPSHOT + 2.14.2 4.0.0 pay-java-ali diff --git a/pay-java-baidu/pom.xml b/pay-java-baidu/pom.xml index 8861985..7fbb57d 100644 --- a/pay-java-baidu/pom.xml +++ b/pay-java-baidu/pom.xml @@ -5,7 +5,7 @@ pay-java-parent com.egzosn - 2.14.2-SNAPSHOT + 2.14.2 4.0.0 pay-java-baidu diff --git a/pay-java-common/pom.xml b/pay-java-common/pom.xml index fb24df8..f19fd5b 100644 --- a/pay-java-common/pom.xml +++ b/pay-java-common/pom.xml @@ -5,7 +5,7 @@ pay-java-parent com.egzosn - 2.14.2-SNAPSHOT + 2.14.2 4.0.0 jar @@ -30,7 +30,6 @@ org.slf4j slf4j-api - 1.7.30 diff --git a/pay-java-common/src/main/java/com/egzosn/pay/common/bean/CloseOrder.java b/pay-java-common/src/main/java/com/egzosn/pay/common/bean/CloseOrder.java index b7e975a..4d50e86 100644 --- a/pay-java-common/src/main/java/com/egzosn/pay/common/bean/CloseOrder.java +++ b/pay-java-common/src/main/java/com/egzosn/pay/common/bean/CloseOrder.java @@ -5,8 +5,10 @@ import java.util.Map; /** * 关闭订单 * @author Egan - * @email egan@egzosn.com - * @date 2021/10/6 + *

+ * email egan@egzosn.com
+ * date 2021/10/6
+ * 
*/ public class CloseOrder implements Order { diff --git a/pay-java-demo/pom.xml b/pay-java-demo/pom.xml index a95cbba..125b476 100644 --- a/pay-java-demo/pom.xml +++ b/pay-java-demo/pom.xml @@ -5,7 +5,7 @@ pay-java-parent com.egzosn - 2.14.2-SNAPSHOT + 2.14.2 4.0.0 war diff --git a/pay-java-fuiou/pom.xml b/pay-java-fuiou/pom.xml index 4ba3acd..8685c30 100644 --- a/pay-java-fuiou/pom.xml +++ b/pay-java-fuiou/pom.xml @@ -5,7 +5,7 @@ pay-java-parent com.egzosn - 2.14.2-SNAPSHOT + 2.14.2 4.0.0 pay-java-fuiou diff --git a/pay-java-payoneer/pom.xml b/pay-java-payoneer/pom.xml index a8e8334..c920ab0 100644 --- a/pay-java-payoneer/pom.xml +++ b/pay-java-payoneer/pom.xml @@ -5,7 +5,7 @@ pay-java-parent com.egzosn - 2.14.2-SNAPSHOT + 2.14.2 4.0.0 pay-java-payoneer diff --git a/pay-java-paypal/pom.xml b/pay-java-paypal/pom.xml index ce4d4a7..4e0f8bd 100644 --- a/pay-java-paypal/pom.xml +++ b/pay-java-paypal/pom.xml @@ -5,7 +5,7 @@ pay-java-parent com.egzosn - 2.14.2-SNAPSHOT + 2.14.2 4.0.0 diff --git a/pay-java-union/pom.xml b/pay-java-union/pom.xml index c1ca7f9..2a00ed4 100644 --- a/pay-java-union/pom.xml +++ b/pay-java-union/pom.xml @@ -5,7 +5,7 @@ pay-java-parent com.egzosn - 2.14.2-SNAPSHOT + 2.14.2 4.0.0 diff --git a/pay-java-web-support/pom.xml b/pay-java-web-support/pom.xml index 7389396..539f476 100644 --- a/pay-java-web-support/pom.xml +++ b/pay-java-web-support/pom.xml @@ -5,7 +5,7 @@ pay-java-parent com.egzosn - 2.14.2-SNAPSHOT + 2.14.2 4.0.0 jar diff --git a/pay-java-wx-youdian/pom.xml b/pay-java-wx-youdian/pom.xml index e88c55c..8f3827a 100644 --- a/pay-java-wx-youdian/pom.xml +++ b/pay-java-wx-youdian/pom.xml @@ -5,7 +5,7 @@ pay-java-parent com.egzosn - 2.14.2-SNAPSHOT + 2.14.2 4.0.0 pay-java-wx-youdian diff --git a/pay-java-wx/pom.xml b/pay-java-wx/pom.xml index b0d3669..d9ad6ff 100644 --- a/pay-java-wx/pom.xml +++ b/pay-java-wx/pom.xml @@ -5,7 +5,7 @@ pay-java-parent com.egzosn - 2.14.2-SNAPSHOT + 2.14.2 4.0.0 pay-java-wx diff --git a/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/api/DefaultWxPayAssistService.java b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/api/DefaultWxPayAssistService.java index a6d0e1b..906a529 100644 --- a/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/api/DefaultWxPayAssistService.java +++ b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/api/DefaultWxPayAssistService.java @@ -47,7 +47,6 @@ import com.egzosn.pay.wx.v3.utils.WxConst; * date 2021/8/7 */ public class DefaultWxPayAssistService implements WxPayAssistService { - protected final Logger LOG = LoggerFactory.getLogger(getClass()); private WxPayConfigStorage payConfigStorage; private HttpRequestTemplate requestTemplate; @@ -118,6 +117,7 @@ public class DefaultWxPayAssistService implements WxPayAssistService { * 构建请求实体 * 这里也做签名处理 * + * @param url url * @param body 请求内容体 * @param method 请求方法 * @return 请求实体 diff --git a/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/api/WxCombinePayService.java b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/api/WxCombinePayService.java index 4c99699..0a19bf0 100644 --- a/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/api/WxCombinePayService.java +++ b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/api/WxCombinePayService.java @@ -69,7 +69,6 @@ public class WxCombinePayService extends WxPayService { * * @param parameters 订单参数 * @param order 订单信息 - * @return 订单参数 */ public void initNotifyUrl(Map parameters, Order order) { OrderParaStructure.loadParameters(parameters, WxConst.NOTIFY_URL, payConfigStorage.getNotifyUrl()); diff --git a/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/api/WxParameterStructure.java b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/api/WxParameterStructure.java index a0bba61..b949b8b 100644 --- a/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/api/WxParameterStructure.java +++ b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/api/WxParameterStructure.java @@ -52,7 +52,6 @@ public class WxParameterStructure { * * @param parameters 订单参数 * @param order 支付订单 - * @return 订单参数 */ public void loadSettleInfo(Map parameters, PayOrder order) { Object profitSharing = order.getAttr("profit_sharing"); @@ -74,7 +73,6 @@ public class WxParameterStructure { * * @param parameters 订单参数 * @param order 订单信息 - * @return 订单参数 */ public void initNotifyUrl(Map parameters, Order order) { OrderParaStructure.loadParameters(parameters, WxConst.NOTIFY_URL, payConfigStorage.getNotifyUrl()); @@ -95,7 +93,6 @@ public class WxParameterStructure { * 初始化商户相关信息 * * @param parameters 参数信息 - * @return 参数信息 */ public void initPartner(Map parameters) { if (null == parameters) { diff --git a/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/api/WxPayAssistService.java b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/api/WxPayAssistService.java index 8bf20e9..58bfe74 100644 --- a/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/api/WxPayAssistService.java +++ b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/api/WxPayAssistService.java @@ -53,6 +53,7 @@ public interface WxPayAssistService { * 构建请求实体 * 这里也做签名处理 * + * @param url url * @param body 请求内容体 * @param method 请求方法 * @return 请求实体 diff --git a/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/combine/CombineCloseOrder.java b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/combine/CombineCloseOrder.java index cef6267..dfe9c84 100644 --- a/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/combine/CombineCloseOrder.java +++ b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/combine/CombineCloseOrder.java @@ -8,8 +8,10 @@ import com.egzosn.pay.wx.v3.utils.WxConst; /** * 微信合单关闭订单 * @author Egan - * @email egan@egzosn.com - * @date 2021/10/6 + *
+ * email egan@egzosn.com
+ * date 2021/10/6
+ * 
*/ public class CombineCloseOrder extends CloseOrder { diff --git a/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/response/WxPayMessage.java b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/response/WxPayMessage.java index f02ffc4..e058663 100644 --- a/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/response/WxPayMessage.java +++ b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/response/WxPayMessage.java @@ -113,7 +113,7 @@ public class WxPayMessage extends PayMessage { * 退款状态,枚举值: * SUCCESS:退款成功 * CLOSE:退款关闭 - * ABNORMAL:退款异常,退款到银行发现用户的卡作废或者冻结了,导致原路退款银行卡失败,可前往【商户平台—>交易中心】,手动处理此笔退款 + * ABNORMAL: 退款异常,退款到银行发现用户的卡作废或者冻结了,导致原路退款银行卡失败,可前往【商户平台—交易中心】,手动处理此笔退款 * 示例值:SUCCESS */ @JSONField(name = "refund_status") diff --git a/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/response/order/PromotionDetail.java b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/response/order/PromotionDetail.java index 697355e..2d6e6e6 100644 --- a/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/response/order/PromotionDetail.java +++ b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/response/order/PromotionDetail.java @@ -7,8 +7,10 @@ import com.alibaba.fastjson.annotation.JSONField; /** * 优惠功能 * - * @author felord.cn - * @since 1.0.0.RELEASE + *
+ * email egan@egzosn.com
+ * date 2021/10/6
+ * 
*/ public class PromotionDetail { diff --git a/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/utils/AntCertificationUtil.java b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/utils/AntCertificationUtil.java index 1807787..2c4cedb 100644 --- a/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/utils/AntCertificationUtil.java +++ b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/utils/AntCertificationUtil.java @@ -67,9 +67,10 @@ public final class AntCertificationUtil { /** * 装载平台证书 - * @param serialNo 证书序列 + * + * @param serialNo 证书序列 * @param certificateStream 证书流 - * @return + * @return 平台证书 */ public static Certificate loadCertificate(String serialNo, InputStream certificateStream) { try { @@ -127,10 +128,12 @@ public final class AntCertificationUtil { /** * 解密响应体. * - * @param associatedData 相关数据 - * @param nonce 随机串 - * @param cipherText 需要解密的文本 - * @return the string + * @param associatedData 相关数据 + * @param nonce 随机串 + * @param cipherText 需要解密的文本 + * @param secretKey 密钥 + * @param characterEncoding 编码类型 + * @return 解密后的信息 */ public static String decryptToString(String associatedData, String nonce, String cipherText, String secretKey, String characterEncoding) { @@ -153,8 +156,7 @@ public final class AntCertificationUtil { * * @param message the message * @param certificate the certificate - * @return encrypt message - * @since 1.0.6.RELEASE + * @return 加密后的内容 */ public static String encryptToString(String message, Certificate certificate) { try { diff --git a/pay-java-yiji/pom.xml b/pay-java-yiji/pom.xml index 2458fcf..c8e4428 100644 --- a/pay-java-yiji/pom.xml +++ b/pay-java-yiji/pom.xml @@ -5,7 +5,7 @@ pay-java-parent com.egzosn - 2.14.2-SNAPSHOT + 2.14.2 4.0.0 diff --git a/pom.xml b/pom.xml index a1d07ce..9c6be50 100644 --- a/pom.xml +++ b/pom.xml @@ -7,7 +7,7 @@ com.egzosn pay-java-parent pom - 2.14.2-SNAPSHOT + 2.14.2 Pay Java - Parent Pay Java Parent @@ -70,7 +70,7 @@ - 2.14.2-SNAPSHOT + 2.14.2 4.5.4 1.2.17 1.2.73 @@ -110,9 +110,9 @@ - log4j - log4j - ${log4j.version} + org.slf4j + slf4j-api + 1.7.30 -- Gitee From 4d00aae8a914fe97dfbb13965ccdabf9e2096457 Mon Sep 17 00:00:00 2001 From: egzosn Date: Wed, 6 Oct 2021 17:51:57 +0800 Subject: [PATCH 231/299] =?UTF-8?q?2.14.3=E5=BC=80=E5=A7=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pay-java-ali/pom.xml | 2 +- .../main/java/com/egzosn/pay/ali/api/AliPayService.java | 7 +++++++ pay-java-baidu/pom.xml | 2 +- pay-java-common/pom.xml | 2 +- pay-java-demo/pom.xml | 2 +- pay-java-fuiou/pom.xml | 2 +- pay-java-payoneer/pom.xml | 2 +- pay-java-paypal/pom.xml | 2 +- pay-java-union/pom.xml | 2 +- pay-java-web-support/pom.xml | 2 +- pay-java-wx-youdian/pom.xml | 2 +- pay-java-wx/pom.xml | 2 +- pay-java-yiji/pom.xml | 2 +- pom.xml | 4 ++-- 14 files changed, 21 insertions(+), 14 deletions(-) diff --git a/pay-java-ali/pom.xml b/pay-java-ali/pom.xml index 7d743ce..aa08115 100644 --- a/pay-java-ali/pom.xml +++ b/pay-java-ali/pom.xml @@ -5,7 +5,7 @@ pay-java-parent com.egzosn - 2.14.2 + 2.14.3-SNAPSHOT 4.0.0 pay-java-ali 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 1b9d724..3112792 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 @@ -253,7 +253,12 @@ public class AliPayService extends BasePayService { break; case WAP: bizContent.put(PASSBACK_PARAMS, order.getAddition()); + //产品码。 + //商家和支付宝签约的产品码。 枚举值(点击查看签约情况): + //QUICK_WAP_WAY:无线快捷支付产品。 + //默认值为QUICK_WAP_PAY。 bizContent.put(PRODUCT_CODE, "QUICK_WAP_PAY"); + OrderParaStructure.loadParameters(bizContent, PRODUCT_CODE, order); setReturnUrl(orderInfo, order); break; case APP: @@ -274,6 +279,8 @@ public class AliPayService extends BasePayService { break; } + + setExpirationTime(bizContent, order); bizContent.putAll(order.getAttrs()); diff --git a/pay-java-baidu/pom.xml b/pay-java-baidu/pom.xml index 7fbb57d..e9f0b16 100644 --- a/pay-java-baidu/pom.xml +++ b/pay-java-baidu/pom.xml @@ -5,7 +5,7 @@ pay-java-parent com.egzosn - 2.14.2 + 2.14.3-SNAPSHOT 4.0.0 pay-java-baidu diff --git a/pay-java-common/pom.xml b/pay-java-common/pom.xml index f19fd5b..e20e284 100644 --- a/pay-java-common/pom.xml +++ b/pay-java-common/pom.xml @@ -5,7 +5,7 @@ pay-java-parent com.egzosn - 2.14.2 + 2.14.3-SNAPSHOT 4.0.0 jar diff --git a/pay-java-demo/pom.xml b/pay-java-demo/pom.xml index 125b476..30f7350 100644 --- a/pay-java-demo/pom.xml +++ b/pay-java-demo/pom.xml @@ -5,7 +5,7 @@ pay-java-parent com.egzosn - 2.14.2 + 2.14.3-SNAPSHOT 4.0.0 war diff --git a/pay-java-fuiou/pom.xml b/pay-java-fuiou/pom.xml index 8685c30..9d1672c 100644 --- a/pay-java-fuiou/pom.xml +++ b/pay-java-fuiou/pom.xml @@ -5,7 +5,7 @@ pay-java-parent com.egzosn - 2.14.2 + 2.14.3-SNAPSHOT 4.0.0 pay-java-fuiou diff --git a/pay-java-payoneer/pom.xml b/pay-java-payoneer/pom.xml index c920ab0..45fdcfb 100644 --- a/pay-java-payoneer/pom.xml +++ b/pay-java-payoneer/pom.xml @@ -5,7 +5,7 @@ pay-java-parent com.egzosn - 2.14.2 + 2.14.3-SNAPSHOT 4.0.0 pay-java-payoneer diff --git a/pay-java-paypal/pom.xml b/pay-java-paypal/pom.xml index 4e0f8bd..c4a49d9 100644 --- a/pay-java-paypal/pom.xml +++ b/pay-java-paypal/pom.xml @@ -5,7 +5,7 @@ pay-java-parent com.egzosn - 2.14.2 + 2.14.3-SNAPSHOT 4.0.0 diff --git a/pay-java-union/pom.xml b/pay-java-union/pom.xml index 2a00ed4..64b8020 100644 --- a/pay-java-union/pom.xml +++ b/pay-java-union/pom.xml @@ -5,7 +5,7 @@ pay-java-parent com.egzosn - 2.14.2 + 2.14.3-SNAPSHOT 4.0.0 diff --git a/pay-java-web-support/pom.xml b/pay-java-web-support/pom.xml index 539f476..36f8dbb 100644 --- a/pay-java-web-support/pom.xml +++ b/pay-java-web-support/pom.xml @@ -5,7 +5,7 @@ pay-java-parent com.egzosn - 2.14.2 + 2.14.3-SNAPSHOT 4.0.0 jar diff --git a/pay-java-wx-youdian/pom.xml b/pay-java-wx-youdian/pom.xml index 8f3827a..ca68448 100644 --- a/pay-java-wx-youdian/pom.xml +++ b/pay-java-wx-youdian/pom.xml @@ -5,7 +5,7 @@ pay-java-parent com.egzosn - 2.14.2 + 2.14.3-SNAPSHOT 4.0.0 pay-java-wx-youdian diff --git a/pay-java-wx/pom.xml b/pay-java-wx/pom.xml index d9ad6ff..52dd4cf 100644 --- a/pay-java-wx/pom.xml +++ b/pay-java-wx/pom.xml @@ -5,7 +5,7 @@ pay-java-parent com.egzosn - 2.14.2 + 2.14.3-SNAPSHOT 4.0.0 pay-java-wx diff --git a/pay-java-yiji/pom.xml b/pay-java-yiji/pom.xml index c8e4428..b736fa5 100644 --- a/pay-java-yiji/pom.xml +++ b/pay-java-yiji/pom.xml @@ -5,7 +5,7 @@ pay-java-parent com.egzosn - 2.14.2 + 2.14.3-SNAPSHOT 4.0.0 diff --git a/pom.xml b/pom.xml index 9c6be50..2189a4a 100644 --- a/pom.xml +++ b/pom.xml @@ -7,7 +7,7 @@ com.egzosn pay-java-parent pom - 2.14.2 + 2.14.3-SNAPSHOT Pay Java - Parent Pay Java Parent @@ -70,7 +70,7 @@ - 2.14.2 + 2.14.3-SNAPSHOT 4.5.4 1.2.17 1.2.73 -- Gitee From 9db3f5a76d3a1bae3cd39005971be2d0e281ac7a Mon Sep 17 00:00:00 2001 From: egzosn Date: Wed, 6 Oct 2021 23:29:54 +0800 Subject: [PATCH 232/299] =?UTF-8?q?=E6=96=B0=E5=A2=9E=20=E8=BE=85=E5=8A=A9?= =?UTF-8?q?=E8=AE=A2=E5=8D=95=E5=AE=9E=E4=BD=93=E5=B9=B6=E5=AF=B9=E5=85=B6?= =?UTF-8?q?=E6=89=80=E6=9C=89=E6=94=AF=E4=BB=98=E5=AE=9E=E7=8E=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/egzosn/pay/ali/api/AliPayService.java | 23 +++- .../egzosn/pay/baidu/api/BaiduPayService.java | 17 ++- .../egzosn/pay/common/api/BasePayService.java | 28 +--- .../com/egzosn/pay/common/api/PayService.java | 42 ++++-- .../egzosn/pay/common/bean/AssistOrder.java | 126 ++++++++++++++++++ .../egzosn/pay/common/bean/CloseOrder.java | 84 +----------- .../com/egzosn/pay/common/bean/PayOrder.java | 121 ++--------------- .../egzosn/pay/common/bean/RefundOrder.java | 66 +-------- .../controller/WxV3CombinePayController.java | 1 - .../demo/controller/WxV3PayController.java | 1 - .../egzosn/pay/fuiou/api/FuiouPayService.java | 43 ++++-- .../pay/payoneer/api/PayoneerPayService.java | 20 ++- .../pay/paypal/api/PayPalPayService.java | 26 ++-- .../pay/paypal/v2/api/PayPalPayService.java | 51 +++---- .../egzosn/pay/union/api/UnionPayService.java | 27 +++- .../wx/youdian/api/WxYouDianPayService.java | 35 +++-- .../java/com/egzosn/pay/wx/api/WxConst.java | 2 +- .../com/egzosn/pay/wx/api/WxPayService.java | 35 +++-- .../egzosn/pay/yiji/api/YiJiPayService.java | 18 ++- 19 files changed, 372 insertions(+), 394 deletions(-) create mode 100644 pay-java-common/src/main/java/com/egzosn/pay/common/bean/AssistOrder.java 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 3112792..126b386 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 @@ -30,8 +30,9 @@ import com.egzosn.pay.ali.bean.AliTransferType; import com.egzosn.pay.ali.bean.CertEnvironment; import com.egzosn.pay.ali.bean.OrderSettle; import com.egzosn.pay.common.api.BasePayService; +import com.egzosn.pay.common.bean.AssistOrder; import com.egzosn.pay.common.bean.BillType; -import com.egzosn.pay.common.bean.CloseOrder; + import com.egzosn.pay.common.bean.MethodType; import com.egzosn.pay.common.bean.NoticeParams; import com.egzosn.pay.common.bean.Order; @@ -256,7 +257,7 @@ public class AliPayService extends BasePayService { //产品码。 //商家和支付宝签约的产品码。 枚举值(点击查看签约情况): //QUICK_WAP_WAY:无线快捷支付产品。 - //默认值为QUICK_WAP_PAY。 + //默认值为QUICK_WAP_PAY。 bizContent.put(PRODUCT_CODE, "QUICK_WAP_PAY"); OrderParaStructure.loadParameters(bizContent, PRODUCT_CODE, order); setReturnUrl(orderInfo, order); @@ -470,6 +471,18 @@ public class AliPayService extends BasePayService { } + /** + * 交易查询接口 + * + * @param assistOrder 查询条件 + * @return 返回查询回来的结果集,支付方原值返回 + */ + @Override + public Map query(AssistOrder assistOrder) { + return secondaryInterface(assistOrder.getTradeNo(), assistOrder.getOutTradeNo(), AliTransactionType.QUERY); + } + + /** * 交易关闭接口 @@ -486,12 +499,12 @@ public class AliPayService extends BasePayService { /** * 交易关闭接口 * - * @param closeOrder 关闭订单 + * @param assistOrder 关闭订单 * @return 返回支付方交易关闭后的结果 */ @Override - public Map close(CloseOrder closeOrder){ - return secondaryInterface(closeOrder.getTradeNo(), closeOrder.getOutTradeNo(), AliTransactionType.CLOSE); + public Map close(AssistOrder assistOrder){ + return secondaryInterface(assistOrder.getTradeNo(), assistOrder.getOutTradeNo(), AliTransactionType.CLOSE); } /** diff --git a/pay-java-baidu/src/main/java/com/egzosn/pay/baidu/api/BaiduPayService.java b/pay-java-baidu/src/main/java/com/egzosn/pay/baidu/api/BaiduPayService.java index 3b7f3af..e529eba 100644 --- a/pay-java-baidu/src/main/java/com/egzosn/pay/baidu/api/BaiduPayService.java +++ b/pay-java-baidu/src/main/java/com/egzosn/pay/baidu/api/BaiduPayService.java @@ -23,9 +23,9 @@ import com.egzosn.pay.baidu.bean.BaiduTransactionType; import com.egzosn.pay.baidu.bean.type.AuditStatus; import com.egzosn.pay.baidu.util.Asserts; import com.egzosn.pay.common.api.BasePayService; +import com.egzosn.pay.common.bean.AssistOrder; import com.egzosn.pay.common.bean.BaseRefundResult; import com.egzosn.pay.common.bean.BillType; -import com.egzosn.pay.common.bean.CloseOrder; import com.egzosn.pay.common.bean.CurType; import com.egzosn.pay.common.bean.MethodType; import com.egzosn.pay.common.bean.NoticeParams; @@ -428,6 +428,17 @@ public class BaiduPayService extends BasePayService { return secondaryInterface(tradeNo, outTradeNo, BaiduTransactionType.PAY_QUERY); } + /** + * 交易查询接口 + * + * @param assistOrder 查询条件 + * @return 返回查询回来的结果集,支付方原值返回 + */ + @Override + public Map query(AssistOrder assistOrder) { + return secondaryInterface(assistOrder.getTradeNo(), assistOrder.getOutTradeNo(), BaiduTransactionType.PAY_QUERY); + } + /** * 百度不支持该操作 * @@ -442,11 +453,11 @@ public class BaiduPayService extends BasePayService { /** * 交易关闭接口 * - * @param closeOrder 关闭订单 + * @param assistOrder 关闭订单 * @return 返回支付方交易关闭后的结果 */ @Override - public Map close(CloseOrder closeOrder){ + public Map close(AssistOrder assistOrder){ throw new UnsupportedOperationException("不支持该操作"); } diff --git a/pay-java-common/src/main/java/com/egzosn/pay/common/api/BasePayService.java b/pay-java-common/src/main/java/com/egzosn/pay/common/api/BasePayService.java index 4e6d1b5..bd8b5bc 100644 --- a/pay-java-common/src/main/java/com/egzosn/pay/common/api/BasePayService.java +++ b/pay-java-common/src/main/java/com/egzosn/pay/common/api/BasePayService.java @@ -257,6 +257,7 @@ public abstract class BasePayService implements Pay * @param 返回类型 * @return 返回支付方交易关闭后的结果 */ + @Deprecated @Override public T close(String tradeNo, String outTradeNo, Callback callback) { return callback.perform(close(tradeNo, outTradeNo)); @@ -431,7 +432,6 @@ public abstract class BasePayService implements Pay return payBack(new DefaultNoticeRequest(parameterMap, is)); } - /** * 回调处理 * @@ -448,31 +448,7 @@ public abstract class BasePayService implements Pay return getPayOutMessage("fail", "失败"); } PayMessage payMessage = this.createMessage(noticeParams.getBody()); - Map context = new HashMap(); - for (PayMessageInterceptor interceptor : interceptors) { - if (!interceptor.intercept(payMessage, context, this)) { - return successPayOutMessage(payMessage); - } - } - return getPayMessageHandler().handle(payMessage, context, this); - } - - /** - * 使用转换过的参数进行回调处理 - * - * @param data 转化后的参数Map - * @return 获得回调响应信息 - */ - @Override - public PayOutMessage payBack(Map data) { - if (LOG.isDebugEnabled()) { - LOG.debug("回调响应:" + JSON.toJSONString(data)); - } - if (!verify(data)) { - return getPayOutMessage("fail", "失败"); - } - PayMessage payMessage = this.createMessage(data); - Map context = new HashMap(); + Map context = new HashMap<>(); for (PayMessageInterceptor interceptor : interceptors) { if (!interceptor.intercept(payMessage, context, this)) { return successPayOutMessage(payMessage); diff --git a/pay-java-common/src/main/java/com/egzosn/pay/common/api/PayService.java b/pay-java-common/src/main/java/com/egzosn/pay/common/api/PayService.java index 6ee633d..cc1f708 100644 --- a/pay-java-common/src/main/java/com/egzosn/pay/common/api/PayService.java +++ b/pay-java-common/src/main/java/com/egzosn/pay/common/api/PayService.java @@ -5,8 +5,9 @@ import java.io.InputStream; import java.util.Date; import java.util.Map; +import com.egzosn.pay.common.bean.AssistOrder; import com.egzosn.pay.common.bean.BillType; -import com.egzosn.pay.common.bean.CloseOrder; + import com.egzosn.pay.common.bean.MethodType; import com.egzosn.pay.common.bean.NoticeParams; import com.egzosn.pay.common.bean.NoticeRequest; @@ -67,6 +68,7 @@ public interface PayService { * 已过时方法,详情{@link #verify(NoticeParams)} * @param params 回调回来的参数集 * @return 签名校验 true通过 + * @see #verify(NoticeParams) */ @Deprecated boolean verify(Map params); @@ -124,6 +126,7 @@ public interface PayService { * @param parameterMap 请求参数 * @param is 请求流 * @return 获得回调的请求参数 + * @see #getNoticeParams(NoticeRequest) */ @Deprecated Map getParameter2Map(Map parameterMap, InputStream is); @@ -199,7 +202,9 @@ public interface PayService { * @param tradeNo 支付平台订单号 * @param outTradeNo 商户单号 * @return 返回查询回来的结果集,支付方原值返回 + * @see #query(AssistOrder) */ + @Deprecated Map query(String tradeNo, String outTradeNo); /** @@ -211,25 +216,38 @@ public interface PayService { * @param 返回类型 * @return 返回查询回来的结果集 */ + @Deprecated T query(String tradeNo, String outTradeNo, Callback callback); + + /** + * 交易查询接口 + * + * @param assistOrder 查询条件 + * @return 返回查询回来的结果集,支付方原值返回 + */ + Map query(AssistOrder assistOrder); + + + + /** * 交易关闭接口 * * @param tradeNo 支付平台订单号 * @param outTradeNo 商户单号 * @return 返回支付方交易关闭后的结果 - * @see #close(CloseOrder) + * @see #close(AssistOrder) */ @Deprecated Map close(String tradeNo, String outTradeNo); /** * 交易关闭接口 * - * @param closeOrder 关闭订单 + * @param assistOrder 关闭订单 * @return 返回支付方交易关闭后的结果 */ - Map close(CloseOrder closeOrder); + Map close(AssistOrder assistOrder); /** @@ -241,8 +259,10 @@ public interface PayService { * @param 返回类型 * @return 返回支付方交易关闭后的结果 */ + @Deprecated T close(String tradeNo, String outTradeNo, Callback callback); + /** * 交易交易撤销 * @@ -261,6 +281,7 @@ public interface PayService { * @param 返回类型 * @return 返回支付方交易撤销后的结果 */ + @Deprecated T cancel(String tradeNo, String outTradeNo, Callback callback); @@ -280,6 +301,7 @@ public interface PayService { * @param 返回类型 * @return 返回支付方申请退款后的结果 */ + @Deprecated T refund(RefundOrder refundOrder, Callback callback); @@ -299,6 +321,7 @@ public interface PayService { * @param 返回类型 * @return 返回支付方查询退款后的结果 */ + @Deprecated T refundquery(RefundOrder refundOrder, Callback callback); /** @@ -337,6 +360,7 @@ public interface PayService { * @param 返回类型 * @return 对应的转账结果 */ + @Deprecated T transfer(TransferOrder order, Callback callback); @@ -358,6 +382,7 @@ public interface PayService { * @param 返回类型 * @return 对应的转账订单 */ + @Deprecated T transferQuery(String outNo, String tradeNo, Callback callback); /** @@ -377,13 +402,7 @@ public interface PayService { */ PayOutMessage payBack(NoticeRequest request); - /** - * 使用转换过的参数进行回调处理 - * - * @param data 转化后的参数Map - * @return 获得回调响应信息 - */ - PayOutMessage payBack(Map data); + /** * 设置支付消息处理器,这里用于处理具体的支付业务 @@ -431,6 +450,7 @@ public interface PayService { * @param 预订单类型 * @return 处理后订单信息 */ + @Deprecated Map preOrderHandler(Map orderInfo, O payOrder); } diff --git a/pay-java-common/src/main/java/com/egzosn/pay/common/bean/AssistOrder.java b/pay-java-common/src/main/java/com/egzosn/pay/common/bean/AssistOrder.java new file mode 100644 index 0000000..67f95f5 --- /dev/null +++ b/pay-java-common/src/main/java/com/egzosn/pay/common/bean/AssistOrder.java @@ -0,0 +1,126 @@ +package com.egzosn.pay.common.bean; + +import java.util.HashMap; +import java.util.Map; + +import com.alibaba.fastjson.annotation.JSONField; + +/** + * 辅助订单实体 + * + * @author egan + *
+ * email egan@egzosn.com
+ * date 2021/10/6
+ * 
+ */ +public class AssistOrder implements Order { + + + /** + * 支付平台订单号,交易号 + */ + private String tradeNo; + /** + * 商户订单号 + */ + private String outTradeNo; + /** + * 交易类型 + */ + private TransactionType transactionType; + /** + * 订单附加信息,可用于预设未提供的参数,这里会覆盖以上所有的订单信息, + */ + @JSONField(serialize = false) + private volatile Map attr; + + public AssistOrder() { + } + + public AssistOrder(String outTradeNo) { + this.outTradeNo = outTradeNo; + } + + public AssistOrder(String tradeNo, String outTradeNo) { + this.tradeNo = tradeNo; + this.outTradeNo = outTradeNo; + } + + public AssistOrder(String tradeNo, TransactionType transactionType) { + this.tradeNo = tradeNo; + this.transactionType = transactionType; + } + + /** + * 支付平台订单号,交易号 + * + * @return 支付平台订单号, 交易号 + */ + public String getTradeNo() { + return tradeNo; + } + + /** + * 支付平台订单号,交易号 + * + * @param tradeNo 支付平台订单号,交易号 + */ + public void setTradeNo(String tradeNo) { + this.tradeNo = tradeNo; + } + + /** + * 获取商户订单号 + * + * @return 商户订单号 + */ + public String getOutTradeNo() { + return outTradeNo; + } + + /** + * 设置商户订单号 + * + * @param outTradeNo 商户订单号 + */ + public void setOutTradeNo(String outTradeNo) { + this.outTradeNo = outTradeNo; + } + + public TransactionType getTransactionType() { + return transactionType; + } + + public void setTransactionType(TransactionType transactionType) { + this.transactionType = transactionType; + } + + + @Override + public Map getAttrs() { + if (null == attr) { + attr = new HashMap<>(); + } + return attr; + } + + @Override + public Object getAttr(String key) { + return getAttrs().get(key); + } + + + /** + * 添加订单信息 + * + * @param key key + * @param value 值 + */ + @Override + public void addAttr(String key, Object value) { + getAttrs().put(key, value); + } + + +} diff --git a/pay-java-common/src/main/java/com/egzosn/pay/common/bean/CloseOrder.java b/pay-java-common/src/main/java/com/egzosn/pay/common/bean/CloseOrder.java index 4d50e86..9ea3277 100644 --- a/pay-java-common/src/main/java/com/egzosn/pay/common/bean/CloseOrder.java +++ b/pay-java-common/src/main/java/com/egzosn/pay/common/bean/CloseOrder.java @@ -1,7 +1,5 @@ package com.egzosn.pay.common.bean; -import java.util.Map; - /** * 关闭订单 * @author Egan @@ -10,85 +8,7 @@ import java.util.Map; * date 2021/10/6 *
*/ -public class CloseOrder implements Order { - - /** - * 支付平台订单号,交易号 - */ - private String tradeNo; - /** - * 商户单号 - */ - private String outTradeNo; - - - /** - * 订单附加信息,可用于预设未提供的参数,这里会覆盖以上所有的订单信息, - */ - private Map attr; - - public CloseOrder() { - } - - public CloseOrder(String outTradeNo) { - this.outTradeNo = outTradeNo; - } - - - - /** - * 添加订单信息 - * - * @param key key - * @param value 值 - */ - @Override - public void addAttr(String key, Object value) { - attr.put(key, value); - } - - /** - * 获取属性 这里可用做覆盖已设置的信息属性,订单信息在签名前进行覆盖。 - * - * @return 属性 - */ - @Override - public Map getAttrs() { - return attr; - } - - /** - * 获取属性 这里可用做覆盖已设置的订单信息属性,订单信息在签名前进行覆盖。 - * - * @param key 属性名 - * @return 属性 - */ - @Override - public Object getAttr(String key) { - return attr.get(key); - } - - public String getTradeNo() { - return tradeNo; - } - - public void setTradeNo(String tradeNo) { - this.tradeNo = tradeNo; - } - - public String getOutTradeNo() { - return outTradeNo; - } - - public void setOutTradeNo(String outTradeNo) { - this.outTradeNo = outTradeNo; - } - - public Map getAttr() { - return attr; - } +@Deprecated +public class CloseOrder extends AssistOrder { - public void setAttr(Map attr) { - this.attr = attr; - } } 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 1db5c6e..1a88e0f 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 @@ -1,11 +1,9 @@ package com.egzosn.pay.common.bean; -import com.egzosn.pay.common.util.str.StringUtils; - import java.math.BigDecimal; import java.util.Date; -import java.util.HashMap; -import java.util.Map; + +import com.egzosn.pay.common.util.str.StringUtils; /** * 支付订单信息 @@ -16,7 +14,7 @@ import java.util.Map; * date 2016/10/19 22:34 *
*/ -public class PayOrder implements Order { +public class PayOrder extends AssistOrder { /** * 商品名称 */ @@ -33,14 +31,7 @@ public class PayOrder implements Order { * 价格 */ private BigDecimal price; - /** - * 支付平台订单号,交易号 - */ - private String tradeNo; - /** - * 商户订单号 - */ - private String outTradeNo; + /** * 银行卡类型 */ @@ -63,12 +54,13 @@ public class PayOrder implements Order { * 微信专用,,,, * WAP支付链接 */ + @Deprecated private String wapUrl; /** * 微信专用,,,, * WAP支付网页名称 */ - + @Deprecated private String wapName; /** * 用户唯一标识 @@ -76,10 +68,7 @@ public class PayOrder implements Order { * 支付宝 buyer_id */ private String openid; - /** - * 交易类型 - */ - private TransactionType transactionType; + /** * 支付币种 */ @@ -89,11 +78,6 @@ public class PayOrder implements Order { */ private Date expirationTime; - /** - * 订单附加信息,可用于预设未提供的参数,这里会覆盖以上所有的订单信息, - */ - private volatile Map attr; - public PayOrder() { } @@ -107,8 +91,8 @@ public class PayOrder implements Order { this.subject = StringUtils.tryTrim(subject); this.body = StringUtils.tryTrim(body); this.price = price; - this.outTradeNo = StringUtils.tryTrim(outTradeNo); - this.transactionType = transactionType; + setOutTradeNo(StringUtils.tryTrim(outTradeNo)); + setTransactionType(transactionType); } @@ -152,49 +136,6 @@ public class PayOrder implements Order { this.price = price; } - /** - * 支付平台订单号,交易号 - * - * @return 支付平台订单号, 交易号 - */ - public String getTradeNo() { - return tradeNo; - } - - /** - * 支付平台订单号,交易号 - * - * @param tradeNo 支付平台订单号,交易号 - */ - public void setTradeNo(String tradeNo) { - this.tradeNo = tradeNo; - } - - /** - * 获取商户订单号 - * - * @return 商户订单号 - */ - public String getOutTradeNo() { - return outTradeNo; - } - - /** - * 设置商户订单号 - * - * @param outTradeNo 商户订单号 - */ - public void setOutTradeNo(String outTradeNo) { - this.outTradeNo = outTradeNo; - } - - public TransactionType getTransactionType() { - return transactionType; - } - - public void setTransactionType(TransactionType transactionType) { - this.transactionType = transactionType; - } public String getBankType() { return bankType; @@ -260,48 +201,4 @@ public class PayOrder implements Order { this.expirationTime = expirationTime; } - @Override - public Map getAttrs() { - if (null == attr){ - attr = new HashMap<>(); - } - return attr; - } - - @Override - public Object getAttr(String key) { - return getAttrs().get(key); - } - - - /** - * 添加订单信息 - * @param key key - * @param value 值 - */ - @Override - public void addAttr(String key, Object value) { - getAttrs().put(key, value); - } - - - - @Override - public String toString() { - return "PayOrder{" + - "subject='" + subject + '\'' + - ", body='" + body + '\'' + - ", price=" + price + - ", outTradeNo='" + outTradeNo + '\'' + - ", bankType='" + bankType + '\'' + - ", deviceInfo='" + deviceInfo + '\'' + - ", spbillCreateIp='" + spbillCreateIp + '\'' + - ", authCode='" + authCode + '\'' + - ", wapUrl='" + wapUrl + '\'' + - ", wapName='" + wapName + '\'' + - ", openid='" + openid + '\'' + - ", transactionType=" + transactionType + - ", curType=" + curType + - '}'; - } } diff --git a/pay-java-common/src/main/java/com/egzosn/pay/common/bean/RefundOrder.java b/pay-java-common/src/main/java/com/egzosn/pay/common/bean/RefundOrder.java index 30e5959..f3cb108 100644 --- a/pay-java-common/src/main/java/com/egzosn/pay/common/bean/RefundOrder.java +++ b/pay-java-common/src/main/java/com/egzosn/pay/common/bean/RefundOrder.java @@ -2,8 +2,6 @@ package com.egzosn.pay.common.bean; import java.math.BigDecimal; import java.util.Date; -import java.util.HashMap; -import java.util.Map; /** * 退款订单信息 @@ -14,19 +12,11 @@ import java.util.Map; * date 2018/1/15 21:40 *
*/ -public class RefundOrder implements Order { +public class RefundOrder extends AssistOrder { /** * 退款单号,每次进行退款的单号,此处唯一 */ private String refundNo; - /** - * 支付平台订单号,交易号 - */ - private String tradeNo; - /** - * 商户单号 - */ - private String outTradeNo; /** * 退款金额 */ @@ -54,10 +44,6 @@ public class RefundOrder implements Order { */ private String userId; - /** - * 订单附加信息,可用于预设未提供的参数,这里会覆盖以上所有的订单信息, - */ - private Map attr; public String getRefundNo() { return refundNo; @@ -67,22 +53,6 @@ public class RefundOrder implements Order { this.refundNo = refundNo; } - public String getTradeNo() { - return tradeNo; - } - - public void setTradeNo(String tradeNo) { - this.tradeNo = tradeNo; - } - - public String getOutTradeNo() { - return outTradeNo; - } - - public void setOutTradeNo(String outTradeNo) { - this.outTradeNo = outTradeNo; - } - public BigDecimal getRefundAmount() { return refundAmount; } @@ -136,47 +106,23 @@ public class RefundOrder implements Order { public RefundOrder(String refundNo, String tradeNo, BigDecimal refundAmount) { this.refundNo = refundNo; - this.tradeNo = tradeNo; + setTradeNo(tradeNo); this.refundAmount = refundAmount; } public RefundOrder(String tradeNo, String outTradeNo, BigDecimal refundAmount, BigDecimal totalAmount) { - this.tradeNo = tradeNo; - this.outTradeNo = outTradeNo; + setTradeNo(tradeNo); + setOutTradeNo(outTradeNo); this.refundAmount = refundAmount; this.totalAmount = totalAmount; } public RefundOrder(String refundNo, String tradeNo, String outTradeNo, BigDecimal refundAmount, BigDecimal totalAmount) { this.refundNo = refundNo; - this.tradeNo = tradeNo; - this.outTradeNo = outTradeNo; + setTradeNo(tradeNo); + setOutTradeNo(outTradeNo); this.refundAmount = refundAmount; this.totalAmount = totalAmount; } - @Override - public Map getAttrs() { - if (null == attr) { - attr = new HashMap<>(); - } - return attr; - } - - @Override - public Object getAttr(String key) { - return getAttrs().get(key); - } - - - /** - * 添加订单信息 - * - * @param key key - * @param value 值 - */ - @Override - public void addAttr(String key, Object value) { - getAttrs().put(key, value); - } } diff --git a/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/WxV3CombinePayController.java b/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/WxV3CombinePayController.java index d9fcbc4..6431bb2 100644 --- a/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/WxV3CombinePayController.java +++ b/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/WxV3CombinePayController.java @@ -30,7 +30,6 @@ import com.egzosn.pay.wx.v3.bean.combine.CombineSubOrder; import com.egzosn.pay.wx.v3.bean.order.H5Info; import com.egzosn.pay.wx.v3.bean.order.SceneInfo; import com.egzosn.pay.wx.v3.bean.order.SubOrder; -import com.egzosn.pay.wx.v3.bean.response.WxRefundResult; /** * 微信V3合单发起支付入口 diff --git a/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/WxV3PayController.java b/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/WxV3PayController.java index fbc4f97..f987656 100644 --- a/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/WxV3PayController.java +++ b/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/WxV3PayController.java @@ -25,7 +25,6 @@ import com.egzosn.pay.web.support.HttpRequestNoticeParams; import com.egzosn.pay.wx.v3.api.WxPayConfigStorage; import com.egzosn.pay.wx.v3.api.WxPayService; import com.egzosn.pay.wx.v3.bean.WxTransactionType; -import com.egzosn.pay.wx.v3.bean.response.WxRefundResult; /** * 微信V3发起支付入口 diff --git a/pay-java-fuiou/src/main/java/com/egzosn/pay/fuiou/api/FuiouPayService.java b/pay-java-fuiou/src/main/java/com/egzosn/pay/fuiou/api/FuiouPayService.java index 94f6dc8..d46bc98 100644 --- a/pay-java-fuiou/src/main/java/com/egzosn/pay/fuiou/api/FuiouPayService.java +++ b/pay-java-fuiou/src/main/java/com/egzosn/pay/fuiou/api/FuiouPayService.java @@ -1,6 +1,5 @@ package com.egzosn.pay.fuiou.api; -import java.io.InputStream; import java.util.Collections; import java.util.Date; import java.util.HashMap; @@ -9,10 +8,11 @@ import java.util.Map; import com.alibaba.fastjson.JSONObject; import com.egzosn.pay.common.api.BasePayService; +import com.egzosn.pay.common.bean.AssistOrder; import com.egzosn.pay.common.bean.BillType; -import com.egzosn.pay.common.bean.CloseOrder; import com.egzosn.pay.common.bean.MethodType; import com.egzosn.pay.common.bean.NoticeParams; +import com.egzosn.pay.common.bean.NoticeRequest; import com.egzosn.pay.common.bean.PayMessage; import com.egzosn.pay.common.bean.PayOrder; import com.egzosn.pay.common.bean.PayOutMessage; @@ -125,6 +125,7 @@ public class FuiouPayService extends BasePayService { return verify(new NoticeParams(params)); } + /** * 回调校验 * @@ -143,10 +144,11 @@ public class FuiouPayService extends BasePayService { return (signVerify(params, (String) params.remove("md5")) && verifySource((String) params.get("order_id"))); } catch (PayErrorException e) { - LOG.error("", e); + LOG.error("", e); } return false; } + /** * 回调签名校验 * @@ -254,15 +256,16 @@ public class FuiouPayService extends BasePayService { return SignUtils.valueOf(payConfigStorage.getSignType().toUpperCase()).createSign(content, "|" + payConfigStorage.getKeyPrivate(), characterEncoding); } + /** * 将请求参数或者请求流转化为 Map * - * @param parameterMap 请求参数 - * @param is 请求流 - * @return 返回参数集合 + * @param request 通知请求 + * @return 获得回调的请求参数 */ @Override - public Map getParameter2Map(Map parameterMap, InputStream is) { + public NoticeParams getNoticeParams(NoticeRequest request) { + Map parameterMap = request.getParameterMap(); Map params = conversion(parameterMap, new LinkedHashMap(), "mchnt_cd"); conversion(parameterMap, params, "order_id"); conversion(parameterMap, params, "order_date"); @@ -273,7 +276,7 @@ public class FuiouPayService extends BasePayService { conversion(parameterMap, params, "resv1"); conversion(parameterMap, params, "fy_ssn"); conversion(parameterMap, params, "md5"); - return params; + return new NoticeParams(params); } /** @@ -393,12 +396,23 @@ public class FuiouPayService extends BasePayService { @Override public Map query(String tradeNo, String outTradeNo) { + return query(new AssistOrder(tradeNo, outTradeNo)); + } + + /** + * 交易查询接口 + * + * @param assistOrder 查询条件 + * @return 返回查询回来的结果集,支付方原值返回 + */ + @Override + public Map query(AssistOrder assistOrder) { + LinkedHashMap params = new LinkedHashMap<>(); params.put("mchnt_cd", payConfigStorage.getPid()); - params.put("order_id", outTradeNo); + params.put("order_id", assistOrder.getOutTradeNo()); params.put("md5", createSign(SignTextUtils.parameters2Md5Str(params, "|"), payConfigStorage.getInputCharset())); - JSONObject resultJson = getHttpRequestTemplate().postForObject(getReqUrl() + URL_FuiouSmpAQueryGate + "?" + UriVariables.getMapToParameters(params), null, JSONObject.class); - return resultJson; + return getHttpRequestTemplate().postForObject(getReqUrl() + URL_FuiouSmpAQueryGate + "?" + UriVariables.getMapToParameters(params), null, JSONObject.class); } @@ -411,16 +425,17 @@ public class FuiouPayService extends BasePayService { */ @Override public Map close(String tradeNo, String outTradeNo) { - return Collections.EMPTY_MAP; + throw new UnsupportedOperationException("不支持该操作"); } + /** * 交易关闭接口 * - * @param closeOrder 关闭订单 + * @param assistOrder 关闭订单 * @return 返回支付方交易关闭后的结果 */ @Override - public Map close(CloseOrder closeOrder){ + public Map close(AssistOrder assistOrder) { throw new UnsupportedOperationException("不支持该操作"); } diff --git a/pay-java-payoneer/src/main/java/com/egzosn/pay/payoneer/api/PayoneerPayService.java b/pay-java-payoneer/src/main/java/com/egzosn/pay/payoneer/api/PayoneerPayService.java index d1572b3..965b67e 100644 --- a/pay-java-payoneer/src/main/java/com/egzosn/pay/payoneer/api/PayoneerPayService.java +++ b/pay-java-payoneer/src/main/java/com/egzosn/pay/payoneer/api/PayoneerPayService.java @@ -15,9 +15,10 @@ import org.apache.http.message.BasicHeader; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONObject; import com.egzosn.pay.common.api.BasePayService; +import com.egzosn.pay.common.bean.AssistOrder; import com.egzosn.pay.common.bean.BaseRefundResult; import com.egzosn.pay.common.bean.BillType; -import com.egzosn.pay.common.bean.CloseOrder; + import com.egzosn.pay.common.bean.CurType; import com.egzosn.pay.common.bean.DefaultCurType; import com.egzosn.pay.common.bean.MethodType; @@ -294,6 +295,17 @@ public class PayoneerPayService extends BasePayService im return secondaryInterface(tradeNo, outTradeNo, PayoneerTransactionType.CHARGE_STATUS); } + /** + * 交易查询接口 + * + * @param assistOrder 查询条件 + * @return 返回查询回来的结果集,支付方原值返回 + */ + @Override + public Map query(AssistOrder assistOrder) { + return secondaryInterface(assistOrder.getTradeNo(), assistOrder.getOutTradeNo(), PayoneerTransactionType.CHARGE_STATUS); + } + /** * 交易关闭接口 @@ -309,12 +321,12 @@ public class PayoneerPayService extends BasePayService im /** * 交易关闭接口 * - * @param closeOrder 关闭订单 + * @param assistOrder 关闭订单 * @return 返回支付方交易关闭后的结果 */ @Override - public Map close(CloseOrder closeOrder){ - return secondaryInterface(closeOrder.getTradeNo(), closeOrder.getOutTradeNo(), PayoneerTransactionType.CHARGE_CANCEL); + public Map close(AssistOrder assistOrder){ + return secondaryInterface(assistOrder.getTradeNo(), assistOrder.getOutTradeNo(), PayoneerTransactionType.CHARGE_CANCEL); } /** * 交易交易撤销 diff --git a/pay-java-paypal/src/main/java/com/egzosn/pay/paypal/api/PayPalPayService.java b/pay-java-paypal/src/main/java/com/egzosn/pay/paypal/api/PayPalPayService.java index 6d44c1c..9d26cf5 100644 --- a/pay-java-paypal/src/main/java/com/egzosn/pay/paypal/api/PayPalPayService.java +++ b/pay-java-paypal/src/main/java/com/egzosn/pay/paypal/api/PayPalPayService.java @@ -19,9 +19,9 @@ import org.apache.http.message.BasicHeader; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONObject; import com.egzosn.pay.common.api.BasePayService; +import com.egzosn.pay.common.bean.AssistOrder; import com.egzosn.pay.common.bean.BaseRefundResult; import com.egzosn.pay.common.bean.BillType; -import com.egzosn.pay.common.bean.CloseOrder; import com.egzosn.pay.common.bean.CurType; import com.egzosn.pay.common.bean.DefaultCurType; import com.egzosn.pay.common.bean.MethodType; @@ -263,8 +263,19 @@ public class PayPalPayService extends BasePayService { */ @Override public Map query(String tradeNo, String outTradeNo) { - JSONObject resp = getHttpRequestTemplate().getForObject(getReqUrl(PayPalTransactionType.ORDERS), authHeader(), JSONObject.class, tradeNo); - return resp; + + return query(new AssistOrder(tradeNo, outTradeNo)); + } + + /** + * 交易查询接口 + * + * @param assistOrder 查询条件 + * @return 返回查询回来的结果集,支付方原值返回 + */ + @Override + public Map query(AssistOrder assistOrder) { + return getHttpRequestTemplate().getForObject(getReqUrl(PayPalTransactionType.ORDERS), authHeader(), JSONObject.class, assistOrder.getTradeNo()); } @Override @@ -275,13 +286,14 @@ public class PayPalPayService extends BasePayService { /** * 交易关闭接口 * - * @param closeOrder 关闭订单 + * @param assistOrder 关闭订单 * @return 返回支付方交易关闭后的结果 */ @Override - public Map close(CloseOrder closeOrder){ + public Map close(AssistOrder assistOrder) { throw new UnsupportedOperationException("不支持该操作"); } + /** * 申请退款接口 * @@ -363,12 +375,10 @@ public class PayPalPayService extends BasePayService { */ @Override public Map refundquery(RefundOrder refundOrder) { - JSONObject resp = getHttpRequestTemplate().getForObject(getReqUrl(PayPalTransactionType.REFUND_QUERY), authHeader(), JSONObject.class, refundOrder.getTradeNo()); - return resp; + return getHttpRequestTemplate().getForObject(getReqUrl(PayPalTransactionType.REFUND_QUERY), authHeader(), JSONObject.class, refundOrder.getTradeNo()); } - @Override public Map downloadBill(Date billDate, BillType billType) { return Collections.emptyMap(); diff --git a/pay-java-paypal/src/main/java/com/egzosn/pay/paypal/v2/api/PayPalPayService.java b/pay-java-paypal/src/main/java/com/egzosn/pay/paypal/v2/api/PayPalPayService.java index c932770..8cab6f9 100644 --- a/pay-java-paypal/src/main/java/com/egzosn/pay/paypal/v2/api/PayPalPayService.java +++ b/pay-java-paypal/src/main/java/com/egzosn/pay/paypal/v2/api/PayPalPayService.java @@ -21,8 +21,9 @@ import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONArray; import com.alibaba.fastjson.JSONObject; import com.egzosn.pay.common.api.BasePayService; +import com.egzosn.pay.common.bean.AssistOrder; import com.egzosn.pay.common.bean.BillType; -import com.egzosn.pay.common.bean.CloseOrder; + import com.egzosn.pay.common.bean.CurType; import com.egzosn.pay.common.bean.DefaultCurType; import com.egzosn.pay.common.bean.MethodType; @@ -351,6 +352,17 @@ public class PayPalPayService extends BasePayService implem return getHttpRequestTemplate().getForObject(getReqUrl(PayPalTransactionType.ORDERS_GET), authHeader(), JSONObject.class, tradeNo); } + /** + * 交易查询接口 + * + * @param assistOrder 查询条件 + * @return 返回查询回来的结果集,支付方原值返回 + */ + @Override + public Map query(AssistOrder assistOrder) { + return getHttpRequestTemplate().getForObject(getReqUrl(PayPalTransactionType.ORDERS_GET), authHeader(), JSONObject.class, assistOrder.getTradeNo()); + } + @Override public Map close(String tradeNo, String outTradeNo) { return null; @@ -358,11 +370,11 @@ public class PayPalPayService extends BasePayService implem /** * 交易关闭接口 * - * @param closeOrder 关闭订单 + * @param assistOrder 关闭订单 * @return 返回支付方交易关闭后的结果 */ @Override - public Map close(CloseOrder closeOrder){ + public Map close(AssistOrder assistOrder){ throw new UnsupportedOperationException("不支持该操作"); } /** @@ -512,38 +524,7 @@ public class PayPalPayService extends BasePayService implem } - /** - * 将请求参数或者请求流转化为 Map - * - * @param parameterMap 请求参数 - * @param is 请求流 - * @return 获得回调的请求参数 - */ - @Override - public Map getParameter2Map(Map parameterMap, InputStream is) { - - Map params = new LinkedHashMap<>(); - for (Map.Entry entry : parameterMap.entrySet()) { - String name = entry.getKey(); - String[] values = entry.getValue(); - String valueStr = ""; - for (int i = 0, len = values.length; i < len; i++) { - valueStr += (i == len - 1) ? values[i] : values[i] + ","; - } - if (StringUtils.isNotEmpty(payConfigStorage.getInputCharset()) && !valueStr.matches("\\w+")) { - try { - if (valueStr.equals(new String(valueStr.getBytes("iso8859-1"), "iso8859-1"))) { - valueStr = new String(valueStr.getBytes("iso8859-1"), payConfigStorage.getInputCharset()); - } - } - catch (UnsupportedEncodingException e) { - LOG.error("", e); - } - } - params.put(name, valueStr); - } - return params; - } + } diff --git a/pay-java-union/src/main/java/com/egzosn/pay/union/api/UnionPayService.java b/pay-java-union/src/main/java/com/egzosn/pay/union/api/UnionPayService.java index 7576302..d236603 100644 --- a/pay-java-union/src/main/java/com/egzosn/pay/union/api/UnionPayService.java +++ b/pay-java-union/src/main/java/com/egzosn/pay/union/api/UnionPayService.java @@ -26,8 +26,9 @@ import java.util.TreeMap; import com.alibaba.fastjson.JSONObject; import com.egzosn.pay.common.api.BasePayService; +import com.egzosn.pay.common.bean.AssistOrder; import com.egzosn.pay.common.bean.BillType; -import com.egzosn.pay.common.bean.CloseOrder; + import com.egzosn.pay.common.bean.MethodType; import com.egzosn.pay.common.bean.NoticeParams; import com.egzosn.pay.common.bean.PayMessage; @@ -224,6 +225,7 @@ public class UnionPayService extends BasePayService { } return this.signVerify(result, (String) result.get(SDKConstants.param_signature)); } + /** * 签名校验 * @@ -581,25 +583,35 @@ public class UnionPayService extends BasePayService { */ @Override public Map query(String tradeNo, String outTradeNo) { + return query(new AssistOrder(tradeNo, outTradeNo)); + + } + + /** + * 交易查询接口 + * + * @param assistOrder 查询条件 + * @return 返回查询回来的结果集,支付方原值返回 + */ + @Override + public Map query(AssistOrder assistOrder) { Map params = this.getCommonParam(); UnionTransactionType.QUERY.convertMap(params); - params.put(SDKConstants.param_orderId, outTradeNo); + params.put(SDKConstants.param_orderId, assistOrder.getOutTradeNo()); this.setSign(params); String responseStr = getHttpRequestTemplate().postForObject(this.getSingleQueryUrl(), params, String.class); JSONObject response = UriVariables.getParametersToMap(responseStr); - if (this.verify(response)) { + if (this.verify(new NoticeParams(response))) { if (SDKConstants.OK_RESP_CODE.equals(response.getString(SDKConstants.param_respCode))) { String origRespCode = response.getString(SDKConstants.param_origRespCode); if ((SDKConstants.OK_RESP_CODE).equals(origRespCode)) { //交易成功,更新商户订单状态 - //TODO return response; } } throw new PayErrorException(new PayException(response.getString(SDKConstants.param_respCode), response.getString(SDKConstants.param_respMsg), response.toJSONString())); } throw new PayErrorException(new PayException("failure", "验证签名失败", response.toJSONString())); - } @@ -657,14 +669,15 @@ public class UnionPayService extends BasePayService { public Map close(String tradeNo, String outTradeNo) { return Collections.emptyMap(); } + /** * 交易关闭接口 * - * @param closeOrder 关闭订单 + * @param assistOrder 关闭订单 * @return 返回支付方交易关闭后的结果 */ @Override - public Map close(CloseOrder closeOrder){ + public Map close(AssistOrder assistOrder) { return Collections.emptyMap(); } 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 5eec170..516696c 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 @@ -12,12 +12,14 @@ import java.util.concurrent.locks.Lock; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONObject; import com.egzosn.pay.common.api.BasePayService; +import com.egzosn.pay.common.bean.AssistOrder; import com.egzosn.pay.common.bean.BaseRefundResult; import com.egzosn.pay.common.bean.BillType; -import com.egzosn.pay.common.bean.CloseOrder; + import com.egzosn.pay.common.bean.CurType; import com.egzosn.pay.common.bean.MethodType; import com.egzosn.pay.common.bean.NoticeParams; +import com.egzosn.pay.common.bean.NoticeRequest; import com.egzosn.pay.common.bean.PayMessage; import com.egzosn.pay.common.bean.PayOrder; import com.egzosn.pay.common.bean.PayOutMessage; @@ -298,15 +300,17 @@ public class WxYouDianPayService extends BasePayService getParameter2Map(Map parameterMap, InputStream is) { + public NoticeParams getNoticeParams(NoticeRequest request) { + final Map parameterMap = request.getParameterMap(); Map params = new TreeMap(); for (Iterator iter = parameterMap.keySet().iterator(); iter.hasNext(); ) { String name = (String) iter.next(); @@ -318,7 +322,7 @@ public class WxYouDianPayService extends BasePayService query(String tradeNo, String outTradeNo) { + return query(new AssistOrder(tradeNo, outTradeNo)); + } + + /** + * 交易查询接口 + * + * @param assistOrder 查询条件 + * @return 返回查询回来的结果集,支付方原值返回 + */ + @Override + public Map query(AssistOrder assistOrder) { String apbNonce = SignTextUtils.randomStr(); TreeMap data = new TreeMap<>(); data.put("access_token", payConfigStorage.getAccessToken()); - if (StringUtils.isEmpty(tradeNo)) { - data.put("order_sn", outTradeNo); + if (StringUtils.isEmpty(assistOrder.getTradeNo())) { + data.put("order_sn", assistOrder.getOutTradeNo()); } else { - data.put("order_sn", tradeNo); + data.put("order_sn", assistOrder.getTradeNo()); } String sign = createSign(SignTextUtils.parameterText(data, "") + apbNonce, payConfigStorage.getInputCharset()); String queryParam = SignTextUtils.parameterText(data) + "&apb_nonce=" + apbNonce + "&sign=" + sign; @@ -426,11 +441,11 @@ public class WxYouDianPayService extends BasePayService close(CloseOrder closeOrder){ + public Map close(AssistOrder assistOrder){ return Collections.emptyMap(); } diff --git a/pay-java-wx/src/main/java/com/egzosn/pay/wx/api/WxConst.java b/pay-java-wx/src/main/java/com/egzosn/pay/wx/api/WxConst.java index 52ea2e9..f41eab5 100644 --- a/pay-java-wx/src/main/java/com/egzosn/pay/wx/api/WxConst.java +++ b/pay-java-wx/src/main/java/com/egzosn/pay/wx/api/WxConst.java @@ -33,6 +33,6 @@ public interface WxConst { String NONCE_STR = "nonce_str"; String OUT_TRADE_NO = "out_trade_no"; String GZIP = "GZIP"; - + String BILL_DATE = "bill_date"; } 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 570feb1..2bee0d8 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 @@ -38,10 +38,12 @@ import static com.egzosn.pay.wx.bean.WxTransferType.TRANSFERS; import com.alibaba.fastjson.JSONObject; import com.egzosn.pay.common.api.BasePayService; +import com.egzosn.pay.common.bean.AssistOrder; import com.egzosn.pay.common.bean.BillType; -import com.egzosn.pay.common.bean.CloseOrder; + import com.egzosn.pay.common.bean.MethodType; import com.egzosn.pay.common.bean.NoticeParams; +import com.egzosn.pay.common.bean.NoticeRequest; import com.egzosn.pay.common.bean.Order; import com.egzosn.pay.common.bean.OrderParaStructure; import com.egzosn.pay.common.bean.PayMessage; @@ -389,25 +391,25 @@ public class WxPayService extends BasePayService implements return signType.createSign(content + "&key=" + (signType == SignUtils.MD5 ? "" : keyPrivate), keyPrivate, characterEncoding).toUpperCase(); } + /** * 将请求参数或者请求流转化为 Map * - * @param parameterMap 请求参数 - * @param is 请求流 + * @param request 通知请求 * @return 获得回调的请求参数 */ @Override - public Map getParameter2Map(Map parameterMap, InputStream is) { + public NoticeParams getNoticeParams(NoticeRequest request) { + TreeMap map = new TreeMap(); try { - return XML.inputStream2Map(is, map); + return new NoticeParams(XML.inputStream2Map(request.getInputStream(), map)); } catch (IOException e) { throw new PayErrorException(new PayException("IOException", e.getMessage())); } } - /** * 获取输出消息,用户返回给支付端 * @@ -502,6 +504,17 @@ public class WxPayService extends BasePayService implements return secondaryInterface(transactionId, outTradeNo, WxTransactionType.QUERY); } + /** + * 交易查询接口 + * + * @param assistOrder 查询条件 + * @return 返回查询回来的结果集,支付方原值返回 + */ + @Override + public Map query(AssistOrder assistOrder) { + return secondaryInterface(assistOrder.getTradeNo(), assistOrder.getOutTradeNo(), WxTransactionType.QUERY); + } + /** * 交易关闭接口 @@ -518,12 +531,12 @@ public class WxPayService extends BasePayService implements /** * 交易关闭接口 * - * @param closeOrder 关闭订单 + * @param assistOrder 关闭订单 * @return 返回支付方交易关闭后的结果 */ @Override - public Map close(CloseOrder closeOrder){ - return secondaryInterface(closeOrder.getTradeNo(), closeOrder.getOutTradeNo(), WxTransactionType.CLOSE); + public Map close(AssistOrder assistOrder){ + return secondaryInterface(assistOrder.getTradeNo(), assistOrder.getOutTradeNo(), WxTransactionType.CLOSE); } @@ -626,7 +639,7 @@ public class WxPayService extends BasePayService implements Map parameters = getPublicParameters(); parameters.put("bill_type", billType); //目前只支持日账单 - parameters.put("bill_date", DateUtils.formatDate(billDate, DateUtils.YYYYMMDD)); + parameters.put(WxConst.BILL_DATE, DateUtils.formatDate(billDate, DateUtils.YYYYMMDD)); String fileType = billType.getFileType(); OrderParaStructure.loadParameters(parameters, "tar_type", fileType); //设置签名 @@ -753,7 +766,7 @@ public class WxPayService extends BasePayService implements Map parameters = getPublicParameters(); parameters.put("bill_type", billType); //目前只支持日账单 - parameters.put("bill_date", DateUtils.formatDate(billDate, DateUtils.YYYYMMDD)); + parameters.put(WxConst.BILL_DATE, DateUtils.formatDate(billDate, DateUtils.YYYYMMDD)); if (tarType) { parameters.put("tar_type", "GZIP"); } diff --git a/pay-java-yiji/src/main/java/com/egzosn/pay/yiji/api/YiJiPayService.java b/pay-java-yiji/src/main/java/com/egzosn/pay/yiji/api/YiJiPayService.java index adae363..5f1f973 100644 --- a/pay-java-yiji/src/main/java/com/egzosn/pay/yiji/api/YiJiPayService.java +++ b/pay-java-yiji/src/main/java/com/egzosn/pay/yiji/api/YiJiPayService.java @@ -8,9 +8,10 @@ import java.util.TreeMap; import com.alibaba.fastjson.JSONObject; import com.egzosn.pay.common.api.BasePayService; +import com.egzosn.pay.common.bean.AssistOrder; import com.egzosn.pay.common.bean.BaseRefundResult; import com.egzosn.pay.common.bean.BillType; -import com.egzosn.pay.common.bean.CloseOrder; + import com.egzosn.pay.common.bean.CurType; import com.egzosn.pay.common.bean.DefaultCurType; import com.egzosn.pay.common.bean.MethodType; @@ -287,6 +288,17 @@ public class YiJiPayService extends BasePayService { return Collections.emptyMap(); } + /** + * 交易查询接口 + * + * @param assistOrder 查询条件 + * @return 返回查询回来的结果集,支付方原值返回 + */ + @Override + public Map query(AssistOrder assistOrder) { + return Collections.emptyMap(); + } + /** * 交易关闭接口 @@ -302,11 +314,11 @@ public class YiJiPayService extends BasePayService { /** * 交易关闭接口 * - * @param closeOrder 关闭订单 + * @param assistOrder 关闭订单 * @return 返回支付方交易关闭后的结果 */ @Override - public Map close(CloseOrder closeOrder){ + public Map close(AssistOrder assistOrder){ return Collections.emptyMap(); } -- Gitee From a888fb58b1d281ffc1462f0a5b225507dcbabf21 Mon Sep 17 00:00:00 2001 From: egzosn Date: Wed, 6 Oct 2021 23:31:37 +0800 Subject: [PATCH 233/299] =?UTF-8?q?=E5=88=86=E8=B4=A6=E5=8A=9F=E8=83=BD?= =?UTF-8?q?=E5=AE=9E=E7=8E=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../pay/wx/v3/api/ProfitSharingService.java | 36 ++ .../pay/wx/v3/api/WxCombinePayService.java | 27 +- .../pay/wx/v3/api/WxParameterStructure.java | 4 +- .../egzosn/pay/wx/v3/api/WxPayService.java | 58 +- .../pay/wx/v3/api/WxProfitSharingService.java | 499 ++++++++++++++++++ .../bean/WxProfitSharingTransactionType.java | 79 +++ .../pay/wx/v3/bean/WxTransactionType.java | 15 +- .../wx/v3/bean/combine/CombineCloseOrder.java | 5 +- .../pay/wx/v3/bean/response/WxPayMessage.java | 2 +- .../wx/v3/bean/response/order/TradeState.java | 9 + .../bean/sharing/ProfitSharingBillType.java | 73 +++ .../v3/bean/sharing/ProfitSharingOrder.java | 106 ++++ .../bean/sharing/ProfitSharingPayMessage.java | 119 +++++ .../sharing/ProfitSharingReturnOrder.java | 29 + .../pay/wx/v3/bean/sharing/Receiver.java | 84 +++ .../pay/wx/v3/bean/sharing/ReceiverType.java | 26 + .../wx/v3/bean/sharing/ReceiversOrder.java | 120 +++++ .../pay/wx/v3/bean/sharing/RelationType.java | 53 ++ .../sharing/WxProfitSharingReturnResult.java | 304 +++++++++++ .../com/egzosn/pay/wx/v3/utils/WxConst.java | 13 + 20 files changed, 1624 insertions(+), 37 deletions(-) create mode 100644 pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/api/ProfitSharingService.java create mode 100644 pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/api/WxProfitSharingService.java create mode 100644 pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/WxProfitSharingTransactionType.java create mode 100644 pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/sharing/ProfitSharingBillType.java create mode 100644 pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/sharing/ProfitSharingOrder.java create mode 100644 pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/sharing/ProfitSharingPayMessage.java create mode 100644 pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/sharing/ProfitSharingReturnOrder.java create mode 100644 pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/sharing/Receiver.java create mode 100644 pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/sharing/ReceiverType.java create mode 100644 pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/sharing/ReceiversOrder.java create mode 100644 pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/sharing/RelationType.java create mode 100644 pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/sharing/WxProfitSharingReturnResult.java diff --git a/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/api/ProfitSharingService.java b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/api/ProfitSharingService.java new file mode 100644 index 0000000..bdae1f3 --- /dev/null +++ b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/api/ProfitSharingService.java @@ -0,0 +1,36 @@ +package com.egzosn.pay.wx.v3.api; + +import java.util.Map; + +import com.egzosn.pay.common.bean.PayOrder; + +/** + * 分账服务 + * @author Egan + *
+ * email egan@egzosn.com
+ * date 2021/10/6
+ * 
+ */ +public interface ProfitSharingService { + + /** + * 添加分账接收方 + * @param order 添加分账 + * @return 结果 + */ + Map add(PayOrder order); + /** + * 删除分账接收方 + * @param order 删除分账 + * @return 结果 + */ + Map delete(PayOrder order); + /** + * 解冻剩余资金 + * @param order 解冻 + * @return 结果 + */ + Map unfreeze(PayOrder order); + +} diff --git a/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/api/WxCombinePayService.java b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/api/WxCombinePayService.java index 0a19bf0..e009e7b 100644 --- a/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/api/WxCombinePayService.java +++ b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/api/WxCombinePayService.java @@ -6,7 +6,7 @@ import java.util.Map; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONObject; import com.alibaba.fastjson.serializer.SerializerFeature; -import com.egzosn.pay.common.bean.CloseOrder; +import com.egzosn.pay.common.bean.AssistOrder; import com.egzosn.pay.common.bean.Order; import com.egzosn.pay.common.bean.OrderParaStructure; import com.egzosn.pay.common.bean.PayMessage; @@ -26,8 +26,8 @@ import com.egzosn.pay.wx.v3.utils.WxConst; * * @author egan *
- * email egzosn@gmail.com
- * date 2016-5-18 14:09:01
+ * email egan@egzosn.com
+ * date 2021/10/6
  * 
*/ public class WxCombinePayService extends WxPayService { @@ -114,9 +114,18 @@ public class WxCombinePayService extends WxPayService { */ @Override public Map query(String transactionId, String outTradeNo) { - return getAssistService().doExecute("", WxTransactionType.COMBINE_TRANSACTION, outTradeNo); + return query(new AssistOrder(outTradeNo)); + } + /** + * 交易查询接口 + * + * @param assistOrder 查询条件 + * @return 返回查询回来的结果集,支付方原值返回 + */ + @Override + public Map query(AssistOrder assistOrder) { + return getAssistService().doExecute("", WxTransactionType.COMBINE_TRANSACTION, assistOrder.getOutTradeNo()); } - /** * 交易关闭接口 @@ -133,16 +142,16 @@ public class WxCombinePayService extends WxPayService { /** * 交易关闭接口 * - * @param closeOrder 关闭订单 + * @param assistOrder 关闭订单 * @return 返回支付方交易关闭后的结果 */ @Override - public Map close(CloseOrder closeOrder) { + public Map close(AssistOrder assistOrder) { Map parameters = new MapGen(WxConst.COMBINE_APPID, payConfigStorage.getAppId()) - .keyValue(WxConst.SUB_ORDERS, closeOrder.getAttr(WxConst.SUB_ORDERS)) + .keyValue(WxConst.SUB_ORDERS, assistOrder.getAttr(WxConst.SUB_ORDERS)) .getAttr(); String requestBody = JSON.toJSONString(parameters, SerializerFeature.WriteMapNullValue); - return getAssistService().doExecute(requestBody, WxTransactionType.COMBINE_CLOSE, closeOrder.getOutTradeNo()); + return getAssistService().doExecute(requestBody, WxTransactionType.COMBINE_CLOSE, assistOrder.getOutTradeNo()); } /** diff --git a/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/api/WxParameterStructure.java b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/api/WxParameterStructure.java index b949b8b..3f3596b 100644 --- a/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/api/WxParameterStructure.java +++ b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/api/WxParameterStructure.java @@ -3,8 +3,6 @@ package com.egzosn.pay.wx.v3.api; import java.util.HashMap; import java.util.LinkedHashMap; import java.util.Map; -import java.util.TreeMap; - import com.egzosn.pay.common.bean.Order; import com.egzosn.pay.common.bean.OrderParaStructure; @@ -56,7 +54,7 @@ public class WxParameterStructure { public void loadSettleInfo(Map parameters, PayOrder order) { Object profitSharing = order.getAttr("profit_sharing"); if (null != profitSharing) { - Map settleInfo = new MapGen("profit_sharing", profitSharing).getAttr(); + Map settleInfo = new MapGen<>("profit_sharing", profitSharing).getAttr(); parameters.put("settle_info", settleInfo); return; } diff --git a/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/api/WxPayService.java b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/api/WxPayService.java index 80c6679..75d5f99 100644 --- a/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/api/WxPayService.java +++ b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/api/WxPayService.java @@ -21,8 +21,9 @@ import static com.egzosn.pay.wx.v3.utils.WxConst.FAILURE; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONObject; import com.egzosn.pay.common.api.BasePayService; +import com.egzosn.pay.common.bean.AssistOrder; import com.egzosn.pay.common.bean.BillType; -import com.egzosn.pay.common.bean.CloseOrder; + import com.egzosn.pay.common.bean.CurType; import com.egzosn.pay.common.bean.MethodType; import com.egzosn.pay.common.bean.NoticeParams; @@ -32,6 +33,7 @@ import com.egzosn.pay.common.bean.PayMessage; import com.egzosn.pay.common.bean.PayOrder; import com.egzosn.pay.common.bean.PayOutMessage; import com.egzosn.pay.common.bean.RefundOrder; +import com.egzosn.pay.common.bean.RefundResult; import com.egzosn.pay.common.bean.TransactionType; import com.egzosn.pay.common.bean.TransferOrder; import com.egzosn.pay.common.bean.result.PayException; @@ -66,8 +68,8 @@ import com.egzosn.pay.wx.v3.utils.WxConst; * * @author egan *
- * email egzosn@gmail.com
- * date 2016-5-18 14:09:01
+ * email egan@egzosn.com
+ * date 2021/10/6
  * 
*/ public class WxPayService extends BasePayService { @@ -111,6 +113,7 @@ public class WxPayService extends BasePayService { /** * 辅助api + * * @return 辅助api */ public WxPayAssistService getAssistService() { @@ -126,7 +129,18 @@ public class WxPayService extends BasePayService { this.assistService = assistService; } + /** + * 初始化之后执行 + */ + @Override + protected void initAfter() { + new Thread(() -> { + payConfigStorage.loadCertEnvironment(); + wxParameterStructure = new WxParameterStructure(payConfigStorage); + getAssistService(); + }).start(); + } /** * 设置api服务器地址 @@ -223,8 +237,8 @@ public class WxPayService extends BasePayService { Map parameters = wxParameterStructure.getPublicParameters(); wxParameterStructure.initPartner(parameters); // 商品描述 - OrderParaStructure.loadParameters(parameters, "description", order.getSubject()); - OrderParaStructure.loadParameters(parameters, "description", order.getBody()); + OrderParaStructure.loadParameters(parameters, WxConst.DESCRIPTION, order.getSubject()); + OrderParaStructure.loadParameters(parameters, WxConst.DESCRIPTION, order.getBody()); // 订单号 parameters.put(WxConst.OUT_TRADE_NO, order.getOutTradeNo()); //交易结束时间 @@ -430,7 +444,19 @@ public class WxPayService extends BasePayService { */ @Override public Map query(String transactionId, String outTradeNo) { + return query(new AssistOrder(transactionId, outTradeNo)); + } + /** + * 交易查询接口 + * + * @param assistOrder 查询条件 + * @return 返回查询回来的结果集,支付方原值返回 + */ + @Override + public Map query(AssistOrder assistOrder) { + String transactionId = assistOrder.getTradeNo(); + String outTradeNo = assistOrder.getOutTradeNo(); String parameters = wxParameterStructure.getSpParameters(); WxTransactionType transactionType = WxTransactionType.QUERY_TRANSACTION_ID; String uriVariable = transactionId; @@ -438,7 +464,9 @@ public class WxPayService extends BasePayService { transactionType = WxTransactionType.QUERY_OUT_TRADE_NO; uriVariable = outTradeNo; } + return getAssistService().doExecute(parameters, transactionType, uriVariable); + } @@ -451,22 +479,22 @@ public class WxPayService extends BasePayService { */ @Override public Map close(String transactionId, String outTradeNo) { - return close(new CloseOrder(outTradeNo)); + return close(new AssistOrder(outTradeNo)); } - /** * 交易关闭接口 * - * @param closeOrder 关闭订单 + * @param assistOrder 关闭订单 * @return 返回支付方交易关闭后的结果 */ @Override - public Map close(CloseOrder closeOrder) { + public Map close(AssistOrder assistOrder) { String parameters = wxParameterStructure.getSpParameters(); - return getAssistService().doExecute(parameters, WxTransactionType.CLOSE, closeOrder.getOutTradeNo()); + return getAssistService().doExecute(parameters, WxTransactionType.CLOSE, assistOrder.getOutTradeNo()); } + /** * 申请退款接口 * @@ -474,7 +502,7 @@ public class WxPayService extends BasePayService { * @return 返回支付方申请退款后的结果 */ @Override - public WxRefundResult refund(RefundOrder refundOrder) { + public RefundResult refund(RefundOrder refundOrder) { //获取公共参数 Map parameters = wxParameterStructure.initSubMchId(null); @@ -538,7 +566,7 @@ public class WxPayService extends BasePayService { Map parameters = new HashMap<>(5); //目前只支持日账单 - parameters.put("bill_date", DateUtils.formatDate(billDate, DateUtils.YYYYMMDD)); + parameters.put(WxConst.BILL_DATE, DateUtils.formatDate(billDate, DateUtils.YYYY_MM_DD)); String fileType = billType.getFileType(); OrderParaStructure.loadParameters(parameters, "tar_type", fileType); if (billType instanceof WxAccountType) { @@ -567,7 +595,7 @@ public class WxPayService extends BasePayService { } Map data = new HashMap<>(); data.put("file", inputStream); - return result; + return data; } @@ -579,7 +607,7 @@ public class WxPayService extends BasePayService { */ @Override public Map transfer(TransferOrder order) { - throw new PayErrorException(new WxPayError("", "等待作者实现")); + throw new PayErrorException(new WxPayError("", "V3不支持转账")); } @@ -596,7 +624,7 @@ public class WxPayService extends BasePayService { */ @Override public Map transferQuery(String outNo, String wxTransferType) { - throw new PayErrorException(new WxPayError("", "等待作者实现")); + throw new PayErrorException(new WxPayError("", "V3不支持转账查询")); } diff --git a/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/api/WxProfitSharingService.java b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/api/WxProfitSharingService.java new file mode 100644 index 0000000..c38ec42 --- /dev/null +++ b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/api/WxProfitSharingService.java @@ -0,0 +1,499 @@ +package com.egzosn.pay.wx.v3.api; + +import java.io.InputStream; +import java.security.PrivateKey; +import java.util.Date; +import java.util.Map; + +import com.alibaba.fastjson.JSONObject; +import com.egzosn.pay.common.bean.AssistOrder; +import com.egzosn.pay.common.bean.BillType; +import com.egzosn.pay.common.bean.MethodType; +import com.egzosn.pay.common.bean.NoticeParams; +import com.egzosn.pay.common.bean.NoticeRequest; +import com.egzosn.pay.common.bean.OrderParaStructure; +import com.egzosn.pay.common.bean.PayMessage; +import com.egzosn.pay.common.bean.PayOrder; +import com.egzosn.pay.common.bean.PayOutMessage; +import com.egzosn.pay.common.bean.RefundOrder; +import com.egzosn.pay.common.bean.RefundResult; +import com.egzosn.pay.common.bean.TransferOrder; +import com.egzosn.pay.common.bean.result.PayException; +import com.egzosn.pay.common.exception.PayErrorException; +import com.egzosn.pay.common.http.HttpConfigStorage; +import com.egzosn.pay.common.http.UriVariables; +import com.egzosn.pay.common.util.DateUtils; +import com.egzosn.pay.common.util.MapGen; +import com.egzosn.pay.common.util.sign.encrypt.RSA2; +import com.egzosn.pay.wx.bean.WxPayError; +import com.egzosn.pay.wx.bean.WxTransferType; +import com.egzosn.pay.wx.v3.bean.WxProfitSharingTransactionType; +import com.egzosn.pay.wx.v3.bean.sharing.ProfitSharingBillType; +import com.egzosn.pay.wx.v3.bean.sharing.ProfitSharingPayMessage; +import com.egzosn.pay.wx.v3.bean.sharing.WxProfitSharingReturnResult; +import com.egzosn.pay.wx.v3.utils.WxConst; + +/** + * 微信分账API服务 + * + * @author egan + *
+ * email egan@egzosn.com
+ * date 2021/10/6
+ * 
+ */ +public class WxProfitSharingService extends WxPayService implements ProfitSharingService { + + /** + * 创建支付服务 + * + * @param payConfigStorage 微信对应的支付配置 + */ + public WxProfitSharingService(WxPayConfigStorage payConfigStorage) { + super(payConfigStorage); + } + + /** + * 创建支付服务 + * + * @param payConfigStorage 微信对应的支付配置 + * @param configStorage 微信对应的网络配置,包含代理配置、ssl证书配置 + */ + public WxProfitSharingService(WxPayConfigStorage payConfigStorage, HttpConfigStorage configStorage) { + super(payConfigStorage, configStorage); + } + + + /** + * 初始化之后执行 + */ + @Override + protected void initAfter() { + new Thread(() -> { + payConfigStorage.loadCertEnvironment(); + getAssistService(); + }).start(); + + } + + /** + * 回调校验 + * + * @param params 回调回来的参数集 + * @return 签名校验 true通过 + */ + @Override + public boolean verify(Map params) { + throw new PayErrorException(new WxPayError("", "分账不支持方式")); + + } + + /** + * 验签,使用微信平台证书. + * + * @param noticeParams 通知参数 + * @return the boolean + */ + public boolean verify(NoticeParams noticeParams) { + throw new PayErrorException(new WxPayError("", "分账不支持方式")); + } + + + /** + * 微信统一下单接口 + * + * @param order 支付订单集 + * @return 下单结果 + */ + public JSONObject unifiedOrder(PayOrder order) { + + Map parameters = new MapGen(WxConst.APPID, payConfigStorage.getAppId()) + .keyValue(WxConst.TRANSACTION_ID, order.getTradeNo()) + .keyValue(WxConst.OUT_ORDER_NO, order.getOutTradeNo()) + .keyValue(WxConst.RECEIVERS, order.getAttr(WxConst.RECEIVERS)) + .keyValue(WxConst.UNFREEZE_UNSPLIT, order.getAttr(WxConst.UNFREEZE_UNSPLIT)) + .getAttr(); + //以下服务商模式必填 + OrderParaStructure.loadParameters(parameters, WxConst.SUB_MCH_ID, order); + OrderParaStructure.loadParameters(parameters, WxConst.SUB_APPID, order); + return getAssistService().doExecute(parameters, order); + } + + + /** + * 返回创建的订单信息 + * + * @param order 支付订单 + * @return 订单信息 + * @see PayOrder 支付订单信息 + */ + @Override + public Map orderInfo(PayOrder order) { + + if (null == order.getTransactionType()) { + order.setTransactionType(WxProfitSharingTransactionType.ORDERS); + } + switch ((WxProfitSharingTransactionType) order.getTransactionType()) { + case ORDERS_UNFREEZE: + return unfreeze(order); + case RECEIVERS_ADD: + return add(order); + case RECEIVERS_DELETE: + return delete(order); + default: + return unifiedOrder(order); + } + } + + + /** + * 签名 + * + * @param content 需要签名的内容 不包含key + * @param characterEncoding 字符编码 + * @return 签名结果 + */ + @Override + public String createSign(String content, String characterEncoding) { + PrivateKey privateKey = payConfigStorage.getCertEnvironment().getPrivateKey(); + return RSA2.sign(content, privateKey, characterEncoding); + } + + /** + * 将请求参数或者请求流转化为 Map + * + * @param parameterMap 请求参数 + * @param is 请求流 + * @return 获得回调的请求参数 + */ + @Deprecated + @Override + public Map getParameter2Map(Map parameterMap, InputStream is) { + throw new PayErrorException(new WxPayError("", "分账不支持方式")); + + } + + /** + * 将请求参数或者请求流转化为 Map + * + * @param request 通知请求 + * @return 获得回调的请求参数 + */ + @Override + public NoticeParams getNoticeParams(NoticeRequest request) { + throw new PayErrorException(new WxPayError("", "分账不支持方式")); + } + + /** + * 获取输出消息,用户返回给支付端 + * + * @param code 状态 + * @param message 消息 + * @return 返回输出消息 + */ + @Override + public PayOutMessage getPayOutMessage(String code, String message) { + return PayOutMessage.JSON().content("code", code).content("message", message).build(); + } + + + /** + * 获取成功输出消息,用户返回给支付端 + * 主要用于拦截器中返回 + * + * @param payMessage 支付回调消息 + * @return 返回输出消息 + */ + @Override + public PayOutMessage successPayOutMessage(PayMessage payMessage) { + return getPayOutMessage("SUCCESS", "成功"); + } + + + /** + * 获取输出消息,用户返回给支付端, 针对于web端 + * + * @param orderInfo 发起支付的订单信息 + * @param method 请求方式 "post" "get", + * @return 获取输出消息,用户返回给支付端, 针对于web端 + * @see MethodType 请求类型 + */ + @Override + public String buildRequest(Map orderInfo, MethodType method) { + throw new PayErrorException(new WxPayError("", "分账不支持方式")); + } + + /** + * 获取输出二维码信息, + * + * @param order 发起支付的订单信息 + * @return 返回二维码信息,,支付时需要的 + */ + @Override + public String getQrPay(PayOrder order) { + throw new PayErrorException(new WxPayError("", "分账不支持方式")); + } + + /** + * 刷卡付,pos主动扫码付款 + * + * @param order 发起支付的订单信息 + * @return 返回支付结果 + */ + @Override + public Map microPay(PayOrder order) { + throw new PayErrorException(new WxPayError("", "分账不支持方式")); + } + + /** + * 查询分账结果API + * 非服务商模式使用 + * + * @param transactionId 微信支付平台订单号 + * @param outTradeNo 商户单号 + * @return 返回查询回来的结果集,支付方原值返回 + */ + @Deprecated + @Override + public Map query(String transactionId, String outTradeNo) { + return query(new AssistOrder(transactionId, outTradeNo)); + } + + /** + * 查询分账结果API + *

+ * 发起分账请求后,可调用此接口查询分账结果 + *

+ * 注意: 发起解冻剩余资金请求后,可调用此接口查询解冻剩余资金的结果 + * + * @param assistOrder 查询条件 + * @return 返回查询回来的结果集,支付方原值返回 + */ + @Override + public Map query(AssistOrder assistOrder) { + if (null == assistOrder.getTransactionType()) { + assistOrder.setTransactionType(WxProfitSharingTransactionType.ORDERS_RESULT); + } + switch ((WxProfitSharingTransactionType) assistOrder.getTransactionType()) { + case AMOUNTS: + return getAssistService().doExecute("", assistOrder.getTransactionType(), assistOrder.getTradeNo()); + case MCH_CONFIG: + return getAssistService().doExecute("", assistOrder.getTransactionType(), assistOrder.getAttr(WxConst.SUB_MCH_ID)); + default: + Map parameters = new MapGen(WxConst.TRANSACTION_ID, assistOrder.getTradeNo()).getAttr(); + //服务商模式使用 + OrderParaStructure.loadParameters(parameters, WxConst.SUB_MCH_ID, assistOrder); + return getAssistService().doExecute(UriVariables.getMapToParameters(parameters), assistOrder.getTransactionType(), assistOrder.getOutTradeNo()); + + } + + } + + + /** + * 交易关闭接口 + * + * @param transactionId 支付平台订单号 + * @param outTradeNo 商户单号 + * @return 返回支付方交易关闭后的结果 + */ + @Override + public Map close(String transactionId, String outTradeNo) { + return close(new AssistOrder(outTradeNo)); + } + + + /** + * 交易关闭接口 + * + * @param assistOrder 关闭订单 + * @return 返回支付方交易关闭后的结果 + */ + @Override + public Map close(AssistOrder assistOrder) { + throw new PayErrorException(new PayException("failure", "V3暂时没有提供此功能,请查看V2版本功能")); + } + + /** + * 申请退款接口 + * + * @param refundOrder 退款订单信息 + * @return 返回支付方申请退款后的结果 + */ + @Override + public RefundResult refund(RefundOrder refundOrder) { + + Map parameters = new MapGen("return_mchid", payConfigStorage.getMchId()) + .keyValue("out_return_no", refundOrder.getRefundNo()) + .keyValue("amount", refundOrder.getRefundAmount().intValue()) + .keyValue(WxConst.DESCRIPTION, refundOrder.getDescription()) + .getAttr(); + //服务商模式使用 + OrderParaStructure.loadParameters(parameters, WxConst.SUB_MCH_ID, refundOrder); + OrderParaStructure.loadParameters(parameters, "order_id", refundOrder.getTradeNo()); + OrderParaStructure.loadParameters(parameters, "out_order_no", refundOrder.getOutTradeNo()); + return WxProfitSharingReturnResult.create(getAssistService().doExecute(parameters, WxProfitSharingTransactionType.RETURN_ORDERS)); + } + + + /** + * 查询退款 + * + * @param refundOrder 退款订单单号信息 + * @return 返回支付方查询退款后的结果 + */ + @Override + public Map refundquery(RefundOrder refundOrder) { + + Map parameters = new MapGen(WxConst.OUT_ORDER_NO, refundOrder.getOutTradeNo()).getAttr(); + //服务商模式使用 + OrderParaStructure.loadParameters(parameters, WxConst.SUB_MCH_ID, refundOrder); + String requestBody = UriVariables.getMapToParameters(parameters); + return getAssistService().doExecute(requestBody, WxProfitSharingTransactionType.RETURN_ORDERS_RESULT, refundOrder.getRefundNo()); + } + + /** + * 下载对账单 + * + * @param billDate 账单时间:日账单格式为yyyy-MM-dd,月账单格式为yyyy-MM。 + * @param billType 账单类型 内部自动转化 {@link BillType} + * @return 返回支付方下载对账单的结果 + */ + @Override + public Map downloadBill(Date billDate, String billType) { + BillType wxBillType = ProfitSharingBillType.valueOf(billType); + return downloadBill(billDate, wxBillType); + } + + /** + * 申请分账账单 + * 目前不支持指定子商户号查询 + * + * @param billDate 下载对账单的日期 + * @param billType 账单类型 {@link ProfitSharingBillType} + * @return 返回支付方下载对账单的结果, 如果【账单类型】为gzip的话则返回值中key为data值为gzip的输入流 + */ + public Map downloadBill(Date billDate, BillType billType) { + + Map parameters = new MapGen(WxConst.BILL_DATE, DateUtils.formatDate(billDate, DateUtils.YYYY_MM_DD)) + .getAttr(); + OrderParaStructure.loadParameters(parameters, WxConst.TAR_TYPE, billType.getType()); + + return getAssistService().doExecute(UriVariables.getMapToParameters(parameters), WxProfitSharingTransactionType.BILLS); + } + + + /** + * 转账 + * + * @param order 转账订单 + * @return 对应的转账结果 + */ + @Override + public Map transfer(TransferOrder order) { + throw new PayErrorException(new WxPayError("", "分账不支持方式")); + } + + + /** + * 转账查询 + * + * @param outNo 商户转账订单号 + * @param wxTransferType 微信转账类型,.....这里没办法了只能这样写(┬_┬),请见谅 {@link WxTransferType} + *

+ * 企业付款到零钱 + * 商户企业付款到银行卡 + *

+ * @return 对应的转账订单 + */ + @Override + public Map transferQuery(String outNo, String wxTransferType) { + throw new PayErrorException(new WxPayError("", "分账不支持方式")); + } + + + /** + * 创建消息 + * + * @param message 支付平台返回的消息 + * @return 支付消息对象 + */ + @Override + public PayMessage createMessage(Map message) { + return ProfitSharingPayMessage.create(message); + } + + + /** + * 添加分账接收方 + * + * @param order 添加分账 + * @return 结果 + */ + @Override + public Map add(PayOrder order) { + if (null == order.getTransactionType()) { + order.setTransactionType(WxProfitSharingTransactionType.RECEIVERS_ADD); + } + Map parameters = new MapGen(WxConst.APPID, payConfigStorage.getAppId()) + .getAttr(); + + OrderParaStructure.loadParameters(parameters, WxConst.TYPE, order); + OrderParaStructure.loadParameters(parameters, WxConst.ACCOUNT, order); + OrderParaStructure.loadParameters(parameters, WxConst.NAME, order); + OrderParaStructure.loadParameters(parameters, WxConst.RELATION_TYPE, order); + OrderParaStructure.loadParameters(parameters, WxConst.CUSTOM_RELATION, order); + //以下服务商模式必填 + OrderParaStructure.loadParameters(parameters, WxConst.SUB_MCH_ID, order); + OrderParaStructure.loadParameters(parameters, WxConst.SUB_APPID, order); + + return getAssistService().doExecute(parameters, order); + } + + /** + * 删除分账接收方 + * + * @param order 删除分账 + * @return 结果 + */ + @Override + public Map delete(PayOrder order) { + if (null == order.getTransactionType()) { + order.setTransactionType(WxProfitSharingTransactionType.RECEIVERS_DELETE); + } + Map parameters = new MapGen(WxConst.APPID, payConfigStorage.getAppId()) + .getAttr(); + + OrderParaStructure.loadParameters(parameters, WxConst.TYPE, order); + OrderParaStructure.loadParameters(parameters, WxConst.ACCOUNT, order); + //以下服务商模式必填 + OrderParaStructure.loadParameters(parameters, WxConst.SUB_MCH_ID, order); + OrderParaStructure.loadParameters(parameters, WxConst.SUB_APPID, order); + + return getAssistService().doExecute(parameters, order); + } + + /** + * 解冻剩余资金 + * + * @param order 解冻 + * @return 结果 + */ + @Override + public Map unfreeze(PayOrder order) { + if (null == order.getTransactionType()) { + order.setTransactionType(WxProfitSharingTransactionType.ORDERS_UNFREEZE); + } + Map parameters = new MapGen(WxConst.TRANSACTION_ID, order.getTradeNo()) + .keyValue(WxConst.OUT_ORDER_NO, order.getOutTradeNo()) + .getAttr(); + // 商品描述 + OrderParaStructure.loadParameters(parameters, WxConst.DESCRIPTION, order.getSubject()); + OrderParaStructure.loadParameters(parameters, WxConst.DESCRIPTION, order.getBody()); + OrderParaStructure.loadParameters(parameters, WxConst.DESCRIPTION, order); + + //以下服务商模式必填 + OrderParaStructure.loadParameters(parameters, WxConst.SUB_MCH_ID, order); + return getAssistService().doExecute(parameters, order); + } +} diff --git a/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/WxProfitSharingTransactionType.java b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/WxProfitSharingTransactionType.java new file mode 100644 index 0000000..e7fb075 --- /dev/null +++ b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/WxProfitSharingTransactionType.java @@ -0,0 +1,79 @@ +package com.egzosn.pay.wx.v3.bean; + +import com.egzosn.pay.common.bean.MethodType; +import com.egzosn.pay.common.bean.TransactionType; + +/** + * 微信V3分账交易类型 + * + * @author egan + *
+ * email egan@egzosn.com
+ * date 2021/10/6
+ * 
+ */ +public enum WxProfitSharingTransactionType implements TransactionType { + /** + * 请求分账 + */ + ORDERS("/v3/profitsharing/orders ", MethodType.POST), + /** + * 查询分账结果 + */ + ORDERS_RESULT("/v3/profitsharing/orders/{out_order_no} ", MethodType.POST), + /** + * 请求分账回退 + */ + RETURN_ORDERS("/v3/profitsharing/return-orders ", MethodType.POST), + /** + * 查询分账回退结果 + */ + RETURN_ORDERS_RESULT("/v3/profitsharing/return-orders/{out_return_no}", MethodType.GET), + /** + * 解冻剩余资金 + */ + ORDERS_UNFREEZE("/v3/profitsharing/orders/unfreeze ", MethodType.POST), + /** + * 查询剩余待分金额 + */ + AMOUNTS("/v3/profitsharing/transactions/{transaction_id}/amounts ", MethodType.GET), + /** + * 服务商专用-查询最大分账比例 + */ + MCH_CONFIG("/v3/profitsharing/merchant-configs/{sub_mchid} ", MethodType.GET), + /** + * 添加分账接收方 + */ + RECEIVERS_ADD("/v3/profitsharing/receivers/add ", MethodType.POST), + /** + * 删除分账接收方 + */ + RECEIVERS_DELETE("/v3/profitsharing/receivers/add", MethodType.POST), + + BILLS("/v3/profitsharing/bills", MethodType.GET), + + ; + + + + WxProfitSharingTransactionType(String type, MethodType method) { + this.type = type; + this.method = method; + } + + private String type; + private MethodType method; + + + + @Override + public String getType() { + return type; + } + + @Override + public String getMethod() { + return this.method.name(); + } + +} diff --git a/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/WxTransactionType.java b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/WxTransactionType.java index 948d3f0..ead42a1 100644 --- a/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/WxTransactionType.java +++ b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/WxTransactionType.java @@ -17,9 +17,10 @@ import com.egzosn.pay.wx.v3.utils.WxConst; * 微信V3交易类型 * * @author egan - *

- * email egzosn@gmail.com + *

+ * email egan@egzosn.com
  * date 2016/10/19 22:58
+ * 
*/ public enum WxTransactionType implements TransactionType { /** @@ -127,7 +128,7 @@ public enum WxTransactionType implements TransactionType { //----------------------------------------------------------------- //以下为合并支付 /** - * 合单下单-APP支付API. + * 合单下单-APP支付 */ COMBINE_APP("/v3/combine-transactions/app", MethodType.POST), @@ -136,20 +137,20 @@ public enum WxTransactionType implements TransactionType { */ COMBINE_JSAPI("/v3/combine-transactions/jsapi", MethodType.POST), /** - * 合单下单-H5支付API. + * 合单下单-H5支付 */ COMBINE_H5("/v3/combine-transactions/h5", MethodType.POST, true), /** - * 合单下单-Native支付API. + * 合单下单-Native支付 */ COMBINE_NATIVE("/v3/combine-transactions/native", MethodType.POST, true), /** - * 合单查询订单API. + * 合单查询订单 */ COMBINE_TRANSACTION("/v3/combine-transactions/out-trade-no/{combine_out_trade_no}", MethodType.GET), /** - * 合单关闭订单API. + * 合单关闭订单 */ COMBINE_CLOSE("/v3/combine-transactions/out-trade-no/{combine_out_trade_no}/close", MethodType.POST), ; diff --git a/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/combine/CombineCloseOrder.java b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/combine/CombineCloseOrder.java index dfe9c84..ae328bd 100644 --- a/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/combine/CombineCloseOrder.java +++ b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/combine/CombineCloseOrder.java @@ -2,7 +2,8 @@ package com.egzosn.pay.wx.v3.bean.combine; import java.util.List; -import com.egzosn.pay.common.bean.CloseOrder; +import com.egzosn.pay.common.bean.AssistOrder; + import com.egzosn.pay.wx.v3.utils.WxConst; /** @@ -13,7 +14,7 @@ import com.egzosn.pay.wx.v3.utils.WxConst; * date 2021/10/6 * */ -public class CombineCloseOrder extends CloseOrder { +public class CombineCloseOrder extends AssistOrder { /** * 子单信息,必填,最多50单 diff --git a/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/response/WxPayMessage.java b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/response/WxPayMessage.java index e058663..2dccdeb 100644 --- a/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/response/WxPayMessage.java +++ b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/response/WxPayMessage.java @@ -361,7 +361,7 @@ public class WxPayMessage extends PayMessage { this.userReceivedAccount = userReceivedAccount; } - public static final WxPayMessage create(Map message) { + public static WxPayMessage create(Map message) { WxPayMessage payMessage = new JSONObject(message).toJavaObject(WxPayMessage.class); // payMessage.setPayType(""); payMessage.setPayMessage(message); diff --git a/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/response/order/TradeState.java b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/response/order/TradeState.java index 9d49a4c..260c2ae 100644 --- a/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/response/order/TradeState.java +++ b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/response/order/TradeState.java @@ -48,4 +48,13 @@ public enum TradeState { * 已接收,等待扣款 */ ACCEPT, + /** + * 如果请求返回为处理中,则商户可以通过调用回退结果查询接口获取请求的最终处理结果。如果查询到回退结果在处理中,请勿变更商户回退单号,使用相同的参数再次发起分账回退,否则会出现资金风险。在处理中状态的回退单如果5天没有成功,会因为超时被设置为已失败。 + * 处理中 + */ + PROCESSING, + /** + * 失败 + */ + FAILED; } \ No newline at end of file diff --git a/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/sharing/ProfitSharingBillType.java b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/sharing/ProfitSharingBillType.java new file mode 100644 index 0000000..487e16f --- /dev/null +++ b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/sharing/ProfitSharingBillType.java @@ -0,0 +1,73 @@ +package com.egzosn.pay.wx.v3.bean.sharing; + +import com.egzosn.pay.common.bean.BillType; + +/** + * 分账账单类型 + * + * @author Egan + *
+ * email egan@egzosn.com
+ * date 2021/10/6
+ * 
+ */ +public enum ProfitSharingBillType implements BillType { + + /** + * 数据流 + */ + STREAM, + /** + * 返回格式为.gzip的压缩包账单 + */ + GZIP("GZIP"); + + ProfitSharingBillType() { + } + + ProfitSharingBillType(String type) { + this.type = type; + } + + private String type; + + /** + * 获取类型名称 + * + * @return 类型 + */ + @Override + public String getType() { + return type; + } + + /** + * 获取类型对应的日期格式化表达式 + * + * @return 日期格式化表达式 + */ + @Override + public String getDatePattern() { + return null; + } + + /** + * 获取文件类型 + * + * @return 文件类型 + */ + @Override + public String getFileType() { + return null; + } + + /** + * 自定义属性 + * + * @return 自定义属性 + */ + @Override + public String getCustom() { + return null; + } +} diff --git a/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/sharing/ProfitSharingOrder.java b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/sharing/ProfitSharingOrder.java new file mode 100644 index 0000000..2e6112b --- /dev/null +++ b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/sharing/ProfitSharingOrder.java @@ -0,0 +1,106 @@ +package com.egzosn.pay.wx.v3.bean.sharing; + +import java.util.List; + +import com.egzosn.pay.common.bean.PayOrder; +import com.egzosn.pay.wx.v3.utils.WxConst; + +/** + * 服务商请求分账订单 + * @author Egan + *
+ * email egan@egzosn.com
+ * date 2021/10/6
+ * 
+ */ +public class ProfitSharingOrder extends PayOrder { + + /** + * 子商户号,选填,服务商必填 + */ + private String subMchid; + + /** + * 子商户应用ID,选填 + *

+ * 分账接收方类型包含{@code PERSONAL_SUB_OPENID}时必填 + */ + private String subAppid; + + /** + * 分账接收方列表,选填 + *

+ * 可以设置出资商户作为分账接受方,最多可有50个分账接收方 + */ + private List receivers; + /** + * 是否解冻剩余未分资金,必填 + *

    + *
  1. 如果为{@code true},该笔订单剩余未分账的金额会解冻回分账方商户;
  2. + *
  3. 如果为{@code false},该笔订单剩余未分账的金额不会解冻回分账方商户,可以对该笔订单再次进行分账。
  4. + *
+ */ + private Boolean unfreezeUnsplit; + + public String getSubMchid() { + return subMchid; + } + + public void setSubMchid(String subMchid) { + this.subMchid = subMchid; + addAttr(WxConst.SUB_MCH_ID, subMchid); + } + + public String getSubAppid() { + return subAppid; + } + + public void setSubAppid(String subAppid) { + this.subAppid = subAppid; + addAttr(WxConst.SUB_APPID, subAppid); + } + + /** + * 微信支付订单号 + * @return 微信支付订单号 + */ + public String getTransactionId() { + return getTradeNo(); + } + + public void setTransactionId(String transactionId) { + setTradeNo(transactionId); + } + /** + * 商户分账单号,必填 + *

+ * 商户系统内部的分账单号,在商户系统内部唯一,同一分账单号多次请求等同一次。 + * 只能是数字、大小写字母_-|*@ + * @return 商户分账单号,必填 + */ + public String getOutOrderNo() { + return getOutTradeNo(); + } + + public void setOutOrderNo(String outOrderNo) { + setOutTradeNo(outOrderNo); + } + + public List getReceivers() { + return receivers; + } + + public void setReceivers(List receivers) { + this.receivers = receivers; + addAttr(WxConst.RECEIVERS, receivers); + } + + public Boolean getUnfreezeUnsplit() { + return unfreezeUnsplit; + } + + public void setUnfreezeUnsplit(Boolean unfreezeUnsplit) { + this.unfreezeUnsplit = unfreezeUnsplit; + addAttr(WxConst.UNFREEZE_UNSPLIT, receivers); + } +} diff --git a/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/sharing/ProfitSharingPayMessage.java b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/sharing/ProfitSharingPayMessage.java new file mode 100644 index 0000000..18ff526 --- /dev/null +++ b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/sharing/ProfitSharingPayMessage.java @@ -0,0 +1,119 @@ +package com.egzosn.pay.wx.v3.bean.sharing; + +import java.util.Date; +import java.util.List; +import java.util.Map; + +import com.alibaba.fastjson.JSONObject; +import com.alibaba.fastjson.annotation.JSONField; +import com.egzosn.pay.common.bean.PayMessage; + +/** + * 分账动账通知 + * @author Egan + *

+ * email egan@egzosn.com
+ * date 2021/10/4
+ * 
+ */ +public class ProfitSharingPayMessage extends PayMessage { + + /** + * 直连商户号. + *

+ * 直连模式分账发起和出资商户 + */ + private String mchid; + + /** + * 微信支付订单号 + */ + @JSONField(name = "transaction_id") + private String transactionId; + + /** + * 微信分账/回退单号. + */ + @JSONField(name = "order_id") + private String orderId; + + /** + * 商户分账/回退单号. + * 分账方系统内部的分账/回退单号 + */ + @JSONField(name = "out_order_no") + private String outOrderNo; + + /** + * 分账接收方. + *

+ * 分账接收方对象 + */ + private List receivers; + + /** + * 成功时间. + *

+ * Rfc3339标准 + */ + @JSONField(name = "success_time", format = "yyyy-MM-dd'T'HH:mm:ssXXX") + private Date successTime; + + + public String getMchid() { + return mchid; + } + + public void setMchid(String mchid) { + this.mchid = mchid; + } + + public String getTransactionId() { + return transactionId; + } + + public void setTransactionId(String transactionId) { + this.transactionId = transactionId; + } + + public String getOrderId() { + return orderId; + } + + public void setOrderId(String orderId) { + this.orderId = orderId; + } + + public String getOutOrderNo() { + return outOrderNo; + } + + public void setOutOrderNo(String outOrderNo) { + this.outOrderNo = outOrderNo; + } + + public List getReceivers() { + return receivers; + } + + public void setReceivers(List receivers) { + this.receivers = receivers; + } + + public Date getSuccessTime() { + return successTime; + } + + public void setSuccessTime(Date successTime) { + this.successTime = successTime; + } + + public static ProfitSharingPayMessage create(Map message) { + ProfitSharingPayMessage payMessage = new JSONObject(message).toJavaObject(ProfitSharingPayMessage.class); +// payMessage.setPayType(""); + payMessage.setPayMessage(message); + return payMessage; + } + + +} diff --git a/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/sharing/ProfitSharingReturnOrder.java b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/sharing/ProfitSharingReturnOrder.java new file mode 100644 index 0000000..8252120 --- /dev/null +++ b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/sharing/ProfitSharingReturnOrder.java @@ -0,0 +1,29 @@ +package com.egzosn.pay.wx.v3.bean.sharing; + +import com.egzosn.pay.common.bean.RefundOrder; +import com.egzosn.pay.wx.v3.utils.WxConst; + +/** + * 分账回退订单 + * @author Egan + *

+ * email egan@egzosn.com
+ * date 2021/10/6
+ * 
+ */ +public class ProfitSharingReturnOrder extends RefundOrder { + + /** + * 子商户号,选填,服务商必填 + */ + private String subMchid; + + public String getSubMchid() { + return subMchid; + } + + public void setSubMchid(String subMchid) { + this.subMchid = subMchid; + addAttr(WxConst.SUB_MCH_ID, subMchid); + } +} diff --git a/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/sharing/Receiver.java b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/sharing/Receiver.java new file mode 100644 index 0000000..cd62f34 --- /dev/null +++ b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/sharing/Receiver.java @@ -0,0 +1,84 @@ +package com.egzosn.pay.wx.v3.bean.sharing; + +/** + * 分账接收方信息 + * + * @author Egan + *
+ * email egan@egzosn.com
+ * date 2021/10/6
+ * 
+ */ + +public class Receiver { + /** + * 分账接收方类型,必填 + */ + private ReceiverType type; + /** + * 分账接收方帐号,必填 + */ + private String account; + /** + * 分账个人接收方姓名,选填 + *

+ * 在接收方类型为个人的时可选填,若有值,会检查与 name 是否实名匹配,不匹配会拒绝分账请求 + *

    + *
  1. 分账接收方类型是{@code PERSONAL_OPENID},是个人姓名的密文(选传,传则校验) 此字段的加密方法详见:敏感信息加密说明
  2. + *
  3. 使用微信支付平台证书中的公钥
  4. + *
  5. 使用RSAES-OAEP算法进行加密
  6. + *
  7. 将请求中HTTP头部的Wechatpay-Serial设置为证书序列号
  8. + *
+ */ + private String name; + /** + * 分账金额,必填 + *

+ * 单位为分,只能为整数,不能超过原订单支付金额及最大分账比例金额 + */ + private Integer amount; + /** + * 分账的原因描述,必填。分账账单中需要体现 + */ + private String description; + + public ReceiverType getType() { + return type; + } + + public void setType(ReceiverType type) { + this.type = type; + } + + public String getAccount() { + return account; + } + + public void setAccount(String account) { + this.account = account; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public Integer getAmount() { + return amount; + } + + public void setAmount(Integer amount) { + this.amount = amount; + } + + public String getDescription() { + return description; + } + + public void setDescription(String description) { + this.description = description; + } +} diff --git a/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/sharing/ReceiverType.java b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/sharing/ReceiverType.java new file mode 100644 index 0000000..00e73c3 --- /dev/null +++ b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/sharing/ReceiverType.java @@ -0,0 +1,26 @@ +package com.egzosn.pay.wx.v3.bean.sharing; + + +/** + * 分账接收方类型 + * + * @author Egan + *

+ * email egan@egzosn.com
+ * date 2021/10/6
+ * 
+ */ +public enum ReceiverType { + /** + * 商户号 + */ + MERCHANT_ID, + /** + * 个人openid(由父商户APPID转换得到) + */ + PERSONAL_OPENID, + /** + * 个人sub_openid(由子商户APPID转换得到),服务商模式 + */ + PERSONAL_SUB_OPENID +} \ No newline at end of file diff --git a/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/sharing/ReceiversOrder.java b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/sharing/ReceiversOrder.java new file mode 100644 index 0000000..d435611 --- /dev/null +++ b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/sharing/ReceiversOrder.java @@ -0,0 +1,120 @@ +package com.egzosn.pay.wx.v3.bean.sharing; + +import com.egzosn.pay.common.bean.PayOrder; +import com.egzosn.pay.wx.v3.utils.WxConst; + +/** + * 添加分账接收方 + * + * @author Egan + *
+ * email egan@egzosn.com
+ * date 2021/10/6
+ * 
+ */ +public class ReceiversOrder extends PayOrder { + + /** + * 子商户号,选填 + */ + private String subMchid; + /** + * 子商户应用ID,选填 + *

+ * 分账接收方类型包含{@code PERSONAL_SUB_OPENID}时必填 + */ + private String subAppid; + /** + * 分账接收方类型,必填 + */ + private ReceiverType type; + /** + * 分账接收方帐号,必填 + */ + private String account; + /** + * 分账个人接收方姓名,选填 + *

+ * 分账接收方类型是{@code MERCHANT_ID}时,是商户全称(必传),当商户是小微商户或个体户时,是开户人姓名 分账接收方类型是{@code PERSONAL_OPENID}时,是个人姓名(选传,传则校验) + *

    + *
  1. 分账接收方类型是{@code PERSONAL_OPENID},是个人姓名的密文(选传,传则校验) 此字段的加密方法详见:敏感信息加密说明
  2. + *
  3. 使用微信支付平台证书中的公钥
  4. + *
  5. 使用RSAES-OAEP算法进行加密
  6. + *
  7. 将请求中HTTP头部的Wechatpay-Serial设置为证书序列号
  8. + *
+ */ + private String name; + /** + * 与分账方的关系类型,必填 + */ + private RelationType relationType; + /** + * 自定义的分账关系,选填 + */ + private String customRelation; + + public String getSubMchid() { + return subMchid; + } + + public void setSubMchid(String subMchid) { + this.subMchid = subMchid; + addAttr(WxConst.SUB_MCH_ID, subMchid); + } + + public String getSubAppid() { + return subAppid; + } + + public void setSubAppid(String subAppid) { + this.subAppid = subAppid; + addAttr(WxConst.SUB_APPID, subAppid); + } + + public ReceiverType getType() { + return type; + } + + public void setType(ReceiverType type) { + this.type = type; + addAttr(WxConst.TYPE, type); + } + + public String getAccount() { + return account; + } + + public void setAccount(String account) { + this.account = account; + addAttr(WxConst.ACCOUNT, account); + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + addAttr(WxConst.NAME, name); + } + + public RelationType getRelationType() { + return relationType; + } + + public void setRelationType(RelationType relationType) { + this.relationType = relationType; + addAttr(WxConst.RELATION_TYPE, relationType); + } + + public String getCustomRelation() { + return customRelation; + } + + public void setCustomRelation(String customRelation) { + this.customRelation = customRelation; + addAttr(WxConst.CUSTOM_RELATION, customRelation); + } + + +} diff --git a/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/sharing/RelationType.java b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/sharing/RelationType.java new file mode 100644 index 0000000..1511892 --- /dev/null +++ b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/sharing/RelationType.java @@ -0,0 +1,53 @@ +package com.egzosn.pay.wx.v3.bean.sharing; + +/** + * 子商户与接收方的关系 + * + * @author Egan + *
+ * email egan@egzosn.com
+ * date 2021/10/6
+ * 
+ */ +public enum RelationType { + /** + * 门店. + */ + STORE, + /** + * 员工. + */ + STAFF, + /** + * 店主. + */ + STORE_OWNER, + /** + * 合作伙伴. + */ + PARTNER, + /** + * 总部. + */ + HEADQUARTER, + /** + * 品牌方. + */ + BRAND, + /** + * 分销商. + */ + DISTRIBUTOR, + /** + * 用户. + */ + USER, + /** + * 供应商. + */ + SUPPLIER, + /** + * 自定义. + */ + CUSTOM + } \ No newline at end of file diff --git a/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/sharing/WxProfitSharingReturnResult.java b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/sharing/WxProfitSharingReturnResult.java new file mode 100644 index 0000000..8f51a5d --- /dev/null +++ b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/sharing/WxProfitSharingReturnResult.java @@ -0,0 +1,304 @@ +package com.egzosn.pay.wx.v3.bean.sharing; + +import java.math.BigDecimal; +import java.util.Map; + +import com.alibaba.fastjson.JSONObject; +import com.alibaba.fastjson.annotation.JSONField; +import com.egzosn.pay.common.bean.BaseRefundResult; +import com.egzosn.pay.common.bean.CurType; +import com.egzosn.pay.wx.v3.bean.response.order.TradeState; +import com.egzosn.pay.wx.v3.utils.WxConst; + +/** + * 微信退款结果 + * + * @author Egan + *
+ * email egan@egzosn.com
+ * date 2021/10/6
+ * 
+ */ +public class WxProfitSharingReturnResult extends BaseRefundResult { + + /** + * 分账回退的接收商户,对应原分账出资的分账方商户,填写微信支付分配的商户号 + * 直连商户不用传二级商户号。 + */ + @JSONField(name = "sub_mchid") + private String subMchid; + + /** + * 微信分账单号,微信系统返回的唯一标识。 + * 示例值:3008450740201411110007820472 + */ + @JSONField(name = "order_id") + private String orderId; + /** + * 商户分账单号 + * 商户系统内部的分账单号,在商户系统内部唯一,同一分账单号多次请求等同一次。 取值范围:[0-9a-zA-Z_*@-] + * 示例值:P20150806125346 + */ + @JSONField(name = WxConst.OUT_ORDER_NO) + private String outOrderNo; + /** + * 此回退单号是商户在自己后台生成的一个新的回退单号,在商户后台唯一 + * 示例值:R20190516001 + */ + @JSONField(name = "out_return_no") + private String outReturnNo; + /** + * 微信分账回退单号,微信系统返回的唯一标识 + * 示例值:3008450740201411110007820472 + */ + @JSONField(name = "return_id") + private String returnId; + /** + * 回退商户号 + * 只能对原分账请求中成功分给商户接收方进行回退 + * 示例值:86693852 + */ + @JSONField(name = "return_mchid") + private String returnMchid; + + + /** + * 回退金额 + * 需要从分账接收方回退的金额,单位为分,只能为整数 + * 示例值:10 + */ + private Integer amount; + + /** + * 分账回退的原因描述 + * 示例值:用户退款 + */ + private String description; + /** + 如果请求返回为处理中,则商户可以通过调用回退结果查询接口获取请求的最终处理结果。如果查询到回退结果在处理中,请勿变更商户回退单号,使用相同的参数再次发起分账回退,否则会出现资金风险。在处理中状态的回退单如果5天没有成功,会因为超时被设置为已失败。 + 枚举值: + PROCESSING:处理中 + SUCCESS:已成功 + FAILED:已失败 + 示例值:SUCCESS + */ + private TradeState result; + /** + * 失败原因。包含以下枚举值: + * ACCOUNT_ABNORMAL : 分账接收方账户异常 + * TIME_OUT_CLOSED : 超时关单 + * 示例值:TIME_OUT_CLOSED + */ + @JSONField(name = "fail_reason") + private String failReason; + + /** + * 分账回退创建时间,遵循rfc3339标准格式,格式为YYYY-MM-DDTHH:mm:ss.sss+TIMEZONE,YYYY-MM-DD表示年月日,T出现在字符串中,表示time元素的开头,HH:mm:ss.sss表示时分秒毫秒,TIMEZONE表示时区(+08:00表示东八区时间,领先UTC 8小时,即北京时间)。例如:2015-05-20T13:29:35.120+08:00表示,北京时间2015年5月20日 13点29分35秒。 + * 示例值:2015-05-20T13:29:35.120+08:00 + */ + @JSONField(name = "create_time") + private String createTime; + + /** + * 分账回退完成时间,遵循rfc3339标准格式,格式为YYYY-MM-DDTHH:mm:ss.sss+TIMEZONE,YYYY-MM-DD表示年月日,T出现在字符串中,表示time元素的开头,HH:mm:ss.sss表示时分秒毫秒,TIMEZONE表示时区(+08:00表示东八区时间,领先UTC 8小时,即北京时间)。例如:2015-05-20T13:29:35.120+08:00表示,北京时间2015年5月20日 13点29分35秒。 + * 示例值:2015-05-20T13:29:35.120+08:00 + */ + @JSONField(name = "finish_time") + private String finishTime; + + + /** + * 获取退款请求结果状态码 + * + * @return 状态码 + */ + @Override + public String getCode() { + return result.name(); + } + + /** + * 获取退款请求结果状态提示信息 + * + * @return 提示信息 + */ + @Override + public String getMsg() { + return failReason; + } + + /** + * 返回业务结果状态码 + * + * @return 业务结果状态码 + */ + @Override + public String getResultCode() { + return result.name(); + } + + /** + * 返回业务结果状态提示信息 + * + * @return 业务结果状态提示信息 + */ + @Override + public String getResultMsg() { + return failReason; + } + + /** + * 退款金额, 金额元 + * + * @return 退款金额 + */ + @Override + public BigDecimal getRefundFee() { + + return new BigDecimal(amount); + } + + /** + * 退款币种信息 + * + * @return 币种信息 + */ + @Override + public CurType getRefundCurrency() { + return null; + } + + /** + * 支付平台交易号 + * 发起支付时 支付平台(如支付宝)返回的交易订单号 + * + * @return 支付平台交易号 + */ + @Override + public String getTradeNo() { + return orderId; + } + + /** + * 支付订单号 + * 发起支付时,用户系统的订单号 + * + * @return 支付订单号 + */ + @Override + public String getOutTradeNo() { + return outOrderNo; + } + + /** + * 商户退款单号 + * + * @return 商户退款单号 + */ + @Override + public String getRefundNo() { + return outReturnNo; + } + + public String getSubMchid() { + return subMchid; + } + + public void setSubMchid(String subMchid) { + this.subMchid = subMchid; + } + + public String getOrderId() { + return orderId; + } + + public void setOrderId(String orderId) { + this.orderId = orderId; + } + + public String getOutOrderNo() { + return outOrderNo; + } + + public void setOutOrderNo(String outOrderNo) { + this.outOrderNo = outOrderNo; + } + + public String getOutReturnNo() { + return outReturnNo; + } + + public void setOutReturnNo(String outReturnNo) { + this.outReturnNo = outReturnNo; + } + + public String getReturnId() { + return returnId; + } + + public void setReturnId(String returnId) { + this.returnId = returnId; + } + + public String getReturnMchid() { + return returnMchid; + } + + public void setReturnMchid(String returnMchid) { + this.returnMchid = returnMchid; + } + + public Integer getAmount() { + return amount; + } + + public void setAmount(Integer amount) { + this.amount = amount; + } + + public String getDescription() { + return description; + } + + public void setDescription(String description) { + this.description = description; + } + + public TradeState getResult() { + return result; + } + + public void setResult(TradeState result) { + this.result = result; + } + + public String getFailReason() { + return failReason; + } + + public void setFailReason(String failReason) { + this.failReason = failReason; + } + + public String getCreateTime() { + return createTime; + } + + public void setCreateTime(String createTime) { + this.createTime = createTime; + } + + public String getFinishTime() { + return finishTime; + } + + public void setFinishTime(String finishTime) { + this.finishTime = finishTime; + } + + public static final WxProfitSharingReturnResult create(Map result) { + WxProfitSharingReturnResult refundResult = new JSONObject(result).toJavaObject(WxProfitSharingReturnResult.class); + refundResult.setAttrs(result); + return refundResult; + } +} diff --git a/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/utils/WxConst.java b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/utils/WxConst.java index 941ff10..06d43f4 100644 --- a/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/utils/WxConst.java +++ b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/utils/WxConst.java @@ -33,6 +33,17 @@ public final class WxConst { public static final String SANDBOXNEW = "sandboxnew/"; public static final String COMBINE = "combine_"; public static final String APPID = "appid"; + public static final String SUB_APPID = "sub_appid"; + public static final String TRANSACTION_ID = "transaction_id"; + public static final String OUT_ORDER_NO = "out_order_no"; + public static final String TYPE = "type"; + public static final String ACCOUNT = "account"; + public static final String NAME = "name"; + public static final String RELATION_TYPE = "relationType"; + public static final String CUSTOM_RELATION = "customRelation"; + public static final String DESCRIPTION = "description"; + public static final String BILL_DATE = "bill_date"; + public static final String TAR_TYPE = "tar_type"; public static final String COMBINE_APPID = COMBINE + APPID; public static final String MCH_ID = "mchid"; @@ -45,6 +56,8 @@ public final class WxConst { public static final String TIME_START = "time_start"; public static final String TIME_EXPIRE = "time_expire"; public static final String SUB_ORDERS = "sub_orders"; + public static final String RECEIVERS = "receivers"; + public static final String UNFREEZE_UNSPLIT = "unfreeze_unsplit"; public static final String TOKEN_PATTERN = "mchid=\"%s\",nonce_str=\"%s\",timestamp=\"%d\",serial_no=\"%s\",signature=\"%s\""; -- Gitee From 973a4698508c7065acad89e7438497c0a970c9bc Mon Sep 17 00:00:00 2001 From: egzosn Date: Wed, 6 Oct 2021 23:32:18 +0800 Subject: [PATCH 234/299] =?UTF-8?q?=E6=A0=B9=E6=8D=AE=E6=9C=80=E6=96=B0?= =?UTF-8?q?=E7=9A=84=E6=8E=A5=E5=8F=A3=E5=8A=9F=E8=83=BD=E5=AE=9E=E7=8E=B0?= =?UTF-8?q?=E6=A1=88=E4=BE=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../pay/demo/controller/AliPayController.java | 5 +-- .../pay/demo/controller/PayController.java | 5 +-- .../controller/PayoneerPayController.java | 5 +-- .../demo/controller/UnionPayController.java | 35 +++++++++---------- .../pay/demo/controller/WxPayController.java | 4 +-- .../controller/WxV3CombinePayController.java | 22 ++++++------ .../demo/controller/WxV3PayController.java | 8 +++-- .../handler/WxV3CombinePayMessageHandler.java | 6 ++-- 8 files changed, 48 insertions(+), 42 deletions(-) diff --git a/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/AliPayController.java b/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/AliPayController.java index f0a674c..8911a83 100644 --- a/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/AliPayController.java +++ b/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/AliPayController.java @@ -25,6 +25,7 @@ import com.egzosn.pay.ali.bean.AliTransactionType; import com.egzosn.pay.ali.bean.AliTransferOrder; import com.egzosn.pay.ali.bean.AliTransferType; import com.egzosn.pay.ali.bean.OrderSettle; +import com.egzosn.pay.common.bean.AssistOrder; import com.egzosn.pay.common.bean.CertStoreType; import com.egzosn.pay.common.bean.NoticeParams; import com.egzosn.pay.common.bean.PayOrder; @@ -283,7 +284,7 @@ public class AliPayController { */ @RequestMapping("query") public Map query(QueryOrder order) { - return service.query(order.getTradeNo(), order.getOutTradeNo()); + return service.query(new AssistOrder(order.getTradeNo(), order.getOutTradeNo())); } /** @@ -311,7 +312,7 @@ public class AliPayController { */ @RequestMapping("close") public Map close(QueryOrder order) { - return service.close(order.getTradeNo(), order.getOutTradeNo()); + return service.close(new AssistOrder(order.getTradeNo(), order.getOutTradeNo())); } /** 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 3e23fd6..93b09e7 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 @@ -26,6 +26,7 @@ import com.egzosn.pay.ali.bean.AliTransactionType; import com.egzosn.pay.common.api.PayConfigStorage; import com.egzosn.pay.common.api.PayMessageInterceptor; import com.egzosn.pay.common.api.PayService; +import com.egzosn.pay.common.bean.AssistOrder; import com.egzosn.pay.common.bean.MethodType; import com.egzosn.pay.common.bean.NoticeParams; import com.egzosn.pay.common.bean.PayMessage; @@ -419,7 +420,7 @@ public class PayController { @RequestMapping("query") public Map query(QueryOrder order) { PayResponse payResponse = service.getPayResponse(order.getPayId()); - return payResponse.getService().query(order.getTradeNo(), order.getOutTradeNo()); + return payResponse.getService().query(new AssistOrder(order.getTradeNo(), order.getOutTradeNo())); } /** * 查询 @@ -444,7 +445,7 @@ public class PayController { @RequestMapping("close") public Map close(QueryOrder order) { PayResponse payResponse = service.getPayResponse(order.getPayId()); - return payResponse.getService().close(order.getTradeNo(), order.getOutTradeNo()); + return payResponse.getService().close(new AssistOrder(order.getTradeNo(), order.getOutTradeNo())); } /** diff --git a/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/PayoneerPayController.java b/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/PayoneerPayController.java index e6580b2..caaee05 100644 --- a/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/PayoneerPayController.java +++ b/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/PayoneerPayController.java @@ -13,6 +13,7 @@ import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.RestController; +import com.egzosn.pay.common.bean.AssistOrder; import com.egzosn.pay.common.bean.DefaultCurType; import com.egzosn.pay.common.bean.PayOrder; import com.egzosn.pay.common.bean.RefundOrder; @@ -160,7 +161,7 @@ public class PayoneerPayController { */ @RequestMapping("query") public Map query(QueryOrder order) { - return service.query(order.getTradeNo(), order.getOutTradeNo()); + return service.query(new AssistOrder(order.getTradeNo(), order.getOutTradeNo())); } @@ -172,7 +173,7 @@ public class PayoneerPayController { */ @RequestMapping("close") public Map close(QueryOrder order) { - return service.close(order.getTradeNo(), order.getOutTradeNo()); + return service.close(new AssistOrder(order.getTradeNo(), order.getOutTradeNo())); } /** diff --git a/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/UnionPayController.java b/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/UnionPayController.java index 297bd06..46e1b4a 100644 --- a/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/UnionPayController.java +++ b/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/UnionPayController.java @@ -2,9 +2,23 @@ package com.egzosn.pay.demo.controller; -import com.egzosn.pay.common.api.PayService; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.math.BigDecimal; +import java.util.HashMap; +import java.util.Map; +import java.util.UUID; + +import javax.imageio.ImageIO; +import javax.servlet.http.HttpServletRequest; + +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import static com.egzosn.pay.union.bean.UnionTransactionType.WEB; + +import com.egzosn.pay.common.bean.AssistOrder; import com.egzosn.pay.common.bean.CertStoreType; -import com.egzosn.pay.common.bean.MethodType; import com.egzosn.pay.common.bean.PayOrder; import com.egzosn.pay.common.bean.RefundOrder; import com.egzosn.pay.common.http.HttpConfigStorage; @@ -17,21 +31,6 @@ import com.egzosn.pay.union.bean.UnionRefundResult; import com.egzosn.pay.union.bean.UnionTransactionType; import com.egzosn.pay.web.support.HttpRequestNoticeParams; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RestController; - -import javax.annotation.PostConstruct; -import javax.imageio.ImageIO; -import javax.servlet.http.HttpServletRequest; -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.math.BigDecimal; -import java.util.HashMap; -import java.util.Map; -import java.util.UUID; - -import static com.egzosn.pay.union.bean.UnionTransactionType.WEB; - /** * 银联相关 * @@ -271,7 +270,7 @@ public class UnionPayController { */ @RequestMapping("query") public Map query(QueryOrder order) { - return service.query(order.getTradeNo(), order.getOutTradeNo()); + return service.query(new AssistOrder(order.getTradeNo(), order.getOutTradeNo())); } diff --git a/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/WxPayController.java b/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/WxPayController.java index 3ac4464..879494e 100644 --- a/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/WxPayController.java +++ b/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/WxPayController.java @@ -303,7 +303,7 @@ public class WxPayController { */ @RequestMapping("query") public Map query(QueryOrder order) { - return service.query(order.getTradeNo(), order.getOutTradeNo()); + return service.query(new AssistOrder(order.getTradeNo(), order.getOutTradeNo())); } @@ -315,7 +315,7 @@ public class WxPayController { */ @RequestMapping("close") public Map close(QueryOrder order) { - return service.close(order.getTradeNo(), order.getOutTradeNo()); + return service.close(new AssistOrder(order.getTradeNo(), order.getOutTradeNo())); } /** diff --git a/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/WxV3CombinePayController.java b/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/WxV3CombinePayController.java index 6431bb2..ce65d86 100644 --- a/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/WxV3CombinePayController.java +++ b/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/WxV3CombinePayController.java @@ -15,8 +15,10 @@ import javax.servlet.http.HttpServletRequest; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; +import com.egzosn.pay.common.bean.AssistOrder; import com.egzosn.pay.common.bean.CertStoreType; import com.egzosn.pay.common.bean.RefundOrder; +import com.egzosn.pay.common.bean.RefundResult; import com.egzosn.pay.demo.request.QueryOrder; import com.egzosn.pay.demo.service.handler.WxV3CombinePayMessageHandler; import com.egzosn.pay.web.support.HttpRequestNoticeParams; @@ -51,8 +53,8 @@ public class WxV3CombinePayController { wxPayConfigStorage.setMchId("1602947765"); //V3密钥 https://pay.weixin.qq.com/wiki/doc/apiv3/wechatpay/wechatpay3_2.shtml wxPayConfigStorage.setV3ApiKey("9bd8f0e7af4841299d782406b7774f57"); - wxPayConfigStorage.setNotifyUrl("http://sailinmu.iok.la/wxV3/payBack.json"); - wxPayConfigStorage.setReturnUrl("http://sailinmu.iok.la/wxV3/payBack.json"); + wxPayConfigStorage.setNotifyUrl("http://sailinmu.iok.la/wxV3combine/payBack.json"); + wxPayConfigStorage.setReturnUrl("http://sailinmu.iok.la/wxV3combine/payBack.json"); wxPayConfigStorage.setInputCharset("utf-8"); //使用证书时设置为true wxPayConfigStorage.setCertSign(true); @@ -93,7 +95,7 @@ public class WxV3CombinePayController { subOrder.setAmount(new CombineAmount(121)); subOrder.setOutTradeNo("子单商户订单号 "); subOrder.setDescription("商品描述"); - subOrder.setSubMchid("二级商户商户号,由微信支付生成并下发。服务商子商户的商户号,被合单方。直连商户不用传二级商户号。 "); + subOrder.setSubMchid("服务商必填----二级商户商户号,由微信支付生成并下发。服务商子商户的商户号,被合单方。直连商户不用传二级商户号。 "); subOrders.add(subOrder); order.setSubOrders(subOrders); order.setTransactionType(WxTransactionType.COMBINE_H5); @@ -121,7 +123,7 @@ public class WxV3CombinePayController { subOrder.setAmount(new CombineAmount(111)); subOrder.setOutTradeNo("子单商户订单号 "); subOrder.setDescription("商品描述"); - subOrder.setSubMchid("二级商户商户号,由微信支付生成并下发。服务商子商户的商户号,被合单方。直连商户不用传二级商户号。 "); + subOrder.setSubMchid("服务商必填----二级商户商户号,由微信支付生成并下发。服务商子商户的商户号,被合单方。直连商户不用传二级商户号。 "); subOrders.add(subOrder); order.setSubOrders(subOrders); Map orderInfo = service.orderInfo(order); @@ -150,7 +152,7 @@ public class WxV3CombinePayController { subOrder.setAmount(new CombineAmount(211)); subOrder.setOutTradeNo("子单商户订单号 "); subOrder.setDescription("商品描述"); - subOrder.setSubMchid("二级商户商户号,由微信支付生成并下发。服务商子商户的商户号,被合单方。直连商户不用传二级商户号。 "); + subOrder.setSubMchid("服务商必填----二级商户商户号,由微信支付生成并下发。服务商子商户的商户号,被合单方。直连商户不用传二级商户号。 "); subOrders.add(subOrder); order.setSubOrders(subOrders); Map orderInfo = service.orderInfo(order); @@ -179,7 +181,7 @@ public class WxV3CombinePayController { subOrder.setAmount(new CombineAmount(131)); subOrder.setOutTradeNo("子单商户订单号 "); subOrder.setDescription("商品描述"); - subOrder.setSubMchid("二级商户商户号,由微信支付生成并下发。服务商子商户的商户号,被合单方。直连商户不用传二级商户号。 "); + subOrder.setSubMchid("服务商必填----二级商户商户号,由微信支付生成并下发。服务商子商户的商户号,被合单方。直连商户不用传二级商户号。 "); subOrders.add(subOrder); order.setSubOrders(subOrders); @@ -210,7 +212,7 @@ public class WxV3CombinePayController { subOrder.setAmount(new CombineAmount(115)); subOrder.setOutTradeNo("子单商户订单号 "); subOrder.setDescription("商品描述"); - subOrder.setSubMchid("二级商户商户号,由微信支付生成并下发。服务商子商户的商户号,被合单方。直连商户不用传二级商户号。 "); + subOrder.setSubMchid("服务商必填----二级商户商户号,由微信支付生成并下发。服务商子商户的商户号,被合单方。直连商户不用传二级商户号。 "); subOrders.add(subOrder); order.setSubOrders(subOrders); //获取对应的支付账户操作工具(可根据账户id) @@ -244,7 +246,7 @@ public class WxV3CombinePayController { */ @RequestMapping("query") public Map query(QueryOrder order) { - return service.query(order.getTradeNo(), order.getOutTradeNo()); + return service.query(new AssistOrder(order.getTradeNo(), order.getOutTradeNo())); } @@ -262,7 +264,7 @@ public class WxV3CombinePayController { CombineSubOrder subOrder = new CombineSubOrder(); subOrder.setMchid("子单商户号"); subOrder.setOutTradeNo("子单商户订单号 "); - subOrder.setSubMchid("二级商户商户号,由微信支付生成并下发。服务商子商户的商户号,被合单方。直连商户不用传二级商户号。 "); + subOrder.setSubMchid("服务商必填----二级商户商户号,由微信支付生成并下发。服务商子商户的商户号,被合单方。直连商户不用传二级商户号。 "); subOrders.add(subOrder); order.setSubOrders(subOrders); return service.close(order); @@ -275,7 +277,7 @@ public class WxV3CombinePayController { * @return 返回支付方申请退款后的结果 */ @RequestMapping("refund") - public WxRefundResult refund(RefundOrder order) { + public RefundResult refund(RefundOrder order) { return service.refund(order); } diff --git a/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/WxV3PayController.java b/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/WxV3PayController.java index f987656..621c83f 100644 --- a/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/WxV3PayController.java +++ b/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/WxV3PayController.java @@ -16,9 +16,11 @@ import javax.servlet.http.HttpServletRequest; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; +import com.egzosn.pay.common.bean.AssistOrder; import com.egzosn.pay.common.bean.CertStoreType; import com.egzosn.pay.common.bean.PayOrder; import com.egzosn.pay.common.bean.RefundOrder; +import com.egzosn.pay.common.bean.RefundResult; import com.egzosn.pay.demo.request.QueryOrder; import com.egzosn.pay.demo.service.handler.WxV3PayMessageHandler; import com.egzosn.pay.web.support.HttpRequestNoticeParams; @@ -178,7 +180,7 @@ public class WxV3PayController { */ @RequestMapping("query") public Map query(QueryOrder order) { - return service.query(order.getTradeNo(), order.getOutTradeNo()); + return service.query(new AssistOrder(order.getTradeNo(), order.getOutTradeNo())); } @@ -190,7 +192,7 @@ public class WxV3PayController { */ @RequestMapping("close") public Map close(QueryOrder order) { - return service.close(order.getTradeNo(), order.getOutTradeNo()); + return service.close(new AssistOrder(order.getTradeNo(), order.getOutTradeNo())); } /** @@ -200,7 +202,7 @@ public class WxV3PayController { * @return 返回支付方申请退款后的结果 */ @RequestMapping("refund") - public WxRefundResult refund(RefundOrder order) { + public RefundResult refund(RefundOrder order) { return service.refund(order); } diff --git a/pay-java-demo/src/main/java/com/egzosn/pay/demo/service/handler/WxV3CombinePayMessageHandler.java b/pay-java-demo/src/main/java/com/egzosn/pay/demo/service/handler/WxV3CombinePayMessageHandler.java index cdbcd0c..ef6c341 100644 --- a/pay-java-demo/src/main/java/com/egzosn/pay/demo/service/handler/WxV3CombinePayMessageHandler.java +++ b/pay-java-demo/src/main/java/com/egzosn/pay/demo/service/handler/WxV3CombinePayMessageHandler.java @@ -11,18 +11,18 @@ import com.egzosn.pay.common.api.PayMessageHandler; import com.egzosn.pay.common.api.PayService; import com.egzosn.pay.common.bean.PayOutMessage; import com.egzosn.pay.common.exception.PayErrorException; -import com.egzosn.pay.wx.v3.bean.combine.CombinePayMessage; +import com.egzosn.pay.wx.v3.bean.sharing.ProfitSharingPayMessage; /** * 微信合单支付回调处理器 * Created by ZaoSheng on 2016/6/1. */ -public class WxV3CombinePayMessageHandler implements PayMessageHandler { +public class WxV3CombinePayMessageHandler implements PayMessageHandler { private final Logger LOG = LoggerFactory.getLogger(DefaultPayMessageHandler.class); @Override - public PayOutMessage handle(CombinePayMessage payMessage, Map context, PayService payService) throws PayErrorException { + public PayOutMessage handle(ProfitSharingPayMessage payMessage, Map context, PayService payService) throws PayErrorException { LOG.info("回调支付消息处理器,回调消息:{}", JSON.toJSONString(payMessage)); return payService.successPayOutMessage(payMessage); } -- Gitee From 9b82bcabefed0bb584a8c1889e70e26acb1ac770 Mon Sep 17 00:00:00 2001 From: egzosn Date: Wed, 6 Oct 2021 23:32:37 +0800 Subject: [PATCH 235/299] =?UTF-8?q?=E5=88=86=E8=B4=A6=E5=8A=9F=E8=83=BD?= =?UTF-8?q?=E5=AE=9E=E7=8E=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../WxV3ProfitSharingController.java | 264 ++++++++++++++++++ .../WxV3ProfitSharingMessageHandler.java | 29 ++ 2 files changed, 293 insertions(+) create mode 100644 pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/WxV3ProfitSharingController.java create mode 100644 pay-java-demo/src/main/java/com/egzosn/pay/demo/service/handler/WxV3ProfitSharingMessageHandler.java diff --git a/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/WxV3ProfitSharingController.java b/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/WxV3ProfitSharingController.java new file mode 100644 index 0000000..299fea1 --- /dev/null +++ b/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/WxV3ProfitSharingController.java @@ -0,0 +1,264 @@ + +package com.egzosn.pay.demo.controller; + + +import java.io.IOException; +import java.math.BigDecimal; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +import javax.annotation.PostConstruct; +import javax.servlet.http.HttpServletRequest; + +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import com.egzosn.pay.common.bean.AssistOrder; +import com.egzosn.pay.common.bean.CertStoreType; +import com.egzosn.pay.common.bean.RefundResult; +import com.egzosn.pay.demo.request.QueryOrder; +import com.egzosn.pay.demo.service.handler.WxV3ProfitSharingMessageHandler; +import com.egzosn.pay.web.support.HttpRequestNoticeParams; +import com.egzosn.pay.wx.v3.api.WxPayConfigStorage; +import com.egzosn.pay.wx.v3.api.WxProfitSharingService; +import com.egzosn.pay.wx.v3.bean.WxProfitSharingTransactionType; +import com.egzosn.pay.wx.v3.bean.sharing.ProfitSharingBillType; +import com.egzosn.pay.wx.v3.bean.sharing.ProfitSharingOrder; +import com.egzosn.pay.wx.v3.bean.sharing.ProfitSharingReturnOrder; +import com.egzosn.pay.wx.v3.bean.sharing.Receiver; +import com.egzosn.pay.wx.v3.bean.sharing.ReceiverType; +import com.egzosn.pay.wx.v3.bean.sharing.ReceiversOrder; +import com.egzosn.pay.wx.v3.bean.sharing.RelationType; + +/** + * 微信V3分账发起支付入口 + * + * @author egan + * email egzosn@gmail.com + * date 2016/11/18 0:25 + */ +@RestController +@RequestMapping("wxV3profitSharing") +public class WxV3ProfitSharingController { + + private WxProfitSharingService service = null; + + @PostConstruct //没有证书的情况下注释掉,避免启动报错 + public void init() { + WxPayConfigStorage wxPayConfigStorage = new WxPayConfigStorage(); + wxPayConfigStorage.setAppId("wxc7b993ff15a9f26c"); + wxPayConfigStorage.setMchId("1602947765"); + //V3密钥 https://pay.weixin.qq.com/wiki/doc/apiv3/wechatpay/wechatpay3_2.shtml + wxPayConfigStorage.setV3ApiKey("9bd8f0e7af4841299d782406b7774f57"); + wxPayConfigStorage.setNotifyUrl("http://sailinmu.iok.la/wxV3profitSharing/payBack.json"); + wxPayConfigStorage.setReturnUrl("http://sailinmu.iok.la/wxV3profitSharing/payBack.json"); + wxPayConfigStorage.setInputCharset("utf-8"); + //使用证书时设置为true + wxPayConfigStorage.setCertSign(true); + //商户API证书 https://pay.weixin.qq.com/wiki/doc/apiv3/wechatpay/wechatpay3_1.shtml + wxPayConfigStorage.setApiClientKeyP12("yifenli_mall.p12"); + wxPayConfigStorage.setCertStoreType(CertStoreType.PATH); + service = new WxProfitSharingService(wxPayConfigStorage); + //设置回调消息处理 + //TODO {@link com.egzosn.pay.demo.controller.WxPayController#payBack} + service.setPayMessageHandler(new WxV3ProfitSharingMessageHandler()); + } + + + /** + * 请求分账API + * + * @return 分账 + */ + @RequestMapping(value = "orders") + public Map orders() { + ProfitSharingOrder order = new ProfitSharingOrder(); + order.setTransactionType(WxProfitSharingTransactionType.ORDERS); + order.setSubMchid("服务商必填----微信支付分配的子商户号,即分账的出资商户号。"); + order.setSubAppid("服务商必填----微信分配的子商户公众账号ID,分账接收方类型包含PERSONAL_SUB_OPENID时必填。"); + order.setTransactionId("微信支付订单号"); + order.setOutOrderNo("务商系统内部的分账单号,在服务商系统内部唯一,同一分账单号多次请求等同一次。只能是数字、大小写字母_-|*@ "); + List receivers = new ArrayList<>(); + Receiver receiver = new Receiver(); + //1、MERCHANT_ID:商户号 + //2、PERSONAL_OPENID:个人openid(由父商户APPID转换得到) + //3、PERSONAL_SUB_OPENID: 个人sub_openid(由子商户APPID转换得到) + //示例值:MERCHANT_ID + receiver.setType(ReceiverType.MERCHANT_ID); + receiver.setName("分账个人接收方姓名"); + receiver.setAccount("分账接收方账号"); + //分账金额,单位为分,只能为整数,不能超过原订单支付金额及最大分账比例金额 + receiver.setAmount(1); + receiver.setDescription("分账的原因描述,分账账单中需要体现"); + + order.setReceivers(receivers); + //1、如果为true,该笔订单剩余未分账的金额会解冻回分账方商户; + //2、如果为false,该笔订单剩余未分账的金额不会解冻回分账方商户,可以对该笔订单再次进行分账。 + order.setUnfreezeUnsplit(true); + Map orderInfo = service.orderInfo(order); + orderInfo.put("code", 0); + return orderInfo; + } + + + /** + * 获取支付预订单信息 + * + * @return 支付预订单信息 + */ + @RequestMapping("unfreeze") + public Map unfreeze() { + ProfitSharingOrder order = new ProfitSharingOrder(); + order.setTransactionType(WxProfitSharingTransactionType.ORDERS_UNFREEZE); + order.setSubMchid("服务商必填----微信支付分配的子商户号,即分账的出资商户号。"); + order.setTransactionId("微信支付订单号"); + order.setOutOrderNo("务商系统内部的分账单号,在服务商系统内部唯一,同一分账单号多次请求等同一次。只能是数字、大小写字母_-|*@ "); + order.setSubject("分账的原因描述,分账账单中需要体现"); + return service.orderInfo(order); + } + /** + * 添加分账接收方 + * + * @return 添加分账接收方 + */ + @RequestMapping("add") + public Map add() { + ReceiversOrder order = new ReceiversOrder(); + order.setTransactionType(WxProfitSharingTransactionType.RECEIVERS_ADD); + order.setSubMchid("服务商必填----微信支付分配的子商户号,即分账的出资商户号。"); + order.setSubAppid("服务商必填----子商户应用ID"); + //分账接收方类型: + order.setType(ReceiverType.MERCHANT_ID); + order.setAccount("分账接收方账号"); + order.setName("分账个人接收方姓名"); + //与分账方的关系类型 + order.setRelationType(RelationType.BRAND); + order.setCustomRelation("自定义的分账关系,子商户与接收方具体的关系,本字段最多10个字。\n" + + "当字段relation_type的值为CUSTOM时,本字段必填;\n" + + "当字段relation_type的值不为CUSTOM时,本字段无需填写"); + return service.orderInfo(order); + } + /** + * 删除分账接收方 + * + * @return 删除分账接收方 + */ + @RequestMapping("delete") + public Map delete() { + ReceiversOrder order = new ReceiversOrder(); + order.setTransactionType(WxProfitSharingTransactionType.RECEIVERS_DELETE); + order.setSubMchid("服务商必填----微信支付分配的子商户号,即分账的出资商户号。"); + order.setSubAppid("服务商必填----子商户应用ID"); + //分账接收方类型: + order.setType(ReceiverType.MERCHANT_ID); + order.setAccount("分账接收方账号"); + + + return service.orderInfo(order); + } + + + /** + * 分账回调地址 + * + * @param request 请求 + * @return 是否成功 + *

+ * 业务处理在对应的PayMessageHandler里面处理,在哪里设置PayMessageHandler,详情查看{@link com.egzosn.pay.common.api.PayService#setPayMessageHandler(com.egzosn.pay.common.api.PayMessageHandler)} + *

+ * 如果未设置 {@link com.egzosn.pay.common.api.PayMessageHandler} 那么会使用默认的 {@link com.egzosn.pay.common.api.DefaultPayMessageHandler} + * @throws IOException IOException + */ + @RequestMapping(value = "payBack.json") + public String payBack(HttpServletRequest request) { + //业务处理在对应的PayMessageHandler里面处理,在哪里设置PayMessageHandler,详情查看com.egzosn.pay.common.api.PayService.setPayMessageHandler() + return service.payBack(new HttpRequestNoticeParams(request)).toMessage(); + } + + + /** + * 查询分账结果 + * + * @return 返回查询回来的结果集,支付方原值返回 + */ + @RequestMapping("query") + public Map query() { + AssistOrder assistOrder = new AssistOrder(); + assistOrder.setOutTradeNo("商户分账单号"); + assistOrder.setTradeNo("微信订单号"); + assistOrder.addAttr("sub_mchid", "服务商必填---子商户号"); + return service.query(assistOrder); + } + + + /** + * 查询剩余待分金额 + * + * @return 返回查询回来的结果集,支付方原值返回 + */ + @RequestMapping("amounts") + public Map amounts() { + AssistOrder assistOrder = new AssistOrder(); + assistOrder.setTradeNo("微信订单号"); + return service.query(assistOrder); + } + + + /** + * 查询最大分账比例 + * 服务商使用 + * @return 返回查询回来的结果集,支付方原值返回 + */ + @RequestMapping("merchantConfigs") + public Map merchantConfigs() { + AssistOrder assistOrder = new AssistOrder(); + assistOrder.addAttr("sub_mchid", "服务商必填---子商户号"); + return service.query(assistOrder); + } + + /** + * 请求分账回退 + * + * @return 返回支付方申请退款后的结果 + */ + @RequestMapping("refund") + public RefundResult refund() { + ProfitSharingReturnOrder returnOrder = new ProfitSharingReturnOrder(); + returnOrder.setSubMchid("服务商必填---子商户号"); + returnOrder.setRefundNo("商户回退单号"); + returnOrder.setTradeNo("微信分账单号"); + returnOrder.setOutTradeNo("商户分账单号"); + returnOrder.setRefundAmount(new BigDecimal(1)); + + returnOrder.setDescription("分账回退的原因描述"); + return service.refund(returnOrder); + } + + /** + * 查询分账回退结果 + * + * @return 返回支付方查询退款后的结果 + */ + @RequestMapping("refundquery") + public Map refundquery() { + ProfitSharingReturnOrder returnOrder = new ProfitSharingReturnOrder(); + returnOrder.setSubMchid("服务商必填---子商户号"); + returnOrder.setRefundNo("商户回退单号"); + returnOrder.setOutTradeNo("商户分账单号"); + return service.refundquery(returnOrder); + } + + /** + * 下载对账单 + * + * @param order 订单的请求体 + * @return 返回支付方下载对账单的结果 + */ + @RequestMapping("downloadbill") + public Object downloadBill(QueryOrder order) { + return service.downloadBill(order.getBillDate(), ProfitSharingBillType.GZIP); + } + + +} diff --git a/pay-java-demo/src/main/java/com/egzosn/pay/demo/service/handler/WxV3ProfitSharingMessageHandler.java b/pay-java-demo/src/main/java/com/egzosn/pay/demo/service/handler/WxV3ProfitSharingMessageHandler.java new file mode 100644 index 0000000..ad083cc --- /dev/null +++ b/pay-java-demo/src/main/java/com/egzosn/pay/demo/service/handler/WxV3ProfitSharingMessageHandler.java @@ -0,0 +1,29 @@ +package com.egzosn.pay.demo.service.handler; + +import java.util.Map; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.alibaba.fastjson.JSON; +import com.egzosn.pay.common.api.DefaultPayMessageHandler; +import com.egzosn.pay.common.api.PayMessageHandler; +import com.egzosn.pay.common.api.PayService; +import com.egzosn.pay.common.bean.PayOutMessage; +import com.egzosn.pay.common.exception.PayErrorException; +import com.egzosn.pay.wx.v3.bean.sharing.ProfitSharingPayMessage; + +/** + * 微信合单支付回调处理器 + * Created by ZaoSheng on 2016/6/1. + */ +public class WxV3ProfitSharingMessageHandler implements PayMessageHandler { + + private final Logger LOG = LoggerFactory.getLogger(DefaultPayMessageHandler.class); + + @Override + public PayOutMessage handle(ProfitSharingPayMessage payMessage, Map context, PayService payService) throws PayErrorException { + LOG.info("回调支付消息处理器,回调消息:{}", JSON.toJSONString(payMessage)); + return payService.successPayOutMessage(payMessage); + } +} -- Gitee From 6d05d587e6d64037258614577a1dd9ce5b990953 Mon Sep 17 00:00:00 2001 From: egzosn Date: Wed, 6 Oct 2021 23:37:56 +0800 Subject: [PATCH 236/299] =?UTF-8?q?=E5=88=86=E8=B4=A6=E5=8A=9F=E8=83=BD?= =?UTF-8?q?=E5=9B=9E=E8=B0=83=E5=A4=84=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../pay/wx/v3/api/WxProfitSharingService.java | 37 ------------------- 1 file changed, 37 deletions(-) diff --git a/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/api/WxProfitSharingService.java b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/api/WxProfitSharingService.java index c38ec42..275b034 100644 --- a/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/api/WxProfitSharingService.java +++ b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/api/WxProfitSharingService.java @@ -173,43 +173,6 @@ public class WxProfitSharingService extends WxPayService implements ProfitSharin } - /** - * 将请求参数或者请求流转化为 Map - * - * @param request 通知请求 - * @return 获得回调的请求参数 - */ - @Override - public NoticeParams getNoticeParams(NoticeRequest request) { - throw new PayErrorException(new WxPayError("", "分账不支持方式")); - } - - /** - * 获取输出消息,用户返回给支付端 - * - * @param code 状态 - * @param message 消息 - * @return 返回输出消息 - */ - @Override - public PayOutMessage getPayOutMessage(String code, String message) { - return PayOutMessage.JSON().content("code", code).content("message", message).build(); - } - - - /** - * 获取成功输出消息,用户返回给支付端 - * 主要用于拦截器中返回 - * - * @param payMessage 支付回调消息 - * @return 返回输出消息 - */ - @Override - public PayOutMessage successPayOutMessage(PayMessage payMessage) { - return getPayOutMessage("SUCCESS", "成功"); - } - - /** * 获取输出消息,用户返回给支付端, 针对于web端 * -- Gitee From b9a7cff0a22f3626e92807f7e6b06149dec05d4e Mon Sep 17 00:00:00 2001 From: egzosn Date: Wed, 6 Oct 2021 23:44:33 +0800 Subject: [PATCH 237/299] 2.14.3 --- README.md | 2 +- pay-java-ali/pom.xml | 2 +- pay-java-baidu/pom.xml | 2 +- pay-java-common/pom.xml | 2 +- pay-java-demo/pom.xml | 2 +- pay-java-fuiou/pom.xml | 2 +- pay-java-payoneer/pom.xml | 2 +- pay-java-paypal/pom.xml | 2 +- pay-java-union/pom.xml | 2 +- pay-java-web-support/pom.xml | 2 +- pay-java-wx-youdian/pom.xml | 2 +- pay-java-wx/pom.xml | 2 +- pay-java-yiji/pom.xml | 2 +- pom.xml | 4 ++-- 14 files changed, 15 insertions(+), 15 deletions(-) diff --git a/README.md b/README.md index ff69992..cd46d5d 100644 --- a/README.md +++ b/README.md @@ -21,7 +21,7 @@ com.egzosn {module-name} - 2.14.2 + 2.14.3 ``` diff --git a/pay-java-ali/pom.xml b/pay-java-ali/pom.xml index aa08115..2cf7477 100644 --- a/pay-java-ali/pom.xml +++ b/pay-java-ali/pom.xml @@ -5,7 +5,7 @@ pay-java-parent com.egzosn - 2.14.3-SNAPSHOT + 2.14.3 4.0.0 pay-java-ali diff --git a/pay-java-baidu/pom.xml b/pay-java-baidu/pom.xml index e9f0b16..0939ce4 100644 --- a/pay-java-baidu/pom.xml +++ b/pay-java-baidu/pom.xml @@ -5,7 +5,7 @@ pay-java-parent com.egzosn - 2.14.3-SNAPSHOT + 2.14.3 4.0.0 pay-java-baidu diff --git a/pay-java-common/pom.xml b/pay-java-common/pom.xml index e20e284..6bddee0 100644 --- a/pay-java-common/pom.xml +++ b/pay-java-common/pom.xml @@ -5,7 +5,7 @@ pay-java-parent com.egzosn - 2.14.3-SNAPSHOT + 2.14.3 4.0.0 jar diff --git a/pay-java-demo/pom.xml b/pay-java-demo/pom.xml index 30f7350..6ad2995 100644 --- a/pay-java-demo/pom.xml +++ b/pay-java-demo/pom.xml @@ -5,7 +5,7 @@ pay-java-parent com.egzosn - 2.14.3-SNAPSHOT + 2.14.3 4.0.0 war diff --git a/pay-java-fuiou/pom.xml b/pay-java-fuiou/pom.xml index 9d1672c..cfc08fc 100644 --- a/pay-java-fuiou/pom.xml +++ b/pay-java-fuiou/pom.xml @@ -5,7 +5,7 @@ pay-java-parent com.egzosn - 2.14.3-SNAPSHOT + 2.14.3 4.0.0 pay-java-fuiou diff --git a/pay-java-payoneer/pom.xml b/pay-java-payoneer/pom.xml index 45fdcfb..26784ed 100644 --- a/pay-java-payoneer/pom.xml +++ b/pay-java-payoneer/pom.xml @@ -5,7 +5,7 @@ pay-java-parent com.egzosn - 2.14.3-SNAPSHOT + 2.14.3 4.0.0 pay-java-payoneer diff --git a/pay-java-paypal/pom.xml b/pay-java-paypal/pom.xml index c4a49d9..2a042ca 100644 --- a/pay-java-paypal/pom.xml +++ b/pay-java-paypal/pom.xml @@ -5,7 +5,7 @@ pay-java-parent com.egzosn - 2.14.3-SNAPSHOT + 2.14.3 4.0.0 diff --git a/pay-java-union/pom.xml b/pay-java-union/pom.xml index 64b8020..9bf99b8 100644 --- a/pay-java-union/pom.xml +++ b/pay-java-union/pom.xml @@ -5,7 +5,7 @@ pay-java-parent com.egzosn - 2.14.3-SNAPSHOT + 2.14.3 4.0.0 diff --git a/pay-java-web-support/pom.xml b/pay-java-web-support/pom.xml index 36f8dbb..af39355 100644 --- a/pay-java-web-support/pom.xml +++ b/pay-java-web-support/pom.xml @@ -5,7 +5,7 @@ pay-java-parent com.egzosn - 2.14.3-SNAPSHOT + 2.14.3 4.0.0 jar diff --git a/pay-java-wx-youdian/pom.xml b/pay-java-wx-youdian/pom.xml index ca68448..c92c64b 100644 --- a/pay-java-wx-youdian/pom.xml +++ b/pay-java-wx-youdian/pom.xml @@ -5,7 +5,7 @@ pay-java-parent com.egzosn - 2.14.3-SNAPSHOT + 2.14.3 4.0.0 pay-java-wx-youdian diff --git a/pay-java-wx/pom.xml b/pay-java-wx/pom.xml index 52dd4cf..d48f95c 100644 --- a/pay-java-wx/pom.xml +++ b/pay-java-wx/pom.xml @@ -5,7 +5,7 @@ pay-java-parent com.egzosn - 2.14.3-SNAPSHOT + 2.14.3 4.0.0 pay-java-wx diff --git a/pay-java-yiji/pom.xml b/pay-java-yiji/pom.xml index b736fa5..4ce9397 100644 --- a/pay-java-yiji/pom.xml +++ b/pay-java-yiji/pom.xml @@ -5,7 +5,7 @@ pay-java-parent com.egzosn - 2.14.3-SNAPSHOT + 2.14.3 4.0.0 diff --git a/pom.xml b/pom.xml index 2189a4a..63391c0 100644 --- a/pom.xml +++ b/pom.xml @@ -7,7 +7,7 @@ com.egzosn pay-java-parent pom - 2.14.3-SNAPSHOT + 2.14.3 Pay Java - Parent Pay Java Parent @@ -70,7 +70,7 @@ - 2.14.3-SNAPSHOT + 2.14.3 4.5.4 1.2.17 1.2.73 -- Gitee From d74b1f79eada2f3869f25c5b5a0974edaff05b60 Mon Sep 17 00:00:00 2001 From: egzosn Date: Wed, 6 Oct 2021 23:57:09 +0800 Subject: [PATCH 238/299] 2.14.3 --- README.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index cd46d5d..8387ad3 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -全能第三方支付对接Java开发工具包.优雅的轻量级支付模块集成支付对接支付整合(微信,支付宝,银联,友店,富友,跨境支付paypal,payoneer(P卡派安盈)易极付)app,扫码,网页支付刷卡付条码付刷脸付转账红包服务商模式、支持多种支付类型多支付账户,支付与业务完全剥离,简单几行代码即可实现支付,简单快速完成支付模块的开发,可轻松嵌入到任何系统里 目前仅是一个开发工具包(即SDK),只提供简单Web实现,建议使用maven或gradle引用本项目即可使用本SDK提供的各种支付相关的功能 +全能第三方支付对接Java开发工具包.优雅的轻量级支付模块集成支付对接支付整合(微信,支付宝,银联,友店,富友,跨境支付paypal,payoneer(P卡派安盈)易极付)app,扫码,网页支付刷卡付条码付刷脸付转账红包服务商模式,微信分账,合并支付、支持多种支付类型多支付账户,支付与业务完全剥离,简单几行代码即可实现支付,简单快速完成支付模块的开发,可轻松嵌入到任何系统里 目前仅是一个开发工具包(即SDK),只提供简单Web实现,建议使用maven或gradle引用本项目即可使用本SDK提供的各种支付相关的功能 ### 特性 @@ -41,6 +41,9 @@ ###### 支付教程 * [基础模块支付宝微信讲解](https://gitee.com/egzosn/pay-java-parent/wikis/Home) + * [微信V3,查看demo/WxV3PayController](pay-java-demo?dir=1&filepath=pay-java-demo) + * [微信合并支付,查看demo/WxV3CombinePayController](pay-java-demo?dir=1&filepath=pay-java-demo) + * [微信分账,查看demo/WxV3ProfitSharingController](pay-java-demo?dir=1&filepath=pay-java-demo) * [银联](pay-java-union?dir=1&filepath=pay-java-union) * [payoneer](pay-java-payoneer?dir=1&filepath=pay-java-payoneer) * [paypal](pay-java-paypal?dir=1&filepath=pay-java-paypal) -- Gitee From 50244c67b1836f62907e76c6ca919531f07b4b51 Mon Sep 17 00:00:00 2001 From: egzosn Date: Mon, 18 Oct 2021 21:07:37 +0800 Subject: [PATCH 239/299] =?UTF-8?q?=E5=AF=B9=E5=BF=85=E9=A1=BB=E8=A6=81?= =?UTF-8?q?=E4=BD=BF=E7=94=A8=E8=AF=81=E4=B9=A6=E7=9A=84=E6=94=AF=E4=BB=98?= =?UTF-8?q?=E6=96=B9=E5=BC=8F=E7=A7=BB=E9=99=A4isCertSign=E5=88=A4?= =?UTF-8?q?=E6=96=AD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pay-java-ali/pom.xml | 2 +- .../src/main/java/com/egzosn/pay/ali/api/AliPayService.java | 2 +- .../main/java/com/egzosn/pay/union/api/UnionPayService.java | 6 ++---- .../java/com/egzosn/pay/wx/v3/api/WxPayConfigStorage.java | 2 +- 4 files changed, 5 insertions(+), 7 deletions(-) diff --git a/pay-java-ali/pom.xml b/pay-java-ali/pom.xml index 2cf7477..aac8ea0 100644 --- a/pay-java-ali/pom.xml +++ b/pay-java-ali/pom.xml @@ -5,7 +5,7 @@ pay-java-parent com.egzosn - 2.14.3 + 2.14.3-b 4.0.0 pay-java-ali 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 126b386..274ef1d 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 @@ -128,7 +128,7 @@ public class AliPayService extends BasePayService { public boolean verify(NoticeParams noticeParams) { final Map params = noticeParams.getBody(); if (params.get(SIGN) == null) { - LOG.debug("支付宝支付异常:params:" + params); + LOG.debug("支付宝支付异常:params:{}", params); return false; } diff --git a/pay-java-union/src/main/java/com/egzosn/pay/union/api/UnionPayService.java b/pay-java-union/src/main/java/com/egzosn/pay/union/api/UnionPayService.java index d236603..1cda4f4 100644 --- a/pay-java-union/src/main/java/com/egzosn/pay/union/api/UnionPayService.java +++ b/pay-java-union/src/main/java/com/egzosn/pay/union/api/UnionPayService.java @@ -108,13 +108,11 @@ public class UnionPayService extends BasePayService { @Override public UnionPayService setPayConfigStorage(UnionPayConfigStorage payConfigStorage) { this.payConfigStorage = payConfigStorage; - if (!payConfigStorage.isCertSign()) { + if (null != certDescriptor) { return this; } - if (null == certDescriptor) { - certDescriptor = new CertDescriptor(); - } try { + certDescriptor = new CertDescriptor(); certDescriptor.initPrivateSignCert(payConfigStorage.getKeyPrivateCertInputStream(), payConfigStorage.getKeyPrivateCertPwd(), "PKCS12"); certDescriptor.initPublicCert(payConfigStorage.getAcpMiddleCertInputStream()); certDescriptor.initRootCert(payConfigStorage.getAcpRootCertInputStream()); diff --git a/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/api/WxPayConfigStorage.java b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/api/WxPayConfigStorage.java index 66ccf53..b668a63 100644 --- a/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/api/WxPayConfigStorage.java +++ b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/api/WxPayConfigStorage.java @@ -244,7 +244,7 @@ public class WxPayConfigStorage extends BasePayConfigStorage { * 初始化证书信息 */ public void loadCertEnvironment() { - if (!isCertSign() || null != this.certEnvironment) { + if (null != this.certEnvironment) { return; } try (InputStream apiKeyCert = certStoreType.getInputStream(getApiClientKeyP12())) { -- Gitee From f4d689d6ba124528d73278fa1dcbddf38b9cf68c Mon Sep 17 00:00:00 2001 From: egzosn Date: Mon, 18 Oct 2021 21:09:20 +0800 Subject: [PATCH 240/299] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E6=9C=AA=E4=BD=BF?= =?UTF-8?q?=E7=94=A8pay-java-web-support=E7=BB=84=E4=BB=B6=E7=9A=84?= =?UTF-8?q?=E5=9B=9E=E8=B0=83=E5=AF=B9=E8=B1=A1=E6=97=B6json=E5=BA=8F?= =?UTF-8?q?=E5=88=97=E5=8C=96=E7=A9=BA=E6=8C=87=E9=92=88=E5=BC=82=E5=B8=B8?= =?UTF-8?q?bug?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../main/java/com/egzosn/pay/common/api/BasePayService.java | 4 ++-- .../main/java/com/egzosn/pay/common/bean/NoticeParams.java | 3 +++ 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/pay-java-common/src/main/java/com/egzosn/pay/common/api/BasePayService.java b/pay-java-common/src/main/java/com/egzosn/pay/common/api/BasePayService.java index bd8b5bc..df18742 100644 --- a/pay-java-common/src/main/java/com/egzosn/pay/common/api/BasePayService.java +++ b/pay-java-common/src/main/java/com/egzosn/pay/common/api/BasePayService.java @@ -350,7 +350,7 @@ public abstract class BasePayService implements Pay */ @Override public Map transfer(TransferOrder order) { - return new HashMap<>(0); + return Collections.emptyMap(); } /** @@ -362,7 +362,7 @@ public abstract class BasePayService implements Pay */ @Override public Map transferQuery(String outNo, String tradeNo) { - return new HashMap<>(0); + return Collections.emptyMap(); } /** diff --git a/pay-java-common/src/main/java/com/egzosn/pay/common/bean/NoticeParams.java b/pay-java-common/src/main/java/com/egzosn/pay/common/bean/NoticeParams.java index 7cd494f..54f59a6 100644 --- a/pay-java-common/src/main/java/com/egzosn/pay/common/bean/NoticeParams.java +++ b/pay-java-common/src/main/java/com/egzosn/pay/common/bean/NoticeParams.java @@ -70,6 +70,9 @@ public class NoticeParams { } public Enumeration getHeaderNames() { + if (null == headers){ + return Collections.enumeration(Collections.emptySet()); + } return Collections.enumeration(this.headers.keySet()); } -- Gitee From 6e75b724280efd2cca1ac1e71060f884622c43ac Mon Sep 17 00:00:00 2001 From: egzosn Date: Mon, 18 Oct 2021 21:10:47 +0800 Subject: [PATCH 241/299] =?UTF-8?q?=E5=BE=AE=E4=BF=A1V2=E9=80=80=E6=AC=BE?= =?UTF-8?q?=E3=80=90=E9=80=80=E6=AC=BE=E4=BB=A3=E9=87=91=E5=88=B8ID?= =?UTF-8?q?=E3=80=91=E7=B1=BB=E5=9E=8B=E9=94=99=E8=AF=AF=E4=BF=AE=E6=AD=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pay-java-web-support/pom.xml | 12 +++++------- .../java/com/egzosn/pay/wx/bean/WxRefundResult.java | 6 +++--- 2 files changed, 8 insertions(+), 10 deletions(-) diff --git a/pay-java-web-support/pom.xml b/pay-java-web-support/pom.xml index af39355..c8bf104 100644 --- a/pay-java-web-support/pom.xml +++ b/pay-java-web-support/pom.xml @@ -5,19 +5,17 @@ pay-java-parent com.egzosn - 2.14.3 + 2.14.3-b 4.0.0 jar pay-java-web-support Pay Java Web 相关支持 + - 4.3.4.RELEASE - 4.10 - 4.3.6.Final + 4.0.1 - com.egzosn @@ -26,10 +24,10 @@ javax.servlet javax.servlet-api - 3.1.0 + provided + ${servlet-api.version} - diff --git a/pay-java-wx/src/main/java/com/egzosn/pay/wx/bean/WxRefundResult.java b/pay-java-wx/src/main/java/com/egzosn/pay/wx/bean/WxRefundResult.java index ce3ade7..7fadc2e 100644 --- a/pay-java-wx/src/main/java/com/egzosn/pay/wx/bean/WxRefundResult.java +++ b/pay-java-wx/src/main/java/com/egzosn/pay/wx/bean/WxRefundResult.java @@ -199,7 +199,7 @@ public class WxRefundResult extends BaseRefundResult { * 这里只接收0的,其余请自行获取 */ @JSONField(name = "coupon_refund_id_0") - private Integer couponRefundId0; + private String couponRefundId0; /** * 获取退款请求结果状态码 * @@ -480,11 +480,11 @@ public class WxRefundResult extends BaseRefundResult { this.couponRefundCount = couponRefundCount; } - public Integer getCouponRefundId0() { + public String getCouponRefundId0() { return couponRefundId0; } - public void setCouponRefundId0(Integer couponRefundId0) { + public void setCouponRefundId0(String couponRefundId0) { this.couponRefundId0 = couponRefundId0; } -- Gitee From d264723d4c4bcbc1f37395a52024eb3fcf7239cc Mon Sep 17 00:00:00 2001 From: egzosn Date: Mon, 18 Oct 2021 21:11:11 +0800 Subject: [PATCH 242/299] 2.14.3-b --- README.md | 5 +-- pay-java-baidu/pom.xml | 8 ++--- .../pay/baidu/api/BaiduPayServiceTest.java | 12 +++---- pay-java-common/pom.xml | 2 +- pay-java-demo/pom.xml | 31 +++++++------------ pay-java-fuiou/pom.xml | 2 +- pay-java-payoneer/pom.xml | 2 +- pay-java-paypal/pom.xml | 2 +- pay-java-union/pom.xml | 2 +- pay-java-wx-youdian/pom.xml | 2 +- pay-java-wx/pom.xml | 2 +- pay-java-yiji/pom.xml | 2 +- pom.xml | 15 ++++----- 13 files changed, 37 insertions(+), 50 deletions(-) diff --git a/README.md b/README.md index 8387ad3..33e6d9d 100644 --- a/README.md +++ b/README.md @@ -8,9 +8,10 @@ 4. 简单快速完成支付模块的开发 5. 支持多种支付类型多支付账户扩展 -### 本项目包含 3 个部分: +### 本项目包含 4 个部分: 1. pay-java-common 公共lib,支付核心与规范定义 + 2. pay-java-web-support web支持包,目前已实现回调相关 2. pay-java-demo 具体的支付demo 3. pay-java-* 具体的支付实现库 @@ -21,7 +22,7 @@ com.egzosn {module-name} - 2.14.3 + 2.14.3-b ``` diff --git a/pay-java-baidu/pom.xml b/pay-java-baidu/pom.xml index 0939ce4..4fd5363 100644 --- a/pay-java-baidu/pom.xml +++ b/pay-java-baidu/pom.xml @@ -5,7 +5,7 @@ pay-java-parent com.egzosn - 2.14.3 + 2.14.3-b 4.0.0 pay-java-baidu @@ -17,11 +17,7 @@ pay-java-common - - org.junit.jupiter - junit-jupiter - test - + diff --git a/pay-java-baidu/src/test/java/com/egzosn/pay/baidu/api/BaiduPayServiceTest.java b/pay-java-baidu/src/test/java/com/egzosn/pay/baidu/api/BaiduPayServiceTest.java index 6f9b5c2..43f31a1 100644 --- a/pay-java-baidu/src/test/java/com/egzosn/pay/baidu/api/BaiduPayServiceTest.java +++ b/pay-java-baidu/src/test/java/com/egzosn/pay/baidu/api/BaiduPayServiceTest.java @@ -1,7 +1,5 @@ package com.egzosn.pay.baidu.api; -import org.junit.jupiter.api.Test; - /** * Created by hocgin on 2019/11/24. * email: hocgin@gmail.com @@ -9,16 +7,16 @@ import org.junit.jupiter.api.Test; * @author hocgin */ public class BaiduPayServiceTest { - - @Test - public void orderInfo() { + + + public static void main(String[] args) { BaiduPayConfigStorage configStorage = new BaiduPayConfigStorage(); configStorage.setAppid("APP ID"); configStorage.setAppKey("APP KEY"); configStorage.setDealId("DEAL ID"); configStorage.setKeyPublic("KEY PUBLIC"); - + BaiduPayService payService = new BaiduPayService(configStorage); - // payService.refund() } + } diff --git a/pay-java-common/pom.xml b/pay-java-common/pom.xml index 6bddee0..5ea672b 100644 --- a/pay-java-common/pom.xml +++ b/pay-java-common/pom.xml @@ -5,7 +5,7 @@ pay-java-parent com.egzosn - 2.14.3 + 2.14.3-b 4.0.0 jar diff --git a/pay-java-demo/pom.xml b/pay-java-demo/pom.xml index 6ad2995..769a561 100644 --- a/pay-java-demo/pom.xml +++ b/pay-java-demo/pom.xml @@ -5,7 +5,7 @@ pay-java-parent com.egzosn - 2.14.3 + 2.14.3-b 4.0.0 war @@ -16,9 +16,9 @@ 4.3.4.RELEASE 4.10 4.3.6.Final + 4.0.1 - @@ -31,11 +31,7 @@ pay-java-wx ${pay.version} - - com.egzosn - pay-java-web-support - ${pay.version} - + com.egzosn pay-java-ali @@ -63,20 +59,10 @@ ${pay.version} - - - javax.servlet - javax.servlet-api - provided - 3.1.0 - - javax.servlet - jsp-api - 2.0 - provided + com.egzosn + pay-java-web-support - org.springframework @@ -87,7 +73,12 @@ - + + javax.servlet + javax.servlet-api + provided + ${servlet-api.version} + com.fasterxml.jackson.core jackson-databind diff --git a/pay-java-fuiou/pom.xml b/pay-java-fuiou/pom.xml index cfc08fc..e8dc77d 100644 --- a/pay-java-fuiou/pom.xml +++ b/pay-java-fuiou/pom.xml @@ -5,7 +5,7 @@ pay-java-parent com.egzosn - 2.14.3 + 2.14.3-b 4.0.0 pay-java-fuiou diff --git a/pay-java-payoneer/pom.xml b/pay-java-payoneer/pom.xml index 26784ed..89b6f58 100644 --- a/pay-java-payoneer/pom.xml +++ b/pay-java-payoneer/pom.xml @@ -5,7 +5,7 @@ pay-java-parent com.egzosn - 2.14.3 + 2.14.3-b 4.0.0 pay-java-payoneer diff --git a/pay-java-paypal/pom.xml b/pay-java-paypal/pom.xml index 2a042ca..5ec9d96 100644 --- a/pay-java-paypal/pom.xml +++ b/pay-java-paypal/pom.xml @@ -5,7 +5,7 @@ pay-java-parent com.egzosn - 2.14.3 + 2.14.3-b 4.0.0 diff --git a/pay-java-union/pom.xml b/pay-java-union/pom.xml index 9bf99b8..7c6eaca 100644 --- a/pay-java-union/pom.xml +++ b/pay-java-union/pom.xml @@ -5,7 +5,7 @@ pay-java-parent com.egzosn - 2.14.3 + 2.14.3-b 4.0.0 diff --git a/pay-java-wx-youdian/pom.xml b/pay-java-wx-youdian/pom.xml index c92c64b..228e9b1 100644 --- a/pay-java-wx-youdian/pom.xml +++ b/pay-java-wx-youdian/pom.xml @@ -5,7 +5,7 @@ pay-java-parent com.egzosn - 2.14.3 + 2.14.3-b 4.0.0 pay-java-wx-youdian diff --git a/pay-java-wx/pom.xml b/pay-java-wx/pom.xml index d48f95c..f636f1a 100644 --- a/pay-java-wx/pom.xml +++ b/pay-java-wx/pom.xml @@ -5,7 +5,7 @@ pay-java-parent com.egzosn - 2.14.3 + 2.14.3-b 4.0.0 pay-java-wx diff --git a/pay-java-yiji/pom.xml b/pay-java-yiji/pom.xml index 4ce9397..d8abf2e 100644 --- a/pay-java-yiji/pom.xml +++ b/pay-java-yiji/pom.xml @@ -5,7 +5,7 @@ pay-java-parent com.egzosn - 2.14.3 + 2.14.3-b 4.0.0 diff --git a/pom.xml b/pom.xml index 63391c0..4b558b7 100644 --- a/pom.xml +++ b/pom.xml @@ -7,7 +7,7 @@ com.egzosn pay-java-parent pom - 2.14.3 + 2.14.3-b Pay Java - Parent Pay Java Parent @@ -70,12 +70,12 @@ - 2.14.3 + 2.14.3-b 4.5.4 1.2.17 1.2.73 3.3.1 - 5.5.1 + 4.0.1 @@ -107,7 +107,6 @@ - org.slf4j @@ -122,10 +121,12 @@ core ${zxing.version} + - org.junit.jupiter - junit-jupiter - ${junit.version} + javax.servlet + javax.servlet-api + provided + ${servlet-api.version} -- Gitee From 2d74c2a959cbeed67a39be6fa8ee1ecd7aa6d19c Mon Sep 17 00:00:00 2001 From: egzosn Date: Thu, 28 Oct 2021 13:00:03 +0800 Subject: [PATCH 243/299] =?UTF-8?q?H5=E6=94=AF=E4=BB=98=E9=81=97=E6=BC=8F?= =?UTF-8?q?=E5=9C=BA=E6=99=AF=E4=BF=A1=E6=81=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pay-java-ali/pom.xml | 4 ++-- pay-java-baidu/pom.xml | 2 +- pay-java-common/pom.xml | 2 +- pay-java-demo/pom.xml | 2 +- pay-java-fuiou/pom.xml | 2 +- pay-java-payoneer/pom.xml | 2 +- pay-java-paypal/pom.xml | 2 +- pay-java-union/pom.xml | 2 +- pay-java-web-support/pom.xml | 2 +- pay-java-wx-youdian/pom.xml | 2 +- pay-java-wx/pom.xml | 8 ++++++-- .../java/com/egzosn/pay/wx/v3/bean/WxTransactionType.java | 1 + pay-java-yiji/pom.xml | 2 +- pom.xml | 5 +++-- 14 files changed, 22 insertions(+), 16 deletions(-) diff --git a/pay-java-ali/pom.xml b/pay-java-ali/pom.xml index aac8ea0..467543a 100644 --- a/pay-java-ali/pom.xml +++ b/pay-java-ali/pom.xml @@ -5,7 +5,7 @@ pay-java-parent com.egzosn - 2.14.3-b + 2.14.3-b1 4.0.0 pay-java-ali @@ -22,7 +22,7 @@ org.bouncycastle bcprov-jdk15on - 1.59 + ${bcprov-jdk15on.version} diff --git a/pay-java-baidu/pom.xml b/pay-java-baidu/pom.xml index 4fd5363..23af590 100644 --- a/pay-java-baidu/pom.xml +++ b/pay-java-baidu/pom.xml @@ -5,7 +5,7 @@ pay-java-parent com.egzosn - 2.14.3-b + 2.14.3-b1 4.0.0 pay-java-baidu diff --git a/pay-java-common/pom.xml b/pay-java-common/pom.xml index 5ea672b..042a30c 100644 --- a/pay-java-common/pom.xml +++ b/pay-java-common/pom.xml @@ -5,7 +5,7 @@ pay-java-parent com.egzosn - 2.14.3-b + 2.14.3-b1 4.0.0 jar diff --git a/pay-java-demo/pom.xml b/pay-java-demo/pom.xml index 769a561..3ddbb47 100644 --- a/pay-java-demo/pom.xml +++ b/pay-java-demo/pom.xml @@ -5,7 +5,7 @@ pay-java-parent com.egzosn - 2.14.3-b + 2.14.3-b1 4.0.0 war diff --git a/pay-java-fuiou/pom.xml b/pay-java-fuiou/pom.xml index e8dc77d..03c375b 100644 --- a/pay-java-fuiou/pom.xml +++ b/pay-java-fuiou/pom.xml @@ -5,7 +5,7 @@ pay-java-parent com.egzosn - 2.14.3-b + 2.14.3-b1 4.0.0 pay-java-fuiou diff --git a/pay-java-payoneer/pom.xml b/pay-java-payoneer/pom.xml index 89b6f58..a6ac61a 100644 --- a/pay-java-payoneer/pom.xml +++ b/pay-java-payoneer/pom.xml @@ -5,7 +5,7 @@ pay-java-parent com.egzosn - 2.14.3-b + 2.14.3-b1 4.0.0 pay-java-payoneer diff --git a/pay-java-paypal/pom.xml b/pay-java-paypal/pom.xml index 5ec9d96..37b129e 100644 --- a/pay-java-paypal/pom.xml +++ b/pay-java-paypal/pom.xml @@ -5,7 +5,7 @@ pay-java-parent com.egzosn - 2.14.3-b + 2.14.3-b1 4.0.0 diff --git a/pay-java-union/pom.xml b/pay-java-union/pom.xml index 7c6eaca..5024eec 100644 --- a/pay-java-union/pom.xml +++ b/pay-java-union/pom.xml @@ -5,7 +5,7 @@ pay-java-parent com.egzosn - 2.14.3-b + 2.14.3-b1 4.0.0 diff --git a/pay-java-web-support/pom.xml b/pay-java-web-support/pom.xml index c8bf104..03c1aab 100644 --- a/pay-java-web-support/pom.xml +++ b/pay-java-web-support/pom.xml @@ -5,7 +5,7 @@ pay-java-parent com.egzosn - 2.14.3-b + 2.14.3-b1 4.0.0 jar diff --git a/pay-java-wx-youdian/pom.xml b/pay-java-wx-youdian/pom.xml index 228e9b1..ac48d3d 100644 --- a/pay-java-wx-youdian/pom.xml +++ b/pay-java-wx-youdian/pom.xml @@ -5,7 +5,7 @@ pay-java-parent com.egzosn - 2.14.3-b + 2.14.3-b1 4.0.0 pay-java-wx-youdian diff --git a/pay-java-wx/pom.xml b/pay-java-wx/pom.xml index f636f1a..13a2089 100644 --- a/pay-java-wx/pom.xml +++ b/pay-java-wx/pom.xml @@ -5,7 +5,7 @@ pay-java-parent com.egzosn - 2.14.3-b + 2.14.3-b1 4.0.0 pay-java-wx @@ -20,7 +20,11 @@ - + + org.bouncycastle + bcprov-jdk15on + ${bcprov-jdk15on.version} + diff --git a/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/WxTransactionType.java b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/WxTransactionType.java index ead42a1..ea883a7 100644 --- a/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/WxTransactionType.java +++ b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/WxTransactionType.java @@ -75,6 +75,7 @@ public enum WxTransactionType implements TransactionType { if (null == h5Info) { sceneInfo.setH5Info(new H5Info(order.getWapName(), order.getWapUrl())); } + parameters.put("scene_info", sceneInfo); } }, diff --git a/pay-java-yiji/pom.xml b/pay-java-yiji/pom.xml index d8abf2e..b52209c 100644 --- a/pay-java-yiji/pom.xml +++ b/pay-java-yiji/pom.xml @@ -5,7 +5,7 @@ pay-java-parent com.egzosn - 2.14.3-b + 2.14.3-b1 4.0.0 diff --git a/pom.xml b/pom.xml index 4b558b7..e5a6f99 100644 --- a/pom.xml +++ b/pom.xml @@ -7,7 +7,7 @@ com.egzosn pay-java-parent pom - 2.14.3-b + 2.14.3-b1 Pay Java - Parent Pay Java Parent @@ -70,12 +70,13 @@ - 2.14.3-b + 2.14.3-b1 4.5.4 1.2.17 1.2.73 3.3.1 4.0.1 + 1.59 -- Gitee From e10812cacc11db2d7e0b8dc9603a578aed157c48 Mon Sep 17 00:00:00 2001 From: egzosn Date: Sun, 31 Oct 2021 21:01:27 +0800 Subject: [PATCH 244/299] =?UTF-8?q?=E7=BD=91=E9=A1=B5=E6=94=AF=E4=BB=98?= =?UTF-8?q?=E7=9B=B8=E5=85=B3=EF=BC=8C=E5=A6=82=E6=9E=9C=E6=9C=89=E5=87=BA?= =?UTF-8?q?=E7=8E=B0'(=E5=8D=95=E5=BC=95=E5=8F=B7)=E5=88=99=E7=9B=B4?= =?UTF-8?q?=E6=8E=A5=E5=89=94=E9=99=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../main/java/com/egzosn/pay/common/api/BasePayService.java | 6 ++++++ .../com/egzosn/pay/demo/controller/AliPayController.java | 4 ++-- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/pay-java-common/src/main/java/com/egzosn/pay/common/api/BasePayService.java b/pay-java-common/src/main/java/com/egzosn/pay/common/api/BasePayService.java index df18742..52ccdbb 100644 --- a/pay-java-common/src/main/java/com/egzosn/pay/common/api/BasePayService.java +++ b/pay-java-common/src/main/java/com/egzosn/pay/common/api/BasePayService.java @@ -162,6 +162,12 @@ public abstract class BasePayService implements Pay */ @Override public String toPay(O order) { + if (StringUtils.isNotEmpty(order.getSubject()) && order.getSubject().contains("'")){ + order.setSubject(order.getSubject().replace("'","")); + } + if (StringUtils.isNotEmpty(order.getBody()) && order.getBody().contains("'")){ + order.setBody(order.getBody().replace("'","")); + } Map orderInfo = orderInfo(order); return buildRequest(orderInfo, MethodType.POST); } diff --git a/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/AliPayController.java b/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/AliPayController.java index 8911a83..df8f2e6 100644 --- a/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/AliPayController.java +++ b/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/AliPayController.java @@ -123,7 +123,7 @@ public class AliPayController { @RequestMapping(value = "toPay.html", produces = "text/html;charset=UTF-8") public String toPay(BigDecimal price) { //及时收款 - PayOrder order = new PayOrder("订单title", "摘要", null == price ? BigDecimal.valueOf(0.01) : price, UUID.randomUUID().toString().replace("-", ""), AliTransactionType.PAGE); + PayOrder order = new PayOrder("订单title", "摘'要", null == price ? BigDecimal.valueOf(0.01) : price, UUID.randomUUID().toString().replace("-", ""), AliTransactionType.PAGE); //WAP // PayOrder order = new PayOrder("订单title", "摘要", null == price ? BigDecimal.valueOf(0.01) : price, UUID.randomUUID().toString().replace("-", ""), AliTransactionType.WAP); @@ -200,7 +200,7 @@ public class AliPayController { //支付结果 Map params = service.microPay(order); //校验 - if (service.verify(params)) { + if (service.verify(new NoticeParams(params))) { //支付校验通过后的处理 //......业务逻辑处理块........ -- Gitee From 7acb047c45122ecc71a09c2161dc9ee2dc35293b Mon Sep 17 00:00:00 2001 From: egzosn Date: Sun, 31 Oct 2021 21:14:36 +0800 Subject: [PATCH 245/299] =?UTF-8?q?=E5=8F=91=E7=89=88=E4=B8=AD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 33e6d9d..42a6764 100644 --- a/README.md +++ b/README.md @@ -22,7 +22,7 @@ com.egzosn {module-name} - 2.14.3-b + 2.14.3-b1 ``` -- Gitee From 556c3e766ec9f412da72182396c6132a0a2fe760 Mon Sep 17 00:00:00 2001 From: egzosn Date: Fri, 12 Nov 2021 20:58:24 +0800 Subject: [PATCH 246/299] =?UTF-8?q?=E5=8A=A0=E5=85=A5TLSv1.2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/egzosn/pay/common/http/HttpRequestTemplate.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pay-java-common/src/main/java/com/egzosn/pay/common/http/HttpRequestTemplate.java b/pay-java-common/src/main/java/com/egzosn/pay/common/http/HttpRequestTemplate.java index 218a26e..ba60626 100644 --- a/pay-java-common/src/main/java/com/egzosn/pay/common/http/HttpRequestTemplate.java +++ b/pay-java-common/src/main/java/com/egzosn/pay/common/http/HttpRequestTemplate.java @@ -153,7 +153,7 @@ public class HttpRequestTemplate { //指定TLS版本 sslsf = new SSLConnectionSocketFactory( - sslcontext, new String[]{"TLSv1"}, null, + sslcontext, new String[]{"TLSv1","TLSv1.2"}, null, new DefaultHostnameVerifier()); return sslsf; -- Gitee From e4ca2a0e7db87fd395250c1f43002a07703e56c9 Mon Sep 17 00:00:00 2001 From: egzosn Date: Fri, 12 Nov 2021 20:59:08 +0800 Subject: [PATCH 247/299] =?UTF-8?q?=E5=8A=A0=E5=85=A5TLSv1.2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pay-java-ali/pom.xml | 2 +- pay-java-baidu/pom.xml | 2 +- pay-java-common/pom.xml | 2 +- pay-java-demo/pom.xml | 2 +- pay-java-fuiou/pom.xml | 2 +- pay-java-payoneer/pom.xml | 2 +- pay-java-paypal/pom.xml | 2 +- pay-java-union/pom.xml | 2 +- pay-java-web-support/pom.xml | 2 +- pay-java-wx-youdian/pom.xml | 2 +- pay-java-wx/pom.xml | 2 +- pay-java-yiji/pom.xml | 2 +- pom.xml | 4 ++-- 13 files changed, 14 insertions(+), 14 deletions(-) diff --git a/pay-java-ali/pom.xml b/pay-java-ali/pom.xml index 467543a..566487f 100644 --- a/pay-java-ali/pom.xml +++ b/pay-java-ali/pom.xml @@ -5,7 +5,7 @@ pay-java-parent com.egzosn - 2.14.3-b1 + 2.14.3-b2 4.0.0 pay-java-ali diff --git a/pay-java-baidu/pom.xml b/pay-java-baidu/pom.xml index 23af590..4d1c45f 100644 --- a/pay-java-baidu/pom.xml +++ b/pay-java-baidu/pom.xml @@ -5,7 +5,7 @@ pay-java-parent com.egzosn - 2.14.3-b1 + 2.14.3-b2 4.0.0 pay-java-baidu diff --git a/pay-java-common/pom.xml b/pay-java-common/pom.xml index 042a30c..7da4d06 100644 --- a/pay-java-common/pom.xml +++ b/pay-java-common/pom.xml @@ -5,7 +5,7 @@ pay-java-parent com.egzosn - 2.14.3-b1 + 2.14.3-b2 4.0.0 jar diff --git a/pay-java-demo/pom.xml b/pay-java-demo/pom.xml index 3ddbb47..348317b 100644 --- a/pay-java-demo/pom.xml +++ b/pay-java-demo/pom.xml @@ -5,7 +5,7 @@ pay-java-parent com.egzosn - 2.14.3-b1 + 2.14.3-b2 4.0.0 war diff --git a/pay-java-fuiou/pom.xml b/pay-java-fuiou/pom.xml index 03c375b..08cea48 100644 --- a/pay-java-fuiou/pom.xml +++ b/pay-java-fuiou/pom.xml @@ -5,7 +5,7 @@ pay-java-parent com.egzosn - 2.14.3-b1 + 2.14.3-b2 4.0.0 pay-java-fuiou diff --git a/pay-java-payoneer/pom.xml b/pay-java-payoneer/pom.xml index a6ac61a..86ed571 100644 --- a/pay-java-payoneer/pom.xml +++ b/pay-java-payoneer/pom.xml @@ -5,7 +5,7 @@ pay-java-parent com.egzosn - 2.14.3-b1 + 2.14.3-b2 4.0.0 pay-java-payoneer diff --git a/pay-java-paypal/pom.xml b/pay-java-paypal/pom.xml index 37b129e..da3ae52 100644 --- a/pay-java-paypal/pom.xml +++ b/pay-java-paypal/pom.xml @@ -5,7 +5,7 @@ pay-java-parent com.egzosn - 2.14.3-b1 + 2.14.3-b2 4.0.0 diff --git a/pay-java-union/pom.xml b/pay-java-union/pom.xml index 5024eec..47d0abc 100644 --- a/pay-java-union/pom.xml +++ b/pay-java-union/pom.xml @@ -5,7 +5,7 @@ pay-java-parent com.egzosn - 2.14.3-b1 + 2.14.3-b2 4.0.0 diff --git a/pay-java-web-support/pom.xml b/pay-java-web-support/pom.xml index 03c1aab..021cb2c 100644 --- a/pay-java-web-support/pom.xml +++ b/pay-java-web-support/pom.xml @@ -5,7 +5,7 @@ pay-java-parent com.egzosn - 2.14.3-b1 + 2.14.3-b2 4.0.0 jar diff --git a/pay-java-wx-youdian/pom.xml b/pay-java-wx-youdian/pom.xml index ac48d3d..3f2bd4a 100644 --- a/pay-java-wx-youdian/pom.xml +++ b/pay-java-wx-youdian/pom.xml @@ -5,7 +5,7 @@ pay-java-parent com.egzosn - 2.14.3-b1 + 2.14.3-b2 4.0.0 pay-java-wx-youdian diff --git a/pay-java-wx/pom.xml b/pay-java-wx/pom.xml index 13a2089..e6dd416 100644 --- a/pay-java-wx/pom.xml +++ b/pay-java-wx/pom.xml @@ -5,7 +5,7 @@ pay-java-parent com.egzosn - 2.14.3-b1 + 2.14.3-b2 4.0.0 pay-java-wx diff --git a/pay-java-yiji/pom.xml b/pay-java-yiji/pom.xml index b52209c..c0f4aa7 100644 --- a/pay-java-yiji/pom.xml +++ b/pay-java-yiji/pom.xml @@ -5,7 +5,7 @@ pay-java-parent com.egzosn - 2.14.3-b1 + 2.14.3-b2 4.0.0 diff --git a/pom.xml b/pom.xml index e5a6f99..3c1d50c 100644 --- a/pom.xml +++ b/pom.xml @@ -7,7 +7,7 @@ com.egzosn pay-java-parent pom - 2.14.3-b1 + 2.14.3-b2 Pay Java - Parent Pay Java Parent @@ -70,7 +70,7 @@ - 2.14.3-b1 + 2.14.3-b2 4.5.4 1.2.17 1.2.73 -- Gitee From 6ab618ca44bd460ae18f1e152f7943c292698d12 Mon Sep 17 00:00:00 2001 From: egzosn Date: Sat, 20 Nov 2021 23:49:58 +0800 Subject: [PATCH 248/299] =?UTF-8?q?1.=E5=BE=AE=E4=BF=A1=E6=94=AF=E4=BB=98?= =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E9=83=A8=E5=88=86jdk=E6=97=A0=E6=B3=95?= =?UTF-8?q?=E5=8A=A0=E8=BD=BD=E8=AF=81=E4=B9=A6=E9=97=AE=E9=A2=98=202.?= =?UTF-8?q?=E5=BE=AE=E4=BF=A1=E4=B8=8B=E8=BD=BD=E5=AF=B9=E8=B4=A6=E5=8D=95?= =?UTF-8?q?=E6=8A=A5=E6=97=A0=E6=B3=95=E7=B1=BB=E5=9E=8B=E8=BD=AC=E6=8D=A2?= =?UTF-8?q?=E9=97=AE=E9=A2=98=203.=E5=BE=AE=E4=BF=A1=E9=80=80=E6=AC=BE?= =?UTF-8?q?=E9=9D=9E=E5=BF=85=E5=A1=AB=E5=8F=82=E6=95=B0=E5=BF=BD=E7=95=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pay-java-ali/pom.xml | 1 - .../pay/ali/utils/AntCertificationUtil.java | 32 +++++----- .../pay/common/http/ClientHttpRequest.java | 61 ++++++++++--------- .../controller/WxV3CombinePayController.java | 2 +- .../demo/controller/WxV3PayController.java | 12 ++-- .../WxV3ProfitSharingController.java | 2 +- pay-java-wx/pom.xml | 2 +- .../wx/v3/api/DefaultWxPayAssistService.java | 3 +- .../egzosn/pay/wx/v3/api/WxPayService.java | 4 +- .../pay/wx/v3/utils/AntCertificationUtil.java | 11 +++- pom.xml | 6 +- 11 files changed, 76 insertions(+), 60 deletions(-) diff --git a/pay-java-ali/pom.xml b/pay-java-ali/pom.xml index 566487f..9014156 100644 --- a/pay-java-ali/pom.xml +++ b/pay-java-ali/pom.xml @@ -22,7 +22,6 @@ org.bouncycastle bcprov-jdk15on - ${bcprov-jdk15on.version} diff --git a/pay-java-ali/src/main/java/com/egzosn/pay/ali/utils/AntCertificationUtil.java b/pay-java-ali/src/main/java/com/egzosn/pay/ali/utils/AntCertificationUtil.java index b3057cd..e21af86 100644 --- a/pay-java-ali/src/main/java/com/egzosn/pay/ali/utils/AntCertificationUtil.java +++ b/pay-java-ali/src/main/java/com/egzosn/pay/ali/utils/AntCertificationUtil.java @@ -24,9 +24,9 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import org.bouncycastle.jce.provider.BouncyCastleProvider; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.bouncycastle.jce.provider.BouncyCastleProvider; import com.egzosn.pay.common.bean.result.PayException; import com.egzosn.pay.common.exception.PayErrorException; @@ -38,17 +38,19 @@ import com.egzosn.pay.common.util.str.StringUtils; * 证书文件可信校验 * * @author junying.wjy - * @version $Id: AntCertificationUtil.java, v 0.1 2019-07-29 下午04:46 junying.wjy Exp $ - * * @author egan update 2020/10/12 - * + * @version $Id: AntCertificationUtil.java, v 0.1 2019-07-29 下午04:46 junying.wjy Exp $ */ public class AntCertificationUtil { private static final Logger LOGGER = LoggerFactory.getLogger(AntCertificationUtil.class); + static { - Security.removeProvider("SunEC"); - Security.addProvider(new BouncyCastleProvider()); + if (null == Security.getProvider("BC")) { + Security.removeProvider("SunEC"); + Security.addProvider(new BouncyCastleProvider()); + } } + /** * 验证证书是否可信 * @@ -141,7 +143,7 @@ public class AntCertificationUtil { //验证证书链 for (int i = 1; i < certs.length; i++) { X509Certificate cert = certs[i]; - if (!checkValidity(cert)){ + if (!checkValidity(cert)) { return false; } verifySignature(prev.getPublicKey(), cert); @@ -155,7 +157,7 @@ public class AntCertificationUtil { /** * 验证证书链是否是信任证书库中证书签发的 * - * @param cert 目标验证证书 + * @param cert 目标验证证书 * @return 验证结果 */ private static boolean checkValidity(X509Certificate cert) { @@ -172,13 +174,11 @@ public class AntCertificationUtil { } - - private static void verifySignature(PublicKey publicKey, X509Certificate cert){ + private static void verifySignature(PublicKey publicKey, X509Certificate cert) { try { cert.verify(publicKey); - } - catch (GeneralSecurityException e) { - throw new PayErrorException(new PayException("证书校验失败", e.getMessage())); + } catch (GeneralSecurityException e) { + throw new PayErrorException(new PayException("证书校验失败", e.getMessage())); } } @@ -281,7 +281,7 @@ public class AntCertificationUtil { addressingDown(issuerMap, certChain, subject); } - private static X509Certificate[] readPemCertChain(String cert){ + private static X509Certificate[] readPemCertChain(String cert) { ByteArrayInputStream inputStream = new ByteArrayInputStream(cert.getBytes()); CertificateFactory factory = null; try { @@ -305,7 +305,7 @@ public class AntCertificationUtil { String rootCertSN = null; try { X509Certificate[] x509Certificates = readPemCertChain(rootCertContent); - if (null == x509Certificates){ + if (null == x509Certificates) { return null; } MessageDigest md = MessageDigest.getInstance("MD5"); @@ -383,8 +383,6 @@ public class AntCertificationUtil { } - - public static String readFromInputStream(InputStream cert) { try { return new String(IOUtils.toByteArray(cert), StandardCharsets.UTF_8); 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 49681ed..99ec689 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 @@ -317,6 +317,38 @@ public class ClientHttpRequest extends HttpEntityEnclosingRequestBase impleme * @throws IOException 响应类型文本转换时抛出异常 */ private T toBean(HttpEntity entity, String[] contentType) throws IOException { + + + //是否为 输入流 + if (InputStream.class.isAssignableFrom(responseType)) { + ByteArrayOutputStream os = new ByteArrayOutputStream(); + entity.writeTo(os); + return (T) new ByteArrayInputStream(os.toByteArray()); + } + //是否为 字节数数组 + if (byte[].class.isAssignableFrom(responseType)) { + ByteArrayOutputStream os = new ByteArrayOutputStream(); + entity.writeTo(os); + return (T) os.toByteArray(); + } + //输出流 + if (OutputStream.class.isAssignableFrom(responseType)) { + try { + OutputStream t; + if (responseType == OutputStream.class){ + t= new ByteArrayOutputStream(); + }else { + t = (OutputStream) responseType.newInstance(); + } + entity.writeTo( t); + return (T) t; + } catch (InstantiationException e) { + throw new PayErrorException(new PayException("InstantiationException", e.getMessage())); + } catch (IllegalAccessException e) { + throw new PayErrorException(new PayException("IllegalAccessException", e.getMessage())); + } + } + //判断内容类型是否为文本类型 if (isText(contentType[0])) { /* String charset = "UTF-8"; @@ -359,35 +391,6 @@ public class ClientHttpRequest extends HttpEntityEnclosingRequestBase impleme throw new PayErrorException(new PayException("failure", "类型转化异常,contentType:" + entity.getContentType().getValue(), result)); } - //是否为 输入流 - if (InputStream.class.isAssignableFrom(responseType)) { - ByteArrayOutputStream os = new ByteArrayOutputStream(); - entity.writeTo(os); - return (T) new ByteArrayInputStream(os.toByteArray()); - } - //是否为 字节数数组 - if (byte[].class.isAssignableFrom(responseType)) { - ByteArrayOutputStream os = new ByteArrayOutputStream(); - entity.writeTo(os); - return (T) os.toByteArray(); - } - //输出流 - if (OutputStream.class.isAssignableFrom(responseType)) { - try { - OutputStream t; - if (responseType == OutputStream.class){ - t= new ByteArrayOutputStream(); - }else { - t = (OutputStream) responseType.newInstance(); - } - entity.writeTo( t); - return (T) t; - } catch (InstantiationException e) { - throw new PayErrorException(new PayException("InstantiationException", e.getMessage())); - } catch (IllegalAccessException e) { - throw new PayErrorException(new PayException("IllegalAccessException", e.getMessage())); - } - } throw new PayErrorException(new PayException("failure", "类型转化异常,contentType:" + entity.getContentType().getValue())); } diff --git a/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/WxV3CombinePayController.java b/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/WxV3CombinePayController.java index ce65d86..2724601 100644 --- a/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/WxV3CombinePayController.java +++ b/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/WxV3CombinePayController.java @@ -46,7 +46,7 @@ public class WxV3CombinePayController { private WxCombinePayService service = null; - @PostConstruct //没有证书的情况下注释掉,避免启动报错 +// @PostConstruct //没有证书的情况下注释掉,避免启动报错 public void init() { WxPayConfigStorage wxPayConfigStorage = new WxPayConfigStorage(); wxPayConfigStorage.setAppId("wxc7b993ff15a9f26c"); diff --git a/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/WxV3PayController.java b/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/WxV3PayController.java index 621c83f..e9fbd6f 100644 --- a/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/WxV3PayController.java +++ b/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/WxV3PayController.java @@ -27,6 +27,9 @@ import com.egzosn.pay.web.support.HttpRequestNoticeParams; import com.egzosn.pay.wx.v3.api.WxPayConfigStorage; import com.egzosn.pay.wx.v3.api.WxPayService; import com.egzosn.pay.wx.v3.bean.WxTransactionType; +import com.egzosn.pay.wx.v3.bean.order.H5Info; +import com.egzosn.pay.wx.v3.bean.order.SceneInfo; +import com.egzosn.pay.wx.v3.utils.WxConst; /** * 微信V3发起支付入口 @@ -75,12 +78,11 @@ public class WxV3PayController { @RequestMapping(value = "toPay.html", produces = "text/html;charset=UTF-8") public String toPay(HttpServletRequest request, BigDecimal price) { PayOrder order = new PayOrder("订单title", "摘要", null == price ? BigDecimal.valueOf(0.01) : price, UUID.randomUUID().toString().replace("-", ""), WxTransactionType.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("在线充值"); + SceneInfo sceneInfo = new SceneInfo(); + sceneInfo.setPayerClientIp(request.getHeader("X-Real-IP")); + sceneInfo.setH5Info(new H5Info("在线充值", requestURL.substring(0, requestURL.indexOf("/") > 0 ? requestURL.indexOf("/") : requestURL.length()))); + order.addAttr(WxConst.SCENE_INFO, sceneInfo); // Map orderInfo = service.orderInfo(order); // return service.buildRequest(orderInfo, MethodType.POST); diff --git a/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/WxV3ProfitSharingController.java b/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/WxV3ProfitSharingController.java index 299fea1..793864d 100644 --- a/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/WxV3ProfitSharingController.java +++ b/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/WxV3ProfitSharingController.java @@ -44,7 +44,7 @@ public class WxV3ProfitSharingController { private WxProfitSharingService service = null; - @PostConstruct //没有证书的情况下注释掉,避免启动报错 +// @PostConstruct //没有证书的情况下注释掉,避免启动报错 public void init() { WxPayConfigStorage wxPayConfigStorage = new WxPayConfigStorage(); wxPayConfigStorage.setAppId("wxc7b993ff15a9f26c"); diff --git a/pay-java-wx/pom.xml b/pay-java-wx/pom.xml index e6dd416..b4ecdce 100644 --- a/pay-java-wx/pom.xml +++ b/pay-java-wx/pom.xml @@ -23,7 +23,7 @@ org.bouncycastle bcprov-jdk15on - ${bcprov-jdk15on.version} + diff --git a/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/api/DefaultWxPayAssistService.java b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/api/DefaultWxPayAssistService.java index 906a529..9d6ab0d 100644 --- a/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/api/DefaultWxPayAssistService.java +++ b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/api/DefaultWxPayAssistService.java @@ -70,7 +70,8 @@ public class DefaultWxPayAssistService implements WxPayAssistService { * @return 响应内容体 */ public JSONObject doExecute(Map parameters, TransactionType transactionType) { - String requestBody = JSON.toJSONString(parameters, SerializerFeature.WriteMapNullValue); +// String requestBody = JSON.toJSONString(parameters, SerializerFeature.WriteMapNullValue); + String requestBody = JSON.toJSONString(parameters); return doExecute(requestBody, transactionType); } diff --git a/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/api/WxPayService.java b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/api/WxPayService.java index 75d5f99..be3e5e1 100644 --- a/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/api/WxPayService.java +++ b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/api/WxPayService.java @@ -302,7 +302,7 @@ public class WxPayService extends BasePayService { params.put("prepayid", prepayId); params.put("package", "Sign=WXPay"); } - String signText = StringUtils.joining("\n", appId, timeStamp, prepayId); + String signText = StringUtils.joining("\n", appId, timeStamp, randomStr, prepayId); String paySign = createSign(signText, payConfigStorage.getInputCharset()); params.put(WxTransactionType.JSAPI.equals(order.getTransactionType()) ? "paySign" : "sign", paySign); return params; @@ -525,6 +525,8 @@ public class WxPayService extends BasePayService { } + + /** * 查询退款 * diff --git a/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/utils/AntCertificationUtil.java b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/utils/AntCertificationUtil.java index 2c4cedb..46c5bfc 100644 --- a/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/utils/AntCertificationUtil.java +++ b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/utils/AntCertificationUtil.java @@ -10,6 +10,7 @@ import java.security.KeyStoreException; import java.security.NoSuchProviderException; import java.security.PrivateKey; import java.security.PublicKey; +import java.security.Security; import java.security.cert.Certificate; import java.security.cert.CertificateException; import java.security.cert.CertificateFactory; @@ -21,6 +22,8 @@ import javax.crypto.Cipher; import javax.crypto.spec.GCMParameterSpec; import javax.crypto.spec.SecretKeySpec; +import org.bouncycastle.jce.provider.BouncyCastleProvider; + import com.egzosn.pay.common.exception.PayErrorException; import com.egzosn.pay.common.util.sign.encrypt.Base64; import com.egzosn.pay.wx.bean.WxPayError; @@ -49,6 +52,10 @@ public final class AntCertificationUtil { static { try { + if (null == Security.getProvider("BC")) { + Security.removeProvider("SunEC"); + Security.addProvider(new BouncyCastleProvider()); + } PKCS12_KEY_STORE = KeyStore.getInstance("PKCS12"); } catch (KeyStoreException e) { @@ -133,7 +140,7 @@ public final class AntCertificationUtil { * @param cipherText 需要解密的文本 * @param secretKey 密钥 * @param characterEncoding 编码类型 - * @return 解密后的信息 + * @return 解密后的信息 */ public static String decryptToString(String associatedData, String nonce, String cipherText, String secretKey, String characterEncoding) { @@ -156,7 +163,7 @@ public final class AntCertificationUtil { * * @param message the message * @param certificate the certificate - * @return 加密后的内容 + * @return 加密后的内容 */ public static String encryptToString(String message, Certificate certificate) { try { diff --git a/pom.xml b/pom.xml index 3c1d50c..0cca8c9 100644 --- a/pom.xml +++ b/pom.xml @@ -106,7 +106,11 @@ fastjson 1.2.73 - + + org.bouncycastle + bcprov-jdk15on + ${bcprov-jdk15on.version} + -- Gitee From f40afda8904ab605f4693795f4992633deac0a3d Mon Sep 17 00:00:00 2001 From: egzosn Date: Tue, 23 Nov 2021 09:36:53 +0800 Subject: [PATCH 249/299] 2.14.3-b2 --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 42a6764..154d076 100644 --- a/README.md +++ b/README.md @@ -22,7 +22,7 @@ com.egzosn {module-name} - 2.14.3-b1 + 2.14.3-b2 ``` -- Gitee From 213c8e84a3dd5745cda7a790fccac10e19d4399e Mon Sep 17 00:00:00 2001 From: egzosn Date: Tue, 23 Nov 2021 22:52:59 +0800 Subject: [PATCH 250/299] =?UTF-8?q?2.14.3-b3=E5=BC=80=E5=A7=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pay-java-ali/pom.xml | 2 +- pay-java-baidu/pom.xml | 2 +- pay-java-common/pom.xml | 2 +- pay-java-demo/pom.xml | 2 +- pay-java-fuiou/pom.xml | 2 +- pay-java-payoneer/pom.xml | 2 +- pay-java-paypal/pom.xml | 2 +- pay-java-union/pom.xml | 2 +- pay-java-web-support/pom.xml | 2 +- pay-java-wx-youdian/pom.xml | 2 +- pay-java-wx/pom.xml | 2 +- pay-java-yiji/pom.xml | 2 +- pom.xml | 4 ++-- 13 files changed, 14 insertions(+), 14 deletions(-) diff --git a/pay-java-ali/pom.xml b/pay-java-ali/pom.xml index 9014156..863e118 100644 --- a/pay-java-ali/pom.xml +++ b/pay-java-ali/pom.xml @@ -5,7 +5,7 @@ pay-java-parent com.egzosn - 2.14.3-b2 + 2.14.3-b3 4.0.0 pay-java-ali diff --git a/pay-java-baidu/pom.xml b/pay-java-baidu/pom.xml index 4d1c45f..54da1d4 100644 --- a/pay-java-baidu/pom.xml +++ b/pay-java-baidu/pom.xml @@ -5,7 +5,7 @@ pay-java-parent com.egzosn - 2.14.3-b2 + 2.14.3-b3 4.0.0 pay-java-baidu diff --git a/pay-java-common/pom.xml b/pay-java-common/pom.xml index 7da4d06..055aed4 100644 --- a/pay-java-common/pom.xml +++ b/pay-java-common/pom.xml @@ -5,7 +5,7 @@ pay-java-parent com.egzosn - 2.14.3-b2 + 2.14.3-b3 4.0.0 jar diff --git a/pay-java-demo/pom.xml b/pay-java-demo/pom.xml index 348317b..78644eb 100644 --- a/pay-java-demo/pom.xml +++ b/pay-java-demo/pom.xml @@ -5,7 +5,7 @@ pay-java-parent com.egzosn - 2.14.3-b2 + 2.14.3-b3 4.0.0 war diff --git a/pay-java-fuiou/pom.xml b/pay-java-fuiou/pom.xml index 08cea48..a941445 100644 --- a/pay-java-fuiou/pom.xml +++ b/pay-java-fuiou/pom.xml @@ -5,7 +5,7 @@ pay-java-parent com.egzosn - 2.14.3-b2 + 2.14.3-b3 4.0.0 pay-java-fuiou diff --git a/pay-java-payoneer/pom.xml b/pay-java-payoneer/pom.xml index 86ed571..0a7817c 100644 --- a/pay-java-payoneer/pom.xml +++ b/pay-java-payoneer/pom.xml @@ -5,7 +5,7 @@ pay-java-parent com.egzosn - 2.14.3-b2 + 2.14.3-b3 4.0.0 pay-java-payoneer diff --git a/pay-java-paypal/pom.xml b/pay-java-paypal/pom.xml index da3ae52..d74b445 100644 --- a/pay-java-paypal/pom.xml +++ b/pay-java-paypal/pom.xml @@ -5,7 +5,7 @@ pay-java-parent com.egzosn - 2.14.3-b2 + 2.14.3-b3 4.0.0 diff --git a/pay-java-union/pom.xml b/pay-java-union/pom.xml index 47d0abc..d5b733d 100644 --- a/pay-java-union/pom.xml +++ b/pay-java-union/pom.xml @@ -5,7 +5,7 @@ pay-java-parent com.egzosn - 2.14.3-b2 + 2.14.3-b3 4.0.0 diff --git a/pay-java-web-support/pom.xml b/pay-java-web-support/pom.xml index 021cb2c..126b206 100644 --- a/pay-java-web-support/pom.xml +++ b/pay-java-web-support/pom.xml @@ -5,7 +5,7 @@ pay-java-parent com.egzosn - 2.14.3-b2 + 2.14.3-b3 4.0.0 jar diff --git a/pay-java-wx-youdian/pom.xml b/pay-java-wx-youdian/pom.xml index 3f2bd4a..2bea82d 100644 --- a/pay-java-wx-youdian/pom.xml +++ b/pay-java-wx-youdian/pom.xml @@ -5,7 +5,7 @@ pay-java-parent com.egzosn - 2.14.3-b2 + 2.14.3-b3 4.0.0 pay-java-wx-youdian diff --git a/pay-java-wx/pom.xml b/pay-java-wx/pom.xml index b4ecdce..fddcf5b 100644 --- a/pay-java-wx/pom.xml +++ b/pay-java-wx/pom.xml @@ -5,7 +5,7 @@ pay-java-parent com.egzosn - 2.14.3-b2 + 2.14.3-b3 4.0.0 pay-java-wx diff --git a/pay-java-yiji/pom.xml b/pay-java-yiji/pom.xml index c0f4aa7..ee63230 100644 --- a/pay-java-yiji/pom.xml +++ b/pay-java-yiji/pom.xml @@ -5,7 +5,7 @@ pay-java-parent com.egzosn - 2.14.3-b2 + 2.14.3-b3 4.0.0 diff --git a/pom.xml b/pom.xml index 0cca8c9..06f14ba 100644 --- a/pom.xml +++ b/pom.xml @@ -7,7 +7,7 @@ com.egzosn pay-java-parent pom - 2.14.3-b2 + 2.14.3-b3 Pay Java - Parent Pay Java Parent @@ -70,7 +70,7 @@ - 2.14.3-b2 + 2.14.3-b3 4.5.4 1.2.17 1.2.73 -- Gitee From 7a80e5a09cbcd75ae477f50e655f8c2a9a632051 Mon Sep 17 00:00:00 2001 From: egzosn Date: Tue, 23 Nov 2021 22:53:12 +0800 Subject: [PATCH 251/299] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E7=9B=B4=E8=BF=9E?= =?UTF-8?q?=E5=95=86=E6=88=B7=E6=9F=A5=E8=AF=A2=E8=AE=A2=E5=8D=95=E4=B8=8E?= =?UTF-8?q?=E5=85=B3=E9=97=AD=E8=AE=A2=E5=8D=95=E6=8F=90=E7=A4=BAMchId?= =?UTF-8?q?=E5=BF=85=E5=A1=AB=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../pay/wx/v3/api/WxParameterStructure.java | 53 ++++++++++--------- .../pay/wx/v3/api/WxPayConfigStorage.java | 8 +-- .../egzosn/pay/wx/v3/api/WxPayService.java | 13 +++-- 3 files changed, 42 insertions(+), 32 deletions(-) diff --git a/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/api/WxParameterStructure.java b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/api/WxParameterStructure.java index 3f3596b..bd0b101 100644 --- a/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/api/WxParameterStructure.java +++ b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/api/WxParameterStructure.java @@ -9,11 +9,11 @@ import com.egzosn.pay.common.bean.OrderParaStructure; import com.egzosn.pay.common.bean.PayOrder; import com.egzosn.pay.common.http.UriVariables; import com.egzosn.pay.common.util.MapGen; -import com.egzosn.pay.common.util.str.StringUtils; import com.egzosn.pay.wx.v3.utils.WxConst; /** * 微信参数构造器 + * * @author Egan *

  * email egzosn@gmail.com
@@ -32,19 +32,19 @@ public class WxParameterStructure {
      *
      * @return 公共参数
      */
-    public Map getPublicParameters() {
-
-        Map parameters = new LinkedHashMap<>();
+    public Map getPublicParameters(Map parameters) {
+        if (payConfigStorage.isPartner()) {
+            return parameters;
+        }
+        if (null == parameters) {
+            parameters = new LinkedHashMap<>();
+        }
         parameters.put(WxConst.APPID, payConfigStorage.getAppId());
         parameters.put(WxConst.MCH_ID, payConfigStorage.getMchId());
         return parameters;
     }
 
 
-
-
-
-
     /**
      * 加载结算信息
      *
@@ -65,7 +65,6 @@ public class WxParameterStructure {
     }
 
 
-
     /**
      * 初始化通知URL必须为直接可访问的URL,不允许携带查询串,要求必须为https地址。
      *
@@ -77,32 +76,38 @@ public class WxParameterStructure {
         OrderParaStructure.loadParameters(parameters, WxConst.NOTIFY_URL, order);
     }
 
+
     /**
-     * 获取服务商相关信息
+     * 获取商户相关信息
      *
-     * @return 服务商相关信息
+     * @return 商户相关信息
      */
-    public String getSpParameters() {
+    public String getMchParameters() {
         Map attr = initSubMchId(null);
-        OrderParaStructure.loadParameters(attr, WxConst.SP_MCH_ID, payConfigStorage.getSpMchId());
+        OrderParaStructure.loadParameters(attr, payConfigStorage.isPartner() ? WxConst.SP_MCH_ID : WxConst.MCH_ID, payConfigStorage.getMchId());
         return UriVariables.getMapToParameters(attr);
     }
+
     /**
      * 初始化商户相关信息
      *
      * @param parameters 参数信息
      */
-    public void initPartner(Map parameters) {
+    public Map initPartner(Map parameters) {
         if (null == parameters) {
-            parameters = new HashMap<>();
+            parameters = new LinkedHashMap<>();
         }
-        if (StringUtils.isNotEmpty(payConfigStorage.getSpAppId()) && StringUtils.isNotEmpty(payConfigStorage.getSpMchId())) {
-            payConfigStorage.setPartner(true);
-            parameters.put("sp_appid", payConfigStorage.getSpAppId());
-            parameters.put(WxConst.SP_MCH_ID, payConfigStorage.getSpMchId());
+        if (payConfigStorage.isPartner()) {
+            parameters.put("sp_appid", payConfigStorage.getAppId());
+            parameters.put(WxConst.SP_MCH_ID, payConfigStorage.getMchId());
+            OrderParaStructure.loadParameters(parameters, "sub_appid", payConfigStorage.getSubAppId());
+            OrderParaStructure.loadParameters(parameters, WxConst.SUB_MCH_ID, payConfigStorage.getSubMchId());
+            return parameters;
         }
-        OrderParaStructure.loadParameters(parameters, "sub_appid", payConfigStorage.getSubAppId());
-        initSubMchId(parameters);
+
+        parameters.put(WxConst.APPID, payConfigStorage.getAppId());
+        parameters.put(WxConst.MCH_ID, payConfigStorage.getMchId());
+        return parameters;
     }
 
 
@@ -116,10 +121,10 @@ public class WxParameterStructure {
         if (null == parameters) {
             parameters = new HashMap<>();
         }
-        if (StringUtils.isNotEmpty(payConfigStorage.getSubMchId())) {
-            payConfigStorage.setPartner(true);
-            parameters.put(WxConst.SUB_MCH_ID, payConfigStorage.getSubMchId());
+        if (payConfigStorage.isPartner()) {
+            OrderParaStructure.loadParameters(parameters, WxConst.SUB_MCH_ID, payConfigStorage.getSubMchId());
         }
+
         return parameters;
 
     }
diff --git a/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/api/WxPayConfigStorage.java b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/api/WxPayConfigStorage.java
index b668a63..63af5eb 100644
--- a/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/api/WxPayConfigStorage.java
+++ b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/api/WxPayConfigStorage.java
@@ -169,20 +169,20 @@ public class WxPayConfigStorage extends BasePayConfigStorage {
     }
 
     public String getSpAppId() {
-        return spAppId;
+        return getAppId();
     }
 
     public void setSpAppId(String spAppId) {
-        this.spAppId = spAppId;
+        setAppId(spAppId);
         addAttr("spAppId", spAppId);
     }
 
     public String getSpMchId() {
-        return spMchId;
+        return getMchId();
     }
 
     public void setSpMchId(String spMchId) {
-        this.spMchId = spMchId;
+        setMchId(spMchId);
         addAttr("spMchId", spMchId);
     }
 
diff --git a/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/api/WxPayService.java b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/api/WxPayService.java
index be3e5e1..f175615 100644
--- a/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/api/WxPayService.java
+++ b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/api/WxPayService.java
@@ -134,6 +134,7 @@ public class WxPayService extends BasePayService {
      */
     @Override
     protected void initAfter() {
+        payConfigStorage.setPartner(StringUtils.isNotEmpty(payConfigStorage.getSubMchId()));
         new Thread(() -> {
             payConfigStorage.loadCertEnvironment();
             wxParameterStructure = new WxParameterStructure(payConfigStorage);
@@ -234,8 +235,9 @@ public class WxPayService extends BasePayService {
     public JSONObject unifiedOrder(PayOrder order) {
 
         //统一下单
-        Map parameters = wxParameterStructure.getPublicParameters();
-        wxParameterStructure.initPartner(parameters);
+        Map parameters =  wxParameterStructure.initPartner(null);
+       ;
+//        wxParameterStructure.getPublicParameters(parameters);
         // 商品描述
         OrderParaStructure.loadParameters(parameters, WxConst.DESCRIPTION, order.getSubject());
         OrderParaStructure.loadParameters(parameters, WxConst.DESCRIPTION, order.getBody());
@@ -283,6 +285,9 @@ public class WxPayService extends BasePayService {
 
         Map params = new LinkedHashMap<>();
         String appId = payConfigStorage.getAppId();
+        if (payConfigStorage.isPartner() && StringUtils.isNotEmpty(payConfigStorage.getSubAppId())){
+            appId = payConfigStorage.getSubAppId();
+        }
         String timeStamp = String.valueOf(DateUtils.toEpochSecond());
         String randomStr = SignTextUtils.randomStr();
         String prepayId = result.getString("prepay_id");
@@ -457,7 +462,7 @@ public class WxPayService extends BasePayService {
     public Map query(AssistOrder assistOrder) {
         String transactionId = assistOrder.getTradeNo();
         String outTradeNo = assistOrder.getOutTradeNo();
-        String parameters = wxParameterStructure.getSpParameters();
+        String parameters = wxParameterStructure.getMchParameters();
         WxTransactionType transactionType = WxTransactionType.QUERY_TRANSACTION_ID;
         String uriVariable = transactionId;
         if (StringUtils.isNotEmpty(outTradeNo)) {
@@ -490,7 +495,7 @@ public class WxPayService extends BasePayService {
      */
     @Override
     public Map close(AssistOrder assistOrder) {
-        String parameters = wxParameterStructure.getSpParameters();
+        String parameters = wxParameterStructure.getMchParameters();
         return getAssistService().doExecute(parameters, WxTransactionType.CLOSE, assistOrder.getOutTradeNo());
     }
 
-- 
Gitee


From 999c446bd45926037c63306f687aa800fe4ef3ed Mon Sep 17 00:00:00 2001
From: egzosn 
Date: Sun, 28 Nov 2021 22:23:45 +0800
Subject: [PATCH 252/299] =?UTF-8?q?v3=E9=80=80=E6=AC=BEint=E7=B1=BB?=
 =?UTF-8?q?=E5=9E=8B=E5=AF=BC=E8=87=B4=E9=BB=98=E8=AE=A4=E5=80=BC=E5=87=BA?=
 =?UTF-8?q?=E7=8E=B0=E7=AD=BE=E5=90=8D=E5=A4=B1=E8=B4=A5=E6=94=B9=E7=94=A8?=
 =?UTF-8?q?Integer?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 .../egzosn/pay/wx/v3/bean/order/Detail.java   |  6 ++--
 .../com/egzosn/pay/wx/v3/bean/order/From.java |  6 ++--
 .../pay/wx/v3/bean/order/GoodsDetail.java     | 12 +++----
 .../pay/wx/v3/bean/order/RefundAmount.java    | 36 +++++++++----------
 4 files changed, 30 insertions(+), 30 deletions(-)

diff --git a/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/order/Detail.java b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/order/Detail.java
index ede9be6..d11875d 100644
--- a/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/order/Detail.java
+++ b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/order/Detail.java
@@ -15,7 +15,7 @@ public class Detail {
      * 订单原价
      */
     @JSONField(name = "cost_price")
-    private int costPrice;
+    private Integer costPrice;
     /**
      * 商家小票
      */
@@ -25,11 +25,11 @@ public class Detail {
     @JSONField(name = "goods_detail")
     private List goodsDetail;
 
-    public int getCostPrice() {
+    public Integer getCostPrice() {
         return costPrice;
     }
 
-    public void setCostPrice(int costPrice) {
+    public void setCostPrice(Integer costPrice) {
         this.costPrice = costPrice;
     }
 
diff --git a/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/order/From.java b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/order/From.java
index 84812ae..7c7a9e0 100644
--- a/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/order/From.java
+++ b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/order/From.java
@@ -19,7 +19,7 @@ public class From {
     /**
      * 对应账户出资金额
      */
-    private int amount;
+    private Integer amount;
 
     public String getAccount() {
         return account;
@@ -29,11 +29,11 @@ public class From {
         this.account = account;
     }
 
-    public int getAmount() {
+    public Integer getAmount() {
         return amount;
     }
 
-    public void setAmount(int amount) {
+    public void setAmount(Integer amount) {
         this.amount = amount;
     }
 }
diff --git a/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/order/GoodsDetail.java b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/order/GoodsDetail.java
index e1155d0..ad09ccb 100644
--- a/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/order/GoodsDetail.java
+++ b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/order/GoodsDetail.java
@@ -33,13 +33,13 @@ public class GoodsDetail {
      * 商品数量
      */
 
-    private int quantity;
+    private Integer quantity;
     /**
      * 商品单价
      * 商品单价,单位为分
      */
     @JSONField(name = "unit_price")
-    private int unitPrice ;
+    private Integer unitPrice ;
 
 
     public String getMerchantGoodsId() {
@@ -66,19 +66,19 @@ public class GoodsDetail {
         this.goodsName = goodsName;
     }
 
-    public int getQuantity() {
+    public Integer getQuantity() {
         return quantity;
     }
 
-    public void setQuantity(int quantity) {
+    public void setQuantity(Integer quantity) {
         this.quantity = quantity;
     }
 
-    public int getUnitPrice() {
+    public Integer getUnitPrice() {
         return unitPrice;
     }
 
-    public void setUnitPrice(int unitPrice) {
+    public void setUnitPrice(Integer unitPrice) {
         this.unitPrice = unitPrice;
     }
 }
diff --git a/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/order/RefundAmount.java b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/order/RefundAmount.java
index fbac901..acc691f 100644
--- a/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/order/RefundAmount.java
+++ b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/order/RefundAmount.java
@@ -17,7 +17,7 @@ public class RefundAmount extends Amount {
     /**
      * 退款金额,单位分
      */
-    private int refund;
+    private Integer refund;
 
     /**
      * 退款出资的账户类型及金额信息
@@ -28,38 +28,38 @@ public class RefundAmount extends Amount {
      * 用户支付金额,单位分
      */
     @JSONField(name = "payer_total")
-    private int payerTotal;
+    private Integer payerTotal;
     /**
      * 用户退款金额
      * 退款给用户的金额,不包含所有优惠券金额
      */
     @JSONField(name = "payer_refund")
-    private int payerRefund;
+    private Integer payerRefund;
     /**
      * 应结退款金额
      * 去掉非充值代金券退款金额后的退款金额,单位为分,退款金额=申请退款金额-非充值代金券退款金额,退款金额<=申请退款金额
      */
     @JSONField(name = "settlement_refund")
-    private int settlementRefund;
+    private Integer settlementRefund;
     /**
      * 应结订单金额
      * 应结订单金额=订单金额-免充值代金券金额,应结订单金额<=订单金额,单位为分
      */
     @JSONField(name = "settlement_total")
-    private int settlementTotal;
+    private Integer settlementTotal;
     /**
      * 优惠退款金额
      * 优惠退款金额<=退款金额,退款金额-代金券或立减优惠退款金额为现金,说明详见代金券或立减优惠,单位为分
      */
     @JSONField(name = "discount_refund")
-    private int discountRefund;
+    private Integer discountRefund;
 
 
-    public int getRefund() {
+    public Integer getRefund() {
         return refund;
     }
 
-    public void setRefund(int refund) {
+    public void setRefund(Integer refund) {
         this.refund = refund;
     }
 
@@ -71,43 +71,43 @@ public class RefundAmount extends Amount {
         this.from = from;
     }
 
-    public int getPayerTotal() {
+    public Integer getPayerTotal() {
         return payerTotal;
     }
 
-    public void setPayerTotal(int payerTotal) {
+    public void setPayerTotal(Integer payerTotal) {
         this.payerTotal = payerTotal;
     }
 
-    public int getPayerRefund() {
+    public Integer getPayerRefund() {
         return payerRefund;
     }
 
-    public void setPayerRefund(int payerRefund) {
+    public void setPayerRefund(Integer payerRefund) {
         this.payerRefund = payerRefund;
     }
 
-    public int getSettlementRefund() {
+    public Integer getSettlementRefund() {
         return settlementRefund;
     }
 
-    public void setSettlementRefund(int settlementRefund) {
+    public void setSettlementRefund(Integer settlementRefund) {
         this.settlementRefund = settlementRefund;
     }
 
-    public int getSettlementTotal() {
+    public Integer getSettlementTotal() {
         return settlementTotal;
     }
 
-    public void setSettlementTotal(int settlementTotal) {
+    public void setSettlementTotal(Integer settlementTotal) {
         this.settlementTotal = settlementTotal;
     }
 
-    public int getDiscountRefund() {
+    public Integer getDiscountRefund() {
         return discountRefund;
     }
 
-    public void setDiscountRefund(int discountRefund) {
+    public void setDiscountRefund(Integer discountRefund) {
         this.discountRefund = discountRefund;
     }
 }
-- 
Gitee


From 1656a55ce6e0369e3e48c52910dd391a70ba3d3e Mon Sep 17 00:00:00 2001
From: egzosn 
Date: Mon, 29 Nov 2021 22:02:25 +0800
Subject: [PATCH 253/299] =?UTF-8?q?=E6=94=AF=E4=BB=98=E5=AE=9D=E5=AE=8C?=
 =?UTF-8?q?=E5=96=84=E9=80=80=E6=AC=BE=E6=A1=88=E4=BE=8B?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 pay-java-ali/README.md                        | 27 ++++++++++++-------
 .../com/egzosn/pay/ali/api/AliPayService.java |  6 ++++-
 .../egzosn/pay/common/bean/RefundOrder.java   | 11 ++++++++
 .../pay/demo/controller/AliPayController.java | 10 +++++--
 4 files changed, 42 insertions(+), 12 deletions(-)

diff --git a/pay-java-ali/README.md b/pay-java-ali/README.md
index 166131d..d48313c 100644
--- a/pay-java-ali/README.md
+++ b/pay-java-ali/README.md
@@ -269,7 +269,14 @@
 
 #### 查询退款
   ```java
-          Map result = service.refundquery("支付宝单号", "我方系统单号");
+        RefundOrder order = new RefundOrder();
+        order.setOutTradeNo("我方系统商户单号");
+        order.setTradeNo("支付宝单号");
+        //退款金额
+        order.setRefundAmount(new BigDecimal(1));
+        order.setRefundNo("退款单号");
+        order.setDescription("");
+        Map result = service.refundquery();
             
 ```
 
@@ -282,15 +289,17 @@
 
 #### 转账
   ```java
-        TransferOrder order = new TransferOrder();
-        order.setOutNo("商户转账订单号");
-        order.setPayeeAccount("收款方账户,支付宝登录号,支持邮箱和手机号格式");
-        order.setAmount(new BigDecimal(10));
-        order.setPayerName("付款方姓名, 非必填");
-        order.setPayeeName("收款方真实姓名, 非必填");
+        order.setOutBizNo("转账单号");
+        order.setTransAmount(new BigDecimal(10));
+        order.setOrderTitle("转账业务的标题");
+        order.setIdentity("参与方的唯一标识");
+        order.setIdentityType("参与方的标识类型,目前支持如下类型:");
+        order.setName("参与方真实姓名");
         order.setRemark("转账备注, 非必填");
-        //收款方账户类型 ,默认值 ALIPAY_LOGONID:支付宝登录号,支持邮箱和手机号格式。
-        order.setTransferType(AliTransferType.ALIPAY_LOGONID);
+        //单笔无密转账到支付宝账户
+        order.setTransferType(AliTransferType.TRANS_ACCOUNT_NO_PWD);
+        //单笔无密转账到银行卡
+//        order.setTransferType(AliTransferType.TRANS_BANKCARD_NO_PWD);
         Map result = service.transfer(order);
 
 ```
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 274ef1d..c80b21b 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
@@ -587,7 +587,11 @@ public class AliPayService extends BasePayService {
         if (!StringUtils.isEmpty(refundOrder.getRefundNo())) {
             bizContent.put("out_request_no", refundOrder.getRefundNo());
         }
-        bizContent.putAll(refundOrder.getAttrs());
+        OrderParaStructure.loadParameters(parameters, "biz_type", refundOrder);
+        OrderParaStructure.loadParameters(parameters, "refund_reason", refundOrder.getDescription());
+        OrderParaStructure.loadParameters(parameters, "store_id", refundOrder);
+        OrderParaStructure.loadParameters(parameters, "terminal_id", refundOrder);
+//        bizContent.putAll(refundOrder.getAttrs());
         //设置请求参数的集合
         parameters.put(BIZ_CONTENT, JSON.toJSONString(bizContent));
         //设置签名
diff --git a/pay-java-common/src/main/java/com/egzosn/pay/common/bean/RefundOrder.java b/pay-java-common/src/main/java/com/egzosn/pay/common/bean/RefundOrder.java
index f3cb108..4bc0fa9 100644
--- a/pay-java-common/src/main/java/com/egzosn/pay/common/bean/RefundOrder.java
+++ b/pay-java-common/src/main/java/com/egzosn/pay/common/bean/RefundOrder.java
@@ -44,6 +44,10 @@ public class RefundOrder extends AssistOrder {
      */
     private String userId;
 
+    /**
+     * 退款URL
+     */
+    private String refundUrl;
 
     public String getRefundNo() {
         return refundNo;
@@ -125,4 +129,11 @@ public class RefundOrder extends AssistOrder {
         this.totalAmount = totalAmount;
     }
 
+    public String getRefundUrl() {
+        return refundUrl;
+    }
+
+    public void setRefundUrl(String refundUrl) {
+        this.refundUrl = refundUrl;
+    }
 }
diff --git a/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/AliPayController.java b/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/AliPayController.java
index df8f2e6..f88f71a 100644
--- a/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/AliPayController.java
+++ b/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/AliPayController.java
@@ -340,11 +340,17 @@ public class AliPayController {
     /**
      * 查询退款
      *
-     * @param order 订单的请求体
      * @return 返回支付方查询退款后的结果
      */
     @RequestMapping("refundquery")
-    public Map refundquery(RefundOrder order) {
+    public Map refundquery() {
+        RefundOrder order = new RefundOrder();
+        order.setOutTradeNo("我方系统商户单号");
+        order.setTradeNo("支付宝单号");
+        //退款金额
+        order.setRefundAmount(new BigDecimal(1));
+        order.setRefundNo("退款单号");
+        order.setDescription("");
         return service.refundquery(order);
     }
 
-- 
Gitee


From 496db0b51f596e54f72d8abcf6219940afc72387 Mon Sep 17 00:00:00 2001
From: egzosn 
Date: Mon, 29 Nov 2021 22:05:06 +0800
Subject: [PATCH 254/299] =?UTF-8?q?v3=E6=8F=90=E4=BE=9B=E6=B5=B7=E5=A4=96?=
 =?UTF-8?q?=E6=94=AF=E4=BB=98=E7=9A=84=E4=BD=BF=E7=94=A8=E6=A1=88=E4=BE=8B?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 .../com/egzosn/pay/demo/controller/WxV3PayController.java     | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/WxV3PayController.java b/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/WxV3PayController.java
index e9fbd6f..7511364 100644
--- a/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/WxV3PayController.java
+++ b/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/WxV3PayController.java
@@ -56,11 +56,13 @@ public class WxV3PayController {
         wxPayConfigStorage.setReturnUrl("http://sailinmu.iok.la/wxV3/payBack.json");
         wxPayConfigStorage.setInputCharset("utf-8");
         //使用证书时设置为true
-        wxPayConfigStorage.setCertSign(true);
+//        wxPayConfigStorage.setCertSign(true);
         //商户API证书 https://pay.weixin.qq.com/wiki/doc/apiv3/wechatpay/wechatpay3_1.shtml
         wxPayConfigStorage.setApiClientKeyP12("yifenli_mall.p12");
         wxPayConfigStorage.setCertStoreType(CertStoreType.PATH);
         service = new WxPayService(wxPayConfigStorage);
+        //微信海外支付:东南亚
+//        service.setApiServerUrl("https://apihk.mch.weixin.qq.com");
         //设置回调消息处理
         //TODO {@link com.egzosn.pay.demo.controller.WxPayController#payBack}
         service.setPayMessageHandler(new WxV3PayMessageHandler());
-- 
Gitee


From 5b6af98ac8cda018307cfa1610f0fd7835e8177a Mon Sep 17 00:00:00 2001
From: egzosn 
Date: Fri, 3 Dec 2021 23:16:04 +0800
Subject: [PATCH 255/299] =?UTF-8?q?V3=20=E8=AF=81=E4=B9=A6=E5=8A=A0?=
 =?UTF-8?q?=E8=BD=BD=E5=85=BC=E5=AE=B9?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 .../com/egzosn/pay/wx/v3/utils/AntCertificationUtil.java    | 6 ++++++
 1 file changed, 6 insertions(+)

diff --git a/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/utils/AntCertificationUtil.java b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/utils/AntCertificationUtil.java
index 46c5bfc..4d8c324 100644
--- a/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/utils/AntCertificationUtil.java
+++ b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/utils/AntCertificationUtil.java
@@ -51,6 +51,11 @@ public final class AntCertificationUtil {
     private static final CertificateFactory CERTIFICATE_FACTORY;
 
     static {
+        String javaVersion = System.getProperty("java.version");
+        if (javaVersion.contains("1.8") || javaVersion.startsWith("8")){
+            Security.setProperty("crypto.policy", "unlimited");
+        }
+
         try {
             if (null == Security.getProvider("BC")) {
                 Security.removeProvider("SunEC");
@@ -180,4 +185,5 @@ public final class AntCertificationUtil {
         }
     }
 
+
 }
-- 
Gitee


From 7a989a7a15db359e87ce6951b0df4f1d0ab59e36 Mon Sep 17 00:00:00 2001
From: egzosn 
Date: Sat, 4 Dec 2021 22:02:55 +0800
Subject: [PATCH 256/299] =?UTF-8?q?=E6=94=AF=E4=BB=98=E5=AE=9D=E9=80=80?=
 =?UTF-8?q?=E6=AC=BE=E5=85=BC=E5=AE=B9?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 .../com/egzosn/pay/ali/api/AliPayService.java | 24 ++++++++-----------
 .../com/egzosn/pay/ali/bean/AliPayConst.java  |  8 +++++++
 .../pay/ali/bean/AliTransactionType.java      |  5 ++++
 3 files changed, 23 insertions(+), 14 deletions(-)

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 c80b21b..3b39bd8 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
@@ -32,7 +32,6 @@ import com.egzosn.pay.ali.bean.OrderSettle;
 import com.egzosn.pay.common.api.BasePayService;
 import com.egzosn.pay.common.bean.AssistOrder;
 import com.egzosn.pay.common.bean.BillType;
-
 import com.egzosn.pay.common.bean.MethodType;
 import com.egzosn.pay.common.bean.NoticeParams;
 import com.egzosn.pay.common.bean.Order;
@@ -499,11 +498,11 @@ public class AliPayService extends BasePayService {
     /**
      * 交易关闭接口
      *
-     * @param assistOrder    关闭订单
+     * @param assistOrder 关闭订单
      * @return 返回支付方交易关闭后的结果
      */
     @Override
-    public Map close(AssistOrder assistOrder){
+    public Map close(AssistOrder assistOrder) {
         return secondaryInterface(assistOrder.getTradeNo(), assistOrder.getOutTradeNo(), AliTransactionType.CLOSE);
     }
 
@@ -554,12 +553,14 @@ public class AliPayService extends BasePayService {
         //获取公共参数
         Map parameters = getPublicParameters(AliTransactionType.REFUND);
         setAppAuthToken(parameters, refundOrder.getAttrs());
+
         Map bizContent = getBizContent(refundOrder.getTradeNo(), refundOrder.getOutTradeNo(), null);
-        if (!StringUtils.isEmpty(refundOrder.getRefundNo())) {
-            bizContent.put("out_request_no", refundOrder.getRefundNo());
-        }
+        OrderParaStructure.loadParameters(bizContent, AliPayConst.OUT_REQUEST_NO, refundOrder.getRefundNo());
         bizContent.put("refund_amount", Util.conversionAmount(refundOrder.getRefundAmount()));
-        bizContent.putAll(refundOrder.getAttrs());
+        OrderParaStructure.loadParameters(bizContent,  AliPayConst.REFUND_REASON, refundOrder.getDescription());
+        OrderParaStructure.loadParameters(bizContent,  AliPayConst.REFUND_REASON, refundOrder);
+        OrderParaStructure.loadParameters(bizContent,"refund_royalty_parameters", refundOrder);
+        OrderParaStructure.loadParameters(bizContent, AliPayConst.QUERY_OPTIONS, refundOrder);
         //设置请求参数的集合
         parameters.put(BIZ_CONTENT, JSON.toJSONString(bizContent));
         //设置签名
@@ -584,13 +585,8 @@ public class AliPayService extends BasePayService {
         Map parameters = getPublicParameters(AliTransactionType.REFUNDQUERY);
         setAppAuthToken(parameters, refundOrder.getAttrs());
         Map bizContent = getBizContent(refundOrder.getTradeNo(), refundOrder.getOutTradeNo(), null);
-        if (!StringUtils.isEmpty(refundOrder.getRefundNo())) {
-            bizContent.put("out_request_no", refundOrder.getRefundNo());
-        }
-        OrderParaStructure.loadParameters(parameters, "biz_type", refundOrder);
-        OrderParaStructure.loadParameters(parameters, "refund_reason", refundOrder.getDescription());
-        OrderParaStructure.loadParameters(parameters, "store_id", refundOrder);
-        OrderParaStructure.loadParameters(parameters, "terminal_id", refundOrder);
+        OrderParaStructure.loadParameters(bizContent, AliPayConst.OUT_REQUEST_NO, refundOrder.getRefundNo());
+        OrderParaStructure.loadParameters(bizContent,  AliPayConst.QUERY_OPTIONS, refundOrder);
 //        bizContent.putAll(refundOrder.getAttrs());
         //设置请求参数的集合
         parameters.put(BIZ_CONTENT, JSON.toJSONString(bizContent));
diff --git a/pay-java-ali/src/main/java/com/egzosn/pay/ali/bean/AliPayConst.java b/pay-java-ali/src/main/java/com/egzosn/pay/ali/bean/AliPayConst.java
index a56db71..edd824b 100644
--- a/pay-java-ali/src/main/java/com/egzosn/pay/ali/bean/AliPayConst.java
+++ b/pay-java-ali/src/main/java/com/egzosn/pay/ali/bean/AliPayConst.java
@@ -60,4 +60,12 @@ public final class AliPayConst {
      * 收款方信息
      */
     public static final String ALIPAY_CERT_SN_FIELD = "alipay_cert_sn";
+    /**
+     * 业务扩展参数
+     */
+    public static final String EXTEND_PARAMS = "extend_params";
+    public static final String BIZ_TYPE = "biz_type";
+    public static final String REFUND_REASON = "refund_reason";
+    public static final String QUERY_OPTIONS = "query_options";
+    public static final String OUT_REQUEST_NO = "out_request_no";
 }
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 13e4f12..10ae405 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
@@ -84,6 +84,11 @@ public enum  AliTransactionType implements TransactionType {
      * 退款查询
      */
     REFUNDQUERY("alipay.trade.fastpay.refund.query"),
+    /**
+     * 收单退款冲退完成通知
+     * 退款存在退到银行卡场景下时,收单会根据银行回执消息发送退款完成信息
+     */
+    REFUND_DEPOSITBACK_COMPLETED ("alipay.trade.refund.depositback.completed"),
     /**
      * 下载对账单
      */
-- 
Gitee


From cd2294e9502aad04eb434475c85f1831ab5a18f6 Mon Sep 17 00:00:00 2001
From: egzosn 
Date: Sat, 4 Dec 2021 22:03:24 +0800
Subject: [PATCH 257/299] =?UTF-8?q?=E5=BE=AE=E4=BF=A1V3=E6=94=AF=E4=BB=98?=
 =?UTF-8?q?=EF=BC=8C=E8=BF=87=E6=9C=9F=E6=97=B6=E9=97=B4=E6=A0=BC=E5=BC=8F?=
 =?UTF-8?q?=E9=97=AE=E9=A2=98?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 .../src/main/java/com/egzosn/pay/common/util/DateUtils.java   | 3 ++-
 .../java/com/egzosn/pay/wx/v3/api/WxCombinePayService.java    | 4 ++--
 .../src/main/java/com/egzosn/pay/wx/v3/api/WxPayService.java  | 2 +-
 3 files changed, 5 insertions(+), 4 deletions(-)

diff --git a/pay-java-common/src/main/java/com/egzosn/pay/common/util/DateUtils.java b/pay-java-common/src/main/java/com/egzosn/pay/common/util/DateUtils.java
index 1691321..cddec8b 100644
--- a/pay-java-common/src/main/java/com/egzosn/pay/common/util/DateUtils.java
+++ b/pay-java-common/src/main/java/com/egzosn/pay/common/util/DateUtils.java
@@ -58,7 +58,8 @@ public final class DateUtils {
     }
 
     public static final String YYYY_MM_DD_HH_MM_SS = "yyyy-MM-dd HH:mm:ss";
-    public static final String YYYY_MM_DD_T_HH_MM_SS = "yyyy-MM-dd'T'HH:mm:ss.SSSXXX";
+    public static final String YYYY_MM_DD_T_HH_MM_SS_SSSXXX = "yyyy-MM-dd'T'HH:mm:ss.SSSXXX";
+    public static final String YYYY_MM_DD_T_HH_MM_SS_XX = "yyyy-MM-dd'T'HH:mm:ssXXX";
     public static final String YYYY_MM_DD = "yyyy-MM-dd";
     public static final String YYYYMMDD = "yyyyMMdd";
     public static final String YYYYMMDDHHMMSS = "yyyyMMddHHmmss";
diff --git a/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/api/WxCombinePayService.java b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/api/WxCombinePayService.java
index e009e7b..a1dc98a 100644
--- a/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/api/WxCombinePayService.java
+++ b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/api/WxCombinePayService.java
@@ -89,8 +89,8 @@ public class WxCombinePayService extends WxPayService {
         // 订单号
         parameters.put(WxConst.COMBINE_OUT_TRADE_NO, order.getOutTradeNo());
 
-        OrderParaStructure.loadDateParameters(parameters, WxConst.TIME_START, order, DateUtils.YYYY_MM_DD_T_HH_MM_SS);
-        OrderParaStructure.loadDateParameters(parameters, WxConst.TIME_EXPIRE, order, DateUtils.YYYY_MM_DD_T_HH_MM_SS);
+        OrderParaStructure.loadDateParameters(parameters, WxConst.TIME_START, order, DateUtils.YYYY_MM_DD_T_HH_MM_SS_XX);
+        OrderParaStructure.loadDateParameters(parameters, WxConst.TIME_EXPIRE, order, DateUtils.YYYY_MM_DD_T_HH_MM_SS_XX);
         initNotifyUrl(parameters, order);
         //支付场景描述
         OrderParaStructure.loadParameters(parameters, WxConst.SCENE_INFO, order);
diff --git a/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/api/WxPayService.java b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/api/WxPayService.java
index f175615..e73284d 100644
--- a/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/api/WxPayService.java
+++ b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/api/WxPayService.java
@@ -245,7 +245,7 @@ public class WxPayService extends BasePayService {
         parameters.put(WxConst.OUT_TRADE_NO, order.getOutTradeNo());
         //交易结束时间
         if (null != order.getExpirationTime()) {
-            parameters.put("time_expire", DateUtils.formatDate(order.getExpirationTime(), DateUtils.YYYYMMDDHHMMSS));
+            parameters.put("time_expire", DateUtils.formatDate(order.getExpirationTime(), DateUtils.YYYY_MM_DD_T_HH_MM_SS_XX));
         }
         OrderParaStructure.loadParameters(parameters, "attach", order.getAddition());
         wxParameterStructure.initNotifyUrl(parameters, order);
-- 
Gitee


From 74c33ca341881c44446b456b3d49274f4a31f3a2 Mon Sep 17 00:00:00 2001
From: egzosn 
Date: Sun, 5 Dec 2021 12:40:14 +0800
Subject: [PATCH 258/299] =?UTF-8?q?=E6=94=AF=E4=BB=98=E5=AE=9D=E6=96=B0?=
 =?UTF-8?q?=E5=A2=9E=20=E7=BD=91=E9=A1=B5=E6=94=AF=E4=BB=98=20=E4=B8=AD?=
 =?UTF-8?q?=E9=80=94=E5=8F=96=E6=B6=88=E6=94=AF=E4=BB=98=E5=9B=9E=E8=B0=83?=
 =?UTF-8?q?=E9=A1=B5=E9=9D=A2=EF=BC=8C=E6=96=B0=E5=A2=9E=20=E6=94=B6?=
 =?UTF-8?q?=E5=8D=95=E9=80=80=E6=AC=BE=E5=86=B2=E9=80=80=E5=AE=8C=E6=88=90?=
 =?UTF-8?q?=E9=80=9A=E7=9F=A5?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 .../com/egzosn/pay/ali/api/AliPayService.java |  76 ++++++++--
 .../egzosn/pay/ali/api/AliPayServiceInf.java  |  22 +++
 .../com/egzosn/pay/ali/bean/AliPayConst.java  |   9 ++
 .../RefundDepositBackCompletedNotify.java     | 139 ++++++++++++++++++
 4 files changed, 236 insertions(+), 10 deletions(-)
 create mode 100644 pay-java-ali/src/main/java/com/egzosn/pay/ali/api/AliPayServiceInf.java
 create mode 100644 pay-java-ali/src/main/java/com/egzosn/pay/ali/bean/RefundDepositBackCompletedNotify.java

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 3b39bd8..82011b8 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
@@ -10,6 +10,7 @@ import static com.egzosn.pay.ali.bean.AliPayConst.ALIPAY_CERT_SN_FIELD;
 import static com.egzosn.pay.ali.bean.AliPayConst.APP_AUTH_TOKEN;
 import static com.egzosn.pay.ali.bean.AliPayConst.BIZ_CONTENT;
 import static com.egzosn.pay.ali.bean.AliPayConst.CODE;
+import static com.egzosn.pay.ali.bean.AliPayConst.DBACK_AMOUNT;
 import static com.egzosn.pay.ali.bean.AliPayConst.HTTPS_REQ_URL;
 import static com.egzosn.pay.ali.bean.AliPayConst.NOTIFY_URL;
 import static com.egzosn.pay.ali.bean.AliPayConst.PASSBACK_PARAMS;
@@ -61,7 +62,7 @@ import com.egzosn.pay.common.util.str.StringUtils;
  * email egzosn@gmail.com
  * date 2017-2-22 20:09
  */
-public class AliPayService extends BasePayService {
+public class AliPayService extends BasePayService implements AliPayServiceInf {
 
 
     /**
@@ -184,7 +185,6 @@ public class AliPayService extends BasePayService {
     }
 
 
-
     /**
      * 生成并设置签名
      *
@@ -249,6 +249,8 @@ public class AliPayService extends BasePayService {
             case PAGE:
                 bizContent.put(PASSBACK_PARAMS, order.getAddition());
                 bizContent.put(PRODUCT_CODE, "FAST_INSTANT_TRADE_PAY");
+                bizContent.put(AliPayConst.REQUEST_FROM_URL, payConfigStorage.getReturnUrl());
+                OrderParaStructure.loadParameters(bizContent, AliPayConst.REQUEST_FROM_URL, order);
                 setReturnUrl(orderInfo, order);
                 break;
             case WAP:
@@ -259,6 +261,9 @@ public class AliPayService extends BasePayService {
                 //默认值为QUICK_WAP_PAY。
                 bizContent.put(PRODUCT_CODE, "QUICK_WAP_PAY");
                 OrderParaStructure.loadParameters(bizContent, PRODUCT_CODE, order);
+
+                bizContent.put(AliPayConst.QUIT_URL, payConfigStorage.getReturnUrl());
+                OrderParaStructure.loadParameters(bizContent, AliPayConst.QUIT_URL, order);
                 setReturnUrl(orderInfo, order);
                 break;
             case APP:
@@ -314,12 +319,24 @@ public class AliPayService extends BasePayService {
      * @return 放回公共请求参数
      */
     protected Map getPublicParameters(TransactionType transactionType) {
+        boolean depositBack = transactionType == AliTransactionType.REFUND_DEPOSITBACK_COMPLETED;
         Map orderInfo = new TreeMap<>();
         orderInfo.put("app_id", payConfigStorage.getAppId());
-        orderInfo.put("method", transactionType.getMethod());
         orderInfo.put("charset", payConfigStorage.getInputCharset());
-        orderInfo.put("timestamp", DateUtils.format(new Date()));
-        orderInfo.put("version", "1.0");
+        String method = "method";
+        String version = "1.0";
+        if (depositBack) {
+            method = "msg_method";
+            orderInfo.put("utc_timestamp", System.currentTimeMillis());
+            version = "1.1";
+        }
+        else {
+            orderInfo.put("timestamp", DateUtils.format(new Date()));
+        }
+
+        orderInfo.put(method, transactionType.getMethod());
+        orderInfo.put("version", version);
+
         loadCertSn(orderInfo);
         return orderInfo;
     }
@@ -482,7 +499,6 @@ public class AliPayService extends BasePayService {
     }
 
 
-
     /**
      * 交易关闭接口
      *
@@ -544,12 +560,19 @@ public class AliPayService extends BasePayService {
 
     /**
      * 申请退款接口
+     * 兼容 收单退款冲退完成通知 {@link #refundDepositBackCompleted(RefundOrder)} 与 {@link com.egzosn.pay.ali.bean.RefundDepositBackCompletedNotify}
      *
      * @param refundOrder 退款订单信息
      * @return 返回支付方申请退款后的结果
      */
     @Override
     public AliRefundResult refund(RefundOrder refundOrder) {
+        if (null != refundOrder.getTransactionType() && refundOrder.getTransactionType() == AliTransactionType.REFUND_DEPOSITBACK_COMPLETED) {
+            String status = refundDepositBackCompleted(refundOrder);
+            AliRefundResult result = new AliRefundResult();
+            result.setCode(status);
+            return result;
+        }
         //获取公共参数
         Map parameters = getPublicParameters(AliTransactionType.REFUND);
         setAppAuthToken(parameters, refundOrder.getAttrs());
@@ -557,9 +580,9 @@ public class AliPayService extends BasePayService {
         Map bizContent = getBizContent(refundOrder.getTradeNo(), refundOrder.getOutTradeNo(), null);
         OrderParaStructure.loadParameters(bizContent, AliPayConst.OUT_REQUEST_NO, refundOrder.getRefundNo());
         bizContent.put("refund_amount", Util.conversionAmount(refundOrder.getRefundAmount()));
-        OrderParaStructure.loadParameters(bizContent,  AliPayConst.REFUND_REASON, refundOrder.getDescription());
-        OrderParaStructure.loadParameters(bizContent,  AliPayConst.REFUND_REASON, refundOrder);
-        OrderParaStructure.loadParameters(bizContent,"refund_royalty_parameters", refundOrder);
+        OrderParaStructure.loadParameters(bizContent, AliPayConst.REFUND_REASON, refundOrder.getDescription());
+        OrderParaStructure.loadParameters(bizContent, AliPayConst.REFUND_REASON, refundOrder);
+        OrderParaStructure.loadParameters(bizContent, "refund_royalty_parameters", refundOrder);
         OrderParaStructure.loadParameters(bizContent, AliPayConst.QUERY_OPTIONS, refundOrder);
         //设置请求参数的集合
         parameters.put(BIZ_CONTENT, JSON.toJSONString(bizContent));
@@ -586,7 +609,7 @@ public class AliPayService extends BasePayService {
         setAppAuthToken(parameters, refundOrder.getAttrs());
         Map bizContent = getBizContent(refundOrder.getTradeNo(), refundOrder.getOutTradeNo(), null);
         OrderParaStructure.loadParameters(bizContent, AliPayConst.OUT_REQUEST_NO, refundOrder.getRefundNo());
-        OrderParaStructure.loadParameters(bizContent,  AliPayConst.QUERY_OPTIONS, refundOrder);
+        OrderParaStructure.loadParameters(bizContent, AliPayConst.QUERY_OPTIONS, refundOrder);
 //        bizContent.putAll(refundOrder.getAttrs());
         //设置请求参数的集合
         parameters.put(BIZ_CONTENT, JSON.toJSONString(bizContent));
@@ -782,4 +805,37 @@ public class AliPayService extends BasePayService {
     public PayMessage createMessage(Map message) {
         return AliPayMessage.create(message);
     }
+
+    /**
+     * 收单退款冲退完成通知
+     * 退款存在退到银行卡场景下时,收单会根据银行回执消息发送退款完成信息
+     *
+     * @param refundOrder 退款订单
+     * @return fail    消息获取失败	是  success	消息获取成功	否
+     */
+    @Override
+    public String refundDepositBackCompleted(RefundOrder refundOrder) {
+        //获取公共参数
+        Map parameters = getPublicParameters(refundOrder.getTransactionType());
+        OrderParaStructure.loadParameters(parameters, "notify_id", refundOrder);
+        OrderParaStructure.loadParameters(parameters, "msg_type", refundOrder);
+        OrderParaStructure.loadParameters(parameters, "msg_uid", refundOrder);
+        OrderParaStructure.loadParameters(parameters, "msg_app_id", refundOrder);
+
+        Map bizContent = getBizContent(refundOrder.getTradeNo(), refundOrder.getOutTradeNo(), null);
+        OrderParaStructure.loadParameters(bizContent, AliPayConst.OUT_REQUEST_NO, refundOrder.getRefundNo());
+        OrderParaStructure.loadParameters(bizContent, "dback_status", refundOrder);
+        bizContent.put(DBACK_AMOUNT, refundOrder.getRefundAmount());
+        OrderParaStructure.loadParameters(bizContent, DBACK_AMOUNT, refundOrder);
+        OrderParaStructure.loadParameters(bizContent, "bank_ack_time", refundOrder);
+        OrderParaStructure.loadParameters(bizContent, "est_bank_receipt_time", refundOrder);
+        //设置请求参数的集合
+        parameters.put(BIZ_CONTENT, JSON.toJSONString(bizContent));
+        //设置签名
+        setSign(parameters);
+
+        return null;
+    }
+
+
 }
diff --git a/pay-java-ali/src/main/java/com/egzosn/pay/ali/api/AliPayServiceInf.java b/pay-java-ali/src/main/java/com/egzosn/pay/ali/api/AliPayServiceInf.java
new file mode 100644
index 0000000..c425c45
--- /dev/null
+++ b/pay-java-ali/src/main/java/com/egzosn/pay/ali/api/AliPayServiceInf.java
@@ -0,0 +1,22 @@
+package com.egzosn.pay.ali.api;
+
+import com.egzosn.pay.common.bean.RefundOrder;
+
+/**
+ * 支付宝定制化服务接口
+ * @author Egan
+ * 
+ * email egan@egzosn.com
+ * date 2021/12/4
+ * 
+ */ +public interface AliPayServiceInf { + + /** + * 收单退款冲退完成通知 + * 退款存在退到银行卡场景下时,收单会根据银行回执消息发送退款完成信息 + * @param refundOrder 退款订单 + * @return fail 消息获取失败 是 success 消息获取成功 否 + */ + String refundDepositBackCompleted(RefundOrder refundOrder); +} diff --git a/pay-java-ali/src/main/java/com/egzosn/pay/ali/bean/AliPayConst.java b/pay-java-ali/src/main/java/com/egzosn/pay/ali/bean/AliPayConst.java index edd824b..dd09e40 100644 --- a/pay-java-ali/src/main/java/com/egzosn/pay/ali/bean/AliPayConst.java +++ b/pay-java-ali/src/main/java/com/egzosn/pay/ali/bean/AliPayConst.java @@ -68,4 +68,13 @@ public final class AliPayConst { public static final String REFUND_REASON = "refund_reason"; public static final String QUERY_OPTIONS = "query_options"; public static final String OUT_REQUEST_NO = "out_request_no"; + /** + * 用户付款中途退出返回商户网站的地址 + */ + public static final String QUIT_URL = "quit_url"; + /** + * 请求来源地址。如果使用ALIAPP的集成方式,用户中途取消支付会返回该地址。 + */ + public static final String REQUEST_FROM_URL = "request_from_url"; + public static final String DBACK_AMOUNT = "dback_amount"; } diff --git a/pay-java-ali/src/main/java/com/egzosn/pay/ali/bean/RefundDepositBackCompletedNotify.java b/pay-java-ali/src/main/java/com/egzosn/pay/ali/bean/RefundDepositBackCompletedNotify.java new file mode 100644 index 0000000..33f1978 --- /dev/null +++ b/pay-java-ali/src/main/java/com/egzosn/pay/ali/bean/RefundDepositBackCompletedNotify.java @@ -0,0 +1,139 @@ +package com.egzosn.pay.ali.bean; + +import com.egzosn.pay.common.bean.RefundOrder; +import com.egzosn.pay.common.bean.SignType; + +/** + * 收单退款冲退完成通知 + * + * @author Egan + *
+ * email egan@egzosn.com
+ * date 2021/12/4
+ * 
+ */ +public class RefundDepositBackCompletedNotify extends RefundOrder { + + /** + * 通知 + */ + private String notifyId; + /** + * 消息类型。目前支持类型:sys:系统消息;usr,用户消息;app,应用消息 + */ + private String msgType; + + /** + * 消息归属的商户支付宝uid。用户消息和应用消息时非空 + */ + private String msgUid; + + /** + * 消息归属方的应用id。应用消息时非空 + */ + private String msgAppId; + /** + * 加密算法 + */ + private SignType encryptType; + + + /** + * 银行卡冲退状态。S-成功,F-失败。银行卡冲退失败,资金自动转入用户支付宝余额。 + */ + private String dbackStatus; + /** + * 银行卡冲退金额 + */ + private String dbackAmount; + /** + * 银行响应时间,格式为yyyy-MM-dd HH:mm:ss + */ + private String bankAckTime; + /** + * 预估银行入账时间,格式为yyyy-MM-dd HH:mm:ss + */ + private String estBankReceiptTime; + + public String getNotifyId() { + return notifyId; + } + + public void setNotifyId(String notifyId) { + this.notifyId = notifyId; + addAttr("notify_id", notifyId); + } + + public String getMsgType() { + return msgType; + } + + public void setMsgType(String msgType) { + this.msgType = msgType; + addAttr("msg_type", msgType); + } + + public String getMsgUid() { + return msgUid; + } + + public void setMsgUid(String msgUid) { + this.msgUid = msgUid; + addAttr("msg_uid", msgUid); + } + + public String getMsgAppId() { + return msgAppId; + } + + public void setMsgAppId(String msgAppId) { + this.msgAppId = msgAppId; + addAttr("msg_app_id", msgAppId); + } + + public SignType getEncryptType() { + return encryptType; + } + + public void setEncryptType(SignType encryptType) { + this.encryptType = encryptType; + addAttr("encrypt_type", encryptType); + } + + + public String getDbackStatus() { + return dbackStatus; + } + + public void setDbackStatus(String dbackStatus) { + this.dbackStatus = dbackStatus; + addAttr("dback_status", dbackStatus); + } + + public String getDbackAmount() { + return dbackAmount; + } + + public void setDbackAmount(String dbackAmount) { + this.dbackAmount = dbackAmount; + addAttr("dback_amount", dbackAmount); + } + + public String getBankAckTime() { + return bankAckTime; + } + + public void setBankAckTime(String bankAckTime) { + this.bankAckTime = bankAckTime; + addAttr("bank_ack_time", bankAckTime); + } + + public String getEstBankReceiptTime() { + return estBankReceiptTime; + } + + public void setEstBankReceiptTime(String estBankReceiptTime) { + this.estBankReceiptTime = estBankReceiptTime; + addAttr("est_bank_receipt_time", estBankReceiptTime); + } +} -- Gitee From c78182d711f29af5ffdcb2d618b0f9de6e552363 Mon Sep 17 00:00:00 2001 From: egzosn Date: Sun, 5 Dec 2021 13:05:06 +0800 Subject: [PATCH 259/299] 2.14.3-b3 --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 154d076..01dac15 100644 --- a/README.md +++ b/README.md @@ -22,7 +22,7 @@ com.egzosn {module-name} - 2.14.3-b2 + 2.14.3-b3 ``` -- Gitee From 2c2e0977857e3f29c9bc31df2e002809fcd85a8a Mon Sep 17 00:00:00 2001 From: egzosn Date: Sun, 2 Jan 2022 20:41:18 +0800 Subject: [PATCH 260/299] =?UTF-8?q?2.14.3-b4=E5=BC=80=E5=A7=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pay-java-ali/pom.xml | 2 +- pay-java-baidu/pom.xml | 2 +- pay-java-common/pom.xml | 2 +- pay-java-demo/pom.xml | 2 +- pay-java-fuiou/pom.xml | 2 +- pay-java-payoneer/pom.xml | 2 +- pay-java-paypal/pom.xml | 2 +- pay-java-union/pom.xml | 2 +- pay-java-web-support/pom.xml | 2 +- pay-java-wx-youdian/pom.xml | 2 +- pay-java-wx/pom.xml | 2 +- pay-java-yiji/pom.xml | 2 +- pom.xml | 4 ++-- 13 files changed, 14 insertions(+), 14 deletions(-) diff --git a/pay-java-ali/pom.xml b/pay-java-ali/pom.xml index 863e118..e222896 100644 --- a/pay-java-ali/pom.xml +++ b/pay-java-ali/pom.xml @@ -5,7 +5,7 @@ pay-java-parent com.egzosn - 2.14.3-b3 + 2.14.3-b4 4.0.0 pay-java-ali diff --git a/pay-java-baidu/pom.xml b/pay-java-baidu/pom.xml index 54da1d4..c8372c4 100644 --- a/pay-java-baidu/pom.xml +++ b/pay-java-baidu/pom.xml @@ -5,7 +5,7 @@ pay-java-parent com.egzosn - 2.14.3-b3 + 2.14.3-b4 4.0.0 pay-java-baidu diff --git a/pay-java-common/pom.xml b/pay-java-common/pom.xml index 055aed4..fab77ed 100644 --- a/pay-java-common/pom.xml +++ b/pay-java-common/pom.xml @@ -5,7 +5,7 @@ pay-java-parent com.egzosn - 2.14.3-b3 + 2.14.3-b4 4.0.0 jar diff --git a/pay-java-demo/pom.xml b/pay-java-demo/pom.xml index 78644eb..91ed777 100644 --- a/pay-java-demo/pom.xml +++ b/pay-java-demo/pom.xml @@ -5,7 +5,7 @@ pay-java-parent com.egzosn - 2.14.3-b3 + 2.14.3-b4 4.0.0 war diff --git a/pay-java-fuiou/pom.xml b/pay-java-fuiou/pom.xml index a941445..76e3587 100644 --- a/pay-java-fuiou/pom.xml +++ b/pay-java-fuiou/pom.xml @@ -5,7 +5,7 @@ pay-java-parent com.egzosn - 2.14.3-b3 + 2.14.3-b4 4.0.0 pay-java-fuiou diff --git a/pay-java-payoneer/pom.xml b/pay-java-payoneer/pom.xml index 0a7817c..4e67128 100644 --- a/pay-java-payoneer/pom.xml +++ b/pay-java-payoneer/pom.xml @@ -5,7 +5,7 @@ pay-java-parent com.egzosn - 2.14.3-b3 + 2.14.3-b4 4.0.0 pay-java-payoneer diff --git a/pay-java-paypal/pom.xml b/pay-java-paypal/pom.xml index d74b445..f4de33c 100644 --- a/pay-java-paypal/pom.xml +++ b/pay-java-paypal/pom.xml @@ -5,7 +5,7 @@ pay-java-parent com.egzosn - 2.14.3-b3 + 2.14.3-b4 4.0.0 diff --git a/pay-java-union/pom.xml b/pay-java-union/pom.xml index d5b733d..1dfb536 100644 --- a/pay-java-union/pom.xml +++ b/pay-java-union/pom.xml @@ -5,7 +5,7 @@ pay-java-parent com.egzosn - 2.14.3-b3 + 2.14.3-b4 4.0.0 diff --git a/pay-java-web-support/pom.xml b/pay-java-web-support/pom.xml index 126b206..630aef4 100644 --- a/pay-java-web-support/pom.xml +++ b/pay-java-web-support/pom.xml @@ -5,7 +5,7 @@ pay-java-parent com.egzosn - 2.14.3-b3 + 2.14.3-b4 4.0.0 jar diff --git a/pay-java-wx-youdian/pom.xml b/pay-java-wx-youdian/pom.xml index 2bea82d..60f911f 100644 --- a/pay-java-wx-youdian/pom.xml +++ b/pay-java-wx-youdian/pom.xml @@ -5,7 +5,7 @@ pay-java-parent com.egzosn - 2.14.3-b3 + 2.14.3-b4 4.0.0 pay-java-wx-youdian diff --git a/pay-java-wx/pom.xml b/pay-java-wx/pom.xml index fddcf5b..ebdb3e6 100644 --- a/pay-java-wx/pom.xml +++ b/pay-java-wx/pom.xml @@ -5,7 +5,7 @@ pay-java-parent com.egzosn - 2.14.3-b3 + 2.14.3-b4 4.0.0 pay-java-wx diff --git a/pay-java-yiji/pom.xml b/pay-java-yiji/pom.xml index ee63230..19ca7d2 100644 --- a/pay-java-yiji/pom.xml +++ b/pay-java-yiji/pom.xml @@ -5,7 +5,7 @@ pay-java-parent com.egzosn - 2.14.3-b3 + 2.14.3-b4 4.0.0 diff --git a/pom.xml b/pom.xml index 06f14ba..3dc1670 100644 --- a/pom.xml +++ b/pom.xml @@ -7,7 +7,7 @@ com.egzosn pay-java-parent pom - 2.14.3-b3 + 2.14.3-b4 Pay Java - Parent Pay Java Parent @@ -70,7 +70,7 @@ - 2.14.3-b3 + 2.14.3-b4 4.5.4 1.2.17 1.2.73 -- Gitee From 70461510d4e06e46680f3aa7bfc7f737ce23cec5 Mon Sep 17 00:00:00 2001 From: egzosn Date: Sun, 2 Jan 2022 20:41:38 +0800 Subject: [PATCH 261/299] =?UTF-8?q?=E7=8A=B6=E6=80=81=E4=B8=8D=E5=8C=BA?= =?UTF-8?q?=E5=88=86=E5=A4=A7=E5=B0=8F=E5=86=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/egzosn/pay/paypal/v2/api/PayPalPayService.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pay-java-paypal/src/main/java/com/egzosn/pay/paypal/v2/api/PayPalPayService.java b/pay-java-paypal/src/main/java/com/egzosn/pay/paypal/v2/api/PayPalPayService.java index 8cab6f9..9dc566f 100644 --- a/pay-java-paypal/src/main/java/com/egzosn/pay/paypal/v2/api/PayPalPayService.java +++ b/pay-java-paypal/src/main/java/com/egzosn/pay/paypal/v2/api/PayPalPayService.java @@ -297,7 +297,7 @@ public class PayPalPayService extends BasePayService implem header.addHeader(new BasicHeader("prefer", "return=representation")); entity.setHeaders(header); JSONObject resp = getHttpRequestTemplate().postForObject(getReqUrl(order.getTransactionType()), entity, JSONObject.class); - if ("created".equals(resp.getString("state")) && StringUtils.isNotEmpty(resp.getString("id"))) { + if ("created".equalsIgnoreCase(resp.getString("state")) && StringUtils.isNotEmpty(resp.getString("id"))) { order.setTradeNo(resp.getString("id")); } return preOrderHandler(resp, order); -- Gitee From d1e26df6265bac463366419977ff67aeccbe8c90 Mon Sep 17 00:00:00 2001 From: egzosn Date: Sun, 9 Jan 2022 15:18:34 +0800 Subject: [PATCH 262/299] =?UTF-8?q?=E5=BE=AE=E4=BF=A1V3=E8=AE=A2=E5=8D=95?= =?UTF-8?q?=E5=85=B3=E9=97=AD=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/egzosn/pay/wx/v3/api/WxParameterStructure.java | 5 +++-- .../src/main/java/com/egzosn/pay/wx/v3/api/WxPayService.java | 4 ++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/api/WxParameterStructure.java b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/api/WxParameterStructure.java index bd0b101..d144801 100644 --- a/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/api/WxParameterStructure.java +++ b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/api/WxParameterStructure.java @@ -82,12 +82,13 @@ public class WxParameterStructure { * * @return 商户相关信息 */ - public String getMchParameters() { + public Map getMchParameters() { Map attr = initSubMchId(null); OrderParaStructure.loadParameters(attr, payConfigStorage.isPartner() ? WxConst.SP_MCH_ID : WxConst.MCH_ID, payConfigStorage.getMchId()); - return UriVariables.getMapToParameters(attr); + return attr; } + /** * 初始化商户相关信息 * diff --git a/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/api/WxPayService.java b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/api/WxPayService.java index e73284d..17c21db 100644 --- a/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/api/WxPayService.java +++ b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/api/WxPayService.java @@ -462,7 +462,7 @@ public class WxPayService extends BasePayService { public Map query(AssistOrder assistOrder) { String transactionId = assistOrder.getTradeNo(); String outTradeNo = assistOrder.getOutTradeNo(); - String parameters = wxParameterStructure.getMchParameters(); + String parameters = UriVariables.getMapToParameters(wxParameterStructure.getMchParameters()); WxTransactionType transactionType = WxTransactionType.QUERY_TRANSACTION_ID; String uriVariable = transactionId; if (StringUtils.isNotEmpty(outTradeNo)) { @@ -495,7 +495,7 @@ public class WxPayService extends BasePayService { */ @Override public Map close(AssistOrder assistOrder) { - String parameters = wxParameterStructure.getMchParameters(); + String parameters = JSON.toJSONString(wxParameterStructure.getMchParameters()); return getAssistService().doExecute(parameters, WxTransactionType.CLOSE, assistOrder.getOutTradeNo()); } -- Gitee From 47b2bf9b3e56c417afff0175ef2f19676ec5f728 Mon Sep 17 00:00:00 2001 From: egzosn Date: Mon, 10 Jan 2022 23:31:37 +0800 Subject: [PATCH 263/299] 2.14.3-b4 --- README.md | 2 +- .../egzosn/pay/baidu/api/BaiduPayService.java | 25 +++---------------- .../pay/demo/controller/AliPayController.java | 2 +- 3 files changed, 6 insertions(+), 23 deletions(-) diff --git a/README.md b/README.md index 01dac15..fbe4f56 100644 --- a/README.md +++ b/README.md @@ -22,7 +22,7 @@ com.egzosn {module-name} - 2.14.3-b3 + 2.14.3-b4 ``` diff --git a/pay-java-baidu/src/main/java/com/egzosn/pay/baidu/api/BaiduPayService.java b/pay-java-baidu/src/main/java/com/egzosn/pay/baidu/api/BaiduPayService.java index e529eba..468a46e 100644 --- a/pay-java-baidu/src/main/java/com/egzosn/pay/baidu/api/BaiduPayService.java +++ b/pay-java-baidu/src/main/java/com/egzosn/pay/baidu/api/BaiduPayService.java @@ -136,11 +136,10 @@ public class BaiduPayService extends BasePayService { * * @param publicKey 公钥原始字符串 * @return X509标准公钥 - * @throws InvalidKeySpecException - * @throws NoSuchAlgorithmException + * @throws InvalidKeySpecException InvalidKeySpecException + * @throws NoSuchAlgorithmException NoSuchAlgorithmException */ - private static PublicKey getPublicKeyX509(String publicKey) throws InvalidKeySpecException, - NoSuchAlgorithmException { + private static PublicKey getPublicKeyX509(String publicKey) throws InvalidKeySpecException, NoSuchAlgorithmException { if (StringUtils.isEmpty(publicKey)) { return null; } @@ -154,7 +153,7 @@ public class BaiduPayService extends BasePayService { * * @param params 待签名参数集合 * @return 待签名内容 - * @throws UnsupportedEncodingException + * @throws UnsupportedEncodingException UnsupportedEncodingException */ private String signContent(Map params) throws UnsupportedEncodingException { Map sortedParams = new TreeMap<>(new Comparator() { @@ -198,22 +197,6 @@ public class BaiduPayService extends BasePayService { return StringUtils.isNotBlank(key) && !SIGN_KEY.equalsIgnoreCase(key); } - - /** - * 验证签名 - * - * @param params 参数集 - * @param sign 签名原文 - * @return 结果 - */ - public boolean signVerify(Map params, String sign) { - String rsaSign = String.valueOf(params.get(RSA_SIGN)); - String targetRsaSign = getRsaSign(params, RSA_SIGN); - LOG.debug("百度返回的签名: " + rsaSign + " 本地产生的签名: " + targetRsaSign); - return StringUtils.equals(rsaSign, targetRsaSign); - } - - /** * 返回创建的订单信息 * diff --git a/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/AliPayController.java b/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/AliPayController.java index f88f71a..7b7b4f7 100644 --- a/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/AliPayController.java +++ b/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/AliPayController.java @@ -76,8 +76,8 @@ public class AliPayController { //设置证书存储方式,这里为路径 aliPayConfigStorage.setCertStoreType(CertStoreType.CLASS_PATH); aliPayConfigStorage.setMerchantCert("ali/appCertPublicKey_2016080400165436.crt"); - aliPayConfigStorage.setAliPayCert("ali/alipayCertPublicKey_RSA2.crt"); aliPayConfigStorage.setAliPayRootCert("ali/alipayRootCert.crt"); + aliPayConfigStorage.setAliPayCert("ali/alipayCertPublicKey_RSA2.crt"); } @PostConstruct -- Gitee From 0faa3014f047edd0402a3fcb5ef73cd10d029064 Mon Sep 17 00:00:00 2001 From: egan Date: Sun, 20 Mar 2022 23:43:17 +0800 Subject: [PATCH 264/299] =?UTF-8?q?=E6=8F=90=E4=BE=9B=E6=94=AF=E4=BB=98?= =?UTF-8?q?=E8=AE=A2=E5=8D=95=E7=9B=B4=E6=8E=A5=E8=AE=BE=E7=BD=AE=E5=BC=82?= =?UTF-8?q?=E6=AD=A5=E5=9B=9E=E8=B0=83=E5=9C=B0=E5=9D=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../egzosn/pay/common/bean/AssistOrder.java | 12 ++++++++++ .../egzosn/pay/fuiou/api/FuiouPayService.java | 15 +++++++++--- .../egzosn/pay/union/api/UnionPayService.java | 24 +++++++++++++++---- 3 files changed, 44 insertions(+), 7 deletions(-) diff --git a/pay-java-common/src/main/java/com/egzosn/pay/common/bean/AssistOrder.java b/pay-java-common/src/main/java/com/egzosn/pay/common/bean/AssistOrder.java index 67f95f5..52cb462 100644 --- a/pay-java-common/src/main/java/com/egzosn/pay/common/bean/AssistOrder.java +++ b/pay-java-common/src/main/java/com/egzosn/pay/common/bean/AssistOrder.java @@ -29,6 +29,12 @@ public class AssistOrder implements Order { * 交易类型 */ private TransactionType transactionType; + + /** + * 异步回调通知 + */ + private String notifyUrl; + /** * 订单附加信息,可用于预设未提供的参数,这里会覆盖以上所有的订单信息, */ @@ -122,5 +128,11 @@ public class AssistOrder implements Order { getAttrs().put(key, value); } + public String getNotifyUrl() { + return notifyUrl; + } + public void setNotifyUrl(String notifyUrl) { + this.notifyUrl = notifyUrl; + } } diff --git a/pay-java-fuiou/src/main/java/com/egzosn/pay/fuiou/api/FuiouPayService.java b/pay-java-fuiou/src/main/java/com/egzosn/pay/fuiou/api/FuiouPayService.java index d46bc98..e942664 100644 --- a/pay-java-fuiou/src/main/java/com/egzosn/pay/fuiou/api/FuiouPayService.java +++ b/pay-java-fuiou/src/main/java/com/egzosn/pay/fuiou/api/FuiouPayService.java @@ -13,6 +13,7 @@ import com.egzosn.pay.common.bean.BillType; import com.egzosn.pay.common.bean.MethodType; import com.egzosn.pay.common.bean.NoticeParams; import com.egzosn.pay.common.bean.NoticeRequest; +import com.egzosn.pay.common.bean.OrderParaStructure; import com.egzosn.pay.common.bean.PayMessage; import com.egzosn.pay.common.bean.PayOrder; import com.egzosn.pay.common.bean.PayOutMessage; @@ -72,6 +73,10 @@ public class FuiouPayService extends BasePayService { * 3.4订单退款 */ public static final String URL_NewSmpRefundGate = "newSmpRefundGate.do"; + /** + * 异步通知 + */ + public static final String BACK_NOTIFY_URL = "back_notify_url"; /** @@ -199,7 +204,12 @@ public class FuiouPayService extends BasePayService { parameters.put("md5", sign); return parameters; } - + private Map initNotifyUrl(Map parameters, AssistOrder order) { + OrderParaStructure.loadParameters(parameters, BACK_NOTIFY_URL, payConfigStorage.getNotifyUrl()); + OrderParaStructure.loadParameters(parameters, BACK_NOTIFY_URL, order.getNotifyUrl()); + OrderParaStructure.loadParameters(parameters, BACK_NOTIFY_URL, order); + return parameters; + } /** * 按序添加请求参数 * @@ -222,8 +232,7 @@ public class FuiouPayService extends BasePayService { //商户接受支付结果通知地址 parameters.put("page_notify_url", payConfigStorage.getReturnUrl()); //商户接受的支付结果后台通知地址 //非必填 - parameters.put("back_notify_url", StringUtils.isBlank(payConfigStorage.getNotifyUrl()) ? "" : payConfigStorage.getNotifyUrl()); - + initNotifyUrl(parameters, order); if (null != order.getExpirationTime()) { parameters.put("order_valid_time", DateUtils.minutesRemaining(order.getExpirationTime()) + "m"); } diff --git a/pay-java-union/src/main/java/com/egzosn/pay/union/api/UnionPayService.java b/pay-java-union/src/main/java/com/egzosn/pay/union/api/UnionPayService.java index 1cda4f4..42f82f3 100644 --- a/pay-java-union/src/main/java/com/egzosn/pay/union/api/UnionPayService.java +++ b/pay-java-union/src/main/java/com/egzosn/pay/union/api/UnionPayService.java @@ -31,6 +31,7 @@ import com.egzosn.pay.common.bean.BillType; import com.egzosn.pay.common.bean.MethodType; import com.egzosn.pay.common.bean.NoticeParams; +import com.egzosn.pay.common.bean.OrderParaStructure; import com.egzosn.pay.common.bean.PayMessage; import com.egzosn.pay.common.bean.PayOrder; import com.egzosn.pay.common.bean.PayOutMessage; @@ -166,6 +167,21 @@ public class UnionPayService extends BasePayService { return String.format(FILE_TRANS_URL, getReqUrl()); } + /** + * 后台通知地址 + * + * @param parameters 预订单信息 + * @param order 订单 + * @return 预订单信息 + */ + private Map initNotifyUrl(Map parameters, AssistOrder order) { + //后台通知地址 + OrderParaStructure.loadParameters(parameters, SDKConstants.param_backUrl, payConfigStorage.getNotifyUrl()); + OrderParaStructure.loadParameters(parameters, SDKConstants.param_backUrl, order.getNotifyUrl()); + OrderParaStructure.loadParameters(parameters, SDKConstants.param_backUrl, order); + return parameters; + } + /** * 银联全渠道系统,产品参数,除了encoding自行选择外其他不需修改 @@ -182,11 +198,11 @@ public class UnionPayService extends BasePayService { //商户代码 params.put(SDKConstants.param_merId, payConfigStorage.getPid()); - DateFormat df = new SimpleDateFormat("yyyyMMddHHmmss"); //订单发送时间 - params.put(SDKConstants.param_txnTime, df.format(System.currentTimeMillis())); + params.put(SDKConstants.param_txnTime, DateUtils.formatDate(new Date(), DateUtils.YYYYMMDDHHMMSS)); //后台通知地址 params.put(SDKConstants.param_backUrl, payConfigStorage.getNotifyUrl()); + //交易币种 params.put(SDKConstants.param_currencyCode, "156"); //接入类型,商户接入填0 ,不需修改(0:直连商户, 1: 收单机构 2:平台商户) @@ -282,7 +298,7 @@ public class UnionPayService extends BasePayService { Map params = this.getCommonParam(); UnionTransactionType type = (UnionTransactionType) order.getTransactionType(); - + initNotifyUrl(params, order); //设置交易类型相关的参数 type.convertMap(params); @@ -645,7 +661,7 @@ public class UnionPayService extends BasePayService { String responseStr = getHttpRequestTemplate().postForObject(this.getBackTransUrl(), params, String.class); JSONObject response = UriVariables.getParametersToMap(responseStr); - if (this.verify(response)) { + if (this.verify(new NoticeParams(response))) { final UnionRefundResult refundResult = UnionRefundResult.create(response); if (SDKConstants.OK_RESP_CODE.equals(refundResult.getRespCode())) { return refundResult; -- Gitee From d9ced7ea050ca8dbed0ab24d82cba632639f1cf7 Mon Sep 17 00:00:00 2001 From: egan Date: Sun, 20 Mar 2022 23:44:23 +0800 Subject: [PATCH 265/299] =?UTF-8?q?=E6=8F=90=E4=BE=9B=E6=94=AF=E4=BB=98?= =?UTF-8?q?=E8=AE=A2=E5=8D=95=E7=9B=B4=E6=8E=A5=E8=AE=BE=E7=BD=AE=E5=BC=82?= =?UTF-8?q?=E6=AD=A5=E5=9B=9E=E8=B0=83=E5=9C=B0=E5=9D=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/main/java/com/egzosn/pay/ali/api/AliPayService.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) 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 82011b8..4d4ecfd 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 @@ -213,8 +213,10 @@ public class AliPayService extends BasePayService implement return setSign(getOrder(order)); } - private void setNotifyUrl(Map orderInfo, PayOrder order) { - orderInfo.put(NOTIFY_URL, payConfigStorage.getNotifyUrl()); + private void setNotifyUrl(Map orderInfo, AssistOrder order) { +// orderInfo.put(NOTIFY_URL, payConfigStorage.getNotifyUrl()); + OrderParaStructure.loadParameters(orderInfo, NOTIFY_URL, payConfigStorage.getNotifyUrl()); + OrderParaStructure.loadParameters(orderInfo, NOTIFY_URL, order.getNotifyUrl()); OrderParaStructure.loadParameters(orderInfo, NOTIFY_URL, order); } -- Gitee From ab775eefd21604f8253c42998e82eb83faf04a4f Mon Sep 17 00:00:00 2001 From: egan Date: Sun, 20 Mar 2022 23:45:07 +0800 Subject: [PATCH 266/299] =?UTF-8?q?AES=E5=AE=9E=E7=8E=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../pay/ali/utils/AntCertificationUtil.java | 15 +--- .../java/com/egzosn/pay/common/util/XML.java | 5 +- .../pay/common/util/sign/encrypt/AES.java | 77 +++++++++++++++++++ 3 files changed, 81 insertions(+), 16 deletions(-) create mode 100644 pay-java-common/src/main/java/com/egzosn/pay/common/util/sign/encrypt/AES.java diff --git a/pay-java-ali/src/main/java/com/egzosn/pay/ali/utils/AntCertificationUtil.java b/pay-java-ali/src/main/java/com/egzosn/pay/ali/utils/AntCertificationUtil.java index e21af86..4d1ffbc 100644 --- a/pay-java-ali/src/main/java/com/egzosn/pay/ali/utils/AntCertificationUtil.java +++ b/pay-java-ali/src/main/java/com/egzosn/pay/ali/utils/AntCertificationUtil.java @@ -10,9 +10,7 @@ import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.security.Principal; import java.security.PublicKey; -import java.security.Security; import java.security.cert.Certificate; -import java.security.cert.CertificateException; import java.security.cert.CertificateExpiredException; import java.security.cert.CertificateFactory; import java.security.cert.CertificateNotYetValidException; @@ -24,7 +22,6 @@ import java.util.HashMap; import java.util.List; import java.util.Map; -import org.bouncycastle.jce.provider.BouncyCastleProvider; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -44,12 +41,7 @@ import com.egzosn.pay.common.util.str.StringUtils; public class AntCertificationUtil { private static final Logger LOGGER = LoggerFactory.getLogger(AntCertificationUtil.class); - static { - if (null == Security.getProvider("BC")) { - Security.removeProvider("SunEC"); - Security.addProvider(new BouncyCastleProvider()); - } - } + /** * 验证证书是否可信 @@ -283,12 +275,11 @@ public class AntCertificationUtil { private static X509Certificate[] readPemCertChain(String cert) { ByteArrayInputStream inputStream = new ByteArrayInputStream(cert.getBytes()); - CertificateFactory factory = null; try { - factory = CertificateFactory.getInstance("X.509"); + CertificateFactory factory = CertificateFactory.getInstance("X.509", "BC");; Collection certificates = factory.generateCertificates(inputStream); return certificates.toArray(new X509Certificate[certificates.size()]); - } catch (CertificateException e) { + } catch (GeneralSecurityException e) { LOGGER.error("提取根证书失败", e); } return null; 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 5ebf06c..3c18fe8 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 @@ -240,10 +240,7 @@ public class XML { } } } - catch (ParserConfigurationException e) { - throw new PayErrorException(new PayException("XML failure", "XML解析失败\n" + e.getMessage())); - } - catch (SAXException e) { + catch (ParserConfigurationException | SAXException e) { throw new PayErrorException(new PayException("XML failure", "XML解析失败\n" + e.getMessage())); } finally { diff --git a/pay-java-common/src/main/java/com/egzosn/pay/common/util/sign/encrypt/AES.java b/pay-java-common/src/main/java/com/egzosn/pay/common/util/sign/encrypt/AES.java new file mode 100644 index 0000000..bd4bf64 --- /dev/null +++ b/pay-java-common/src/main/java/com/egzosn/pay/common/util/sign/encrypt/AES.java @@ -0,0 +1,77 @@ +package com.egzosn.pay.common.util.sign.encrypt; + +import java.io.IOException; +import java.security.GeneralSecurityException; +import java.security.Security; + +import javax.crypto.Cipher; +import javax.crypto.spec.SecretKeySpec; + +import org.apache.commons.codec.digest.DigestUtils; +import org.bouncycastle.jce.provider.BouncyCastleProvider; + +/** + * AES 加解密 + * + * @author Egan + *
+ *  email egan@egzosn.com
+ *  date 2022/3/20
+ *  
+ */ +public class AES { + /** + * 密钥算法 + */ + private static final String ALGORITHM = "AES"; + /** + * 加解密算法/工作模式/填充方式 + */ + private static final String ALGORITHM_MODE_PADDING = "AES/ECB/PKCS7Padding"; + + static { + if (null == Security.getProvider("BC")) { + Security.removeProvider("SunEC"); + Security.addProvider(new BouncyCastleProvider()); + } + } + + /** + * 解密 + * + * @param content 密文 + * @param privateKey 商户私钥 + * @param characterEncoding 编码格式 + * @return 解密后的字符串 + * @throws GeneralSecurityException 解密异常 + * @throws IOException IOException + */ + public static String decrypt(String content, String privateKey, String characterEncoding) throws GeneralSecurityException, IOException { + byte[] reqInfoB = Base64.decode(content); + String key$ = DigestUtils.md5Hex(privateKey).toLowerCase(); + Cipher cipher = Cipher.getInstance(ALGORITHM_MODE_PADDING, "BC"); + SecretKeySpec secretKeySpec = new SecretKeySpec(key$.getBytes(), ALGORITHM); + cipher.init(Cipher.DECRYPT_MODE, secretKeySpec); + return new String(cipher.doFinal(reqInfoB), characterEncoding); + } + + /** + * 解密 + * + * @param content 密文 + * @param privateKey 商户私钥 + * @param characterEncoding 编码格式 + * @return 解密后的字符串 + * @throws GeneralSecurityException 解密异常 + * @throws IOException IOException + */ + public static String encrypt(String content, String privateKey, String characterEncoding) throws GeneralSecurityException, IOException { + String key$ = DigestUtils.md5Hex(privateKey).toLowerCase(); + Cipher cipher = Cipher.getInstance(ALGORITHM_MODE_PADDING, "BC"); + SecretKeySpec secretKeySpec = new SecretKeySpec(key$.getBytes(), ALGORITHM); + cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec); + byte[] doFinal = cipher.doFinal(content.getBytes(characterEncoding)); + return Base64.encode(doFinal); + } + +} -- Gitee From a4140dab5c72a77016161e559954b2f876d38fc2 Mon Sep 17 00:00:00 2001 From: egan Date: Sun, 20 Mar 2022 23:45:40 +0800 Subject: [PATCH 267/299] =?UTF-8?q?=E8=AF=B7=E6=B1=82=E5=B7=A5=E5=85=B7?= =?UTF-8?q?=E6=96=B0=E5=A2=9E=E6=97=A0=E5=86=85=E5=AE=B9=E8=BF=94=E5=9B=9E?= =?UTF-8?q?=E6=97=B6=E5=A4=84=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/egzosn/pay/common/http/ClientHttpRequest.java | 3 +++ 1 file changed, 3 insertions(+) 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 99ec689..060a6f6 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 @@ -284,6 +284,9 @@ public class ClientHttpRequest extends HttpEntityEnclosingRequestBase impleme final StatusLine statusLine = response.getStatusLine(); final HttpEntity entity = response.getEntity(); + if (null == entity){ + return null; + } String[] value = null; if (null == entity.getContentType()) { value = new String[]{"application/x-www-form-urlencoded"}; -- Gitee From be3b66e025c3c8fc21b8628356e0c7d14a02b157 Mon Sep 17 00:00:00 2001 From: egan Date: Sun, 20 Mar 2022 23:47:12 +0800 Subject: [PATCH 268/299] =?UTF-8?q?paypal=20v1=E9=A2=84=E8=AE=BE=E5=95=86?= =?UTF-8?q?=E6=88=B7=E5=8D=95=E5=8F=B7=EF=BC=9Bv2=20=E7=8A=B6=E6=80=81?= =?UTF-8?q?=E5=88=A4=E6=96=AD=E4=BF=AE=E5=A4=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../egzosn/pay/demo/controller/PayPalV2PayController.java | 8 +++++++- .../java/com/egzosn/pay/paypal/api/PayPalPayService.java | 2 +- .../com/egzosn/pay/paypal/v2/api/PayPalPayService.java | 4 ++-- 3 files changed, 10 insertions(+), 4 deletions(-) diff --git a/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/PayPalV2PayController.java b/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/PayPalV2PayController.java index 0febd38..13f5eeb 100644 --- a/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/PayPalV2PayController.java +++ b/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/PayPalV2PayController.java @@ -165,7 +165,13 @@ public class PayPalV2PayController { * 业务处理在对应的PayMessageHandler里面处理,在哪里设置PayMessageHandler,详情查看{@link com.egzosn.pay.common.api.PayService#setPayMessageHandler(com.egzosn.pay.common.api.PayMessageHandler)} *

* 如果未设置 {@link com.egzosn.pay.common.api.PayMessageHandler} 那么会使用默认的 {@link com.egzosn.pay.common.api.DefaultPayMessageHandler} - * @throws IOException IOException + * 付款之后不会进行扣款,需要调用 {@link PayPalPayService#ordersCapture(java.lang.String)}进行扣款,并返回 captureId使用,后续退款,查订单等等使用,用来替换下单返回的id + * 注意:最好在付款成功之后回调时进行调用 {@link PayPalPayService#ordersCapture(java.lang.String)} + * 确认订单并返回确认后订单信息 + * 注意:此方法一个订单只能调用一次, 建议在支付回调时进行调用 + * 这里主要用来获取captureId使用,后续退款,查订单等等使用,用来替换下单返回的id + * 详情: https://developer.paypal.com/docs/api/orders/v2/#orders_capture + * */ @RequestMapping(value = "payBack.json") public String payBack(HttpServletRequest request) { diff --git a/pay-java-paypal/src/main/java/com/egzosn/pay/paypal/api/PayPalPayService.java b/pay-java-paypal/src/main/java/com/egzosn/pay/paypal/api/PayPalPayService.java index 9d26cf5..d375df6 100644 --- a/pay-java-paypal/src/main/java/com/egzosn/pay/paypal/api/PayPalPayService.java +++ b/pay-java-paypal/src/main/java/com/egzosn/pay/paypal/api/PayPalPayService.java @@ -195,7 +195,7 @@ public class PayPalPayService extends BasePayService { transaction.setDescription(order.getBody()); } transaction.setAmount(amount); - + transaction.setCustom(order.getOutTradeNo()); List transactions = new ArrayList<>(); transactions.add(transaction); diff --git a/pay-java-paypal/src/main/java/com/egzosn/pay/paypal/v2/api/PayPalPayService.java b/pay-java-paypal/src/main/java/com/egzosn/pay/paypal/v2/api/PayPalPayService.java index 9dc566f..7219f41 100644 --- a/pay-java-paypal/src/main/java/com/egzosn/pay/paypal/v2/api/PayPalPayService.java +++ b/pay-java-paypal/src/main/java/com/egzosn/pay/paypal/v2/api/PayPalPayService.java @@ -297,7 +297,7 @@ public class PayPalPayService extends BasePayService implem header.addHeader(new BasicHeader("prefer", "return=representation")); entity.setHeaders(header); JSONObject resp = getHttpRequestTemplate().postForObject(getReqUrl(order.getTransactionType()), entity, JSONObject.class); - if ("created".equalsIgnoreCase(resp.getString("state")) && StringUtils.isNotEmpty(resp.getString("id"))) { + if ("created".equalsIgnoreCase(resp.getString("status")) && StringUtils.isNotEmpty(resp.getString("id"))) { order.setTradeNo(resp.getString("id")); } return preOrderHandler(resp, order); @@ -390,7 +390,7 @@ public class PayPalPayService extends BasePayService implem */ @Override public Map ordersCapture(String tradeNo) { - JSONObject ordersCaptureInfo = getHttpRequestTemplate().getForObject(getReqUrl(PayPalTransactionType.ORDERS_CAPTURE), authHeader(), JSONObject.class, tradeNo); + JSONObject ordersCaptureInfo = getHttpRequestTemplate().postForObject(getReqUrl(PayPalTransactionType.ORDERS_CAPTURE), authHeader(), JSONObject.class, tradeNo); // String captureId = ordersCaptureInfo.getJSONArray("purchaseUnits").getJSONObject(0).getJSONObject("payments").getJSONArray("captures").getJSONObject(0).getString("id"); return ordersCaptureInfo; } -- Gitee From 80ce0950ea82813bdc9d60be5792460e6d34b0f0 Mon Sep 17 00:00:00 2001 From: egan Date: Sun, 20 Mar 2022 23:48:47 +0800 Subject: [PATCH 269/299] =?UTF-8?q?v3=E5=88=A0=E9=99=A4=E5=88=86=E8=B4=A6?= =?UTF-8?q?=E6=8E=A5=E6=94=B6=E6=96=B9=E8=AF=B7=E6=B1=82url=E5=9C=B0?= =?UTF-8?q?=E5=9D=80=E9=94=99=E8=AF=AF=20#88=20=E8=AE=A2=E5=8D=95=E5=85=B3?= =?UTF-8?q?=E9=97=AD=E6=A0=B9=E6=8D=AE=E7=8A=B6=E6=80=81=E7=A0=81=E8=BF=9B?= =?UTF-8?q?=E8=A1=8C=E5=88=A4=E6=96=AD=E6=88=90=E5=8A=9F=E5=A4=B1=E8=B4=A5?= =?UTF-8?q?=20=E7=A7=BB=E9=99=A4=E5=BC=82=E6=AD=A5=E5=8A=A0=E8=BD=BD?= =?UTF-8?q?=E8=AF=81=E4=B9=A6=E6=96=B9=E5=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../wx/v3/api/DefaultWxPayAssistService.java | 24 ++++++++++------- .../pay/wx/v3/api/WxParameterStructure.java | 6 ++--- .../pay/wx/v3/api/WxPayAssistService.java | 12 ++++++++- .../pay/wx/v3/api/WxPayConfigStorage.java | 3 ++- .../egzosn/pay/wx/v3/api/WxPayService.java | 27 ++++++++++--------- .../bean/WxProfitSharingTransactionType.java | 14 +++++----- .../v3/bean/sharing/ProfitSharingOrder.java | 2 +- .../pay/wx/v3/utils/AntCertificationUtil.java | 10 +++---- 8 files changed, 57 insertions(+), 41 deletions(-) diff --git a/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/api/DefaultWxPayAssistService.java b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/api/DefaultWxPayAssistService.java index 9d6ab0d..e9daaf6 100644 --- a/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/api/DefaultWxPayAssistService.java +++ b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/api/DefaultWxPayAssistService.java @@ -2,25 +2,19 @@ package com.egzosn.pay.wx.v3.api; import java.io.ByteArrayInputStream; import java.nio.charset.StandardCharsets; -import java.security.Signature; import java.security.cert.Certificate; import java.util.Map; -import java.util.TreeMap; import org.apache.http.HttpEntity; import org.apache.http.entity.ContentType; import org.apache.http.message.BasicHeader; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import static org.apache.http.entity.ContentType.APPLICATION_JSON; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONArray; import com.alibaba.fastjson.JSONObject; -import com.alibaba.fastjson.serializer.SerializerFeature; import com.egzosn.pay.common.bean.MethodType; -import com.egzosn.pay.common.bean.NoticeParams; import com.egzosn.pay.common.bean.PayOrder; import com.egzosn.pay.common.bean.TransactionType; import com.egzosn.pay.common.exception.PayErrorException; @@ -30,9 +24,6 @@ import com.egzosn.pay.common.http.ResponseEntity; import com.egzosn.pay.common.http.UriVariables; import com.egzosn.pay.common.util.DateUtils; import com.egzosn.pay.common.util.sign.SignTextUtils; -import com.egzosn.pay.common.util.sign.encrypt.Base64; -import com.egzosn.pay.common.util.sign.encrypt.RSA; -import com.egzosn.pay.common.util.sign.encrypt.RSA2; import com.egzosn.pay.common.util.str.StringUtils; import com.egzosn.pay.wx.bean.WxPayError; import com.egzosn.pay.wx.v3.bean.WxTransactionType; @@ -84,7 +75,7 @@ public class DefaultWxPayAssistService implements WxPayAssistService { * @param uriVariables 用于匹配表达式 * @return 响应内容体 */ - public JSONObject doExecute(String body, TransactionType transactionType, Object... uriVariables) { + public ResponseEntity doExecuteEntity(String body, TransactionType transactionType, Object... uriVariables) { String reqUrl = UriVariables.getUri(wxPayService.getReqUrl(transactionType), uriVariables); MethodType method = MethodType.valueOf(transactionType.getMethod()); if (MethodType.GET == method && StringUtils.isNotEmpty(body)) { @@ -93,6 +84,19 @@ public class DefaultWxPayAssistService implements WxPayAssistService { } HttpEntity entity = buildHttpEntity(reqUrl, body, transactionType.getMethod()); ResponseEntity responseEntity = requestTemplate.doExecuteEntity(reqUrl, entity, JSONObject.class, method); + return responseEntity; + } + + /** + * 发起请求 + * + * @param body 请求内容 + * @param transactionType 交易类型 + * @param uriVariables 用于匹配表达式 + * @return 响应内容体 + */ + public JSONObject doExecute(String body, TransactionType transactionType, Object... uriVariables) { + final ResponseEntity responseEntity = doExecuteEntity(body, transactionType, uriVariables); int statusCode = responseEntity.getStatusCode(); JSONObject responseBody = responseEntity.getBody(); if (statusCode >= 400) { diff --git a/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/api/WxParameterStructure.java b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/api/WxParameterStructure.java index d144801..24c2553 100644 --- a/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/api/WxParameterStructure.java +++ b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/api/WxParameterStructure.java @@ -4,10 +4,9 @@ import java.util.HashMap; import java.util.LinkedHashMap; import java.util.Map; -import com.egzosn.pay.common.bean.Order; +import com.egzosn.pay.common.bean.AssistOrder; import com.egzosn.pay.common.bean.OrderParaStructure; import com.egzosn.pay.common.bean.PayOrder; -import com.egzosn.pay.common.http.UriVariables; import com.egzosn.pay.common.util.MapGen; import com.egzosn.pay.wx.v3.utils.WxConst; @@ -71,8 +70,9 @@ public class WxParameterStructure { * @param parameters 订单参数 * @param order 订单信息 */ - public void initNotifyUrl(Map parameters, Order order) { + public void initNotifyUrl(Map parameters, AssistOrder order) { OrderParaStructure.loadParameters(parameters, WxConst.NOTIFY_URL, payConfigStorage.getNotifyUrl()); + OrderParaStructure.loadParameters(parameters, WxConst.NOTIFY_URL, order.getNotifyUrl()); OrderParaStructure.loadParameters(parameters, WxConst.NOTIFY_URL, order); } diff --git a/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/api/WxPayAssistService.java b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/api/WxPayAssistService.java index 58bfe74..92e45ef 100644 --- a/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/api/WxPayAssistService.java +++ b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/api/WxPayAssistService.java @@ -8,7 +8,7 @@ import org.apache.http.HttpEntity; import com.alibaba.fastjson.JSONObject; import com.egzosn.pay.common.bean.PayOrder; import com.egzosn.pay.common.bean.TransactionType; -import com.egzosn.pay.common.http.HttpStringEntity; +import com.egzosn.pay.common.http.ResponseEntity; /** * 微信支付辅助服务 @@ -40,6 +40,16 @@ public interface WxPayAssistService { */ JSONObject doExecute(String body, TransactionType transactionType, Object... uriVariables); + /** + * 发起请求 + * + * @param body 请求内容 + * @param transactionType 交易类型 + * @param uriVariables 用于匹配表达式 + * @return 响应内容体 + */ + ResponseEntity doExecuteEntity(String body, TransactionType transactionType, Object... uriVariables); + /** * 发起请求 * diff --git a/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/api/WxPayConfigStorage.java b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/api/WxPayConfigStorage.java index 63af5eb..02e0747 100644 --- a/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/api/WxPayConfigStorage.java +++ b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/api/WxPayConfigStorage.java @@ -72,7 +72,7 @@ public class WxPayConfigStorage extends BasePayConfigStorage { /** * 证书信息 */ - private CertEnvironment certEnvironment; + private volatile CertEnvironment certEnvironment; /** * 是否为服务商模式, 默认为false @@ -233,6 +233,7 @@ public class WxPayConfigStorage extends BasePayConfigStorage { } public CertEnvironment getCertEnvironment() { + loadCertEnvironment(); return certEnvironment; } diff --git a/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/api/WxPayService.java b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/api/WxPayService.java index 17c21db..8d7b3f6 100644 --- a/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/api/WxPayService.java +++ b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/api/WxPayService.java @@ -135,11 +135,11 @@ public class WxPayService extends BasePayService { @Override protected void initAfter() { payConfigStorage.setPartner(StringUtils.isNotEmpty(payConfigStorage.getSubMchId())); - new Thread(() -> { - payConfigStorage.loadCertEnvironment(); - wxParameterStructure = new WxParameterStructure(payConfigStorage); - getAssistService(); - }).start(); +// new Thread(() -> { + payConfigStorage.loadCertEnvironment(); + wxParameterStructure = new WxParameterStructure(payConfigStorage); + getAssistService(); +// }).start(); } @@ -235,8 +235,8 @@ public class WxPayService extends BasePayService { public JSONObject unifiedOrder(PayOrder order) { //统一下单 - Map parameters = wxParameterStructure.initPartner(null); - ; + Map parameters = wxParameterStructure.initPartner(null); + ; // wxParameterStructure.getPublicParameters(parameters); // 商品描述 OrderParaStructure.loadParameters(parameters, WxConst.DESCRIPTION, order.getSubject()); @@ -261,7 +261,8 @@ public class WxPayService extends BasePayService { TransactionType transactionType = order.getTransactionType(); ((WxTransactionType) transactionType).setAttribute(parameters, order); - + // 订单附加信息,可用于预设未提供的参数,这里会覆盖以上所有的订单信息, + parameters.putAll(order.getAttrs()); return getAssistService().doExecute(parameters, order); } @@ -285,7 +286,7 @@ public class WxPayService extends BasePayService { Map params = new LinkedHashMap<>(); String appId = payConfigStorage.getAppId(); - if (payConfigStorage.isPartner() && StringUtils.isNotEmpty(payConfigStorage.getSubAppId())){ + if (payConfigStorage.isPartner() && StringUtils.isNotEmpty(payConfigStorage.getSubAppId())) { appId = payConfigStorage.getSubAppId(); } String timeStamp = String.valueOf(DateUtils.toEpochSecond()); @@ -496,7 +497,11 @@ public class WxPayService extends BasePayService { @Override public Map close(AssistOrder assistOrder) { String parameters = JSON.toJSONString(wxParameterStructure.getMchParameters()); - return getAssistService().doExecute(parameters, WxTransactionType.CLOSE, assistOrder.getOutTradeNo()); + final ResponseEntity responseEntity = getAssistService().doExecuteEntity(parameters, WxTransactionType.CLOSE, assistOrder.getOutTradeNo()); + if (responseEntity.getStatusCode() == 204) { + return new MapGen("statusCode", responseEntity.getStatusCode()).getAttr(); + } + return responseEntity.getBody(); } @@ -530,8 +535,6 @@ public class WxPayService extends BasePayService { } - - /** * 查询退款 * diff --git a/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/WxProfitSharingTransactionType.java b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/WxProfitSharingTransactionType.java index e7fb075..edb9b91 100644 --- a/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/WxProfitSharingTransactionType.java +++ b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/WxProfitSharingTransactionType.java @@ -16,15 +16,15 @@ public enum WxProfitSharingTransactionType implements TransactionType { /** * 请求分账 */ - ORDERS("/v3/profitsharing/orders ", MethodType.POST), + ORDERS("/v3/profitsharing/orders", MethodType.POST), /** * 查询分账结果 */ - ORDERS_RESULT("/v3/profitsharing/orders/{out_order_no} ", MethodType.POST), + ORDERS_RESULT("/v3/profitsharing/orders/{out_order_no}", MethodType.GET), /** * 请求分账回退 */ - RETURN_ORDERS("/v3/profitsharing/return-orders ", MethodType.POST), + RETURN_ORDERS("/v3/profitsharing/return-orders", MethodType.POST), /** * 查询分账回退结果 */ @@ -32,19 +32,19 @@ public enum WxProfitSharingTransactionType implements TransactionType { /** * 解冻剩余资金 */ - ORDERS_UNFREEZE("/v3/profitsharing/orders/unfreeze ", MethodType.POST), + ORDERS_UNFREEZE("/v3/profitsharing/orders/unfreeze", MethodType.POST), /** * 查询剩余待分金额 */ - AMOUNTS("/v3/profitsharing/transactions/{transaction_id}/amounts ", MethodType.GET), + AMOUNTS("/v3/profitsharing/transactions/{transaction_id}/amounts", MethodType.GET), /** * 服务商专用-查询最大分账比例 */ - MCH_CONFIG("/v3/profitsharing/merchant-configs/{sub_mchid} ", MethodType.GET), + MCH_CONFIG("/v3/profitsharing/merchant-configs/{sub_mchid}", MethodType.GET), /** * 添加分账接收方 */ - RECEIVERS_ADD("/v3/profitsharing/receivers/add ", MethodType.POST), + RECEIVERS_ADD("/v3/profitsharing/receivers/add", MethodType.POST), /** * 删除分账接收方 */ diff --git a/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/sharing/ProfitSharingOrder.java b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/sharing/ProfitSharingOrder.java index 2e6112b..4cbdabd 100644 --- a/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/sharing/ProfitSharingOrder.java +++ b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/sharing/ProfitSharingOrder.java @@ -101,6 +101,6 @@ public class ProfitSharingOrder extends PayOrder { public void setUnfreezeUnsplit(Boolean unfreezeUnsplit) { this.unfreezeUnsplit = unfreezeUnsplit; - addAttr(WxConst.UNFREEZE_UNSPLIT, receivers); + addAttr(WxConst.UNFREEZE_UNSPLIT, unfreezeUnsplit); } } diff --git a/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/utils/AntCertificationUtil.java b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/utils/AntCertificationUtil.java index 4d8c324..b494c62 100644 --- a/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/utils/AntCertificationUtil.java +++ b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/utils/AntCertificationUtil.java @@ -21,8 +21,7 @@ import java.util.concurrent.ConcurrentHashMap; import javax.crypto.Cipher; import javax.crypto.spec.GCMParameterSpec; import javax.crypto.spec.SecretKeySpec; - -import org.bouncycastle.jce.provider.BouncyCastleProvider; +import javax.management.openmbean.InvalidKeyException; import com.egzosn.pay.common.exception.PayErrorException; import com.egzosn.pay.common.util.sign.encrypt.Base64; @@ -57,10 +56,6 @@ public final class AntCertificationUtil { } try { - if (null == Security.getProvider("BC")) { - Security.removeProvider("SunEC"); - Security.addProvider(new BouncyCastleProvider()); - } PKCS12_KEY_STORE = KeyStore.getInstance("PKCS12"); } catch (KeyStoreException e) { @@ -127,6 +122,9 @@ public final class AntCertificationUtil { PrivateKey privateKey = (PrivateKey) PKCS12_KEY_STORE.getKey(keyAlias, pem); return new CertEnvironment(privateKey, publicKey, serialNumber); } + catch (InvalidKeyException e) { + throw new PayErrorException(new WxPayError(WxConst.FAILURE, "获取公私钥失败, 解决方式:替换jre包:local_policy.jar,US_export_policy.jar"), e); + } catch (GeneralSecurityException e) { throw new PayErrorException(new WxPayError(WxConst.FAILURE, "获取公私钥失败"), e); } -- Gitee From 4baa476df8c1a70a3ace689816aa946cc79122bd Mon Sep 17 00:00:00 2001 From: egan Date: Sun, 20 Mar 2022 23:49:14 +0800 Subject: [PATCH 270/299] =?UTF-8?q?=E5=BE=AE=E4=BF=A1V2=E9=80=80=E6=AC=BE?= =?UTF-8?q?=E5=9B=9E=E8=B0=83=E5=A4=84=E7=90=86=E5=AE=9E=E7=8E=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/egzosn/pay/wx/api/WxConst.java | 2 + .../com/egzosn/pay/wx/api/WxPayService.java | 68 ++++++++++++++----- 2 files changed, 53 insertions(+), 17 deletions(-) diff --git a/pay-java-wx/src/main/java/com/egzosn/pay/wx/api/WxConst.java b/pay-java-wx/src/main/java/com/egzosn/pay/wx/api/WxConst.java index f41eab5..fe3d4e4 100644 --- a/pay-java-wx/src/main/java/com/egzosn/pay/wx/api/WxConst.java +++ b/pay-java-wx/src/main/java/com/egzosn/pay/wx/api/WxConst.java @@ -34,5 +34,7 @@ public interface WxConst { String OUT_TRADE_NO = "out_trade_no"; String GZIP = "GZIP"; String BILL_DATE = "bill_date"; + String REQ_INFO = "req_info"; + String NOTIFY_URL = "notify_url"; } 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 2bee0d8..2f2e7f3 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 @@ -25,6 +25,7 @@ import static com.egzosn.pay.wx.api.WxConst.HMAC_SHA256; import static com.egzosn.pay.wx.api.WxConst.MCH_ID; import static com.egzosn.pay.wx.api.WxConst.NONCE_STR; import static com.egzosn.pay.wx.api.WxConst.OUT_TRADE_NO; +import static com.egzosn.pay.wx.api.WxConst.REQ_INFO; import static com.egzosn.pay.wx.api.WxConst.RESULT_CODE; import static com.egzosn.pay.wx.api.WxConst.RETURN_CODE; import static com.egzosn.pay.wx.api.WxConst.RETURN_MSG_CODE; @@ -40,7 +41,6 @@ import com.alibaba.fastjson.JSONObject; import com.egzosn.pay.common.api.BasePayService; import com.egzosn.pay.common.bean.AssistOrder; import com.egzosn.pay.common.bean.BillType; - import com.egzosn.pay.common.bean.MethodType; import com.egzosn.pay.common.bean.NoticeParams; import com.egzosn.pay.common.bean.NoticeRequest; @@ -65,6 +65,7 @@ import com.egzosn.pay.common.util.Util; import com.egzosn.pay.common.util.XML; import com.egzosn.pay.common.util.sign.SignTextUtils; import com.egzosn.pay.common.util.sign.SignUtils; +import com.egzosn.pay.common.util.sign.encrypt.AES; import com.egzosn.pay.common.util.sign.encrypt.RSA2; import com.egzosn.pay.common.util.str.StringUtils; import com.egzosn.pay.wx.bean.RedpackOrder; @@ -156,6 +157,11 @@ public class WxPayService extends BasePayService implements */ public boolean verify(NoticeParams noticeParams) { final Map params = noticeParams.getBody(); + //如果为退款不需要校验, 直接返回, + if (params.containsKey(REQ_INFO)){ + return true; + } + if (null == params.get(SIGN) || !(SUCCESS.equals(params.get(RETURN_CODE)) && SUCCESS.equals(params.get(RESULT_CODE)))) { if (LOG.isErrorEnabled()) { LOG.error(String.format("微信支付异常:return_code=%s,参数集=%s", params.get(RETURN_CODE), params)); @@ -247,7 +253,7 @@ public class WxPayService extends BasePayService implements ((WxTransactionType) order.getTransactionType()).setAttribute(parameters, order); //可覆盖参数 -/* OrderParaStructure.loadParameters(parameters, "notify_url", order); +/* OrderParaStructure.loadParameters(parameters, NOTIFY_URL, order); OrderParaStructure.loadParameters(parameters, "goods_tag", order); OrderParaStructure.loadParameters(parameters, "limit_pay", order); OrderParaStructure.loadParameters(parameters, "receipt", order); @@ -286,7 +292,7 @@ public class WxPayService extends BasePayService implements ////统一下单 JSONObject result = unifiedOrder(order); // 对微信返回的数据进行校验 - if (verify(preOrderHandler(result, order))) { + if (verify(new NoticeParams(preOrderHandler(result, order)))) { //如果是扫码支付或者刷卡付无需处理,直接返回 if (((WxTransactionType) order.getTransactionType()).isReturn()) { return result; @@ -392,6 +398,29 @@ public class WxPayService extends BasePayService implements } + /** + * 将请求参数或者请求流转化为 Map + * + * @param body 通知请求` + * @return 获得回调的请求参数 + */ + public Map getRefundNoticeParams(Map body) { + String reqInfo = (String) body.get(REQ_INFO); + if (StringUtils.isEmpty(reqInfo)) { + return body; + } + try { + String decrypt = AES.decrypt(reqInfo, payConfigStorage.getSecretKey(), payConfigStorage.getInputCharset()); + JSONObject data = XML.toJSONObject(decrypt); + body.putAll(data); + return body; + } + catch (GeneralSecurityException | IOException e) { + throw new PayErrorException(new WxPayError(FAIL, e.getMessage()), e); + } + } + + /** * 将请求参数或者请求流转化为 Map * @@ -403,13 +432,16 @@ public class WxPayService extends BasePayService implements TreeMap map = new TreeMap(); try { - return new NoticeParams(XML.inputStream2Map(request.getInputStream(), map)); + Map body = XML.inputStream2Map(request.getInputStream(), map); + body = getRefundNoticeParams(body); + return new NoticeParams(body); } catch (IOException e) { throw new PayErrorException(new PayException("IOException", e.getMessage())); } } + /** * 获取输出消息,用户返回给支付端 * @@ -528,14 +560,15 @@ public class WxPayService extends BasePayService implements return secondaryInterface(transactionId, outTradeNo, WxTransactionType.CLOSE); } + /** * 交易关闭接口 * - * @param assistOrder 关闭订单 + * @param assistOrder 关闭订单 * @return 返回支付方交易关闭后的结果 */ @Override - public Map close(AssistOrder assistOrder){ + public Map close(AssistOrder assistOrder) { return secondaryInterface(assistOrder.getTradeNo(), assistOrder.getOutTradeNo(), WxTransactionType.CLOSE); } @@ -553,9 +586,10 @@ public class WxPayService extends BasePayService implements } - private Map initNotifyUrl(Map parameters, Order order) { - OrderParaStructure.loadParameters(parameters, "notify_url", payConfigStorage.getNotifyUrl()); - OrderParaStructure.loadParameters(parameters, "notify_url", order); + private Map initNotifyUrl(Map parameters, AssistOrder order) { + OrderParaStructure.loadParameters(parameters, WxConst.NOTIFY_URL, payConfigStorage.getNotifyUrl()); + OrderParaStructure.loadParameters(parameters, WxConst.NOTIFY_URL, order.getNotifyUrl()); + OrderParaStructure.loadParameters(parameters, WxConst.NOTIFY_URL, order); return parameters; } @@ -620,7 +654,7 @@ public class WxPayService extends BasePayService implements * @return 返回支付方下载对账单的结果 */ public Map downloadBill(Date billDate, String billType) { - return downloadBill(billDate, WxPayBillType.valueOf(billType)); + return downloadBill(billDate, WxPayBillType.valueOf(billType)); } /** @@ -637,7 +671,7 @@ public class WxPayService extends BasePayService implements public Map downloadBill(Date billDate, BillType billType) { //获取公共参数 Map parameters = getPublicParameters(); - parameters.put("bill_type", billType); + parameters.put("bill_type", billType.getType()); //目前只支持日账单 parameters.put(WxConst.BILL_DATE, DateUtils.formatDate(billDate, DateUtils.YYYYMMDD)); String fileType = billType.getFileType(); @@ -811,12 +845,12 @@ public class WxPayService extends BasePayService implements * @param order 转账订单 *

      *
-     *                                                     注意事项:
-     *                                                     ◆ 当返回错误码为“SYSTEMERROR”时,请不要更换商户订单号,一定要使用原商户订单号重试,否则可能造成重复支付等资金风险。
-     *                                                     ◆ XML具有可扩展性,因此返回参数可能会有新增,而且顺序可能不完全遵循此文档规范,如果在解析回包的时候发生错误,请商户务必不要换单重试,请商户联系客服确认付款情况。如果有新回包字段,会更新到此API文档中。
-     *                                                     ◆ 因为错误代码字段err_code的值后续可能会增加,所以商户如果遇到回包返回新的错误码,请商户务必不要换单重试,请商户联系客服确认付款情况。如果有新的错误码,会更新到此API文档中。
-     *                                                     ◆ 错误代码描述字段err_code_des只供人工定位问题时做参考,系统实现时请不要依赖这个字段来做自动化处理。
-     *                                                     
+ * 注意事项: + * ◆ 当返回错误码为“SYSTEMERROR”时,请不要更换商户订单号,一定要使用原商户订单号重试,否则可能造成重复支付等资金风险。 + * ◆ XML具有可扩展性,因此返回参数可能会有新增,而且顺序可能不完全遵循此文档规范,如果在解析回包的时候发生错误,请商户务必不要换单重试,请商户联系客服确认付款情况。如果有新回包字段,会更新到此API文档中。 + * ◆ 因为错误代码字段err_code的值后续可能会增加,所以商户如果遇到回包返回新的错误码,请商户务必不要换单重试,请商户联系客服确认付款情况。如果有新的错误码,会更新到此API文档中。 + * ◆ 错误代码描述字段err_code_des只供人工定位问题时做参考,系统实现时请不要依赖这个字段来做自动化处理。 + *
* @return 对应的转账结果 */ @Override -- Gitee From 592a619c909d4fd834df767770fe6228a4941d5e Mon Sep 17 00:00:00 2001 From: egan Date: Sun, 20 Mar 2022 23:53:31 +0800 Subject: [PATCH 271/299] =?UTF-8?q?2.14.4=E5=8F=91=E7=89=88?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 2 +- pay-java-ali/pom.xml | 2 +- pay-java-baidu/pom.xml | 2 +- pay-java-common/pom.xml | 6 +++++- pay-java-demo/pom.xml | 2 +- pay-java-fuiou/pom.xml | 2 +- pay-java-payoneer/pom.xml | 2 +- pay-java-paypal/pom.xml | 2 +- pay-java-union/pom.xml | 2 +- pay-java-web-support/pom.xml | 2 +- pay-java-wx-youdian/pom.xml | 2 +- pay-java-wx/pom.xml | 2 +- pay-java-yiji/pom.xml | 2 +- pom.xml | 6 +++--- 14 files changed, 20 insertions(+), 16 deletions(-) diff --git a/README.md b/README.md index fbe4f56..d24b69d 100644 --- a/README.md +++ b/README.md @@ -22,7 +22,7 @@ com.egzosn {module-name} - 2.14.3-b4 + 2.14.4 ``` diff --git a/pay-java-ali/pom.xml b/pay-java-ali/pom.xml index e222896..e8540ec 100644 --- a/pay-java-ali/pom.xml +++ b/pay-java-ali/pom.xml @@ -5,7 +5,7 @@ pay-java-parent com.egzosn - 2.14.3-b4 + 2.14.4 4.0.0 pay-java-ali diff --git a/pay-java-baidu/pom.xml b/pay-java-baidu/pom.xml index c8372c4..34f8eb6 100644 --- a/pay-java-baidu/pom.xml +++ b/pay-java-baidu/pom.xml @@ -5,7 +5,7 @@ pay-java-parent com.egzosn - 2.14.3-b4 + 2.14.4 4.0.0 pay-java-baidu diff --git a/pay-java-common/pom.xml b/pay-java-common/pom.xml index fab77ed..e3dfd1d 100644 --- a/pay-java-common/pom.xml +++ b/pay-java-common/pom.xml @@ -5,7 +5,7 @@ pay-java-parent com.egzosn - 2.14.3-b4 + 2.14.4 4.0.0 jar @@ -38,6 +38,10 @@ com.google.zxing core
+ + org.bouncycastle + bcprov-jdk15on + diff --git a/pay-java-demo/pom.xml b/pay-java-demo/pom.xml index 91ed777..7bd2b09 100644 --- a/pay-java-demo/pom.xml +++ b/pay-java-demo/pom.xml @@ -5,7 +5,7 @@ pay-java-parent com.egzosn - 2.14.3-b4 + 2.14.4 4.0.0 war diff --git a/pay-java-fuiou/pom.xml b/pay-java-fuiou/pom.xml index 76e3587..b07c340 100644 --- a/pay-java-fuiou/pom.xml +++ b/pay-java-fuiou/pom.xml @@ -5,7 +5,7 @@ pay-java-parent com.egzosn - 2.14.3-b4 + 2.14.4 4.0.0 pay-java-fuiou diff --git a/pay-java-payoneer/pom.xml b/pay-java-payoneer/pom.xml index 4e67128..968ecdf 100644 --- a/pay-java-payoneer/pom.xml +++ b/pay-java-payoneer/pom.xml @@ -5,7 +5,7 @@ pay-java-parent com.egzosn - 2.14.3-b4 + 2.14.4 4.0.0 pay-java-payoneer diff --git a/pay-java-paypal/pom.xml b/pay-java-paypal/pom.xml index f4de33c..fca02e4 100644 --- a/pay-java-paypal/pom.xml +++ b/pay-java-paypal/pom.xml @@ -5,7 +5,7 @@ pay-java-parent com.egzosn - 2.14.3-b4 + 2.14.4 4.0.0 diff --git a/pay-java-union/pom.xml b/pay-java-union/pom.xml index 1dfb536..6a2584a 100644 --- a/pay-java-union/pom.xml +++ b/pay-java-union/pom.xml @@ -5,7 +5,7 @@ pay-java-parent com.egzosn - 2.14.3-b4 + 2.14.4 4.0.0 diff --git a/pay-java-web-support/pom.xml b/pay-java-web-support/pom.xml index 630aef4..4093bdc 100644 --- a/pay-java-web-support/pom.xml +++ b/pay-java-web-support/pom.xml @@ -5,7 +5,7 @@ pay-java-parent com.egzosn - 2.14.3-b4 + 2.14.4 4.0.0 jar diff --git a/pay-java-wx-youdian/pom.xml b/pay-java-wx-youdian/pom.xml index 60f911f..db19f8a 100644 --- a/pay-java-wx-youdian/pom.xml +++ b/pay-java-wx-youdian/pom.xml @@ -5,7 +5,7 @@ pay-java-parent com.egzosn - 2.14.3-b4 + 2.14.4 4.0.0 pay-java-wx-youdian diff --git a/pay-java-wx/pom.xml b/pay-java-wx/pom.xml index ebdb3e6..d9a1c21 100644 --- a/pay-java-wx/pom.xml +++ b/pay-java-wx/pom.xml @@ -5,7 +5,7 @@ pay-java-parent com.egzosn - 2.14.3-b4 + 2.14.4 4.0.0 pay-java-wx diff --git a/pay-java-yiji/pom.xml b/pay-java-yiji/pom.xml index 19ca7d2..1993eb7 100644 --- a/pay-java-yiji/pom.xml +++ b/pay-java-yiji/pom.xml @@ -5,7 +5,7 @@ pay-java-parent com.egzosn - 2.14.3-b4 + 2.14.4 4.0.0 diff --git a/pom.xml b/pom.xml index 3dc1670..a919890 100644 --- a/pom.xml +++ b/pom.xml @@ -7,7 +7,7 @@ com.egzosn pay-java-parent pom - 2.14.3-b4 + 2.14.4 Pay Java - Parent Pay Java Parent @@ -64,13 +64,13 @@ pay-java-paypal pay-java-yiji pay-java-baidu - pay-java-demo +
- 2.14.3-b4 + 2.14.4 4.5.4 1.2.17 1.2.73 -- Gitee From 7073d904fee980a99a31b18fb9aa57880f0c0995 Mon Sep 17 00:00:00 2001 From: egan Date: Thu, 5 May 2022 22:10:02 +0800 Subject: [PATCH 272/299] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E5=B7=B2=E7=9F=A5bug?= =?UTF-8?q?=20=202.14.4=E7=89=88=E6=9C=AC=20=E5=BE=AE=E4=BF=A1=E6=94=AF?= =?UTF-8?q?=E4=BB=98=20v3=20=E5=90=AF=E5=8A=A8=E6=8A=A5=E9=94=99=20#90=20?= =?UTF-8?q?=20=E5=BE=AE=E4=BF=A1V3=E5=88=9D=E5=A7=8B=E5=8C=96apiServerUrl?= =?UTF-8?q?=E4=B8=BAnull?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 2 +- pay-java-ali/pom.xml | 2 +- .../pay/ali/utils/AntCertificationUtil.java | 50 +++++++++++++------ pay-java-baidu/pom.xml | 2 +- pay-java-common/pom.xml | 2 +- .../egzosn/pay/common/api/BasePayService.java | 9 ++-- .../pay/common/http/HttpRequestTemplate.java | 2 +- .../pay/common/util/sign/SignUtils.java | 32 +++++------- .../pay/common/util/sign/encrypt/AES.java | 10 ++-- pay-java-demo/pom.xml | 2 +- .../pay/demo/controller/AliPayController.java | 8 +-- pay-java-fuiou/pom.xml | 2 +- pay-java-payoneer/pom.xml | 2 +- pay-java-paypal/pom.xml | 2 +- pay-java-union/pom.xml | 2 +- pay-java-web-support/pom.xml | 2 +- pay-java-wx-youdian/pom.xml | 2 +- pay-java-wx/pom.xml | 2 +- .../egzosn/pay/wx/v3/api/WxPayService.java | 6 ++- .../pay/wx/v3/api/WxProfitSharingService.java | 4 +- .../pay/wx/v3/utils/AntCertificationUtil.java | 3 +- pay-java-yiji/pom.xml | 2 +- pom.xml | 9 +--- 23 files changed, 81 insertions(+), 78 deletions(-) diff --git a/README.md b/README.md index d24b69d..1ac066e 100644 --- a/README.md +++ b/README.md @@ -22,7 +22,7 @@ com.egzosn {module-name} - 2.14.4 + 2.14.4-fix ``` diff --git a/pay-java-ali/pom.xml b/pay-java-ali/pom.xml index e8540ec..53896ee 100644 --- a/pay-java-ali/pom.xml +++ b/pay-java-ali/pom.xml @@ -5,7 +5,7 @@ pay-java-parent com.egzosn - 2.14.4 + 2.14.4-fix 4.0.0 pay-java-ali diff --git a/pay-java-ali/src/main/java/com/egzosn/pay/ali/utils/AntCertificationUtil.java b/pay-java-ali/src/main/java/com/egzosn/pay/ali/utils/AntCertificationUtil.java index 4d1ffbc..e574d20 100644 --- a/pay-java-ali/src/main/java/com/egzosn/pay/ali/utils/AntCertificationUtil.java +++ b/pay-java-ali/src/main/java/com/egzosn/pay/ali/utils/AntCertificationUtil.java @@ -28,6 +28,7 @@ import org.slf4j.LoggerFactory; import com.egzosn.pay.common.bean.result.PayException; import com.egzosn.pay.common.exception.PayErrorException; import com.egzosn.pay.common.util.IOUtils; +import com.egzosn.pay.common.util.sign.SignUtils; import com.egzosn.pay.common.util.sign.encrypt.Base64; import com.egzosn.pay.common.util.str.StringUtils; @@ -41,7 +42,9 @@ import com.egzosn.pay.common.util.str.StringUtils; public class AntCertificationUtil { private static final Logger LOGGER = LoggerFactory.getLogger(AntCertificationUtil.class); - + static { + SignUtils.initBc(); + } /** * 验证证书是否可信 @@ -54,7 +57,8 @@ public class AntCertificationUtil { X509Certificate[] certificates; try { certificates = readPemCertChain(certContent); - } catch (Exception e) { + } + catch (Exception e) { LOGGER.error("读取证书失败", e); throw new RuntimeException(e); } @@ -63,7 +67,8 @@ public class AntCertificationUtil { try { X509Certificate[] certs = readPemCertChain(rootCertContent); rootCerts.addAll(Arrays.asList(certs)); - } catch (Exception e) { + } + catch (Exception e) { LOGGER.error("读取根证书失败", e); throw new RuntimeException(e); } @@ -81,10 +86,12 @@ public class AntCertificationUtil { private static boolean verifyCert(X509Certificate cert, X509Certificate[] rootCerts) { try { cert.checkValidity(); - } catch (CertificateExpiredException e) { + } + catch (CertificateExpiredException e) { LOGGER.error("证书已经过期", e); return false; - } catch (CertificateNotYetValidException e) { + } + catch (CertificateNotYetValidException e) { LOGGER.error("证书未激活", e); return false; } @@ -104,7 +111,8 @@ public class AntCertificationUtil { try { PublicKey publicKey = issuer.getPublicKey(); verifySignature(publicKey, cert); - } catch (PayErrorException e) { + } + catch (PayErrorException e) { LOGGER.error("证书链验证失败", e); return false; } @@ -155,10 +163,12 @@ public class AntCertificationUtil { private static boolean checkValidity(X509Certificate cert) { try { cert.checkValidity(); - } catch (CertificateExpiredException e) { + } + catch (CertificateExpiredException e) { LOGGER.error("证书已经过期"); return false; - } catch (CertificateNotYetValidException e) { + } + catch (CertificateNotYetValidException e) { LOGGER.error("证书未激活"); return false; } @@ -169,7 +179,8 @@ public class AntCertificationUtil { private static void verifySignature(PublicKey publicKey, X509Certificate cert) { try { cert.verify(publicKey); - } catch (GeneralSecurityException e) { + } + catch (GeneralSecurityException e) { throw new PayErrorException(new PayException("证书校验失败", e.getMessage())); } } @@ -276,10 +287,12 @@ public class AntCertificationUtil { private static X509Certificate[] readPemCertChain(String cert) { ByteArrayInputStream inputStream = new ByteArrayInputStream(cert.getBytes()); try { - CertificateFactory factory = CertificateFactory.getInstance("X.509", "BC");; + CertificateFactory factory = CertificateFactory.getInstance("X.509", "BC"); + ; Collection certificates = factory.generateCertificates(inputStream); return certificates.toArray(new X509Certificate[certificates.size()]); - } catch (GeneralSecurityException e) { + } + catch (GeneralSecurityException e) { LOGGER.error("提取根证书失败", e); } return null; @@ -308,13 +321,15 @@ public class AntCertificationUtil { certSN = fillMD5(certSN); if (StringUtils.isEmpty(rootCertSN)) { rootCertSN = certSN; - } else { + } + else { rootCertSN = rootCertSN + "_" + certSN; } } } - } catch (NoSuchAlgorithmException e) { + } + catch (NoSuchAlgorithmException e) { LOGGER.error("提取根证书失败", e); } return rootCertSN; @@ -332,7 +347,8 @@ public class AntCertificationUtil { CertificateFactory factory = CertificateFactory.getInstance("X.509", "BC"); X509Certificate cert = (X509Certificate) factory.generateCertificate(inputStream); return md5((cert.getIssuerX500Principal().getName() + cert.getSerialNumber()).getBytes()); - } catch (GeneralSecurityException e) { + } + catch (GeneralSecurityException e) { throw new PayErrorException(new PayException(" 获取公钥证书序列号异常", e.getMessage())); } } @@ -368,7 +384,8 @@ public class AntCertificationUtil { CertificateFactory factory = CertificateFactory.getInstance("X.509", "BC"); X509Certificate cert = (X509Certificate) factory.generateCertificate(inputStream); return Base64.encode(cert.getPublicKey().getEncoded()); - } catch (GeneralSecurityException e) { + } + catch (GeneralSecurityException e) { throw new PayErrorException(new PayException(" 提取公钥证书中的公钥异常", e.getMessage())); } } @@ -377,7 +394,8 @@ public class AntCertificationUtil { public static String readFromInputStream(InputStream cert) { try { return new String(IOUtils.toByteArray(cert), StandardCharsets.UTF_8); - } catch (IOException e) { + } + catch (IOException e) { throw new PayErrorException(new PayException("读取证书异常", e.getMessage())); } } diff --git a/pay-java-baidu/pom.xml b/pay-java-baidu/pom.xml index 34f8eb6..576a6f5 100644 --- a/pay-java-baidu/pom.xml +++ b/pay-java-baidu/pom.xml @@ -5,7 +5,7 @@ pay-java-parent com.egzosn - 2.14.4 + 2.14.4-fix 4.0.0 pay-java-baidu diff --git a/pay-java-common/pom.xml b/pay-java-common/pom.xml index e3dfd1d..37a7b9d 100644 --- a/pay-java-common/pom.xml +++ b/pay-java-common/pom.xml @@ -5,7 +5,7 @@ pay-java-parent com.egzosn - 2.14.4 + 2.14.4-fix 4.0.0 jar diff --git a/pay-java-common/src/main/java/com/egzosn/pay/common/api/BasePayService.java b/pay-java-common/src/main/java/com/egzosn/pay/common/api/BasePayService.java index 52ccdbb..50bade5 100644 --- a/pay-java-common/src/main/java/com/egzosn/pay/common/api/BasePayService.java +++ b/pay-java-common/src/main/java/com/egzosn/pay/common/api/BasePayService.java @@ -94,6 +94,7 @@ public abstract class BasePayService implements Pay } + public BasePayService(PC payConfigStorage) { this(payConfigStorage, null); } @@ -101,15 +102,11 @@ public abstract class BasePayService implements Pay public BasePayService(PC payConfigStorage, HttpConfigStorage configStorage) { setPayConfigStorage(payConfigStorage); setRequestTemplateConfigStorage(configStorage); - initAfter(); + } - /** - * 初始化之后执行 - */ - protected void initAfter(){ - } + /** * Generate a Base64 encoded String from user , password * diff --git a/pay-java-common/src/main/java/com/egzosn/pay/common/http/HttpRequestTemplate.java b/pay-java-common/src/main/java/com/egzosn/pay/common/http/HttpRequestTemplate.java index ba60626..d53f372 100644 --- a/pay-java-common/src/main/java/com/egzosn/pay/common/http/HttpRequestTemplate.java +++ b/pay-java-common/src/main/java/com/egzosn/pay/common/http/HttpRequestTemplate.java @@ -428,7 +428,7 @@ public class HttpRequestTemplate { return new ResponseEntity<>(statusCode, allHeaders, body); } catch (IOException e) { - throw new PayErrorException(new PayException("IOException", e.getLocalizedMessage())); + throw new PayErrorException(new PayException("IOException", e.getLocalizedMessage()), e); } finally { httpRequest.releaseConnection(); 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 0085513..69b7090 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 @@ -1,33 +1,15 @@ package com.egzosn.pay.common.util.sign; -import java.io.UnsupportedEncodingException; -import java.security.InvalidKeyException; -import java.security.NoSuchAlgorithmException; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.LinkedHashMap; -import java.util.List; +import java.security.Security; import java.util.Map; -import java.util.Set; -import java.util.SortedMap; -import java.util.UUID; -import javax.crypto.Mac; -import javax.crypto.spec.SecretKeySpec; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.apache.http.message.BasicNameValuePair; +import org.bouncycastle.jce.provider.BouncyCastleProvider; import static com.egzosn.pay.common.util.sign.SignTextUtils.parameterText; import com.egzosn.pay.common.bean.SignType; -import com.egzosn.pay.common.bean.result.PayException; -import com.egzosn.pay.common.exception.PayErrorException; import com.egzosn.pay.common.util.sign.encrypt.HmacSha256; -import com.egzosn.pay.common.util.str.StringUtils; /** * 签名 工具 @@ -209,5 +191,15 @@ public enum SignUtils implements SignType { return this.verify(parameterText(params), sign, key, characterEncoding); } + /** + * 初始化BC + */ + public static void initBc() { + if (null == Security.getProvider("BC")) { + Security.removeProvider("SunEC"); + Security.addProvider(new BouncyCastleProvider()); + } + } + } diff --git a/pay-java-common/src/main/java/com/egzosn/pay/common/util/sign/encrypt/AES.java b/pay-java-common/src/main/java/com/egzosn/pay/common/util/sign/encrypt/AES.java index bd4bf64..9d9bb7e 100644 --- a/pay-java-common/src/main/java/com/egzosn/pay/common/util/sign/encrypt/AES.java +++ b/pay-java-common/src/main/java/com/egzosn/pay/common/util/sign/encrypt/AES.java @@ -2,13 +2,13 @@ package com.egzosn.pay.common.util.sign.encrypt; import java.io.IOException; import java.security.GeneralSecurityException; -import java.security.Security; import javax.crypto.Cipher; import javax.crypto.spec.SecretKeySpec; import org.apache.commons.codec.digest.DigestUtils; -import org.bouncycastle.jce.provider.BouncyCastleProvider; + +import com.egzosn.pay.common.util.sign.SignUtils; /** * AES 加解密 @@ -30,12 +30,10 @@ public class AES { private static final String ALGORITHM_MODE_PADDING = "AES/ECB/PKCS7Padding"; static { - if (null == Security.getProvider("BC")) { - Security.removeProvider("SunEC"); - Security.addProvider(new BouncyCastleProvider()); - } + SignUtils.initBc(); } + /** * 解密 * diff --git a/pay-java-demo/pom.xml b/pay-java-demo/pom.xml index 7bd2b09..8e14d35 100644 --- a/pay-java-demo/pom.xml +++ b/pay-java-demo/pom.xml @@ -5,7 +5,7 @@ pay-java-parent com.egzosn - 2.14.4 + 2.14.4-fix 4.0.0 war diff --git a/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/AliPayController.java b/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/AliPayController.java index 7b7b4f7..1ca3cc2 100644 --- a/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/AliPayController.java +++ b/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/AliPayController.java @@ -87,10 +87,10 @@ public class AliPayController { aliPayConfigStorage.setAppId("2016080400165436"); // aliPayConfigStorage.setAppAuthToken("ISV代商户代用,指定appAuthToken"); //普通公钥方式与证书公钥方式为两者取其一的方式 -// keyPublic(aliPayConfigStorage); -// aliPayConfigStorage.setKeyPrivate("MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBAKroe/8h5vC4L6T+B2WdXiVwGsMvUKgb2XsKix6VY3m2wcf6tyzpNRDCNykbIwGtaeo7FshN+qZxdXHLiIam9goYncBit/8ojfLGy2gLxO/PXfzGxYGs0KsDZ+ryVPPmE34ZZ8jiJpR0ygzCFl8pN3QJPJRGTJn5+FTT9EF/9zyZAgMBAAECgYAktngcYC35u7cQXDk+jMVyiVhWYU2ULxdSpPspgLGzrZyG1saOcTIi/XVX8Spd6+B6nmLQeF/FbU3rOeuD8U2clzul2Z2YMbJ0FYay9oVZFfp5gTEFpFRTVfzqUaZQBIjJe/xHL9kQVqc5xHlE/LVA27/Kx3dbC35Y7B4EVBDYAQJBAOhsX8ZreWLKPhXiXHTyLmNKhOHJc+0tFH7Ktise/0rNspojU7o9prOatKpNylp9v6kux7migcMRdVUWWiVe+4ECQQC8PqsuEz7B0yqirQchRg1DbHjh64bw9Kj82EN1/NzOUd53tP9tg+SO97EzsibK1F7tOcuwqsa7n2aY48mQ+y0ZAkBndA2xcRcnvOOjtAz5VO8G7R12rse181HjGfG6AeMadbKg30aeaGCyIxN1loiSfNR5xsPJwibGIBg81mUrqzqBAkB+K6rkaPXJR9XtzvdWb/N3235yPkDlw7Z4MiOVM3RzvR/VMDV7m8lXoeDde2zQyeMOMYy6ztwA6WgE1bhGOnQRAkEAouUBv1sVdSBlsexX15qphOmAevzYrpufKgJIRLFWQxroXMS7FTesj+f+FmGrpPCxIde1dqJ8lqYLTyJmbzMPYw=="); - certKeyPublic(aliPayConfigStorage); - aliPayConfigStorage.setKeyPrivate("MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQCw7MD2Cwv/jnXssFjXnGx3JlGF57gJa2aYbJRV8MnNiPVpX4Ha+8ZjnQDhvkrWH4hHmzcujOr213HqloMpUSYBzCPiXGVRUUvdimejcHHTod7nI4g6nztzzfey/TXNDHmp7vY3pOIcjB0Zn0pkNAz2tKAFkqb4raHOqTB0QA0zD24Cn+26J2UJyYRcgeH0GtSQuUrm7yaGsuKakh+qtgWF6R71n5PMGOTQ5LH3i0WVHfCBkNGgJC6yC96HR4D7cosoyKD0+lp8UB/NVUWl7Tt/KLOgFUwh0GKSYFfv56O/VBV2+xqCGE4PlZESfVuOqz5vjjxzw3xDAUJrV8hSX/AJAgMBAAECggEBAKE0d3U4B4yo/2XUIH8EdgfykCFUSum6RFbpyBauORHfksyaSzV+ZvtomN8XhhSn0oJ8OMFfgM+86nz2+zdwSxMkMCYWTfLUAi4v59KRqAVO3kz4oS3Y3FDeAK3D7XuRvGFL7GgzAhtEx1cLPrsiehVn6s5pG15GxsIIgq/JlL1J88wn1zENLrVHmD6z/JpXvfb/RS1yR+5lyoohp4g0Ph9jJ3bCyUbRpK0QkPEzgAuWL0K2ITCL7PYHNAplI8d2xHHOLF9Qdjyx+ZrQ/RxtqzfyWzhqjsmp2qlgNCxWlt3woS9UhDB+nRvjEoWTJmIOszAMYuj8wGlX+3Ui3ALOdQECgYEA25EqnFPFinUnzgNvB6NYmh5STmZun6s4bUOLqwefKtEvrOtRwTu7sB7NIf37fizG3/MJUWHxiLy2/3ub4d2JxdDNBtJoEqnp6QB12qglCNa4CajdjtJa1dR81F9QvytsqEkmPYXFPPyviB0FcSIDAGMb3IbwvIfzBPY9WY8dJnECgYEAzkg3yKEFBZ8BU0WQ+3hyfKUoAhBEnxouxRSTBcXxwstJRiqaGTVe5aoJGQI+0xS7Z6q07XDtN2t97s6DnRLWbljsX6B64itzNhXRyzjdD3iZDU/KSw7khjhXf8XOZaj9eXmACDiUnkEn1xsM8bLiRGqB8y5f3aMY/RpuACGXnxkCgYEAx/zwT9Vpr1RIfjfYcJ+Su0X0994K0roUukj0tUJK8qf4gcsQ+y1aJe/YLib1ZBaKyj7G9O5+HmqtUAUZld/AdoJZzOXmz2EeYhD+R7wxh1xz4rCBpW3qOKvDS3jJxmZaIOoHv6/RWFxb0WGFrGcrTrX3EaWDLmWxr4pNlP5qsbECgYATllntrBR8/ycyEAX/SuWcHlaZM5BAh0zvm8+GGdCmDYWMqxjs0duL9URd4o+ynWJaKqR5c2KjA4r2tRdcP+Cqo7j2L5fbiAKtnQ7JvEGJaYsm72+nBuf+MrVkRZUepBhFg5r7rNu31zoAO+pTvQetNWvXeozRz93ckrjlPEtYaQKBgQDFwbV92rlRMLjZzlY+o0knoeJBjPQmPdiBTpGNimdy9L4c2Ure7affjcUiYhkKqrK5k5SScJTATgyQ7JF346FdtUtZ/6Kkj1RwJmmprPrDa9CATLoTle7g9OVd4sHT2ITHZMzPaF3ILvzcwJ70AD1xcxCQb+/7sDPmw7Mc8gOA7Q=="); + keyPublic(aliPayConfigStorage); + aliPayConfigStorage.setKeyPrivate("MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBAKroe/8h5vC4L6T+B2WdXiVwGsMvUKgb2XsKix6VY3m2wcf6tyzpNRDCNykbIwGtaeo7FshN+qZxdXHLiIam9goYncBit/8ojfLGy2gLxO/PXfzGxYGs0KsDZ+ryVPPmE34ZZ8jiJpR0ygzCFl8pN3QJPJRGTJn5+FTT9EF/9zyZAgMBAAECgYAktngcYC35u7cQXDk+jMVyiVhWYU2ULxdSpPspgLGzrZyG1saOcTIi/XVX8Spd6+B6nmLQeF/FbU3rOeuD8U2clzul2Z2YMbJ0FYay9oVZFfp5gTEFpFRTVfzqUaZQBIjJe/xHL9kQVqc5xHlE/LVA27/Kx3dbC35Y7B4EVBDYAQJBAOhsX8ZreWLKPhXiXHTyLmNKhOHJc+0tFH7Ktise/0rNspojU7o9prOatKpNylp9v6kux7migcMRdVUWWiVe+4ECQQC8PqsuEz7B0yqirQchRg1DbHjh64bw9Kj82EN1/NzOUd53tP9tg+SO97EzsibK1F7tOcuwqsa7n2aY48mQ+y0ZAkBndA2xcRcnvOOjtAz5VO8G7R12rse181HjGfG6AeMadbKg30aeaGCyIxN1loiSfNR5xsPJwibGIBg81mUrqzqBAkB+K6rkaPXJR9XtzvdWb/N3235yPkDlw7Z4MiOVM3RzvR/VMDV7m8lXoeDde2zQyeMOMYy6ztwA6WgE1bhGOnQRAkEAouUBv1sVdSBlsexX15qphOmAevzYrpufKgJIRLFWQxroXMS7FTesj+f+FmGrpPCxIde1dqJ8lqYLTyJmbzMPYw=="); +// certKeyPublic(aliPayConfigStorage); +// aliPayConfigStorage.setKeyPrivate("MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQCw7MD2Cwv/jnXssFjXnGx3JlGF57gJa2aYbJRV8MnNiPVpX4Ha+8ZjnQDhvkrWH4hHmzcujOr213HqloMpUSYBzCPiXGVRUUvdimejcHHTod7nI4g6nztzzfey/TXNDHmp7vY3pOIcjB0Zn0pkNAz2tKAFkqb4raHOqTB0QA0zD24Cn+26J2UJyYRcgeH0GtSQuUrm7yaGsuKakh+qtgWF6R71n5PMGOTQ5LH3i0WVHfCBkNGgJC6yC96HR4D7cosoyKD0+lp8UB/NVUWl7Tt/KLOgFUwh0GKSYFfv56O/VBV2+xqCGE4PlZESfVuOqz5vjjxzw3xDAUJrV8hSX/AJAgMBAAECggEBAKE0d3U4B4yo/2XUIH8EdgfykCFUSum6RFbpyBauORHfksyaSzV+ZvtomN8XhhSn0oJ8OMFfgM+86nz2+zdwSxMkMCYWTfLUAi4v59KRqAVO3kz4oS3Y3FDeAK3D7XuRvGFL7GgzAhtEx1cLPrsiehVn6s5pG15GxsIIgq/JlL1J88wn1zENLrVHmD6z/JpXvfb/RS1yR+5lyoohp4g0Ph9jJ3bCyUbRpK0QkPEzgAuWL0K2ITCL7PYHNAplI8d2xHHOLF9Qdjyx+ZrQ/RxtqzfyWzhqjsmp2qlgNCxWlt3woS9UhDB+nRvjEoWTJmIOszAMYuj8wGlX+3Ui3ALOdQECgYEA25EqnFPFinUnzgNvB6NYmh5STmZun6s4bUOLqwefKtEvrOtRwTu7sB7NIf37fizG3/MJUWHxiLy2/3ub4d2JxdDNBtJoEqnp6QB12qglCNa4CajdjtJa1dR81F9QvytsqEkmPYXFPPyviB0FcSIDAGMb3IbwvIfzBPY9WY8dJnECgYEAzkg3yKEFBZ8BU0WQ+3hyfKUoAhBEnxouxRSTBcXxwstJRiqaGTVe5aoJGQI+0xS7Z6q07XDtN2t97s6DnRLWbljsX6B64itzNhXRyzjdD3iZDU/KSw7khjhXf8XOZaj9eXmACDiUnkEn1xsM8bLiRGqB8y5f3aMY/RpuACGXnxkCgYEAx/zwT9Vpr1RIfjfYcJ+Su0X0994K0roUukj0tUJK8qf4gcsQ+y1aJe/YLib1ZBaKyj7G9O5+HmqtUAUZld/AdoJZzOXmz2EeYhD+R7wxh1xz4rCBpW3qOKvDS3jJxmZaIOoHv6/RWFxb0WGFrGcrTrX3EaWDLmWxr4pNlP5qsbECgYATllntrBR8/ycyEAX/SuWcHlaZM5BAh0zvm8+GGdCmDYWMqxjs0duL9URd4o+ynWJaKqR5c2KjA4r2tRdcP+Cqo7j2L5fbiAKtnQ7JvEGJaYsm72+nBuf+MrVkRZUepBhFg5r7rNu31zoAO+pTvQetNWvXeozRz93ckrjlPEtYaQKBgQDFwbV92rlRMLjZzlY+o0knoeJBjPQmPdiBTpGNimdy9L4c2Ure7affjcUiYhkKqrK5k5SScJTATgyQ7JF346FdtUtZ/6Kkj1RwJmmprPrDa9CATLoTle7g9OVd4sHT2ITHZMzPaF3ILvzcwJ70AD1xcxCQb+/7sDPmw7Mc8gOA7Q=="); aliPayConfigStorage.setNotifyUrl("http://pay.egzosn.com/payBack.json"); aliPayConfigStorage.setReturnUrl("http://pay.egzosn.com/payBack.html"); aliPayConfigStorage.setSignType(SignUtils.RSA2.name()); diff --git a/pay-java-fuiou/pom.xml b/pay-java-fuiou/pom.xml index b07c340..c5e6c90 100644 --- a/pay-java-fuiou/pom.xml +++ b/pay-java-fuiou/pom.xml @@ -5,7 +5,7 @@ pay-java-parent com.egzosn - 2.14.4 + 2.14.4-fix 4.0.0 pay-java-fuiou diff --git a/pay-java-payoneer/pom.xml b/pay-java-payoneer/pom.xml index 968ecdf..e935206 100644 --- a/pay-java-payoneer/pom.xml +++ b/pay-java-payoneer/pom.xml @@ -5,7 +5,7 @@ pay-java-parent com.egzosn - 2.14.4 + 2.14.4-fix 4.0.0 pay-java-payoneer diff --git a/pay-java-paypal/pom.xml b/pay-java-paypal/pom.xml index fca02e4..6f7ba60 100644 --- a/pay-java-paypal/pom.xml +++ b/pay-java-paypal/pom.xml @@ -5,7 +5,7 @@ pay-java-parent com.egzosn - 2.14.4 + 2.14.4-fix 4.0.0 diff --git a/pay-java-union/pom.xml b/pay-java-union/pom.xml index 6a2584a..28795c7 100644 --- a/pay-java-union/pom.xml +++ b/pay-java-union/pom.xml @@ -5,7 +5,7 @@ pay-java-parent com.egzosn - 2.14.4 + 2.14.4-fix 4.0.0 diff --git a/pay-java-web-support/pom.xml b/pay-java-web-support/pom.xml index 4093bdc..9995c94 100644 --- a/pay-java-web-support/pom.xml +++ b/pay-java-web-support/pom.xml @@ -5,7 +5,7 @@ pay-java-parent com.egzosn - 2.14.4 + 2.14.4-fix 4.0.0 jar diff --git a/pay-java-wx-youdian/pom.xml b/pay-java-wx-youdian/pom.xml index db19f8a..11bd564 100644 --- a/pay-java-wx-youdian/pom.xml +++ b/pay-java-wx-youdian/pom.xml @@ -5,7 +5,7 @@ pay-java-parent com.egzosn - 2.14.4 + 2.14.4-fix 4.0.0 pay-java-wx-youdian diff --git a/pay-java-wx/pom.xml b/pay-java-wx/pom.xml index d9a1c21..660c134 100644 --- a/pay-java-wx/pom.xml +++ b/pay-java-wx/pom.xml @@ -5,7 +5,7 @@ pay-java-parent com.egzosn - 2.14.4 + 2.14.4-fix 4.0.0 pay-java-wx diff --git a/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/api/WxPayService.java b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/api/WxPayService.java index 8d7b3f6..0900af2 100644 --- a/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/api/WxPayService.java +++ b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/api/WxPayService.java @@ -98,6 +98,7 @@ public class WxPayService extends BasePayService { */ public WxPayService(WxPayConfigStorage payConfigStorage) { super(payConfigStorage); + } /** @@ -109,7 +110,9 @@ public class WxPayService extends BasePayService { public WxPayService(WxPayConfigStorage payConfigStorage, HttpConfigStorage configStorage) { super(payConfigStorage, configStorage); } - + { + initAfter(); + } /** * 辅助api @@ -132,7 +135,6 @@ public class WxPayService extends BasePayService { /** * 初始化之后执行 */ - @Override protected void initAfter() { payConfigStorage.setPartner(StringUtils.isNotEmpty(payConfigStorage.getSubMchId())); // new Thread(() -> { diff --git a/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/api/WxProfitSharingService.java b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/api/WxProfitSharingService.java index 275b034..cc9d1a9 100644 --- a/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/api/WxProfitSharingService.java +++ b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/api/WxProfitSharingService.java @@ -69,10 +69,10 @@ public class WxProfitSharingService extends WxPayService implements ProfitSharin */ @Override protected void initAfter() { - new Thread(() -> { +// new Thread(() -> { payConfigStorage.loadCertEnvironment(); getAssistService(); - }).start(); +// }).start(); } diff --git a/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/utils/AntCertificationUtil.java b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/utils/AntCertificationUtil.java index b494c62..e29e163 100644 --- a/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/utils/AntCertificationUtil.java +++ b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/utils/AntCertificationUtil.java @@ -24,6 +24,7 @@ import javax.crypto.spec.SecretKeySpec; import javax.management.openmbean.InvalidKeyException; import com.egzosn.pay.common.exception.PayErrorException; +import com.egzosn.pay.common.util.sign.SignUtils; import com.egzosn.pay.common.util.sign.encrypt.Base64; import com.egzosn.pay.wx.bean.WxPayError; import com.egzosn.pay.wx.v3.bean.CertEnvironment; @@ -54,7 +55,7 @@ public final class AntCertificationUtil { if (javaVersion.contains("1.8") || javaVersion.startsWith("8")){ Security.setProperty("crypto.policy", "unlimited"); } - + SignUtils.initBc(); try { PKCS12_KEY_STORE = KeyStore.getInstance("PKCS12"); } diff --git a/pay-java-yiji/pom.xml b/pay-java-yiji/pom.xml index 1993eb7..c49d41e 100644 --- a/pay-java-yiji/pom.xml +++ b/pay-java-yiji/pom.xml @@ -5,7 +5,7 @@ pay-java-parent com.egzosn - 2.14.4 + 2.14.4-fix 4.0.0 diff --git a/pom.xml b/pom.xml index a919890..8006320 100644 --- a/pom.xml +++ b/pom.xml @@ -7,7 +7,7 @@ com.egzosn pay-java-parent pom - 2.14.4 + 2.14.4-fix Pay Java - Parent Pay Java Parent @@ -39,11 +39,6 @@ hocgin@gmail.com https://github.com/hocgin - - Menjoe - 1030009014@qq.com - https://gitee.com/menjoe-z - scm:git:https://github.com/egzosn/pay-java-parent.git @@ -70,7 +65,7 @@ - 2.14.4 + 2.14.4-fix 4.5.4 1.2.17 1.2.73 -- Gitee From 739c0cc03e6bffec572ab36162ade31e04f1e46a Mon Sep 17 00:00:00 2001 From: egan Date: Thu, 5 May 2022 22:10:25 +0800 Subject: [PATCH 273/299] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E5=B7=B2=E7=9F=A5bug?= =?UTF-8?q?=20=202.14.4=E7=89=88=E6=9C=AC=20=E5=BE=AE=E4=BF=A1=E6=94=AF?= =?UTF-8?q?=E4=BB=98=20v3=20=E5=90=AF=E5=8A=A8=E6=8A=A5=E9=94=99=20#90=20?= =?UTF-8?q?=20=E5=BE=AE=E4=BF=A1V3=E5=88=9D=E5=A7=8B=E5=8C=96apiServerUrl?= =?UTF-8?q?=E4=B8=BAnull?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 8006320..65aa5fb 100644 --- a/pom.xml +++ b/pom.xml @@ -59,7 +59,7 @@ pay-java-paypal pay-java-yiji pay-java-baidu - + pay-java-demo -- Gitee From 29db0f99e2e7f11f969f2e8cb4cec7b1421dfb64 Mon Sep 17 00:00:00 2001 From: egan Date: Sat, 2 Jul 2022 21:29:46 +0800 Subject: [PATCH 274/299] =?UTF-8?q?=E4=BB=A3=E7=A0=81=E6=95=B4=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pay-java-ali/pom.xml | 2 +- .../com/egzosn/pay/ali/api/AliPayService.java | 2 + pay-java-baidu/pom.xml | 2 +- .../egzosn/pay/baidu/api/BaiduPayService.java | 2 + pay-java-common/pom.xml | 2 +- .../egzosn/pay/common/api/BasePayService.java | 54 ++++---- .../egzosn/pay/common/http/UriVariables.java | 5 +- pay-java-demo/pom.xml | 8 +- .../demo/controller/WxV3PayController.java | 2 +- pay-java-fuiou/pom.xml | 2 +- pay-java-payoneer/pom.xml | 2 +- pay-java-paypal/pom.xml | 2 +- .../pay/paypal/v2/api/PayPalPayService.java | 2 +- pay-java-union/pom.xml | 2 +- .../egzosn/pay/union/api/UnionPayService.java | 2 + pay-java-web-support/pom.xml | 2 +- pay-java-wx-youdian/pom.xml | 2 +- .../wx/youdian/api/WxYouDianPayService.java | 126 ++++++------------ pay-java-wx/pom.xml | 2 +- .../pay/wx/v3/api/WxCombinePayService.java | 1 + .../egzosn/pay/wx/v3/api/WxPayService.java | 3 +- .../pay/wx/v3/api/WxProfitSharingService.java | 3 + pay-java-yiji/pom.xml | 2 +- .../egzosn/pay/yiji/api/YiJiPayService.java | 14 +- pom.xml | 4 +- 25 files changed, 115 insertions(+), 135 deletions(-) diff --git a/pay-java-ali/pom.xml b/pay-java-ali/pom.xml index 53896ee..feabf14 100644 --- a/pay-java-ali/pom.xml +++ b/pay-java-ali/pom.xml @@ -5,7 +5,7 @@ pay-java-parent com.egzosn - 2.14.4-fix + 2.14.5 4.0.0 pay-java-ali 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 4d4ecfd..f43b76f 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 @@ -628,6 +628,7 @@ public class AliPayService extends BasePayService implement * @param billType 账单类型,商户通过接口或商户经开放平台授权后其所属服务商通过接口可以获取以下账单类型:trade、signcustomer;trade指商户基于支付宝交易收单的业务账单;signcustomer是指基于商户支付宝余额收入及支出等资金变动的帐务账单; * @return 返回支付方下载对账单的结果 */ + @Override public Map downloadBill(Date billDate, String billType) { return this.downloadBill(billDate, "trade".equals(billType) ? AliPayBillType.TRADE_DAY : AliPayBillType.SIGNCUSTOMER_DAY); @@ -640,6 +641,7 @@ public class AliPayService extends BasePayService implement * @param billType 账单类型,商户通过接口或商户经开放平台授权后其所属服务商通过接口可以获取以下账单类型:trade、signcustomer;trade指商户基于支付宝交易收单的业务账单;signcustomer是指基于商户支付宝余额收入及支出等资金变动的帐务账单; * @return 返回支付方下载对账单的结果 */ + @Override public Map downloadBill(Date billDate, BillType billType) { //获取公共参数 Map parameters = getPublicParameters(AliTransactionType.DOWNLOADBILL); diff --git a/pay-java-baidu/pom.xml b/pay-java-baidu/pom.xml index 576a6f5..e2723cd 100644 --- a/pay-java-baidu/pom.xml +++ b/pay-java-baidu/pom.xml @@ -5,7 +5,7 @@ pay-java-parent com.egzosn - 2.14.4-fix + 2.14.5 4.0.0 pay-java-baidu diff --git a/pay-java-baidu/src/main/java/com/egzosn/pay/baidu/api/BaiduPayService.java b/pay-java-baidu/src/main/java/com/egzosn/pay/baidu/api/BaiduPayService.java index 468a46e..42c064c 100644 --- a/pay-java-baidu/src/main/java/com/egzosn/pay/baidu/api/BaiduPayService.java +++ b/pay-java-baidu/src/main/java/com/egzosn/pay/baidu/api/BaiduPayService.java @@ -542,6 +542,7 @@ public class BaiduPayService extends BasePayService { * @param accessToken 用户token * @return 对账单 */ + @Override public Map downloadBill(Date billDate, String accessToken) { return downloadBill(billDate, new BaiduBillType(accessToken, BaiduTransactionType.DOWNLOAD_ORDER_BILL.name())); } @@ -553,6 +554,7 @@ public class BaiduPayService extends BasePayService { * @param billType 账单类型 {@link BaiduBillType} * @return 返回支付方下载对账单的结果 */ + @Override public Map downloadBill(Date billDate, BillType billType) { Map parameters = new HashMap<>(); parameters.put("access_token", billType.getCustom()); diff --git a/pay-java-common/pom.xml b/pay-java-common/pom.xml index 37a7b9d..c7d3e72 100644 --- a/pay-java-common/pom.xml +++ b/pay-java-common/pom.xml @@ -5,7 +5,7 @@ pay-java-parent com.egzosn - 2.14.4-fix + 2.14.5 4.0.0 jar diff --git a/pay-java-common/src/main/java/com/egzosn/pay/common/api/BasePayService.java b/pay-java-common/src/main/java/com/egzosn/pay/common/api/BasePayService.java index 50bade5..1b54d95 100644 --- a/pay-java-common/src/main/java/com/egzosn/pay/common/api/BasePayService.java +++ b/pay-java-common/src/main/java/com/egzosn/pay/common/api/BasePayService.java @@ -3,6 +3,7 @@ package com.egzosn.pay.common.api; import java.awt.image.BufferedImage; import java.io.InputStream; import java.io.UnsupportedEncodingException; +import java.nio.charset.Charset; import java.util.ArrayList; import java.util.Collections; import java.util.Date; @@ -11,6 +12,7 @@ import java.util.List; import java.util.Map; import java.util.TreeMap; +import org.apache.http.Consts; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -59,6 +61,7 @@ public abstract class BasePayService implements Pay */ protected List> interceptors = new ArrayList>(); + private Charset inputCharset = Consts.UTF_8; /** * 设置支付配置 @@ -68,6 +71,10 @@ public abstract class BasePayService implements Pay @Override public BasePayService setPayConfigStorage(PC payConfigStorage) { this.payConfigStorage = payConfigStorage; + + if (StringUtils.isNotEmpty(payConfigStorage.getInputCharset())) { + this.inputCharset = Charset.forName(payConfigStorage.getInputCharset()); + } return this; } @@ -94,7 +101,6 @@ public abstract class BasePayService implements Pay } - public BasePayService(PC payConfigStorage) { this(payConfigStorage, null); } @@ -106,7 +112,6 @@ public abstract class BasePayService implements Pay } - /** * Generate a Base64 encoded String from user , password * @@ -159,11 +164,11 @@ public abstract class BasePayService implements Pay */ @Override public String toPay(O order) { - if (StringUtils.isNotEmpty(order.getSubject()) && order.getSubject().contains("'")){ - order.setSubject(order.getSubject().replace("'","")); + if (StringUtils.isNotEmpty(order.getSubject()) && order.getSubject().contains("'")) { + order.setSubject(order.getSubject().replace("'", "")); } - if (StringUtils.isNotEmpty(order.getBody()) && order.getBody().contains("'")){ - order.setBody(order.getBody().replace("'","")); + if (StringUtils.isNotEmpty(order.getBody()) && order.getBody().contains("'")) { + order.setBody(order.getBody().replace("'", "")); } Map orderInfo = orderInfo(order); return buildRequest(orderInfo, MethodType.POST); @@ -203,6 +208,7 @@ public abstract class BasePayService implements Pay public Map getParameter2Map(Map parameterMap, InputStream is) { return getNoticeParams(new DefaultNoticeRequest(parameterMap, is)).getBody(); } + /** * 将请求参数或者请求流转化为 Map * @@ -213,22 +219,18 @@ public abstract class BasePayService implements Pay public NoticeParams getNoticeParams(NoticeRequest request) { final Map parameterMap = request.getParameterMap(); - Map params = new TreeMap(); + Map params = new TreeMap<>(); for (Map.Entry entry : parameterMap.entrySet()) { String name = entry.getKey(); String[] values = entry.getValue(); - String valueStr = ""; + StringBuilder sb = new StringBuilder(); for (int i = 0, len = values.length; i < len; i++) { - valueStr += (i == len - 1) ? values[i] : values[i] + ","; + sb.append(values[i]).append((i == len - 1) ? "" : ','); } + String valueStr = sb.toString(); if (StringUtils.isNotEmpty(payConfigStorage.getInputCharset()) && !valueStr.matches("\\w+")) { - try { - if (valueStr.equals(new String(valueStr.getBytes("iso8859-1"), "iso8859-1"))) { - valueStr = new String(valueStr.getBytes("iso8859-1"), payConfigStorage.getInputCharset()); - } - } - catch (UnsupportedEncodingException e) { - LOG.error("", e); + if (valueStr.equals(new String(valueStr.getBytes(Consts.ISO_8859_1), Consts.ISO_8859_1))) { + valueStr = new String(valueStr.getBytes(Consts.ISO_8859_1), inputCharset); } } params.put(name, valueStr); @@ -329,9 +331,10 @@ public abstract class BasePayService implements Pay * @return 返回支付方下载对账单的结果 */ @Override - public Map downloadBill(Date billDate, String billType){ + public Map downloadBill(Date billDate, String billType) { return Collections.emptyMap(); } + /** * 转账 * @@ -421,7 +424,6 @@ public abstract class BasePayService implements Pay } - /** * 将请求参数或者请求流转化为 Map * @@ -432,7 +434,7 @@ public abstract class BasePayService implements Pay @Deprecated @Override public PayOutMessage payBack(Map parameterMap, InputStream is) { - return payBack(new DefaultNoticeRequest(parameterMap, is)); + return payBack(new DefaultNoticeRequest(parameterMap, is)); } /** @@ -445,7 +447,7 @@ public abstract class BasePayService implements Pay public PayOutMessage payBack(NoticeRequest request) { final NoticeParams noticeParams = getNoticeParams(request); if (LOG.isDebugEnabled()) { - LOG.debug("回调响应:{}" , JSON.toJSONString(noticeParams)); + LOG.debug("回调响应:{}", JSON.toJSONString(noticeParams)); } if (!verify(noticeParams)) { return getPayOutMessage("fail", "失败"); @@ -480,26 +482,30 @@ public abstract class BasePayService implements Pay * @param orderInfo 订单信息 * @return 处理后订单信息 */ + @Override public Map preOrderHandler(Map orderInfo, O payOrder) { return orderInfo; } /** * 过时 + * * @param parameters 参数map - * @param key key - * @param value 值 + * @param key key + * @param value 值 * @return 返回订单参数 */ @Deprecated protected Map setParameters(Map parameters, String key, String value) { return OrderParaStructure.loadParameters(parameters, key, value); } + /** * 过时 + * * @param parameters 参数map - * @param key key - * @param order 订单对象 + * @param key key + * @param order 订单对象 * @return 返回订单参数 */ @Deprecated diff --git a/pay-java-common/src/main/java/com/egzosn/pay/common/http/UriVariables.java b/pay-java-common/src/main/java/com/egzosn/pay/common/http/UriVariables.java index cff3608..5da5719 100644 --- a/pay-java-common/src/main/java/com/egzosn/pay/common/http/UriVariables.java +++ b/pay-java-common/src/main/java/com/egzosn/pay/common/http/UriVariables.java @@ -146,7 +146,8 @@ public final class UriVariables { boolean isOpen = false;//值里有嵌套 char openName = 0; if (len > 0) { - for (int i = 0; i < len; i++) {// 遍历整个带解析的字符串 + // 遍历整个带解析的字符串 + for (int i = 0; i < len; i++) { curChar = str.charAt(i);// 取当前字符 if (isKey) {// 如果当前生成的是key @@ -177,7 +178,7 @@ public final class UriVariables { } } if (curChar == '&' && !isOpen) {// 如果读取到&分割符,同时这个分割符不是值域,这时将map里添加 - putKeyValueToMap(temp, isKey, key, map); + putKeyValueToMap(temp, false, key, map); temp.setLength(0); isKey = true; } diff --git a/pay-java-demo/pom.xml b/pay-java-demo/pom.xml index 8e14d35..ddc9ab0 100644 --- a/pay-java-demo/pom.xml +++ b/pay-java-demo/pom.xml @@ -5,7 +5,7 @@ pay-java-parent com.egzosn - 2.14.4-fix + 2.14.5 4.0.0 war @@ -84,7 +84,11 @@ jackson-databind 2.9.10.6 - + + org.slf4j + slf4j-log4j12 + 1.7.30 + diff --git a/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/WxV3PayController.java b/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/WxV3PayController.java index 7511364..a3bc5da 100644 --- a/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/WxV3PayController.java +++ b/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/WxV3PayController.java @@ -45,7 +45,7 @@ public class WxV3PayController { private WxPayService service = null; - @PostConstruct //没有证书的情况下注释掉,避免启动报错 +// @PostConstruct //没有证书的情况下注释掉,避免启动报错 public void init() { WxPayConfigStorage wxPayConfigStorage = new WxPayConfigStorage(); wxPayConfigStorage.setAppId("wxc7b993ff15a9f26c"); diff --git a/pay-java-fuiou/pom.xml b/pay-java-fuiou/pom.xml index c5e6c90..514bcb9 100644 --- a/pay-java-fuiou/pom.xml +++ b/pay-java-fuiou/pom.xml @@ -5,7 +5,7 @@ pay-java-parent com.egzosn - 2.14.4-fix + 2.14.5 4.0.0 pay-java-fuiou diff --git a/pay-java-payoneer/pom.xml b/pay-java-payoneer/pom.xml index e935206..415950d 100644 --- a/pay-java-payoneer/pom.xml +++ b/pay-java-payoneer/pom.xml @@ -5,7 +5,7 @@ pay-java-parent com.egzosn - 2.14.4-fix + 2.14.5 4.0.0 pay-java-payoneer diff --git a/pay-java-paypal/pom.xml b/pay-java-paypal/pom.xml index 6f7ba60..efcb5e2 100644 --- a/pay-java-paypal/pom.xml +++ b/pay-java-paypal/pom.xml @@ -5,7 +5,7 @@ pay-java-parent com.egzosn - 2.14.4-fix + 2.14.5 4.0.0 diff --git a/pay-java-paypal/src/main/java/com/egzosn/pay/paypal/v2/api/PayPalPayService.java b/pay-java-paypal/src/main/java/com/egzosn/pay/paypal/v2/api/PayPalPayService.java index 7219f41..5eb631d 100644 --- a/pay-java-paypal/src/main/java/com/egzosn/pay/paypal/v2/api/PayPalPayService.java +++ b/pay-java-paypal/src/main/java/com/egzosn/pay/paypal/v2/api/PayPalPayService.java @@ -518,7 +518,7 @@ public class PayPalPayService extends BasePayService implem JSONObject resp = getHttpRequestTemplate().getForObject(getReqUrl(PayPalTransactionType.REFUND_GET), authHeader(), JSONObject.class, refundOrder.getRefundNo()); return resp; } - + @Override public Map downloadBill(Date billDate, BillType billType) { return Collections.emptyMap(); } diff --git a/pay-java-union/pom.xml b/pay-java-union/pom.xml index 28795c7..7058352 100644 --- a/pay-java-union/pom.xml +++ b/pay-java-union/pom.xml @@ -5,7 +5,7 @@ pay-java-parent com.egzosn - 2.14.4-fix + 2.14.5 4.0.0 diff --git a/pay-java-union/src/main/java/com/egzosn/pay/union/api/UnionPayService.java b/pay-java-union/src/main/java/com/egzosn/pay/union/api/UnionPayService.java index 42f82f3..97c913d 100644 --- a/pay-java-union/src/main/java/com/egzosn/pay/union/api/UnionPayService.java +++ b/pay-java-union/src/main/java/com/egzosn/pay/union/api/UnionPayService.java @@ -231,6 +231,7 @@ public class UnionPayService extends BasePayService { * @param noticeParams 回调回来的参数集 * @return 签名校验 true通过 */ + @Override public boolean verify(NoticeParams noticeParams) { final Map result = noticeParams.getBody(); if (null == result || result.get(SDKConstants.param_signature) == null) { @@ -719,6 +720,7 @@ public class UnionPayService extends BasePayService { * @param fileType 文件类型 文件类型,一般商户填写00即可 * @return 返回fileContent 请自行将数据落地 */ + @Override public Map downloadBill(Date billDate, String fileType) { return downloadBill(billDate, new UnionPayBillType(fileType)); } diff --git a/pay-java-web-support/pom.xml b/pay-java-web-support/pom.xml index 9995c94..8ddb16d 100644 --- a/pay-java-web-support/pom.xml +++ b/pay-java-web-support/pom.xml @@ -5,7 +5,7 @@ pay-java-parent com.egzosn - 2.14.4-fix + 2.14.5 4.0.0 jar diff --git a/pay-java-wx-youdian/pom.xml b/pay-java-wx-youdian/pom.xml index 11bd564..6d1afc9 100644 --- a/pay-java-wx-youdian/pom.xml +++ b/pay-java-wx-youdian/pom.xml @@ -5,7 +5,7 @@ pay-java-parent com.egzosn - 2.14.4-fix + 2.14.5 4.0.0 pay-java-wx-youdian 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 516696c..d16cdd8 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 @@ -1,10 +1,8 @@ package com.egzosn.pay.wx.youdian.api; -import java.io.InputStream; import java.math.BigDecimal; import java.util.Collections; import java.util.Date; -import java.util.Iterator; import java.util.Map; import java.util.TreeMap; import java.util.concurrent.locks.Lock; @@ -15,11 +13,9 @@ import com.egzosn.pay.common.api.BasePayService; import com.egzosn.pay.common.bean.AssistOrder; import com.egzosn.pay.common.bean.BaseRefundResult; import com.egzosn.pay.common.bean.BillType; - import com.egzosn.pay.common.bean.CurType; import com.egzosn.pay.common.bean.MethodType; import com.egzosn.pay.common.bean.NoticeParams; -import com.egzosn.pay.common.bean.NoticeRequest; import com.egzosn.pay.common.bean.PayMessage; import com.egzosn.pay.common.bean.PayOrder; import com.egzosn.pay.common.bean.PayOutMessage; @@ -47,21 +43,19 @@ import com.egzosn.pay.wx.youdian.bean.YoudianTransactionType; */ public class WxYouDianPayService extends BasePayService { - private final static String URL = "http://life.51youdian.com/Api/CheckoutCounter/"; - + private static final String URL = "http://life.51youdian.com/Api/CheckoutCounter/"; + private static final String ACCESS_TOKEN = "access_token"; + private static final String RETURN_CODE = "return_code"; + private static final String ERROR_CODE = "errorcode"; + private static final String ORDER_SN = "order_sn"; /** * 获取请求token * * @return 授权令牌 */ - public String getAccessToken() { - try { - return getAccessToken(false); - } - catch (PayErrorException e) { - throw e; - } + private String getAccessToken() { + return getAccessToken(false); } /** @@ -71,7 +65,7 @@ public class WxYouDianPayService extends BasePayService data = new TreeMap<>(); data.put("username", payConfigStorage.getSeller()); data.put("password", payConfigStorage.getKeyPrivate()); @@ -125,7 +119,7 @@ public class WxYouDianPayService extends BasePayService params = noticeParams.getBody(); - 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("友店微信支付异常:return_code={},参数集={}", params.get(RETURN_CODE), params); return false; } if (params.get("sign") == null) { - LOG.debug("友店微信支付异常:签名为空!out_trade_no=" + params.get("out_trade_no")); + 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) { - LOG.error(e.getMessage()); - } - return false; + return signVerify(params, (String) params.get("sign")) && verifySource((String) params.get("out_trade_no")); } /** @@ -176,7 +165,7 @@ public class WxYouDianPayService extends BasePayService params, String sign) { + private boolean signVerify(Map params, String sign) { return SignUtils.valueOf(payConfigStorage.getSignType()).verify(params, sign, "&key=" + payConfigStorage.getKeyPublic(), payConfigStorage.getInputCharset()); } @@ -188,12 +177,12 @@ public class WxYouDianPayService extends BasePayService= 400) { @@ -216,13 +205,13 @@ public class WxYouDianPayService extends BasePayService data = new TreeMap<>(); - data.put("access_token", getAccessToken()); + data.put(ACCESS_TOKEN, getAccessToken()); data.put("paymoney", Util.conversionAmount(order.getPrice()).toString()); data.putAll(order.getAttrs()); data = preOrderHandler(data, order); @@ -271,15 +260,10 @@ public class WxYouDianPayService extends BasePayService parameterMap = request.getParameterMap(); - Map params = new TreeMap(); - for (Iterator iter = parameterMap.keySet().iterator(); iter.hasNext(); ) { - String name = (String) iter.next(); - String[] values = parameterMap.get(name); - String valueStr = ""; - for (int i = 0; i < values.length; i++) { - valueStr = (i == values.length - 1) ? valueStr + values[i] : valueStr + values[i] + ","; - } - params.put(name, valueStr.trim()); - } - - return new NoticeParams(params); - - } - /** * 具体需要返回的数据为 * return_code 返回码只有SUCCESS和FAIL @@ -342,11 +301,11 @@ public class WxYouDianPayService extends BasePayService builder = new TreeMap<>(); - builder.put("return_code", code.toUpperCase()); + builder.put(RETURN_CODE, code.toUpperCase()); builder.put("return_msg", message); builder.put("nonce_str", SignTextUtils.randomStr()); String sgin = SignUtils.valueOf(payConfigStorage.getSignType()).sign(builder, "&key=" + payConfigStorage.getKeyPrivate(), payConfigStorage.getInputCharset()); - return PayOutMessage.TEXT().content("{\"return_code\":\"" + builder.get("return_code") + "\",\"return_msg\":\"" + builder.get("return_msg") + "\",\"nonce_str\":\"" + builder.get("nonce_str") + "\",\"sign\":\"" + sgin + "\"}").build(); + return PayOutMessage.TEXT().content("{\"return_code\":\"" + builder.get(RETURN_CODE) + "\",\"return_msg\":\"" + builder.get("return_msg") + "\",\"nonce_str\":\"" + builder.get("nonce_str") + "\",\"sign\":\"" + sgin + "\"}").build(); } @@ -393,8 +352,7 @@ public class WxYouDianPayService extends BasePayService microPay(PayOrder order) { order.setTransactionType(YoudianTransactionType.MICROPAY); - JSONObject orderInfo = orderInfo(order); - return orderInfo; + return orderInfo(order); } /** @@ -406,7 +364,7 @@ public class WxYouDianPayService extends BasePayService query(String tradeNo, String outTradeNo) { - return query(new AssistOrder(tradeNo, outTradeNo)); + return query(new AssistOrder(tradeNo, outTradeNo)); } /** @@ -419,18 +377,17 @@ public class WxYouDianPayService extends BasePayService query(AssistOrder assistOrder) { String apbNonce = SignTextUtils.randomStr(); TreeMap data = new TreeMap<>(); - data.put("access_token", payConfigStorage.getAccessToken()); + data.put(ACCESS_TOKEN, payConfigStorage.getAccessToken()); if (StringUtils.isEmpty(assistOrder.getTradeNo())) { - data.put("order_sn", assistOrder.getOutTradeNo()); + data.put(ORDER_SN, assistOrder.getOutTradeNo()); } else { - data.put("order_sn", assistOrder.getTradeNo()); + data.put(ORDER_SN, assistOrder.getTradeNo()); } String sign = createSign(SignTextUtils.parameterText(data, "") + apbNonce, payConfigStorage.getInputCharset()); String queryParam = SignTextUtils.parameterText(data) + "&apb_nonce=" + apbNonce + "&sign=" + sign; - JSONObject jsonObject = execute(getReqUrl(YoudianTransactionType.NATIVE_STATUS) + "?" + queryParam, MethodType.GET, null); - return jsonObject; + return execute(getReqUrl(YoudianTransactionType.NATIVE_STATUS) + "?" + queryParam, MethodType.GET, null); } @@ -438,14 +395,15 @@ public class WxYouDianPayService extends BasePayService close(String tradeNo, String outTradeNo) { return Collections.emptyMap(); } + /** * 交易关闭接口 * - * @param assistOrder 关闭订单 + * @param assistOrder 关闭订单 * @return 返回支付方交易关闭后的结果 */ @Override - public Map close(AssistOrder assistOrder){ + public Map close(AssistOrder assistOrder) { return Collections.emptyMap(); } @@ -459,13 +417,13 @@ public class WxYouDianPayService extends BasePayService data = new TreeMap<>(); - data.put("access_token", payConfigStorage.getAccessToken()); + data.put(ACCESS_TOKEN, payConfigStorage.getAccessToken()); if (StringUtils.isEmpty(refundOrder.getOutTradeNo())) { - data.put("order_sn", refundOrder.getOutTradeNo()); + data.put(ORDER_SN, refundOrder.getOutTradeNo()); } else { - data.put("order_sn", refundOrder.getTradeNo()); + data.put(ORDER_SN, refundOrder.getTradeNo()); } //支付类型刷卡为3扫码为4 data.put("type", "4"); @@ -476,7 +434,7 @@ public class WxYouDianPayService extends BasePayService pay-java-parent com.egzosn - 2.14.4-fix + 2.14.5 4.0.0 pay-java-wx diff --git a/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/api/WxCombinePayService.java b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/api/WxCombinePayService.java index a1dc98a..de4a6e8 100644 --- a/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/api/WxCombinePayService.java +++ b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/api/WxCombinePayService.java @@ -81,6 +81,7 @@ public class WxCombinePayService extends WxPayService { * @param order 支付订单集 * @return 下单结果 */ + @Override public JSONObject unifiedOrder(PayOrder order) { //统一下单 diff --git a/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/api/WxPayService.java b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/api/WxPayService.java index 0900af2..2d84635 100644 --- a/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/api/WxPayService.java +++ b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/api/WxPayService.java @@ -200,6 +200,7 @@ public class WxPayService extends BasePayService { * @param noticeParams 通知参数 * @return the boolean */ + @Override public boolean verify(NoticeParams noticeParams) { //当前使用的微信平台证书序列号 @@ -238,7 +239,6 @@ public class WxPayService extends BasePayService { //统一下单 Map parameters = wxParameterStructure.initPartner(null); - ; // wxParameterStructure.getPublicParameters(parameters); // 商品描述 OrderParaStructure.loadParameters(parameters, WxConst.DESCRIPTION, order.getSubject()); @@ -573,6 +573,7 @@ public class WxPayService extends BasePayService { * @param billType 账单类型 {@link WxBillType} 与 {@link WxAccountType} * @return 返回支付方下载对账单的结果, 如果【账单类型】为gzip的话则返回值中key为data值为gzip的输入流 */ + @Override public Map downloadBill(Date billDate, BillType billType) { //获取公共参数 Map parameters = new HashMap<>(5); diff --git a/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/api/WxProfitSharingService.java b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/api/WxProfitSharingService.java index cc9d1a9..d030742 100644 --- a/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/api/WxProfitSharingService.java +++ b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/api/WxProfitSharingService.java @@ -94,6 +94,7 @@ public class WxProfitSharingService extends WxPayService implements ProfitSharin * @param noticeParams 通知参数 * @return the boolean */ + @Override public boolean verify(NoticeParams noticeParams) { throw new PayErrorException(new WxPayError("", "分账不支持方式")); } @@ -105,6 +106,7 @@ public class WxProfitSharingService extends WxPayService implements ProfitSharin * @param order 支付订单集 * @return 下单结果 */ + @Override public JSONObject unifiedOrder(PayOrder order) { Map parameters = new MapGen(WxConst.APPID, payConfigStorage.getAppId()) @@ -336,6 +338,7 @@ public class WxProfitSharingService extends WxPayService implements ProfitSharin * @param billType 账单类型 {@link ProfitSharingBillType} * @return 返回支付方下载对账单的结果, 如果【账单类型】为gzip的话则返回值中key为data值为gzip的输入流 */ + @Override public Map downloadBill(Date billDate, BillType billType) { Map parameters = new MapGen(WxConst.BILL_DATE, DateUtils.formatDate(billDate, DateUtils.YYYY_MM_DD)) diff --git a/pay-java-yiji/pom.xml b/pay-java-yiji/pom.xml index c49d41e..b3a7b48 100644 --- a/pay-java-yiji/pom.xml +++ b/pay-java-yiji/pom.xml @@ -5,7 +5,7 @@ pay-java-parent com.egzosn - 2.14.4-fix + 2.14.5 4.0.0 diff --git a/pay-java-yiji/src/main/java/com/egzosn/pay/yiji/api/YiJiPayService.java b/pay-java-yiji/src/main/java/com/egzosn/pay/yiji/api/YiJiPayService.java index 5f1f973..7a93c0d 100644 --- a/pay-java-yiji/src/main/java/com/egzosn/pay/yiji/api/YiJiPayService.java +++ b/pay-java-yiji/src/main/java/com/egzosn/pay/yiji/api/YiJiPayService.java @@ -11,7 +11,6 @@ import com.egzosn.pay.common.api.BasePayService; import com.egzosn.pay.common.bean.AssistOrder; import com.egzosn.pay.common.bean.BaseRefundResult; import com.egzosn.pay.common.bean.BillType; - import com.egzosn.pay.common.bean.CurType; import com.egzosn.pay.common.bean.DefaultCurType; import com.egzosn.pay.common.bean.MethodType; @@ -94,7 +93,7 @@ public class YiJiPayService extends BasePayService { @Override public boolean verify(Map params) { - return verify(new NoticeParams(params)); + return verify(new NoticeParams(params)); } @@ -104,10 +103,11 @@ public class YiJiPayService extends BasePayService { * @param noticeParams 回调回来的参数集 * @return 签名校验 true通过 */ - public boolean verify(NoticeParams noticeParams){ + @Override + public boolean verify(NoticeParams noticeParams) { final Map params = noticeParams.getBody(); if (params.get(SIGN) == null) { - LOG.debug("易极付支付异常:params:" + params); + LOG.debug("易极付支付异常:params:{}", params); return false; } @@ -311,14 +311,15 @@ public class YiJiPayService extends BasePayService { public Map close(String tradeNo, String outTradeNo) { return Collections.emptyMap(); } + /** * 交易关闭接口 * - * @param assistOrder 关闭订单 + * @param assistOrder 关闭订单 * @return 返回支付方交易关闭后的结果 */ @Override - public Map close(AssistOrder assistOrder){ + public Map close(AssistOrder assistOrder) { return Collections.emptyMap(); } @@ -400,7 +401,6 @@ public class YiJiPayService extends BasePayService { /** - * * @param billDate 账单类型,商户通过接口或商户经开放平台授权后其所属服务商通过接口可以获取以下账单类型:trade、signcustomer;trade指商户基于易极付交易收单的业务账单;signcustomer是指基于商户易极付余额收入及支出等资金变动的帐务账单; * @param billType 账单时间:日账单格式为yyyy-MM-dd,月账单格式为yyyy-MM。 * @return 返回支付方下载对账单的结果 diff --git a/pom.xml b/pom.xml index 65aa5fb..00881fe 100644 --- a/pom.xml +++ b/pom.xml @@ -7,7 +7,7 @@ com.egzosn pay-java-parent pom - 2.14.4-fix + 2.14.5 Pay Java - Parent Pay Java Parent @@ -65,7 +65,7 @@ - 2.14.4-fix + 2.14.5 4.5.4 1.2.17 1.2.73 -- Gitee From bbc10565b89295917eb7b4e0793912fa96254502 Mon Sep 17 00:00:00 2001 From: egan Date: Sat, 2 Jul 2022 21:31:23 +0800 Subject: [PATCH 275/299] =?UTF-8?q?=E5=8F=91=E7=BA=A2=E5=8C=85=E4=BF=AE?= =?UTF-8?q?=E5=A4=8Dwxappid=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/egzosn/pay/wx/api/WxPayService.java | 142 ++++++++++-------- .../egzosn/pay/wx/api/WxRedPackService.java | 20 +++ .../com/egzosn/pay/wx/bean/RedpackOrder.java | 63 ++++++-- 3 files changed, 151 insertions(+), 74 deletions(-) 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 2f2e7f3..f5eaf7b 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 @@ -12,6 +12,7 @@ import java.security.GeneralSecurityException; import java.util.Date; import java.util.HashMap; import java.util.Map; +import java.util.Objects; import java.util.SortedMap; import java.util.TreeMap; import java.util.zip.GZIPInputStream; @@ -44,7 +45,6 @@ import com.egzosn.pay.common.bean.BillType; import com.egzosn.pay.common.bean.MethodType; import com.egzosn.pay.common.bean.NoticeParams; import com.egzosn.pay.common.bean.NoticeRequest; -import com.egzosn.pay.common.bean.Order; import com.egzosn.pay.common.bean.OrderParaStructure; import com.egzosn.pay.common.bean.PayMessage; import com.egzosn.pay.common.bean.PayOrder; @@ -155,27 +155,23 @@ public class WxPayService extends BasePayService implements * @param noticeParams 回调回来的参数集 * @return 签名校验 true通过 */ + @Override public boolean verify(NoticeParams noticeParams) { final Map params = noticeParams.getBody(); //如果为退款不需要校验, 直接返回, - if (params.containsKey(REQ_INFO)){ + if (params.containsKey(REQ_INFO)) { return true; } - if (null == params.get(SIGN) || !(SUCCESS.equals(params.get(RETURN_CODE)) && SUCCESS.equals(params.get(RESULT_CODE)))) { + if (Objects.isNull(params.get(SIGN)) || !(SUCCESS.equals(params.get(RETURN_CODE)) && SUCCESS.equals(params.get(RESULT_CODE)))) { if (LOG.isErrorEnabled()) { LOG.error(String.format("微信支付异常:return_code=%s,参数集=%s", params.get(RETURN_CODE), params)); } return false; } - try { - return signVerify(params, (String) params.get(SIGN)); - } - catch (PayErrorException e) { - LOG.error("", e); - } - return false; + return signVerify(params, (String) params.get(SIGN)); + } @@ -186,7 +182,7 @@ public class WxPayService extends BasePayService implements * @param sign 比对的签名结果 * @return 生成的签名结果 */ - public boolean signVerify(Map params, String sign) { + private boolean signVerify(Map params, String sign) { return signVerify(params, sign, payConfigStorage.isTest()); } @@ -207,7 +203,7 @@ public class WxPayService extends BasePayService implements */ private Map getPublicParameters() { - Map parameters = new TreeMap(); + Map parameters = new TreeMap<>(); parameters.put(APPID, payConfigStorage.getAppId()); parameters.put(MCH_ID, payConfigStorage.getMchId()); //判断如果是服务商模式信息则加入 @@ -298,7 +294,7 @@ public class WxPayService extends BasePayService implements return result; } - Map params = new TreeMap(); + Map params = new TreeMap<>(); if (WxTransactionType.JSAPI == order.getTransactionType()) { params.put("signType", payConfigStorage.getSignType()); @@ -653,6 +649,7 @@ public class WxPayService extends BasePayService implements * RECHARGE_REFUND,返回当日充值退款订单 * @return 返回支付方下载对账单的结果 */ + @Override public Map downloadBill(Date billDate, String billType) { return downloadBill(billDate, WxPayBillType.valueOf(billType)); } @@ -668,6 +665,7 @@ public class WxPayService extends BasePayService implements * RECHARGE_REFUND,返回当日充值退款订单 * @return 返回支付方下载对账单的结果, 如果【账单类型】为gzip的话则返回值中key为data值为gzip的输入流 */ + @Override public Map downloadBill(Date billDate, BillType billType) { //获取公共参数 Map parameters = getPublicParameters(); @@ -710,24 +708,19 @@ public class WxPayService extends BasePayService implements //设置签名 setSign(parameters); InputStream inputStream = requestTemplate.postForObject(getReqUrl(WxTransactionType.DOWNLOADBILL), XML.getMap2Xml(parameters), InputStream.class); - try { - //解压流 - inputStream = uncompress(inputStream); - writeToLocal(path + DateUtils.formatDate(new Date(), DateUtils.YYYYMM) + "/" + DateUtils.formatDate(new Date(), DateUtils.YYYYMMDDHHMMSS) + ".txt", inputStream); - Map ret = new HashMap(3); + //解压流 + try (InputStream fileIs = uncompress(inputStream);) { + writeToLocal(path + DateUtils.formatDate(new Date(), DateUtils.YYYYMM) + "/" + DateUtils.formatDate(new Date(), DateUtils.YYYYMMDDHHMMSS) + ".txt", fileIs); + Map ret = new HashMap<>(3); ret.put(RETURN_CODE, SUCCESS); ret.put(RETURN_MSG_CODE, "ok"); ret.put("data", path); return ret; } - catch (Exception e) { - e.printStackTrace(); - Map ret = new HashMap(3); - ret.put(RETURN_CODE, FAIL); - ret.put(RETURN_MSG_CODE, "fail"); - ret.put("data", e.getMessage()); - return ret; + catch (IOException e) { + throw new PayErrorException(new WxPayError(FAIL, e.getMessage()), e); } + } /** @@ -739,15 +732,15 @@ public class WxPayService extends BasePayService implements */ @Deprecated public static InputStream uncompress(InputStream input) throws IOException { - ByteArrayOutputStream out = new ByteArrayOutputStream(); - GZIPInputStream ungzip = new GZIPInputStream(input); - byte[] buffer = new byte[1024]; - int n; - while ((n = ungzip.read(buffer)) >= 0) { - out.write(buffer, 0, n); + try (GZIPInputStream ungZip = new GZIPInputStream(input); ByteArrayOutputStream out = new ByteArrayOutputStream()) { + byte[] buffer = new byte[1024]; + int n; + while ((n = ungZip.read(buffer)) >= 0) { + out.write(buffer, 0, n); + } + return new ByteArrayInputStream(out.toByteArray()); } - InputStream is = new ByteArrayInputStream(out.toByteArray()); - return is; + } /** @@ -758,12 +751,11 @@ public class WxPayService extends BasePayService implements * @throws IOException IOException */ @Deprecated - private void writeToLocal(String destination, InputStream inputStream) - throws IOException { + private void writeToLocal(String destination, InputStream inputStream) throws IOException { // 判断字节大小 if (inputStream.available() != 0) { - System.out.println("结果大小:" + inputStream.available()); + LOG.debug("结果大小:{}", inputStream.available()); File file = new File(destination); if (!file.getParentFile().exists()) { boolean result = file.getParentFile().mkdirs(); @@ -771,17 +763,17 @@ public class WxPayService extends BasePayService implements LOG.warn("创建失败"); } } - OutputStream out = new FileOutputStream(file); - int size = 0; - int len = 0; - byte[] buf = new byte[1024]; - while ((size = inputStream.read(buf)) != -1) { - len += size; - out.write(buf, 0, size); + try (OutputStream out = new FileOutputStream(file)) { + int size = 0; + int len = 0; + byte[] buf = new byte[1024]; + while ((size = inputStream.read(buf)) != -1) { + len += size; + out.write(buf, 0, size); + } + LOG.debug("最终写入字节数大小:{}", len); } - LOG.info("最终写入字节数大小:" + len); - inputStream.close(); - out.close(); + } } @@ -813,7 +805,7 @@ public class WxPayService extends BasePayService implements * @param transactionType 交易类型 * @return 返回支付方对应接口的结果 */ - public Map secondaryInterface(Object transactionIdOrBillDate, String outTradeNoBillType, TransactionType transactionType) { + private Map secondaryInterface(Object transactionIdOrBillDate, String outTradeNoBillType, TransactionType transactionType) { if (transactionType == WxTransactionType.REFUND) { throw new PayErrorException(new PayException(FAILURE, "通用接口不支持:" + transactionType)); @@ -844,18 +836,11 @@ public class WxPayService extends BasePayService implements * * @param order 转账订单 *
-     *
-     * 注意事项:
-     * ◆ 当返回错误码为“SYSTEMERROR”时,请不要更换商户订单号,一定要使用原商户订单号重试,否则可能造成重复支付等资金风险。
-     * ◆ XML具有可扩展性,因此返回参数可能会有新增,而且顺序可能不完全遵循此文档规范,如果在解析回包的时候发生错误,请商户务必不要换单重试,请商户联系客服确认付款情况。如果有新回包字段,会更新到此API文档中。
-     * ◆ 因为错误代码字段err_code的值后续可能会增加,所以商户如果遇到回包返回新的错误码,请商户务必不要换单重试,请商户联系客服确认付款情况。如果有新的错误码,会更新到此API文档中。
-     * ◆ 错误代码描述字段err_code_des只供人工定位问题时做参考,系统实现时请不要依赖这个字段来做自动化处理。
-     * 
- * @return 对应的转账结果 + * @return 对应的转账结果 */ @Override public Map transfer(TransferOrder order) { - Map parameters = new TreeMap(); + Map parameters = new TreeMap<>(); parameters.put("partner_trade_no", order.getOutNo()); @@ -889,7 +874,7 @@ public class WxPayService extends BasePayService implements * 商户企业付款到银行卡 *

*/ - public Map transfers(Map parameters, TransferOrder order) { + private Map transfers(Map parameters, TransferOrder order) { //转账到余额, 申请商户号的appid或商户号绑定的appid parameters.put("mch_appid", payConfigStorage.getAppId()); parameters.put("openid", order.getPayeeAccount()); @@ -911,7 +896,7 @@ public class WxPayService extends BasePayService implements * @param order 转账订单 * @return 包装后参数信息 */ - public Map payBank(Map parameters, TransferOrder order) { + private Map payBank(Map parameters, TransferOrder order) { parameters.put("enc_bank_no", keyPublic(order.getPayeeAccount())); parameters.put("enc_true_name", keyPublic(order.getPayeeName())); @@ -952,7 +937,7 @@ public class WxPayService extends BasePayService implements } - public String keyPublic(String content) { + private String keyPublic(String content) { try { return RSA2.encrypt(content, payConfigStorage.getKeyPublic(), CIPHER_ALGORITHM, payConfigStorage.getInputCharset()); } @@ -981,7 +966,19 @@ public class WxPayService extends BasePayService implements */ @Override public Map sendredpack(RedpackOrder redpackOrder) { - Map parameters = new TreeMap(); + return sendRedPack(redpackOrder); + } + + /** + * 微信发红包 + * + * @param redpackOrder 红包实体 + * @return 返回发红包实体后的结果 + * @author faymanwang 1057438332@qq.com + */ + @Override + public Map sendRedPack(RedpackOrder redpackOrder) { + Map parameters = new TreeMap<>(); redPackParam(redpackOrder, parameters); final TransferType transferType = redpackOrder.getTransferType(); if (WxSendredpackType.SENDGROUPREDPACK == transferType) { @@ -998,7 +995,7 @@ public class WxPayService extends BasePayService implements if (WxSendredpackType.SENDMINIPROGRAMHB != transferType || FAIL.equals(resp.getString(RESULT_CODE))) { return resp; } - Map params = new TreeMap(); + Map params = new TreeMap<>(); params.put("appId", payConfigStorage.getAppId()); params.put("timeStamp", System.currentTimeMillis() / 1000 + ""); params.put("nonceStr", parameters.get(NONCE_STR)); @@ -1021,8 +1018,22 @@ public class WxPayService extends BasePayService implements */ @Override public Map gethbinfo(String mchBillno) { + return getHbInfo(mchBillno); + } + + /** + * 查询红包记录 + * 用于商户对已发放的红包进行查询红包的具体信息,可支持普通红包和裂变包 + * 查询红包记录API只支持查询30天内的红包订单,30天之前的红包订单请登录商户平台查询。 + * + * @param mchBillNo 商户发放红包的商户订单号 + * @return 返回查询结果 + * @author faymanwang 1057438332@qq.com + */ + @Override + public Map getHbInfo(String mchBillNo) { Map parameters = this.getPublicParameters(); - parameters.put("mch_billno", mchBillno); + parameters.put("mch_billno", mchBillNo); parameters.put("bill_type", "MCHT"); parameters.put(SIGN, createSign(SignTextUtils.parameterText(parameters, "&", SIGN), payConfigStorage.getInputCharset())); return requestTemplate.postForObject(getReqUrl(WxSendredpackType.GETHBINFO), XML.getMap2Xml(parameters), JSONObject.class); @@ -1037,10 +1048,13 @@ public class WxPayService extends BasePayService implements private void redPackParam(RedpackOrder redpackOrder, Map parameters) { parameters.put(NONCE_STR, SignTextUtils.randomStr()); parameters.put(MCH_ID, payConfigStorage.getPid()); - parameters.put("wxappid", payConfigStorage.getAppId()); + if (StringUtils.isEmpty(redpackOrder.getWxAppId())) { + throw new PayErrorException(new WxPayError(FAIL, "RedpackOrder#getWxAppId()公众账号appid 必填")); + } + parameters.put("wxappid", redpackOrder.getWxAppId()); parameters.put("send_name", redpackOrder.getSendName()); parameters.put("re_openid", redpackOrder.getReOpenid()); - parameters.put("mch_billno", redpackOrder.getMchBillno()); + parameters.put("mch_billno", redpackOrder.getMchBillNo()); parameters.put("total_amount", Util.conversionCentAmount(redpackOrder.getTotalAmount())); parameters.put("total_num", 1); parameters.put("wishing", redpackOrder.getWishing()); diff --git a/pay-java-wx/src/main/java/com/egzosn/pay/wx/api/WxRedPackService.java b/pay-java-wx/src/main/java/com/egzosn/pay/wx/api/WxRedPackService.java index 341f3a8..a18cff5 100644 --- a/pay-java-wx/src/main/java/com/egzosn/pay/wx/api/WxRedPackService.java +++ b/pay-java-wx/src/main/java/com/egzosn/pay/wx/api/WxRedPackService.java @@ -18,8 +18,17 @@ public interface WxRedPackService { * * @param redpackOrder 红包实体 * @return 返回发红包实体后的结果 + * @see #sendRedPack(RedpackOrder) */ + @Deprecated Map sendredpack(RedpackOrder redpackOrder); + /** + * 微信发红包 + * + * @param redpackOrder 红包实体 + * @return 返回发红包实体后的结果 + */ + Map sendRedPack(RedpackOrder redpackOrder); /** * 查询红包记录 @@ -28,6 +37,17 @@ public interface WxRedPackService { * * @param mchBillno 商户发放红包的商户订单号 * @return 返回查询结果 + * @see #getHbInfo(String) */ + @Deprecated Map gethbinfo(String mchBillno); + /** + * 查询红包记录 + * 用于商户对已发放的红包进行查询红包的具体信息,可支持普通红包和裂变包 + * 查询红包记录API只支持查询30天内的红包订单,30天之前的红包订单请登录商户平台查询。 + * + * @param mchBillNo 商户发放红包的商户订单号 + * @return 返回查询结果 + */ + Map getHbInfo(String mchBillNo); } diff --git a/pay-java-wx/src/main/java/com/egzosn/pay/wx/bean/RedpackOrder.java b/pay-java-wx/src/main/java/com/egzosn/pay/wx/bean/RedpackOrder.java index 8354f3c..2587431 100644 --- a/pay-java-wx/src/main/java/com/egzosn/pay/wx/bean/RedpackOrder.java +++ b/pay-java-wx/src/main/java/com/egzosn/pay/wx/bean/RedpackOrder.java @@ -1,32 +1,57 @@ package com.egzosn.pay.wx.bean; -import com.egzosn.pay.common.bean.TransferOrder; - import java.math.BigDecimal; +import com.egzosn.pay.common.bean.TransferOrder; + /** - * 发红包订单 + * 发红包订单 + * * @author 保网 faymanwang 1057438332@qq.com * 2020/5/15 12:40 */ public class RedpackOrder extends TransferOrder { - + /** + * 微信为发放红包商户分配的公众账号ID,接口传入的appid应该为公众号的appid或小程序的appid(在mp.weixin.qq.com申请的)或APP的appid(在open.weixin.qq.com申请的)。 + * 校验规则: + * 1、该appid需要与接口传入中的re_openid有对应关系; + * 2、该appid需要与发放红包商户号有绑定关系,若未绑定,可参考该指引完成绑定(商家商户号与AppID账号关联管理) + */ + private String wxAppId; + /** + * 商户订单号(每个订单号必须唯一。取值范围:0~9,a~z,A~Z) + * 接口根据商户订单号支持重入,如出现超时可再调用。 + */ + private String mchBillNo; /** * 商户订单号(每个订单号必须唯一。取值范围:0~9,a~z,A~Z) * 接口根据商户订单号支持重入,如出现超时可再调用 + * * @return 商户订单号 */ + @Deprecated public String getMchBillno() { - return getOutNo(); + return getMchBillNo(); } + @Deprecated public void setMchBillno(String mchBillno) { - setOutNo(mchBillno); + setMchBillNo(mchBillno); + } + + public String getMchBillNo() { + return mchBillNo; + } + + public void setMchBillNo(String mchBillNo) { + setOutNo(mchBillNo); + this.mchBillNo = mchBillNo; } /** * 商户名称:红包发送者名称 + * * @return 红包发送者名称 */ public String getSendName() { @@ -34,11 +59,12 @@ public class RedpackOrder extends TransferOrder { } public void setSendName(String sendName) { - super.setPayerName(sendName); + super.setPayerName(sendName); } /** * 用户openid + * * @return 用户openid */ public String getReOpenid() { @@ -46,11 +72,12 @@ public class RedpackOrder extends TransferOrder { } public void setReOpenid(String reOpenid) { - super.setPayeeAccount(reOpenid); + super.setPayeeAccount(reOpenid); } /** * 付款金额 每个红包金额必须在默认额度内(默认大于1元,小于200元,可在产品设置中自行申请调高额度) + * * @return 付款金额 */ public BigDecimal getTotalAmount() { @@ -60,22 +87,26 @@ public class RedpackOrder extends TransferOrder { public void setTotalAmount(BigDecimal totalAmount) { super.setAmount(totalAmount); } + /** * 红包发放总人数 * 普通红包:1 * 裂变:必须介于(包括)3到20之间 + * * @return 红包发放总人数 */ public int getTotalNum() { Object totalNum = getAttr("total_num"); - return null == totalNum ? 1 : (Integer)totalNum; + return null == totalNum ? 1 : (Integer) totalNum; } public void setTotalNum(int totalNum) { addAttr("total_num", totalNum); } + /** * 红包祝福语 + * * @return 红包祝福语 */ public String getWishing() { @@ -90,6 +121,7 @@ public class RedpackOrder extends TransferOrder { /** * 活动名称 + * * @return 活动名称 */ public String getActName() { @@ -103,6 +135,7 @@ public class RedpackOrder extends TransferOrder { public String getSceneId() { return (String) getAttr("scene_id"); } + /** * 发放红包使用场景,红包金额大于200或者小于1元时必传 * PRODUCT_1:商品促销 @@ -113,7 +146,8 @@ public class RedpackOrder extends TransferOrder { * PRODUCT_6:保险回馈 * PRODUCT_7:彩票派奖 * PRODUCT_8:税务刮奖 - * @param sceneId 红包使用场景 + * + * @param sceneId 红包使用场景 */ public void setSceneId(String sceneId) { addAttr("scene_id", sceneId); @@ -123,4 +157,13 @@ public class RedpackOrder extends TransferOrder { public void setTransferType(WxSendredpackType transferType) { super.setTransferType(transferType); } + + public String getWxAppId() { + return wxAppId; + } + + public void setWxAppId(String wxAppId) { + addAttr("wxappid", wxAppId); + this.wxAppId = wxAppId; + } } -- Gitee From db34cb0c7e69a258937361fc71203bfe0adb564c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=83=91=E7=81=B6=E7=94=9F?= Date: Sun, 8 Jan 2023 22:58:23 +0800 Subject: [PATCH 276/299] =?UTF-8?q?=E8=BD=AC=E8=B4=A6=E6=9C=8D=E5=8A=A1?= =?UTF-8?q?=E5=AE=9A=E4=B9=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../pay/common/api/TransferService.java | 33 +++++++ .../egzosn/pay/common/bean/AssistOrder.java | 12 +-- .../com/egzosn/pay/common/bean/Attrs.java | 91 ++++++++++++++++++- 3 files changed, 129 insertions(+), 7 deletions(-) create mode 100644 pay-java-common/src/main/java/com/egzosn/pay/common/api/TransferService.java diff --git a/pay-java-common/src/main/java/com/egzosn/pay/common/api/TransferService.java b/pay-java-common/src/main/java/com/egzosn/pay/common/api/TransferService.java new file mode 100644 index 0000000..695fe66 --- /dev/null +++ b/pay-java-common/src/main/java/com/egzosn/pay/common/api/TransferService.java @@ -0,0 +1,33 @@ +package com.egzosn.pay.common.api; + +import java.util.Map; + +import com.egzosn.pay.common.bean.AssistOrder; +import com.egzosn.pay.common.bean.TransferOrder; + +/** + * 转账服务 + * + * @author Egan + * @email egan@egzosn.com + * @date 2023/1/8 + */ +public interface TransferService { + + /** + * 转账 + * + * @param transferOrder 转账订单 + * @return 结果 + */ + Map transfer(TransferOrder transferOrder); + + /** + * 转账查询 + * + * @param assistOrder 辅助交易订单 + * @return 对应的转账订单 + */ + Map transferQuery(AssistOrder assistOrder); + +} diff --git a/pay-java-common/src/main/java/com/egzosn/pay/common/bean/AssistOrder.java b/pay-java-common/src/main/java/com/egzosn/pay/common/bean/AssistOrder.java index 52cb462..09dbd18 100644 --- a/pay-java-common/src/main/java/com/egzosn/pay/common/bean/AssistOrder.java +++ b/pay-java-common/src/main/java/com/egzosn/pay/common/bean/AssistOrder.java @@ -18,11 +18,11 @@ public class AssistOrder implements Order { /** - * 支付平台订单号,交易号 + * 支付平台订单号,交易号, 平台批次单号 */ private String tradeNo; /** - * 商户订单号 + * 商户订单号,商家批次单号 */ private String outTradeNo; /** @@ -77,18 +77,18 @@ public class AssistOrder implements Order { } /** - * 获取商户订单号 + * 获取商户订单号,商家批次单号 * - * @return 商户订单号 + * @return 商户订单号, 商家批次单号 */ public String getOutTradeNo() { return outTradeNo; } /** - * 设置商户订单号 + * 设置商户订单号,商家批次单号 * - * @param outTradeNo 商户订单号 + * @param outTradeNo 商户订单号,商家批次单号 */ public void setOutTradeNo(String outTradeNo) { this.outTradeNo = outTradeNo; diff --git a/pay-java-common/src/main/java/com/egzosn/pay/common/bean/Attrs.java b/pay-java-common/src/main/java/com/egzosn/pay/common/bean/Attrs.java index cd7d883..2713b0d 100644 --- a/pay-java-common/src/main/java/com/egzosn/pay/common/bean/Attrs.java +++ b/pay-java-common/src/main/java/com/egzosn/pay/common/bean/Attrs.java @@ -1,6 +1,8 @@ package com.egzosn.pay.common.bean; import java.io.Serializable; +import java.math.BigDecimal; +import java.util.Date; import java.util.Map; /** @@ -27,5 +29,92 @@ public interface Attrs extends Serializable { * @param key 属性名 * @return 属性 */ - Object getAttr(String key); + default Object getAttr(String key) { + return getAttrs().get(key); + } + + + /** + * 获取属性 这里可用做覆盖已设置的属性信息属性。 + * + * @param key 属性名 + * @return 属性 + */ + default Number getAttrForNumber(String key) { + final Object attr = getAttr(key); + if (null == attr || "".equals(attr)) { + return null; + } + if (attr instanceof Number) { + return (Number) attr; + } + + return new BigDecimal(attr.toString()); + } + + /** + * 获取属性 这里可用做覆盖已设置的属性信息属性。 + * + * @param key 属性名 + * @return 属性 + */ + default Integer getAttrForInt(String key) { + Number attr = getAttrForNumber(key); + if (null == attr) { + return null; + } + if (attr instanceof Integer) { + return (Integer) attr; + } + return attr.intValue(); + } + + /** + * 获取属性 这里可用做覆盖已设置的属性信息属性。 + * + * @param key 属性名 + * @return 属性 + */ + default Integer getAttrForInt(String key, Integer defaultValue) { + Integer value = getAttrForInt(key); + return null == value ? defaultValue : value; + } + + /** + * 获取属性 这里可用做覆盖已设置的属性信息属性。 + * + * @param key 属性名 + * @return 属性 + */ + default Long getAttrForLong(String key) { + Number attr = getAttrForNumber(key); + if (null == attr) { + return null; + } + if (attr instanceof Long) { + return (Long) attr; + } + + return attr.longValue(); + } + + /** + * 获取属性 这里可用做覆盖已设置的属性信息属性。 + * + * @param key 属性名 + * @return 属性 + */ + default String getAttrForString(String key) { + return (String) getAttr(key); + } + + /** + * 获取属性 这里可用做覆盖已设置的属性信息属性。 + * + * @param key 属性名 + * @return 属性 + */ + default Date getAttrForDate(String key) { + return (Date) getAttr(key); + } } -- Gitee From 8f7ca71d9162596ff0184de623765daf5a8c20fb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=83=91=E7=81=B6=E7=94=9F?= Date: Sun, 8 Jan 2023 22:59:16 +0800 Subject: [PATCH 277/299] =?UTF-8?q?=E9=80=9A=E7=9F=A5=E5=8F=82=E6=95=B0?= =?UTF-8?q?=E8=AF=B7=E6=B1=82=E5=A4=B4=E5=90=8D=E7=A7=B0=E4=B8=8D=E5=8C=BA?= =?UTF-8?q?=E5=88=86=E5=A4=A7=E5=B0=8F=E5=86=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../egzosn/pay/common/bean/NoticeParams.java | 25 +++++++++++++++---- 1 file changed, 20 insertions(+), 5 deletions(-) diff --git a/pay-java-common/src/main/java/com/egzosn/pay/common/bean/NoticeParams.java b/pay-java-common/src/main/java/com/egzosn/pay/common/bean/NoticeParams.java index 54f59a6..6b19357 100644 --- a/pay-java-common/src/main/java/com/egzosn/pay/common/bean/NoticeParams.java +++ b/pay-java-common/src/main/java/com/egzosn/pay/common/bean/NoticeParams.java @@ -1,5 +1,5 @@ /* - * Copyright 2017-2021 the original Egan. + * Copyright 2017-2023 the original Egan. * email egzosn@gmail.com * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -59,19 +59,34 @@ public class NoticeParams { this.headers = headers; } + private T getValueMatchingKey(Map values, String key) { + T value = values.get(key); + if (null != value) { + return value; + } + + for (Map.Entry entry : values.entrySet()) { + if (entry.getKey().equalsIgnoreCase(key)) { + return entry.getValue(); + } + } + return null; + } + + public String getHeader(String name) { - List value = this.headers.get(name); + List value = getValueMatchingKey(headers, name); return (null == value || value.isEmpty()) ? null : value.get(0); } public Enumeration getHeaders(String name) { - List value = this.headers.get(name); + List value = getValueMatchingKey(headers, name); return (Collections.enumeration(value != null ? value : Collections.emptySet())); } public Enumeration getHeaderNames() { - if (null == headers){ - return Collections.enumeration(Collections.emptySet()); + if (null == headers) { + return Collections.enumeration(Collections.emptySet()); } return Collections.enumeration(this.headers.keySet()); } -- Gitee From fdb7fcdbb908470f5a47509a8b5d7156314f2961 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=83=91=E7=81=B6=E7=94=9F?= Date: Sun, 8 Jan 2023 23:01:14 +0800 Subject: [PATCH 278/299] =?UTF-8?q?=E5=BE=AE=E4=BF=A1V3=E5=95=86=E5=AE=B6?= =?UTF-8?q?=E8=BD=AC=E8=B4=A6=E5=88=B0=E9=9B=B6=E9=92=B1=E7=9B=B8=E5=85=B3?= =?UTF-8?q?API=E5=AE=9E=E7=8E=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../wx/v3/api/DefaultWxPayAssistService.java | 5 + .../egzosn/pay/wx/v3/api/WxPayService.java | 108 +++++++++++++-- .../pay/wx/v3/bean/WxTransactionType.java | 2 +- .../egzosn/pay/wx/v3/bean/WxTransferType.java | 89 ++++++++++++ .../wx/v3/bean/transfer/TransferDetail.java | 101 ++++++++++++++ .../wx/v3/bean/transfer/WxTransferOrder.java | 124 +++++++++++++++++ .../bean/transfer/WxTransferQueryOrder.java | 131 ++++++++++++++++++ .../com/egzosn/pay/wx/v3/utils/WxConst.java | 16 +++ 8 files changed, 566 insertions(+), 10 deletions(-) create mode 100644 pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/WxTransferType.java create mode 100644 pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/transfer/TransferDetail.java create mode 100644 pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/transfer/WxTransferOrder.java create mode 100644 pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/transfer/WxTransferQueryOrder.java diff --git a/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/api/DefaultWxPayAssistService.java b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/api/DefaultWxPayAssistService.java index e9daaf6..6e7703e 100644 --- a/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/api/DefaultWxPayAssistService.java +++ b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/api/DefaultWxPayAssistService.java @@ -60,6 +60,7 @@ public class DefaultWxPayAssistService implements WxPayAssistService { * @param transactionType 交易类型 * @return 响应内容体 */ + @Override public JSONObject doExecute(Map parameters, TransactionType transactionType) { // String requestBody = JSON.toJSONString(parameters, SerializerFeature.WriteMapNullValue); String requestBody = JSON.toJSONString(parameters); @@ -75,6 +76,7 @@ public class DefaultWxPayAssistService implements WxPayAssistService { * @param uriVariables 用于匹配表达式 * @return 响应内容体 */ + @Override public ResponseEntity doExecuteEntity(String body, TransactionType transactionType, Object... uriVariables) { String reqUrl = UriVariables.getUri(wxPayService.getReqUrl(transactionType), uriVariables); MethodType method = MethodType.valueOf(transactionType.getMethod()); @@ -95,6 +97,7 @@ public class DefaultWxPayAssistService implements WxPayAssistService { * @param uriVariables 用于匹配表达式 * @return 响应内容体 */ + @Override public JSONObject doExecute(String body, TransactionType transactionType, Object... uriVariables) { final ResponseEntity responseEntity = doExecuteEntity(body, transactionType, uriVariables); int statusCode = responseEntity.getStatusCode(); @@ -112,6 +115,7 @@ public class DefaultWxPayAssistService implements WxPayAssistService { * @param order 订单 * @return 请求响应 */ + @Override public JSONObject doExecute(Map parameters, PayOrder order) { TransactionType transactionType = order.getTransactionType(); return doExecute(parameters, transactionType); @@ -127,6 +131,7 @@ public class DefaultWxPayAssistService implements WxPayAssistService { * @param method 请求方法 * @return 请求实体 */ + @Override public HttpEntity buildHttpEntity(String url, String body, String method) { String nonceStr = SignTextUtils.randomStr(); long timestamp = DateUtils.toEpochSecond(); diff --git a/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/api/WxPayService.java b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/api/WxPayService.java index 2d84635..36c2a89 100644 --- a/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/api/WxPayService.java +++ b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/api/WxPayService.java @@ -4,6 +4,7 @@ import java.io.IOException; import java.io.InputStream; import java.security.PrivateKey; import java.security.cert.Certificate; +import java.util.ArrayList; import java.util.Collections; import java.util.Date; import java.util.Enumeration; @@ -11,6 +12,7 @@ import java.util.HashMap; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; +import java.util.stream.Collectors; import org.apache.http.HttpEntity; @@ -21,9 +23,9 @@ import static com.egzosn.pay.wx.v3.utils.WxConst.FAILURE; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONObject; import com.egzosn.pay.common.api.BasePayService; +import com.egzosn.pay.common.api.TransferService; import com.egzosn.pay.common.bean.AssistOrder; import com.egzosn.pay.common.bean.BillType; - import com.egzosn.pay.common.bean.CurType; import com.egzosn.pay.common.bean.MethodType; import com.egzosn.pay.common.bean.NoticeParams; @@ -50,16 +52,17 @@ import com.egzosn.pay.common.util.sign.SignUtils; import com.egzosn.pay.common.util.sign.encrypt.RSA2; import com.egzosn.pay.common.util.str.StringUtils; import com.egzosn.pay.wx.bean.WxPayError; -import com.egzosn.pay.wx.bean.WxTransferType; import com.egzosn.pay.wx.v3.bean.WxAccountType; import com.egzosn.pay.wx.v3.bean.WxBillType; import com.egzosn.pay.wx.v3.bean.WxTransactionType; +import com.egzosn.pay.wx.v3.bean.WxTransferType; import com.egzosn.pay.wx.v3.bean.order.Amount; import com.egzosn.pay.wx.v3.bean.order.RefundAmount; import com.egzosn.pay.wx.v3.bean.response.Resource; import com.egzosn.pay.wx.v3.bean.response.WxNoticeParams; import com.egzosn.pay.wx.v3.bean.response.WxPayMessage; import com.egzosn.pay.wx.v3.bean.response.WxRefundResult; +import com.egzosn.pay.wx.v3.bean.transfer.TransferDetail; import com.egzosn.pay.wx.v3.utils.AntCertificationUtil; import com.egzosn.pay.wx.v3.utils.WxConst; @@ -72,7 +75,7 @@ import com.egzosn.pay.wx.v3.utils.WxConst; * date 2021/10/6 * */ -public class WxPayService extends BasePayService { +public class WxPayService extends BasePayService implements TransferService { /** @@ -91,14 +94,14 @@ public class WxPayService extends BasePayService { */ private volatile WxParameterStructure wxParameterStructure; + /** * 创建支付服务 * * @param payConfigStorage 微信对应的支付配置 */ public WxPayService(WxPayConfigStorage payConfigStorage) { - super(payConfigStorage); - + this(payConfigStorage, null); } /** @@ -110,6 +113,7 @@ public class WxPayService extends BasePayService { public WxPayService(WxPayConfigStorage payConfigStorage, HttpConfigStorage configStorage) { super(payConfigStorage, configStorage); } + { initAfter(); } @@ -613,14 +617,100 @@ public class WxPayService extends BasePayService { /** - * 转账 + * 发起商家转账, 转账账单电子回单申请受理接口 * - * @param order 转账订单 + * @param transferOrder 转账订单 * @return 对应的转账结果 */ @Override - public Map transfer(TransferOrder order) { - throw new PayErrorException(new WxPayError("", "V3不支持转账")); + public Map transfer(TransferOrder transferOrder) { + //转账账单电子回单申请受理接口 + if (transferOrder.getTransferType() == WxTransferType.TRANSFER_BILL_RECEIPT) { + Map attr = new MapGen(WxConst.OUT_BATCH_NO, transferOrder.getBatchNo()).getAttr(); + return getAssistService().doExecute(attr, transferOrder.getTransferType()); + } + + Map parameters = new HashMap<>(12); + parameters.put(WxConst.APPID, payConfigStorage.getAppId()); + parameters.put(WxConst.OUT_BATCH_NO, transferOrder.getBatchNo()); + OrderParaStructure.loadParameters(parameters, WxConst.BATCH_NAME, transferOrder); + parameters.put(WxConst.BATCH_REMARK, transferOrder.getRemark()); + parameters.put(WxConst.TOTAL_AMOUNT, Util.conversionCentAmount(transferOrder.getAmount())); + parameters.put(WxConst.TOTAL_NUM, transferOrder.getAttr(WxConst.TOTAL_NUM)); + Object transferDetailListAttr = transferOrder.getAttr(WxConst.TRANSFER_DETAIL_LIST); + List transferDetails = initTransferDetailListAttr(transferDetailListAttr); + parameters.put(WxConst.TRANSFER_DETAIL_LIST, transferDetails); + OrderParaStructure.loadParameters(parameters, WxConst.TRANSFER_SCENE_ID, transferOrder); + return getAssistService().doExecute(parameters, transferOrder.getTransferType()); + } + + private List initTransferDetailListAttr(Object transferDetailListAttr) { + List transferDetails = null; + if (transferDetailListAttr instanceof String) { + transferDetails = JSON.parseArray((String) transferDetailListAttr, TransferDetail.class); + } + else if (null != transferDetailListAttr) { + //偷懒的做法 + transferDetails = JSON.parseArray(JSON.toJSONString(transferDetailListAttr), TransferDetail.class); + } + else { + return null; + } + + String serialNumber = payConfigStorage.getCertEnvironment().getSerialNumber(); + Certificate certificate = getAssistService().getCertificate(serialNumber); + return transferDetails.stream() + .peek(transferDetailListItem -> { + String userName = transferDetailListItem.getUserName(); + if (StringUtils.isNotEmpty(userName)) { + String encryptedUserName = AntCertificationUtil.encryptToString(userName, certificate); + transferDetailListItem.setUserName(encryptedUserName); + } + String userIdCard = transferDetailListItem.getUserIdCard(); + if (StringUtils.isNotEmpty(userIdCard)) { + String encryptedUserIdCard = AntCertificationUtil.encryptToString(userIdCard, certificate); + transferDetailListItem.setUserIdCard(encryptedUserIdCard); + } + }).collect(Collectors.toList()); + } + + /** + * 转账查询API + * 通过批次单号查询批次单 与 通过明细单号查询明细单 + * + * @param assistOrder 辅助交易订单 + * @return 对应的转账订单 + */ + @Override + public Map transferQuery(AssistOrder assistOrder) { + Map parameters = new HashMap<>(6); + TransactionType transactionType = assistOrder.getTransactionType(); + List uriVariables = new ArrayList<>(3); + + if (StringUtils.isNotEmpty(assistOrder.getTradeNo())) { + uriVariables.add(assistOrder.getTradeNo()); + String detailId = assistOrder.getAttrForString(WxConst.DETAIL_ID); + if (StringUtils.isNotEmpty(detailId)) { + uriVariables.add(detailId); + } + } + else if (StringUtils.isNotEmpty(assistOrder.getOutTradeNo())) { + uriVariables.add(assistOrder.getOutTradeNo()); + String outDetailNo = assistOrder.getAttrForString(WxConst.OUT_DETAIL_NO); + if (StringUtils.isNotEmpty(outDetailNo)) { + uriVariables.add(outDetailNo); + } + } + + if (transactionType == com.egzosn.pay.wx.v3.bean.WxTransferType.QUERY_BATCH_BY_BATCH_ID || transactionType == com.egzosn.pay.wx.v3.bean.WxTransferType.QUERY_BATCH_BY_OUT_BATCH_NO) { + OrderParaStructure.loadParameters(parameters, WxConst.NEED_QUERY_DETAIL, assistOrder); + OrderParaStructure.loadParameters(parameters, WxConst.OFFSET, assistOrder); + OrderParaStructure.loadParameters(parameters, WxConst.LIMIT, assistOrder); + OrderParaStructure.loadParameters(parameters, WxConst.DETAIL_STATUS, assistOrder); + } + + String requestBody = JSON.toJSONString(parameters); + return getAssistService().doExecute(requestBody, assistOrder.getTransactionType(), uriVariables.toArray()); } diff --git a/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/WxTransactionType.java b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/WxTransactionType.java index ea883a7..873e2ee 100644 --- a/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/WxTransactionType.java +++ b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/WxTransactionType.java @@ -153,7 +153,7 @@ public enum WxTransactionType implements TransactionType { /** * 合单关闭订单 */ - COMBINE_CLOSE("/v3/combine-transactions/out-trade-no/{combine_out_trade_no}/close", MethodType.POST), + COMBINE_CLOSE("/v3/combine-transactions/out-trade-no/{combine_out_trade_no}/close", MethodType.POST) ; WxTransactionType(String type, MethodType method) { diff --git a/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/WxTransferType.java b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/WxTransferType.java new file mode 100644 index 0000000..1e6c55a --- /dev/null +++ b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/WxTransferType.java @@ -0,0 +1,89 @@ +package com.egzosn.pay.wx.v3.bean; + +import java.util.Map; + +import com.egzosn.pay.common.bean.MethodType; +import com.egzosn.pay.common.bean.TransferOrder; +import com.egzosn.pay.common.bean.TransferType; + +/** + * 微信转账类型 + * + * @author egan + * email egzosn@gmail.com + * date 2018/9/28.19:56 + */ +public enum WxTransferType implements TransferType { + /** + * 转账到零钱 + */ + TRANSFER_BATCHES("/v3/transfer/batches", MethodType.POST), + /** + * 查询转账到零钱的记录,通过微信批次单号查询批次单 + */ + QUERY_BATCH_BY_BATCH_ID("/v3/transfer/batches/batch-id/{batch_id}"), + /** + * 查询转账到零钱的记录,通过商家批次单号查询批次单 + */ + QUERY_BATCH_BY_OUT_BATCH_NO("/v3/transfer/batches/out-batch-no/{out_batch_no}"), + /** + * 通过微信明细单号查询明细单 + */ + QUERY_BATCH_DETAIL_BY_BATCH_ID("/v3/transfer/batches/batch-id/{batch_id}/details/detail-id/{detail_id}"), + /** + * 通过商家明细单号查询明细单 + */ + QUERY_BATCH_DETAIL_BY_OUT_BATCH_NO("/v3/transfer/batches/out-batch-no/{out_batch_no}/details/out-detail-no/{out_detail_no}"), + /** + * 转账账单电子回单申请受理接口 + */ + TRANSFER_BILL_RECEIPT("/v3/transfer/bill-receipt", MethodType.POST), + /** + * 查询转账账单电子回单接口 + */ + QUERY_TRANSFER_BILL_RECEIPT("/v3/transfer/bill-receipt/{out_batch_no}"), + /** + * 受理转账明细电子回单API + */ + TRANSFER_DETAIL_ELECTRONIC_RECEIPTS("/v3/transfer-detail/electronic-receipts", MethodType.POST), + /** + * 查询转账明细电子回单受理结果API + */ + QUERY_TRANSFER_DETAIL_ELECTRONIC_RECEIPTS("/v3/transfer-detail/electronic-receipts"), + ; + + WxTransferType(String type, MethodType method) { + this.type = type; + this.method = method; + } + + WxTransferType(String type) { + this(type, MethodType.GET); + } + + private String type; + private MethodType method; + + + @Override + public String getType() { + return type; + } + + @Override + public String getMethod() { + return this.method.name(); + } + + /** + * 设置属性 + * + * @param attr 已有属性对象 + * @param order 转账订单 + * @return 属性对象 + */ + @Override + public Map setAttr(Map attr, TransferOrder order) { + return attr; + } +} diff --git a/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/transfer/TransferDetail.java b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/transfer/TransferDetail.java new file mode 100644 index 0000000..cfe858b --- /dev/null +++ b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/transfer/TransferDetail.java @@ -0,0 +1,101 @@ +package com.egzosn.pay.wx.v3.bean.transfer; + +import com.alibaba.fastjson.annotation.JSONField; + +/** + * + * 转账明细 + * @author Egan + * @email egan@egzosn.com + * @date 2023/1/8 + */ +public class TransferDetail { + /** + * 商家明细单号 + */ + @JSONField(name = "out_detail_no") + private String outDetailNo; + /** + * 转账金额,单位为分 + */ + @JSONField(name = "transfer_amount") + private Integer transferAmount; + /** + * 单条转账备注(微信用户会收到该备注),UTF8编码,最多允许32个字符 + */ + @JSONField(name = "transfer_remark") + private String transferRemark; + /** + * 用户在直连商户appid下的唯一标识 + */ + private String openid; + /** + * 收款用户姓名 + */ + @JSONField(name = "user_name") + private String userName; + /** + * 收款用户身份证 + */ + @JSONField(name = "user_id_card") + private String userIdCard; + + + public TransferDetail() { + } + + public TransferDetail(String outDetailNo, Integer transferAmount, String transferRemark, String openid) { + this.outDetailNo = outDetailNo; + this.transferAmount = transferAmount; + this.transferRemark = transferRemark; + this.openid = openid; + } + + public String getOutDetailNo() { + return outDetailNo; + } + + public void setOutDetailNo(String outDetailNo) { + this.outDetailNo = outDetailNo; + } + + public Integer getTransferAmount() { + return transferAmount; + } + + public void setTransferAmount(Integer transferAmount) { + this.transferAmount = transferAmount; + } + + public String getTransferRemark() { + return transferRemark; + } + + public void setTransferRemark(String transferRemark) { + this.transferRemark = transferRemark; + } + + public String getOpenid() { + return openid; + } + + public void setOpenid(String openid) { + this.openid = openid; + } + + public String getUserName() { + return userName; + } + + public void setUserName(String userName) { + this.userName = userName; + } + + public String getUserIdCard() { + return userIdCard; + } + + public void setUserIdCard(String userIdCard) { + this.userIdCard = userIdCard; + } +} diff --git a/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/transfer/WxTransferOrder.java b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/transfer/WxTransferOrder.java new file mode 100644 index 0000000..0407a0d --- /dev/null +++ b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/transfer/WxTransferOrder.java @@ -0,0 +1,124 @@ +package com.egzosn.pay.wx.v3.bean.transfer; + +import java.math.BigDecimal; +import java.util.List; + +import com.egzosn.pay.common.bean.TransferOrder; +import com.egzosn.pay.wx.v3.utils.WxConst; + +/** + * 发起商家转账 + * + * @author Egan + * @email egan@egzosn.com + * @date 2023/1/8 + */ +public class WxTransferOrder extends TransferOrder { + + /** + * 商家批次单号 + */ + private String outBatchNo; + /** + * 批次名称 + */ + private String batchName; + /** + * 批次备注 + */ + private String batchRemark; + /** + * 发起批量转账的明细列表,最多三千笔 + */ + private List transferDetailList; + /** + * 转账总金额,单位为“分”。转账总金额必须与批次内所有明细转账金额之和保持一致,否则无法发起转账操作 + */ + private BigDecimal totalAmount; + /** + * 转账总笔数,一个转账批次单最多发起三千笔转账。转账总笔数必须与批次内所有明细之和保持一致,否则无法发起转账操作 + */ + private Integer totalNum; + + /** + * 必填,指定该笔转账使用的转账场景ID + */ + private String transferSceneId; + + + public String getOutBatchNo() { + return outBatchNo; + } + + public void setOutBatchNo(String outBatchNo) { + setBatchNo(outBatchNo); + this.outBatchNo = outBatchNo; + } + + public String getBatchName() { + return batchName; + } + + public void setBatchName(String batchName) { + addAttr(WxConst.BATCH_NAME, batchName); + this.batchName = batchName; + } + + public String getBatchRemark() { + return batchRemark; + } + + public void setBatchRemark(String batchRemark) { + setRemark(batchRemark); + this.batchRemark = batchRemark; + } + + public List getTransferDetailList() { + return transferDetailList; + } + + public void setTransferDetailList(List transferDetailList) { + addAttr(WxConst.TRANSFER_DETAIL_LIST, transferDetailList); + this.transferDetailList = transferDetailList; + } + + public BigDecimal getTotalAmount() { + return totalAmount; + } + + /** + * 转账金额单位为“元”。转账总金额必须与批次内所有明细转账金额之和保持一致,否则无法发起转账操作 + * @param totalAmount 元 + */ + public void setTotalAmount(BigDecimal totalAmount) { + setAmount(totalAmount); + this.totalAmount = totalAmount; + } + + /** + * 转账金额单位为“分”。转账总金额必须与批次内所有明细转账金额之和保持一致,否则无法发起转账操作 + * + * @param totalAmount 分 + */ + public void setTotalAmount(Integer totalAmount) { + setTotalAmount(new BigDecimal(totalAmount / 100)); + } + + public Integer getTotalNum() { + return totalNum; + } + + public void setTotalNum(Integer totalNum) { + addAttr(WxConst.TOTAL_NUM, totalNum); + this.totalNum = totalNum; + } + + public String getTransferSceneId() { + return transferSceneId; + } + + public void setTransferSceneId(String transferSceneId) { + addAttr(WxConst.TRANSFER_SCENE_ID, transferSceneId); + this.transferSceneId = transferSceneId; + } +} diff --git a/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/transfer/WxTransferQueryOrder.java b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/transfer/WxTransferQueryOrder.java new file mode 100644 index 0000000..36585a9 --- /dev/null +++ b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/transfer/WxTransferQueryOrder.java @@ -0,0 +1,131 @@ +package com.egzosn.pay.wx.v3.bean.transfer; + +import com.egzosn.pay.common.bean.AssistOrder; +import com.egzosn.pay.wx.v3.utils.WxConst; + +/** + * 微信转账查询订单 + * + * @author Egan + * @email egan@egzosn.com + * @date 2023/1/8 + */ +public class WxTransferQueryOrder extends AssistOrder { + + /** + * 微信批次单号 或 商家批次单号 + */ + private String batchId; + + /** + * 微信支付系统内部区分转账批次单下不同转账明细单的唯一标识 + */ + private String detailId; + + + /** + * 商户系统内部的商家批次单号,在商户系统内部唯一 + */ + private String outBatchNo; + /** + * 商户系统内部区分转账批次单下不同转账明细单的唯一标识 + */ + private String outDetailNo; + + /** + * 是否查询转账明细单 + * true-是;false-否,默认否。商户可选择是否查询指定状态的转账明细单,当转账批次单状态为“FINISHED”(已完成)时,才会返回满足条件的转账明细单 + */ + private Boolean needQueryDetail; + /** + * 请求资源起始位置 + * 该次请求资源的起始位置。返回的明细是按照设置的明细条数进行分页展示的,一次查询可能无法返回所有明细,我们使用该参数标识查询开始位置,默认值为0 + */ + private Integer offset; + /** + * 最大资源条数 + * 该次请求可返回的最大明细条数,最小20条,最大100条,不传则默认20条。不足20条按实际条数返回 + */ + private Integer limit; + /** + * 明细状态 + * WAIT_PAY: 待确认。待商户确认, 符合免密条件时, 系统会自动扭转为转账中 + * ALL:全部。需要同时查询转账成功和转账失败的明细单 + * SUCCESS:转账成功 + * FAIL:转账失败。需要确认失败原因后,再决定是否重新发起对该笔明细单的转账(并非整个转账批次单) + */ + private String detailStatus; + + public String getBatchId() { + return batchId; + } + + public void setBatchId(String batchId) { + setTradeNo(batchId); + this.batchId = batchId; + } + + public String getOutBatchNo() { + return outBatchNo; + } + + public void setOutBatchNo(String outBatchNo) { + setOutTradeNo(outBatchNo); + this.outBatchNo = outBatchNo; + } + + public String getDetailId() { + return detailId; + } + + public void setDetailId(String detailId) { + addAttr(WxConst.DETAIL_ID, detailId); + this.detailId = detailId; + } + + public String getOutDetailNo() { + return outDetailNo; + } + + public void setOutDetailNo(String outDetailNo) { + addAttr(WxConst.OUT_DETAIL_NO, detailId); + this.outDetailNo = outDetailNo; + } + + public Boolean getNeedQueryDetail() { + return needQueryDetail; + } + + public void setNeedQueryDetail(Boolean needQueryDetail) { + addAttr(WxConst.NEED_QUERY_DETAIL, needQueryDetail); + this.needQueryDetail = needQueryDetail; + } + + public Integer getOffset() { + return offset; + } + + public void setOffset(Integer offset) { + addAttr(WxConst.OFFSET, offset); + this.offset = offset; + } + + public Integer getLimit() { + return limit; + } + + public void setLimit(Integer limit) { + addAttr(WxConst.LIMIT, limit); + this.limit = limit; + } + + + public String getDetailStatus() { + return detailStatus; + } + + public void setDetailStatus(String detailStatus) { + addAttr(WxConst.DETAIL_STATUS, detailStatus); + this.detailStatus = detailStatus; + } +} diff --git a/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/utils/WxConst.java b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/utils/WxConst.java index 06d43f4..38ab1bd 100644 --- a/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/utils/WxConst.java +++ b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/utils/WxConst.java @@ -69,4 +69,20 @@ public final class WxConst { public static final String FAILURE = "failure"; public static final String RESP_BODY = WxPayService.class.getName() + "$RESP_BODY"; + public static final String OUT_BATCH_NO = "out_batch_no"; + public static final String OUT_DETAIL_NO = "out_detail_no"; + public static final String DETAIL_ID = "detail_id"; + public static final String BATCH_NAME = "batch_name"; + public static final String BATCH_REMARK = "batch_remark"; + public static final String TRANSFER_DETAIL_LIST = "transfer_detail_list"; + public static final String TOTAL_AMOUNT = "total_amount"; + public static final String TOTAL_NUM = "total_num"; + public static final String TRANSFER_SCENE_ID = "transfer_scene_id"; + public static final String BATCH_ID = "batch_id"; + public static final String NEED_QUERY_DETAIL = "need_query_detail"; + public static final String OFFSET = "offset"; + public static final String LIMIT = "limit"; + public static final String DETAIL_STATUS = "detail_status"; + + } -- Gitee From 547cbd6f841721d4f5f6676ad1baa18f85d9d11c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=83=91=E7=81=B6=E7=94=9F?= Date: Sun, 8 Jan 2023 23:01:56 +0800 Subject: [PATCH 279/299] =?UTF-8?q?=E5=BE=AE=E4=BF=A1V3=E5=95=86=E5=AE=B6?= =?UTF-8?q?=E8=BD=AC=E8=B4=A6=E5=88=B0=E9=9B=B6=E9=92=B1=E7=9B=B8=E5=85=B3?= =?UTF-8?q?API=20=E6=A1=88=E4=BE=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../demo/controller/WxV3PayController.java | 138 +++++++++++++++++- 1 file changed, 137 insertions(+), 1 deletion(-) diff --git a/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/WxV3PayController.java b/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/WxV3PayController.java index a3bc5da..f3d5088 100644 --- a/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/WxV3PayController.java +++ b/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/WxV3PayController.java @@ -5,11 +5,11 @@ package com.egzosn.pay.demo.controller; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.math.BigDecimal; +import java.util.Collections; import java.util.HashMap; import java.util.Map; import java.util.UUID; -import javax.annotation.PostConstruct; import javax.imageio.ImageIO; import javax.servlet.http.HttpServletRequest; @@ -27,8 +27,12 @@ import com.egzosn.pay.web.support.HttpRequestNoticeParams; import com.egzosn.pay.wx.v3.api.WxPayConfigStorage; import com.egzosn.pay.wx.v3.api.WxPayService; import com.egzosn.pay.wx.v3.bean.WxTransactionType; +import com.egzosn.pay.wx.v3.bean.WxTransferType; import com.egzosn.pay.wx.v3.bean.order.H5Info; import com.egzosn.pay.wx.v3.bean.order.SceneInfo; +import com.egzosn.pay.wx.v3.bean.transfer.TransferDetail; +import com.egzosn.pay.wx.v3.bean.transfer.WxTransferOrder; +import com.egzosn.pay.wx.v3.bean.transfer.WxTransferQueryOrder; import com.egzosn.pay.wx.v3.utils.WxConst; /** @@ -233,5 +237,137 @@ public class WxV3PayController { return service.downloadBill(order.getBillDate(), order.getBillType()); } + /** + * 转账到余额 + * + * + * @return 对应的转账结果 + */ + @RequestMapping("transfer") + public Map transfer() { + + WxTransferOrder order = new WxTransferOrder(); + order.setOutBatchNo("商户系统内部的商家批次单号,要求此参数只能由数字、大小写字母组成,在商户系统内部唯一"); + order.setBatchName("该笔批量转账的名称"); + order.setBatchRemark("转账说明,UTF8编码,最多允许32个字符"); + // 转账金额单位为“分”。转账总金额必须与批次内所有明细转账金额之和保持一致,否则无法发起转账操作 + order.setTotalAmount(100); + //一个转账批次单最多发起一千笔转账。转账总笔数必须与批次内所有明细之和保持一致,否则无法发起转账操作 + order.setTotalNum(1); + TransferDetail transferDetail = new TransferDetail(); + transferDetail.setOutDetailNo("商户系统内部区分转账批次单下不同转账明细单的唯一标识,要求此参数只能由数字、大小写字母组成"); + // 转账金额单位为“分”。转账总金额必须与批次内所有明细转账金额之和保持一致,否则无法发起转账操作 + transferDetail.setTransferAmount(100); + transferDetail.setTransferRemark("单条转账备注(微信用户会收到该备注),UTF8编码,最多允许32个字符"); + transferDetail.setOpenid("商户appid下,某用户的openid"); + transferDetail.setUserName("收款方真实姓名: 张三"); + transferDetail.setUserIdCard("当填入收款方身份证号时,姓名字段必须填入。"); + order.setTransferDetailList(Collections.singletonList(transferDetail)); + //发起商家转账,转账到零钱 + order.setTransferType(WxTransferType.TRANSFER_BATCHES); + order.setTransferSceneId("必填,指定该笔转账使用的转账场景ID"); + return service.transfer(order); + } + /** + * 转账账单电子回单申请受理接口 + * + * + * @return 转账账单电子回单申请受理接口结果 + */ + @RequestMapping("billReceipt") + public Map billReceipt() { + WxTransferOrder order = new WxTransferOrder(); + order.setOutBatchNo("商户系统内部的商家批次单号,要求此参数只能由数字、大小写字母组成,在商户系统内部唯一"); + //转账账单电子回单申请受理接口 + order.setTransferType(WxTransferType.TRANSFER_BILL_RECEIPT); + return service.transfer(order); + } + + + /** + * 通过微信批次单号查询批次单 + * + *

+ * 通过微信批次单号查询批次单 + *

+ * @return 对应的转账订单 + */ + @RequestMapping("getTransferBatchByNo") + public Map getTransferBatchByNo() { + WxTransferQueryOrder queryOrder = new WxTransferQueryOrder(); + queryOrder.setBatchId("1030000071100999991182020050700019480001"); + queryOrder.setNeedQueryDetail(true); + queryOrder.setOffset(0); + queryOrder.setLimit(20); + queryOrder.setDetailStatus("FAIL"); + queryOrder.setTransactionType(WxTransferType.QUERY_BATCH_BY_BATCH_ID); + return service.transferQuery(queryOrder); + } + /** + * 通过微信批次单号查询批次单 + * + *

+ * 通过商家批次单号查询批次单 + *

+ * @return 对应的转账订单 + */ + @RequestMapping("getTransferBatchByOutNo") + public Map getTransferBatchByOutNo() { + WxTransferQueryOrder queryOrder = new WxTransferQueryOrder(); + queryOrder.setOutBatchNo("1030000071100999991182020050700019480001"); + queryOrder.setNeedQueryDetail(true); + queryOrder.setOffset(0); + queryOrder.setLimit(20); + queryOrder.setDetailStatus("FAIL"); + queryOrder.setTransactionType(WxTransferType.QUERY_BATCH_BY_OUT_BATCH_NO); + return service.transferQuery(queryOrder); + } + /** + * 通过微信明细单号查询明细单 + * + *

+ * 通过微信明细单号查询明细单 + *

+ * @return 对应的转账订单 + */ + @RequestMapping("getTransferDetailByNo") + public Map getTransferDetailByNo() { + WxTransferQueryOrder queryOrder = new WxTransferQueryOrder(); + queryOrder.setBatchId("1030000071100999991182020050700019480001"); + queryOrder.setDetailId("1040000071100999991182020050700019500100"); + queryOrder.setTransactionType(WxTransferType.QUERY_BATCH_DETAIL_BY_BATCH_ID); + return service.transferQuery(queryOrder); + } + /** + * 通过商家明细单号查询明细单 + * + *

+ * 通过商家明细单号查询明细单 + *

+ * @return 对应的转账订单 + */ + @RequestMapping("getTransferDetailByOutNo") + public Map getTransferDetailByOutNo() { + WxTransferQueryOrder queryOrder = new WxTransferQueryOrder(); + queryOrder.setOutDetailNo("x23zy545Bd5436"); + queryOrder.setOutBatchNo("plfk2020042013"); + queryOrder.setTransactionType(WxTransferType.QUERY_BATCH_DETAIL_BY_OUT_BATCH_NO); + return service.transferQuery(queryOrder); + } + /** + * 查询转账账单电子回单接口 + * + *

+ * 查询转账账单电子回单接口 + *

+ * @return 对应的转账订单 + */ + @RequestMapping("getElectronicSignatureByOutNo") + public Map getElectronicSignatureByOutNo() { + WxTransferQueryOrder queryOrder = new WxTransferQueryOrder(); + queryOrder.setOutBatchNo("plfk2020042013"); + queryOrder.setTransactionType(WxTransferType.QUERY_TRANSFER_BILL_RECEIPT); + return service.transferQuery(queryOrder); + } } -- Gitee From 73bcd4f8331ed0663a50ae98b655286fd7fca54b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=83=91=E7=81=B6=E7=94=9F?= Date: Sun, 8 Jan 2023 23:21:47 +0800 Subject: [PATCH 280/299] =?UTF-8?q?=E7=BB=9F=E4=B8=80=E6=94=B6=E5=8D=95?= =?UTF-8?q?=E4=BA=A4=E6=98=93=E6=9F=A5=E8=AF=A2=E8=A1=A5=E5=85=85=E3=80=90?= =?UTF-8?q?=E6=9F=A5=E8=AF=A2=E9=80=89=E9=A1=B9=E3=80=91=E5=8F=82=E6=95=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/egzosn/pay/ali/api/AliPayService.java | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) 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 f43b76f..14bc62d 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 @@ -216,7 +216,7 @@ public class AliPayService extends BasePayService implement private void setNotifyUrl(Map orderInfo, AssistOrder order) { // orderInfo.put(NOTIFY_URL, payConfigStorage.getNotifyUrl()); OrderParaStructure.loadParameters(orderInfo, NOTIFY_URL, payConfigStorage.getNotifyUrl()); - OrderParaStructure.loadParameters(orderInfo, NOTIFY_URL, order.getNotifyUrl()); + OrderParaStructure.loadParameters(orderInfo, NOTIFY_URL, order.getNotifyUrl()); OrderParaStructure.loadParameters(orderInfo, NOTIFY_URL, order); } @@ -497,7 +497,16 @@ public class AliPayService extends BasePayService implement */ @Override public Map query(AssistOrder assistOrder) { - return secondaryInterface(assistOrder.getTradeNo(), assistOrder.getOutTradeNo(), AliTransactionType.QUERY); + //获取公共参数 + Map parameters = getPublicParameters(assistOrder.getTransactionType()); + Map bizContent = new TreeMap<>(); + OrderParaStructure.loadParameters(bizContent, "query_options", assistOrder); + + //设置请求参数的集合 + parameters.put(BIZ_CONTENT, JSON.toJSONString(getBizContent(assistOrder.getOutTradeNo(), assistOrder.getTradeNo(), bizContent))); + //设置签名 + setSign(parameters); + return requestTemplate.getForObject(getReqUrl(assistOrder.getTransactionType()) + "?" + UriVariables.getMapToParameters(parameters), JSONObject.class); } -- Gitee From c8a99c00de652ff4e3578b423a87f070382579b7 Mon Sep 17 00:00:00 2001 From: egan Date: Sun, 5 Feb 2023 16:15:48 +0800 Subject: [PATCH 281/299] =?UTF-8?q?2.14.5=E5=8F=91=E5=B8=83?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/egzosn/pay/common/api/TransferService.java | 6 ++++-- .../src/main/java/com/egzosn/pay/common/bean/Attrs.java | 1 + .../src/main/java/com/egzosn/pay/wx/api/WxPayService.java | 3 +-- .../java/com/egzosn/pay/wx/v3/api/WxParameterStructure.java | 3 ++- .../com/egzosn/pay/wx/v3/bean/transfer/TransferDetail.java | 6 ++++-- .../com/egzosn/pay/wx/v3/bean/transfer/WxTransferOrder.java | 6 ++++-- .../pay/wx/v3/bean/transfer/WxTransferQueryOrder.java | 6 ++++-- 7 files changed, 20 insertions(+), 11 deletions(-) diff --git a/pay-java-common/src/main/java/com/egzosn/pay/common/api/TransferService.java b/pay-java-common/src/main/java/com/egzosn/pay/common/api/TransferService.java index 695fe66..69d51e2 100644 --- a/pay-java-common/src/main/java/com/egzosn/pay/common/api/TransferService.java +++ b/pay-java-common/src/main/java/com/egzosn/pay/common/api/TransferService.java @@ -9,8 +9,10 @@ import com.egzosn.pay.common.bean.TransferOrder; * 转账服务 * * @author Egan - * @email egan@egzosn.com - * @date 2023/1/8 + *
+ *  email egan@egzosn.com
+ *  date 2023/1/8
+ *  
*/ public interface TransferService { diff --git a/pay-java-common/src/main/java/com/egzosn/pay/common/bean/Attrs.java b/pay-java-common/src/main/java/com/egzosn/pay/common/bean/Attrs.java index 2713b0d..48dcf88 100644 --- a/pay-java-common/src/main/java/com/egzosn/pay/common/bean/Attrs.java +++ b/pay-java-common/src/main/java/com/egzosn/pay/common/bean/Attrs.java @@ -73,6 +73,7 @@ public interface Attrs extends Serializable { * 获取属性 这里可用做覆盖已设置的属性信息属性。 * * @param key 属性名 + * @param defaultValue 默认值 * @return 属性 */ default Integer getAttrForInt(String key, Integer defaultValue) { 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 f5eaf7b..09c7922 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 @@ -835,8 +835,7 @@ public class WxPayService extends BasePayService implements * 转账 * * @param order 转账订单 - *
-     *              @return 对应的转账结果
+     * @return 对应的转账结果
      */
     @Override
     public Map transfer(TransferOrder order) {
diff --git a/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/api/WxParameterStructure.java b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/api/WxParameterStructure.java
index 24c2553..21f2cbe 100644
--- a/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/api/WxParameterStructure.java
+++ b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/api/WxParameterStructure.java
@@ -28,7 +28,7 @@ public class WxParameterStructure {
 
     /**
      * 获取公共参数
-     *
+     * @param parameters 参数
      * @return 公共参数
      */
     public Map getPublicParameters(Map parameters) {
@@ -93,6 +93,7 @@ public class WxParameterStructure {
      * 初始化商户相关信息
      *
      * @param parameters 参数信息
+     * @return 参数信息
      */
     public Map initPartner(Map parameters) {
         if (null == parameters) {
diff --git a/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/transfer/TransferDetail.java b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/transfer/TransferDetail.java
index cfe858b..0dd09f9 100644
--- a/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/transfer/TransferDetail.java
+++ b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/transfer/TransferDetail.java
@@ -6,8 +6,10 @@ import com.alibaba.fastjson.annotation.JSONField;
  *
  * 转账明细
  * @author Egan
- * @email egan@egzosn.com
- * @date 2023/1/8
+ * 
+ * email egan@egzosn.com
+ * date 2023/1/8
+ * 
*/ public class TransferDetail { /** diff --git a/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/transfer/WxTransferOrder.java b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/transfer/WxTransferOrder.java index 0407a0d..a3008dc 100644 --- a/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/transfer/WxTransferOrder.java +++ b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/transfer/WxTransferOrder.java @@ -10,8 +10,10 @@ import com.egzosn.pay.wx.v3.utils.WxConst; * 发起商家转账 * * @author Egan - * @email egan@egzosn.com - * @date 2023/1/8 + *
+ * email egan@egzosn.com
+ * date 2023/1/8
+ * 
*/ public class WxTransferOrder extends TransferOrder { diff --git a/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/transfer/WxTransferQueryOrder.java b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/transfer/WxTransferQueryOrder.java index 36585a9..34cc2df 100644 --- a/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/transfer/WxTransferQueryOrder.java +++ b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/transfer/WxTransferQueryOrder.java @@ -7,8 +7,10 @@ import com.egzosn.pay.wx.v3.utils.WxConst; * 微信转账查询订单 * * @author Egan - * @email egan@egzosn.com - * @date 2023/1/8 + *
+ * email egan@egzosn.com
+ * date 2023/1/8
+ * 
*/ public class WxTransferQueryOrder extends AssistOrder { -- Gitee From c2e9b16809dba950e8fb6702b9121a49b7a1f63b Mon Sep 17 00:00:00 2001 From: egan Date: Sun, 5 Feb 2023 16:16:54 +0800 Subject: [PATCH 282/299] =?UTF-8?q?2.14.5=E5=8F=91=E5=B8=83?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 1ac066e..e80219f 100644 --- a/README.md +++ b/README.md @@ -22,7 +22,7 @@ com.egzosn {module-name} - 2.14.4-fix + 2.14.5 ``` -- Gitee From 7656c3322ba41d9b4843fdcbdc75b9cdb56e2564 Mon Sep 17 00:00:00 2001 From: lwejn <12523192+lwejn@user.noreply.gitee.com> Date: Mon, 27 Mar 2023 08:16:07 +0000 Subject: [PATCH 283/299] =?UTF-8?q?=E5=8D=87=E7=BA=A7fastjson=E7=89=88?= =?UTF-8?q?=E6=9C=AC=E5=88=B01.2.83,1.2.83=E7=89=88=E6=9C=AC=E4=B9=8B?= =?UTF-8?q?=E5=89=8D=E5=AD=98=E5=9C=A8=E4=BB=A3=E7=A0=81=E6=89=A7=E8=A1=8C?= =?UTF-8?q?=E6=BC=8F=E6=B4=9E=E9=A3=8E=E9=99=A9=EF=BC=8CCVE-2022-25845=20?= =?UTF-8?q?=E5=8D=87=E7=BA=A7fastjson=E7=89=88=E6=9C=AC=E5=88=B01.2.83,1.2?= =?UTF-8?q?.83=E7=89=88=E6=9C=AC=E4=B9=8B=E5=89=8D=E5=AD=98=E5=9C=A8?= =?UTF-8?q?=E4=BB=A3=E7=A0=81=E6=89=A7=E8=A1=8C=E6=BC=8F=E6=B4=9E=E9=A3=8E?= =?UTF-8?q?=E9=99=A9=EF=BC=8CCVE-2022-25845?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: lwejn <12523192+lwejn@user.noreply.gitee.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 00881fe..ab7ffe1 100644 --- a/pom.xml +++ b/pom.xml @@ -68,7 +68,7 @@ 2.14.5 4.5.4 1.2.17 - 1.2.73 + 1.2.83 3.3.1 4.0.1 1.59 -- Gitee From c102c2a1f3c8e2d96f602bd082e35126fb099de2 Mon Sep 17 00:00:00 2001 From: pengkaibing <1787009158@qq.com> Date: Fri, 2 Jun 2023 23:46:11 +0800 Subject: [PATCH 284/299] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=20=E5=BE=AE=E4=BF=A1?= =?UTF-8?q?V3=EF=BC=9A=E5=95=86=E6=88=B7=E4=B8=8A=E9=80=81=E6=95=8F?= =?UTF-8?q?=E6=84=9F=E4=BF=A1=E6=81=AF=E6=97=B6=E5=8A=A0=E5=AF=86=E7=A7=98?= =?UTF-8?q?=E9=92=A5=E9=94=99=E8=AF=AF=E9=97=AE=E9=A2=98=20=E4=BF=AE?= =?UTF-8?q?=E5=A4=8D=20=E6=94=AF=E4=BB=98=E5=AE=9D=EF=BC=9A=E4=BA=A4?= =?UTF-8?q?=E6=98=93=E6=9F=A5=E8=AF=A2=E6=8E=A5=E5=8F=A3=E8=B0=83=E7=94=A8?= =?UTF-8?q?getBizContent=E6=96=B9=E6=B3=95tradeNo=E4=B8=8EoutTradeNo?= =?UTF-8?q?=E5=BC=84=E5=8F=8D=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/egzosn/pay/ali/api/AliPayService.java | 2 +- .../pay/wx/v3/api/DefaultWxPayAssistService.java | 12 ++++++++++++ .../com/egzosn/pay/wx/v3/api/WxPayService.java | 3 ++- .../egzosn/pay/wx/v3/bean/CertEnvironment.java | 15 +++++++++++++-- 4 files changed, 28 insertions(+), 4 deletions(-) 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 14bc62d..1e2e1f1 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 @@ -503,7 +503,7 @@ public class AliPayService extends BasePayService implement OrderParaStructure.loadParameters(bizContent, "query_options", assistOrder); //设置请求参数的集合 - parameters.put(BIZ_CONTENT, JSON.toJSONString(getBizContent(assistOrder.getOutTradeNo(), assistOrder.getTradeNo(), bizContent))); + parameters.put(BIZ_CONTENT, JSON.toJSONString(getBizContent(assistOrder.getTradeNo(), assistOrder.getOutTradeNo(), bizContent))); //设置签名 setSign(parameters); return requestTemplate.getForObject(getReqUrl(assistOrder.getTransactionType()) + "?" + UriVariables.getMapToParameters(parameters), JSONObject.class); diff --git a/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/api/DefaultWxPayAssistService.java b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/api/DefaultWxPayAssistService.java index 6e7703e..3bf1e68 100644 --- a/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/api/DefaultWxPayAssistService.java +++ b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/api/DefaultWxPayAssistService.java @@ -5,6 +5,7 @@ import java.nio.charset.StandardCharsets; import java.security.cert.Certificate; import java.util.Map; +import org.apache.http.Header; import org.apache.http.HttpEntity; import org.apache.http.entity.ContentType; import org.apache.http.message.BasicHeader; @@ -105,6 +106,17 @@ public class DefaultWxPayAssistService implements WxPayAssistService { if (statusCode >= 400) { throw new PayErrorException(new WxPayError(responseBody.getString(WxConst.CODE), responseBody.getString(WxConst.MESSAGE), responseBody.toJSONString())); } + Header[] headers = responseEntity.getHeaders(); + if (headers == null) { + return responseBody; + } + for (Header header : headers) { + if ("Wechatpay-Serial".equals(header.getName())) { + // 更新平台证书的序列号,需要每次都更新,因为这个可能会改变 + payConfigStorage.getCertEnvironment().setPlatformSerialNumber(header.getValue()); + break; + } + } return responseBody; } diff --git a/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/api/WxPayService.java b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/api/WxPayService.java index 36c2a89..3a25ab5 100644 --- a/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/api/WxPayService.java +++ b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/api/WxPayService.java @@ -657,7 +657,8 @@ public class WxPayService extends BasePayService implements return null; } - String serialNumber = payConfigStorage.getCertEnvironment().getSerialNumber(); + // 商户上送敏感信息时使用`微信支付平台公钥`加密 + String serialNumber = payConfigStorage.getCertEnvironment().getPlatformSerialNumber(); Certificate certificate = getAssistService().getCertificate(serialNumber); return transferDetails.stream() .peek(transferDetailListItem -> { diff --git a/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/CertEnvironment.java b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/CertEnvironment.java index eed1c5b..c1c4360 100644 --- a/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/CertEnvironment.java +++ b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/CertEnvironment.java @@ -26,7 +26,10 @@ public class CertEnvironment { */ private String serialNumber; - + /** + * 微信平台证书序列号 + */ + private String platformSerialNumber; @@ -63,4 +66,12 @@ public class CertEnvironment { public void setSerialNumber(String serialNumber) { this.serialNumber = serialNumber; } -} \ No newline at end of file + + public String getPlatformSerialNumber() { + return platformSerialNumber; + } + + public void setPlatformSerialNumber(String platformSerialNumber) { + this.platformSerialNumber = platformSerialNumber; + } +} -- Gitee From 84cf034338655f3ed5b55bcad58aada4ed444dec Mon Sep 17 00:00:00 2001 From: egan Date: Tue, 12 Sep 2023 22:33:37 +0800 Subject: [PATCH 285/299] =?UTF-8?q?=E6=96=B0=E5=A2=9E=E8=AF=B7=E6=B1=82?= =?UTF-8?q?=E4=BD=93=E5=8E=9F=E5=A7=8B=E5=AD=97=E7=AC=A6=E4=B8=B2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/egzosn/pay/common/api/PayService.java | 16 +++---- .../egzosn/pay/common/bean/NoticeParams.java | 25 ++++++++++- .../pay/common/http/HttpRequestTemplate.java | 45 +++++++++++++------ .../pay/common/util/sign/SignUtils.java | 3 ++ 4 files changed, 67 insertions(+), 22 deletions(-) diff --git a/pay-java-common/src/main/java/com/egzosn/pay/common/api/PayService.java b/pay-java-common/src/main/java/com/egzosn/pay/common/api/PayService.java index cc1f708..28f4106 100644 --- a/pay-java-common/src/main/java/com/egzosn/pay/common/api/PayService.java +++ b/pay-java-common/src/main/java/com/egzosn/pay/common/api/PayService.java @@ -7,7 +7,6 @@ import java.util.Map; import com.egzosn.pay.common.bean.AssistOrder; import com.egzosn.pay.common.bean.BillType; - import com.egzosn.pay.common.bean.MethodType; import com.egzosn.pay.common.bean.NoticeParams; import com.egzosn.pay.common.bean.NoticeRequest; @@ -223,14 +222,12 @@ public interface PayService { /** * 交易查询接口 * - * @param assistOrder 查询条件 + * @param assistOrder 查询条件 * @return 返回查询回来的结果集,支付方原值返回 */ Map query(AssistOrder assistOrder); - - /** * 交易关闭接口 * @@ -241,10 +238,11 @@ public interface PayService { */ @Deprecated Map close(String tradeNo, String outTradeNo); + /** * 交易关闭接口 * - * @param assistOrder 关闭订单 + * @param assistOrder 关闭订单 * @return 返回支付方交易关闭后的结果 */ Map close(AssistOrder assistOrder); @@ -343,7 +341,6 @@ public interface PayService { Map downloadBill(Date billDate, BillType billType); - /** * 转账 * @@ -370,7 +367,9 @@ public interface PayService { * @param outNo 商户转账订单号 * @param tradeNo 支付平台转账订单号 * @return 对应的转账订单 + * @deprecated 替代{@link TransferService#transferQuery(com.egzosn.pay.common.bean.AssistOrder)} */ + @Deprecated Map transferQuery(String outNo, String tradeNo); /** @@ -395,15 +394,16 @@ public interface PayService { */ @Deprecated PayOutMessage payBack(Map parameterMap, InputStream is); + /** - * 回调处理 + * 回调处理 + * * @param request 请求参数 * @return 获得回调响应信息 */ PayOutMessage payBack(NoticeRequest request); - /** * 设置支付消息处理器,这里用于处理具体的支付业务 * diff --git a/pay-java-common/src/main/java/com/egzosn/pay/common/bean/NoticeParams.java b/pay-java-common/src/main/java/com/egzosn/pay/common/bean/NoticeParams.java index 6b19357..752e7fb 100644 --- a/pay-java-common/src/main/java/com/egzosn/pay/common/bean/NoticeParams.java +++ b/pay-java-common/src/main/java/com/egzosn/pay/common/bean/NoticeParams.java @@ -29,8 +29,12 @@ import java.util.Map; * email egzosn@gmail.com * date 2021/8/8 */ -public class NoticeParams { +public class NoticeParams implements Attrs { + /** + * body原始字符串 + */ + private String bodyStr; /** * 为了获取request里面传过来的动态参数 @@ -59,6 +63,14 @@ public class NoticeParams { this.headers = headers; } + public String getBodyStr() { + return bodyStr; + } + + public void setBodyStr(String bodyStr) { + this.bodyStr = bodyStr; + } + private T getValueMatchingKey(Map values, String key) { T value = values.get(key); if (null != value) { @@ -115,4 +127,15 @@ public class NoticeParams { public void setAttr(Map attr) { this.attr = attr; } + + + /** + * 获取属性 这里可用做覆盖已设置的信息属性,订单信息在签名前进行覆盖。 + * + * @return 属性 + */ + @Override + public Map getAttrs() { + return attr; + } } diff --git a/pay-java-common/src/main/java/com/egzosn/pay/common/http/HttpRequestTemplate.java b/pay-java-common/src/main/java/com/egzosn/pay/common/http/HttpRequestTemplate.java index d53f372..0f9564c 100644 --- a/pay-java-common/src/main/java/com/egzosn/pay/common/http/HttpRequestTemplate.java +++ b/pay-java-common/src/main/java/com/egzosn/pay/common/http/HttpRequestTemplate.java @@ -153,7 +153,7 @@ public class HttpRequestTemplate { //指定TLS版本 sslsf = new SSLConnectionSocketFactory( - sslcontext, new String[]{"TLSv1","TLSv1.2"}, null, + sslcontext, new String[]{"TLSv1", "TLSv1.2"}, null, new DefaultHostnameVerifier()); return sslsf; @@ -268,7 +268,7 @@ public class HttpRequestTemplate { * @param uriVariables 用于匹配表达式 * @param 响应类型 * @return 类型对象 - * + * * * getForObject("http://egan.in/pay/{id}/f/{type}", String.class, "1", "APP") * @@ -288,11 +288,11 @@ public class HttpRequestTemplate { * @return 类型对象 * * Map<String, String> uriVariables = new HashMap<String, String>();
- * + *

* uriVariables.put("id", "1");
- * + *

* uriVariables.put("type", "APP");
- * + *

* getForObject("http://egan.in/pay/{id}/f/{type}", String.class, uriVariables)
*
*/ @@ -334,11 +334,29 @@ public class HttpRequestTemplate { * getForObject("http://egan.in/pay/{id}/f/{type}", String.class, "1", "APP") * */ - public ResponseEntity getForObjectEntity(String uri, HttpHeader header, Class responseType, Object... uriVariables) { + public ResponseEntity getForObjectEntity(String uri, HttpHeader header, Class responseType, Object... uriVariables) { return doExecuteEntity(URI.create(UriVariables.getUri(uri, uriVariables)), header, responseType, MethodType.GET); } + /** + * get 请求 + * + * @param uri 请求地址 + * @param responseType 响应类型 + * @param uriVariables 用于匹配表达式 + * @param 响应类型 + * @return 类型对象 + * + * + * getForObject("http://egan.in/pay/{id}/f/{type}", String.class, "1", "APP") + * + */ + public ResponseEntity getForObjectEntity(String uri, Class responseType, Object... uriVariables) { + + return doExecuteEntity(URI.create(UriVariables.getUri(uri, uriVariables)), null, responseType, MethodType.GET); + } + /** * get 请求 * @@ -350,17 +368,18 @@ public class HttpRequestTemplate { * @return 类型对象 * * Map<String, String> uriVariables = new HashMap<String, String>();
- * + *

* uriVariables.put("id", "1");
- * + *

* uriVariables.put("type", "APP");
- * + *

* getForObject("http://egan.in/pay/{id}/f/{type}", String.class, uriVariables)
*
*/ public T getForObject(String uri, HttpHeader header, Class responseType, Map uriVariables) { return getForObjectEntity(uri, header, responseType, uriVariables).getBody(); } + /** * get 请求 * @@ -372,15 +391,15 @@ public class HttpRequestTemplate { * @return 类型对象 * * Map<String, String> uriVariables = new HashMap<String, String>();
- * + *

* uriVariables.put("id", "1");
- * + *

* uriVariables.put("type", "APP");
- * + *

* getForObject("http://egan.in/pay/{id}/f/{type}", String.class, uriVariables)
*
*/ - public ResponseEntity getForObjectEntity(String uri, HttpHeader header, Class responseType, Map uriVariables) { + public ResponseEntity getForObjectEntity(String uri, HttpHeader header, Class responseType, Map uriVariables) { return doExecuteEntity(URI.create(UriVariables.getUri(uri, uriVariables)), header, responseType, MethodType.GET); } 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 69b7090..7d6aaa3 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 @@ -156,6 +156,7 @@ public enum SignUtils implements SignType { * @param characterEncoding 编码格式 * @return 签名值 */ + @Override public String sign(Map parameters, String key, String characterEncoding) { return createSign(parameterText(parameters, "&"), key, characterEncoding); @@ -170,6 +171,7 @@ public enum SignUtils implements SignType { * @param characterEncoding 编码格式 * @return 签名值 */ + @Override public String sign(Map parameters, String key, String separator, String characterEncoding) { return createSign(parameterText(parameters, separator), key, characterEncoding); @@ -186,6 +188,7 @@ public enum SignUtils implements SignType { * @param characterEncoding 编码格式 * @return 签名结果 */ + @Override public boolean verify(Map params, String sign, String key, String characterEncoding) { //判断是否一样 return this.verify(parameterText(params), sign, key, characterEncoding); -- Gitee From c51499a6463959f82c84bdbfa0b7fd18b1de5acc Mon Sep 17 00:00:00 2001 From: egan Date: Tue, 12 Sep 2023 22:33:49 +0800 Subject: [PATCH 286/299] =?UTF-8?q?=E6=94=AF=E4=BB=98=E5=AE=9D=E6=96=B0?= =?UTF-8?q?=E5=A2=9E=E8=87=AA=E5=AE=9A=E4=B9=89URL?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/egzosn/pay/ali/api/AliPayService.java | 20 +++++++++++++++++++ .../egzosn/pay/ali/api/AliPayServiceInf.java | 9 +++++++++ .../com/egzosn/pay/ali/bean/AliPayConst.java | 2 +- 3 files changed, 30 insertions(+), 1 deletion(-) 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 1e2e1f1..8217515 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 @@ -65,6 +65,11 @@ import com.egzosn.pay.common.util.str.StringUtils; public class AliPayService extends BasePayService implements AliPayServiceInf { + /** + * api服务地址,默认为国内 + */ + private String apiServerUrl; + /** * 获取对应的请求地址 * @@ -72,6 +77,9 @@ public class AliPayService extends BasePayService implement */ @Override public String getReqUrl(TransactionType transactionType) { + if (StringUtils.isNotEmpty(apiServerUrl)) { + return apiServerUrl; + } return payConfigStorage.isTest() ? AliPayConst.DEV_REQ_URL : HTTPS_REQ_URL; } @@ -850,5 +858,17 @@ public class AliPayService extends BasePayService implement return null; } + /** + * 设置api服务器地址 + * + * @param apiServerUrl api服务器地址 + * @return 自身 + */ + @Override + public AliPayServiceInf setApiServerUrl(String apiServerUrl) { + this.apiServerUrl = apiServerUrl; + return this; + } + } diff --git a/pay-java-ali/src/main/java/com/egzosn/pay/ali/api/AliPayServiceInf.java b/pay-java-ali/src/main/java/com/egzosn/pay/ali/api/AliPayServiceInf.java index c425c45..699f2f8 100644 --- a/pay-java-ali/src/main/java/com/egzosn/pay/ali/api/AliPayServiceInf.java +++ b/pay-java-ali/src/main/java/com/egzosn/pay/ali/api/AliPayServiceInf.java @@ -19,4 +19,13 @@ public interface AliPayServiceInf { * @return fail 消息获取失败 是 success 消息获取成功 否 */ String refundDepositBackCompleted(RefundOrder refundOrder); + + + /** + * 设置api服务器地址 + * + * @param apiServerUrl api服务器地址 + * @return 自身 + */ + AliPayServiceInf setApiServerUrl(String apiServerUrl); } diff --git a/pay-java-ali/src/main/java/com/egzosn/pay/ali/bean/AliPayConst.java b/pay-java-ali/src/main/java/com/egzosn/pay/ali/bean/AliPayConst.java index dd09e40..7bce3cd 100644 --- a/pay-java-ali/src/main/java/com/egzosn/pay/ali/bean/AliPayConst.java +++ b/pay-java-ali/src/main/java/com/egzosn/pay/ali/bean/AliPayConst.java @@ -20,7 +20,7 @@ public final class AliPayConst { /** * 沙箱测试环境账号 */ - public static final String DEV_REQ_URL = "https://openapi.alipaydev.com/gateway.do"; + public static final String DEV_REQ_URL = "https://openapi-sandbox.dl.alipaydev.com/gateway.do"; public static final String SIGN = "sign"; -- Gitee From 89beb1d5b4a8b8bb6aabe2c32f5e82a9498ba9c4 Mon Sep 17 00:00:00 2001 From: egan Date: Tue, 12 Sep 2023 22:34:04 +0800 Subject: [PATCH 287/299] =?UTF-8?q?=E5=BE=AE=E4=BF=A1=E4=BF=AE=E5=A4=8DV3?= =?UTF-8?q?=E8=BD=AC=E8=B4=A6=E6=9F=A5=E8=AF=A2=E6=8E=A5=E5=8F=A3=EF=BC=8C?= =?UTF-8?q?=E8=A1=A5=E5=85=85=E5=88=86=E8=B4=A6=E5=8A=9F=E8=83=BD=E8=AF=81?= =?UTF-8?q?=E4=B9=A6=E5=BA=8F=E5=88=97=E5=8F=82=E6=95=B0=EF=BC=8CV2?= =?UTF-8?q?=E6=B2=99=E7=AE=B1=E5=9C=B0=E5=9D=80=E5=8F=98=E6=9B=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/egzosn/pay/wx/api/WxConst.java | 2 +- .../wx/v3/api/DefaultWxPayAssistService.java | 13 ++-- .../egzosn/pay/wx/v3/api/WxPayService.java | 27 ++++---- .../egzosn/pay/wx/v3/api/WxPayServiceInf.java | 67 +++++++++++++++++++ .../pay/wx/v3/api/WxProfitSharingService.java | 35 +++++----- .../pay/wx/v3/bean/CertEnvironment.java | 7 +- .../com/egzosn/pay/wx/v3/utils/WxConst.java | 1 + 7 files changed, 111 insertions(+), 41 deletions(-) create mode 100644 pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/api/WxPayServiceInf.java diff --git a/pay-java-wx/src/main/java/com/egzosn/pay/wx/api/WxConst.java b/pay-java-wx/src/main/java/com/egzosn/pay/wx/api/WxConst.java index fe3d4e4..07a290b 100644 --- a/pay-java-wx/src/main/java/com/egzosn/pay/wx/api/WxConst.java +++ b/pay-java-wx/src/main/java/com/egzosn/pay/wx/api/WxConst.java @@ -16,7 +16,7 @@ public interface WxConst { /** * 沙箱 */ - String SANDBOXNEW = "sandboxnew/"; + String SANDBOXNEW = "xdc/apiv2sandbox/"; String SUCCESS = "SUCCESS"; String FAIL = "FAIL"; diff --git a/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/api/DefaultWxPayAssistService.java b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/api/DefaultWxPayAssistService.java index 3bf1e68..54897ff 100644 --- a/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/api/DefaultWxPayAssistService.java +++ b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/api/DefaultWxPayAssistService.java @@ -43,10 +43,10 @@ public class DefaultWxPayAssistService implements WxPayAssistService { private HttpRequestTemplate requestTemplate; - private WxPayService wxPayService; + private WxPayServiceInf wxPayService; - public DefaultWxPayAssistService(WxPayService wxPayService) { + public DefaultWxPayAssistService(WxPayServiceInf wxPayService) { this.wxPayService = wxPayService; payConfigStorage = wxPayService.getPayConfigStorage(); requestTemplate = wxPayService.getHttpRequestTemplate(); @@ -111,7 +111,7 @@ public class DefaultWxPayAssistService implements WxPayAssistService { return responseBody; } for (Header header : headers) { - if ("Wechatpay-Serial".equals(header.getName())) { + if (WxConst.WECHATPAY_SERIAL.equals(header.getName())) { // 更新平台证书的序列号,需要每次都更新,因为这个可能会改变 payConfigStorage.getCertEnvironment().setPlatformSerialNumber(header.getValue()); break; @@ -138,7 +138,7 @@ public class DefaultWxPayAssistService implements WxPayAssistService { * 构建请求实体 * 这里也做签名处理 * - * @param url url + * @param url url * @param body 请求内容体 * @param method 请求方法 * @return 请求实体 @@ -158,7 +158,9 @@ public class DefaultWxPayAssistService implements WxPayAssistService { entity.addHeader(new BasicHeader("Authorization", WxConst.SCHEMA.concat(token))); entity.addHeader(new BasicHeader("User-Agent", "Pay-Java-Parent")); entity.addHeader(new BasicHeader("Accept", APPLICATION_JSON.getMimeType())); - return entity; + + + return wxPayService.hookHttpEntity(entity); } @@ -210,5 +212,4 @@ public class DefaultWxPayAssistService implements WxPayAssistService { } - } diff --git a/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/api/WxPayService.java b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/api/WxPayService.java index 3a25ab5..3170155 100644 --- a/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/api/WxPayService.java +++ b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/api/WxPayService.java @@ -75,7 +75,7 @@ import com.egzosn.pay.wx.v3.utils.WxConst; * date 2021/10/6 *

*/ -public class WxPayService extends BasePayService implements TransferService { +public class WxPayService extends BasePayService implements TransferService, WxPayServiceInf { /** @@ -123,12 +123,13 @@ public class WxPayService extends BasePayService implements * * @return 辅助api */ + @Override public WxPayAssistService getAssistService() { if (null == assistService) { assistService = new DefaultWxPayAssistService(this); - //在这预先进行初始化 - assistService.refreshCertificate(); } + //在这预先进行初始化 + assistService.refreshCertificate(); return assistService; } @@ -144,7 +145,7 @@ public class WxPayService extends BasePayService implements // new Thread(() -> { payConfigStorage.loadCertEnvironment(); wxParameterStructure = new WxParameterStructure(payConfigStorage); - getAssistService(); + setApiServerUrl(WxConst.URI); // }).start(); } @@ -155,11 +156,14 @@ public class WxPayService extends BasePayService implements * @param apiServerUrl api服务器地址 * @return 自身 */ + @Override public WxPayService setApiServerUrl(String apiServerUrl) { this.apiServerUrl = apiServerUrl; + getAssistService(); return this; } + @Override public String getApiServerUrl() { return apiServerUrl; } @@ -218,14 +222,9 @@ public class WxPayService extends BasePayService implements Certificate certificate = getAssistService().getCertificate(serial); - Map attr = noticeParams.getAttr(); - - if (Util.isEmpty(attr)) { - throw new PayErrorException(new WxPayError(FAILURE, "请勿置空NoticeParams.attr中的数据")); - } //这里为微信回调时的请求内容体,原值数据 - String body = (String) attr.get(WxConst.RESP_BODY); + String body = noticeParams.getBodyStr(); //签名信息 String signText = StringUtils.joining("\n", timestamp, nonce, body); @@ -362,6 +361,7 @@ public class WxPayService extends BasePayService implements String body = IOUtils.toString(is); noticeParams = JSON.parseObject(body, WxNoticeParams.class); noticeParams.setAttr(new MapGen(WxConst.RESP_BODY, body).getAttr()); + noticeParams.setBodyStr(body); Resource resource = noticeParams.getResource(); String associatedData = resource.getAssociatedData(); String nonce = resource.getNonce(); @@ -703,14 +703,15 @@ public class WxPayService extends BasePayService implements } } - if (transactionType == com.egzosn.pay.wx.v3.bean.WxTransferType.QUERY_BATCH_BY_BATCH_ID || transactionType == com.egzosn.pay.wx.v3.bean.WxTransferType.QUERY_BATCH_BY_OUT_BATCH_NO) { + if (transactionType == WxTransferType.QUERY_BATCH_BY_BATCH_ID || transactionType == WxTransferType.QUERY_BATCH_BY_OUT_BATCH_NO) { OrderParaStructure.loadParameters(parameters, WxConst.NEED_QUERY_DETAIL, assistOrder); OrderParaStructure.loadParameters(parameters, WxConst.OFFSET, assistOrder); OrderParaStructure.loadParameters(parameters, WxConst.LIMIT, assistOrder); OrderParaStructure.loadParameters(parameters, WxConst.DETAIL_STATUS, assistOrder); } - String requestBody = JSON.toJSONString(parameters); + + String requestBody = UriVariables.getMapToParameters(parameters); return getAssistService().doExecute(requestBody, assistOrder.getTransactionType(), uriVariables.toArray()); } @@ -728,7 +729,7 @@ public class WxPayService extends BasePayService implements */ @Override public Map transferQuery(String outNo, String wxTransferType) { - throw new PayErrorException(new WxPayError("", "V3不支持转账查询")); + throw new PayErrorException(new WxPayError("", "V3不支持此转账查询:替代方法transferQuery(AssistOrder assistOrder)")); } diff --git a/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/api/WxPayServiceInf.java b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/api/WxPayServiceInf.java new file mode 100644 index 0000000..01080b9 --- /dev/null +++ b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/api/WxPayServiceInf.java @@ -0,0 +1,67 @@ +package com.egzosn.pay.wx.v3.api; + +import com.egzosn.pay.common.api.PayService; +import com.egzosn.pay.common.bean.NoticeParams; +import com.egzosn.pay.common.bean.TransactionType; +import com.egzosn.pay.common.http.HttpStringEntity; + +/** + * @author Egan + * @email egan@egzosn.com + * @date 2023/9/11 + */ +public interface WxPayServiceInf extends PayService { + /** + * 辅助api + * + * @return 辅助api + */ + WxPayAssistService getAssistService(); + + /** + * 设置api服务器地址 + * + * @param apiServerUrl api服务器地址 + * @return 自身 + */ + WxPayService setApiServerUrl(String apiServerUrl); + + String getApiServerUrl(); + + /** + * 根据交易类型获取url + * + * @param transactionType 交易类型 + * @return 请求url + */ + @Override + String getReqUrl(TransactionType transactionType); + + /** + * 验签,使用微信平台证书. + * + * @param noticeParams 通知参数 + * @return the boolean + */ + @Override + boolean verify(NoticeParams noticeParams); + + /** + * 签名 + * + * @param content 需要签名的内容 不包含key + * @param characterEncoding 字符编码 + * @return 签名结果 + */ + @Override + String createSign(String content, String characterEncoding); + + /** + * http 实体 钩子 + * @param entity 实体 + * @return 返回处理后的实体 + */ + default HttpStringEntity hookHttpEntity(HttpStringEntity entity){ + return entity; + } +} diff --git a/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/api/WxProfitSharingService.java b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/api/WxProfitSharingService.java index d030742..d4bc919 100644 --- a/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/api/WxProfitSharingService.java +++ b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/api/WxProfitSharingService.java @@ -1,30 +1,29 @@ package com.egzosn.pay.wx.v3.api; import java.io.InputStream; -import java.security.PrivateKey; import java.util.Date; import java.util.Map; +import org.apache.http.message.BasicHeader; + import com.alibaba.fastjson.JSONObject; import com.egzosn.pay.common.bean.AssistOrder; import com.egzosn.pay.common.bean.BillType; import com.egzosn.pay.common.bean.MethodType; import com.egzosn.pay.common.bean.NoticeParams; -import com.egzosn.pay.common.bean.NoticeRequest; import com.egzosn.pay.common.bean.OrderParaStructure; import com.egzosn.pay.common.bean.PayMessage; import com.egzosn.pay.common.bean.PayOrder; -import com.egzosn.pay.common.bean.PayOutMessage; import com.egzosn.pay.common.bean.RefundOrder; import com.egzosn.pay.common.bean.RefundResult; import com.egzosn.pay.common.bean.TransferOrder; import com.egzosn.pay.common.bean.result.PayException; import com.egzosn.pay.common.exception.PayErrorException; import com.egzosn.pay.common.http.HttpConfigStorage; +import com.egzosn.pay.common.http.HttpStringEntity; import com.egzosn.pay.common.http.UriVariables; import com.egzosn.pay.common.util.DateUtils; import com.egzosn.pay.common.util.MapGen; -import com.egzosn.pay.common.util.sign.encrypt.RSA2; import com.egzosn.pay.wx.bean.WxPayError; import com.egzosn.pay.wx.bean.WxTransferType; import com.egzosn.pay.wx.v3.bean.WxProfitSharingTransactionType; @@ -70,8 +69,8 @@ public class WxProfitSharingService extends WxPayService implements ProfitSharin @Override protected void initAfter() { // new Thread(() -> { - payConfigStorage.loadCertEnvironment(); - getAssistService(); + payConfigStorage.loadCertEnvironment(); + setApiServerUrl(WxConst.URI); // }).start(); } @@ -121,6 +120,17 @@ public class WxProfitSharingService extends WxPayService implements ProfitSharin return getAssistService().doExecute(parameters, order); } + /** + * http 实体 钩子 + * + * @param entity 实体 + * @return 返回处理后的实体 + */ + @Override + public HttpStringEntity hookHttpEntity(HttpStringEntity entity) { + entity.addHeader(new BasicHeader(WxConst.WECHATPAY_SERIAL, payConfigStorage.getCertEnvironment().getPlatformSerialNumber())); + return entity; + } /** * 返回创建的订单信息 @@ -148,19 +158,6 @@ public class WxProfitSharingService extends WxPayService implements ProfitSharin } - /** - * 签名 - * - * @param content 需要签名的内容 不包含key - * @param characterEncoding 字符编码 - * @return 签名结果 - */ - @Override - public String createSign(String content, String characterEncoding) { - PrivateKey privateKey = payConfigStorage.getCertEnvironment().getPrivateKey(); - return RSA2.sign(content, privateKey, characterEncoding); - } - /** * 将请求参数或者请求流转化为 Map * diff --git a/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/CertEnvironment.java b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/CertEnvironment.java index c1c4360..4b226a3 100644 --- a/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/CertEnvironment.java +++ b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/CertEnvironment.java @@ -3,6 +3,8 @@ package com.egzosn.pay.wx.v3.bean; import java.security.PrivateKey; import java.security.PublicKey; +import com.egzosn.pay.common.util.str.StringUtils; + /** * 证书模式运行时环境 * @@ -32,8 +34,6 @@ public class CertEnvironment { private String platformSerialNumber; - - public CertEnvironment() { } @@ -68,6 +68,9 @@ public class CertEnvironment { } public String getPlatformSerialNumber() { + if (StringUtils.isEmpty(platformSerialNumber)) { + setPlatformSerialNumber(serialNumber); + } return platformSerialNumber; } diff --git a/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/utils/WxConst.java b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/utils/WxConst.java index 38ab1bd..b491eb5 100644 --- a/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/utils/WxConst.java +++ b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/utils/WxConst.java @@ -83,6 +83,7 @@ public final class WxConst { public static final String OFFSET = "offset"; public static final String LIMIT = "limit"; public static final String DETAIL_STATUS = "detail_status"; + public static final String WECHATPAY_SERIAL = "Wechatpay-Serial"; } -- Gitee From 17be83c20f2439d86d8adb0e85a9899b8af1f2d0 Mon Sep 17 00:00:00 2001 From: egan Date: Tue, 12 Sep 2023 22:34:15 +0800 Subject: [PATCH 288/299] =?UTF-8?q?paypal=20v2=E6=96=B0=E5=A2=9Ewebhook?= =?UTF-8?q?=E5=9B=9E=E8=B0=83=E6=A0=A1=E9=AA=8C=E6=96=B9=E5=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../v2/api/PayPalOutMessageBuilder.java | 26 ----- .../pay/paypal/v2/api/PayPalPayService.java | 100 +++++++++++++++--- .../egzosn/pay/paypal/v2/bean/Constants.java | 59 +++++++++++ .../pay/paypal/v2/utils/PayPalUtil.java | 93 ++++++++++++++++ 4 files changed, 238 insertions(+), 40 deletions(-) delete mode 100644 pay-java-paypal/src/main/java/com/egzosn/pay/paypal/v2/api/PayPalOutMessageBuilder.java create mode 100644 pay-java-paypal/src/main/java/com/egzosn/pay/paypal/v2/bean/Constants.java create mode 100644 pay-java-paypal/src/main/java/com/egzosn/pay/paypal/v2/utils/PayPalUtil.java diff --git a/pay-java-paypal/src/main/java/com/egzosn/pay/paypal/v2/api/PayPalOutMessageBuilder.java b/pay-java-paypal/src/main/java/com/egzosn/pay/paypal/v2/api/PayPalOutMessageBuilder.java deleted file mode 100644 index 71e19c4..0000000 --- a/pay-java-paypal/src/main/java/com/egzosn/pay/paypal/v2/api/PayPalOutMessageBuilder.java +++ /dev/null @@ -1,26 +0,0 @@ -package com.egzosn.pay.paypal.v2.api; - -import java.util.Map; - -import com.egzosn.pay.common.bean.outbuilder.TextBuilder; - -/** - * @author Egan - *
- * email egzosn@gmail.com
- * date 2021/1/17
- * 
- */ -public class PayPalOutMessageBuilder extends TextBuilder { - - - public PayPalOutMessageBuilder(Map message) { - StringBuilder out = new StringBuilder(); - for (Map.Entry entry : message.entrySet()) { - out.append(entry.getKey()).append('=').append(entry.getValue()).append("
"); - } - super.content(out.toString()); - } - - -} diff --git a/pay-java-paypal/src/main/java/com/egzosn/pay/paypal/v2/api/PayPalPayService.java b/pay-java-paypal/src/main/java/com/egzosn/pay/paypal/v2/api/PayPalPayService.java index 5eb631d..0dae83f 100644 --- a/pay-java-paypal/src/main/java/com/egzosn/pay/paypal/v2/api/PayPalPayService.java +++ b/pay-java-paypal/src/main/java/com/egzosn/pay/paypal/v2/api/PayPalPayService.java @@ -1,13 +1,16 @@ package com.egzosn.pay.paypal.v2.api; +import java.io.IOException; import java.io.InputStream; import java.io.UnsupportedEncodingException; +import java.security.cert.X509Certificate; import java.util.ArrayList; +import java.util.Collection; import java.util.Collections; import java.util.Date; +import java.util.Enumeration; import java.util.HashMap; -import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.UUID; @@ -23,11 +26,11 @@ import com.alibaba.fastjson.JSONObject; import com.egzosn.pay.common.api.BasePayService; import com.egzosn.pay.common.bean.AssistOrder; import com.egzosn.pay.common.bean.BillType; - import com.egzosn.pay.common.bean.CurType; import com.egzosn.pay.common.bean.DefaultCurType; import com.egzosn.pay.common.bean.MethodType; import com.egzosn.pay.common.bean.NoticeParams; +import com.egzosn.pay.common.bean.NoticeRequest; import com.egzosn.pay.common.bean.PayMessage; import com.egzosn.pay.common.bean.PayOrder; import com.egzosn.pay.common.bean.PayOutMessage; @@ -38,10 +41,13 @@ import com.egzosn.pay.common.bean.result.PayException; import com.egzosn.pay.common.exception.PayErrorException; import com.egzosn.pay.common.http.HttpHeader; import com.egzosn.pay.common.http.HttpStringEntity; +import com.egzosn.pay.common.http.ResponseEntity; import com.egzosn.pay.common.http.UriVariables; +import com.egzosn.pay.common.util.IOUtils; import com.egzosn.pay.common.util.Util; import com.egzosn.pay.common.util.str.StringUtils; import com.egzosn.pay.paypal.api.PayPalConfigStorage; +import com.egzosn.pay.paypal.v2.bean.Constants; import com.egzosn.pay.paypal.v2.bean.PayPalRefundResult; import com.egzosn.pay.paypal.v2.bean.PayPalTransactionType; import com.egzosn.pay.paypal.v2.bean.order.ApplicationContext; @@ -49,6 +55,7 @@ import com.egzosn.pay.paypal.v2.bean.order.Money; import com.egzosn.pay.paypal.v2.bean.order.OrderRequest; import com.egzosn.pay.paypal.v2.bean.order.PurchaseUnitRequest; import com.egzosn.pay.paypal.v2.bean.order.ShippingDetail; +import com.egzosn.pay.paypal.v2.utils.PayPalUtil; /** @@ -61,6 +68,7 @@ import com.egzosn.pay.paypal.v2.bean.order.ShippingDetail; */ public class PayPalPayService extends BasePayService implements PayPalPayServiceInf { + /** * 沙箱环境 */ @@ -161,12 +169,18 @@ public class PayPalPayService extends BasePayService implem @Deprecated @Override public boolean verify(Map params) { - return verify(new NoticeParams(params)); + + throw new PayErrorException(new PayException("failure", "payPal V2版本不支持此校验方式")); } - @Override - public boolean verify(NoticeParams noticeParams) { + + /** + * 保留IPN的校验方式 + * @param noticeParams 参数 + * @return 结果 + */ + public boolean verifyIpn(NoticeParams noticeParams) { final Map params = noticeParams.getBody(); Object paymentStatus = params.get("payment_status"); if (!"Completed".equals(paymentStatus)) { @@ -177,6 +191,64 @@ public class PayPalPayService extends BasePayService implem return "VERIFIED".equals(resp); } + @Override + public boolean verify(NoticeParams noticeParams) { + + final Map> headers = noticeParams.getHeaders(); + if (null == headers || headers.isEmpty()) { + throw new PayErrorException(new PayException("failure", "校验失败,请求头不能为空")); + } + + + String clientCertificateLocation = noticeParams.getHeader(Constants.PAYPAL_HEADER_CERT_URL); + ResponseEntity clientCertificateResponseEntity = requestTemplate.getForObjectEntity(clientCertificateLocation, InputStream.class); + if (clientCertificateResponseEntity.getStatusCode() > 400) { + LOG.error("获取证书信息失败,无法进行webHook校验:{}", clientCertificateLocation); + return false; + } + InputStream inputStream = clientCertificateResponseEntity.getBody(); + Collection clientCerts = PayPalUtil.getCertificateFromStream(inputStream); + Map body = noticeParams.getBody(); + String webHookId = (String) body.get(Constants.ID); + String actualSignatureEncoded = noticeParams.getHeader(Constants.PAYPAL_HEADER_TRANSMISSION_SIG); + String authAlgo = noticeParams.getHeader(Constants.PAYPAL_HEADER_AUTH_ALGO); + String transmissionId = noticeParams.getHeader(Constants.PAYPAL_HEADER_TRANSMISSION_ID); + String transmissionTime = noticeParams.getHeader(Constants.PAYPAL_HEADER_TRANSMISSION_TIME); + String requestBody = noticeParams.getBodyStr(); + String expectedSignature = String.format("%s|%s|%s|%s", transmissionId, transmissionTime, webHookId, PayPalUtil.crc32(requestBody)); + boolean isDataValid = PayPalUtil.validateData(clientCerts, authAlgo, actualSignatureEncoded, expectedSignature); + LOG.debug("数据校验结果: {}", isDataValid); + return isDataValid; + + } + + /** + * 将请求参数或者请求流转化为 Map + * + * @param request 通知请求 + * @return 获得回调的请求参数 + */ + @Override + public NoticeParams getNoticeParams(NoticeRequest request) { + NoticeParams noticeParams = new NoticeParams(); + try (InputStream is = request.getInputStream()) { + String body = IOUtils.toString(is); + noticeParams.setBodyStr(body); + noticeParams.setBody(JSON.parseObject(body)); + } + catch (IOException e) { + throw new PayErrorException(new PayException("failure", "获取回调参数异常"), e); + } + Map> headers = new HashMap<>(); + Enumeration headerNames = request.getHeaderNames(); + while (headerNames.hasMoreElements()) { + String name = headerNames.nextElement(); + headers.put(name, Collections.list(request.getHeaders(name))); + } + noticeParams.setHeaders(headers); + return noticeParams; + } + /** * 获取授权请求头 * @@ -305,14 +377,14 @@ public class PayPalPayService extends BasePayService implem @Override public PayOutMessage getPayOutMessage(String code, String message) { - String out = "The response from IPN was: " + code + ""; - return PayOutMessage.TEXT().content(out).build(); + + return PayOutMessage.TEXT().content(code).build(); } @Override public PayOutMessage successPayOutMessage(PayMessage payMessage) { - Map message = payMessage.getPayMessage(); - return new PayPalOutMessageBuilder(message).build(); + + return PayOutMessage.TEXT().content("200").build(); } @Override @@ -367,16 +439,18 @@ public class PayPalPayService extends BasePayService implem public Map close(String tradeNo, String outTradeNo) { return null; } + /** * 交易关闭接口 * - * @param assistOrder 关闭订单 + * @param assistOrder 关闭订单 * @return 返回支付方交易关闭后的结果 */ @Override - public Map close(AssistOrder assistOrder){ + public Map close(AssistOrder assistOrder) { throw new UnsupportedOperationException("不支持该操作"); } + /** * 注意:最好在付款成功之后回调时进行调用 * 确认订单并返回确认后订单信息 @@ -518,13 +592,11 @@ public class PayPalPayService extends BasePayService implem JSONObject resp = getHttpRequestTemplate().getForObject(getReqUrl(PayPalTransactionType.REFUND_GET), authHeader(), JSONObject.class, refundOrder.getRefundNo()); return resp; } + @Override public Map downloadBill(Date billDate, BillType billType) { return Collections.emptyMap(); } - - - } diff --git a/pay-java-paypal/src/main/java/com/egzosn/pay/paypal/v2/bean/Constants.java b/pay-java-paypal/src/main/java/com/egzosn/pay/paypal/v2/bean/Constants.java new file mode 100644 index 0000000..f0819fa --- /dev/null +++ b/pay-java-paypal/src/main/java/com/egzosn/pay/paypal/v2/bean/Constants.java @@ -0,0 +1,59 @@ +package com.egzosn.pay.paypal.v2.bean; + +/** + * @author Egan + * @email egan@egzosn.com + * @date 2023/9/12 + */ +public final class Constants { + private Constants() { + } + + /** + * PayPal webhook transmission ID HTTP request header + */ + public static final String PAYPAL_HEADER_TRANSMISSION_ID = "PAYPAL-TRANSMISSION-ID"; + + /** + * PayPal webhook transmission time HTTP request header + */ + public static final String PAYPAL_HEADER_TRANSMISSION_TIME = "PAYPAL-TRANSMISSION-TIME"; + + /** + * PayPal webhook transmission signature HTTP request header + */ + public static final String PAYPAL_HEADER_TRANSMISSION_SIG = "PAYPAL-TRANSMISSION-SIG"; + /** + * PayPal webhook certificate URL HTTP request header + */ + public static final String PAYPAL_HEADER_CERT_URL = "PAYPAL-CERT-URL"; + + /** + * PayPal webhook authentication algorithm HTTP request header + */ + public static final String PAYPAL_HEADER_AUTH_ALGO = "PAYPAL-AUTH-ALGO"; + + /** + * Trust Certificate Location to be used to validate webhook certificates + */ + public static final String PAYPAL_TRUST_CERT_URL = "webhook.trustCert"; + + + /** + * Default Trust Certificate that comes packaged with SDK. + */ + public static final String PAYPAL_TRUST_DEFAULT_CERT = "DigiCertSHA2ExtendedValidationServerCA.crt"; + + /** + * Webhook Id to be set for validation purposes + */ + public static final String PAYPAL_WEBHOOK_ID = "webhook.id"; + + /** + * Webhook Id to be set for validation purposes + */ + public static final String PAYPAL_WEBHOOK_CERTIFICATE_AUTHTYPE = "webhook.authType"; + public static final String ID = "id"; + + +} diff --git a/pay-java-paypal/src/main/java/com/egzosn/pay/paypal/v2/utils/PayPalUtil.java b/pay-java-paypal/src/main/java/com/egzosn/pay/paypal/v2/utils/PayPalUtil.java new file mode 100644 index 0000000..aaec461 --- /dev/null +++ b/pay-java-paypal/src/main/java/com/egzosn/pay/paypal/v2/utils/PayPalUtil.java @@ -0,0 +1,93 @@ +package com.egzosn.pay.paypal.v2.utils; + +import java.io.InputStream; +import java.nio.charset.Charset; +import java.security.GeneralSecurityException; +import java.security.Signature; +import java.security.cert.CertificateException; +import java.security.cert.CertificateFactory; +import java.security.cert.X509Certificate; +import java.util.Collection; +import java.util.zip.CRC32; +import java.util.zip.Checksum; + +import org.apache.commons.codec.binary.Base64; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.egzosn.pay.common.bean.result.PayException; +import com.egzosn.pay.common.exception.PayErrorException; + +/** + * @author Egan + * @email egan@egzosn.com + * @date 2023/9/12 + */ +public final class PayPalUtil { + private static final Logger LOG = LoggerFactory.getLogger(PayPalUtil.class); + + private PayPalUtil() { + } + + public static Collection getCertificateFromStream(InputStream stream) { + if (stream == null) { + throw new PayErrorException(new PayException("failure", "未找到证书")); + } + try { + CertificateFactory cf = CertificateFactory.getInstance("X.509"); + + return (Collection) cf.generateCertificates(stream); + } + catch (CertificateException ex) { + throw new PayErrorException(new PayException("failure", "证书加载异常"), ex); + } + + } + + /** + * 生成字符串传递的CRC 32值 + * + * @param data 字符 + * @return 返回长crc32输入值。-1如果string为null + */ + public static long crc32(String data) { + if (data == null) { + return -1; + } + byte[] bytes = data.getBytes(Charset.forName("utf-8")); + Checksum checksum = new CRC32(); + checksum.update(bytes, 0, bytes.length); + return checksum.getValue(); + + } + + + /** + * 基于https://developer.paypal.com/docs/integration/direct/rest-webhooks-overview/#event-signature验证Webhook签名验证,如果签名有效则返回true + * + * @param clientCerts 客户端证书 + * @param algo 服务器生成签名时使用的算法 + * @param actualSignatureEncoded Paypal-Transmission-Sig服务器传递的报头值 + * @param expectedSignature 用请求体的CRC32值格式化数据生成的签名 + * @return true 校验通过 + */ + public static boolean validateData(Collection clientCerts, String algo, String actualSignatureEncoded, String expectedSignature) { + // 从paypal-auth-algorithm HTTP头中获取signatureAlgorithm + Signature signatureAlgorithm = null; + try { + signatureAlgorithm = Signature.getInstance(algo); + //从HTTP头中提供的URL中获取certData并缓存它 + X509Certificate[] clientChain = clientCerts.toArray(new X509Certificate[0]); + signatureAlgorithm.initVerify(clientChain[0].getPublicKey()); + signatureAlgorithm.update(expectedSignature.getBytes()); + // 实际的签名是base 64编码的,可以在HTTP头中找到 + byte[] actualSignature = Base64.decodeBase64(actualSignatureEncoded.getBytes()); + return signatureAlgorithm.verify(actualSignature); + } + catch (GeneralSecurityException e) { + LOG.error("校验异常", e); + return false; + } + + } +} -- Gitee From 15ac60e29e87ee3170fdb133a5a57ead8e1b9ee1 Mon Sep 17 00:00:00 2001 From: egan Date: Tue, 12 Sep 2023 22:34:28 +0800 Subject: [PATCH 289/299] =?UTF-8?q?fastjson.version=E7=89=88=E6=9C=AC1.2.8?= =?UTF-8?q?3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index ab7ffe1..aae5e54 100644 --- a/pom.xml +++ b/pom.xml @@ -99,7 +99,7 @@ com.alibaba fastjson - 1.2.73 + ${fastjson.version} org.bouncycastle -- Gitee From 0ebe2878a4c6750d304ef9c0e80548463ee0e8b0 Mon Sep 17 00:00:00 2001 From: egan Date: Tue, 12 Sep 2023 22:35:01 +0800 Subject: [PATCH 290/299] =?UTF-8?q?paypal=20v2=E6=96=B0=E5=A2=9Ewebhook?= =?UTF-8?q?=E5=9B=9E=E8=B0=83=E6=A0=A1=E9=AA=8C=E6=96=B9=E5=BC=8Fdemo?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/PayPalV2PayController.java | 60 +++++-------------- 1 file changed, 14 insertions(+), 46 deletions(-) diff --git a/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/PayPalV2PayController.java b/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/PayPalV2PayController.java index 13f5eeb..1393847 100644 --- a/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/PayPalV2PayController.java +++ b/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/PayPalV2PayController.java @@ -1,15 +1,14 @@ package com.egzosn.pay.demo.controller; -import java.io.IOException; -import java.io.InputStream; import java.math.BigDecimal; import java.util.Map; import javax.annotation.PostConstruct; import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; -import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @@ -117,49 +116,15 @@ public class PayPalV2PayController { order.setRefundNo("退款成功之后返回的退款单号"); return service.refundquery(order); } - /** - * 注意:这里不是异步回调的通知 IPN 地址设置的路径:https://developer.paypal.com/developer/ipnSimulator/ - * PayPal确认付款调用的接口 - * 用户确认付款后,paypal调用的这个方法执行付款 - * - * @param request 请求 - * @return 付款成功信息 - * @throws IOException IOException - */ - @GetMapping(value = "payBackBefore.json") - public String payBackBefore(HttpServletRequest request) throws IOException { - try (InputStream is = request.getInputStream()) { - // 参数解析与校验 https://developer.paypal.com/docs/api-basics/notifications/ipn/IPNIntro/#id08CKFJ00JYK - if (service.verify(service.getParameter2Map(request.getParameterMap(), is))) { - // TODO 这里进行成功后的订单业务处理 - // TODO 返回成功付款页面,这个到时候再做一个漂亮的页面显示,并使用前后端分离的模式 - return service.successPayOutMessage(null).toMessage(); - } - } - return "failure"; - } + /** * 支付回调地址 + * 请求方式必须为post, 回调详情文档:https://developer.paypal.com/api/rest/webhooks + * 回调成功之后必须返回http状态码 200 才能行,不然一直会重复回调,注意:如果您的应用程序以任何其他状态码响应,贝宝会在三天内重试25次通知消息 * - * @param request 请求 - * - * @return 是否成功 - * - * 业务处理在对应的PayMessageHandler里面处理,在哪里设置PayMessageHandler,详情查看{@link com.egzosn.pay.common.api.PayService#setPayMessageHandler(com.egzosn.pay.common.api.PayMessageHandler)} - * - * 如果未设置 {@link com.egzosn.pay.common.api.PayMessageHandler} 那么会使用默认的 {@link com.egzosn.pay.common.api.DefaultPayMessageHandler} - * @throws IOException IOException - */ - @RequestMapping(value = "payBackOld.json") - public String payBackOld(HttpServletRequest request) throws IOException { - //业务处理在对应的PayMessageHandler里面处理,在哪里设置PayMessageHandler,详情查看com.egzosn.pay.common.api.PayService.setPayMessageHandler() - return service.payBack(request.getParameterMap(), request.getInputStream()).toMessage(); - } - /** - * 支付回调地址 - * - * @param request 请求 + * @param request 请求 + * @param response 响应 * @return 是否成功 *

* 业务处理在对应的PayMessageHandler里面处理,在哪里设置PayMessageHandler,详情查看{@link com.egzosn.pay.common.api.PayService#setPayMessageHandler(com.egzosn.pay.common.api.PayMessageHandler)} @@ -171,12 +136,15 @@ public class PayPalV2PayController { * 注意:此方法一个订单只能调用一次, 建议在支付回调时进行调用 * 这里主要用来获取captureId使用,后续退款,查订单等等使用,用来替换下单返回的id * 详情: https://developer.paypal.com/docs/api/orders/v2/#orders_capture - * */ - @RequestMapping(value = "payBack.json") - public String payBack(HttpServletRequest request) { + @PostMapping(value = "payBack.json") + public String payBack(HttpServletRequest request, HttpServletResponse response) { //业务处理在对应的PayMessageHandler里面处理,在哪里设置PayMessageHandler,详情查看com.egzosn.pay.common.api.PayService.setPayMessageHandler() - return service.payBack(new HttpRequestNoticeParams(request)).toMessage(); + final String message = service.payBack(new HttpRequestNoticeParams(request)).toMessage(); + if (!"200".equals(message)) { + response.setStatus(400); + } + return message; } -- Gitee From bf3670665d700a3794d3b58e373926d5a66a0597 Mon Sep 17 00:00:00 2001 From: egan Date: Wed, 13 Sep 2023 21:23:24 +0800 Subject: [PATCH 291/299] =?UTF-8?q?2.14.6=E5=8F=91=E5=B8=83?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 2 +- pay-java-ali/pom.xml | 2 +- pay-java-baidu/pom.xml | 2 +- pay-java-common/pom.xml | 2 +- .../pay/common/http/HttpRequestTemplate.java | 18 +++++++++--------- pay-java-demo/pom.xml | 2 +- pay-java-fuiou/pom.xml | 2 +- pay-java-payoneer/pom.xml | 2 +- pay-java-paypal/pom.xml | 2 +- .../pay/paypal/v2/api/PayPalPayService.java | 2 +- .../egzosn/pay/paypal/v2/bean/Constants.java | 4 ++-- .../egzosn/pay/paypal/v2/utils/PayPalUtil.java | 4 ++-- pay-java-union/pom.xml | 2 +- pay-java-web-support/pom.xml | 2 +- pay-java-wx-youdian/pom.xml | 2 +- pay-java-wx/pom.xml | 2 +- .../egzosn/pay/wx/v3/api/WxPayServiceInf.java | 5 +++-- pay-java-yiji/pom.xml | 2 +- pom.xml | 4 ++-- 19 files changed, 32 insertions(+), 31 deletions(-) diff --git a/README.md b/README.md index e80219f..7c75183 100644 --- a/README.md +++ b/README.md @@ -22,7 +22,7 @@ com.egzosn {module-name} - 2.14.5 + 2.14.6 ``` diff --git a/pay-java-ali/pom.xml b/pay-java-ali/pom.xml index feabf14..08e8e88 100644 --- a/pay-java-ali/pom.xml +++ b/pay-java-ali/pom.xml @@ -5,7 +5,7 @@ pay-java-parent com.egzosn - 2.14.5 + 2.14.6 4.0.0 pay-java-ali diff --git a/pay-java-baidu/pom.xml b/pay-java-baidu/pom.xml index e2723cd..d89dd0f 100644 --- a/pay-java-baidu/pom.xml +++ b/pay-java-baidu/pom.xml @@ -5,7 +5,7 @@ pay-java-parent com.egzosn - 2.14.5 + 2.14.6 4.0.0 pay-java-baidu diff --git a/pay-java-common/pom.xml b/pay-java-common/pom.xml index c7d3e72..c62b177 100644 --- a/pay-java-common/pom.xml +++ b/pay-java-common/pom.xml @@ -5,7 +5,7 @@ pay-java-parent com.egzosn - 2.14.5 + 2.14.6 4.0.0 jar diff --git a/pay-java-common/src/main/java/com/egzosn/pay/common/http/HttpRequestTemplate.java b/pay-java-common/src/main/java/com/egzosn/pay/common/http/HttpRequestTemplate.java index 0f9564c..ae0b000 100644 --- a/pay-java-common/src/main/java/com/egzosn/pay/common/http/HttpRequestTemplate.java +++ b/pay-java-common/src/main/java/com/egzosn/pay/common/http/HttpRequestTemplate.java @@ -288,11 +288,11 @@ public class HttpRequestTemplate { * @return 类型对象 * * Map<String, String> uriVariables = new HashMap<String, String>();
- *

+ * * uriVariables.put("id", "1");
- *

+ * * uriVariables.put("type", "APP");
- *

+ * * getForObject("http://egan.in/pay/{id}/f/{type}", String.class, uriVariables)
*
*/ @@ -368,11 +368,11 @@ public class HttpRequestTemplate { * @return 类型对象 * * Map<String, String> uriVariables = new HashMap<String, String>();
- *

+ * * uriVariables.put("id", "1");
- *

+ * * uriVariables.put("type", "APP");
- *

+ * * getForObject("http://egan.in/pay/{id}/f/{type}", String.class, uriVariables)
*
*/ @@ -391,11 +391,11 @@ public class HttpRequestTemplate { * @return 类型对象 * * Map<String, String> uriVariables = new HashMap<String, String>();
- *

+ * * uriVariables.put("id", "1");
- *

+ * * uriVariables.put("type", "APP");
- *

+ * * getForObject("http://egan.in/pay/{id}/f/{type}", String.class, uriVariables)
*
*/ diff --git a/pay-java-demo/pom.xml b/pay-java-demo/pom.xml index ddc9ab0..fd3776c 100644 --- a/pay-java-demo/pom.xml +++ b/pay-java-demo/pom.xml @@ -5,7 +5,7 @@ pay-java-parent com.egzosn - 2.14.5 + 2.14.6 4.0.0 war diff --git a/pay-java-fuiou/pom.xml b/pay-java-fuiou/pom.xml index 514bcb9..2fbaac4 100644 --- a/pay-java-fuiou/pom.xml +++ b/pay-java-fuiou/pom.xml @@ -5,7 +5,7 @@ pay-java-parent com.egzosn - 2.14.5 + 2.14.6 4.0.0 pay-java-fuiou diff --git a/pay-java-payoneer/pom.xml b/pay-java-payoneer/pom.xml index 415950d..bf98485 100644 --- a/pay-java-payoneer/pom.xml +++ b/pay-java-payoneer/pom.xml @@ -5,7 +5,7 @@ pay-java-parent com.egzosn - 2.14.5 + 2.14.6 4.0.0 pay-java-payoneer diff --git a/pay-java-paypal/pom.xml b/pay-java-paypal/pom.xml index efcb5e2..b6e88f7 100644 --- a/pay-java-paypal/pom.xml +++ b/pay-java-paypal/pom.xml @@ -5,7 +5,7 @@ pay-java-parent com.egzosn - 2.14.5 + 2.14.6 4.0.0 diff --git a/pay-java-paypal/src/main/java/com/egzosn/pay/paypal/v2/api/PayPalPayService.java b/pay-java-paypal/src/main/java/com/egzosn/pay/paypal/v2/api/PayPalPayService.java index 0dae83f..a5c2eb1 100644 --- a/pay-java-paypal/src/main/java/com/egzosn/pay/paypal/v2/api/PayPalPayService.java +++ b/pay-java-paypal/src/main/java/com/egzosn/pay/paypal/v2/api/PayPalPayService.java @@ -62,7 +62,7 @@ import com.egzosn.pay.paypal.v2.utils.PayPalUtil; * 贝宝支付配置存储 * * @author egan - *

+ * * email egzosn@gmail.com * date 2021-1-16 ‏‎22:15:09 */ diff --git a/pay-java-paypal/src/main/java/com/egzosn/pay/paypal/v2/bean/Constants.java b/pay-java-paypal/src/main/java/com/egzosn/pay/paypal/v2/bean/Constants.java index f0819fa..3f8fc20 100644 --- a/pay-java-paypal/src/main/java/com/egzosn/pay/paypal/v2/bean/Constants.java +++ b/pay-java-paypal/src/main/java/com/egzosn/pay/paypal/v2/bean/Constants.java @@ -2,8 +2,8 @@ package com.egzosn.pay.paypal.v2.bean; /** * @author Egan - * @email egan@egzosn.com - * @date 2023/9/12 + * email egan@egzosn.com + * date 2023/9/12 */ public final class Constants { private Constants() { diff --git a/pay-java-paypal/src/main/java/com/egzosn/pay/paypal/v2/utils/PayPalUtil.java b/pay-java-paypal/src/main/java/com/egzosn/pay/paypal/v2/utils/PayPalUtil.java index aaec461..cc8c0d9 100644 --- a/pay-java-paypal/src/main/java/com/egzosn/pay/paypal/v2/utils/PayPalUtil.java +++ b/pay-java-paypal/src/main/java/com/egzosn/pay/paypal/v2/utils/PayPalUtil.java @@ -20,8 +20,8 @@ import com.egzosn.pay.common.exception.PayErrorException; /** * @author Egan - * @email egan@egzosn.com - * @date 2023/9/12 + * email egan@egzosn.com + * date 2023/9/12 */ public final class PayPalUtil { private static final Logger LOG = LoggerFactory.getLogger(PayPalUtil.class); diff --git a/pay-java-union/pom.xml b/pay-java-union/pom.xml index 7058352..9a1a2ca 100644 --- a/pay-java-union/pom.xml +++ b/pay-java-union/pom.xml @@ -5,7 +5,7 @@ pay-java-parent com.egzosn - 2.14.5 + 2.14.6 4.0.0 diff --git a/pay-java-web-support/pom.xml b/pay-java-web-support/pom.xml index 8ddb16d..a5132df 100644 --- a/pay-java-web-support/pom.xml +++ b/pay-java-web-support/pom.xml @@ -5,7 +5,7 @@ pay-java-parent com.egzosn - 2.14.5 + 2.14.6 4.0.0 jar diff --git a/pay-java-wx-youdian/pom.xml b/pay-java-wx-youdian/pom.xml index 6d1afc9..519d7a0 100644 --- a/pay-java-wx-youdian/pom.xml +++ b/pay-java-wx-youdian/pom.xml @@ -5,7 +5,7 @@ pay-java-parent com.egzosn - 2.14.5 + 2.14.6 4.0.0 pay-java-wx-youdian diff --git a/pay-java-wx/pom.xml b/pay-java-wx/pom.xml index b12b890..93bae15 100644 --- a/pay-java-wx/pom.xml +++ b/pay-java-wx/pom.xml @@ -5,7 +5,7 @@ pay-java-parent com.egzosn - 2.14.5 + 2.14.6 4.0.0 pay-java-wx diff --git a/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/api/WxPayServiceInf.java b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/api/WxPayServiceInf.java index 01080b9..dbf5436 100644 --- a/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/api/WxPayServiceInf.java +++ b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/api/WxPayServiceInf.java @@ -6,9 +6,10 @@ import com.egzosn.pay.common.bean.TransactionType; import com.egzosn.pay.common.http.HttpStringEntity; /** + * 微信支付接口 * @author Egan - * @email egan@egzosn.com - * @date 2023/9/11 + * email egan@egzosn.com + * date 2023/9/11 */ public interface WxPayServiceInf extends PayService { /** diff --git a/pay-java-yiji/pom.xml b/pay-java-yiji/pom.xml index b3a7b48..eb9fd92 100644 --- a/pay-java-yiji/pom.xml +++ b/pay-java-yiji/pom.xml @@ -5,7 +5,7 @@ pay-java-parent com.egzosn - 2.14.5 + 2.14.6 4.0.0 diff --git a/pom.xml b/pom.xml index aae5e54..42783ae 100644 --- a/pom.xml +++ b/pom.xml @@ -7,7 +7,7 @@ com.egzosn pay-java-parent pom - 2.14.5 + 2.14.6 Pay Java - Parent Pay Java Parent @@ -65,7 +65,7 @@ - 2.14.5 + 2.14.6 4.5.4 1.2.17 1.2.83 -- Gitee From ac256c3ef54d760d003a5ba9ea4d49272951fdb2 Mon Sep 17 00:00:00 2001 From: egan Date: Wed, 13 Sep 2023 22:17:39 +0800 Subject: [PATCH 292/299] =?UTF-8?q?2.14.7=E5=BC=80=E5=A7=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pay-java-ali/pom.xml | 2 +- pay-java-baidu/pom.xml | 2 +- pay-java-common/pom.xml | 2 +- pay-java-demo/pom.xml | 2 +- pay-java-fuiou/pom.xml | 2 +- pay-java-payoneer/pom.xml | 2 +- pay-java-paypal/pom.xml | 2 +- pay-java-union/pom.xml | 2 +- pay-java-web-support/pom.xml | 2 +- pay-java-wx-youdian/pom.xml | 2 +- pay-java-wx/pom.xml | 2 +- pay-java-yiji/pom.xml | 2 +- pom.xml | 4 ++-- 13 files changed, 14 insertions(+), 14 deletions(-) diff --git a/pay-java-ali/pom.xml b/pay-java-ali/pom.xml index 08e8e88..2425630 100644 --- a/pay-java-ali/pom.xml +++ b/pay-java-ali/pom.xml @@ -5,7 +5,7 @@ pay-java-parent com.egzosn - 2.14.6 + 2.14.7 4.0.0 pay-java-ali diff --git a/pay-java-baidu/pom.xml b/pay-java-baidu/pom.xml index d89dd0f..4d2ed8f 100644 --- a/pay-java-baidu/pom.xml +++ b/pay-java-baidu/pom.xml @@ -5,7 +5,7 @@ pay-java-parent com.egzosn - 2.14.6 + 2.14.7 4.0.0 pay-java-baidu diff --git a/pay-java-common/pom.xml b/pay-java-common/pom.xml index c62b177..217b192 100644 --- a/pay-java-common/pom.xml +++ b/pay-java-common/pom.xml @@ -5,7 +5,7 @@ pay-java-parent com.egzosn - 2.14.6 + 2.14.7 4.0.0 jar diff --git a/pay-java-demo/pom.xml b/pay-java-demo/pom.xml index fd3776c..2e0b44b 100644 --- a/pay-java-demo/pom.xml +++ b/pay-java-demo/pom.xml @@ -5,7 +5,7 @@ pay-java-parent com.egzosn - 2.14.6 + 2.14.7 4.0.0 war diff --git a/pay-java-fuiou/pom.xml b/pay-java-fuiou/pom.xml index 2fbaac4..59192fe 100644 --- a/pay-java-fuiou/pom.xml +++ b/pay-java-fuiou/pom.xml @@ -5,7 +5,7 @@ pay-java-parent com.egzosn - 2.14.6 + 2.14.7 4.0.0 pay-java-fuiou diff --git a/pay-java-payoneer/pom.xml b/pay-java-payoneer/pom.xml index bf98485..683ac49 100644 --- a/pay-java-payoneer/pom.xml +++ b/pay-java-payoneer/pom.xml @@ -5,7 +5,7 @@ pay-java-parent com.egzosn - 2.14.6 + 2.14.7 4.0.0 pay-java-payoneer diff --git a/pay-java-paypal/pom.xml b/pay-java-paypal/pom.xml index b6e88f7..d818075 100644 --- a/pay-java-paypal/pom.xml +++ b/pay-java-paypal/pom.xml @@ -5,7 +5,7 @@ pay-java-parent com.egzosn - 2.14.6 + 2.14.7 4.0.0 diff --git a/pay-java-union/pom.xml b/pay-java-union/pom.xml index 9a1a2ca..a49a64b 100644 --- a/pay-java-union/pom.xml +++ b/pay-java-union/pom.xml @@ -5,7 +5,7 @@ pay-java-parent com.egzosn - 2.14.6 + 2.14.7 4.0.0 diff --git a/pay-java-web-support/pom.xml b/pay-java-web-support/pom.xml index a5132df..a69e2c7 100644 --- a/pay-java-web-support/pom.xml +++ b/pay-java-web-support/pom.xml @@ -5,7 +5,7 @@ pay-java-parent com.egzosn - 2.14.6 + 2.14.7 4.0.0 jar diff --git a/pay-java-wx-youdian/pom.xml b/pay-java-wx-youdian/pom.xml index 519d7a0..01f935b 100644 --- a/pay-java-wx-youdian/pom.xml +++ b/pay-java-wx-youdian/pom.xml @@ -5,7 +5,7 @@ pay-java-parent com.egzosn - 2.14.6 + 2.14.7 4.0.0 pay-java-wx-youdian diff --git a/pay-java-wx/pom.xml b/pay-java-wx/pom.xml index 93bae15..7a41e74 100644 --- a/pay-java-wx/pom.xml +++ b/pay-java-wx/pom.xml @@ -5,7 +5,7 @@ pay-java-parent com.egzosn - 2.14.6 + 2.14.7 4.0.0 pay-java-wx diff --git a/pay-java-yiji/pom.xml b/pay-java-yiji/pom.xml index eb9fd92..8cb8f7f 100644 --- a/pay-java-yiji/pom.xml +++ b/pay-java-yiji/pom.xml @@ -5,7 +5,7 @@ pay-java-parent com.egzosn - 2.14.6 + 2.14.7 4.0.0 diff --git a/pom.xml b/pom.xml index 42783ae..cf2cacc 100644 --- a/pom.xml +++ b/pom.xml @@ -7,7 +7,7 @@ com.egzosn pay-java-parent pom - 2.14.6 + 2.14.7 Pay Java - Parent Pay Java Parent @@ -65,7 +65,7 @@ - 2.14.6 + 2.14.7 4.5.4 1.2.17 1.2.83 -- Gitee From b95efc4f3ac9eca7a4e49502d27967d3ece19d7d Mon Sep 17 00:00:00 2001 From: egan Date: Wed, 13 Sep 2023 22:18:30 +0800 Subject: [PATCH 293/299] =?UTF-8?q?paypal=20v2=20=E7=A1=AE=E8=AE=A4?= =?UTF-8?q?=E8=AE=A2=E5=8D=95=E6=8E=A5=E5=8F=A3=E8=A1=A5=E5=85=85=E8=AF=B7?= =?UTF-8?q?=E6=B1=82=E5=A4=B4Content-Type?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/egzosn/pay/paypal/v2/api/PayPalPayService.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/pay-java-paypal/src/main/java/com/egzosn/pay/paypal/v2/api/PayPalPayService.java b/pay-java-paypal/src/main/java/com/egzosn/pay/paypal/v2/api/PayPalPayService.java index a5c2eb1..0679e6a 100644 --- a/pay-java-paypal/src/main/java/com/egzosn/pay/paypal/v2/api/PayPalPayService.java +++ b/pay-java-paypal/src/main/java/com/egzosn/pay/paypal/v2/api/PayPalPayService.java @@ -367,6 +367,7 @@ public class PayPalPayService extends BasePayService implem HttpStringEntity entity = new HttpStringEntity(JSON.toJSONString(orderRequest), ContentType.APPLICATION_JSON); HttpHeader header = authHeader(); header.addHeader(new BasicHeader("prefer", "return=representation")); + entity.setHeaders(header); JSONObject resp = getHttpRequestTemplate().postForObject(getReqUrl(order.getTransactionType()), entity, JSONObject.class); if ("created".equalsIgnoreCase(resp.getString("status")) && StringUtils.isNotEmpty(resp.getString("id"))) { @@ -464,7 +465,9 @@ public class PayPalPayService extends BasePayService implem */ @Override public Map ordersCapture(String tradeNo) { - JSONObject ordersCaptureInfo = getHttpRequestTemplate().postForObject(getReqUrl(PayPalTransactionType.ORDERS_CAPTURE), authHeader(), JSONObject.class, tradeNo); + final HttpHeader header = authHeader(); + header.addHeader(new BasicHeader("Content-Type","application/json")); + JSONObject ordersCaptureInfo = getHttpRequestTemplate().postForObject(getReqUrl(PayPalTransactionType.ORDERS_CAPTURE), header, JSONObject.class, tradeNo); // String captureId = ordersCaptureInfo.getJSONArray("purchaseUnits").getJSONObject(0).getJSONObject("payments").getJSONArray("captures").getJSONObject(0).getString("id"); return ordersCaptureInfo; } -- Gitee From 7ff87a2aed5ff535023261c5e82f03280b16ee07 Mon Sep 17 00:00:00 2001 From: zhaoyz Date: Tue, 26 Sep 2023 15:32:53 +0800 Subject: [PATCH 294/299] =?UTF-8?q?=E6=96=B0=E5=A2=9E=E5=BE=AE=E4=BF=A1?= =?UTF-8?q?=E6=94=AF=E4=BB=98=E5=88=86=E6=8E=A5=E5=8F=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/WxV3PayScoreController.java | 142 ++++++++++++++ .../pay/wx/v3/api/WxPayConfigStorage.java | 14 +- .../pay/wx/v3/api/WxPayScoreService.java | 179 ++++++++++++++++++ .../wx/v3/bean/WxPayScoreTransactionType.java | 54 ++++++ .../pay/wx/v3/bean/payscore/CancelOrder.java | 16 ++ .../wx/v3/bean/payscore/CompleteOrder.java | 39 ++++ .../pay/wx/v3/bean/payscore/CreateOrder.java | 95 ++++++++++ .../pay/wx/v3/bean/payscore/ModifyOrder.java | 42 ++++ .../pay/wx/v3/bean/payscore/PostPayment.java | 59 ++++++ .../pay/wx/v3/bean/payscore/RiskFund.java | 48 +++++ .../pay/wx/v3/bean/payscore/SyncOrder.java | 18 ++ .../pay/wx/v3/bean/payscore/TimeRange.java | 30 +++ .../com/egzosn/pay/wx/v3/utils/WxConst.java | 3 +- 13 files changed, 735 insertions(+), 4 deletions(-) create mode 100644 pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/WxV3PayScoreController.java create mode 100644 pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/api/WxPayScoreService.java create mode 100644 pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/WxPayScoreTransactionType.java create mode 100644 pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/payscore/CancelOrder.java create mode 100644 pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/payscore/CompleteOrder.java create mode 100644 pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/payscore/CreateOrder.java create mode 100644 pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/payscore/ModifyOrder.java create mode 100644 pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/payscore/PostPayment.java create mode 100644 pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/payscore/RiskFund.java create mode 100644 pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/payscore/SyncOrder.java create mode 100644 pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/payscore/TimeRange.java diff --git a/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/WxV3PayScoreController.java b/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/WxV3PayScoreController.java new file mode 100644 index 0000000..695e4ff --- /dev/null +++ b/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/WxV3PayScoreController.java @@ -0,0 +1,142 @@ +package com.egzosn.pay.demo.controller; + +import com.egzosn.pay.common.bean.*; +import com.egzosn.pay.common.http.HttpConfigStorage; +import com.egzosn.pay.wx.v3.api.WxPayScoreService; +import com.egzosn.pay.wx.v3.api.WxPayConfigStorage; +import com.egzosn.pay.wx.v3.api.WxPayService; +import com.egzosn.pay.wx.v3.bean.payscore.*; +import org.springframework.web.bind.annotation.*; + +import java.math.BigDecimal; +import java.util.Arrays; +import java.util.Date; +import java.util.Map; + +@RestController +@RequestMapping("/wxV3CreditScore") +public class WxV3PayScoreController { + + private WxPayService service3 = null; + private WxPayScoreService wxPayScoreService = null; + + private static final String APPID = "wxc7b993ff15a9f26c"; + private static final String SERVICE_ID = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxx"; + + private static final String MCH_ID = "1602947765"; // 商户号 + + private static final String V3_API_KEY = "9bd8f0e7af4841299d782406b7774f57"; + // @PostConstruct + public void init() { + WxPayConfigStorage paymentStandardConfig = new WxPayConfigStorage(); + paymentStandardConfig.setAppid(APPID); + paymentStandardConfig.setServiceId(SERVICE_ID); + paymentStandardConfig.setMchId(MCH_ID); + paymentStandardConfig.setV3ApiKey(V3_API_KEY); + paymentStandardConfig.setNotifyUrl("http://sailinmu.iok.la/wxV3combine/payBack.json"); + paymentStandardConfig.setInputCharset("UTF-8"); + paymentStandardConfig.setSignType("MD5"); + paymentStandardConfig.setCertStoreType(CertStoreType.PATH); + paymentStandardConfig.setApiClientKeyP12("apiclient_cert.p12"); + + HttpConfigStorage httpConfigStorage = new HttpConfigStorage(); + httpConfigStorage.setStorePassword(MCH_ID); + httpConfigStorage.setKeystore("apiclient_cert.p12"); + service3 = new WxPayService(paymentStandardConfig,httpConfigStorage); + wxPayScoreService = new WxPayScoreService(paymentStandardConfig,httpConfigStorage); + } + @PostMapping("/refund") + public RefundResult refund() { + RefundOrder refundOrder = new RefundOrder(); + + refundOrder.setRefundNo("R2023082416493947872"); + + refundOrder.setTradeNo("4200001930202308240314610507"); + //订单号 + refundOrder.setOutTradeNo("P2023082416243247872"); + //退款金额 + refundOrder.setRefundAmount(new BigDecimal("0.01")); + //退款备注 + refundOrder.setDescription("退款测试"); + + refundOrder.setCurType(DefaultCurType.CNY); + //总金额 + refundOrder.setTotalAmount(new BigDecimal("0.01")); + refundOrder.addAttr("funds_account","AVAILABLE"); + return service3.refund(refundOrder); + } + + + + + @GetMapping("/queryOrder") + public Map queryOrder(AssistOrder order) { + return wxPayScoreService.query(order); + } + @GetMapping("/queryRefundOrder") + public Map queryRefundOrder(RefundOrder refundOrder) { + return service3.refundquery(refundOrder); + } + + + + @PostMapping("/create") + public Map create() { + CreateOrder createOrder = new CreateOrder(); + createOrder.setOutTradeNo("P2023091301010100000"); + createOrder.setStartTime("OnAccept"); + //paymentRequest.setStartTime(DateUtils.formatDate(new Date(),DateUtils.YYYYMMDDHHMMSS)); + createOrder.setServiceIntroduction("测试"); + createOrder.setRiskFundAmount(new BigDecimal("1.11")); + createOrder.setRiskFundName("ESTIMATE_ORDER_COST"); + createOrder.setOpenId("oZu615JDX_H9Ni4KXmiXzuCKiBqQ"); + return wxPayScoreService.create(createOrder); + } + + + @PostMapping("/cancel") + public Map cancel() { + //撤销智慧零售 + CancelOrder cancelOrder = new CancelOrder(); + cancelOrder.setOutTradeNo("商户订单号"); + cancelOrder.setReason("测试"); + return wxPayScoreService.cancel(cancelOrder.getOutTradeNo(),cancelOrder.getReason()); + } + + + @PostMapping("/modify") + public Map modify() { + //修改订单金额 + ModifyOrder modifyOrder = new ModifyOrder(); + modifyOrder.setOutTradeNo("P2023091301010100000"); + PostPayment postPayment = new PostPayment(); + postPayment.setAmount(BigDecimal.ONE); + postPayment.setName("ESTIMATE_ORDER_COST"); + modifyOrder.setPostPayments(Arrays.asList(postPayment)); + modifyOrder.setTotalAmount(BigDecimal.ONE); + modifyOrder.setReason("test"); + return wxPayScoreService.modify(modifyOrder); + } + + @PostMapping("/complete") + public Map complete() { + //修改订单金额 + CompleteOrder completeOrder = new CompleteOrder(); + completeOrder.setOutTradeNo("P2023091301010100000"); + PostPayment postPayment = new PostPayment(); + postPayment.setAmount(BigDecimal.ONE); + postPayment.setName("ESTIMATE_ORDER_COST"); + completeOrder.setPostPayments(Arrays.asList(postPayment)); + completeOrder.setTotalAmount(BigDecimal.ONE); + return wxPayScoreService.complete(completeOrder); + } + + @PostMapping("/sync") + public Map sync() { + //修改订单金额 + SyncOrder syncOrder = new SyncOrder(); + syncOrder.setOutTradeNo("商户订单号"); + syncOrder.setPaidTime(new Date()); + return wxPayScoreService.sync(syncOrder.getOutTradeNo(),syncOrder.getPaidTime()); + } +} diff --git a/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/api/WxPayConfigStorage.java b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/api/WxPayConfigStorage.java index 02e0747..1457d1b 100644 --- a/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/api/WxPayConfigStorage.java +++ b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/api/WxPayConfigStorage.java @@ -78,8 +78,10 @@ public class WxPayConfigStorage extends BasePayConfigStorage { * 是否为服务商模式, 默认为false */ private boolean partner = false; - - + /** + * 微信支付分服务服务ID + */ + private String serviceId; @Deprecated @Override @@ -263,4 +265,12 @@ public class WxPayConfigStorage extends BasePayConfigStorage { public void setPartner(boolean partner) { this.partner = partner; } + + public String getServiceId() { + return serviceId; + } + + public void setServiceId(String serviceId) { + this.serviceId = serviceId; + } } diff --git a/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/api/WxPayScoreService.java b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/api/WxPayScoreService.java new file mode 100644 index 0000000..7a857be --- /dev/null +++ b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/api/WxPayScoreService.java @@ -0,0 +1,179 @@ +package com.egzosn.pay.wx.v3.api; + +import com.alibaba.fastjson.JSON; +import com.egzosn.pay.common.bean.AssistOrder; +import com.egzosn.pay.common.bean.Order; +import com.egzosn.pay.common.bean.OrderParaStructure; +import com.egzosn.pay.common.http.HttpConfigStorage; +import com.egzosn.pay.common.http.UriVariables; +import com.egzosn.pay.common.util.DateUtils; +import com.egzosn.pay.common.util.str.StringUtils; +import com.egzosn.pay.wx.v3.bean.WxPayScoreTransactionType; +import com.egzosn.pay.wx.v3.bean.payscore.*; +import com.egzosn.pay.wx.v3.utils.WxConst; + +import java.net.URLEncoder; +import java.util.Date; +import java.util.HashMap; +import java.util.Map; +import java.util.TreeMap; + +/** + * 微信支付分API服务 + * + * @author neon + * date 2023/9/12 + */ +public class WxPayScoreService extends WxPayService{ + + public WxPayScoreService(WxPayConfigStorage payConfigStorage) { + super(payConfigStorage); + } + + public WxPayScoreService(WxPayConfigStorage payConfigStorage, HttpConfigStorage configStorage) { + super(payConfigStorage, configStorage); + } + + private Map getPublicParameters() { + Map parameters = new TreeMap<>(); + parameters.put("appid", payConfigStorage.getAppId()); + parameters.put("mch_id", payConfigStorage.getPid()); + parameters.put("service_id", payConfigStorage.getServiceId()); + return parameters; + } + + /** + * 初始化通知URL必须为直接可访问的URL,不允许携带查询串,要求必须为https地址。 + * + * @param parameters 订单参数 + * @param order 订单信息 + */ + public void initNotifyUrl(Map parameters, Order order) { + OrderParaStructure.loadParameters(parameters, WxConst.NOTIFY_URL, payConfigStorage.getNotifyUrl()); + OrderParaStructure.loadParameters(parameters, WxConst.NOTIFY_URL, order); + } + + /** + * 商户预授权API + */ + public Map permissions(String authorizationCode) { + Map parameters = getPublicParameters(); + OrderParaStructure.loadParameters(parameters,WxConst.AUTHORIZATION_CODE,authorizationCode); + OrderParaStructure.loadParameters(parameters, WxConst.NOTIFY_URL, payConfigStorage.getNotifyUrl()); + return getAssistService().doExecute(parameters, WxPayScoreTransactionType.PERMISSIONS); + } + + public Map queryPermissionsByAuthorizationCode(String authorizationCode) { + Map parameters = getPublicParameters(); + OrderParaStructure.loadParameters(parameters,WxConst.AUTHORIZATION_CODE,authorizationCode); + String params = JSON.toJSONString(parameters); + return getAssistService().doExecute(params, WxPayScoreTransactionType.QUERY_PERMISSIONS_AUTHORIZATION_CODE,authorizationCode); + } + + public Map terminatePermissionsByAuthorizationCode(String authorizationCode,String reason) { + Map parameters = getPublicParameters(); + OrderParaStructure.loadParameters(parameters,WxConst.AUTHORIZATION_CODE,authorizationCode); + OrderParaStructure.loadParameters(parameters,"reason",reason); + String params = JSON.toJSONString(parameters); + return getAssistService().doExecute(params, WxPayScoreTransactionType.UNBIND_PERMISSIONS_AUTHORIZATION_CODE,authorizationCode); + } + + public Map queryPermissionsByOpenId(String openId) { + Map parameters = getPublicParameters(); + OrderParaStructure.loadParameters(parameters,"open_id",openId); + String params = JSON.toJSONString(parameters); + return getAssistService().doExecute(params, WxPayScoreTransactionType.QUERY_PERMISSIONS_OPENID,openId); + } + + public Map terminatePermissionsByOpenId(String openId,String reason) { + Map parameters = getPublicParameters(); + OrderParaStructure.loadParameters(parameters,"open_id",openId); + OrderParaStructure.loadParameters(parameters,"reason",reason); + String params = JSON.toJSONString(parameters); + return getAssistService().doExecute(params, WxPayScoreTransactionType.UNBIND_PERMISSIONS_OPENID,openId); + } + + /** + * 微信创建支付分订单 + */ + public Map create(CreateOrder createOrder) { + Map parameters = getPublicParameters(); + OrderParaStructure.loadParameters(parameters,"out_order_no", createOrder.getOutTradeNo()); + OrderParaStructure.loadParameters(parameters,"service_introduction", createOrder.getServiceIntroduction()); + RiskFund riskFund = new RiskFund(); + riskFund.setName(createOrder.getRiskFundName()); + riskFund.setAmount(createOrder.getRiskFundAmount()); + parameters.put("risk_fund",riskFund); + + String attach = createOrder.getAttach(); + if (StringUtils.isNotBlank(attach)) { + String attachEncode = URLEncoder.encode(attach); + OrderParaStructure.loadParameters(parameters,"attach",attachEncode.length() <= 256 ? attachEncode : attachEncode.substring(0, 255)); + } + TimeRange timeRange = new TimeRange(); + timeRange.setStartTime(createOrder.getStartTime()); + timeRange.setEndTime(createOrder.getEndTime()); + parameters.put("time_range", timeRange); + initNotifyUrl(parameters, createOrder); + OrderParaStructure.loadParameters(parameters,"openid", createOrder.getOpenId()); + if (null != createOrder.getNeedUserConfirm()) { + OrderParaStructure.loadParameters(parameters,"need_user_confirm", createOrder.getNeedUserConfirm().toString()); + } + return getAssistService().doExecute(parameters, WxPayScoreTransactionType.CREATE); + } + + /** + * 支付分订单撤销 + */ + @Override + public Map cancel(String orderNo, String reason) { + Map parameters = getPublicParameters(); + reason = reason.length() <= 50 ? reason : reason.substring(0, 50); + parameters.put("reason", reason); + String params = JSON.toJSONString(parameters); + return getAssistService().doExecute(params, WxPayScoreTransactionType.CANCEL, orderNo); + } + + + public Map modify(ModifyOrder modifyOrder) { + Map parameters = getPublicParameters(); + + parameters.put("post_payments", modifyOrder.getPostPayments()); + parameters.put("total_amount", modifyOrder.getTotalAmount()); + OrderParaStructure.loadParameters(parameters,"reason", modifyOrder.getReason()); + String params = JSON.toJSONString(parameters); + return getAssistService().doExecute(params, WxPayScoreTransactionType.MODIFY, modifyOrder.getOutTradeNo()); + } + + + public Map complete(CompleteOrder completeOrder){ + Map parameters = getPublicParameters(); + parameters.put("post_payments", completeOrder.getPostPayments()); + parameters.put("total_amount", completeOrder.getTotalAmount()); + String params = JSON.toJSONString(parameters); + return getAssistService().doExecute(params, WxPayScoreTransactionType.COMPLETE, completeOrder.getOutTradeNo()); + } + + + public Map sync(String outOrderNo, Date payTime){ + Map parameters = getPublicParameters(); + parameters.put("type", "Order_Paid"); + Map detail = new HashMap<>(); + detail.put("paid_time", DateUtils.formatDate(payTime,DateUtils.YYYYMMDDHHMMSS)); + parameters.put("detail", detail); + String params = JSON.toJSONString(parameters); + return getAssistService().doExecute(params, WxPayScoreTransactionType.SYNC,outOrderNo); + } + + + @Override + public Map query(AssistOrder assistOrder){ + String outTradeNo = assistOrder.getOutTradeNo(); + Map publicParameters = getPublicParameters(); + if (StringUtils.isNotBlank(outTradeNo)) { + OrderParaStructure.loadParameters(publicParameters,"out_order_no",outTradeNo); + } + String parameters = UriVariables.getMapToParameters(publicParameters); + return getAssistService().doExecute(parameters, WxPayScoreTransactionType.QUERY); + } +} diff --git a/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/WxPayScoreTransactionType.java b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/WxPayScoreTransactionType.java new file mode 100644 index 0000000..e7b37fc --- /dev/null +++ b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/WxPayScoreTransactionType.java @@ -0,0 +1,54 @@ +package com.egzosn.pay.wx.v3.bean; + +import com.egzosn.pay.common.bean.MethodType; +import com.egzosn.pay.common.bean.TransactionType; + +public enum WxPayScoreTransactionType implements TransactionType { + + + + PERMISSIONS("/v3/payscore/permissions", MethodType.POST), + + QUERY_PERMISSIONS_AUTHORIZATION_CODE("/v3/payscore/permissions/authorization-code/{authorization_code}", MethodType.POST), + + UNBIND_PERMISSIONS_AUTHORIZATION_CODE("/v3/payscore/permissions/authorization-code/{authorization_code}/terminate", MethodType.POST), + + QUERY_PERMISSIONS_OPENID("/v3/payscore/permissions/openid/{openid}", MethodType.POST), + + UNBIND_PERMISSIONS_OPENID("/v3/payscore/permissions/openid/{openid}/terminate", MethodType.POST), + + CREATE("/v3/payscore/serviceorder", MethodType.POST), + + CANCEL("/v3/payscore/serviceorder/{out_order_no}/cancel", MethodType.POST), + + COMPLETE("/v3/payscore/serviceorder/{out_order_no}/complete", MethodType.POST), + + SYNC("/v3/payscore/serviceorder/{out_order_no}/sync", MethodType.POST), + + MODIFY("/v3/payscore/serviceorder/{out_order_no}/modify", MethodType.POST), + + QUERY("/v3/payscore/serviceorder", MethodType.GET), + + ; + + WxPayScoreTransactionType(String type, MethodType method) { + this.type = type; + this.method = method; + } + + private String type; + private MethodType method; + + + + @Override + public String getType() { + return type; + } + + @Override + public String getMethod() { + return this.method.name(); + } + +} diff --git a/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/payscore/CancelOrder.java b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/payscore/CancelOrder.java new file mode 100644 index 0000000..bac21da --- /dev/null +++ b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/payscore/CancelOrder.java @@ -0,0 +1,16 @@ +package com.egzosn.pay.wx.v3.bean.payscore; + +import com.egzosn.pay.common.bean.AssistOrder; + +public class CancelOrder extends AssistOrder { + + private String reason; + + public String getReason() { + return reason; + } + + public void setReason(String reason) { + this.reason = reason; + } +} diff --git a/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/payscore/CompleteOrder.java b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/payscore/CompleteOrder.java new file mode 100644 index 0000000..3f70512 --- /dev/null +++ b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/payscore/CompleteOrder.java @@ -0,0 +1,39 @@ +package com.egzosn.pay.wx.v3.bean.payscore; + +import com.alibaba.fastjson.annotation.JSONField; + +import java.math.BigDecimal; +import java.util.List; + +public class CompleteOrder extends CreateOrder { + + private BigDecimal totalAmount; + @JSONField(name="post_payments") + private List postPayments; + + private Boolean profitSharing = false; + + public BigDecimal getTotalAmount() { + return totalAmount; + } + + public void setTotalAmount(BigDecimal totalAmount) { + this.totalAmount = totalAmount; + } + + public List getPostPayments() { + return postPayments; + } + + public void setPostPayments(List postPayments) { + this.postPayments = postPayments; + } + + public Boolean getProfitSharing() { + return profitSharing; + } + + public void setProfitSharing(Boolean profitSharing) { + this.profitSharing = profitSharing; + } +} diff --git a/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/payscore/CreateOrder.java b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/payscore/CreateOrder.java new file mode 100644 index 0000000..68dd755 --- /dev/null +++ b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/payscore/CreateOrder.java @@ -0,0 +1,95 @@ +package com.egzosn.pay.wx.v3.bean.payscore; + +import com.egzosn.pay.common.bean.AssistOrder; + +import java.math.BigDecimal; + +public class CreateOrder extends AssistOrder { + + private String openId; + + private String serviceIntroduction; + + /** + * 服务开始时间 + * 支持三种格式:yyyyMMddHHmmss、yyyyMMdd和OnAccept + */ + private String startTime; + + private String endTime; + + + private String riskFundName; + + private BigDecimal riskFundAmount; + + private String attach; + + + private Boolean needUserConfirm; + + + public String getOpenId() { + return openId; + } + + public void setOpenId(String openId) { + this.openId = openId; + } + + public String getServiceIntroduction() { + return serviceIntroduction; + } + + public void setServiceIntroduction(String serviceIntroduction) { + this.serviceIntroduction = serviceIntroduction; + } + + public String getStartTime() { + return startTime; + } + + public void setStartTime(String startTime) { + this.startTime = startTime; + } + + public String getEndTime() { + return endTime; + } + + public void setEndTime(String endTime) { + this.endTime = endTime; + } + + public String getRiskFundName() { + return riskFundName; + } + + public void setRiskFundName(String riskFundName) { + this.riskFundName = riskFundName; + } + + public BigDecimal getRiskFundAmount() { + return riskFundAmount; + } + + public void setRiskFundAmount(BigDecimal riskFundAmount) { + this.riskFundAmount = riskFundAmount; + } + + public String getAttach() { + return attach; + } + + public void setAttach(String attach) { + this.attach = attach; + } + + public Boolean getNeedUserConfirm() { + return needUserConfirm; + } + + public void setNeedUserConfirm(Boolean needUserConfirm) { + this.needUserConfirm = needUserConfirm; + } +} diff --git a/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/payscore/ModifyOrder.java b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/payscore/ModifyOrder.java new file mode 100644 index 0000000..3edc086 --- /dev/null +++ b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/payscore/ModifyOrder.java @@ -0,0 +1,42 @@ +package com.egzosn.pay.wx.v3.bean.payscore; + +import com.alibaba.fastjson.annotation.JSONField; +import com.egzosn.pay.common.bean.AssistOrder; + +import java.math.BigDecimal; +import java.util.List; + +public class ModifyOrder extends AssistOrder { + + + private BigDecimal totalAmount; + + @JSONField(name="post_payments") + private List postPayments; + + private String reason; + + public BigDecimal getTotalAmount() { + return totalAmount; + } + + public void setTotalAmount(BigDecimal totalAmount) { + this.totalAmount = totalAmount; + } + + public List getPostPayments() { + return postPayments; + } + + public void setPostPayments(List postPayments) { + this.postPayments = postPayments; + } + + public String getReason() { + return reason; + } + + public void setReason(String reason) { + this.reason = reason; + } +} diff --git a/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/payscore/PostPayment.java b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/payscore/PostPayment.java new file mode 100644 index 0000000..288266c --- /dev/null +++ b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/payscore/PostPayment.java @@ -0,0 +1,59 @@ +package com.egzosn.pay.wx.v3.bean.payscore; + +import java.io.Serializable; +import java.math.BigDecimal; + +public class PostPayment implements Serializable { + + /** + * 付费项目名称 + */ + private String name; + + /** + * 金额 + */ + private BigDecimal amount; + + /** + * 说明 + */ + private String description; + + /** + * 数量 + */ + private Integer count; + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public BigDecimal getAmount() { + return amount; + } + + public void setAmount(BigDecimal amount) { + this.amount = amount; + } + + public String getDescription() { + return description; + } + + public void setDescription(String description) { + this.description = description; + } + + public Integer getCount() { + return count; + } + + public void setCount(Integer count) { + this.count = count; + } +} diff --git a/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/payscore/RiskFund.java b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/payscore/RiskFund.java new file mode 100644 index 0000000..d432948 --- /dev/null +++ b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/payscore/RiskFund.java @@ -0,0 +1,48 @@ +package com.egzosn.pay.wx.v3.bean.payscore; + +import com.egzosn.pay.common.util.Util; + +import java.io.Serializable; +import java.math.BigDecimal; + +public class RiskFund implements Serializable { + + /** + * 风险金名称 + */ + private String name; + + /** + * 风险金额 + */ + private BigDecimal amount; + + /** + * 风险说明 + */ + private String description; + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public Integer getAmount() { + return Util.conversionCentAmount(amount); + } + + public void setAmount(BigDecimal amount) { + this.amount = amount; + } + + public String getDescription() { + return description; + } + + public void setDescription(String description) { + this.description = description; + } +} diff --git a/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/payscore/SyncOrder.java b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/payscore/SyncOrder.java new file mode 100644 index 0000000..333fd3c --- /dev/null +++ b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/payscore/SyncOrder.java @@ -0,0 +1,18 @@ +package com.egzosn.pay.wx.v3.bean.payscore; + +import com.egzosn.pay.common.bean.AssistOrder; + +import java.util.Date; + +public class SyncOrder extends AssistOrder { + + private Date paidTime; + + public Date getPaidTime() { + return paidTime; + } + + public void setPaidTime(Date paidTime) { + this.paidTime = paidTime; + } +} diff --git a/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/payscore/TimeRange.java b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/payscore/TimeRange.java new file mode 100644 index 0000000..02d466a --- /dev/null +++ b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/bean/payscore/TimeRange.java @@ -0,0 +1,30 @@ +package com.egzosn.pay.wx.v3.bean.payscore; + +import com.alibaba.fastjson.annotation.JSONField; + +import java.io.Serializable; + +public class TimeRange implements Serializable { + + @JSONField(name = "start_time") + private String startTime; + + @JSONField(name = "end_time") + private String endTime; + + public String getStartTime() { + return startTime; + } + + public void setStartTime(String startTime) { + this.startTime = startTime; + } + + public String getEndTime() { + return endTime; + } + + public void setEndTime(String endTime) { + this.endTime = endTime; + } +} diff --git a/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/utils/WxConst.java b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/utils/WxConst.java index b491eb5..bbd311d 100644 --- a/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/utils/WxConst.java +++ b/pay-java-wx/src/main/java/com/egzosn/pay/wx/v3/utils/WxConst.java @@ -84,6 +84,5 @@ public final class WxConst { public static final String LIMIT = "limit"; public static final String DETAIL_STATUS = "detail_status"; public static final String WECHATPAY_SERIAL = "Wechatpay-Serial"; - - + public static final String AUTHORIZATION_CODE = "authorization_code"; } -- Gitee From 8cdef6f818b59682d325c16209f4cbc9ea439316 Mon Sep 17 00:00:00 2001 From: egan Date: Mon, 9 Oct 2023 21:45:29 +0800 Subject: [PATCH 295/299] =?UTF-8?q?=E6=94=AF=E4=BB=98=E5=AE=9D=E8=A1=A5?= =?UTF-8?q?=E5=85=85=E8=AE=A2=E5=8D=95=E6=9F=A5=E8=AF=A2=E9=BB=98=E8=AE=A4?= =?UTF-8?q?=E5=80=BC=EF=BC=8C=E8=BD=AC=E8=B4=A6=E8=AE=A2=E5=8D=95=E6=9F=A5?= =?UTF-8?q?=E8=AF=A2=E6=96=B0=E6=8E=A5=E5=8F=A3=E5=AE=9E=E7=8E=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/egzosn/pay/ali/api/AliPayService.java | 47 +++++++++++++------ 1 file changed, 32 insertions(+), 15 deletions(-) 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 8217515..f55316a 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 @@ -31,6 +31,7 @@ import com.egzosn.pay.ali.bean.AliTransferType; import com.egzosn.pay.ali.bean.CertEnvironment; import com.egzosn.pay.ali.bean.OrderSettle; import com.egzosn.pay.common.api.BasePayService; +import com.egzosn.pay.common.api.TransferService; import com.egzosn.pay.common.bean.AssistOrder; import com.egzosn.pay.common.bean.BillType; import com.egzosn.pay.common.bean.MethodType; @@ -62,7 +63,7 @@ import com.egzosn.pay.common.util.str.StringUtils; * email egzosn@gmail.com * date 2017-2-22 20:09 */ -public class AliPayService extends BasePayService implements AliPayServiceInf { +public class AliPayService extends BasePayService implements TransferService, AliPayServiceInf { /** @@ -505,6 +506,9 @@ public class AliPayService extends BasePayService implement */ @Override public Map query(AssistOrder assistOrder) { + if (null == assistOrder.getTransactionType()) { + assistOrder.setTransactionType(AliTransactionType.QUERY); + } //获取公共参数 Map parameters = getPublicParameters(assistOrder.getTransactionType()); Map bizContent = new TreeMap<>(); @@ -739,6 +743,32 @@ public class AliPayService extends BasePayService implement return getHttpRequestTemplate().postForObject(getReqUrl() + "?" + UriVariables.getMapToParameters(parameters), null, JSONObject.class); } + /** + * 转账查询 + * + * @param assistOrder 辅助交易订单 + * @return 对应的转账订单 + */ + @Override + public Map transferQuery(AssistOrder assistOrder) { + //获取公共参数 + Map parameters = getPublicParameters(AliTransferType.TRANS_QUERY); + + Map bizContent = new TreeMap(); + if (StringUtils.isEmpty(assistOrder.getOutTradeNo())) { + bizContent.put("order_id", assistOrder.getTradeNo()); + } + else { + bizContent.put("out_biz_no", assistOrder.getOutTradeNo()); + } + //设置请求参数的集合 + parameters.put(BIZ_CONTENT, JSON.toJSONString(bizContent)); + //设置签名 + setSign(parameters); + return getHttpRequestTemplate().postForObject(getReqUrl() + "?" + UriVariables.getMapToParameters(parameters), null, JSONObject.class); + + } + private Map setPayeeInfo(Map bizContent, Order order) { final Object attr = order.getAttr(PAYEE_INFO); @@ -765,21 +795,8 @@ public class AliPayService extends BasePayService implement */ @Override public Map transferQuery(String outNo, String tradeNo) { - //获取公共参数 - Map parameters = getPublicParameters(AliTransferType.TRANS_QUERY); - Map bizContent = new TreeMap(); - if (StringUtils.isEmpty(outNo)) { - bizContent.put("order_id", tradeNo); - } - else { - bizContent.put("out_biz_no", outNo); - } - //设置请求参数的集合 - parameters.put(BIZ_CONTENT, JSON.toJSONString(bizContent)); - //设置签名 - setSign(parameters); - return getHttpRequestTemplate().postForObject(getReqUrl() + "?" + UriVariables.getMapToParameters(parameters), null, JSONObject.class); + return transferQuery(new AssistOrder(tradeNo, outNo)); } -- Gitee From e7892bc27f4b3507935bd5ae7e1e3c6f8808969c Mon Sep 17 00:00:00 2001 From: egan Date: Mon, 9 Oct 2023 21:59:30 +0800 Subject: [PATCH 296/299] =?UTF-8?q?=E8=BD=AC=E8=B4=A6=E8=AE=A2=E5=8D=95?= =?UTF-8?q?=E6=9F=A5=E8=AF=A2=E6=96=B0=E6=8E=A5=E5=8F=A3=E5=AE=9E=E7=8E=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../pay/payoneer/api/PayoneerPayService.java | 23 ++++++++++--- .../com/egzosn/pay/wx/api/WxPayService.java | 32 +++++++++++++++---- 2 files changed, 45 insertions(+), 10 deletions(-) diff --git a/pay-java-payoneer/src/main/java/com/egzosn/pay/payoneer/api/PayoneerPayService.java b/pay-java-payoneer/src/main/java/com/egzosn/pay/payoneer/api/PayoneerPayService.java index 965b67e..fbc74a9 100644 --- a/pay-java-payoneer/src/main/java/com/egzosn/pay/payoneer/api/PayoneerPayService.java +++ b/pay-java-payoneer/src/main/java/com/egzosn/pay/payoneer/api/PayoneerPayService.java @@ -15,10 +15,10 @@ import org.apache.http.message.BasicHeader; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONObject; import com.egzosn.pay.common.api.BasePayService; +import com.egzosn.pay.common.api.TransferService; import com.egzosn.pay.common.bean.AssistOrder; import com.egzosn.pay.common.bean.BaseRefundResult; import com.egzosn.pay.common.bean.BillType; - import com.egzosn.pay.common.bean.CurType; import com.egzosn.pay.common.bean.DefaultCurType; import com.egzosn.pay.common.bean.MethodType; @@ -52,7 +52,7 @@ import com.egzosn.pay.payoneer.bean.PayoneerTransactionType; * create 2018-01-19 * */ -public class PayoneerPayService extends BasePayService implements AdvancedPayService { +public class PayoneerPayService extends BasePayService implements AdvancedPayService, TransferService { /** * 测试地址 */ @@ -150,6 +150,7 @@ public class PayoneerPayService extends BasePayService im return verify(new NoticeParams(params)); } + /** * 回调校验 * @@ -318,16 +319,18 @@ public class PayoneerPayService extends BasePayService im public Map close(String tradeNo, String outTradeNo) { return secondaryInterface(tradeNo, outTradeNo, PayoneerTransactionType.CHARGE_CANCEL); } + /** * 交易关闭接口 * - * @param assistOrder 关闭订单 + * @param assistOrder 关闭订单 * @return 返回支付方交易关闭后的结果 */ @Override - public Map close(AssistOrder assistOrder){ + public Map close(AssistOrder assistOrder) { return secondaryInterface(assistOrder.getTradeNo(), assistOrder.getOutTradeNo(), PayoneerTransactionType.CHARGE_CANCEL); } + /** * 交易交易撤销 * @@ -465,6 +468,17 @@ public class PayoneerPayService extends BasePayService im return response; } + /** + * 转账查询 + * + * @param assistOrder 辅助交易订单 + * @return 对应的转账订单 + */ + @Override + public Map transferQuery(AssistOrder assistOrder) { + return secondaryInterface(assistOrder.getTradeNo(), assistOrder.getOutTradeNo(), PayoneerTransactionType.PAYOUT_STATUS); + } + /** * 转账 * @@ -482,6 +496,7 @@ public class PayoneerPayService extends BasePayService im * * @return 请求地址 */ + @Override public String getReqUrl(TransactionType type) { return (payConfigStorage.isTest() ? SANDBOX_DOMAIN : RELEASE_DOMAIN) + payConfigStorage.getPid() + "/" + type.getMethod(); } 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 09c7922..fad36ae 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 @@ -40,6 +40,7 @@ import static com.egzosn.pay.wx.bean.WxTransferType.TRANSFERS; import com.alibaba.fastjson.JSONObject; import com.egzosn.pay.common.api.BasePayService; +import com.egzosn.pay.common.api.TransferService; import com.egzosn.pay.common.bean.AssistOrder; import com.egzosn.pay.common.bean.BillType; import com.egzosn.pay.common.bean.MethodType; @@ -86,7 +87,7 @@ import com.egzosn.pay.wx.bean.WxTransferType; * date 2016-5-18 14:09:01 * */ -public class WxPayService extends BasePayService implements WxRedPackService, WxBillService { +public class WxPayService extends BasePayService implements WxRedPackService, WxBillService, TransferService { /** @@ -862,6 +863,7 @@ public class WxPayService extends BasePayService implements return getHttpRequestTemplate().postForObject(getReqUrl(order.getTransferType()), XML.getMap2Xml(parameters), JSONObject.class); } + /** * 转账到余额所需要参数 * @@ -914,18 +916,37 @@ public class WxPayService extends BasePayService implements * 商户企业付款到银行卡 *

* @return 对应的转账订单 + * @deprecated {@link #transferQuery(AssistOrder)} */ + @Deprecated @Override public Map transferQuery(String outNo, String wxTransferType) { + if (StringUtils.isEmpty(wxTransferType)) { + throw new PayErrorException(new WxPayError(FAILURE, "微信转账类型必填,详情com.egzosn.pay.wx.bean.WxTransferType")); + } + AssistOrder assistOrder = new AssistOrder(outNo); + + assistOrder.setTransactionType(WxTransferType.valueOf(wxTransferType)); + return transferQuery(assistOrder); + } + + /** + * 转账查询 + * + * @param assistOrder 辅助交易订单 + * @return 对应的转账订单 + */ + @Override + public Map transferQuery(AssistOrder assistOrder) { Map parameters = new TreeMap(); parameters.put(MCH_ID, payConfigStorage.getPid()); - parameters.put("partner_trade_no", outNo); + parameters.put("partner_trade_no", assistOrder.getOutTradeNo()); parameters.put(NONCE_STR, SignTextUtils.randomStr()); - if (StringUtils.isEmpty(wxTransferType)) { - throw new PayErrorException(new WxPayError(FAILURE, "微信转账类型 #transferQuery(String outNo, String wxTransferType) 必填,详情com.egzosn.pay.wx.bean.WxTransferType")); + if (null == assistOrder.getTransactionType()) { + throw new PayErrorException(new WxPayError(FAILURE, "微信转账类型必填,详情com.egzosn.pay.wx.bean.WxTransferType")); } //如果类型为余额方式 - if (TRANSFERS.getType().equals(wxTransferType) || GETTRANSFERINFO.getType().equals(wxTransferType)) { + if (TRANSFERS == assistOrder.getTransactionType() || GETTRANSFERINFO == assistOrder.getTransactionType()) { parameters.put(APPID, payConfigStorage.getAppId()); parameters.put(SIGN, createSign(SignTextUtils.parameterText(parameters, "&", SIGN), payConfigStorage.getInputCharset())); return getHttpRequestTemplate().postForObject(getReqUrl(GETTRANSFERINFO), XML.getMap2Xml(parameters), JSONObject.class); @@ -935,7 +956,6 @@ public class WxPayService extends BasePayService implements return getHttpRequestTemplate().postForObject(getReqUrl(QUERY_BANK), XML.getMap2Xml(parameters), JSONObject.class); } - private String keyPublic(String content) { try { return RSA2.encrypt(content, payConfigStorage.getKeyPublic(), CIPHER_ALGORITHM, payConfigStorage.getInputCharset()); -- Gitee From 7c76dd206a66c2c6c123ce3dbbdbe0a193a8b5cc Mon Sep 17 00:00:00 2001 From: egan Date: Mon, 9 Oct 2023 22:22:46 +0800 Subject: [PATCH 297/299] =?UTF-8?q?=E8=BD=AC=E8=B4=A6=E8=AE=A2=E5=8D=95?= =?UTF-8?q?=E6=9F=A5=E8=AF=A2=E6=96=B0=E6=8E=A5=E5=8F=A3=E6=A1=88=E4=BE=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/egzosn/pay/demo/controller/AliPayController.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/AliPayController.java b/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/AliPayController.java index 1ca3cc2..4be22b6 100644 --- a/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/AliPayController.java +++ b/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/AliPayController.java @@ -397,6 +397,6 @@ public class AliPayController { */ @RequestMapping("transferQuery") public Map transferQuery(String outNo, String tradeNo) { - return service.transferQuery(outNo, tradeNo); + return service.transferQuery(new AssistOrder(tradeNo, outNo)); } } -- Gitee From 2b47bf95cb1161f338a6db50e74f5aaf4deb66da Mon Sep 17 00:00:00 2001 From: egan Date: Sat, 13 Jan 2024 19:45:08 +0800 Subject: [PATCH 298/299] =?UTF-8?q?#I8VQM7=20paypal=20webhook=E5=9B=9E?= =?UTF-8?q?=E8=B0=83=E9=AA=8C=E7=AD=BE=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../pay/demo/controller/PayPalPayController.java | 4 +++- .../pay/paypal/api/PayPalConfigStorage.java | 15 ++++++++++++++- .../pay/paypal/v2/api/PayPalPayService.java | 9 +++++---- 3 files changed, 22 insertions(+), 6 deletions(-) diff --git a/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/PayPalPayController.java b/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/PayPalPayController.java index e40d037..01af6e9 100644 --- a/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/PayPalPayController.java +++ b/pay-java-demo/src/main/java/com/egzosn/pay/demo/controller/PayPalPayController.java @@ -41,8 +41,10 @@ public class PayPalPayController { @PostConstruct public void init() { PayPalConfigStorage storage = new PayPalConfigStorage(); - storage.setClientID("AZDS0IhUZvJTO99unlvSDMfbZIP-p-UecYXZdJoweha9LFuqKXKcQIGZgfVaX6oGiAOJAUuJD7JwyTl1"); + storage.setClientId("AZDS0IhUZvJTO99unlvSDMfbZIP-p-UecYXZdJoweha9LFuqKXKcQIGZgfVaX6oGiAOJAUuJD7JwyTl1"); storage.setClientSecret("EK2YaOrw3oLSDWIRzvb9BWGTjiPPhY1fFUu5ylhUsGYLc_h_dlpJ0hr_LDEkbO9MyKP2P83YcywbPaem"); + //webhook回调时验签使用必须https://developer.paypal.com/dashboard/webhooksSimulator + storage.setWebHookId("AZDS0IhUZvJTO99unlvSDMfbZIP"); storage.setTest(true); //发起付款后的页面转跳地址 storage.setReturnUrl("http://www.egzosn.com/payPal/payBack.json"); diff --git a/pay-java-paypal/src/main/java/com/egzosn/pay/paypal/api/PayPalConfigStorage.java b/pay-java-paypal/src/main/java/com/egzosn/pay/paypal/api/PayPalConfigStorage.java index 7c267dc..69c5a89 100644 --- a/pay-java-paypal/src/main/java/com/egzosn/pay/paypal/api/PayPalConfigStorage.java +++ b/pay-java-paypal/src/main/java/com/egzosn/pay/paypal/api/PayPalConfigStorage.java @@ -16,6 +16,12 @@ public class PayPalConfigStorage extends BasePayConfigStorage { private String clientId; + /** + * 回调验签使用 + */ + private String webHookId; + + @Override @Deprecated public String getAppid() { @@ -86,7 +92,6 @@ public class PayPalConfigStorage extends BasePayConfigStorage { * 获取取消页面的url *
      * 注意:这里不是异步回调的通知
-     * IPN 地址设置的路径:https://developer.paypal.com/developer/ipnSimulator/
      * 
* * @return 取消页面的url @@ -98,4 +103,12 @@ public class PayPalConfigStorage extends BasePayConfigStorage { public PayPalConfigStorage() { setAccessTokenLock(new ReentrantLock()); } + + public String getWebHookId() { + return webHookId; + } + + public void setWebHookId(String webHookId) { + this.webHookId = webHookId; + } } diff --git a/pay-java-paypal/src/main/java/com/egzosn/pay/paypal/v2/api/PayPalPayService.java b/pay-java-paypal/src/main/java/com/egzosn/pay/paypal/v2/api/PayPalPayService.java index 0679e6a..e6cce20 100644 --- a/pay-java-paypal/src/main/java/com/egzosn/pay/paypal/v2/api/PayPalPayService.java +++ b/pay-java-paypal/src/main/java/com/egzosn/pay/paypal/v2/api/PayPalPayService.java @@ -62,7 +62,7 @@ import com.egzosn.pay.paypal.v2.utils.PayPalUtil; * 贝宝支付配置存储 * * @author egan - * + *

* email egzosn@gmail.com * date 2021-1-16 ‏‎22:15:09 */ @@ -177,6 +177,7 @@ public class PayPalPayService extends BasePayService implem /** * 保留IPN的校验方式 + * * @param noticeParams 参数 * @return 结果 */ @@ -191,6 +192,7 @@ public class PayPalPayService extends BasePayService implem return "VERIFIED".equals(resp); } + @Override public boolean verify(NoticeParams noticeParams) { @@ -208,8 +210,7 @@ public class PayPalPayService extends BasePayService implem } InputStream inputStream = clientCertificateResponseEntity.getBody(); Collection clientCerts = PayPalUtil.getCertificateFromStream(inputStream); - Map body = noticeParams.getBody(); - String webHookId = (String) body.get(Constants.ID); + String webHookId = payConfigStorage.getWebHookId(); String actualSignatureEncoded = noticeParams.getHeader(Constants.PAYPAL_HEADER_TRANSMISSION_SIG); String authAlgo = noticeParams.getHeader(Constants.PAYPAL_HEADER_AUTH_ALGO); String transmissionId = noticeParams.getHeader(Constants.PAYPAL_HEADER_TRANSMISSION_ID); @@ -466,7 +467,7 @@ public class PayPalPayService extends BasePayService implem @Override public Map ordersCapture(String tradeNo) { final HttpHeader header = authHeader(); - header.addHeader(new BasicHeader("Content-Type","application/json")); + header.addHeader(new BasicHeader("Content-Type", "application/json")); JSONObject ordersCaptureInfo = getHttpRequestTemplate().postForObject(getReqUrl(PayPalTransactionType.ORDERS_CAPTURE), header, JSONObject.class, tradeNo); // String captureId = ordersCaptureInfo.getJSONArray("purchaseUnits").getJSONObject(0).getJSONObject("payments").getJSONArray("captures").getJSONObject(0).getString("id"); return ordersCaptureInfo; -- Gitee From cd8f37c930a5119209b1fa7630119064a8d9b9e1 Mon Sep 17 00:00:00 2001 From: egan Date: Sat, 13 Jan 2024 20:05:49 +0800 Subject: [PATCH 299/299] =?UTF-8?q?2.14.7=E7=89=88=E6=9C=AC=E5=8F=91?= =?UTF-8?q?=E5=B8=83?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 2 +- pom.xml | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/README.md b/README.md index 7c75183..228b7c3 100644 --- a/README.md +++ b/README.md @@ -22,7 +22,7 @@ com.egzosn {module-name} - 2.14.6 + 2.14.7 ``` diff --git a/pom.xml b/pom.xml index cf2cacc..1220e08 100644 --- a/pom.xml +++ b/pom.xml @@ -60,7 +60,6 @@ pay-java-yiji pay-java-baidu pay-java-demo - -- Gitee

+ * 企业付款到零钱 + * 商户企业付款到银行卡 + *