From e6e5ae2c2bb3c7108ebf8cfd1db6555a2af9d109 Mon Sep 17 00:00:00 2001 From: bootx Date: Sun, 10 Mar 2024 22:48:18 +0800 Subject: [PATCH 1/9] =?UTF-8?q?doc=20=E6=9B=B4=E6=96=B0=E5=BE=AE=E4=BF=A1?= =?UTF-8?q?=E7=BE=A4=E4=BA=8C=E7=BB=B4=E7=A0=81?= 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 d6c4ad29..3dae1968 100644 --- a/README.md +++ b/README.md @@ -190,10 +190,9 @@ QQ扫码加入QQ交流群 微信扫码加入微信交流群

-微信图片_20240226144703 +微信图片_20240226144703

- ## 🍻 鸣谢 感谢 JetBrains 提供的免费开源 License: From 861b5cbfb3d6579bd35b41512e4b93f3b04bb12f Mon Sep 17 00:00:00 2001 From: xxm1995 Date: Fri, 8 Mar 2024 18:27:27 +0800 Subject: [PATCH 2/9] =?UTF-8?q?feat=20=E4=BA=91=E9=97=AA=E4=BB=98=E5=AF=B9?= =?UTF-8?q?=E6=8E=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- _license/LICENSE.md | 3 + _license/pay-java-parent/LICENSE | 191 +++++++++++++++ .../controller/PayCallbackController.java | 10 +- daxpay-single/daxpay-single-service/pom.xml | 8 +- .../daxpay/service/code/UnionPayCode.java | 3 + .../service/code/UnionPaySignTypeEnum.java | 20 ++ .../sequence/DaxPaySequenceConfiguration.java | 8 + .../service/AliPayReconcileService.java | 7 - .../union/convert/UnionPayConvert.java | 3 + .../channel/union/entity/UnionPayConfig.java | 70 +++++- .../service/UnionPayCallbackService.java | 10 +- .../union/service/UnionPayCloseService.java | 27 +-- .../union/service/UnionPayConfigService.java | 45 ++++ .../service/UnionPayReconcileService.java | 13 + .../union/service/UnionPayRefundService.java | 50 ++-- .../union/service/UnionPayService.java | 226 ++++++------------ .../union/service/UnionPaySyncService.java | 119 ++++----- .../close/strategy/UnionPayCloseStrategy.java | 4 +- .../pay/strategy/UnionPayStrategy.java | 4 +- .../strategy/UnionPayReconcileStrategy.java | 102 ++++++++ .../refund/strategy/UnionRefundStrategy.java | 4 +- .../strategy/pay/UnionPaySyncStrategy.java | 4 +- .../service/sdk/union/api/UnionPayKit.java | 25 ++ .../service/sdk/union/bean/UnionPayOrder.java | 11 + .../sdk/union/bean/UnionRefundOrder.java | 11 + pom.xml | 1 + 26 files changed, 695 insertions(+), 284 deletions(-) create mode 100644 _license/pay-java-parent/LICENSE create mode 100644 daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/code/UnionPaySignTypeEnum.java create mode 100644 daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/payment/reconcile/strategy/UnionPayReconcileStrategy.java create mode 100644 daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/sdk/union/api/UnionPayKit.java create mode 100644 daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/sdk/union/bean/UnionPayOrder.java create mode 100644 daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/sdk/union/bean/UnionRefundOrder.java diff --git a/_license/LICENSE.md b/_license/LICENSE.md index aed2e432..0a680ef7 100644 --- a/_license/LICENSE.md +++ b/_license/LICENSE.md @@ -44,3 +44,6 @@ https://gitee.com/dromara/easy_trans vue3-vant4-mobile Vant4脚手架: https://github.com/xiangshu233/vue3-vant4-mobile + +全能第三方支付对接Java开发工具包: +https://gitee.com/egzosn/pay-java-parent diff --git a/_license/pay-java-parent/LICENSE b/_license/pay-java-parent/LICENSE new file mode 100644 index 00000000..9e66ea75 --- /dev/null +++ b/_license/pay-java-parent/LICENSE @@ -0,0 +1,191 @@ +Apache License +Version 2.0, January 2004 +http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + +"License" shall mean the terms and conditions for use, reproduction, and +distribution as defined by Sections 1 through 9 of this document. + +"Licensor" shall mean the copyright owner or entity authorized by the copyright +owner that is granting the License. + +"Legal Entity" shall mean the union of the acting entity and all other entities +that control, are controlled by, or are under common control with that entity. +For the purposes of this definition, "control" means (i) the power, direct or +indirect, to cause the direction or management of such entity, whether by +contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the +outstanding shares, or (iii) beneficial ownership of such entity. + +"You" (or "Your") shall mean an individual or Legal Entity exercising +permissions granted by this License. + +"Source" form shall mean the preferred form for making modifications, including +but not limited to software source code, documentation source, and configuration +files. + +"Object" form shall mean any form resulting from mechanical transformation or +translation of a Source form, including but not limited to compiled object code, +generated documentation, and conversions to other media types. + +"Work" shall mean the work of authorship, whether in Source or Object form, made +available under the License, as indicated by a copyright notice that is included +in or attached to the work (an example is provided in the Appendix below). + +"Derivative Works" shall mean any work, whether in Source or Object form, that +is based on (or derived from) the Work and for which the editorial revisions, +annotations, elaborations, or other modifications represent, as a whole, an +original work of authorship. For the purposes of this License, Derivative Works +shall not include works that remain separable from, or merely link (or bind by +name) to the interfaces of, the Work and Derivative Works thereof. + +"Contribution" shall mean any work of authorship, including the original version +of the Work and any modifications or additions to that Work or Derivative Works +thereof, that is intentionally submitted to Licensor for inclusion in the Work +by the copyright owner or by an individual or Legal Entity authorized to submit +on behalf of the copyright owner. For the purposes of this definition, +"submitted" means any form of electronic, verbal, or written communication sent +to the Licensor or its representatives, including but not limited to +communication on electronic mailing lists, source code control systems, and +issue tracking systems that are managed by, or on behalf of, the Licensor for +the purpose of discussing and improving the Work, but excluding communication +that is conspicuously marked or otherwise designated in writing by the copyright +owner as "Not a Contribution." + +"Contributor" shall mean Licensor and any individual or Legal Entity on behalf +of whom a Contribution has been received by Licensor and subsequently +incorporated within the Work. + +2. Grant of Copyright License. + +Subject to the terms and conditions of this License, each Contributor hereby +grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, +irrevocable copyright license to reproduce, prepare Derivative Works of, +publicly display, publicly perform, sublicense, and distribute the Work and such +Derivative Works in Source or Object form. + +3. Grant of Patent License. + +Subject to the terms and conditions of this License, each Contributor hereby +grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, +irrevocable (except as stated in this section) patent license to make, have +made, use, offer to sell, sell, import, and otherwise transfer the Work, where +such license applies only to those patent claims licensable by such Contributor +that are necessarily infringed by their Contribution(s) alone or by combination +of their Contribution(s) with the Work to which such Contribution(s) was +submitted. If You institute patent litigation against any entity (including a +cross-claim or counterclaim in a lawsuit) alleging that the Work or a +Contribution incorporated within the Work constitutes direct or contributory +patent infringement, then any patent licenses granted to You under this License +for that Work shall terminate as of the date such litigation is filed. + +4. Redistribution. + +You may reproduce and distribute copies of the Work or Derivative Works thereof +in any medium, with or without modifications, and in Source or Object form, +provided that You meet the following conditions: + +You must give any other recipients of the Work or Derivative Works a copy of +this License; and +You must cause any modified files to carry prominent notices stating that You +changed the files; and +You must retain, in the Source form of any Derivative Works that You distribute, +all copyright, patent, trademark, and attribution notices from the Source form +of the Work, excluding those notices that do not pertain to any part of the +Derivative Works; and +If the Work includes a "NOTICE" text file as part of its distribution, then any +Derivative Works that You distribute must include a readable copy of the +attribution notices contained within such NOTICE file, excluding those notices +that do not pertain to any part of the Derivative Works, in at least one of the +following places: within a NOTICE text file distributed as part of the +Derivative Works; within the Source form or documentation, if provided along +with the Derivative Works; or, within a display generated by the Derivative +Works, if and wherever such third-party notices normally appear. The contents of +the NOTICE file are for informational purposes only and do not modify the +License. You may add Your own attribution notices within Derivative Works that +You distribute, alongside or as an addendum to the NOTICE text from the Work, +provided that such additional attribution notices cannot be construed as +modifying the License. +You may add Your own copyright statement to Your modifications and may provide +additional or different license terms and conditions for use, reproduction, or +distribution of Your modifications, or for any such Derivative Works as a whole, +provided Your use, reproduction, and distribution of the Work otherwise complies +with the conditions stated in this License. + +5. Submission of Contributions. + +Unless You explicitly state otherwise, any Contribution intentionally submitted +for inclusion in the Work by You to the Licensor shall be under the terms and +conditions of this License, without any additional terms or conditions. +Notwithstanding the above, nothing herein shall supersede or modify the terms of +any separate license agreement you may have executed with Licensor regarding +such Contributions. + +6. Trademarks. + +This License does not grant permission to use the trade names, trademarks, +service marks, or product names of the Licensor, except as required for +reasonable and customary use in describing the origin of the Work and +reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. + +Unless required by applicable law or agreed to in writing, Licensor provides the +Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, +including, without limitation, any warranties or conditions of TITLE, +NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are +solely responsible for determining the appropriateness of using or +redistributing the Work and assume any risks associated with Your exercise of +permissions under this License. + +8. Limitation of Liability. + +In no event and under no legal theory, whether in tort (including negligence), +contract, or otherwise, unless required by applicable law (such as deliberate +and grossly negligent acts) or agreed to in writing, shall any Contributor be +liable to You for damages, including any direct, indirect, special, incidental, +or consequential damages of any character arising as a result of this License or +out of the use or inability to use the Work (including but not limited to +damages for loss of goodwill, work stoppage, computer failure or malfunction, or +any and all other commercial damages or losses), even if such Contributor has +been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. + +While redistributing the Work or Derivative Works thereof, You may choose to +offer, and charge a fee for, acceptance of support, warranty, indemnity, or +other liability obligations and/or rights consistent with this License. However, +in accepting such obligations, You may act only on Your own behalf and on Your +sole responsibility, not on behalf of any other Contributor, and only if You +agree to indemnify, defend, and hold each Contributor harmless for any liability +incurred by, or claims asserted against, such Contributor by reason of your +accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work + +To apply the Apache License to your work, attach the following boilerplate +notice, with the fields enclosed by brackets "{}" replaced with your own +identifying information. (Don't include the brackets!) The text should be +enclosed in the appropriate comment syntax for the file format. We also +recommend that a file or class name and description of purpose be included on +the same "printed page" as the copyright notice for easier identification within +third-party archives. + + Copyright 2017 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. \ No newline at end of file diff --git a/daxpay-single/daxpay-single-gateway/src/main/java/cn/bootx/platform/daxpay/gateway/controller/PayCallbackController.java b/daxpay-single/daxpay-single-gateway/src/main/java/cn/bootx/platform/daxpay/gateway/controller/PayCallbackController.java index 0baac59a..bae29e2b 100644 --- a/daxpay-single/daxpay-single-gateway/src/main/java/cn/bootx/platform/daxpay/gateway/controller/PayCallbackController.java +++ b/daxpay-single/daxpay-single-gateway/src/main/java/cn/bootx/platform/daxpay/gateway/controller/PayCallbackController.java @@ -4,6 +4,8 @@ import cn.bootx.platform.common.core.annotation.IgnoreAuth; import cn.bootx.platform.daxpay.service.core.channel.alipay.service.AliPayCallbackService; import cn.bootx.platform.daxpay.service.core.channel.union.service.UnionPayCallbackService; import cn.bootx.platform.daxpay.service.core.channel.wechat.service.WeChatPayCallbackService; +import cn.bootx.platform.daxpay.service.sdk.union.api.UnionPayKit; +import com.egzosn.pay.union.api.UnionPayConfigStorage; import com.ijpay.alipay.AliPayApi; import com.ijpay.core.kit.HttpKit; import com.ijpay.core.kit.WxPayKit; @@ -55,12 +57,14 @@ public class PayCallbackController { return weChatPayCallbackService.callback(params); } + @SuppressWarnings({"unchecked", "rawtypes"}) @SneakyThrows @Operation(summary = "云闪付支付信息回调") @PostMapping("/union") public String unionPayNotify(HttpServletRequest request) { - String xmlMsg = HttpKit.readData(request); - Map params = WxPayKit.xmlToMap(xmlMsg); - return unionPayCallbackService.callback(params); + UnionPayKit unionPayKit = new UnionPayKit(new UnionPayConfigStorage()); + // 实际返回的是 Map 格式数据 + Map parameter2Map = unionPayKit.getParameter2Map(request.getParameterMap(), request.getInputStream()); + return unionPayCallbackService.callback(parameter2Map); } } diff --git a/daxpay-single/daxpay-single-service/pom.xml b/daxpay-single/daxpay-single-service/pom.xml index e3d81187..f24b058b 100644 --- a/daxpay-single/daxpay-single-service/pom.xml +++ b/daxpay-single/daxpay-single-service/pom.xml @@ -100,11 +100,11 @@ ${wxjava.version} - + - com.github.javen205 - IJPay-UnionPay - ${IJPay.version} + com.egzosn + pay-java-union + ${egzosn-java.version} diff --git a/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/code/UnionPayCode.java b/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/code/UnionPayCode.java index 7813fd1a..81e245e5 100644 --- a/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/code/UnionPayCode.java +++ b/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/code/UnionPayCode.java @@ -64,4 +64,7 @@ public interface UnionPayCode { String ERR_MSG = "err_msg"; + /** 对账单下载类型编码 */ + String RECONCILE_BILL_TYPE = "00"; + } diff --git a/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/code/UnionPaySignTypeEnum.java b/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/code/UnionPaySignTypeEnum.java new file mode 100644 index 00000000..cf7b6db4 --- /dev/null +++ b/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/code/UnionPaySignTypeEnum.java @@ -0,0 +1,20 @@ +package cn.bootx.platform.daxpay.service.code; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** + * 云闪付签名类型 + * @author xxm + * @since 2024/3/8 + */ +@Getter +@AllArgsConstructor +public enum UnionPaySignTypeEnum { + + RSA2("RSA2","RSA2"); + + + private final String code; + private final String name; +} diff --git a/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/configuration/sequence/DaxPaySequenceConfiguration.java b/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/configuration/sequence/DaxPaySequenceConfiguration.java index d4966a79..fa56036f 100644 --- a/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/configuration/sequence/DaxPaySequenceConfiguration.java +++ b/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/configuration/sequence/DaxPaySequenceConfiguration.java @@ -31,4 +31,12 @@ public class DaxPaySequenceConfiguration { public Sequence wechatReconcileSequence(SeqRangeManager seqRangeManager) { return SequenceUtil.create(1,1,1,"WechatReconcileSequence"); } + + /** + * 云闪付对账单序列生成器 + */ + @Bean + public Sequence unionPayReconcileSequence(SeqRangeManager seqRangeManager) { + return SequenceUtil.create(1,1,1,"UnionPayReconcileSequence"); + } } diff --git a/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/channel/alipay/service/AliPayReconcileService.java b/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/channel/alipay/service/AliPayReconcileService.java index 8057bdb0..0a796519 100644 --- a/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/channel/alipay/service/AliPayReconcileService.java +++ b/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/channel/alipay/service/AliPayReconcileService.java @@ -5,10 +5,8 @@ import cn.bootx.platform.daxpay.code.ReconcileTradeEnum; import cn.bootx.platform.daxpay.exception.pay.PayFailureException; import cn.bootx.platform.daxpay.service.code.AliPayCode; import cn.bootx.platform.daxpay.service.common.local.PaymentContextLocal; -import cn.bootx.platform.daxpay.service.core.channel.alipay.dao.AliPayRecordManager; import cn.bootx.platform.daxpay.service.core.channel.alipay.dao.AliReconcileBillDetailManager; import cn.bootx.platform.daxpay.service.core.channel.alipay.dao.AliReconcileBillTotalManager; -import cn.bootx.platform.daxpay.service.core.channel.alipay.entity.AliPayConfig; import cn.bootx.platform.daxpay.service.core.channel.alipay.entity.AliReconcileBillDetail; import cn.bootx.platform.daxpay.service.core.channel.alipay.entity.AliReconcileBillTotal; import cn.bootx.platform.daxpay.service.core.order.reconcile.entity.ReconcileDetail; @@ -48,14 +46,11 @@ import java.util.stream.Collectors; @Service @RequiredArgsConstructor public class AliPayReconcileService { - private final AliPayConfigService configService; private final AliReconcileBillDetailManager reconcileBillDetailManager; private final AliReconcileBillTotalManager reconcileBillTotalManager; - private final AliPayRecordManager recordManager; - /** * 下载对账单, 并进行解析进行保存 * @@ -65,8 +60,6 @@ public class AliPayReconcileService { @SneakyThrows @Transactional(rollbackFor = Exception.class) public void downAndSave(String date, Long recordOrderId){ - AliPayConfig config = configService.getConfig(); - configService.initConfig(config); try { AlipayDataDataserviceBillDownloadurlQueryModel model = new AlipayDataDataserviceBillDownloadurlQueryModel(); diff --git a/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/channel/union/convert/UnionPayConvert.java b/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/channel/union/convert/UnionPayConvert.java index 81f27622..e26e41cf 100644 --- a/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/channel/union/convert/UnionPayConvert.java +++ b/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/channel/union/convert/UnionPayConvert.java @@ -2,6 +2,7 @@ package cn.bootx.platform.daxpay.service.core.channel.union.convert; import cn.bootx.platform.daxpay.service.core.channel.union.entity.UnionPayConfig; import cn.bootx.platform.daxpay.service.core.channel.union.entity.UnionPayRecord; +import cn.bootx.platform.daxpay.service.core.payment.reconcile.domain.GeneralReconcileRecord; import cn.bootx.platform.daxpay.service.dto.channel.union.UnionPayConfigDto; import cn.bootx.platform.daxpay.service.dto.channel.union.UnionPayRecordDto; import org.mapstruct.Mapper; @@ -20,4 +21,6 @@ public interface UnionPayConvert { UnionPayRecordDto convert(UnionPayRecord in); + GeneralReconcileRecord convertReconcileRecord(UnionPayRecord in); + } diff --git a/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/channel/union/entity/UnionPayConfig.java b/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/channel/union/entity/UnionPayConfig.java index bff8023e..0952a8ba 100644 --- a/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/channel/union/entity/UnionPayConfig.java +++ b/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/channel/union/entity/UnionPayConfig.java @@ -1,8 +1,11 @@ package cn.bootx.platform.daxpay.service.core.channel.union.entity; +import cn.bootx.platform.common.core.annotation.BigField; +import cn.bootx.platform.common.core.annotation.EncryptionField; import cn.bootx.platform.common.core.function.EntityBaseFunction; import cn.bootx.platform.common.mybatisplus.base.MpBaseEntity; import cn.bootx.platform.common.mybatisplus.handler.StringListTypeHandler; +import cn.bootx.platform.daxpay.service.code.UnionPaySignTypeEnum; import cn.bootx.platform.daxpay.service.core.channel.union.convert.UnionPayConvert; import cn.bootx.platform.daxpay.service.dto.channel.union.UnionPayConfigDto; import cn.bootx.table.modify.annotation.DbColumn; @@ -38,9 +41,61 @@ public class UnionPayConfig extends MpBaseEntity implements EntityBaseFunction 本网关进行处理 -> 重定向到业务系统中 + */ + @DbColumn(comment = "同步通知页面路径") + private String returnUrl; + /** 可用支付方式 */ @DbColumn(comment = "可用支付方式") @DbMySqlFieldType(MySqlFieldTypeEnum.LONGTEXT) diff --git a/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/channel/union/service/UnionPayCallbackService.java b/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/channel/union/service/UnionPayCallbackService.java index ff278d35..f7eabb22 100644 --- a/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/channel/union/service/UnionPayCallbackService.java +++ b/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/channel/union/service/UnionPayCallbackService.java @@ -8,10 +8,11 @@ import cn.bootx.platform.daxpay.service.common.context.CallbackLocal; import cn.bootx.platform.daxpay.service.common.local.PaymentContextLocal; import cn.bootx.platform.daxpay.service.core.channel.union.entity.UnionPayConfig; import cn.bootx.platform.daxpay.service.func.AbsCallbackStrategy; +import cn.bootx.platform.daxpay.service.sdk.union.api.UnionPayKit; import cn.hutool.core.date.DatePattern; import cn.hutool.core.util.StrUtil; import cn.hutool.json.JSONUtil; -import com.ijpay.core.enums.SignType; +import com.egzosn.pay.common.bean.NoticeParams; import com.ijpay.core.kit.WxPayKit; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; @@ -19,6 +20,7 @@ import org.springframework.stereotype.Service; import javax.annotation.Resource; import java.time.LocalDateTime; +import java.util.Collections; import java.util.Map; import java.util.Objects; @@ -57,13 +59,15 @@ public class UnionPayCallbackService extends AbsCallbackStrategy { // 支付回调信息校验 UnionPayConfig config = unionPayConfigService.getConfig(); + UnionPayKit unionPayKit = unionPayConfigService.initPayService(config); if (Objects.isNull(config)) { log.warn("云闪付支付配置不存在"); return false; } - // 注意此处签名方式需与统一下单的签名类型一致 - return WxPayKit.verifyNotify(params, config.getAppKey(), SignType.MD5); + NoticeParams noticeParams = new NoticeParams(); + noticeParams.setBody(Collections.unmodifiableMap(params)); + return unionPayKit.verify(noticeParams); } /** diff --git a/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/channel/union/service/UnionPayCloseService.java b/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/channel/union/service/UnionPayCloseService.java index 7f10649d..42b1b7e9 100644 --- a/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/channel/union/service/UnionPayCloseService.java +++ b/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/channel/union/service/UnionPayCloseService.java @@ -2,14 +2,10 @@ package cn.bootx.platform.daxpay.service.core.channel.union.service; import cn.bootx.platform.daxpay.exception.pay.PayFailureException; import cn.bootx.platform.daxpay.service.code.UnionPayCode; -import cn.bootx.platform.daxpay.service.core.channel.union.entity.UnionPayConfig; import cn.bootx.platform.daxpay.service.core.order.pay.entity.PayOrder; +import cn.bootx.platform.daxpay.service.sdk.union.api.UnionPayKit; import cn.hutool.core.util.StrUtil; -import com.ijpay.core.enums.SignType; -import com.ijpay.core.kit.WxPayKit; -import com.ijpay.unionpay.enums.ServiceEnum; -import com.ijpay.unionpay.model.CloseOrderModel; -import com.ijpay.wxpay.WxPayApi; +import com.egzosn.pay.common.bean.AssistOrder; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; @@ -32,17 +28,14 @@ public class UnionPayCloseService { /** * 关闭订单 */ - public void close(PayOrder payOrder, UnionPayConfig unionPayConfig) { - Map params = CloseOrderModel.builder() - .service(ServiceEnum.CLOSE.toString()) - .mch_id(unionPayConfig.getMachId()) - .out_trade_no(String.valueOf(payOrder.getId())) - .nonce_str(WxPayKit.generateStr()) - .build() - .createSign(unionPayConfig.getAppKey(), SignType.HMACSHA256); - String xmlResult = WxPayApi.closeOrder(params); - Map result = WxPayKit.xmlToMap(xmlResult); - this.verifyErrorMsg(result); + public void close(PayOrder payOrder, UnionPayKit unionPayKit) { + AssistOrder closeOrder = new AssistOrder(); + + closeOrder.setOutTradeNo(String.valueOf(payOrder.getId())); + + Map result = unionPayKit.close(closeOrder); + +// this.verifyErrorMsg(result); } /** diff --git a/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/channel/union/service/UnionPayConfigService.java b/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/channel/union/service/UnionPayConfigService.java index a3454fd5..74ab897b 100644 --- a/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/channel/union/service/UnionPayConfigService.java +++ b/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/channel/union/service/UnionPayConfigService.java @@ -9,8 +9,12 @@ import cn.bootx.platform.daxpay.service.core.channel.union.dao.UnionPayConfigMan import cn.bootx.platform.daxpay.service.core.channel.union.entity.UnionPayConfig; import cn.bootx.platform.daxpay.service.core.system.config.service.PayChannelConfigService; import cn.bootx.platform.daxpay.service.param.channel.alipay.AliPayConfigParam; +import cn.bootx.platform.daxpay.service.sdk.union.api.UnionPayKit; import cn.hutool.core.bean.BeanUtil; import cn.hutool.core.bean.copier.CopyOptions; +import com.egzosn.pay.common.bean.CertStoreType; +import com.egzosn.pay.common.http.HttpConfigStorage; +import com.egzosn.pay.union.api.UnionPayConfigStorage; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; @@ -77,4 +81,45 @@ public class UnionPayConfigService { return unionPayConfig; } + + /** + * 生成云闪付支付服务 + */ + public UnionPayKit initPayService(UnionPayConfig config){ + UnionPayConfigStorage unionPayConfigStorage = new UnionPayConfigStorage(); + unionPayConfigStorage.setMerId(config.getMachId()); + //是否为证书签名 + unionPayConfigStorage.setCertSign(config.isCertSign()); + + //中级证书 证书字符串信息 + unionPayConfigStorage.setAcpMiddleCert(config.getAcpMiddleCert()); + //根证书路径 证书字符串信息 + unionPayConfigStorage.setAcpRootCert(config.getAcpRootCert()); + // 私钥证书路径 证书字符串信息 + unionPayConfigStorage.setKeyPrivateCert(config.getKeyPrivateCert()); + //私钥证书对应的密码 私钥证书对应的密码 + unionPayConfigStorage.setKeyPrivateCertPwd(config.getKeyPrivateCertPwd()); + //设置证书对应的存储方式,证书字符串信息 + unionPayConfigStorage.setCertStoreType(CertStoreType.STR); + + // 回调地址 + unionPayConfigStorage.setNotifyUrl(config.getNotifyUrl()); + // 同步回调可不填 + unionPayConfigStorage.setReturnUrl(config.getReturnUrl()); + unionPayConfigStorage.setSignType(config.signType); + //是否为测试账号,沙箱环境 + unionPayConfigStorage.setTest(config.isSandbox()); + + // 网络请求配置 + HttpConfigStorage httpConfigStorage = new HttpConfigStorage(); + httpConfigStorage.setCertStoreType(CertStoreType.STR); + //最大连接数 + httpConfigStorage.setMaxTotal(20); + //默认的每个路由的最大连接数 + httpConfigStorage.setDefaultMaxPerRoute(10); + + // 创建支付服务 + return new UnionPayKit(unionPayConfigStorage, httpConfigStorage); + } + } diff --git a/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/channel/union/service/UnionPayReconcileService.java b/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/channel/union/service/UnionPayReconcileService.java index 7deb0a9f..d6abb838 100644 --- a/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/channel/union/service/UnionPayReconcileService.java +++ b/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/channel/union/service/UnionPayReconcileService.java @@ -1,9 +1,15 @@ package cn.bootx.platform.daxpay.service.core.channel.union.service; +import cn.bootx.platform.daxpay.service.sdk.union.api.UnionPayKit; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; +import java.util.Date; +import java.util.Map; + +import static cn.bootx.platform.daxpay.service.code.UnionPayCode.RECONCILE_BILL_TYPE; + /** * 云闪付对账 * @author xxm @@ -14,4 +20,11 @@ import org.springframework.stereotype.Service; @RequiredArgsConstructor public class UnionPayReconcileService { + /** + * 下载对账单 + */ + public void downAndSave(Date date, Long recordOrderId, UnionPayKit unionPayKit){ + // 下载对账单 + Map stringObjectMap = unionPayKit.downloadBill(date, RECONCILE_BILL_TYPE); + } } diff --git a/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/channel/union/service/UnionPayRefundService.java b/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/channel/union/service/UnionPayRefundService.java index 4e277619..912c976b 100644 --- a/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/channel/union/service/UnionPayRefundService.java +++ b/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/channel/union/service/UnionPayRefundService.java @@ -1,23 +1,20 @@ package cn.bootx.platform.daxpay.service.core.channel.union.service; -import cn.bootx.platform.daxpay.code.RefundStatusEnum; import cn.bootx.platform.daxpay.exception.pay.PayFailureException; import cn.bootx.platform.daxpay.service.code.UnionPayCode; import cn.bootx.platform.daxpay.service.common.context.RefundLocal; import cn.bootx.platform.daxpay.service.common.local.PaymentContextLocal; -import cn.bootx.platform.daxpay.service.core.channel.union.entity.UnionPayConfig; import cn.bootx.platform.daxpay.service.core.order.pay.entity.PayChannelOrder; import cn.bootx.platform.daxpay.service.core.order.refund.entity.RefundOrder; +import cn.bootx.platform.daxpay.service.sdk.union.api.UnionPayKit; +import cn.bootx.platform.daxpay.service.sdk.union.bean.UnionRefundOrder; import cn.hutool.core.util.StrUtil; -import com.ijpay.core.enums.SignType; -import com.ijpay.core.kit.WxPayKit; -import com.ijpay.unionpay.UnionPayApi; -import com.ijpay.unionpay.enums.ServiceEnum; -import com.ijpay.unionpay.model.RefundModel; +import com.egzosn.pay.union.bean.UnionRefundResult; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; +import java.math.BigDecimal; import java.util.Map; import java.util.Objects; import java.util.Optional; @@ -34,30 +31,29 @@ import static cn.bootx.platform.daxpay.service.code.UnionPayCode.*; @RequiredArgsConstructor public class UnionPayRefundService { - /** * 退款方法 */ - public void refund(RefundOrder refundOrder, int amount, PayChannelOrder channelOrder, UnionPayConfig unionPayConfig) { - Map params = RefundModel.builder() - .service(ServiceEnum.REFUND.toString()) - .mch_id(unionPayConfig.getMachId()) - .out_trade_no(String.valueOf(refundOrder.getPaymentId())) - .out_refund_no(String.valueOf(refundOrder.getId())) - .total_fee(String.valueOf(channelOrder.getAmount())) - .refund_fee(String.valueOf(amount)) - .op_user_id(unionPayConfig.getMachId()) - .nonce_str(WxPayKit.generateStr()) - .build() - .createSign(unionPayConfig.getAppKey(), SignType.MD5); + public void refund(RefundOrder refundOrder, int amount, PayChannelOrder channelOrder, UnionPayKit unionPayKit) { - String xmlResult = UnionPayApi.execution(unionPayConfig.getServerUrl(), params); - Map result = WxPayKit.xmlToMap(xmlResult); - this.verifyErrorMsg(result); - // 云闪付退款是否成功需要查询状态, 所以设置为退款中状态 - RefundLocal refundInfo = PaymentContextLocal.get().getRefundInfo(); - refundInfo.setStatus(RefundStatusEnum.PROGRESS) - .setGatewayOrderNo(result.get(REFUND_ID)); + // 金额转换 + BigDecimal refundAmount = BigDecimal.valueOf(amount * 0.01); + BigDecimal orderAmount = BigDecimal.valueOf(channelOrder.getAmount() * 0.01); + + UnionRefundOrder unionRefundOrder = new UnionRefundOrder(); + unionRefundOrder.setOutTradeNo(String.valueOf(refundOrder.getPaymentId())); + unionRefundOrder.setRefundNo(String.valueOf(refundOrder.getId())); + unionRefundOrder.setRefundAmount(refundAmount); + unionRefundOrder.setTotalAmount(orderAmount); + UnionRefundResult refund = unionPayKit.refund(unionRefundOrder); + +// String xmlResult = UnionPayApi.execution(unionPayConfig.getServerUrl(), params); +// Map result = WxPayKit.xmlToMap(xmlResult); +// this.verifyErrorMsg(result); +// // 云闪付退款是否成功需要查询状态, 所以设置为退款中状态 +// RefundLocal refundInfo = PaymentContextLocal.get().getRefundInfo(); +// refundInfo.setStatus(RefundStatusEnum.PROGRESS) +// .setGatewayOrderNo(result.get(REFUND_ID)); } /** diff --git a/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/channel/union/service/UnionPayService.java b/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/channel/union/service/UnionPayService.java index c024d7ac..43f0dbf2 100644 --- a/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/channel/union/service/UnionPayService.java +++ b/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/channel/union/service/UnionPayService.java @@ -10,19 +10,17 @@ import cn.bootx.platform.daxpay.service.common.context.PayLocal; import cn.bootx.platform.daxpay.service.common.local.PaymentContextLocal; import cn.bootx.platform.daxpay.service.core.channel.union.entity.UnionPayConfig; import cn.bootx.platform.daxpay.service.core.order.pay.entity.PayOrder; -import cn.bootx.platform.daxpay.util.PayUtil; +import cn.bootx.platform.daxpay.service.sdk.union.api.UnionPayKit; +import cn.bootx.platform.daxpay.service.sdk.union.bean.UnionPayOrder; import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.date.DateUtil; import cn.hutool.core.util.StrUtil; -import com.ijpay.core.enums.SignType; -import com.ijpay.core.kit.WxPayKit; -import com.ijpay.unionpay.UnionPayApi; -import com.ijpay.unionpay.enums.ServiceEnum; -import com.ijpay.unionpay.model.MicroPayModel; -import com.ijpay.unionpay.model.UnifiedOrderModel; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; +import java.math.BigDecimal; +import java.util.Date; import java.util.Map; import java.util.Objects; import java.util.Optional; @@ -59,180 +57,110 @@ public class UnionPayService { /** * 支付接口 */ - public void pay(PayOrder payOrder, PayChannelParam payChannelParam, UnionPayParam unionPayParam, UnionPayConfig unionPayConfig){ + public void pay(PayOrder payOrder, PayChannelParam payChannelParam, UnionPayParam unionPayParam, UnionPayKit unionPayKit){ Integer amount = payChannelParam.getAmount(); - String totalFee = String.valueOf(amount); + BigDecimal totalFee = BigDecimal.valueOf(amount * 0.01); PayLocal asyncPayInfo = PaymentContextLocal.get().getPayInfo();; String payBody = null; PayWayEnum payWayEnum = PayWayEnum.findByCode(payChannelParam.getWay()); - // 微信APP支付 - if (payWayEnum == PayWayEnum.APP) { - payBody = this.wxAppPay(totalFee, payOrder, unionPayParam, unionPayConfig); - } - // 微信公众号支付或者小程序支付 - else if (payWayEnum == PayWayEnum.JSAPI_WX_PAY) { - payBody = this.wxJsPay(totalFee, payOrder, unionPayParam.getOpenId(), unionPayConfig); - } - // 支付宝JS支付 - else if (payWayEnum == PayWayEnum.JSAPI_ALI_PAY) { - payBody = this.aliJsPay(totalFee, payOrder, unionPayParam, unionPayConfig); - } - // 银联JS支付 - else if (payWayEnum == PayWayEnum.JSAPI) { - payBody = this.unionJsPay(totalFee, payOrder, unionPayParam, unionPayConfig); - } // 二维码支付 - else if (payWayEnum == PayWayEnum.QRCODE) { - payBody = this.qrCodePay(totalFee, payOrder, unionPayConfig); + if (payWayEnum == PayWayEnum.QRCODE) { + payBody = this.qrCodePay(totalFee, payOrder, unionPayKit); } // 付款码支付 else if (payWayEnum == PayWayEnum.BARCODE) { - this.barCodePay(totalFee, payOrder, unionPayParam.getAuthCode(), unionPayConfig); + this.barCodePay(totalFee, payOrder, unionPayParam.getAuthCode(), unionPayKit); + } + // APP支付 + else if (payWayEnum == PayWayEnum.APP) { + payBody = this.appPay(totalFee, payOrder, unionPayParam, unionPayKit); } asyncPayInfo.setPayBody(payBody); } /** - * 支付宝生活号支付 + * APP支付 */ - private String aliJsPay(String amount, PayOrder payOrder, UnionPayParam unionPayParam, UnionPayConfig unionPayConfig) { - Map params = UnifiedOrderModel.builder() - .service(ServiceEnum.ALI_PAY_JS_PAY.toString()) - .mch_id(unionPayConfig.getMachId()) - .out_trade_no(WxPayKit.generateStr()) - .body(payOrder.getTitle()) - .total_fee(amount) - .mch_create_ip("127.0.0.15") - .notify_url(unionPayConfig.getNotifyUrl()) - .nonce_str(WxPayKit.generateStr()) - .buyer_id(unionPayParam.getBuyerId()) - .build() - .createSign(unionPayConfig.getAppKey(), SignType.MD5); + private String appPay(BigDecimal amount, PayOrder payOrder, UnionPayParam unionPayParam, UnionPayKit unionPayKit) { - String xmlResult = UnionPayApi.execution(unionPayConfig.getServerUrl(), params); - Map result = WxPayKit.xmlToMap(xmlResult); - this.verifyErrorMsg(result); - return null; - } + Date expiredTime = DateUtil.date(payOrder.getExpiredTime()); - /** - * 银联JS支付 - */ - private String unionJsPay(String amount, PayOrder payOrder, UnionPayParam unionPayParam, UnionPayConfig unionPayConfig) { - Map params = UnifiedOrderModel.builder() - .service(ServiceEnum.UNION_JS_PAY.toString()) - .mch_id(unionPayConfig.getMachId()) - .out_trade_no(WxPayKit.generateStr()) - .body(payOrder.getTitle()) - .user_id(unionPayParam.getUserId()) - .customer_ip(unionPayParam.getCustomerIp()) - .total_fee(amount) - .mch_create_ip("127.0.0.1") - .notify_url(unionPayConfig.getNotifyUrl()) - .nonce_str(WxPayKit.generateStr()) - .build() - .createSign(unionPayConfig.getAppKey(), SignType.MD5); - System.out.println(params); + UnionPayOrder unionPayOrder = new UnionPayOrder(); + unionPayOrder.setOutTradeNo(String.valueOf(payOrder.getId())); + unionPayOrder.setSubject(payOrder.getTitle()); + unionPayOrder.setPrice(amount); + unionPayOrder.setExpirationTime(expiredTime); - String xmlResult = UnionPayApi.execution(unionPayConfig.getServerUrl(), params); - Map result = WxPayKit.xmlToMap(xmlResult); - this.verifyErrorMsg(result); - return null; - } - - /** - * 微信APP支付 - */ - private String wxAppPay(String amount, PayOrder payOrder, UnionPayParam unionPayParam, UnionPayConfig unionPayConfig) { - Map params = UnifiedOrderModel.builder() - .service(ServiceEnum.WEI_XIN_APP_PAY.toString()) - .mch_id(unionPayConfig.getMachId()) - .appid(unionPayParam.getAppId()) - .sub_appid(unionPayParam.getSubAppId()) - .out_trade_no(WxPayKit.generateStr()) - .body(payOrder.getTitle()) - .attach("聚合支付 SDK") - .total_fee(amount) - .mch_create_ip("127.0.0.1") - .notify_url(unionPayConfig.getNotifyUrl()) - .nonce_str(WxPayKit.generateStr()) - .build() - .createSign(unionPayConfig.getAppKey(), SignType.MD5); - - String xmlResult = UnionPayApi.execution(unionPayConfig.getServerUrl(), params); - Map result = WxPayKit.xmlToMap(xmlResult); - this.verifyErrorMsg(result); - return null; - } - - /** - * 微信公众号支付或者小程序支付 - */ - private String wxJsPay(String amount, PayOrder payOrder, String openId, UnionPayConfig unionPayConfig) { - - Map params = UnifiedOrderModel.builder() - .service(ServiceEnum.WEI_XIN_JS_PAY.toString()) - .mch_id(unionPayConfig.getMachId()) - // 原生JS 值为1 - .is_raw("1") - .out_trade_no(WxPayKit.generateStr()) - .body(payOrder.getTitle()) - .sub_openid(openId) - .total_fee(amount) - .mch_create_ip("127.0.0.1") - .notify_url(unionPayConfig.getNotifyUrl()) - .nonce_str(WxPayKit.generateStr()) - .build() - .createSign(unionPayConfig.getAppKey(), SignType.MD5); - String xmlResult = UnionPayApi.execution(unionPayConfig.getServerUrl(), params); - Map result = WxPayKit.xmlToMap(xmlResult); - this.verifyErrorMsg(result); + Map app = unionPayKit.app(unionPayOrder); return null; } /** * 扫码支付 */ - private String qrCodePay(String amount, PayOrder payOrder, UnionPayConfig config){ - Map params = UnifiedOrderModel.builder() - .service(ServiceEnum.NATIVE.toString()) - .mch_id(config.getMachId()) - .out_trade_no(String.valueOf(payOrder.getId())) - .body(payOrder.getTitle()) - .total_fee(amount) - .time_expire(PayUtil.getUnionExpiredTime(payOrder.getExpiredTime())) - .mch_create_ip("127.0.0.1") - .notify_url(config.getNotifyUrl()) - .nonce_str(WxPayKit.generateStr()) - .build() - .createSign(config.getAppKey(), SignType.MD5); - String xmlResult = UnionPayApi.execution(config.getServerUrl(), params); - Map result = WxPayKit.xmlToMap(xmlResult); - this.verifyErrorMsg(result); - return result.get("code_url"); + private String qrCodePay(BigDecimal amount, PayOrder payOrder, UnionPayKit unionPayKit){ + Date expiredTime = DateUtil.date(payOrder.getExpiredTime()); + + UnionPayOrder unionPayOrder = new UnionPayOrder(); + + unionPayOrder.setOutTradeNo(String.valueOf(payOrder.getId())); + unionPayOrder.setSubject(payOrder.getTitle()); + unionPayOrder.setPrice(amount); + unionPayOrder.setExpirationTime(expiredTime); + + return unionPayKit.getQrPay(unionPayOrder); + + +// Map params = UnifiedOrderModel.builder() +// .service(ServiceEnum.NATIVE.toString()) +// .mch_id(unionPayKit.getMachId()) +// .out_trade_no(String.valueOf(payOrder.getId())) +// .body(payOrder.getTitle()) +// .total_fee(amount) +// .time_expire(PayUtil.getUnionExpiredTime(payOrder.getExpiredTime())) +// .mch_create_ip("127.0.0.1") +// .notify_url(unionPayKit.getNotifyUrl()) +// .nonce_str(WxPayKit.generateStr()) +// .build() +// .createSign(unionPayKit.getAppKey(), SignType.MD5); +// String xmlResult = UnionPayApi.execution(unionPayKit.getServerUrl(), params); +// Map result = WxPayKit.xmlToMap(xmlResult); +// this.verifyErrorMsg(result); +// return result.get("code_url"); } /** * 付款码支付 */ - private void barCodePay(String amount, PayOrder payOrder, String authCode, UnionPayConfig unionPayConfig) { - Map params = MicroPayModel.builder() - .service(ServiceEnum.MICRO_PAY.toString()) - .mch_id(unionPayConfig.getMachId()) - .out_trade_no(WxPayKit.generateStr()) - .body(payOrder.getTitle()) - .total_fee(amount) - .op_device_id("daxpay") - .mch_create_ip("127.0.0.1") - .auth_code(authCode) - .nonce_str(WxPayKit.generateStr()) - .build() - .createSign(unionPayConfig.getAppKey(), SignType.MD5); + private void barCodePay(BigDecimal amount, PayOrder payOrder, String authCode, UnionPayKit unionPayKit) { + Date expiredTime = DateUtil.date(payOrder.getExpiredTime()); - String xmlResult = UnionPayApi.execution(unionPayConfig.getServerUrl(), params); + UnionPayOrder unionPayOrder = new UnionPayOrder(); + + unionPayOrder.setAuthCode(authCode); + unionPayOrder.setOutTradeNo(String.valueOf(payOrder.getId())); + unionPayOrder.setSubject(payOrder.getTitle()); + unionPayOrder.setPrice(amount); + unionPayOrder.setExpirationTime(expiredTime); + Map stringObjectMap = unionPayKit.microPay(unionPayOrder); + +// Map params = MicroPayModel.builder() +// .service(ServiceEnum.MICRO_PAY.toString()) +// .mch_id(unionPayKit.getMachId()) +// .out_trade_no(WxPayKit.generateStr()) +// .body(payOrder.getTitle()) +// .total_fee(amount) +// .op_device_id("daxpay") +// .mch_create_ip("127.0.0.1") +// .auth_code(authCode) +// .nonce_str(WxPayKit.generateStr()) +// .build() +// .createSign(unionPayKit.getAppKey(), SignType.MD5); +// +// String xmlResult = UnionPayApi.execution(unionPayKit.getServerUrl(), params); } diff --git a/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/channel/union/service/UnionPaySyncService.java b/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/channel/union/service/UnionPaySyncService.java index 59f22413..eabe590d 100644 --- a/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/channel/union/service/UnionPaySyncService.java +++ b/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/channel/union/service/UnionPaySyncService.java @@ -1,29 +1,21 @@ package cn.bootx.platform.daxpay.service.core.channel.union.service; -import cn.bootx.platform.common.core.util.LocalDateTimeUtil; import cn.bootx.platform.daxpay.code.PaySyncStatusEnum; import cn.bootx.platform.daxpay.code.RefundSyncStatusEnum; import cn.bootx.platform.daxpay.service.code.UnionPayCode; -import cn.bootx.platform.daxpay.service.core.channel.union.entity.UnionPayConfig; import cn.bootx.platform.daxpay.service.core.order.pay.entity.PayOrder; import cn.bootx.platform.daxpay.service.core.order.refund.entity.RefundOrder; import cn.bootx.platform.daxpay.service.core.payment.sync.result.PayGatewaySyncResult; import cn.bootx.platform.daxpay.service.core.payment.sync.result.RefundGatewaySyncResult; -import cn.hutool.core.date.DatePattern; +import cn.bootx.platform.daxpay.service.sdk.union.api.UnionPayKit; +import cn.bootx.platform.daxpay.service.sdk.union.bean.UnionRefundOrder; import cn.hutool.core.util.StrUtil; import cn.hutool.json.JSONUtil; -import com.ijpay.core.enums.SignType; -import com.ijpay.core.kit.WxPayKit; -import com.ijpay.unionpay.UnionPayApi; -import com.ijpay.unionpay.enums.ServiceEnum; -import com.ijpay.unionpay.model.OrderQueryModel; -import com.ijpay.unionpay.model.RefundQueryModel; -import com.ijpay.wxpay.WxPayApi; +import com.egzosn.pay.common.bean.AssistOrder; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; -import java.time.LocalDateTime; import java.util.Map; import java.util.Objects; @@ -42,52 +34,47 @@ public class UnionPaySyncService { /** * 支付信息查询 */ - public PayGatewaySyncResult syncPayStatus(PayOrder order, UnionPayConfig unionPayConfig) { + public PayGatewaySyncResult syncPayStatus(PayOrder order, UnionPayKit unionPayKit) { PayGatewaySyncResult syncResult = new PayGatewaySyncResult().setSyncStatus(PaySyncStatusEnum.FAIL); - Map params = OrderQueryModel.builder() - .service(ServiceEnum.QUERY.toString()) - .mch_id(unionPayConfig.getMachId()) - .out_trade_no(String.valueOf(order.getId())) - .nonce_str(WxPayKit.generateStr()) - .build() - .createSign(unionPayConfig.getAppKey(), SignType.MD5); - String xmlResult = UnionPayApi.execution(unionPayConfig.getServerUrl(), params); - Map result = WxPayKit.xmlToMap(xmlResult); + AssistOrder query = new AssistOrder(); + query.setOutTradeNo(String.valueOf(order.getId())); + Map result = unionPayKit.query(query); + syncResult.setSyncInfo(JSONUtil.toJsonStr(result)); - String status = result.get(STATUS); - String returnCode = result.get(RESULT_CODE); - - // 判断查询是否成功 - if (!(Objects.equals(SUCCESS, status) && Objects.equals(SUCCESS, returnCode))){ - log.warn("查询云闪付订单失败:{}", result); - return syncResult; - } - - // 设置微信支付网关订单号 - syncResult.setGatewayOrderNo(result.get(TRANSACTION_ID)); - // 查询到订单的状态 - String tradeStatus = result.get(TRADE_STATE); - // 支付完成 - if (Objects.equals(tradeStatus, SUCCESS)) { - String timeEnd = result.get(TIME_END); - LocalDateTime time = LocalDateTimeUtil.parse(timeEnd, DatePattern.PURE_DATETIME_PATTERN); - return syncResult.setPayTime(time).setSyncStatus(PaySyncStatusEnum.SUCCESS); - } - // 待支付 - if (Objects.equals(tradeStatus, TRADE_NOT_PAY)) { - return syncResult.setSyncStatus(PaySyncStatusEnum.PROGRESS); - } - - // 已退款/退款中 - if (Objects.equals(tradeStatus, TRADE_REFUND)) { - return syncResult.setSyncStatus(PaySyncStatusEnum.REFUND); - } - // 已关闭 - if (Objects.equals(tradeStatus, TRADE_CLOSED)) { - return syncResult.setSyncStatus(PaySyncStatusEnum.CLOSED); - } +// String status = result.get(STATUS); +// String returnCode = result.get(RESULT_CODE); +// +// // 判断查询是否成功 +// if (!(Objects.equals(SUCCESS, status) && Objects.equals(SUCCESS, returnCode))){ +// log.warn("查询云闪付订单失败:{}", result); +// return syncResult; +// } +// +// // 设置微信支付网关订单号 +// syncResult.setGatewayOrderNo(result.get(TRANSACTION_ID)); +// // 查询到订单的状态 +// String tradeStatus = result.get(TRADE_STATE); +// // 支付完成 +// if (Objects.equals(tradeStatus, SUCCESS)) { +// String timeEnd = result.get(TIME_END); +// LocalDateTime time = LocalDateTimeUtil.parse(timeEnd, DatePattern.PURE_DATETIME_PATTERN); +// return syncResult.setPayTime(time).setSyncStatus(PaySyncStatusEnum.SUCCESS); +// } +// // 待支付 +// if (Objects.equals(tradeStatus, TRADE_NOT_PAY)) { +// return syncResult.setSyncStatus(PaySyncStatusEnum.PROGRESS); +// } +// +// // 已退款/退款中 +// if (Objects.equals(tradeStatus, TRADE_REFUND)) { +// return syncResult.setSyncStatus(PaySyncStatusEnum.REFUND); +// } +// // 已关闭 +// if (Objects.equals(tradeStatus, TRADE_CLOSED)) { +// return syncResult.setSyncStatus(PaySyncStatusEnum.CLOSED); +// } return syncResult; } @@ -95,23 +82,21 @@ public class UnionPaySyncService { /** * 退款信息查询 */ - public RefundGatewaySyncResult syncRefundStatus(RefundOrder refundOrder, UnionPayConfig unionPayConfig){ + public RefundGatewaySyncResult syncRefundStatus(RefundOrder refundOrder, UnionPayKit unionPayKit){ RefundGatewaySyncResult syncResult = new RefundGatewaySyncResult(); - Map params = RefundQueryModel.builder() - .service(ServiceEnum.REFUND_QUERY.toString()) - .mch_id(unionPayConfig.getMachId()) - .refund_id(refundOrder.getGatewayOrderNo()) - .nonce_str(WxPayKit.generateStr()) - .build() - .createSign(unionPayConfig.getAppKey(), SignType.MD5); + + UnionRefundOrder query = new UnionRefundOrder(); + query.setRefundNo(String.valueOf(refundOrder.getId())); + Map results = unionPayKit.refundquery(query); + try { - String xmlResult = WxPayApi.orderRefundQuery(false, params); - Map result = WxPayKit.xmlToMap(xmlResult); - syncResult.setSyncInfo(JSONUtil.toJsonStr(result)); +// String xmlResult = WxPayApi.orderRefundQuery(false, params); +// Map result = WxPayKit.xmlToMap(xmlResult); +// syncResult.setSyncInfo(JSONUtil.toJsonStr(result)); // 设置微信支付网关订单号 - syncResult.setGatewayOrderNo(result.get(UnionPayCode.REFUND_ID)); +// syncResult.setGatewayOrderNo(result.get(UnionPayCode.REFUND_ID)); // 状态 // String tradeStatus = result.get(UnionPayCode.REFUND_STATUS); // // 退款成功 @@ -124,8 +109,8 @@ public class UnionPaySyncService { // if (Objects.equals(tradeStatus, UnionPayCode.REFUND_PROCESSING)) { // return syncResult.setSyncStatus(RefundSyncStatusEnum.PROGRESS); // } - String errorMsg = this.getErrorMsg(result); - return syncResult.setSyncStatus(RefundSyncStatusEnum.FAIL).setErrorMsg(errorMsg); +// String errorMsg = this.getErrorMsg(result); +// return syncResult.setSyncStatus(RefundSyncStatusEnum.FAIL).setErrorMsg(errorMsg); } catch (Exception e) { log.error("查询退款订单失败:", e); syncResult.setSyncStatus(RefundSyncStatusEnum.PROGRESS).setErrorMsg(e.getMessage()); diff --git a/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/payment/close/strategy/UnionPayCloseStrategy.java b/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/payment/close/strategy/UnionPayCloseStrategy.java index 86a054b1..a8f666ab 100644 --- a/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/payment/close/strategy/UnionPayCloseStrategy.java +++ b/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/payment/close/strategy/UnionPayCloseStrategy.java @@ -5,6 +5,7 @@ import cn.bootx.platform.daxpay.service.core.channel.union.entity.UnionPayConfig import cn.bootx.platform.daxpay.service.core.channel.union.service.UnionPayCloseService; import cn.bootx.platform.daxpay.service.core.channel.union.service.UnionPayConfigService; import cn.bootx.platform.daxpay.service.func.AbsPayCloseStrategy; +import cn.bootx.platform.daxpay.service.sdk.union.api.UnionPayKit; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.context.annotation.Scope; @@ -48,6 +49,7 @@ public class UnionPayCloseStrategy extends AbsPayCloseStrategy { */ @Override public void doCloseHandler() { - unionPayCloseService.close(this.getOrder(), this.unionPayConfig); + UnionPayKit unionPayKit = unionPayConfigService.initPayService(this.unionPayConfig); + unionPayCloseService.close(this.getOrder(), unionPayKit); } } diff --git a/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/payment/pay/strategy/UnionPayStrategy.java b/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/payment/pay/strategy/UnionPayStrategy.java index 7a3c9bd4..3f8de3bc 100644 --- a/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/payment/pay/strategy/UnionPayStrategy.java +++ b/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/payment/pay/strategy/UnionPayStrategy.java @@ -14,6 +14,7 @@ import cn.bootx.platform.daxpay.service.core.channel.union.service.UnionPayServi import cn.bootx.platform.daxpay.service.core.order.pay.entity.PayChannelOrder; import cn.bootx.platform.daxpay.service.core.order.pay.service.PayChannelOrderService; import cn.bootx.platform.daxpay.service.func.AbsPayStrategy; +import cn.bootx.platform.daxpay.service.sdk.union.api.UnionPayKit; import cn.hutool.core.bean.BeanUtil; import cn.hutool.core.collection.CollUtil; import cn.hutool.json.JSONException; @@ -87,7 +88,8 @@ public class UnionPayStrategy extends AbsPayStrategy { */ @Override public void doPayHandler() { - unionPayService.pay(this.getOrder(), this.getPayChannelParam(), this.unionPayParam, this.unionPayConfig); + UnionPayKit unionPayKit = unionPayConfigService.initPayService(unionPayConfig); + unionPayService.pay(this.getOrder(), this.getPayChannelParam(), this.unionPayParam, unionPayKit); } /** diff --git a/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/payment/reconcile/strategy/UnionPayReconcileStrategy.java b/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/payment/reconcile/strategy/UnionPayReconcileStrategy.java new file mode 100644 index 00000000..4ddd151d --- /dev/null +++ b/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/payment/reconcile/strategy/UnionPayReconcileStrategy.java @@ -0,0 +1,102 @@ +package cn.bootx.platform.daxpay.service.core.payment.reconcile.strategy; + +import cn.bootx.platform.common.core.util.LocalDateTimeUtil; +import cn.bootx.platform.common.sequence.func.Sequence; +import cn.bootx.platform.daxpay.code.PayChannelEnum; +import cn.bootx.platform.daxpay.service.core.channel.union.convert.UnionPayConvert; +import cn.bootx.platform.daxpay.service.core.channel.union.dao.UnionPayRecordManager; +import cn.bootx.platform.daxpay.service.core.channel.union.entity.UnionPayConfig; +import cn.bootx.platform.daxpay.service.core.channel.union.entity.UnionPayRecord; +import cn.bootx.platform.daxpay.service.core.channel.union.service.UnionPayConfigService; +import cn.bootx.platform.daxpay.service.core.channel.union.service.UnionPayReconcileService; +import cn.bootx.platform.daxpay.service.core.payment.reconcile.domain.GeneralReconcileRecord; +import cn.bootx.platform.daxpay.service.func.AbsReconcileStrategy; +import cn.bootx.platform.daxpay.service.sdk.union.api.UnionPayKit; +import cn.hutool.core.date.DatePattern; +import cn.hutool.core.date.DateUtil; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.context.annotation.Scope; +import org.springframework.stereotype.Service; + +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.util.Date; +import java.util.List; +import java.util.stream.Collectors; + +import static org.springframework.beans.factory.config.BeanDefinition.SCOPE_PROTOTYPE; + +/** + * + * @author xxm + * @since 2024/3/8 + */ +@Slf4j +@Service +@Scope(SCOPE_PROTOTYPE) +@RequiredArgsConstructor +public class UnionPayReconcileStrategy extends AbsReconcileStrategy { + + private final UnionPayConfigService configService; + + private final UnionPayReconcileService reconcileService; + + private final UnionPayRecordManager recordManager; + + @Qualifier("unionPayReconcileSequence") + private final Sequence sequence; + + private UnionPayKit unionPayKit; + + /** + * 生成对账序列号 + */ + @Override + public String generateSequence(LocalDate date) { + String prefix = getChannel().getReconcilePrefix(); + String dateStr = LocalDateTimeUtil.format(date, DatePattern.PURE_DATE_PATTERN); + String key = String.format("%02d", sequence.next()); + return prefix + dateStr + key; + } + + /** + * 对账前处理, 主要是初始化支付SDK配置 + */ + @Override + public void doBeforeHandler() { + UnionPayConfig config = configService.getConfig(); + this.unionPayKit = configService.initPayService(config); + } + + /** + * 下载对账单到本地进行保存 + */ + @Override + public void downAndSave() { + Date date = DateUtil.date(this.getRecordOrder().getDate()); + reconcileService.downAndSave(date, this.getRecordOrder().getId(), this.unionPayKit); + } + + /** + * 获取通用对账对象, 将流水记录转换为对账对象 + */ + @Override + public List getGeneralReconcileRecord() { + // 查询流水 + LocalDateTime localDateTime = LocalDateTimeUtil.date2DateTime(this.getRecordOrder().getDate()); + LocalDateTime start = LocalDateTimeUtil.beginOfDay(localDateTime); + LocalDateTime end = LocalDateTimeUtil.endOfDay(localDateTime); + List records = recordManager.findByDate(start, end); + return records.stream().map(UnionPayConvert.CONVERT::convertReconcileRecord).collect(Collectors.toList()); + } + + /** + * 策略标识 + */ + @Override + public PayChannelEnum getChannel() { + return PayChannelEnum.UNION_PAY; + } +} diff --git a/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/payment/refund/strategy/UnionRefundStrategy.java b/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/payment/refund/strategy/UnionRefundStrategy.java index a417c5bb..20678ffd 100644 --- a/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/payment/refund/strategy/UnionRefundStrategy.java +++ b/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/payment/refund/strategy/UnionRefundStrategy.java @@ -9,6 +9,7 @@ import cn.bootx.platform.daxpay.service.core.channel.union.service.UnionPayRecor import cn.bootx.platform.daxpay.service.core.channel.union.service.UnionPayRefundService; import cn.bootx.platform.daxpay.service.core.order.pay.service.PayChannelOrderService; import cn.bootx.platform.daxpay.service.func.AbsRefundStrategy; +import cn.bootx.platform.daxpay.service.sdk.union.api.UnionPayKit; import lombok.RequiredArgsConstructor; import org.springframework.context.annotation.Scope; import org.springframework.stereotype.Component; @@ -61,7 +62,8 @@ public class UnionRefundStrategy extends AbsRefundStrategy { */ @Override public void doRefundHandler() { - unionPayRefundService.refund(this.getRefundOrder(), this.getRefundChannelParam().getAmount(), this.getPayChannelOrder(), this.unionPayConfig); + UnionPayKit unionPayKit = unionPayConfigService.initPayService(unionPayConfig); + unionPayRefundService.refund(this.getRefundOrder(), this.getRefundChannelParam().getAmount(), this.getPayChannelOrder(), unionPayKit); } /** diff --git a/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/payment/sync/strategy/pay/UnionPaySyncStrategy.java b/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/payment/sync/strategy/pay/UnionPaySyncStrategy.java index f7a06dbd..2217ed95 100644 --- a/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/payment/sync/strategy/pay/UnionPaySyncStrategy.java +++ b/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/payment/sync/strategy/pay/UnionPaySyncStrategy.java @@ -7,6 +7,7 @@ import cn.bootx.platform.daxpay.service.core.channel.union.service.UnionPayConfi import cn.bootx.platform.daxpay.service.core.channel.union.service.UnionPaySyncService; import cn.bootx.platform.daxpay.service.core.payment.sync.result.PayGatewaySyncResult; import cn.bootx.platform.daxpay.service.func.AbsPaySyncStrategy; +import cn.bootx.platform.daxpay.service.sdk.union.api.UnionPayKit; import lombok.RequiredArgsConstructor; import org.springframework.context.annotation.Scope; import org.springframework.stereotype.Component; @@ -35,7 +36,8 @@ public class UnionPaySyncStrategy extends AbsPaySyncStrategy { @Override public PayGatewaySyncResult doSyncStatus() { UnionPayConfig config = unionPayConfigService.getConfig(); - return unionPaySyncService.syncPayStatus(this.getOrder(),config); + UnionPayKit unionPayKit = unionPayConfigService.initPayService(config); + return unionPaySyncService.syncPayStatus(this.getOrder(),unionPayKit); } /** diff --git a/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/sdk/union/api/UnionPayKit.java b/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/sdk/union/api/UnionPayKit.java new file mode 100644 index 00000000..273638c3 --- /dev/null +++ b/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/sdk/union/api/UnionPayKit.java @@ -0,0 +1,25 @@ +package cn.bootx.platform.daxpay.service.sdk.union.api; + +import com.egzosn.pay.common.http.HttpConfigStorage; +import com.egzosn.pay.union.api.UnionPayConfigStorage; +import com.egzosn.pay.union.api.UnionPayService; + +/** + * 云闪付支付服务类重命名, 避免与系统中类名冲突 + * @author xxm + * @since 2024/3/8 + */ +public class UnionPayKit extends UnionPayService { + /** + * 构造函数 + * + * @param payConfigStorage 支付配置 + */ + public UnionPayKit(UnionPayConfigStorage payConfigStorage) { + super(payConfigStorage); + } + + public UnionPayKit(UnionPayConfigStorage payConfigStorage, HttpConfigStorage configStorage) { + super(payConfigStorage, configStorage); + } +} diff --git a/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/sdk/union/bean/UnionPayOrder.java b/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/sdk/union/bean/UnionPayOrder.java new file mode 100644 index 00000000..d5653ed7 --- /dev/null +++ b/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/sdk/union/bean/UnionPayOrder.java @@ -0,0 +1,11 @@ +package cn.bootx.platform.daxpay.service.sdk.union.bean; + +import com.egzosn.pay.common.bean.PayOrder; + +/** + * 继承SDK中的类, 防止与系统中的类名称冲突 + * @author xxm + * @since 2024/3/8 + */ +public class UnionPayOrder extends PayOrder { +} diff --git a/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/sdk/union/bean/UnionRefundOrder.java b/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/sdk/union/bean/UnionRefundOrder.java new file mode 100644 index 00000000..27c3563e --- /dev/null +++ b/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/sdk/union/bean/UnionRefundOrder.java @@ -0,0 +1,11 @@ +package cn.bootx.platform.daxpay.service.sdk.union.bean; + +import com.egzosn.pay.common.bean.RefundOrder; + +/** + * 继承SDK中的类, 防止与系统中的类名称冲突 + * @author xxm + * @since 2024/3/8 + */ +public class UnionRefundOrder extends RefundOrder { +} diff --git a/pom.xml b/pom.xml index 93f22ecf..82b39fd0 100644 --- a/pom.xml +++ b/pom.xml @@ -38,6 +38,7 @@ 0.0.38 3.5.2 2.9.10 + 2.14.7 4.4.0 1.5.3.Final 0.2.0 From 21c0433706ad4040ca775d709c316b648cf844e0 Mon Sep 17 00:00:00 2001 From: bootx Date: Sun, 10 Mar 2024 22:45:57 +0800 Subject: [PATCH 3/9] =?UTF-8?q?feat=20=E4=BA=91=E9=97=AA=E4=BB=98=E5=AF=B9?= =?UTF-8?q?=E6=8E=A5=E8=B0=83=E8=AF=95,=20=E9=87=8D=E5=86=99SDK=E4=B8=AD?= =?UTF-8?q?=E4=B8=80=E4=BB=BD=E8=B0=83=E7=94=A8=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- _doc/Task.md | 1 + .../channel/UnionPayConfigController.java | 57 ++ .../channel/UnionPayController.java | 18 + .../daxpay/code/PaySyncStatusEnum.java | 2 +- .../controller/PayReturnController.java | 6 + .../daxpay/service/code/UnionPayCode.java | 10 +- .../daxpay/service/code/UnionPayWay.java | 39 + .../channel/union/entity/UnionPayConfig.java | 2 +- .../service/UnionPayCallbackService.java | 6 +- .../union/service/UnionPayCloseService.java | 33 +- .../union/service/UnionPayConfigService.java | 36 +- .../union/service/UnionPaySyncService.java | 63 +- .../dto/channel/union/UnionPayConfigDto.java | 90 +++ .../channel/union/UnionPayConfigParam.java | 97 +++ .../service/sdk/union/api/UnionPayKit.java | 718 ++++++++++++++++++ 15 files changed, 1097 insertions(+), 81 deletions(-) create mode 100644 daxpay-single/daxpay-single-admin/src/main/java/cn/bootx/platform/daxpay/admin/controller/channel/UnionPayConfigController.java create mode 100644 daxpay-single/daxpay-single-admin/src/main/java/cn/bootx/platform/daxpay/admin/controller/channel/UnionPayController.java create mode 100644 daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/code/UnionPayWay.java create mode 100644 daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/param/channel/union/UnionPayConfigParam.java diff --git a/_doc/Task.md b/_doc/Task.md index ed8f520b..0f7c51f8 100644 --- a/_doc/Task.md +++ b/_doc/Task.md @@ -15,6 +15,7 @@ - [ ] 增加定时同步退款中的退款订单 2.0.x 版本内容 +- [ ] 首页驾驶舱功能: 各通道收入和支付情况 - [ ] 增加各类日志记录,例如钱包的各项操作 - [ ] 支付流程涉及异步支付时, 更换支付方式需要控制预防客户重复付款 - [ ] 增加撤销功能,用于处理线下支付订单的情况 diff --git a/daxpay-single/daxpay-single-admin/src/main/java/cn/bootx/platform/daxpay/admin/controller/channel/UnionPayConfigController.java b/daxpay-single/daxpay-single-admin/src/main/java/cn/bootx/platform/daxpay/admin/controller/channel/UnionPayConfigController.java new file mode 100644 index 00000000..d18e146a --- /dev/null +++ b/daxpay-single/daxpay-single-admin/src/main/java/cn/bootx/platform/daxpay/admin/controller/channel/UnionPayConfigController.java @@ -0,0 +1,57 @@ +package cn.bootx.platform.daxpay.admin.controller.channel; + +import cn.bootx.platform.common.core.rest.Res; +import cn.bootx.platform.common.core.rest.ResResult; +import cn.bootx.platform.common.core.rest.dto.LabelValue; +import cn.bootx.platform.daxpay.service.core.channel.union.service.UnionPayConfigService; +import cn.bootx.platform.daxpay.service.dto.channel.union.UnionPayConfigDto; +import cn.bootx.platform.daxpay.service.param.channel.union.UnionPayConfigParam; +import cn.hutool.core.codec.Base64; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.RequiredArgsConstructor; +import lombok.SneakyThrows; +import org.springframework.web.bind.annotation.*; +import org.springframework.web.multipart.MultipartFile; + +import java.util.List; + +/** + * 云闪付配置 + * @author xxm + * @since 2024/3/9 + */ +@Tag(name = "云闪付配置") +@RestController +@RequestMapping("/union/pay/config") +@RequiredArgsConstructor +public class UnionPayConfigController { + + private final UnionPayConfigService unionPayConfigService; + + @Operation(summary = "获取配置") + @GetMapping("/getConfig") + public ResResult getConfig() { + return Res.ok(unionPayConfigService.getConfig().toDto()); + } + + @Operation(summary = "更新") + @PostMapping("/update") + public ResResult update(@RequestBody UnionPayConfigParam param) { + unionPayConfigService.update(param); + return Res.ok(); + } + + @Operation(summary = "支持的支付方式") + @GetMapping("/findPayWays") + public ResResult> findPayWays() { + return Res.ok(unionPayConfigService.findPayWays()); + } + + @SneakyThrows + @Operation(summary = "读取证书文件内容") + @PostMapping("/toBase64") + public ResResult toBase64(MultipartFile file){ + return Res.ok(Base64.encode(file.getBytes())); + } +} diff --git a/daxpay-single/daxpay-single-admin/src/main/java/cn/bootx/platform/daxpay/admin/controller/channel/UnionPayController.java b/daxpay-single/daxpay-single-admin/src/main/java/cn/bootx/platform/daxpay/admin/controller/channel/UnionPayController.java new file mode 100644 index 00000000..f2648789 --- /dev/null +++ b/daxpay-single/daxpay-single-admin/src/main/java/cn/bootx/platform/daxpay/admin/controller/channel/UnionPayController.java @@ -0,0 +1,18 @@ +package cn.bootx.platform.daxpay.admin.controller.channel; + +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.RequiredArgsConstructor; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +/** + * 云闪付控制器 + * @author xxm + * @since 2024/3/9 + */ +@Tag(name = "云闪付控制器") +@RestController +@RequestMapping("/union/pay") +@RequiredArgsConstructor +public class UnionPayController { +} diff --git a/daxpay-single/daxpay-single-core/src/main/java/cn/bootx/platform/daxpay/code/PaySyncStatusEnum.java b/daxpay-single/daxpay-single-core/src/main/java/cn/bootx/platform/daxpay/code/PaySyncStatusEnum.java index ac325d7e..f41170b9 100644 --- a/daxpay-single/daxpay-single-core/src/main/java/cn/bootx/platform/daxpay/code/PaySyncStatusEnum.java +++ b/daxpay-single/daxpay-single-core/src/main/java/cn/bootx/platform/daxpay/code/PaySyncStatusEnum.java @@ -28,7 +28,7 @@ public enum PaySyncStatusEnum { * 所以查询为了区分,增加一个未知的状态, 用于处理这种特殊情况, 然后根据业务需要,关闭订单或者进行其他操作 */ NOT_FOUND_UNKNOWN("pay_not_found_unknown","交易不存在(特殊)"), - /** 不属于网关同步过来的状态, 需要手动设置, 处理本地订单到了超时时间, 但是网关和本地都未关闭, 需要触发关闭相关处理 */ + /** 本地订单到了超时时间, 但是网关和本地都未关闭, 需要触发关闭相关处理, 可以进行手动设置 */ TIMEOUT("pay_timeout", "支付超时"); /** 编码 */ diff --git a/daxpay-single/daxpay-single-gateway/src/main/java/cn/bootx/platform/daxpay/gateway/controller/PayReturnController.java b/daxpay-single/daxpay-single-gateway/src/main/java/cn/bootx/platform/daxpay/gateway/controller/PayReturnController.java index 524f9ed7..2ce224af 100644 --- a/daxpay-single/daxpay-single-gateway/src/main/java/cn/bootx/platform/daxpay/gateway/controller/PayReturnController.java +++ b/daxpay-single/daxpay-single-gateway/src/main/java/cn/bootx/platform/daxpay/gateway/controller/PayReturnController.java @@ -36,4 +36,10 @@ public class PayReturnController { public ModelAndView wechat(){ return null; } + + @Operation(summary = "云闪付同步通知") + @GetMapping("/union") + public ModelAndView union(){ + return null; + } } diff --git a/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/code/UnionPayCode.java b/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/code/UnionPayCode.java index 81e245e5..1eb44a0d 100644 --- a/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/code/UnionPayCode.java +++ b/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/code/UnionPayCode.java @@ -18,26 +18,26 @@ public interface UnionPayCode { String RESULT_CODE = "result_code"; /** 网关订单号 */ - String TRANSACTION_ID = "transaction_id"; + String QUERY_ID = "queryId"; /** 第三方订单号 */ - String OUT_TRANSACTION_ID = "out_transaction_id"; + String ORDER_ID = "orderId"; /** 退款ID */ String REFUND_ID = "refund_id"; /** - * 支付完成时间 + * 订单发送时间 * 格式: yyyyMMddHHmmss */ - String TIME_END = "time_end"; + String TXN_TIME = "txnTime"; /** 支付结果 */ String PAY_RESULT = "pay_result"; /** 总金额 */ - String TOTAL_FEE = "total_fee"; + String TOTAL_FEE = "settleAmt"; /** 交易状态 */ String TRADE_STATE = "trade_state"; diff --git a/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/code/UnionPayWay.java b/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/code/UnionPayWay.java new file mode 100644 index 00000000..9f9ebb7c --- /dev/null +++ b/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/code/UnionPayWay.java @@ -0,0 +1,39 @@ +package cn.bootx.platform.daxpay.service.code; + +import cn.bootx.platform.daxpay.code.PayWayEnum; +import cn.bootx.platform.daxpay.exception.pay.PayFailureException; +import lombok.experimental.UtilityClass; + +import java.util.Arrays; +import java.util.List; +import java.util.Objects; + +/** + * 云闪付支付方式 + * @author xxm + * @since 2024/3/9 + */ +@UtilityClass +public class UnionPayWay { + + // 支付方式 + private static final List PAY_WAYS = Arrays.asList(PayWayEnum.WAP, PayWayEnum.APP, PayWayEnum.WEB, + PayWayEnum.QRCODE, PayWayEnum.BARCODE); + + /** + * 根据编码获取 + */ + public PayWayEnum findByCode(String code) { + return PAY_WAYS.stream() + .filter(e -> Objects.equals(code, e.getCode())) + .findFirst() + .orElseThrow(() -> new PayFailureException("不存在的支付方式")); + } + + /** + * 获取支持的支付方式 + */ + public List getPayWays() { + return PAY_WAYS; + } +} diff --git a/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/channel/union/entity/UnionPayConfig.java b/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/channel/union/entity/UnionPayConfig.java index 0952a8ba..44c94e90 100644 --- a/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/channel/union/entity/UnionPayConfig.java +++ b/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/channel/union/entity/UnionPayConfig.java @@ -53,7 +53,7 @@ public class UnionPayConfig extends MpBaseEntity implements EntityBaseFunction callbackParam = callbackInfo.getCallbackParam(); // 网关订单号 - callbackInfo.setGatewayOrderNo(callbackParam.get(TRANSACTION_ID)); + callbackInfo.setGatewayOrderNo(callbackParam.get(QUERY_ID)); // 支付订单ID - callbackInfo.setOrderId(Long.valueOf(callbackParam.get(OUT_TRANSACTION_ID))); + callbackInfo.setOrderId(Long.valueOf(callbackParam.get(ORDER_ID))); // 支付结果 PayStatusEnum payStatus = WxPayKit.codeIsOk(callbackParam.get(PAY_RESULT)) ? PayStatusEnum.SUCCESS : PayStatusEnum.FAIL; callbackInfo.setGatewayStatus(payStatus.getCode()); // 支付金额 callbackInfo.setAmount(callbackParam.get(TOTAL_FEE)); - String timeEnd = callbackParam.get(TIME_END); + String timeEnd = callbackParam.get(TXN_TIME); if (StrUtil.isNotBlank(timeEnd)) { LocalDateTime time = LocalDateTimeUtil.parse(timeEnd, DatePattern.PURE_DATETIME_PATTERN); callbackInfo.setFinishTime(time); diff --git a/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/channel/union/service/UnionPayCloseService.java b/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/channel/union/service/UnionPayCloseService.java index 42b1b7e9..b28f1b2f 100644 --- a/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/channel/union/service/UnionPayCloseService.java +++ b/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/channel/union/service/UnionPayCloseService.java @@ -1,20 +1,12 @@ package cn.bootx.platform.daxpay.service.core.channel.union.service; import cn.bootx.platform.daxpay.exception.pay.PayFailureException; -import cn.bootx.platform.daxpay.service.code.UnionPayCode; import cn.bootx.platform.daxpay.service.core.order.pay.entity.PayOrder; import cn.bootx.platform.daxpay.service.sdk.union.api.UnionPayKit; -import cn.hutool.core.util.StrUtil; -import com.egzosn.pay.common.bean.AssistOrder; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; -import java.util.Map; -import java.util.Objects; - -import static cn.bootx.platform.daxpay.service.code.UnionPayCode.*; - /** * 云闪付支付关闭 * @author xxm @@ -29,30 +21,7 @@ public class UnionPayCloseService { * 关闭订单 */ public void close(PayOrder payOrder, UnionPayKit unionPayKit) { - AssistOrder closeOrder = new AssistOrder(); - - closeOrder.setOutTradeNo(String.valueOf(payOrder.getId())); - - Map result = unionPayKit.close(closeOrder); - + throw new PayFailureException("云闪付没有关闭订单功能!"); // this.verifyErrorMsg(result); } - - /** - * 验证错误信息 - */ - private void verifyErrorMsg(Map result) { - String status = result.get(UnionPayCode.STATUS); - String returnCode = result.get(UnionPayCode.RESULT_CODE); - - // 判断查询是否成功 - if (!(Objects.equals(SUCCESS, status) && Objects.equals(SUCCESS, returnCode))){ - String errorMsg = result.get(ERR_MSG); - if (StrUtil.isBlank(errorMsg)) { - errorMsg = result.get(MESSAGE); - } - log.error("订单关闭失败 {}", errorMsg); - throw new PayFailureException(errorMsg); - } - } } diff --git a/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/channel/union/service/UnionPayConfigService.java b/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/channel/union/service/UnionPayConfigService.java index 74ab897b..b96a48c1 100644 --- a/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/channel/union/service/UnionPayConfigService.java +++ b/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/channel/union/service/UnionPayConfigService.java @@ -4,14 +4,16 @@ import cn.bootx.platform.common.core.exception.DataNotExistException; import cn.bootx.platform.common.core.rest.dto.LabelValue; import cn.bootx.platform.daxpay.code.PayChannelEnum; import cn.bootx.platform.daxpay.exception.pay.PayFailureException; -import cn.bootx.platform.daxpay.service.code.AliPayWay; +import cn.bootx.platform.daxpay.service.code.UnionPayWay; import cn.bootx.platform.daxpay.service.core.channel.union.dao.UnionPayConfigManager; import cn.bootx.platform.daxpay.service.core.channel.union.entity.UnionPayConfig; import cn.bootx.platform.daxpay.service.core.system.config.service.PayChannelConfigService; -import cn.bootx.platform.daxpay.service.param.channel.alipay.AliPayConfigParam; +import cn.bootx.platform.daxpay.service.param.channel.union.UnionPayConfigParam; import cn.bootx.platform.daxpay.service.sdk.union.api.UnionPayKit; import cn.hutool.core.bean.BeanUtil; import cn.hutool.core.bean.copier.CopyOptions; +import cn.hutool.core.codec.Base64; +import cn.hutool.core.util.CharsetUtil; import com.egzosn.pay.common.bean.CertStoreType; import com.egzosn.pay.common.http.HttpConfigStorage; import com.egzosn.pay.union.api.UnionPayConfigStorage; @@ -20,6 +22,7 @@ import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; +import java.io.ByteArrayInputStream; import java.util.List; import java.util.Objects; import java.util.stream.Collectors; @@ -42,7 +45,7 @@ public class UnionPayConfigService { * 修改 */ @Transactional(rollbackFor = Exception.class) - public void update(AliPayConfigParam param) { + public void update(UnionPayConfigParam param) { UnionPayConfig unionPayConfig = unionPayConfigManager.findById(ID).orElseThrow(() -> new DataNotExistException("支付宝配置不存在")); // 启用或停用 if (!Objects.equals(param.getEnable(), unionPayConfig.getEnable())){ @@ -54,10 +57,10 @@ public class UnionPayConfigService { } /** - * 支付宝支持支付方式 + * 云闪付支持支付方式 */ public List findPayWays() { - return AliPayWay.getPayWays() + return UnionPayWay.getPayWays() .stream() .map(e -> new LabelValue(e.getName(),e.getCode())) .collect(Collectors.toList()); @@ -67,7 +70,7 @@ public class UnionPayConfigService { * 获取支付配置 */ public UnionPayConfig getConfig(){ - return unionPayConfigManager.findById(ID).orElseThrow(() -> new DataNotExistException("支付宝配置不存在")); + return unionPayConfigManager.findById(ID).orElseThrow(() -> new DataNotExistException("云闪付支付配置不存在")); } /** @@ -87,32 +90,35 @@ public class UnionPayConfigService { */ public UnionPayKit initPayService(UnionPayConfig config){ UnionPayConfigStorage unionPayConfigStorage = new UnionPayConfigStorage(); + unionPayConfigStorage.setInputCharset(CharsetUtil.UTF_8); + // 商户号 unionPayConfigStorage.setMerId(config.getMachId()); //是否为证书签名 unionPayConfigStorage.setCertSign(config.isCertSign()); - //中级证书 证书字符串信息 - unionPayConfigStorage.setAcpMiddleCert(config.getAcpMiddleCert()); - //根证书路径 证书字符串信息 - unionPayConfigStorage.setAcpRootCert(config.getAcpRootCert()); - // 私钥证书路径 证书字符串信息 - unionPayConfigStorage.setKeyPrivateCert(config.getKeyPrivateCert()); + //中级证书 流 + unionPayConfigStorage.setAcpMiddleCert(new ByteArrayInputStream(Base64.decode(config.getAcpMiddleCert()))); + //根证书 流 + unionPayConfigStorage.setAcpRootCert(new ByteArrayInputStream(Base64.decode(config.getAcpRootCert()))); + // 私钥证书 流 + unionPayConfigStorage.setKeyPrivateCert(new ByteArrayInputStream(Base64.decode(config.getKeyPrivateCert()))); + //私钥证书对应的密码 私钥证书对应的密码 unionPayConfigStorage.setKeyPrivateCertPwd(config.getKeyPrivateCertPwd()); //设置证书对应的存储方式,证书字符串信息 - unionPayConfigStorage.setCertStoreType(CertStoreType.STR); + unionPayConfigStorage.setCertStoreType(CertStoreType.INPUT_STREAM); // 回调地址 unionPayConfigStorage.setNotifyUrl(config.getNotifyUrl()); // 同步回调可不填 unionPayConfigStorage.setReturnUrl(config.getReturnUrl()); - unionPayConfigStorage.setSignType(config.signType); + unionPayConfigStorage.setSignType(config.getSignType()); //是否为测试账号,沙箱环境 unionPayConfigStorage.setTest(config.isSandbox()); // 网络请求配置 HttpConfigStorage httpConfigStorage = new HttpConfigStorage(); - httpConfigStorage.setCertStoreType(CertStoreType.STR); + httpConfigStorage.setCertStoreType(CertStoreType.INPUT_STREAM); //最大连接数 httpConfigStorage.setMaxTotal(20); //默认的每个路由的最大连接数 diff --git a/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/channel/union/service/UnionPaySyncService.java b/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/channel/union/service/UnionPaySyncService.java index eabe590d..605cdbdd 100644 --- a/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/channel/union/service/UnionPaySyncService.java +++ b/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/channel/union/service/UnionPaySyncService.java @@ -1,5 +1,6 @@ package cn.bootx.platform.daxpay.service.core.channel.union.service; +import cn.bootx.platform.common.core.util.LocalDateTimeUtil; import cn.bootx.platform.daxpay.code.PaySyncStatusEnum; import cn.bootx.platform.daxpay.code.RefundSyncStatusEnum; import cn.bootx.platform.daxpay.service.code.UnionPayCode; @@ -9,13 +10,18 @@ import cn.bootx.platform.daxpay.service.core.payment.sync.result.PayGatewaySyncR import cn.bootx.platform.daxpay.service.core.payment.sync.result.RefundGatewaySyncResult; import cn.bootx.platform.daxpay.service.sdk.union.api.UnionPayKit; import cn.bootx.platform.daxpay.service.sdk.union.bean.UnionRefundOrder; +import cn.hutool.core.date.DatePattern; +import cn.hutool.core.map.MapUtil; import cn.hutool.core.util.StrUtil; import cn.hutool.json.JSONUtil; import com.egzosn.pay.common.bean.AssistOrder; +import com.egzosn.pay.common.bean.NoticeParams; +import com.egzosn.pay.union.bean.SDKConstants; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; +import java.time.LocalDateTime; import java.util.Map; import java.util.Objects; @@ -39,33 +45,42 @@ public class UnionPaySyncService { AssistOrder query = new AssistOrder(); query.setOutTradeNo(String.valueOf(order.getId())); + Map result = unionPayKit.query(query); - syncResult.setSyncInfo(JSONUtil.toJsonStr(result)); + if (!unionPayKit.verify(new NoticeParams(result))) { + log.warn("查询云闪付订单验签失败:{}", result); + return syncResult.setErrorMsg("查询订单验签失败"); + } -// String status = result.get(STATUS); -// String returnCode = result.get(RESULT_CODE); -// -// // 判断查询是否成功 -// if (!(Objects.equals(SUCCESS, status) && Objects.equals(SUCCESS, returnCode))){ -// log.warn("查询云闪付订单失败:{}", result); -// return syncResult; -// } -// -// // 设置微信支付网关订单号 -// syncResult.setGatewayOrderNo(result.get(TRANSACTION_ID)); -// // 查询到订单的状态 -// String tradeStatus = result.get(TRADE_STATE); -// // 支付完成 -// if (Objects.equals(tradeStatus, SUCCESS)) { -// String timeEnd = result.get(TIME_END); -// LocalDateTime time = LocalDateTimeUtil.parse(timeEnd, DatePattern.PURE_DATETIME_PATTERN); -// return syncResult.setPayTime(time).setSyncStatus(PaySyncStatusEnum.SUCCESS); -// } -// // 待支付 -// if (Objects.equals(tradeStatus, TRADE_NOT_PAY)) { -// return syncResult.setSyncStatus(PaySyncStatusEnum.PROGRESS); -// } + // 查询失败 + String resultCode = MapUtil.getStr(result, SDKConstants.param_respCode); + if (!SDKConstants.OK_RESP_CODE.equals(resultCode)) { + log.warn("查询云闪付订单失败:{}", result); + return syncResult.setErrorMsg(MapUtil.getStr(result, SDKConstants.param_respMsg)); + } + String origRespCode = MapUtil.getStr(result, SDKConstants.param_origRespCode); + + // 查询流水号, 相当于网关订单号 + + + // 成功 + if (Objects.equals(origRespCode, SDKConstants.OK_RESP_CODE)) { + String queryId = MapUtil.getStr(result, QUERY_ID); + String timeEnd = MapUtil.getStr(result, TXN_TIME); + LocalDateTime time = LocalDateTimeUtil.parse(timeEnd, DatePattern.PURE_DATETIME_PATTERN); + return syncResult.setGatewayOrderNo(queryId).setPayTime(time).setSyncStatus(PaySyncStatusEnum.SUCCESS); + } + // 支付超时 交易不在受理时间范围内 + if (Objects.equals(origRespCode, "39")) { + return syncResult.setSyncStatus(PaySyncStatusEnum.TIMEOUT) + .setErrorMsg(MapUtil.getStr(result, SDKConstants.param_origRespMsg)); + } + + // 待支付 + if (Objects.equals(origRespCode, "05")) { + return syncResult.setSyncStatus(PaySyncStatusEnum.PROGRESS); + } // // // 已退款/退款中 // if (Objects.equals(tradeStatus, TRADE_REFUND)) { diff --git a/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/dto/channel/union/UnionPayConfigDto.java b/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/dto/channel/union/UnionPayConfigDto.java index 5022835d..3b77c904 100644 --- a/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/dto/channel/union/UnionPayConfigDto.java +++ b/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/dto/channel/union/UnionPayConfigDto.java @@ -1,11 +1,15 @@ package cn.bootx.platform.daxpay.service.dto.channel.union; import cn.bootx.platform.common.core.rest.dto.BaseDto; +import cn.bootx.platform.daxpay.service.code.UnionPaySignTypeEnum; +import cn.bootx.platform.starter.data.perm.sensitive.SensitiveInfo; import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; import lombok.EqualsAndHashCode; import lombok.experimental.Accessors; +import java.util.List; + /** * @author xxm * @since 2022/3/11 @@ -16,4 +20,90 @@ import lombok.experimental.Accessors; @Schema(title = "云闪付配置") public class UnionPayConfigDto extends BaseDto { + /** 商户号 */ + @Schema(description = "商户号") + private String machId; + + /** 是否启用, 只影响支付和退款操作 */ + @Schema(description = "是否启用") + private Boolean enable; + + + /** + * 商户收款账号 + */ + @Schema(description = "商户收款账号") + private String seller; + + /** + * 签名类型 + * @see UnionPaySignTypeEnum + */ + @Schema(description = "签名类型") + public String signType; + + /** + * 是否为证书签名 + */ + @Schema(description = "是否为证书签名") + private boolean certSign; + + /** + * 应用私钥证书 字符串 + */ + @SensitiveInfo(value = SensitiveInfo.SensitiveType.OTHER, front = 15) + @Schema(description = "应用私钥证书") + private String keyPrivateCert; + /** + * 私钥证书对应的密码 + */ + @SensitiveInfo(value = SensitiveInfo.SensitiveType.PASSWORD) + @Schema(description = "私钥证书对应的密码") + private String keyPrivateCertPwd; + + /** + * 中级证书 + */ + @SensitiveInfo(value = SensitiveInfo.SensitiveType.OTHER, front = 15) + @Schema(description = "中级证书") + private String acpMiddleCert; + /** + * 根证书 + */ + @SensitiveInfo(value = SensitiveInfo.SensitiveType.OTHER, front = 15) + @Schema(description = "根证书") + private String acpRootCert; + + /** 是否沙箱环境 */ + @Schema(description = "是否沙箱环境") + private boolean sandbox; + + /** 支付网关地址 */ + @Schema(description = "支付网关地址") + private String serverUrl; + + /** + * 服务器异步通知页面路径, 需要填写本网关服务的地址, 不可以直接填写业务系统的地址 + * 1. 需http://或者https://格式的完整路径, + * 2. 不能加?id=123这类自定义参数,必须外网可以正常访问 + * 3. 消息顺序 银联网关 -> 本网关进行处理 -> 发送消息通知业务系统 + */ + @Schema(description = "异步通知路径") + private String notifyUrl; + + /** + * 服务器同步通知页面路径, 需要填写本网关服务的地址, 不可以直接填写业务系统的地址 + * 1. 需http://或者https://格式的完整路径, + * 2. 不能加?id=123这类自定义参数,必须外网可以正常访问 + * 3. 消息顺序 银联网关 -> 本网关进行处理 -> 重定向到业务系统中 + */ + @Schema(description = "同步通知页面路径") + private String returnUrl; + + /** 可用支付方式 */ + @Schema(description = "可用支付方式") + private List payWays; + + @Schema(description = "备注") + private String remark; } diff --git a/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/param/channel/union/UnionPayConfigParam.java b/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/param/channel/union/UnionPayConfigParam.java new file mode 100644 index 00000000..10cc47ef --- /dev/null +++ b/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/param/channel/union/UnionPayConfigParam.java @@ -0,0 +1,97 @@ +package cn.bootx.platform.daxpay.service.param.channel.union; + +import cn.bootx.platform.daxpay.service.code.UnionPaySignTypeEnum; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.experimental.Accessors; + +import java.util.List; + +/** + * 云闪付支付配置参数 + * @author xxm + * @since 2024/3/9 + */ +@Data +@Accessors(chain = true) +@Schema(title = "云闪付支付配置参数") +public class UnionPayConfigParam { + + /** 商户号 */ + @Schema(description = "商户号") + private String machId; + + /** 是否启用, 只影响支付和退款操作 */ + @Schema(description = "是否启用") + private Boolean enable; + + /** + * 商户收款账号 + */ + @Schema(description = "商户收款账号") + private String seller; + + /** + * 签名类型 + * @see UnionPaySignTypeEnum + */ + @Schema(description = "签名类型") + public String signType; + + /** + * 是否为证书签名 + */ + @Schema(description = "是否为证书签名") + private boolean certSign; + + /** + * 应用私钥证书 + */ + @Schema(description = "应用私钥证书") + private String keyPrivateCert; + /** + * 私钥证书对应的密码 + */ + @Schema(description = "私钥证书对应的密码") + private String keyPrivateCertPwd; + + /** + * 中级证书 + */ + @Schema(description = "中级证书") + private String acpMiddleCert; + /** + * 根证书 + */ + @Schema(description = "根证书") + private String acpRootCert; + + /** 是否沙箱环境 */ + @Schema(description = "是否沙箱环境") + private boolean sandbox; + + /** + * 服务器异步通知页面路径, 需要填写本网关服务的地址, 不可以直接填写业务系统的地址 + * 1. 需http://或者https://格式的完整路径, + * 2. 不能加?id=123这类自定义参数,必须外网可以正常访问 + * 3. 消息顺序 银联网关 -> 本网关进行处理 -> 发送消息通知业务系统 + */ + @Schema(description = "异步通知路径") + private String notifyUrl; + + /** + * 服务器同步通知页面路径, 需要填写本网关服务的地址, 不可以直接填写业务系统的地址 + * 1. 需http://或者https://格式的完整路径, + * 2. 不能加?id=123这类自定义参数,必须外网可以正常访问 + * 3. 消息顺序 银联网关 -> 本网关进行处理 -> 重定向到业务系统中 + */ + @Schema(description = "同步通知页面路径") + private String returnUrl; + + /** 可用支付方式 */ + @Schema(description = "可用支付方式") + private List payWays; + + @Schema(description = "备注") + private String remark; +} diff --git a/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/sdk/union/api/UnionPayKit.java b/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/sdk/union/api/UnionPayKit.java index 273638c3..63245a5a 100644 --- a/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/sdk/union/api/UnionPayKit.java +++ b/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/sdk/union/api/UnionPayKit.java @@ -1,8 +1,32 @@ package cn.bootx.platform.daxpay.service.sdk.union.api; +import com.alibaba.fastjson.JSONObject; +import com.egzosn.pay.common.bean.*; +import com.egzosn.pay.common.bean.outbuilder.PayTextOutMessage; +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.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; +import com.egzosn.pay.common.util.str.StringUtils; import com.egzosn.pay.union.api.UnionPayConfigStorage; import com.egzosn.pay.union.api.UnionPayService; +import com.egzosn.pay.union.bean.*; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.math.BigDecimal; +import java.security.GeneralSecurityException; +import java.security.cert.*; +import java.sql.Timestamp; +import java.util.*; /** * 云闪付支付服务类重命名, 避免与系统中类名冲突 @@ -10,6 +34,29 @@ import com.egzosn.pay.union.api.UnionPayService; * @since 2024/3/8 */ public class UnionPayKit extends UnionPayService { + /** + * 测试域名 + */ + private static final String TEST_BASE_DOMAIN = "test.95516.com"; + /** + * 正式域名 + */ + private static final String RELEASE_BASE_DOMAIN = "95516.com"; + /** + * 交易请求地址 + */ + private static final String FRONT_TRANS_URL = "https://gateway.%s/gateway/api/frontTransReq.do"; + private static final String BACK_TRANS_URL = "https://gateway.%s/gateway/api/backTransReq.do"; + private static final String SINGLE_QUERY_URL = "https://gateway.%s/gateway/api/queryTrans.do"; + private static final String BATCH_TRANS_URL = "https://gateway.%s/gateway/api/batchTrans.do"; + 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 volatile CertDescriptor certDescriptor; + /** * 构造函数 * @@ -22,4 +69,675 @@ public class UnionPayKit extends UnionPayService { public UnionPayKit(UnionPayConfigStorage payConfigStorage, HttpConfigStorage configStorage) { super(payConfigStorage, configStorage); } + + /** + * 设置支付配置 + * + * @param payConfigStorage 支付配置 + */ + @Override + public UnionPayService setPayConfigStorage(UnionPayConfigStorage payConfigStorage) { + this.payConfigStorage = payConfigStorage; + if (null != certDescriptor) { + return this; + } + try { + certDescriptor = new CertDescriptor(); + certDescriptor.initPrivateSignCert(payConfigStorage.getKeyPrivateCertInputStream(), payConfigStorage.getKeyPrivateCertPwd(), "PKCS12"); + certDescriptor.initPublicCert(payConfigStorage.getAcpMiddleCertInputStream()); + certDescriptor.initRootCert(payConfigStorage.getAcpRootCertInputStream()); + } + catch (IOException e) { + LOG.error("", e); + } + + + 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 getReqUrl(null); + } + + public String getFrontTransUrl() { + return String.format(FRONT_TRANS_URL, getReqUrl()); + } + + 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()); + } + + + public String getFileTransUrl() { + 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自行选择外其他不需修改 + * + * @return 返回参数集合 + */ + private Map getCommonParam() { + Map params = new TreeMap<>(); + UnionPayConfigStorage configStorage = payConfigStorage; + //银联接口版本 + params.put(SDKConstants.param_version, configStorage.getVersion()); + //编码方式 + params.put(SDKConstants.param_encoding, payConfigStorage.getInputCharset().toUpperCase()); + //商户代码 + params.put(SDKConstants.param_merId, payConfigStorage.getPid()); + + //订单发送时间 + 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:平台商户) + params.put(SDKConstants.param_accessType, configStorage.getAccessType()); + return params; + } + + + /** + * 回调校验 + * + * @param result 回调回来的参数集 + * @return 签名校验 true通过 + */ + @Deprecated + @Override + public boolean verify(Map result) { + + + return verify(new NoticeParams(result)); + } + + /** + * 回调校验 + * + * @param noticeParams 回调回来的参数集 + * @return 签名校验 true通过 + */ + @Override + 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)); + } + + /** + * 签名校验 + * + * @param params 参数集 + * @param sign 签名原文 + * @return 签名校验 true通过 + */ + public boolean signVerify(Map params, String sign) { + SignUtils signUtils = SignUtils.valueOf(payConfigStorage.getSignType()); + + String data = SignTextUtils.parameterText(params, "&", "signature"); + switch (signUtils) { + case RSA: + data = SignUtils.SHA1.createSign(data, "", payConfigStorage.getInputCharset()); + return RSA.verify(data, sign, verifyCertificate(genCertificateByStr((String) params.get(SDKConstants.param_signPubKeyCert))).getPublicKey(), payConfigStorage.getInputCharset()); + case RSA2: + data = SignUtils.SHA256.createSign(data, "", payConfigStorage.getInputCharset()); + return RSA2.verify(data, sign, verifyCertificate(genCertificateByStr((String) params.get(SDKConstants.param_signPubKeyCert))).getPublicKey(), payConfigStorage.getInputCharset()); + case SHA1: + case SHA256: + case SM3: + String before = signUtils.createSign(payConfigStorage.getKeyPublic(), "", payConfigStorage.getInputCharset()); + return signUtils.verify(data, sign, "&" + before, payConfigStorage.getInputCharset()); + default: + return false; + } + } + + + /** + * 订单超时时间。 + * 超过此时间后,除网银交易外,其他交易银联系统会拒绝受理,提示超时。 跳转银行网银交易如果超时后交易成功,会自动退款,大约5个工作日金额返还到持卡人账户。 + * 此时间建议取支付时的北京时间加15分钟。 + * 超过超时时间调查询接口应答origRespCode不是A6或者00的就可以判断为失败。 + * + * @param expirationTime 超时时间 + * @return 具体的时间字符串 + */ + private String getPayTimeout(Date expirationTime) { + // + if (null != expirationTime) { + return DateUtils.formatDate(expirationTime, DateUtils.YYYYMMDDHHMMSS); + } + return DateUtils.formatDate(new Timestamp(System.currentTimeMillis() + 30 * 60 * 1000), DateUtils.YYYYMMDDHHMMSS); + } + + /** + * 返回创建的订单信息 + * + * @param order 支付订单 + * @return 订单信息 + * @see PayOrder 支付订单信息 + */ + @Override + public Map orderInfo(PayOrder order) { + Map params = this.getCommonParam(); + + UnionTransactionType type = (UnionTransactionType) order.getTransactionType(); + initNotifyUrl(params, order); + + //设置交易类型相关的参数 + type.convertMap(params); + + params.put(SDKConstants.param_orderId, order.getOutTradeNo()); + + if (StringUtils.isNotEmpty(order.getAddition())) { + params.put(SDKConstants.param_reqReserved, order.getAddition()); + } + switch (type) { + case WAP: + case WEB: + //todo PCwap网关跳转支付特殊用法.txt + case B2B: + params.put(SDKConstants.param_txnAmt, Util.conversionCentAmount(order.getPrice())); + params.put("orderDesc", order.getSubject()); + params.put(SDKConstants.param_payTimeout, getPayTimeout(order.getExpirationTime())); + + params.put(SDKConstants.param_frontUrl, payConfigStorage.getReturnUrl()); + break; + case CONSUME: + params.put(SDKConstants.param_txnAmt, Util.conversionCentAmount(order.getPrice())); + params.put(SDKConstants.param_qrNo, order.getAuthCode()); + break; + case APPLY_QR_CODE: + if (null != order.getPrice()) { + params.put(SDKConstants.param_txnAmt, Util.conversionCentAmount(order.getPrice())); + } + params.put(SDKConstants.param_payTimeout, getPayTimeout(order.getExpirationTime())); + break; + default: + params.put(SDKConstants.param_txnAmt, Util.conversionCentAmount(order.getPrice())); + params.put(SDKConstants.param_payTimeout, getPayTimeout(order.getExpirationTime())); + params.put("orderDesc", order.getSubject()); + } + params.putAll(order.getAttrs()); + params = preOrderHandler(params, order); + return setSign(params); + } + + + /** + * 生成并设置签名 + * + * @param parameters 请求参数 + * @return 请求参数 + */ + private Map setSign(Map parameters) { + + SignUtils signUtils = SignUtils.valueOf(payConfigStorage.getSignType()); + + String signStr; + switch (signUtils) { + case RSA: + parameters.put(SDKConstants.param_signMethod, SDKConstants.SIGNMETHOD_RSA); + parameters.put(SDKConstants.param_certId, certDescriptor.getSignCertId()); + 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(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 = SignTextUtils.parameterText(parameters, "&", "signature"); + key = signUtils.createSign(key, "", payConfigStorage.getInputCharset()) + "&"; + parameters.put(SDKConstants.param_signature, signUtils.createSign(signStr, key, payConfigStorage.getInputCharset())); + break; + default: + throw new PayErrorException(new PayException("sign fail", "未找到的签名类型")); + } + + + return parameters; + } + + + /** + * 验证证书链 + * + * @param cert 需要验证的证书 + */ + private X509Certificate verifyCertificate(X509Certificate cert) { + try { + cert.checkValidity();//验证有效期 + X509Certificate middleCert = certDescriptor.getPublicCert(); + X509Certificate rootCert = certDescriptor.getRootCert(); + + X509CertSelector selector = new X509CertSelector(); + selector.setCertificate(cert); + + Set trustAnchors = new HashSet(); + trustAnchors.add(new TrustAnchor(rootCert, null)); + PKIXBuilderParameters pkixParams = new PKIXBuilderParameters(trustAnchors, selector); + + Set intermediateCerts = new HashSet(); + intermediateCerts.add(rootCert); + intermediateCerts.add(middleCert); + intermediateCerts.add(cert); + + pkixParams.setRevocationEnabled(false); + + CertStore intermediateCertStore = CertStore.getInstance("Collection", new CollectionCertStoreParameters(intermediateCerts)); + pkixParams.addCertStore(intermediateCertStore); + + CertPathBuilder builder = CertPathBuilder.getInstance("PKIX"); + + /*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 (GeneralSecurityException e) { + LOG.error("", e); + } + return null; + } + + /** + * 发送订单 + * + * @param order 发起支付的订单信息 + * @return 返回支付结果 + */ + + public JSONObject postOrder(PayOrder order, String url) { + Map params = orderInfo(order); + String responseStr = getHttpRequestTemplate().postForObject(url, 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) { + + 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); + } + + /** + * 获取输出二维码,用户返回给支付端, + * + * @param order 发起支付的订单信息 + * @return 返回图片信息,支付时需要的 + */ + @Override + public String getQrPay(PayOrder order) { + order.setTransactionType(UnionTransactionType.APPLY_QR_CODE); + JSONObject response = postOrder(order, getBackTransUrl()); + 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), response.toJSONString())); + } + throw new PayErrorException(new PayException("failure", "验证签名失败", response.toJSONString())); + } + + /** + * 刷卡付,pos主动扫码付款(条码付) + * + * @param order 发起支付的订单信息 + * @return 返回支付结果 + */ + @Override + public Map microPay(PayOrder order) { + order.setTransactionType(UnionTransactionType.CONSUME); + JSONObject response = postOrder(order, getBackTransUrl()); + return response; + } + + + /** + * 将字符串转换为X509Certificate对象. + * + * @param x509CertString 证书串 + * @return X509Certificate + */ + public static X509Certificate genCertificateByStr(String x509CertString) { + X509Certificate x509Cert = null; + try { + CertificateFactory cf = CertificateFactory.getInstance("X.509"); + InputStream tIn = new ByteArrayInputStream(x509CertString.getBytes("ISO-8859-1")); + x509Cert = (X509Certificate) cf.generateCertificate(tIn); + } + catch (Exception e) { + throw new PayErrorException(new PayException("证书加载失败", "gen certificate error:" + e.getLocalizedMessage())); + } + return x509Cert; + } + + /** + * 获取输出消息,用户返回给支付端 + * + * @param code 状态 + * @param message 消息 + * @return 返回输出消息 + */ + @Override + public PayOutMessage getPayOutMessage(String code, String message) { + return PayTextOutMessage.TEXT().content(code.toLowerCase()).build(); + } + + /** + * 获取成功输出消息,用户返回给支付端 + * 主要用于拦截器中返回 + * + * @param payMessage 支付回调消息 + * @return 返回输出消息 + */ + @Override + public PayOutMessage successPayOutMessage(PayMessage payMessage) { + return getPayOutMessage("ok", null); + } + + /** + * 功能:生成自动跳转的Html表单 + * + * @param orderInfo 发起支付的订单信息 + * @param method 请求方式 "post" "get", + * @return 生成自动跳转的Html表单返回给支付端, 针对于PC端 + * @see MethodType 请求类型 + */ + @Override + public String buildRequest(Map orderInfo, MethodType method) { + StringBuffer sf = new StringBuffer(); + sf.append(""); + sf.append("
"); + if (null != orderInfo && 0 != orderInfo.size()) { + for (Map.Entry entry : orderInfo.entrySet()) { + String key = entry.getKey(); + Object value = entry.getValue(); + sf.append(""); + } + } + sf.append(""); + sf.append(""); + sf.append(""); + sf.append(""); + return sf.toString(); + } + + /** + * 功能:将订单信息进行签名并提交请求 + * 业务范围:手机支付控件(含安卓Pay) + * + * @param order 订单信息 + * @return 成功:返回支付结果 失败:返回 + */ + @Override + public Map app(PayOrder order) { + if (null == order.getTransactionType()) { + order.setTransactionType(UnionTransactionType.APP); + } + JSONObject response = postOrder(order, getAppTransUrl()); + if (this.verify(response)) { + if (SDKConstants.OK_RESP_CODE.equals(response.get(SDKConstants.param_respCode))) { +// //成功,获取tn号 +// String tn = (String)response.get(SDKConstants.param_tn); +// //TODO + return response; + } + 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", "验证签名失败", response.toJSONString())); + } + + /** + * 交易查询接口 + * + * @param tradeNo 支付平台订单号 + * @param outTradeNo 商户单号 + * @return 返回查询回来的结果集,支付方原值返回 + */ + @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, assistOrder.getOutTradeNo()); + this.setSign(params); + String responseStr = getHttpRequestTemplate().postForObject(this.getSingleQueryUrl(), params, String.class); + return UriVariables.getParametersToMap(responseStr); + } + + + /** + * 消费撤销/退货接口 + * + * @param origQryId 原交易查询流水号. + * @param orderId 退款单号 + * @param refundAmount 退款金额 + * @param type UnionTransactionType.REFUND 或者UnionTransactionType.CONSUME_UNDO + * @return 返回支付方申请退款后的结果 + */ + public UnionRefundResult unionRefundOrConsumeUndo(String origQryId, String orderId, BigDecimal refundAmount, UnionTransactionType type) { + return unionRefundOrConsumeUndo(new RefundOrder(orderId, origQryId, refundAmount), type); + + } + + /** + * 消费撤销/退货接口 + * + * @param refundOrder 退款订单信息 + * @param type UnionTransactionType.REFUND 或者UnionTransactionType.CONSUME_UNDO + * @return 返回支付方申请退款后的结果 + */ + public UnionRefundResult unionRefundOrConsumeUndo(RefundOrder refundOrder, UnionTransactionType type) { + Map params = this.getCommonParam(); + type.convertMap(params); + 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); + + if (this.verify(new NoticeParams(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())); + } + throw new PayErrorException(new PayException("failure", "验证签名失败", response.toJSONString())); + } + + /** + * 交易关闭接口 + * 使用冲正接口 + * + * @param tradeNo 支付平台订单号 + * @param outTradeNo 商户单号 + * @return 返回支付方交易关闭后的结果 + */ + @Override + public Map close(String tradeNo, String outTradeNo) { + return Collections.emptyMap(); + } + + /** + * 交易关闭接口 + * TODO 这个是冲正接口, 后续进行迁移 + * + * @param assistOrder 关闭订单 + * @return 返回支付方交易关闭后的结果 + */ + @Override + public Map close(AssistOrder assistOrder) { + Map params = this.getCommonParam(); + //交易类型 + params.put(SDKConstants.param_txnType, "99"); + //交易子类 + params.put(SDKConstants.param_txnSubType, "01"); + //业务类型 + params.put(SDKConstants.param_bizType,"000000"); + //渠道类型 + params.put(SDKConstants.param_channelType,"08"); + // 订单号 + params.put(SDKConstants.param_orderId, assistOrder.getOutTradeNo()); + this.setSign(params); + String responseStr = getHttpRequestTemplate().postForObject(this.getBackTransUrl(), params, String.class); + return UriVariables.getParametersToMap(responseStr); + } + + @Override + public UnionRefundResult refund(RefundOrder refundOrder) { + return unionRefundOrConsumeUndo(refundOrder, UnionTransactionType.REFUND); + } + + + /** + * 查询退款 + * + * @param refundOrder 退款订单单号信息 + * @return 返回支付方查询退款后的结果 + */ + @Override + public Map refundquery(RefundOrder refundOrder) { + return Collections.emptyMap(); + } + + /** + * 下载对账单 + * + * @param billDate 账单时间 + * @param fileType 文件类型 文件类型,一般商户填写00即可 + * @return 返回fileContent 请自行将数据落地 + */ + @Override + public Map downloadBill(Date billDate, String fileType) { + return downloadBill(billDate, new UnionPayBillType(fileType)); + } + + /** + * 下载对账单 + * + * @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.getFileType()); + 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())); + } + + + /** + * 创建消息 + * + * @param message 支付平台返回的消息 + * @return 支付消息对象 + */ + @Override + public PayMessage createMessage(Map message) { + return UnionPayMessage.create(message); + } } From f92788998b4add3481098d04b378a5a9ce5240f2 Mon Sep 17 00:00:00 2001 From: xxm1995 Date: Mon, 11 Mar 2024 13:28:49 +0800 Subject: [PATCH 4/9] =?UTF-8?q?doc=20=E6=9B=B4=E6=96=B0=E7=94=A8=E6=88=B7?= =?UTF-8?q?=E5=8D=8F=E8=AE=AE=E5=92=8C=E5=BC=80=E6=BA=90=E5=8D=8F=E8=AE=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- LICENSE | 2 +- README.md | 10 ++++++-- 用户授权使用协议.txt | 56 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 65 insertions(+), 3 deletions(-) create mode 100644 用户授权使用协议.txt diff --git a/LICENSE b/LICENSE index bfa5a867..077410a0 100644 --- a/LICENSE +++ b/LICENSE @@ -186,7 +186,7 @@ same "printed page" as the copyright notice for easier identification within third-party archives. - Copyright (c) 2021 bootx Authors. All Rights Reserved. + Copyright (c) 2022 济南易杯光年软件技术有限公司 Authors. All Rights Reserved. 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/README.md b/README.md index 3dae1968..7085bcca 100644 --- a/README.md +++ b/README.md @@ -9,13 +9,17 @@

+## ❗使用须知 + +`DaxPay`是一款基于`Apache License 2.0`协议分发的开源软件,受中华人民共和国相关法律法规的保护和限制,可以在符合[《用户授权使用协议》](用户授权使用协议.txt)和 +[《Apache License 2.0》](LICENSE)开源协议情况下进行免费使用、学习和交流。**在使用前请阅读上述协议,如果不同意请勿进行使用。** + + ## 🍈项目介绍 > DaxPay是一套基于Bootx-Platform脚手架构建的开源支付网关系统,已经对接支付宝、微信支付相关的接口,以及扩展了钱包支付、储值卡支付、现金支付等新的支付方式。 > 可以独立部署,提供接口供业务系统进行调用,不对原有系统产生影响 -> 当前处于功能开发阶段,部分功能可能会有调整,`V2.1.0`时将作为正式生产可用版本进行发布,之后会保证系统版本非大版本升级时,API接口和数据接口向前兼容 - ## 🧭 特色功能 - 封装各类支付通道的接口为统一的接口,方便业务系统进行调用,简化对接多种支付方式的复杂度 - 已对接`微信支付`和`支付宝`相关的接口,目前已经支持`V2`版本的接口,后续版本将支持`V3`版本的接口 @@ -156,6 +160,8 @@ public class SimplePayOrderTest { ![](https://s11.ax1x.com/2024/02/13/pF8s2VS.jpg) ## 🛣️ 路线图 +> 当前处于功能开发阶段,部分功能可能会有调整,`V2.1.0`时将作为正式生产可用版本进行发布,之后会保证系统版本非大版本升级时,API接口和数据接口向前兼容 + [**开发进度和任务池**](/_doc/Task.md) [**更新记录**](/_doc/ChangeLog.md) diff --git a/用户授权使用协议.txt b/用户授权使用协议.txt new file mode 100644 index 00000000..5906b43f --- /dev/null +++ b/用户授权使用协议.txt @@ -0,0 +1,56 @@ +感谢您选择DaxPay支付系统(以下简称DaxPay),DaxPay是一款支付系统,基于 Java 的技术开发,全部源码开放。 + +为了使您正确并合法地使用本软件,请您在使用前务必阅读清楚下面的协议条款: + +一、本授权协议适用且仅适用于 DaxPay 2.X.X 版本,如果不同意该协议,请不要进行使用 + +二、 产品介绍和产品模式 +1. DaxPay支付系统是由济南易杯光年软件技术有限公司研发的支付产品,版权归济南易杯光年软件技术有限公司所有 + +2. DaxPay旨在解决需要支付交易流的开发者、个体户、公司等提供便捷的支付产品 + +3. DaxPay只是一个应用软件,用户部署后只负责信息流的传递,不负责资金流,支付资金全部由用户选择的支付平台直接结算 + +4. DaxPay不会私自上传任何数据到外部,但无法保证所系统中三方SDK和依赖是否会对外传输数据 + +三、协议许可的权利 + +1. 您可以在完全遵守本最终用户授权使用协议的基础上,将本软件应用于商业用途,而不必支付软件版权授权费用 + +2. 您可以在协议规定的约束和限制范围内修改 DaxPay 源代码或界面风格以适应您的要求 + +3. 您拥有使用本软件构建的网站全部内容所有权,并独立承担与这些内容的相关法律义务 + +4. 本软件由济南易杯光年软件技术有限公司进行分发,没有授权分销商,也没有任何分公司、代理商、办事处、经销商等销售和分发本产品 + +四、有限担保和免责声明 +1. 本软件及所附带的文件是作为不提供任何明确的或隐含的赔偿或担保的形式提供的。 + +2. 用户出于自愿而使用本软件,您必须了解使用本软件的风险,我们不承诺对用户提供任何形式的技术支持、使用担保,也不承担任何因使用本软件而产生问题的相关责任。 + +3. 电子文本形式的授权协议如同双方书面签署的协议一样,具有完全的和等同的法律效力。您一旦开始确认本协议并使用DaxPay,即被视为完全理解并接受本协议的各项条款,在享有上述条款授予的权力的同时,受到相关的约束和限制。协议许可范围以外的行为,将直接违反本授权协议并构成侵权,我们有权随时终止授权,责令停止损害,并保留追究相关责任的权力。 + +4. DaxPay系统由用户自行部署使用,不提供在线服务、服务托管等服务,用户对外提供服务时,请保证已经根据国家相关法律法规获取到相应的许可 + +5. 如果本软件带有其它软件的整合API示范例子包,这些文件版权不属于本软件官方,并且这些文件是没经过授权发布的,请参考相关软件的使用许可合法的使用。 + +6. 客户不得利用DaxPay产品从事非法行为,客户应当合法合规的使用产品,DaxPay不承担客户因非法行为造成的任何法律责任。 + +7. 如发现客户在使用产品时有任何的非法行为,有权解除授权停止技术支持,并配合有关机关进行调查或向政府部门举报。 + +五、禁止接入的内容包括但不限于以下内容 + +1. 诈骗、BC严禁接入、赛车、V盘、X资金盘、贷款、P2P、汇兑、ICO、二清支付严禁接入 + +2. 1元购类、高额返利类 、多级分销类、盲盒类、单纯形式抽奖类、发卡平台严禁接入 + +3. 恋爱话术类、数字藏品、色情、政治相关、影视小说、电子书、游戏平台严禁接入 + +4. 付费入群、帐号销售、刷单、卡盟平台、刷粉、VPN、售卖假货严禁接入 + +5. 未取得相关资质的游戏、私服等禁止接入 + +6. 微信/支付宝/银联官方要求禁止接入的、其他支付通道禁止接入的、以及所有国家法规规定禁止的一切情景 + +协议时间:2023年01月01日 +济南易杯光年软件技术有限公司 From 4e3ac1dca65039ad5f6e5fcd63dd361196e9fb70 Mon Sep 17 00:00:00 2001 From: xxm1995 Date: Mon, 11 Mar 2024 18:47:01 +0800 Subject: [PATCH 5/9] =?UTF-8?q?feat=20=E4=BA=91=E9=97=AA=E4=BB=98=E6=94=AF?= =?UTF-8?q?=E4=BB=98=E5=AF=B9=E6=8E=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 1 + _doc/Task.md | 6 +- .../daxpay/sdk/payment/PayCloseOrderTest.java | 2 + .../daxpay/sdk/payment/PayOrderSyncTest.java | 2 + .../daxpay/sdk/payment/PayOrderTest.java | 2 + .../sdk/payment/RefundOrderSyncTest.java | 2 + .../daxpay/sdk/payment/RefundOrderTest.java | 2 + .../sdk/payment/SimplePayOrderTest.java | 2 + .../sdk/payment/SimpleRefundOrderTest.java | 2 + .../daxpay/sdk/query/QueryPayOrderTest.java | 2 + .../sdk/query/QueryRefundOrderTest.java | 2 + .../daxpay/code/RefundSyncStatusEnum.java | 4 +- .../daxpay/service/code/UnionPayCode.java | 48 +++----- .../service/UnionPayCallbackService.java | 59 ++++++--- .../union/service/UnionPayConfigService.java | 10 +- .../union/service/UnionPayRefundService.java | 45 ++----- .../union/service/UnionPayService.java | 24 ---- .../union/service/UnionPaySyncService.java | 112 ++++++++---------- .../service/WeChatPayCallbackService.java | 7 +- .../refund/service/RefundAssistService.java | 1 + .../refund/strategy/UnionRefundStrategy.java | 2 +- .../strategy/pay/UnionPayRepairStrategy.java | 31 ++++- .../factory/RefundSyncStrategyFactory.java | 4 + .../payment/sync/service/PaySyncService.java | 6 +- .../sync/service/RefundSyncService.java | 5 +- .../Refund/UnionRefundSyncStrategy.java | 49 ++++++++ .../service/sdk/union/api/UnionPayKit.java | 7 +- 用户授权使用协议.txt | 7 +- 28 files changed, 252 insertions(+), 194 deletions(-) create mode 100644 daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/payment/sync/strategy/Refund/UnionRefundSyncStrategy.java diff --git a/README.md b/README.md index 7085bcca..9822fdd8 100644 --- a/README.md +++ b/README.md @@ -126,6 +126,7 @@ public class SimplePayOrderTest { .serviceUrl("http://127.0.0.1:9000") // 需要跟网关中配置一致 .signSecret("123456") + .signType(SignTypeEnum.HMAC_SHA256) .build(); DaxPayKit.initConfig(config); } diff --git a/_doc/Task.md b/_doc/Task.md index 0f7c51f8..affe8267 100644 --- a/_doc/Task.md +++ b/_doc/Task.md @@ -1,9 +1,9 @@ 2.0.3: - [ ] 云闪付接入 - - [ ] 支付 - - [ ] 退款 - - [ ] 同步 + - [x] 支付 + - [x] 退款 + - [x] 同步 - [ ] 对账 - [ ] 回调 - [ ] 统一关闭接口增加使用撤销关闭订单 diff --git a/daxpay-single-sdk/src/test/java/cn/bootx/platform/daxpay/sdk/payment/PayCloseOrderTest.java b/daxpay-single-sdk/src/test/java/cn/bootx/platform/daxpay/sdk/payment/PayCloseOrderTest.java index c65ce586..d223ea94 100644 --- a/daxpay-single-sdk/src/test/java/cn/bootx/platform/daxpay/sdk/payment/PayCloseOrderTest.java +++ b/daxpay-single-sdk/src/test/java/cn/bootx/platform/daxpay/sdk/payment/PayCloseOrderTest.java @@ -1,5 +1,6 @@ package cn.bootx.platform.daxpay.sdk.payment; +import cn.bootx.platform.daxpay.sdk.code.SignTypeEnum; import cn.bootx.platform.daxpay.sdk.model.pay.PayCloseModel; import cn.bootx.platform.daxpay.sdk.net.DaxPayConfig; import cn.bootx.platform.daxpay.sdk.net.DaxPayKit; @@ -22,6 +23,7 @@ public class PayCloseOrderTest { DaxPayConfig config = DaxPayConfig.builder() .serviceUrl("http://127.0.0.1:9000") .signSecret("123456") + .signType(SignTypeEnum.HMAC_SHA256) .build(); DaxPayKit.initConfig(config); } diff --git a/daxpay-single-sdk/src/test/java/cn/bootx/platform/daxpay/sdk/payment/PayOrderSyncTest.java b/daxpay-single-sdk/src/test/java/cn/bootx/platform/daxpay/sdk/payment/PayOrderSyncTest.java index 89ec9cf6..e3eaf9d1 100644 --- a/daxpay-single-sdk/src/test/java/cn/bootx/platform/daxpay/sdk/payment/PayOrderSyncTest.java +++ b/daxpay-single-sdk/src/test/java/cn/bootx/platform/daxpay/sdk/payment/PayOrderSyncTest.java @@ -1,5 +1,6 @@ package cn.bootx.platform.daxpay.sdk.payment; +import cn.bootx.platform.daxpay.sdk.code.SignTypeEnum; import cn.bootx.platform.daxpay.sdk.model.sync.PaySyncModel; import cn.bootx.platform.daxpay.sdk.net.DaxPayConfig; import cn.bootx.platform.daxpay.sdk.net.DaxPayKit; @@ -22,6 +23,7 @@ public class PayOrderSyncTest { DaxPayConfig config = DaxPayConfig.builder() .serviceUrl("http://127.0.0.1:9000") .signSecret("123456") + .signType(SignTypeEnum.HMAC_SHA256) .build(); DaxPayKit.initConfig(config); } diff --git a/daxpay-single-sdk/src/test/java/cn/bootx/platform/daxpay/sdk/payment/PayOrderTest.java b/daxpay-single-sdk/src/test/java/cn/bootx/platform/daxpay/sdk/payment/PayOrderTest.java index 80e0bec6..77de4fe8 100644 --- a/daxpay-single-sdk/src/test/java/cn/bootx/platform/daxpay/sdk/payment/PayOrderTest.java +++ b/daxpay-single-sdk/src/test/java/cn/bootx/platform/daxpay/sdk/payment/PayOrderTest.java @@ -2,6 +2,7 @@ package cn.bootx.platform.daxpay.sdk.payment; import cn.bootx.platform.daxpay.sdk.code.PayChannelEnum; import cn.bootx.platform.daxpay.sdk.code.PayWayEnum; +import cn.bootx.platform.daxpay.sdk.code.SignTypeEnum; import cn.bootx.platform.daxpay.sdk.model.pay.PayOrderModel; import cn.bootx.platform.daxpay.sdk.net.DaxPayConfig; import cn.bootx.platform.daxpay.sdk.net.DaxPayKit; @@ -30,6 +31,7 @@ public class PayOrderTest { DaxPayConfig config = DaxPayConfig.builder() .serviceUrl("http://127.0.0.1:9000") .signSecret("123456") + .signType(SignTypeEnum.HMAC_SHA256) .build(); DaxPayKit.initConfig(config); } diff --git a/daxpay-single-sdk/src/test/java/cn/bootx/platform/daxpay/sdk/payment/RefundOrderSyncTest.java b/daxpay-single-sdk/src/test/java/cn/bootx/platform/daxpay/sdk/payment/RefundOrderSyncTest.java index be665b11..e71aaa4e 100644 --- a/daxpay-single-sdk/src/test/java/cn/bootx/platform/daxpay/sdk/payment/RefundOrderSyncTest.java +++ b/daxpay-single-sdk/src/test/java/cn/bootx/platform/daxpay/sdk/payment/RefundOrderSyncTest.java @@ -1,5 +1,6 @@ package cn.bootx.platform.daxpay.sdk.payment; +import cn.bootx.platform.daxpay.sdk.code.SignTypeEnum; import cn.bootx.platform.daxpay.sdk.model.sync.RefundSyncModel; import cn.bootx.platform.daxpay.sdk.net.DaxPayConfig; import cn.bootx.platform.daxpay.sdk.net.DaxPayKit; @@ -22,6 +23,7 @@ public class RefundOrderSyncTest { DaxPayConfig config = DaxPayConfig.builder() .serviceUrl("http://127.0.0.1:9000") .signSecret("123456") + .signType(SignTypeEnum.HMAC_SHA256) .build(); DaxPayKit.initConfig(config); } diff --git a/daxpay-single-sdk/src/test/java/cn/bootx/platform/daxpay/sdk/payment/RefundOrderTest.java b/daxpay-single-sdk/src/test/java/cn/bootx/platform/daxpay/sdk/payment/RefundOrderTest.java index 680a7a76..7e88af42 100644 --- a/daxpay-single-sdk/src/test/java/cn/bootx/platform/daxpay/sdk/payment/RefundOrderTest.java +++ b/daxpay-single-sdk/src/test/java/cn/bootx/platform/daxpay/sdk/payment/RefundOrderTest.java @@ -1,6 +1,7 @@ package cn.bootx.platform.daxpay.sdk.payment; import cn.bootx.platform.daxpay.sdk.code.PayChannelEnum; +import cn.bootx.platform.daxpay.sdk.code.SignTypeEnum; import cn.bootx.platform.daxpay.sdk.model.refund.RefundModel; import cn.bootx.platform.daxpay.sdk.net.DaxPayConfig; import cn.bootx.platform.daxpay.sdk.net.DaxPayKit; @@ -29,6 +30,7 @@ public class RefundOrderTest { DaxPayConfig config = DaxPayConfig.builder() .serviceUrl("http://127.0.0.1:9000") .signSecret("123456") + .signType(SignTypeEnum.HMAC_SHA256) .build(); DaxPayKit.initConfig(config); } diff --git a/daxpay-single-sdk/src/test/java/cn/bootx/platform/daxpay/sdk/payment/SimplePayOrderTest.java b/daxpay-single-sdk/src/test/java/cn/bootx/platform/daxpay/sdk/payment/SimplePayOrderTest.java index 865cc241..f2e59004 100644 --- a/daxpay-single-sdk/src/test/java/cn/bootx/platform/daxpay/sdk/payment/SimplePayOrderTest.java +++ b/daxpay-single-sdk/src/test/java/cn/bootx/platform/daxpay/sdk/payment/SimplePayOrderTest.java @@ -2,6 +2,7 @@ package cn.bootx.platform.daxpay.sdk.payment; import cn.bootx.platform.daxpay.sdk.code.PayChannelEnum; import cn.bootx.platform.daxpay.sdk.code.PayWayEnum; +import cn.bootx.platform.daxpay.sdk.code.SignTypeEnum; import cn.bootx.platform.daxpay.sdk.model.pay.PayOrderModel; import cn.bootx.platform.daxpay.sdk.net.DaxPayConfig; import cn.bootx.platform.daxpay.sdk.net.DaxPayKit; @@ -24,6 +25,7 @@ public class SimplePayOrderTest { DaxPayConfig config = DaxPayConfig.builder() .serviceUrl("http://127.0.0.1:9000") .signSecret("123456") + .signType(SignTypeEnum.HMAC_SHA256) .build(); DaxPayKit.initConfig(config); } diff --git a/daxpay-single-sdk/src/test/java/cn/bootx/platform/daxpay/sdk/payment/SimpleRefundOrderTest.java b/daxpay-single-sdk/src/test/java/cn/bootx/platform/daxpay/sdk/payment/SimpleRefundOrderTest.java index e5b5f03b..597c2161 100644 --- a/daxpay-single-sdk/src/test/java/cn/bootx/platform/daxpay/sdk/payment/SimpleRefundOrderTest.java +++ b/daxpay-single-sdk/src/test/java/cn/bootx/platform/daxpay/sdk/payment/SimpleRefundOrderTest.java @@ -1,5 +1,6 @@ package cn.bootx.platform.daxpay.sdk.payment; +import cn.bootx.platform.daxpay.sdk.code.SignTypeEnum; import cn.bootx.platform.daxpay.sdk.model.refund.RefundModel; import cn.bootx.platform.daxpay.sdk.net.DaxPayConfig; import cn.bootx.platform.daxpay.sdk.net.DaxPayKit; @@ -26,6 +27,7 @@ public class SimpleRefundOrderTest { DaxPayConfig config = DaxPayConfig.builder() .serviceUrl("http://127.0.0.1:9000") .signSecret("123456") + .signType(SignTypeEnum.HMAC_SHA256) .build(); DaxPayKit.initConfig(config); } diff --git a/daxpay-single-sdk/src/test/java/cn/bootx/platform/daxpay/sdk/query/QueryPayOrderTest.java b/daxpay-single-sdk/src/test/java/cn/bootx/platform/daxpay/sdk/query/QueryPayOrderTest.java index 9758efd9..a7ad6a84 100644 --- a/daxpay-single-sdk/src/test/java/cn/bootx/platform/daxpay/sdk/query/QueryPayOrderTest.java +++ b/daxpay-single-sdk/src/test/java/cn/bootx/platform/daxpay/sdk/query/QueryPayOrderTest.java @@ -1,5 +1,6 @@ package cn.bootx.platform.daxpay.sdk.query; +import cn.bootx.platform.daxpay.sdk.code.SignTypeEnum; import cn.bootx.platform.daxpay.sdk.model.pay.QueryPayOrderModel; import cn.bootx.platform.daxpay.sdk.net.DaxPayConfig; import cn.bootx.platform.daxpay.sdk.net.DaxPayKit; @@ -21,6 +22,7 @@ public class QueryPayOrderTest { DaxPayConfig config = DaxPayConfig.builder() .serviceUrl("http://127.0.0.1:9000") .signSecret("123456") + .signType(SignTypeEnum.HMAC_SHA256) .build(); DaxPayKit.initConfig(config); } diff --git a/daxpay-single-sdk/src/test/java/cn/bootx/platform/daxpay/sdk/query/QueryRefundOrderTest.java b/daxpay-single-sdk/src/test/java/cn/bootx/platform/daxpay/sdk/query/QueryRefundOrderTest.java index 86fbff96..751c19c5 100644 --- a/daxpay-single-sdk/src/test/java/cn/bootx/platform/daxpay/sdk/query/QueryRefundOrderTest.java +++ b/daxpay-single-sdk/src/test/java/cn/bootx/platform/daxpay/sdk/query/QueryRefundOrderTest.java @@ -1,5 +1,6 @@ package cn.bootx.platform.daxpay.sdk.query; +import cn.bootx.platform.daxpay.sdk.code.SignTypeEnum; import cn.bootx.platform.daxpay.sdk.model.refund.QueryRefundOrderModel; import cn.bootx.platform.daxpay.sdk.net.DaxPayConfig; import cn.bootx.platform.daxpay.sdk.net.DaxPayKit; @@ -21,6 +22,7 @@ public class QueryRefundOrderTest { DaxPayConfig config = DaxPayConfig.builder() .serviceUrl("http://127.0.0.1:9000") .signSecret("123456") + .signType(SignTypeEnum.HMAC_SHA256) .build(); DaxPayKit.initConfig(config); } diff --git a/daxpay-single/daxpay-single-core/src/main/java/cn/bootx/platform/daxpay/code/RefundSyncStatusEnum.java b/daxpay-single/daxpay-single-core/src/main/java/cn/bootx/platform/daxpay/code/RefundSyncStatusEnum.java index 09a16592..bf37fc89 100644 --- a/daxpay-single/daxpay-single-core/src/main/java/cn/bootx/platform/daxpay/code/RefundSyncStatusEnum.java +++ b/daxpay-single/daxpay-single-core/src/main/java/cn/bootx/platform/daxpay/code/RefundSyncStatusEnum.java @@ -11,9 +11,11 @@ import lombok.Getter; @Getter @AllArgsConstructor public enum RefundSyncStatusEnum { + SUCCESS("refund_success","退款成功"), FAIL("refund_fail","退款失败"), - PROGRESS("refund_progress","退款中"); + PROGRESS("refund_progress","退款中"), + NOT_FOUND("pay_not_found", "交易不存在"); /** 编码 */ private final String code; diff --git a/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/code/UnionPayCode.java b/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/code/UnionPayCode.java index 1eb44a0d..8566b01d 100644 --- a/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/code/UnionPayCode.java +++ b/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/code/UnionPayCode.java @@ -1,5 +1,7 @@ package cn.bootx.platform.daxpay.service.code; +import com.egzosn.pay.union.bean.SDKConstants; + /** * 云闪付常量 * @author xxm @@ -11,21 +13,29 @@ public interface UnionPayCode { /** 成功状态 */ String SUCCESS = "0"; - /** 状态 0表示成功 */ - String STATUS = "status"; + /** 状态 00表示成功 */ + String RESP_CODE = SDKConstants.param_respCode; - /** 业务结果 0表示成功,非0表示失败 */ - String RESULT_CODE = "result_code"; + /** 业务结果 00表示成功 */ + String RESP_SUCCESS = SDKConstants.OK_RESP_CODE; + + /** 交易类型 */ + String TXN_TYPE = "txnType"; /** 网关订单号 */ String QUERY_ID = "queryId"; - /** 第三方订单号 */ + /** 第三方订单号(本地订单号) */ String ORDER_ID = "orderId"; /** 退款ID */ String REFUND_ID = "refund_id"; + /** 交易类型 支付 */ + String TXN_TYPE_PAY = "01"; + + /** 交易类型 退款 */ + String TXN_TYPE_REFUND = "04"; /** * 订单发送时间 @@ -33,35 +43,13 @@ public interface UnionPayCode { */ String TXN_TIME = "txnTime"; - /** 支付结果 */ - String PAY_RESULT = "pay_result"; + + /** 退款金额 */ + String TXN_AMT = "txnAmt"; /** 总金额 */ String TOTAL_FEE = "settleAmt"; - /** 交易状态 */ - String TRADE_STATE = "trade_state"; - - /** 支付成功 */ - String TRADE_SUCCESS = "SUCCESS"; - - /** 转入退款 */ - String TRADE_REFUND = "REFUND"; - - /** 未支付 */ - String TRADE_NOT_PAY = "NOTPAY"; - - /** 已关闭 */ - String TRADE_CLOSED = "CLOSED"; - - /** 支付失败(其他原因,如银行返回失败) */ - String TRADE_PAY_ERROR = "PAYERROR"; - - /** 返回信息 */ - String MESSAGE = "message"; - - /** 错误代码描述 */ - String ERR_MSG = "err_msg"; /** 对账单下载类型编码 */ diff --git a/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/channel/union/service/UnionPayCallbackService.java b/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/channel/union/service/UnionPayCallbackService.java index 3e5b0617..5fd7706a 100644 --- a/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/channel/union/service/UnionPayCallbackService.java +++ b/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/channel/union/service/UnionPayCallbackService.java @@ -3,17 +3,19 @@ package cn.bootx.platform.daxpay.service.core.channel.union.service; import cn.bootx.platform.common.core.util.LocalDateTimeUtil; import cn.bootx.platform.daxpay.code.PayChannelEnum; import cn.bootx.platform.daxpay.code.PayStatusEnum; +import cn.bootx.platform.daxpay.code.RefundStatusEnum; import cn.bootx.platform.daxpay.service.code.PaymentTypeEnum; +import cn.bootx.platform.daxpay.service.code.UnionPayCode; import cn.bootx.platform.daxpay.service.common.context.CallbackLocal; import cn.bootx.platform.daxpay.service.common.local.PaymentContextLocal; import cn.bootx.platform.daxpay.service.core.channel.union.entity.UnionPayConfig; import cn.bootx.platform.daxpay.service.func.AbsCallbackStrategy; import cn.bootx.platform.daxpay.service.sdk.union.api.UnionPayKit; import cn.hutool.core.date.DatePattern; +import cn.hutool.core.thread.ThreadUtil; import cn.hutool.core.util.StrUtil; import cn.hutool.json.JSONUtil; import com.egzosn.pay.common.bean.NoticeParams; -import com.ijpay.core.kit.WxPayKit; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; @@ -22,7 +24,6 @@ import javax.annotation.Resource; import java.time.LocalDateTime; import java.util.Collections; import java.util.Map; -import java.util.Objects; import static cn.bootx.platform.daxpay.service.code.UnionPayCode.*; @@ -49,21 +50,9 @@ public class UnionPayCallbackService extends AbsCallbackStrategy { String callReq = JSONUtil.toJsonStr(params); log.info("云闪付发起回调 报文: {}", callReq); - String status = params.get(STATUS); - String returnCode = params.get(RESULT_CODE); - - // 处理失败 - if (!Objects.equals(SUCCESS, status)||!Objects.equals(SUCCESS, returnCode)){ - return false; - } - // 支付回调信息校验 UnionPayConfig config = unionPayConfigService.getConfig(); UnionPayKit unionPayKit = unionPayConfigService.initPayService(config); - if (Objects.isNull(config)) { - log.warn("云闪付支付配置不存在"); - return false; - } NoticeParams noticeParams = new NoticeParams(); noticeParams.setBody(Collections.unmodifiableMap(params)); @@ -71,13 +60,20 @@ public class UnionPayCallbackService extends AbsCallbackStrategy { } /** - * 判断类型 支付回调/退款回调, 云闪付只有支付回调 + * 判断类型 支付回调/退款回调 * * @see PaymentTypeEnum */ @Override public PaymentTypeEnum getCallbackType() { - return PaymentTypeEnum.PAY; + CallbackLocal callbackInfo = PaymentContextLocal.get().getCallbackInfo(); + Map params = callbackInfo.getCallbackParam(); + String txnType = params.get(TXN_TYPE); + if (UnionPayCode.TXN_TYPE_PAY.equals(txnType)){ + return PaymentTypeEnum.PAY; + } else { + return PaymentTypeEnum.REFUND; + } } /** @@ -93,10 +89,12 @@ public class UnionPayCallbackService extends AbsCallbackStrategy { // 支付订单ID callbackInfo.setOrderId(Long.valueOf(callbackParam.get(ORDER_ID))); // 支付结果 - PayStatusEnum payStatus = WxPayKit.codeIsOk(callbackParam.get(PAY_RESULT)) ? PayStatusEnum.SUCCESS : PayStatusEnum.FAIL; + String resultCode = callbackParam.get(UnionPayCode.RESP_CODE); + PayStatusEnum payStatus = UnionPayCode.RESP_SUCCESS.equals(resultCode) ? PayStatusEnum.SUCCESS : PayStatusEnum.FAIL; + callbackInfo.setGatewayStatus(payStatus.getCode()); // 支付金额 - callbackInfo.setAmount(callbackParam.get(TOTAL_FEE)); + callbackInfo.setAmount(callbackParam.get(UnionPayCode.TXN_AMT)); String timeEnd = callbackParam.get(TXN_TIME); if (StrUtil.isNotBlank(timeEnd)) { LocalDateTime time = LocalDateTimeUtil.parse(timeEnd, DatePattern.PURE_DATETIME_PATTERN); @@ -111,6 +109,31 @@ public class UnionPayCallbackService extends AbsCallbackStrategy { */ @Override public void resolveRefundData() { + // 云闪付需要延迟半秒再进行处理, 不然会出现业务未处理完, 但回调已经到达的情况 + ThreadUtil.sleep(300); + + CallbackLocal callbackInfo = PaymentContextLocal.get().getCallbackInfo(); + Map callbackParam = callbackInfo.getCallbackParam(); + // 网关订单号 + callbackInfo.setGatewayOrderNo(callbackParam.get(QUERY_ID)); + // 退款订单Id + callbackInfo.setOrderId(Long.valueOf(callbackParam.get(ORDER_ID))); + // 退款金额 + callbackInfo.setAmount(callbackParam.get(TXN_AMT)); + + // 交易状态 + String resultCode = callbackParam.get(UnionPayCode.RESP_CODE); + RefundStatusEnum refundStatus = UnionPayCode.RESP_SUCCESS.equals(resultCode) ? RefundStatusEnum.SUCCESS : RefundStatusEnum.FAIL; + callbackInfo.setGatewayStatus(refundStatus.getCode()); + + // 退款时间 + String timeEnd = callbackParam.get(TXN_TIME); + if (StrUtil.isNotBlank(timeEnd)) { + LocalDateTime time = LocalDateTimeUtil.parse(timeEnd, DatePattern.PURE_DATETIME_PATTERN); + callbackInfo.setFinishTime(time); + } else { + callbackInfo.setFinishTime(LocalDateTime.now()); + } } diff --git a/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/channel/union/service/UnionPayConfigService.java b/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/channel/union/service/UnionPayConfigService.java index b96a48c1..9e72aed5 100644 --- a/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/channel/union/service/UnionPayConfigService.java +++ b/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/channel/union/service/UnionPayConfigService.java @@ -93,19 +93,19 @@ public class UnionPayConfigService { unionPayConfigStorage.setInputCharset(CharsetUtil.UTF_8); // 商户号 unionPayConfigStorage.setMerId(config.getMachId()); - //是否为证书签名 - unionPayConfigStorage.setCertSign(config.isCertSign()); + // 云闪付必须使用证书才可以进行调用 + unionPayConfigStorage.setCertSign(true); - //中级证书 流 + // 中级证书 流 unionPayConfigStorage.setAcpMiddleCert(new ByteArrayInputStream(Base64.decode(config.getAcpMiddleCert()))); - //根证书 流 + // 根证书 流 unionPayConfigStorage.setAcpRootCert(new ByteArrayInputStream(Base64.decode(config.getAcpRootCert()))); // 私钥证书 流 unionPayConfigStorage.setKeyPrivateCert(new ByteArrayInputStream(Base64.decode(config.getKeyPrivateCert()))); //私钥证书对应的密码 私钥证书对应的密码 unionPayConfigStorage.setKeyPrivateCertPwd(config.getKeyPrivateCertPwd()); - //设置证书对应的存储方式,证书字符串信息 + //设置证书对应的存储方式,证书流 unionPayConfigStorage.setCertStoreType(CertStoreType.INPUT_STREAM); // 回调地址 diff --git a/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/channel/union/service/UnionPayRefundService.java b/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/channel/union/service/UnionPayRefundService.java index 912c976b..c0956ef7 100644 --- a/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/channel/union/service/UnionPayRefundService.java +++ b/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/channel/union/service/UnionPayRefundService.java @@ -1,25 +1,20 @@ package cn.bootx.platform.daxpay.service.core.channel.union.service; -import cn.bootx.platform.daxpay.exception.pay.PayFailureException; +import cn.bootx.platform.daxpay.code.RefundStatusEnum; import cn.bootx.platform.daxpay.service.code.UnionPayCode; import cn.bootx.platform.daxpay.service.common.context.RefundLocal; import cn.bootx.platform.daxpay.service.common.local.PaymentContextLocal; import cn.bootx.platform.daxpay.service.core.order.pay.entity.PayChannelOrder; +import cn.bootx.platform.daxpay.service.core.order.pay.entity.PayOrder; import cn.bootx.platform.daxpay.service.core.order.refund.entity.RefundOrder; import cn.bootx.platform.daxpay.service.sdk.union.api.UnionPayKit; import cn.bootx.platform.daxpay.service.sdk.union.bean.UnionRefundOrder; -import cn.hutool.core.util.StrUtil; import com.egzosn.pay.union.bean.UnionRefundResult; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; import java.math.BigDecimal; -import java.util.Map; -import java.util.Objects; -import java.util.Optional; - -import static cn.bootx.platform.daxpay.service.code.UnionPayCode.*; /** * 云闪付退款操作 @@ -34,46 +29,22 @@ public class UnionPayRefundService { /** * 退款方法 */ - public void refund(RefundOrder refundOrder, int amount, PayChannelOrder channelOrder, UnionPayKit unionPayKit) { + public void refund(RefundOrder refundOrder, PayOrder payOrder, int amount, PayChannelOrder channelOrder, UnionPayKit unionPayKit) { // 金额转换 BigDecimal refundAmount = BigDecimal.valueOf(amount * 0.01); BigDecimal orderAmount = BigDecimal.valueOf(channelOrder.getAmount() * 0.01); UnionRefundOrder unionRefundOrder = new UnionRefundOrder(); - unionRefundOrder.setOutTradeNo(String.valueOf(refundOrder.getPaymentId())); unionRefundOrder.setRefundNo(String.valueOf(refundOrder.getId())); + unionRefundOrder.setTradeNo(String.valueOf(payOrder.getGatewayOrderNo())); unionRefundOrder.setRefundAmount(refundAmount); unionRefundOrder.setTotalAmount(orderAmount); UnionRefundResult refund = unionPayKit.refund(unionRefundOrder); -// String xmlResult = UnionPayApi.execution(unionPayConfig.getServerUrl(), params); -// Map result = WxPayKit.xmlToMap(xmlResult); -// this.verifyErrorMsg(result); -// // 云闪付退款是否成功需要查询状态, 所以设置为退款中状态 -// RefundLocal refundInfo = PaymentContextLocal.get().getRefundInfo(); -// refundInfo.setStatus(RefundStatusEnum.PROGRESS) -// .setGatewayOrderNo(result.get(REFUND_ID)); - } - - /** - * 验证错误信息 - */ - private void verifyErrorMsg(Map result) { - String status = result.get(UnionPayCode.STATUS); - String returnCode = result.get(UnionPayCode.RESULT_CODE); - - // 判断查询是否成功 - if (!(Objects.equals(SUCCESS, status) && Objects.equals(SUCCESS, returnCode))){ - String errorMsg = result.get(ERR_MSG); - if (StrUtil.isBlank(errorMsg)) { - errorMsg = result.get(MESSAGE); - } - log.error("订单退款失败 {}", errorMsg); - RefundLocal refundInfo = PaymentContextLocal.get().getRefundInfo(); - refundInfo.setErrorMsg(errorMsg); - refundInfo.setErrorCode(Optional.ofNullable(returnCode).orElse(returnCode)); - throw new PayFailureException(errorMsg); - } + String gatewayNo = (String) refund.getAttr(UnionPayCode.QUERY_ID); + // 云闪付退款是否成功需要查询状态, 所以设置为退款中状态 + RefundLocal refundInfo = PaymentContextLocal.get().getRefundInfo(); + refundInfo.setStatus(RefundStatusEnum.PROGRESS).setGatewayOrderNo(gatewayNo); } } diff --git a/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/channel/union/service/UnionPayService.java b/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/channel/union/service/UnionPayService.java index 43f0dbf2..f114e5a9 100644 --- a/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/channel/union/service/UnionPayService.java +++ b/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/channel/union/service/UnionPayService.java @@ -5,7 +5,6 @@ import cn.bootx.platform.daxpay.exception.pay.PayFailureException; import cn.bootx.platform.daxpay.param.channel.UnionPayParam; import cn.bootx.platform.daxpay.param.pay.PayChannelParam; import cn.bootx.platform.daxpay.service.code.AliPayWay; -import cn.bootx.platform.daxpay.service.code.UnionPayCode; import cn.bootx.platform.daxpay.service.common.context.PayLocal; import cn.bootx.platform.daxpay.service.common.local.PaymentContextLocal; import cn.bootx.platform.daxpay.service.core.channel.union.entity.UnionPayConfig; @@ -14,7 +13,6 @@ import cn.bootx.platform.daxpay.service.sdk.union.api.UnionPayKit; import cn.bootx.platform.daxpay.service.sdk.union.bean.UnionPayOrder; import cn.hutool.core.collection.CollUtil; import cn.hutool.core.date.DateUtil; -import cn.hutool.core.util.StrUtil; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; @@ -22,11 +20,8 @@ import org.springframework.stereotype.Service; import java.math.BigDecimal; import java.util.Date; import java.util.Map; -import java.util.Objects; import java.util.Optional; -import static cn.bootx.platform.daxpay.service.code.UnionPayCode.*; - /** * 云闪付支付 * @author xxm @@ -163,23 +158,4 @@ public class UnionPayService { // String xmlResult = UnionPayApi.execution(unionPayKit.getServerUrl(), params); } - - /** - * 验证错误信息 - */ - private void verifyErrorMsg(Map result) { - String status = result.get(UnionPayCode.STATUS); - String returnCode = result.get(UnionPayCode.RESULT_CODE); - - // 判断查询是否成功 - if (!(Objects.equals(SUCCESS, status) && Objects.equals(SUCCESS, returnCode))){ - String errorMsg = result.get(ERR_MSG); - if (StrUtil.isBlank(errorMsg)) { - errorMsg = result.get(MESSAGE); - } - log.error("订单关闭失败 {}", errorMsg); - throw new PayFailureException(errorMsg); - } - } - } diff --git a/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/channel/union/service/UnionPaySyncService.java b/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/channel/union/service/UnionPaySyncService.java index 605cdbdd..b4b1bb1a 100644 --- a/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/channel/union/service/UnionPaySyncService.java +++ b/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/channel/union/service/UnionPaySyncService.java @@ -9,10 +9,8 @@ import cn.bootx.platform.daxpay.service.core.order.refund.entity.RefundOrder; import cn.bootx.platform.daxpay.service.core.payment.sync.result.PayGatewaySyncResult; import cn.bootx.platform.daxpay.service.core.payment.sync.result.RefundGatewaySyncResult; import cn.bootx.platform.daxpay.service.sdk.union.api.UnionPayKit; -import cn.bootx.platform.daxpay.service.sdk.union.bean.UnionRefundOrder; import cn.hutool.core.date.DatePattern; import cn.hutool.core.map.MapUtil; -import cn.hutool.core.util.StrUtil; import cn.hutool.json.JSONUtil; import com.egzosn.pay.common.bean.AssistOrder; import com.egzosn.pay.common.bean.NoticeParams; @@ -25,7 +23,8 @@ import java.time.LocalDateTime; import java.util.Map; import java.util.Objects; -import static cn.bootx.platform.daxpay.service.code.UnionPayCode.*; +import static cn.bootx.platform.daxpay.service.code.UnionPayCode.QUERY_ID; +import static cn.bootx.platform.daxpay.service.code.UnionPayCode.TXN_TIME; /** * 云闪付支付同步 @@ -44,6 +43,7 @@ public class UnionPaySyncService { PayGatewaySyncResult syncResult = new PayGatewaySyncResult().setSyncStatus(PaySyncStatusEnum.FAIL); AssistOrder query = new AssistOrder(); +// query.setOutTradeNo("1766811070348001280"); query.setOutTradeNo(String.valueOf(order.getId())); Map result = unionPayKit.query(query); @@ -53,24 +53,29 @@ public class UnionPaySyncService { return syncResult.setErrorMsg("查询订单验签失败"); } + String resultCode = MapUtil.getStr(result, UnionPayCode.RESP_CODE); + + // 订单不存在 + if (Objects.equals(resultCode, "34")) { + return syncResult.setSyncStatus(PaySyncStatusEnum.NOT_FOUND); + } // 查询失败 - String resultCode = MapUtil.getStr(result, SDKConstants.param_respCode); - if (!SDKConstants.OK_RESP_CODE.equals(resultCode)) { + if (!UnionPayCode.RESP_SUCCESS.equals(resultCode)) { log.warn("查询云闪付订单失败:{}", result); return syncResult.setErrorMsg(MapUtil.getStr(result, SDKConstants.param_respMsg)); } + // 状态响应码 String origRespCode = MapUtil.getStr(result, SDKConstants.param_origRespCode); - // 查询流水号, 相当于网关订单号 - - // 成功 - if (Objects.equals(origRespCode, SDKConstants.OK_RESP_CODE)) { + if (Objects.equals(origRespCode, UnionPayCode.RESP_SUCCESS)) { + // 查询流水号, 相当于网关订单号 String queryId = MapUtil.getStr(result, QUERY_ID); String timeEnd = MapUtil.getStr(result, TXN_TIME); LocalDateTime time = LocalDateTimeUtil.parse(timeEnd, DatePattern.PURE_DATETIME_PATTERN); return syncResult.setGatewayOrderNo(queryId).setPayTime(time).setSyncStatus(PaySyncStatusEnum.SUCCESS); } + // 支付超时 交易不在受理时间范围内 if (Objects.equals(origRespCode, "39")) { return syncResult.setSyncStatus(PaySyncStatusEnum.TIMEOUT) @@ -81,74 +86,55 @@ public class UnionPaySyncService { if (Objects.equals(origRespCode, "05")) { return syncResult.setSyncStatus(PaySyncStatusEnum.PROGRESS); } -// -// // 已退款/退款中 -// if (Objects.equals(tradeStatus, TRADE_REFUND)) { -// return syncResult.setSyncStatus(PaySyncStatusEnum.REFUND); -// } -// // 已关闭 -// if (Objects.equals(tradeStatus, TRADE_CLOSED)) { -// return syncResult.setSyncStatus(PaySyncStatusEnum.CLOSED); -// } return syncResult; } /** * 退款信息查询 + * 云闪付退款和支付查询接口是一个 */ public RefundGatewaySyncResult syncRefundStatus(RefundOrder refundOrder, UnionPayKit unionPayKit){ RefundGatewaySyncResult syncResult = new RefundGatewaySyncResult(); - UnionRefundOrder query = new UnionRefundOrder(); - query.setRefundNo(String.valueOf(refundOrder.getId())); - Map results = unionPayKit.refundquery(query); + AssistOrder query = new AssistOrder(); + query.setOutTradeNo(String.valueOf(refundOrder.getId())); + Map result = unionPayKit.query(query); - try { -// String xmlResult = WxPayApi.orderRefundQuery(false, params); -// Map result = WxPayKit.xmlToMap(xmlResult); -// syncResult.setSyncInfo(JSONUtil.toJsonStr(result)); + if (!unionPayKit.verify(new NoticeParams(result))) { + log.warn("查询云闪付订单验签失败:{}", result); + return syncResult.setErrorMsg("查询订单验签失败"); + } + String resultCode = MapUtil.getStr(result, UnionPayCode.RESP_CODE); - // 设置微信支付网关订单号 -// syncResult.setGatewayOrderNo(result.get(UnionPayCode.REFUND_ID)); - // 状态 -// String tradeStatus = result.get(UnionPayCode.REFUND_STATUS); -// // 退款成功 -// if (Objects.equals(tradeStatus, UnionPayCode.REFUND_SUCCESS)) { -// String timeEnd = result.get(UnionPayCode.REFUND_SUCCESS_TIME); -// LocalDateTime time = LocalDateTimeUtil.parse(timeEnd, DatePattern.NORM_DATETIME_PATTERN); -// return syncResult.setRefundTime(time).setSyncStatus(RefundSyncStatusEnum.SUCCESS); -// } -// // 退款中 -// if (Objects.equals(tradeStatus, UnionPayCode.REFUND_PROCESSING)) { -// return syncResult.setSyncStatus(RefundSyncStatusEnum.PROGRESS); -// } -// String errorMsg = this.getErrorMsg(result); -// return syncResult.setSyncStatus(RefundSyncStatusEnum.FAIL).setErrorMsg(errorMsg); - } catch (Exception e) { - log.error("查询退款订单失败:", e); - syncResult.setSyncStatus(RefundSyncStatusEnum.PROGRESS).setErrorMsg(e.getMessage()); + // 订单不存在 + if (Objects.equals(resultCode, "34")) { + return syncResult.setSyncStatus(RefundSyncStatusEnum.NOT_FOUND); + } + + // 查询失败 + if (!UnionPayCode.RESP_SUCCESS.equals(resultCode)) { + log.warn("查询云闪付订单失败:{}", result); + return syncResult.setErrorMsg(MapUtil.getStr(result, SDKConstants.param_respMsg)); + } + + // 状态响应码 + String origRespCode = MapUtil.getStr(result, SDKConstants.param_origRespCode); + + // 成功 + if (Objects.equals(origRespCode, UnionPayCode.RESP_SUCCESS)) { + // 查询流水号, 相当于网关订单号 + String queryId = MapUtil.getStr(result, QUERY_ID); + String timeEnd = MapUtil.getStr(result, TXN_TIME); + LocalDateTime time = LocalDateTimeUtil.parse(timeEnd, DatePattern.PURE_DATETIME_PATTERN); + return syncResult.setGatewayOrderNo(queryId).setRefundTime(time).setSyncStatus(RefundSyncStatusEnum.SUCCESS); + } + + // 退款中 + if (Objects.equals(origRespCode, "05")) { + return syncResult.setSyncStatus(RefundSyncStatusEnum.PROGRESS); } return syncResult; } - - /** - * 验证错误信息 - */ - private String getErrorMsg(Map result) { - String status = result.get(UnionPayCode.STATUS); - String returnCode = result.get(UnionPayCode.RESULT_CODE); - - // 判断查询是否成功 - if (!(Objects.equals(SUCCESS, status) && Objects.equals(SUCCESS, returnCode))){ - String errorMsg = result.get(ERR_MSG); - if (StrUtil.isBlank(errorMsg)) { - errorMsg = result.get(MESSAGE); - } - log.error("订单查询失败 {}", errorMsg); - return errorMsg; - } - return null; - } } diff --git a/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/channel/wechat/service/WeChatPayCallbackService.java b/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/channel/wechat/service/WeChatPayCallbackService.java index ab03eff6..34534224 100644 --- a/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/channel/wechat/service/WeChatPayCallbackService.java +++ b/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/channel/wechat/service/WeChatPayCallbackService.java @@ -3,6 +3,7 @@ package cn.bootx.platform.daxpay.service.core.channel.wechat.service; import cn.bootx.platform.common.core.util.LocalDateTimeUtil; import cn.bootx.platform.daxpay.code.PayChannelEnum; import cn.bootx.platform.daxpay.code.PayStatusEnum; +import cn.bootx.platform.daxpay.code.RefundStatusEnum; import cn.bootx.platform.daxpay.service.code.PaymentTypeEnum; import cn.bootx.platform.daxpay.service.common.context.CallbackLocal; import cn.bootx.platform.daxpay.service.common.local.PaymentContextLocal; @@ -126,9 +127,9 @@ public class WeChatPayCallbackService extends AbsCallbackStrategy { callbackInfo.setAmount(callbackParam.get(CALLBACK_REFUND_FEE)); // 交易状态 - PayStatusEnum payStatus = Objects.equals(callbackParam.get(CALLBACK_REFUND_STATUS), REFUND_SUCCESS) - ? PayStatusEnum.SUCCESS : PayStatusEnum.FAIL; - callbackInfo.setGatewayStatus(payStatus.getCode()); + RefundStatusEnum refundStatus = Objects.equals(callbackParam.get(CALLBACK_REFUND_STATUS), REFUND_SUCCESS) + ? RefundStatusEnum.SUCCESS : RefundStatusEnum.FAIL; + callbackInfo.setGatewayStatus(refundStatus.getCode()); // 退款时间 String timeEnd = callbackParam.get(CALLBACK_SUCCESS_TIME); diff --git a/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/payment/refund/service/RefundAssistService.java b/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/payment/refund/service/RefundAssistService.java index fe6280f3..d4cc69c9 100644 --- a/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/payment/refund/service/RefundAssistService.java +++ b/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/payment/refund/service/RefundAssistService.java @@ -244,6 +244,7 @@ public class RefundAssistService { refundOrder.setErrorMsg(refundInfo.getErrorMsg()); // 退款失败不保存剩余可退余额, 否则数据看起开会产生困惑 refundOrder.setRefundableBalance(null); + refundOrderManager.updateById(refundOrder); } } diff --git a/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/payment/refund/strategy/UnionRefundStrategy.java b/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/payment/refund/strategy/UnionRefundStrategy.java index 20678ffd..f2b042c5 100644 --- a/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/payment/refund/strategy/UnionRefundStrategy.java +++ b/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/payment/refund/strategy/UnionRefundStrategy.java @@ -63,7 +63,7 @@ public class UnionRefundStrategy extends AbsRefundStrategy { @Override public void doRefundHandler() { UnionPayKit unionPayKit = unionPayConfigService.initPayService(unionPayConfig); - unionPayRefundService.refund(this.getRefundOrder(), this.getRefundChannelParam().getAmount(), this.getPayChannelOrder(), unionPayKit); + unionPayRefundService.refund(this.getRefundOrder(), this.getPayOrder(), this.getRefundChannelParam().getAmount(), this.getPayChannelOrder(), unionPayKit); } /** diff --git a/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/payment/repair/strategy/pay/UnionPayRepairStrategy.java b/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/payment/repair/strategy/pay/UnionPayRepairStrategy.java index 7ae4a84c..c1cdf87f 100644 --- a/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/payment/repair/strategy/pay/UnionPayRepairStrategy.java +++ b/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/payment/repair/strategy/pay/UnionPayRepairStrategy.java @@ -1,12 +1,17 @@ package cn.bootx.platform.daxpay.service.core.payment.repair.strategy.pay; import cn.bootx.platform.daxpay.code.PayChannelEnum; +import cn.bootx.platform.daxpay.code.PayStatusEnum; +import cn.bootx.platform.daxpay.service.common.local.PaymentContextLocal; +import cn.bootx.platform.daxpay.service.core.channel.union.service.UnionPayRecordService; import cn.bootx.platform.daxpay.service.func.AbsPayRepairStrategy; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.context.annotation.Scope; import org.springframework.stereotype.Service; +import java.time.LocalDateTime; + import static org.springframework.beans.factory.config.BeanDefinition.SCOPE_PROTOTYPE; /** @@ -20,6 +25,7 @@ import static org.springframework.beans.factory.config.BeanDefinition.SCOPE_PROT @RequiredArgsConstructor public class UnionPayRepairStrategy extends AbsPayRepairStrategy { + private final UnionPayRecordService unionPayRecordService; /** * 策略标识 @@ -30,10 +36,31 @@ public class UnionPayRepairStrategy extends AbsPayRepairStrategy { } /** - * 取消支付 + * 支付成功处理 + */ + @Override + public void doPaySuccessHandler() { + LocalDateTime payTime = PaymentContextLocal.get() + .getRepairInfo() + .getFinishTime(); + this.getChannelOrder().setStatus(PayStatusEnum.SUCCESS.getCode()) + .setPayTime(payTime); + unionPayRecordService.pay(this.getOrder(), this.getChannelOrder()); + } + + /** + * 等待支付处理 + */ + @Override + public void doWaitPayHandler(){ + this.getChannelOrder().setPayTime(null).setStatus(PayStatusEnum.PROGRESS.getCode()); + } + + /** + * 关闭本地支付 */ @Override public void doCloseLocalHandler() { - + this.getChannelOrder().setStatus(PayStatusEnum.CLOSE.getCode()); } } diff --git a/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/payment/sync/factory/RefundSyncStrategyFactory.java b/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/payment/sync/factory/RefundSyncStrategyFactory.java index 07484d6f..6d15ce75 100644 --- a/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/payment/sync/factory/RefundSyncStrategyFactory.java +++ b/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/payment/sync/factory/RefundSyncStrategyFactory.java @@ -3,6 +3,7 @@ package cn.bootx.platform.daxpay.service.core.payment.sync.factory; import cn.bootx.platform.daxpay.code.PayChannelEnum; import cn.bootx.platform.daxpay.exception.pay.PayUnsupportedMethodException; import cn.bootx.platform.daxpay.service.core.payment.sync.strategy.Refund.AliRefundSyncStrategy; +import cn.bootx.platform.daxpay.service.core.payment.sync.strategy.Refund.UnionRefundSyncStrategy; import cn.bootx.platform.daxpay.service.core.payment.sync.strategy.Refund.WeChatRefundSyncStrategy; import cn.bootx.platform.daxpay.service.func.AbsRefundSyncStrategy; import cn.hutool.extra.spring.SpringUtil; @@ -27,6 +28,9 @@ public class RefundSyncStrategyFactory { case ALI: strategy = SpringUtil.getBean(AliRefundSyncStrategy.class); break; + case UNION_PAY: + strategy = SpringUtil.getBean(UnionRefundSyncStrategy.class); + break; case WECHAT: strategy = SpringUtil.getBean(WeChatRefundSyncStrategy.class); break; diff --git a/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/payment/sync/service/PaySyncService.java b/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/payment/sync/service/PaySyncService.java index b2abc2d0..da328166 100644 --- a/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/payment/sync/service/PaySyncService.java +++ b/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/payment/sync/service/PaySyncService.java @@ -181,7 +181,11 @@ public class PaySyncService { } // 退款比对状态不做额外处理, 需要通过退款接口进行处理 - if (orderStatus.equals(PayStatusEnum.REFUNDED.getCode()) && syncStatus.equals(PaySyncStatusEnum.REFUND)){ + List orderClose = Arrays.asList( + PayStatusEnum.REFUNDED.getCode(), + PayStatusEnum.REFUNDING.getCode(), + PayStatusEnum.PARTIAL_REFUND.getCode()); + if (orderClose.contains(orderStatus) || syncStatus.equals(PaySyncStatusEnum.REFUND)){ return true; } return false; diff --git a/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/payment/sync/service/RefundSyncService.java b/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/payment/sync/service/RefundSyncService.java index 08fbe6c8..4fd38998 100644 --- a/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/payment/sync/service/RefundSyncService.java +++ b/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/payment/sync/service/RefundSyncService.java @@ -170,18 +170,19 @@ public class RefundSyncService { RefundRepairResult repair = new RefundRepairResult(); // 对支付网关同步的结果进行处理 switch (syncStatusEnum) { - // 调用出错 case SUCCESS: repair = repairService.repair(order, RefundRepairWayEnum.REFUND_SUCCESS); break; case PROGRESS: // 不进行处理 - log.warn("退款状态同步接口调用出错"); break; case FAIL: { repair = repairService.repair(order, RefundRepairWayEnum.REFUND_FAIL); break; } + case NOT_FOUND: + repair = repairService.repair(order, RefundRepairWayEnum.REFUND_FAIL); + break; default: { throw new BizException("代码有问题"); } diff --git a/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/payment/sync/strategy/Refund/UnionRefundSyncStrategy.java b/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/payment/sync/strategy/Refund/UnionRefundSyncStrategy.java new file mode 100644 index 00000000..b064a1c9 --- /dev/null +++ b/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/payment/sync/strategy/Refund/UnionRefundSyncStrategy.java @@ -0,0 +1,49 @@ +package cn.bootx.platform.daxpay.service.core.payment.sync.strategy.Refund; + +import cn.bootx.platform.daxpay.code.PayChannelEnum; +import cn.bootx.platform.daxpay.code.PaySyncStatusEnum; +import cn.bootx.platform.daxpay.service.core.channel.union.entity.UnionPayConfig; +import cn.bootx.platform.daxpay.service.core.channel.union.service.UnionPayConfigService; +import cn.bootx.platform.daxpay.service.core.channel.union.service.UnionPaySyncService; +import cn.bootx.platform.daxpay.service.core.payment.sync.result.RefundGatewaySyncResult; +import cn.bootx.platform.daxpay.service.func.AbsRefundSyncStrategy; +import cn.bootx.platform.daxpay.service.sdk.union.api.UnionPayKit; +import lombok.RequiredArgsConstructor; +import org.springframework.context.annotation.Scope; +import org.springframework.stereotype.Component; + +import static org.springframework.beans.factory.config.BeanDefinition.SCOPE_PROTOTYPE; + +/** + * 云闪付退款同步 + * @author xxm + * @since 2024/3/11 + */ +@Scope(SCOPE_PROTOTYPE) +@Component +@RequiredArgsConstructor +public class UnionRefundSyncStrategy extends AbsRefundSyncStrategy { + + private final UnionPaySyncService unionPaySyncService; + + private final UnionPayConfigService unionPayConfigService; + /** + * 异步支付单与支付网关进行状态比对后的结果 + * + * @see PaySyncStatusEnum + */ + @Override + public RefundGatewaySyncResult doSyncStatus() { + UnionPayConfig config = unionPayConfigService.getConfig(); + UnionPayKit unionPayKit = unionPayConfigService.initPayService(config); + return unionPaySyncService.syncRefundStatus(this.getRefundOrder(),unionPayKit); + } + + /** + * 策略标识 + */ + @Override + public PayChannelEnum getChannel() { + return PayChannelEnum.UNION_PAY; + } +} diff --git a/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/sdk/union/api/UnionPayKit.java b/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/sdk/union/api/UnionPayKit.java index 63245a5a..9b4e19b7 100644 --- a/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/sdk/union/api/UnionPayKit.java +++ b/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/sdk/union/api/UnionPayKit.java @@ -81,6 +81,11 @@ public class UnionPayKit extends UnionPayService { if (null != certDescriptor) { return this; } + // + if (!payConfigStorage.isCertSign()){ + return this; + } + try { certDescriptor = new CertDescriptor(); certDescriptor.initPrivateSignCert(payConfigStorage.getKeyPrivateCertInputStream(), payConfigStorage.getKeyPrivateCertPwd(), "PKCS12"); @@ -683,7 +688,7 @@ public class UnionPayKit extends UnionPayService { */ @Override public Map refundquery(RefundOrder refundOrder) { - return Collections.emptyMap(); + return this.query(refundOrder); } /** diff --git a/用户授权使用协议.txt b/用户授权使用协议.txt index 5906b43f..b8183046 100644 --- a/用户授权使用协议.txt +++ b/用户授权使用协议.txt @@ -21,8 +21,6 @@ 3. 您拥有使用本软件构建的网站全部内容所有权,并独立承担与这些内容的相关法律义务 -4. 本软件由济南易杯光年软件技术有限公司进行分发,没有授权分销商,也没有任何分公司、代理商、办事处、经销商等销售和分发本产品 - 四、有限担保和免责声明 1. 本软件及所附带的文件是作为不提供任何明确的或隐含的赔偿或担保的形式提供的。 @@ -38,6 +36,9 @@ 7. 如发现客户在使用产品时有任何的非法行为,有权解除授权停止技术支持,并配合有关机关进行调查或向政府部门举报。 +8. 本软件由济南易杯光年软件技术有限公司进行分发,没有授权分销商,也没有任何分公司、代理商、办事处、经销商等销售和分发本产品 + + 五、禁止接入的内容包括但不限于以下内容 1. 诈骗、BC严禁接入、赛车、V盘、X资金盘、贷款、P2P、汇兑、ICO、二清支付严禁接入 @@ -52,5 +53,5 @@ 6. 微信/支付宝/银联官方要求禁止接入的、其他支付通道禁止接入的、以及所有国家法规规定禁止的一切情景 -协议时间:2023年01月01日 +协议日期:2023年01月01日 济南易杯光年软件技术有限公司 From ce048d3747f077442ae47ca7d5a80514ed506d05 Mon Sep 17 00:00:00 2001 From: bootx Date: Mon, 11 Mar 2024 23:41:19 +0800 Subject: [PATCH 6/9] =?UTF-8?q?feat=20=E4=BA=91=E9=97=AA=E4=BB=98=E6=9D=A1?= =?UTF-8?q?=E7=A0=81=E6=94=AF=E4=BB=98/APP=E6=94=AF=E4=BB=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- _doc/Task.md | 1 + .../controller/PayReturnController.java | 9 ++- .../daxpay/service/code/UnionPayCode.java | 15 +++-- .../service/UnionPayCallbackService.java | 2 +- .../union/service/UnionPayService.java | 67 ++++++++----------- .../union/service/UnionPaySyncService.java | 6 +- .../service/RefundCallbackService.java | 1 - .../notice/service/PayReturnService.java | 20 ++++++ .../payment/refund/service/RefundService.java | 8 +-- .../payment/sync/service/PaySyncService.java | 1 - .../channel/alipay/AliPayReturnParam.java | 4 +- .../channel/union/UnionPayReturnParam.java | 36 ++++++++++ 12 files changed, 113 insertions(+), 57 deletions(-) create mode 100644 daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/param/channel/union/UnionPayReturnParam.java diff --git a/_doc/Task.md b/_doc/Task.md index affe8267..17843504 100644 --- a/_doc/Task.md +++ b/_doc/Task.md @@ -20,6 +20,7 @@ - [ ] 支付流程涉及异步支付时, 更换支付方式需要控制预防客户重复付款 - [ ] 增加撤销功能,用于处理线下支付订单的情况 - [ ] 数据库表进行规则, 字段设置长度, 增加索引 +- [ ] 回调处理可以配置延迟时间, **任务池** - [x] 支付SDK编写 diff --git a/daxpay-single/daxpay-single-gateway/src/main/java/cn/bootx/platform/daxpay/gateway/controller/PayReturnController.java b/daxpay-single/daxpay-single-gateway/src/main/java/cn/bootx/platform/daxpay/gateway/controller/PayReturnController.java index 2ce224af..26a0ac22 100644 --- a/daxpay-single/daxpay-single-gateway/src/main/java/cn/bootx/platform/daxpay/gateway/controller/PayReturnController.java +++ b/daxpay-single/daxpay-single-gateway/src/main/java/cn/bootx/platform/daxpay/gateway/controller/PayReturnController.java @@ -3,10 +3,12 @@ package cn.bootx.platform.daxpay.gateway.controller; import cn.bootx.platform.common.core.annotation.IgnoreAuth; import cn.bootx.platform.daxpay.service.core.payment.notice.service.PayReturnService; import cn.bootx.platform.daxpay.service.param.channel.alipay.AliPayReturnParam; +import cn.bootx.platform.daxpay.service.param.channel.union.UnionPayReturnParam; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.tags.Tag; import lombok.RequiredArgsConstructor; 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; import org.springframework.web.servlet.ModelAndView; @@ -38,8 +40,9 @@ public class PayReturnController { } @Operation(summary = "云闪付同步通知") - @GetMapping("/union") - public ModelAndView union(){ - return null; + @PostMapping("/union") + public ModelAndView union(UnionPayReturnParam param){ + String url = payReturnService.union(param); + return new ModelAndView("redirect:" + url); } } diff --git a/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/code/UnionPayCode.java b/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/code/UnionPayCode.java index 8566b01d..4d6b8591 100644 --- a/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/code/UnionPayCode.java +++ b/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/code/UnionPayCode.java @@ -10,12 +10,15 @@ import com.egzosn.pay.union.bean.SDKConstants; public interface UnionPayCode { - /** 成功状态 */ - String SUCCESS = "0"; - - /** 状态 00表示成功 */ + /** 应答码 00表示成功 */ String RESP_CODE = SDKConstants.param_respCode; + /** 原交易应答码 00表示成功 */ + String RESP_ORIG_CODE = SDKConstants.param_origRespCode; + + /** 应答码信息 */ + String RESP_MSG = SDKConstants.param_respMsg; + /** 业务结果 00表示成功 */ String RESP_SUCCESS = SDKConstants.OK_RESP_CODE; @@ -28,8 +31,8 @@ public interface UnionPayCode { /** 第三方订单号(本地订单号) */ String ORDER_ID = "orderId"; - /** 退款ID */ - String REFUND_ID = "refund_id"; + /** APP支付 银联订单号 */ + String PAY_APP_TN = SDKConstants.param_tn; /** 交易类型 支付 */ String TXN_TYPE_PAY = "01"; diff --git a/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/channel/union/service/UnionPayCallbackService.java b/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/channel/union/service/UnionPayCallbackService.java index 5fd7706a..007629e1 100644 --- a/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/channel/union/service/UnionPayCallbackService.java +++ b/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/channel/union/service/UnionPayCallbackService.java @@ -110,7 +110,7 @@ public class UnionPayCallbackService extends AbsCallbackStrategy { @Override public void resolveRefundData() { // 云闪付需要延迟半秒再进行处理, 不然会出现业务未处理完, 但回调已经到达的情况 - ThreadUtil.sleep(300); + ThreadUtil.sleep(100); CallbackLocal callbackInfo = PaymentContextLocal.get().getCallbackInfo(); Map callbackParam = callbackInfo.getCallbackParam(); diff --git a/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/channel/union/service/UnionPayService.java b/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/channel/union/service/UnionPayService.java index f114e5a9..f61006c9 100644 --- a/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/channel/union/service/UnionPayService.java +++ b/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/channel/union/service/UnionPayService.java @@ -5,6 +5,7 @@ import cn.bootx.platform.daxpay.exception.pay.PayFailureException; import cn.bootx.platform.daxpay.param.channel.UnionPayParam; import cn.bootx.platform.daxpay.param.pay.PayChannelParam; import cn.bootx.platform.daxpay.service.code.AliPayWay; +import cn.bootx.platform.daxpay.service.code.UnionPayCode; import cn.bootx.platform.daxpay.service.common.context.PayLocal; import cn.bootx.platform.daxpay.service.common.local.PaymentContextLocal; import cn.bootx.platform.daxpay.service.core.channel.union.entity.UnionPayConfig; @@ -13,6 +14,8 @@ import cn.bootx.platform.daxpay.service.sdk.union.api.UnionPayKit; import cn.bootx.platform.daxpay.service.sdk.union.bean.UnionPayOrder; import cn.hutool.core.collection.CollUtil; import cn.hutool.core.date.DateUtil; +import cn.hutool.core.map.MapUtil; +import com.egzosn.pay.common.bean.NoticeParams; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; @@ -20,6 +23,7 @@ import org.springframework.stereotype.Service; import java.math.BigDecimal; import java.util.Date; import java.util.Map; +import java.util.Objects; import java.util.Optional; /** @@ -89,8 +93,17 @@ public class UnionPayService { unionPayOrder.setPrice(amount); unionPayOrder.setExpirationTime(expiredTime); - Map app = unionPayKit.app(unionPayOrder); - return null; + Map result = unionPayKit.app(unionPayOrder); + String resultCode = MapUtil.getStr(result, UnionPayCode.RESP_CODE); + + // 支付失败 + if (!(Objects.equals(resultCode, UnionPayCode.RESP_SUCCESS))) { + log.warn("云闪付支付失败:{}", result); + String errMsg = MapUtil.getStr(result, UnionPayCode.RESP_MSG); + throw new PayFailureException(errMsg); + } + + return MapUtil.getStr(result, UnionPayCode.PAY_APP_TN); } /** @@ -100,31 +113,11 @@ public class UnionPayService { Date expiredTime = DateUtil.date(payOrder.getExpiredTime()); UnionPayOrder unionPayOrder = new UnionPayOrder(); - unionPayOrder.setOutTradeNo(String.valueOf(payOrder.getId())); unionPayOrder.setSubject(payOrder.getTitle()); unionPayOrder.setPrice(amount); unionPayOrder.setExpirationTime(expiredTime); - return unionPayKit.getQrPay(unionPayOrder); - - -// Map params = UnifiedOrderModel.builder() -// .service(ServiceEnum.NATIVE.toString()) -// .mch_id(unionPayKit.getMachId()) -// .out_trade_no(String.valueOf(payOrder.getId())) -// .body(payOrder.getTitle()) -// .total_fee(amount) -// .time_expire(PayUtil.getUnionExpiredTime(payOrder.getExpiredTime())) -// .mch_create_ip("127.0.0.1") -// .notify_url(unionPayKit.getNotifyUrl()) -// .nonce_str(WxPayKit.generateStr()) -// .build() -// .createSign(unionPayKit.getAppKey(), SignType.MD5); -// String xmlResult = UnionPayApi.execution(unionPayKit.getServerUrl(), params); -// Map result = WxPayKit.xmlToMap(xmlResult); -// this.verifyErrorMsg(result); -// return result.get("code_url"); } /** @@ -134,28 +127,26 @@ public class UnionPayService { Date expiredTime = DateUtil.date(payOrder.getExpiredTime()); UnionPayOrder unionPayOrder = new UnionPayOrder(); - unionPayOrder.setAuthCode(authCode); unionPayOrder.setOutTradeNo(String.valueOf(payOrder.getId())); unionPayOrder.setSubject(payOrder.getTitle()); unionPayOrder.setPrice(amount); unionPayOrder.setExpirationTime(expiredTime); - Map stringObjectMap = unionPayKit.microPay(unionPayOrder); + Map result = unionPayKit.microPay(unionPayOrder); -// Map params = MicroPayModel.builder() -// .service(ServiceEnum.MICRO_PAY.toString()) -// .mch_id(unionPayKit.getMachId()) -// .out_trade_no(WxPayKit.generateStr()) -// .body(payOrder.getTitle()) -// .total_fee(amount) -// .op_device_id("daxpay") -// .mch_create_ip("127.0.0.1") -// .auth_code(authCode) -// .nonce_str(WxPayKit.generateStr()) -// .build() -// .createSign(unionPayKit.getAppKey(), SignType.MD5); -// -// String xmlResult = UnionPayApi.execution(unionPayKit.getServerUrl(), params); + if (!unionPayKit.verify(new NoticeParams(result))) { + log.warn("云闪付支付验签失败:{}", result); + throw new PayFailureException("云闪付支付验签失败"); + } + + String resultCode = MapUtil.getStr(result, UnionPayCode.RESP_CODE); + + // 支付失败 + if (!(Objects.equals(resultCode, UnionPayCode.RESP_SUCCESS))) { + log.warn("云闪付支付失败:{}", result); + String errMsg = MapUtil.getStr(result, UnionPayCode.RESP_MSG); + throw new PayFailureException(errMsg); + } } } diff --git a/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/channel/union/service/UnionPaySyncService.java b/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/channel/union/service/UnionPaySyncService.java index b4b1bb1a..6eba5be1 100644 --- a/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/channel/union/service/UnionPaySyncService.java +++ b/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/channel/union/service/UnionPaySyncService.java @@ -43,7 +43,6 @@ public class UnionPaySyncService { PayGatewaySyncResult syncResult = new PayGatewaySyncResult().setSyncStatus(PaySyncStatusEnum.FAIL); AssistOrder query = new AssistOrder(); -// query.setOutTradeNo("1766811070348001280"); query.setOutTradeNo(String.valueOf(order.getId())); Map result = unionPayKit.query(query); @@ -87,6 +86,11 @@ public class UnionPaySyncService { return syncResult.setSyncStatus(PaySyncStatusEnum.PROGRESS); } + // 二维码支付无效 + if (Objects.equals(origRespCode, "86")) { + return syncResult.setSyncStatus(PaySyncStatusEnum.CLOSED).setErrorMsg("支付二维码无效"); + } + return syncResult; } diff --git a/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/payment/callback/service/RefundCallbackService.java b/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/payment/callback/service/RefundCallbackService.java index c64a10dd..cca9a606 100644 --- a/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/payment/callback/service/RefundCallbackService.java +++ b/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/payment/callback/service/RefundCallbackService.java @@ -65,7 +65,6 @@ public class RefundCallbackService { RefundRepairResult repair = reflectionService.repair(refundOrder, RefundRepairWayEnum.REFUND_SUCCESS); callbackInfo.setPayRepairNo(repair.getRepairNo()); } else { - // 设置退款订单完成时间 RefundRepairResult repair = reflectionService.repair(refundOrder, RefundRepairWayEnum.REFUND_FAIL); callbackInfo.setPayRepairNo(repair.getRepairNo()); } diff --git a/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/payment/notice/service/PayReturnService.java b/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/payment/notice/service/PayReturnService.java index 6acb90f0..b0c54a14 100644 --- a/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/payment/notice/service/PayReturnService.java +++ b/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/payment/notice/service/PayReturnService.java @@ -7,6 +7,7 @@ import cn.bootx.platform.daxpay.service.core.order.pay.entity.PayOrderExtra; import cn.bootx.platform.daxpay.service.core.order.pay.service.PayOrderQueryService; import cn.bootx.platform.daxpay.service.core.system.config.service.PlatformConfigService; import cn.bootx.platform.daxpay.service.param.channel.alipay.AliPayReturnParam; +import cn.bootx.platform.daxpay.service.param.channel.union.UnionPayReturnParam; import cn.hutool.core.net.URLEncodeUtil; import cn.hutool.core.util.StrUtil; import lombok.RequiredArgsConstructor; @@ -51,4 +52,23 @@ public class PayReturnService { // 跳转到默认页 return StrUtil.format("{}/result/success?msg={}", properties.getFrontH5Url(), URLEncodeUtil.encode("支付成功...")); } + + public String union(UnionPayReturnParam param) { + PayOrderExtra payOrderExtra = payOrderExtraManager.findById(param.getOrderNo()).orElse(null); + PayOrder prOrder = payOrderQueryService.findById(Long.valueOf(param.getOrderNo())).orElse(null); + if (Objects.isNull(payOrderExtra) || Objects.isNull(prOrder)){ + return StrUtil.format("{}/result/error?msg={}", properties.getFrontH5Url(), URLEncodeUtil.encode("支付订单有问题,请排查")); + } + + // 如果同步跳转参数为空, 获取系统配置地址, 系统配置如果也为空, 则返回默认地址 + String returnUrl = payOrderExtra.getReturnUrl(); + if (StrUtil.isBlank(returnUrl)){ + returnUrl = platformConfigService.getConfig().getReturnUrl(); + } + if (StrUtil.isNotBlank(returnUrl)){ + return StrUtil.format("{}?paymentId={}&businessNo={}", payOrderExtra.getReturnUrl(),prOrder.getId(),prOrder.getBusinessNo()); + } + // 跳转到默认页 + return StrUtil.format("{}/result/success?msg={}", properties.getFrontH5Url(), URLEncodeUtil.encode("支付成功...")); + } } diff --git a/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/payment/refund/service/RefundService.java b/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/payment/refund/service/RefundService.java index ffe428f0..1a819f5d 100644 --- a/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/payment/refund/service/RefundService.java +++ b/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/payment/refund/service/RefundService.java @@ -1,5 +1,6 @@ package cn.bootx.platform.daxpay.service.core.payment.refund.service; +import cn.bootx.platform.common.core.annotation.CountTime; import cn.bootx.platform.common.core.exception.DataNotExistException; import cn.bootx.platform.common.core.exception.RepetitiveOperationException; import cn.bootx.platform.common.core.function.CollectorsFunction; @@ -67,6 +68,7 @@ public class RefundService { /** * 支付退款 */ + @CountTime @Transactional(rollbackFor = Exception.class ) public RefundResult refund(RefundParam param){ return this.refundAdapter(param,false); @@ -75,6 +77,7 @@ public class RefundService { /** * 简单退款 */ + @CountTime @Transactional(rollbackFor = Exception.class ) public RefundResult simpleRefund(SimpleRefundParam param){ ValidationUtil.validateParam(param); @@ -273,7 +276,7 @@ public class RefundService { this.successHandler(refundOrder, refundChannelOrders, payOrder); } catch (Exception e) { - // 5. 失败处理, 所有记录都会回滚, 可以调用重新 + // 5. 失败处理, 所有记录都会回滚, 可以调用退款重试 PaymentContextLocal.get().getRefundInfo().setStatus(RefundStatusEnum.FAIL); this.errorHandler(refundOrder); throw e; @@ -289,13 +292,10 @@ public class RefundService { int refundableBalance = refundOrder.getRefundableBalance(); // 设置支付订单状态 if (refundInfo.getStatus() == RefundStatusEnum.PROGRESS) { - // 设置为退款中 payOrder.setStatus(PayStatusEnum.REFUNDING.getCode()); } else if (refundableBalance == 0) { - // 退款完成 payOrder.setStatus(PayStatusEnum.REFUNDED.getCode()); } else { - // 部分退款 payOrder.setStatus(PayStatusEnum.PARTIAL_REFUND.getCode()); } payOrderService.updateById(payOrder); diff --git a/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/payment/sync/service/PaySyncService.java b/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/payment/sync/service/PaySyncService.java index da328166..de5cf3cb 100644 --- a/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/payment/sync/service/PaySyncService.java +++ b/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/payment/sync/service/PaySyncService.java @@ -74,7 +74,6 @@ public class PaySyncService { } // 如果不是异步支付, 直接返回返回 if (!payOrder.isAsyncPay()){ -// return new SyncResult().setSuccess(false).setRepair(false).setErrorMsg("订单没有异步支付方式,不需要同步"); throw new PayFailureException("订单没有异步支付方式,不需要同步"); } // 执行订单同步逻辑 diff --git a/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/param/channel/alipay/AliPayReturnParam.java b/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/param/channel/alipay/AliPayReturnParam.java index 306a026d..84f597f9 100644 --- a/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/param/channel/alipay/AliPayReturnParam.java +++ b/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/param/channel/alipay/AliPayReturnParam.java @@ -6,13 +6,13 @@ import lombok.Data; import lombok.experimental.Accessors; /** - * 微信同步回调参数 + * 支付宝同步回调参数 * @author xxm * @since 2024/2/11 */ @Data @Accessors(chain = true) -@Schema(title = "微信同步回调参数") +@Schema(title = "支付宝同步回调参数") public class AliPayReturnParam { /** diff --git a/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/param/channel/union/UnionPayReturnParam.java b/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/param/channel/union/UnionPayReturnParam.java new file mode 100644 index 00000000..f1917c19 --- /dev/null +++ b/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/param/channel/union/UnionPayReturnParam.java @@ -0,0 +1,36 @@ +package cn.bootx.platform.daxpay.service.param.channel.union; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.experimental.Accessors; + +/** + * 云闪付同步回调参数 + * @author xxm + * @since 2024/2/11 + */ +@Data +@Accessors(chain = true) +@Schema(title = "云闪付同步回调参数") +public class UnionPayReturnParam { + private String orderNo; + private String signature; + private String merName; + private String settleDate; + private String certId; + private String voucherNum; + private String version; + private String settleKey; + private String termId; + private String origReqType; + private String qrNo; + private String reqReserved; + private String reqType; + private String origRespMsg; + private String comInfo; + private String merId; + private String merCatCode; + private String currencyCode; + private String origRespCode; + private String txnAmt; +} From d69fdea2dd1f78c57871ac66f28a89e0b045f13e Mon Sep 17 00:00:00 2001 From: xxm1995 Date: Tue, 12 Mar 2024 17:57:15 +0800 Subject: [PATCH 7/9] =?UTF-8?q?feat=20=E9=80=80=E6=AC=BE=E8=AE=A2=E5=8D=95?= =?UTF-8?q?=E5=AE=9A=E6=97=B6=E5=90=8C=E6=AD=A5=E4=BB=BB=E5=8A=A1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 185 ++++++++++++++++++ .../core/order/pay/dao/PayOrderManager.java | 8 + .../order/pay/service/PayOrderService.java | 13 ++ .../order/refund/dao/RefundOrderManager.java | 14 ++ .../callback/service/PayCallbackService.java | 2 +- .../service/RefundCallbackService.java | 2 +- .../close/service/PayCloseService.java | 2 +- .../notice/service/ClientNoticeService.java | 2 +- .../pay/task/PayWaitOrderSyncTask.java | 54 ----- .../repair/service/PayRepairService.java | 16 +- .../repair/service/RefundRepairService.java | 95 +++++---- .../payment/sync/service/PaySyncService.java | 2 +- .../daxpay/service/task/RefundSyncTask.java | 27 +++ .../task/service/RefundSyncTaskService.java | 41 ++++ 14 files changed, 360 insertions(+), 103 deletions(-) delete mode 100644 daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/payment/pay/task/PayWaitOrderSyncTask.java create mode 100644 daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/task/RefundSyncTask.java create mode 100644 daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/task/service/RefundSyncTaskService.java diff --git a/README.md b/README.md index 9822fdd8..cbba445a 100644 --- a/README.md +++ b/README.md @@ -213,3 +213,188 @@ QQ扫码加入QQ交流群 ## 🍷License Apache License Version 2.0 + +# 🚀Dromara成员项目 + + + + + diff --git a/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/order/pay/dao/PayOrderManager.java b/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/order/pay/dao/PayOrderManager.java index 0a614c5f..4922d97d 100644 --- a/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/order/pay/dao/PayOrderManager.java +++ b/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/order/pay/dao/PayOrderManager.java @@ -37,4 +37,12 @@ public class PayOrderManager extends BaseManager { QueryWrapper generator = QueryGenerator.generator(query); return page(mpPage, generator); } + + /** + * 强制更新 (忽略版本号) + */ + public void updateForceById(PayOrder payOrder) { + payOrder.setVersion(null); + this.updateById(payOrder); + } } diff --git a/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/order/pay/service/PayOrderService.java b/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/order/pay/service/PayOrderService.java index f96de686..7c0c4470 100644 --- a/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/order/pay/service/PayOrderService.java +++ b/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/order/pay/service/PayOrderService.java @@ -56,4 +56,17 @@ public class PayOrderService { } payOrderManager.updateById(payOrder); } + + /** + * 使用强制更新 + */ + public void updateForceById(PayOrder payOrder){ + // 如果是异步支付且支付订单完成, 需要删除订单超时任务记录 + if (payOrder.isAsyncPay() && ORDER_FINISH.contains(payOrder.getStatus())){ + expiredTimeService.cancelExpiredTime(payOrder.getId()); + } + payOrder.setVersion(null); + payOrderManager.updateForceById(payOrder); + } + } diff --git a/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/order/refund/dao/RefundOrderManager.java b/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/order/refund/dao/RefundOrderManager.java index 740c5b0c..3de8c697 100644 --- a/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/order/refund/dao/RefundOrderManager.java +++ b/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/order/refund/dao/RefundOrderManager.java @@ -4,6 +4,7 @@ import cn.bootx.platform.common.core.rest.param.PageParam; import cn.bootx.platform.common.mybatisplus.impl.BaseManager; import cn.bootx.platform.common.mybatisplus.util.MpUtil; import cn.bootx.platform.common.query.generator.QueryGenerator; +import cn.bootx.platform.daxpay.code.RefundStatusEnum; import cn.bootx.platform.daxpay.service.core.order.refund.entity.RefundOrder; import cn.bootx.platform.daxpay.service.param.order.RefundOrderQuery; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; @@ -12,6 +13,8 @@ import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Repository; +import java.time.LocalDateTime; +import java.util.List; import java.util.Optional; /** @@ -46,4 +49,15 @@ public class RefundOrderManager extends BaseManager findAllByProgress() { + LocalDateTime now = LocalDateTime.now(); + return lambdaQuery() + .le(RefundOrder::getRefundTime,now) + .eq(RefundOrder::getStatus, RefundStatusEnum.PROGRESS) + .list(); + } } diff --git a/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/payment/callback/service/PayCallbackService.java b/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/payment/callback/service/PayCallbackService.java index 83581af7..a1c6bfe2 100644 --- a/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/payment/callback/service/PayCallbackService.java +++ b/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/payment/callback/service/PayCallbackService.java @@ -42,7 +42,7 @@ public class PayCallbackService { public void payCallback() { CallbackLocal callbackInfo = PaymentContextLocal.get().getCallbackInfo(); // 加锁 - LockInfo lock = lockTemplate.lock("callback:payment:" + callbackInfo.getOrderId()); + LockInfo lock = lockTemplate.lock("callback:payment:" + callbackInfo.getOrderId(),10000, 200); if (Objects.isNull(lock)){ callbackInfo.setCallbackStatus(PayCallbackStatusEnum.IGNORE).setMsg("回调正在处理中,忽略本次回调请求"); log.warn("订单号: {} 回调正在处理中,忽略本次回调请求", callbackInfo.getOrderId()); diff --git a/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/payment/callback/service/RefundCallbackService.java b/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/payment/callback/service/RefundCallbackService.java index cca9a606..7db3736a 100644 --- a/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/payment/callback/service/RefundCallbackService.java +++ b/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/payment/callback/service/RefundCallbackService.java @@ -39,7 +39,7 @@ public class RefundCallbackService { CallbackLocal callbackInfo = PaymentContextLocal.get().getCallbackInfo(); // 加锁 - LockInfo lock = lockTemplate.lock("callback:refund:" + callbackInfo.getOrderId()); + LockInfo lock = lockTemplate.lock("callback:refund:" + callbackInfo.getOrderId(),10000, 200); if (Objects.isNull(lock)){ callbackInfo.setCallbackStatus(PayCallbackStatusEnum.IGNORE).setMsg("回调正在处理中,忽略本次回调请求"); log.warn("订单号: {} 回调正在处理中,忽略本次回调请求", callbackInfo.getOrderId()); diff --git a/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/payment/close/service/PayCloseService.java b/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/payment/close/service/PayCloseService.java index 87b26a0a..b3b8870e 100644 --- a/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/payment/close/service/PayCloseService.java +++ b/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/payment/close/service/PayCloseService.java @@ -63,7 +63,7 @@ public class PayCloseService { payOrder = payOrderQueryService.findByBusinessNo(param.getBusinessNo()) .orElseThrow(() -> new PayFailureException("未查询到支付订单")); } - LockInfo lock = lockTemplate.lock("payment:close:" + payOrder.getId()); + LockInfo lock = lockTemplate.lock("payment:close:" + payOrder.getId(),10000, 50); if (Objects.isNull(lock)){ throw new RepetitiveOperationException("支付订单已在关闭中,请勿重复发起"); } diff --git a/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/payment/notice/service/ClientNoticeService.java b/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/payment/notice/service/ClientNoticeService.java index f06f408d..5228723a 100644 --- a/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/payment/notice/service/ClientNoticeService.java +++ b/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/payment/notice/service/ClientNoticeService.java @@ -189,7 +189,7 @@ public class ClientNoticeService { private void run(Long taskId){ LocalDateTime now = LocalDateTime.now(); // 开启分布式锁 - LockInfo lock = lockTemplate.lock(KEY + ":" + taskId,2000, 50); + LockInfo lock = lockTemplate.lock(KEY + ":" + taskId,10000, 200); if (Objects.isNull(lock)){ throw new RepetitiveOperationException("支付同步处理中,请勿重复操作"); } diff --git a/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/payment/pay/task/PayWaitOrderSyncTask.java b/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/payment/pay/task/PayWaitOrderSyncTask.java deleted file mode 100644 index 2cad28b0..00000000 --- a/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/payment/pay/task/PayWaitOrderSyncTask.java +++ /dev/null @@ -1,54 +0,0 @@ -package cn.bootx.platform.daxpay.service.core.payment.pay.task; - -import cn.bootx.platform.common.core.exception.RepetitiveOperationException; -import cn.bootx.platform.daxpay.param.pay.PaySyncParam; -import cn.bootx.platform.daxpay.service.core.payment.pay.dao.PayExpiredTimeRepository; -import cn.bootx.platform.daxpay.service.core.payment.sync.service.PaySyncService; -import com.baomidou.lock.LockInfo; -import com.baomidou.lock.LockTemplate; -import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; -import org.springframework.stereotype.Service; - -import java.util.Objects; -import java.util.Set; - -/** - * 待支付订单的状态同步, 先不进行启用 - * @author xxm - * @since 2024/1/5 - */ -@Slf4j -@Service -@RequiredArgsConstructor -public class PayWaitOrderSyncTask { - private final PayExpiredTimeRepository repository; - - private final PaySyncService paySyncService; - - private final LockTemplate lockTemplate; - - public void task(){ - log.debug("开始同步支付订单状态"); - // 从超时订单列表中获取到未超时的订单号 - Set keys = repository.getNormalKeysBy30Day(); - for (String key : keys) { - LockInfo lock = lockTemplate.lock("payment:sync:" + key,10000,200); - if (Objects.isNull(lock)){ - throw new RepetitiveOperationException("支付同步处理中,请勿重复操作"); - } - try { - Long paymentId = Long.parseLong(key); - PaySyncParam paySyncParam = new PaySyncParam(); - paySyncParam.setPaymentId(paymentId); - // 执行网关同步, 网关同步时会对支付的进行状态的处理 - paySyncService.sync(paySyncParam); - } catch (Exception e) { - log.error("同步支付订单异常", e); - } finally { - lockTemplate.releaseLock(lock); - } - } - - } -} diff --git a/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/payment/repair/service/PayRepairService.java b/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/payment/repair/service/PayRepairService.java index d3d3d841..cd74d922 100644 --- a/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/payment/repair/service/PayRepairService.java +++ b/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/payment/repair/service/PayRepairService.java @@ -17,16 +17,15 @@ import cn.bootx.platform.daxpay.service.core.record.repair.entity.PayRepairRecor import cn.bootx.platform.daxpay.service.core.record.repair.service.PayRepairRecordService; import cn.bootx.platform.daxpay.service.func.AbsPayRepairStrategy; import cn.hutool.core.util.IdUtil; +import com.baomidou.lock.LockInfo; +import com.baomidou.lock.LockTemplate; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import java.time.LocalDateTime; -import java.util.ArrayList; -import java.util.List; -import java.util.Map; -import java.util.Optional; +import java.util.*; import java.util.function.Function; import java.util.stream.Collectors; @@ -48,11 +47,20 @@ public class PayRepairService { private final PayRepairRecordService recordService; + private final LockTemplate lockTemplate; + /** * 修复支付单 */ @Transactional(rollbackFor = Exception.class) public PayRepairResult repair(PayOrder order, PayRepairWayEnum repairType){ + // 添加分布式锁 + LockInfo lock = lockTemplate.lock("repair:pay:" + order.getId(), 10000, 200); + if (Objects.isNull(lock)){ + log.warn("当前支付定单正在修复中: {}", order.getId()); + return new PayRepairResult(); + } + // 1. 获取支付单管理的通道支付订单 Map channelOrderMap = channelOrderManager.findAllByPaymentId(order.getId()) .stream() diff --git a/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/payment/repair/service/RefundRepairService.java b/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/payment/repair/service/RefundRepairService.java index b623be0e..7e5fce7e 100644 --- a/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/payment/repair/service/RefundRepairService.java +++ b/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/payment/repair/service/RefundRepairService.java @@ -23,6 +23,8 @@ import cn.bootx.platform.daxpay.service.core.record.repair.entity.PayRepairRecor import cn.bootx.platform.daxpay.service.core.record.repair.service.PayRepairRecordService; import cn.bootx.platform.daxpay.service.func.AbsRefundRepairStrategy; import cn.hutool.core.util.IdUtil; +import com.baomidou.lock.LockInfo; +import com.baomidou.lock.LockTemplate; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; @@ -56,54 +58,67 @@ public class RefundRepairService { private final PayRepairRecordService recordService; + private final LockTemplate lockTemplate; + /** * 修复退款单 */ @Transactional(rollbackFor = Exception.class) public RefundRepairResult repair(RefundOrder refundOrder, RefundRepairWayEnum repairType){ - - // 获取关联支付单 - PayOrder payOrder = payOrderQueryService.findById(refundOrder.getPaymentId()) - .orElseThrow(() -> new RuntimeException("支付单不存在")); - // 关联支付通道支付单 - Map payChannelOrderMap = payChannelOrderManager.findAllByPaymentId(refundOrder.getPaymentId()) - .stream() - .collect(Collectors.toMap(PayChannelOrder::getChannel, Function.identity(), CollectorsFunction::retainLatest)); - // 异步通道退款单 - Map refundChannelOrderMap = refundChannelOrderManager.findAllByRefundId(refundOrder.getId()) - .stream() - .collect(Collectors.toMap(RefundChannelOrder::getChannel, Function.identity(), CollectorsFunction::retainLatest)); - - // 2 初始化修复参数 - List channels = new ArrayList<>(payChannelOrderMap.keySet()); - List repairStrategies = RefundRepairStrategyFactory.createAsyncLast(channels); - for (AbsRefundRepairStrategy repairStrategy : repairStrategies) { - PayChannelOrder payChannelOrder = payChannelOrderMap.get(repairStrategy.getChannel().getCode()); - RefundChannelOrder refundChannelOrder = refundChannelOrderMap.get(repairStrategy.getChannel().getCode()); - repairStrategy.initRepairParam(refundOrder, refundChannelOrder, payOrder, payChannelOrder); + // 添加分布式锁 + LockInfo lock = lockTemplate.lock("repair:refund:" + refundOrder.getId(), 10000, 200); + if (Objects.isNull(lock)){ + log.warn("当前退款单正在修复中: {}", refundOrder.getId()); + return new RefundRepairResult(); } + try { + // 获取关联支付单 + PayOrder payOrder = payOrderQueryService.findById(refundOrder.getPaymentId()) + .orElseThrow(() -> new RuntimeException("支付单不存在")); + // 关联支付通道支付单 + Map payChannelOrderMap = payChannelOrderManager.findAllByPaymentId(refundOrder.getPaymentId()) + .stream() + .collect(Collectors.toMap(PayChannelOrder::getChannel, Function.identity(), CollectorsFunction::retainLatest)); + // 异步通道退款单 + Map refundChannelOrderMap = refundChannelOrderManager.findAllByRefundId(refundOrder.getId()) + .stream() + .collect(Collectors.toMap(RefundChannelOrder::getChannel, Function.identity(), CollectorsFunction::retainLatest)); - // 根据不同的类型执行对应的修复逻辑 - RefundRepairResult repairResult = new RefundRepairResult(); - if (Objects.requireNonNull(repairType) == RefundRepairWayEnum.REFUND_SUCCESS) { - repairResult = this.success(refundOrder,payOrder,repairStrategies); - } else if (repairType == RefundRepairWayEnum.REFUND_FAIL) { - repairResult = this.close(refundOrder,payOrder,repairStrategies); - } else { - log.error("走到了理论上讲不会走到的分支"); + // 2 初始化修复参数 + List channels = new ArrayList<>(payChannelOrderMap.keySet()); + List repairStrategies = RefundRepairStrategyFactory.createAsyncLast(channels); + for (AbsRefundRepairStrategy repairStrategy : repairStrategies) { + PayChannelOrder payChannelOrder = payChannelOrderMap.get(repairStrategy.getChannel() + .getCode()); + RefundChannelOrder refundChannelOrder = refundChannelOrderMap.get(repairStrategy.getChannel() + .getCode()); + repairStrategy.initRepairParam(refundOrder, refundChannelOrder, payOrder, payChannelOrder); + } + + // 根据不同的类型执行对应的修复逻辑 + RefundRepairResult repairResult = new RefundRepairResult(); + if (Objects.requireNonNull(repairType) == RefundRepairWayEnum.REFUND_SUCCESS) { + repairResult = this.success(refundOrder, payOrder, repairStrategies); + } else if (repairType == RefundRepairWayEnum.REFUND_FAIL) { + repairResult = this.close(refundOrder, payOrder, repairStrategies); + } else { + log.error("走到了理论上讲不会走到的分支"); + } + + // 设置修复ID并保存修复记录 + repairResult.setRepairNo(IdUtil.getSnowflakeNextIdStr()); + // 支付修复记录 + PayRepairRecord payRepairRecord = this.payRepairRecord(payOrder, repairType, repairResult); + // 退款修复记录 + PayRepairRecord refundRepairRecord = this.refundRepairRecord(refundOrder, repairType, repairResult); + + // 发送通知 + clientNoticeService.registerRefundNotice(refundOrder, null, new ArrayList<>(refundChannelOrderMap.values())); + recordService.saveAllRecord(Arrays.asList(payRepairRecord, refundRepairRecord)); + return repairResult; + } finally { + lockTemplate.releaseLock(lock); } - - // 设置修复ID并保存修复记录 - repairResult.setRepairNo(IdUtil.getSnowflakeNextIdStr()); - // 支付修复记录 - PayRepairRecord payRepairRecord = this.payRepairRecord(payOrder, repairType, repairResult); - // 退款修复记录 - PayRepairRecord refundRepairRecord = this.refundRepairRecord(refundOrder, repairType, repairResult); - - // 发送通知 - clientNoticeService.registerRefundNotice(refundOrder, null, new ArrayList<>(refundChannelOrderMap.values())); - recordService.saveAllRecord(Arrays.asList(payRepairRecord, refundRepairRecord)); - return repairResult; } /** diff --git a/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/payment/sync/service/PaySyncService.java b/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/payment/sync/service/PaySyncService.java index de5cf3cb..5faa1faf 100644 --- a/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/payment/sync/service/PaySyncService.java +++ b/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/payment/sync/service/PaySyncService.java @@ -88,7 +88,7 @@ public class PaySyncService { @Transactional(propagation = Propagation.REQUIRES_NEW, rollbackFor = Exception.class) public SyncResult syncPayOrder(PayOrder payOrder) { // 加锁 - LockInfo lock = lockTemplate.lock("sync:payment" + payOrder.getId(),10000,200); + LockInfo lock = lockTemplate.lock("sync:pay" + payOrder.getId(),10000,200); if (Objects.isNull(lock)){ throw new RepetitiveOperationException("支付同步处理中,请勿重复操作"); } diff --git a/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/task/RefundSyncTask.java b/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/task/RefundSyncTask.java new file mode 100644 index 00000000..d4050ccb --- /dev/null +++ b/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/task/RefundSyncTask.java @@ -0,0 +1,27 @@ +package cn.bootx.platform.daxpay.service.task; + +import cn.bootx.platform.daxpay.service.task.service.RefundSyncTaskService; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.quartz.*; +import org.springframework.stereotype.Component; + +/** + * 退款定时同步任务 一分钟一次, 查询退款中的订单进行同步 + * @author xxm + * @since 2024/3/12 + */ +@Slf4j +@Component +@DisallowConcurrentExecution +@PersistJobDataAfterExecution +@RequiredArgsConstructor +public class RefundSyncTask implements Job { + + private final RefundSyncTaskService refundSyncTaskService; + + @Override + public void execute(JobExecutionContext context) throws JobExecutionException { + refundSyncTaskService.syncTask(); + } +} diff --git a/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/task/service/RefundSyncTaskService.java b/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/task/service/RefundSyncTaskService.java new file mode 100644 index 00000000..7054a2bb --- /dev/null +++ b/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/task/service/RefundSyncTaskService.java @@ -0,0 +1,41 @@ +package cn.bootx.platform.daxpay.service.task.service; + +import cn.bootx.platform.daxpay.service.core.order.refund.dao.RefundOrderManager; +import cn.bootx.platform.daxpay.service.core.order.refund.entity.RefundOrder; +import cn.bootx.platform.daxpay.service.core.payment.sync.service.RefundSyncService; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; + +import java.util.List; + +/** + * 定时 + * @author xxm + * @since 2024/3/12 + */ +@Slf4j +@Service +@RequiredArgsConstructor +public class RefundSyncTaskService { + + private final RefundSyncService refundSyncService; + + private final RefundOrderManager refundOrderManager; + + /** + * 同步任务 + */ + public void syncTask(){ + // 查询退款中的退款订单 + List list = refundOrderManager.findAllByProgress(); + for (RefundOrder refundOrder : list) { + try { + // 调用同步方法 + refundSyncService.syncRefundOrder(refundOrder); + } catch (Exception e) { + log.warn("退款执行同步失败, ID: {}",refundOrder.getId()); + } + } + } +} From 495f97dbc23b82cb27b08e8d7ec51030fec6e92a Mon Sep 17 00:00:00 2001 From: xxm1995 Date: Tue, 12 Mar 2024 18:46:55 +0800 Subject: [PATCH 8/9] =?UTF-8?q?feat=20wap=E5=92=8Cweb=E6=94=AF=E4=BB=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../platform/daxpay/code/PayWayEnum.java | 5 +--- .../daxpay/service/code/UnionPayWay.java | 2 +- .../union/service/UnionPayService.java | 28 +++++++++++++++++-- .../service/sdk/union/api/UnionPayKit.java | 1 - 4 files changed, 28 insertions(+), 8 deletions(-) diff --git a/daxpay-single/daxpay-single-core/src/main/java/cn/bootx/platform/daxpay/code/PayWayEnum.java b/daxpay-single/daxpay-single-core/src/main/java/cn/bootx/platform/daxpay/code/PayWayEnum.java index 33708cb1..7a41bd5b 100644 --- a/daxpay-single/daxpay-single-core/src/main/java/cn/bootx/platform/daxpay/code/PayWayEnum.java +++ b/daxpay-single/daxpay-single-core/src/main/java/cn/bootx/platform/daxpay/code/PayWayEnum.java @@ -24,10 +24,7 @@ public enum PayWayEnum { QRCODE("qrcode", "扫码支付"), BARCODE("barcode", "付款码"), // 通用 - JSAPI("jsapi", "公众号/小程序支付"), - // 在非支付宝和微信中, 但支持这两类支付的时候, 需要进行区分 - JSAPI_WX_PAY("jsapi_wx_pay", "微信JS支付"), - JSAPI_ALI_PAY("jsapi_ali_pay", "支付宝JS支付"); + JSAPI("jsapi", "公众号/小程序支付"); /** 编码 */ private final String code; diff --git a/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/code/UnionPayWay.java b/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/code/UnionPayWay.java index 9f9ebb7c..21d627f5 100644 --- a/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/code/UnionPayWay.java +++ b/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/code/UnionPayWay.java @@ -18,7 +18,7 @@ public class UnionPayWay { // 支付方式 private static final List PAY_WAYS = Arrays.asList(PayWayEnum.WAP, PayWayEnum.APP, PayWayEnum.WEB, - PayWayEnum.QRCODE, PayWayEnum.BARCODE); + PayWayEnum.JSAPI, PayWayEnum.QRCODE, PayWayEnum.BARCODE); /** * 根据编码获取 diff --git a/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/channel/union/service/UnionPayService.java b/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/channel/union/service/UnionPayService.java index f61006c9..80e3d447 100644 --- a/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/channel/union/service/UnionPayService.java +++ b/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/channel/union/service/UnionPayService.java @@ -4,8 +4,8 @@ import cn.bootx.platform.daxpay.code.PayWayEnum; import cn.bootx.platform.daxpay.exception.pay.PayFailureException; import cn.bootx.platform.daxpay.param.channel.UnionPayParam; import cn.bootx.platform.daxpay.param.pay.PayChannelParam; -import cn.bootx.platform.daxpay.service.code.AliPayWay; import cn.bootx.platform.daxpay.service.code.UnionPayCode; +import cn.bootx.platform.daxpay.service.code.UnionPayWay; import cn.bootx.platform.daxpay.service.common.context.PayLocal; import cn.bootx.platform.daxpay.service.common.local.PaymentContextLocal; import cn.bootx.platform.daxpay.service.core.channel.union.entity.UnionPayConfig; @@ -16,6 +16,7 @@ import cn.hutool.core.collection.CollUtil; import cn.hutool.core.date.DateUtil; import cn.hutool.core.map.MapUtil; import com.egzosn.pay.common.bean.NoticeParams; +import com.egzosn.pay.union.bean.UnionTransactionType; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; @@ -46,7 +47,7 @@ public class UnionPayService { throw new PayFailureException("云闪付未配置可用的支付方式"); } // 发起的支付类型是否在支持的范围内 - PayWayEnum payWayEnum = Optional.ofNullable(AliPayWay.findByCode(payChannelParam.getWay())) + PayWayEnum payWayEnum = Optional.ofNullable(UnionPayWay.findByCode(payChannelParam.getWay())) .orElseThrow(() -> new PayFailureException("非法的云闪付支付类型")); if (!unionPayConfig.getPayWays().contains(payWayEnum.getCode())) { throw new PayFailureException("该云闪付支付方式不可用"); @@ -75,10 +76,33 @@ public class UnionPayService { else if (payWayEnum == PayWayEnum.APP) { payBody = this.appPay(totalFee, payOrder, unionPayParam, unionPayKit); } + // web支付 + else if (payWayEnum == PayWayEnum.WEB) { + payBody = this.formPay(totalFee, payOrder, unionPayKit, UnionTransactionType.WEB); + } + // wap支付 + else if (payWayEnum == PayWayEnum.WAP) { + payBody = this.formPay(totalFee, payOrder, unionPayKit, UnionTransactionType.WAP ); + } asyncPayInfo.setPayBody(payBody); } + /** + * jsapi支付 + */ + private String formPay(BigDecimal amount, PayOrder payOrder, UnionPayKit unionPayKit, UnionTransactionType type) { + Date expiredTime = DateUtil.date(payOrder.getExpiredTime()); + + UnionPayOrder unionPayOrder = new UnionPayOrder(); + unionPayOrder.setOutTradeNo(String.valueOf(payOrder.getId())); + unionPayOrder.setSubject(payOrder.getTitle()); + unionPayOrder.setPrice(amount); + unionPayOrder.setExpirationTime(expiredTime); + unionPayOrder.setTransactionType(type); + return unionPayKit.toPay(unionPayOrder); + } + /** * APP支付 */ diff --git a/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/sdk/union/api/UnionPayKit.java b/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/sdk/union/api/UnionPayKit.java index 9b4e19b7..b4c7e0af 100644 --- a/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/sdk/union/api/UnionPayKit.java +++ b/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/sdk/union/api/UnionPayKit.java @@ -555,7 +555,6 @@ public class UnionPayKit extends UnionPayService { if (SDKConstants.OK_RESP_CODE.equals(response.get(SDKConstants.param_respCode))) { // //成功,获取tn号 // String tn = (String)response.get(SDKConstants.param_tn); -// //TODO return response; } throw new PayErrorException(new PayException((String) response.get(SDKConstants.param_respCode), (String) response.get(SDKConstants.param_respMsg), response.toJSONString())); From 5afc344ceddc3243c748ff379e0759c0ff31213e Mon Sep 17 00:00:00 2001 From: bootx Date: Tue, 12 Mar 2024 22:46:18 +0800 Subject: [PATCH 9/9] =?UTF-8?q?feat=20=E8=81=94=E8=B0=83wap=E5=92=8Cweb?= =?UTF-8?q?=E6=94=AF=E4=BB=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../wallet/service/WalletQueryService.java | 5 ++-- .../notice/service/PayReturnService.java | 10 ++++++-- .../channel/union/UnionPayReturnParam.java | 23 ++++--------------- 3 files changed, 15 insertions(+), 23 deletions(-) diff --git a/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/channel/wallet/service/WalletQueryService.java b/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/channel/wallet/service/WalletQueryService.java index 392b14af..f1d1a998 100644 --- a/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/channel/wallet/service/WalletQueryService.java +++ b/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/channel/wallet/service/WalletQueryService.java @@ -4,6 +4,7 @@ import cn.bootx.platform.common.core.exception.DataNotExistException; import cn.bootx.platform.common.core.rest.PageResult; import cn.bootx.platform.common.core.rest.param.PageParam; import cn.bootx.platform.common.mybatisplus.util.MpUtil; +import cn.bootx.platform.daxpay.exception.waller.WalletNotExistsException; import cn.bootx.platform.daxpay.param.channel.WalletPayParam; import cn.bootx.platform.daxpay.service.core.channel.wallet.dao.WalletManager; import cn.bootx.platform.daxpay.service.core.channel.wallet.entity.Wallet; @@ -64,10 +65,10 @@ public class WalletQueryService { Wallet wallet = null; if (Objects.nonNull(param.getWalletId())){ - wallet = walletManager.findById(param.getWalletId()).orElseThrow(DataNotExistException::new); + wallet = walletManager.findById(param.getWalletId()).orElseThrow(WalletNotExistsException::new); } if (Objects.isNull(wallet)){ - wallet = walletManager.findByUser(param.getUserId()).orElseThrow(DataNotExistException::new); + wallet = walletManager.findByUser(param.getUserId()).orElseThrow(WalletNotExistsException::new); } return wallet; } diff --git a/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/payment/notice/service/PayReturnService.java b/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/payment/notice/service/PayReturnService.java index b0c54a14..5e577925 100644 --- a/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/payment/notice/service/PayReturnService.java +++ b/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/payment/notice/service/PayReturnService.java @@ -54,8 +54,14 @@ public class PayReturnService { } public String union(UnionPayReturnParam param) { - PayOrderExtra payOrderExtra = payOrderExtraManager.findById(param.getOrderNo()).orElse(null); - PayOrder prOrder = payOrderQueryService.findById(Long.valueOf(param.getOrderNo())).orElse(null); + // 获取orderId + String orderId = param.getOrderId(); + if (StrUtil.isBlank(orderId)){ + orderId = param.getOrderNo(); + } + + PayOrderExtra payOrderExtra = payOrderExtraManager.findById(orderId).orElse(null); + PayOrder prOrder = payOrderQueryService.findById(Long.valueOf(orderId)).orElse(null); if (Objects.isNull(payOrderExtra) || Objects.isNull(prOrder)){ return StrUtil.format("{}/result/error?msg={}", properties.getFrontH5Url(), URLEncodeUtil.encode("支付订单有问题,请排查")); } diff --git a/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/param/channel/union/UnionPayReturnParam.java b/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/param/channel/union/UnionPayReturnParam.java index f1917c19..fdc3cc28 100644 --- a/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/param/channel/union/UnionPayReturnParam.java +++ b/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/param/channel/union/UnionPayReturnParam.java @@ -6,6 +6,7 @@ import lombok.experimental.Accessors; /** * 云闪付同步回调参数 + * 不同支付方式回调的字段会不一样 * @author xxm * @since 2024/2/11 */ @@ -13,24 +14,8 @@ import lombok.experimental.Accessors; @Accessors(chain = true) @Schema(title = "云闪付同步回调参数") public class UnionPayReturnParam { + /** 条码支付返回的 */ private String orderNo; - private String signature; - private String merName; - private String settleDate; - private String certId; - private String voucherNum; - private String version; - private String settleKey; - private String termId; - private String origReqType; - private String qrNo; - private String reqReserved; - private String reqType; - private String origRespMsg; - private String comInfo; - private String merId; - private String merCatCode; - private String currencyCode; - private String origRespCode; - private String txnAmt; + /** Web/Wap支付返回的 */ + private String orderId; }