diff --git a/_doc/Task.md b/_doc/Task.md index ea2506bb9..9917ac51e 100644 --- a/_doc/Task.md +++ b/_doc/Task.md @@ -1,28 +1,22 @@ ## 单商户 2.0.8: 对账完善和系统优化 -- [ ] 细分各种支付异常类和编码 -- [ ] 修复功能支付/退款/对账 +- [ ] 支持撤销接口 - [ ] 增加转账接口功能 +- [ ] 细分各种支付异常类和编码 +- [ ] 增加对账修复功能拆 - [ ] DEMO增加获取微信OpenID和支付宝OpenId功能 - [ ] 管理端界面支持扫码绑定对账接收方功能 -- [ ] 对账提供外部接口调用 - - [ ] 下载系统账单 -- [ ] 分账结果通知处理 - - [ ] 支付宝通知 - - [ ] 微信通知 - - [ ] 通知记录保存 - [ ] 增加收单收银台功能 - [ ] 增加资金对账单功能 - [ ] 支付通道两个独立的配置进行合并为一个 -- [ ] 支持撤销接口 -- [ ] 支付和退款达到终态不可以再回退回之前的状态, 只能添加差错单进行处理 - -2.0.8: 转账和功能功能优化 -- [ ] 支持转账操作, 通过支付通道专有参数进行实现, 转账时只能单个通道进行操作 +- [ ] 支付和退款达到终态不可以再回退回之前的状态 +- [x] 修复支付关闭参数名称不正确问题 +2.0.9: 消息通知和功能功能优化 +- [ ] 将系统通知消息重构为类似支付宝应用通知的方式 - [ ] 支付成功回调后, 如果订单已超时, 则进入待退款订单中,提示进行退款,或者自动退款 -- [ ] 新增支付单预警功能, 处理支付单与网关状态不一致且无法自动修复的情况 -2.1.x 版本内容 + 2.1.x 版本内容 +- [ ] 新增支付单预警功能, 处理支付单与网关状态不一致且无法自动修复的情况 - [ ] 差错单据处理 - [ ] 特殊退款接口 - [ ] 统计报表功能 diff --git a/bootx-platform/bootx-services/service-baseapi/src/main/java/cn/bootx/platform/baseapi/dto/chinaword/ChinaWordDto.java b/bootx-platform/bootx-services/service-baseapi/src/main/java/cn/bootx/platform/baseapi/dto/chinaword/ChinaWordDto.java deleted file mode 100644 index f31a395b9..000000000 --- a/bootx-platform/bootx-services/service-baseapi/src/main/java/cn/bootx/platform/baseapi/dto/chinaword/ChinaWordDto.java +++ /dev/null @@ -1,30 +0,0 @@ -package cn.bootx.platform.baseapi.dto.chinaword; - -import cn.bootx.platform.common.core.rest.dto.BaseDto; -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; -import lombok.EqualsAndHashCode; -import lombok.experimental.Accessors; - -/** - * 敏感词 - * @author xxm - * @since 2023-08-09 - */ -@EqualsAndHashCode(callSuper = true) -@Data -@Schema(title = "敏感词") -@Accessors(chain = true) -public class ChinaWordDto extends BaseDto { - - @Schema(description = "敏感词") - private String word; - @Schema(description = "分类") - private String type; - @Schema(description = "描述") - private String description; - @Schema(description = "是否启用") - private Boolean enable; - @Schema(description = "是否是白名单名词") - private Boolean white; -} diff --git a/bootx-platform/bootx-services/service-baseapi/src/main/java/cn/bootx/platform/baseapi/dto/chinaword/ChinaWordVerifyResult.java b/bootx-platform/bootx-services/service-baseapi/src/main/java/cn/bootx/platform/baseapi/dto/chinaword/ChinaWordVerifyResult.java deleted file mode 100644 index dc8887f59..000000000 --- a/bootx-platform/bootx-services/service-baseapi/src/main/java/cn/bootx/platform/baseapi/dto/chinaword/ChinaWordVerifyResult.java +++ /dev/null @@ -1,27 +0,0 @@ -package cn.bootx.platform.baseapi.dto.chinaword; - -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; -import lombok.experimental.Accessors; - -import java.util.Set; - -/** - * 敏感词验证结果 - * @author xxm - * @since 2023/8/9 - */ -@Data -@Accessors(chain = true) -@Schema(title = "敏感词验证结果") -public class ChinaWordVerifyResult { - - @Schema(description = "是否敏感") - private boolean sensitive; - @Schema(description = "敏感词数量") - private int count; - @Schema(description = "去重后的敏感词列表") - private Set sensitiveWords; - @Schema(description = "脱敏后的文本") - private String text; -} diff --git a/bootx-platform/bootx-services/service-baseapi/src/main/java/cn/bootx/platform/baseapi/param/chinaword/ChinaWordImportParam.java b/bootx-platform/bootx-services/service-baseapi/src/main/java/cn/bootx/platform/baseapi/param/chinaword/ChinaWordImportParam.java deleted file mode 100644 index 5edd178b8..000000000 --- a/bootx-platform/bootx-services/service-baseapi/src/main/java/cn/bootx/platform/baseapi/param/chinaword/ChinaWordImportParam.java +++ /dev/null @@ -1,19 +0,0 @@ -package cn.bootx.platform.baseapi.param.chinaword; - -import com.alibaba.excel.annotation.ExcelProperty; -import lombok.Data; - -/** - * 敏感词导入参数 - * @author xxm - * @since 2023/8/12 - */ -@Data -public class ChinaWordImportParam { - @ExcelProperty(value = "类型") - private String type; - @ExcelProperty("黑/白名单") - private String whiteOrBlack; - @ExcelProperty("敏感词") - private String word; -} diff --git a/bootx-platform/bootx-services/service-baseapi/src/main/java/cn/bootx/platform/baseapi/param/chinaword/ChinaWordParam.java b/bootx-platform/bootx-services/service-baseapi/src/main/java/cn/bootx/platform/baseapi/param/chinaword/ChinaWordParam.java deleted file mode 100644 index 3e5c1fc78..000000000 --- a/bootx-platform/bootx-services/service-baseapi/src/main/java/cn/bootx/platform/baseapi/param/chinaword/ChinaWordParam.java +++ /dev/null @@ -1,31 +0,0 @@ -package cn.bootx.platform.baseapi.param.chinaword; - -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; -import lombok.experimental.Accessors; - -/** - * 敏感词 - * @author xxm - * @since 2023-08-09 - */ -@Data -@Schema(title = "敏感词") -@Accessors(chain = true) -public class ChinaWordParam { - - @Schema(description= "主键") - private Long id; - - @Schema(description = "敏感词") - private String word; - @Schema(description = "分类") - private String type; - @Schema(description = "描述") - private String description; - @Schema(description = "是否启用") - private Boolean enable; - @Schema(description = "白名单名词") - private Boolean white; - -} diff --git a/bootx-platform/bootx-services/service-baseapi/src/main/java/cn/bootx/platform/baseapi/param/chinaword/ChinaWordVerifyParam.java b/bootx-platform/bootx-services/service-baseapi/src/main/java/cn/bootx/platform/baseapi/param/chinaword/ChinaWordVerifyParam.java deleted file mode 100644 index 46d2465a6..000000000 --- a/bootx-platform/bootx-services/service-baseapi/src/main/java/cn/bootx/platform/baseapi/param/chinaword/ChinaWordVerifyParam.java +++ /dev/null @@ -1,24 +0,0 @@ -package cn.bootx.platform.baseapi.param.chinaword; - -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; -import lombok.experimental.Accessors; - -/** - * 敏感词验证参数类 - * @author xxm - * @since 2023/8/10 - */ -@Data -@Accessors(chain = true) -@Schema(title = "敏感词验证参数类") -public class ChinaWordVerifyParam { - @Schema(description = "文本") - private String text; - - @Schema(description = "间隔距离") - private int skip = 0; - - @Schema(description = "替换字符") - private char symbol = '*'; -} diff --git a/daxpay-single-sdk/src/main/java/cn/daxpay/single/sdk/model/pay/PayCancelModel.java b/daxpay-single-sdk/src/main/java/cn/daxpay/single/sdk/model/pay/PayCancelModel.java new file mode 100644 index 000000000..427cd572e --- /dev/null +++ b/daxpay-single-sdk/src/main/java/cn/daxpay/single/sdk/model/pay/PayCancelModel.java @@ -0,0 +1,17 @@ +package cn.daxpay.single.sdk.model.pay; + +import cn.daxpay.single.sdk.net.DaxPayResponseModel; +import lombok.Getter; +import lombok.Setter; +import lombok.ToString; + +/** + * 支付撤销 + * @author xxm + * @since 2024/2/2 + */ +@Getter +@Setter +@ToString(callSuper = true) +public class PayCancelModel extends DaxPayResponseModel { +} diff --git a/daxpay-single-sdk/src/main/java/cn/daxpay/single/sdk/param/pay/PayCancelParam.java b/daxpay-single-sdk/src/main/java/cn/daxpay/single/sdk/param/pay/PayCancelParam.java new file mode 100644 index 000000000..089994a1f --- /dev/null +++ b/daxpay-single-sdk/src/main/java/cn/daxpay/single/sdk/param/pay/PayCancelParam.java @@ -0,0 +1,41 @@ +package cn.daxpay.single.sdk.param.pay; + +import cn.daxpay.single.sdk.model.pay.PayCancelModel; +import cn.daxpay.single.sdk.net.DaxPayRequest; +import cn.daxpay.single.sdk.response.DaxPayResult; +import cn.hutool.core.lang.TypeReference; +import cn.hutool.json.JSONUtil; +import lombok.Getter; +import lombok.Setter; + +/** + * 支付撤销参数 + * @author xxm + * @since 2023/12/17 + */ +@Getter +@Setter +public class PayCancelParam extends DaxPayRequest { + + /** 订单号 */ + private String orderNo; + + /** 商户订单号 */ + private String bizOrderNo; + + /** + * 方法请求路径 + */ + @Override + public String path() { + return "/unipay/cancel"; + } + + /** + * 将请求返回结果反序列化为实体类 + */ + @Override + public DaxPayResult toModel(String json) { + return JSONUtil.toBean(json, new TypeReference>() {}, false); + } +} diff --git a/daxpay-single-sdk/src/main/java/cn/daxpay/single/sdk/param/pay/PayCloseParam.java b/daxpay-single-sdk/src/main/java/cn/daxpay/single/sdk/param/pay/PayCloseParam.java index f08834584..7d6e752e5 100644 --- a/daxpay-single-sdk/src/main/java/cn/daxpay/single/sdk/param/pay/PayCloseParam.java +++ b/daxpay-single-sdk/src/main/java/cn/daxpay/single/sdk/param/pay/PayCloseParam.java @@ -5,23 +5,22 @@ import cn.daxpay.single.sdk.net.DaxPayRequest; import cn.daxpay.single.sdk.response.DaxPayResult; import cn.hutool.core.lang.TypeReference; import cn.hutool.json.JSONUtil; -import lombok.Data; -import lombok.EqualsAndHashCode; +import lombok.*; /** * 支付关闭参数 * @author xxm * @since 2023/12/17 */ -@EqualsAndHashCode(callSuper = true) -@Data +@Getter +@Setter public class PayCloseParam extends DaxPayRequest { /** 订单号 */ private String orderNo; /** 商户订单号 */ - private String bizTradeNo; + private String bizOrderNo; /** * 方法请求路径 diff --git a/daxpay-single-sdk/src/main/java/cn/daxpay/single/sdk/param/pay/PayParam.java b/daxpay-single-sdk/src/main/java/cn/daxpay/single/sdk/param/pay/PayParam.java index 10f5555c9..1f275bdca 100644 --- a/daxpay-single-sdk/src/main/java/cn/daxpay/single/sdk/param/pay/PayParam.java +++ b/daxpay-single-sdk/src/main/java/cn/daxpay/single/sdk/param/pay/PayParam.java @@ -13,7 +13,6 @@ import cn.hutool.core.lang.TypeReference; import cn.hutool.json.JSONUtil; import lombok.Getter; import lombok.Setter; -import lombok.ToString; import java.time.LocalDateTime; @@ -24,7 +23,6 @@ import java.time.LocalDateTime; */ @Getter @Setter -@ToString(callSuper = true) public class PayParam extends DaxPayRequest { /** 商户订单号 */ diff --git a/daxpay-single-sdk/src/test/java/cn/daxpay/single/sdk/payment/PayCloseOrderTest.java b/daxpay-single-sdk/src/test/java/cn/daxpay/single/sdk/payment/PayCloseOrderTest.java index 51592947b..8cb86723c 100644 --- a/daxpay-single-sdk/src/test/java/cn/daxpay/single/sdk/payment/PayCloseOrderTest.java +++ b/daxpay-single-sdk/src/test/java/cn/daxpay/single/sdk/payment/PayCloseOrderTest.java @@ -1,9 +1,11 @@ package cn.daxpay.single.sdk.payment; import cn.daxpay.single.sdk.code.SignTypeEnum; +import cn.daxpay.single.sdk.model.pay.PayCancelModel; import cn.daxpay.single.sdk.model.pay.PayCloseModel; import cn.daxpay.single.sdk.net.DaxPayConfig; import cn.daxpay.single.sdk.net.DaxPayKit; +import cn.daxpay.single.sdk.param.pay.PayCancelParam; import cn.daxpay.single.sdk.param.pay.PayCloseParam; import cn.daxpay.single.sdk.response.DaxPayResult; import cn.hutool.json.JSONUtil; @@ -37,4 +39,13 @@ public class PayCloseOrderTest { DaxPayResult execute = DaxPayKit.execute(param); System.out.println(JSONUtil.toJsonStr(execute)); } + + @Test + public void cancel(){ + PayCancelParam param = new PayCancelParam(); + param.setOrderNo("DEVP24060518083863000001"); + param.setClientIp("127.0.0.1"); + DaxPayResult execute = DaxPayKit.execute(param); + System.out.println(JSONUtil.toJsonStr(execute)); + } } diff --git a/daxpay-single/daxpay-single-core/src/main/java/cn/daxpay/single/code/PayStatusEnum.java b/daxpay-single/daxpay-single-core/src/main/java/cn/daxpay/single/code/PayStatusEnum.java index 87946d9d7..92da52189 100644 --- a/daxpay-single/daxpay-single-core/src/main/java/cn/daxpay/single/code/PayStatusEnum.java +++ b/daxpay-single/daxpay-single-core/src/main/java/cn/daxpay/single/code/PayStatusEnum.java @@ -18,6 +18,7 @@ public enum PayStatusEnum { PROGRESS("progress","支付中"), SUCCESS("success","成功"), CLOSE("close","支付关闭"), + CANCEL("cancel","支付撤销"), REFUNDING("refunding","退款中"), PARTIAL_REFUND("partial_refund","部分退款"), REFUNDED("refunded","全部退款"), diff --git a/daxpay-single/daxpay-single-core/src/main/java/cn/daxpay/single/code/PaymentApiCode.java b/daxpay-single/daxpay-single-core/src/main/java/cn/daxpay/single/code/PaymentApiCode.java index fbe8bd278..d0d7e9308 100644 --- a/daxpay-single/daxpay-single-core/src/main/java/cn/daxpay/single/code/PaymentApiCode.java +++ b/daxpay-single/daxpay-single-core/src/main/java/cn/daxpay/single/code/PaymentApiCode.java @@ -12,6 +12,8 @@ public interface PaymentApiCode { String REFUND = "refund"; /** 关闭订单 */ String CLOSE = "close"; + /** 撤销订单 */ + String CANCEL = "cancel"; /** 分账 */ String ALLOCATION = "allocation"; /** 转账 */ diff --git a/daxpay-single/daxpay-single-core/src/main/java/cn/daxpay/single/entity/RefundableInfo.java b/daxpay-single/daxpay-single-core/src/main/java/cn/daxpay/single/entity/RefundableInfo.java deleted file mode 100644 index 450bcfeb3..000000000 --- a/daxpay-single/daxpay-single-core/src/main/java/cn/daxpay/single/entity/RefundableInfo.java +++ /dev/null @@ -1,25 +0,0 @@ -package cn.daxpay.single.entity; - -import cn.daxpay.single.code.PayChannelEnum; -import lombok.Data; -import lombok.experimental.Accessors; - -/** - * 可退款信息(不持久化,直接保存为json) - * @author xxm - * @since 2023/12/18 - */ -@Data -@Accessors(chain = true) -public class RefundableInfo { - /** - * 通道 - * @see PayChannelEnum#getCode() - */ - private String channel; - - /** - * 可退款金额 - */ - private Integer amount; -} diff --git a/daxpay-single/daxpay-single-core/src/main/java/cn/daxpay/single/param/payment/pay/PayCancelParam.java b/daxpay-single/daxpay-single-core/src/main/java/cn/daxpay/single/param/payment/pay/PayCancelParam.java index d06882294..e1ebcbb98 100644 --- a/daxpay-single/daxpay-single-core/src/main/java/cn/daxpay/single/param/payment/pay/PayCancelParam.java +++ b/daxpay-single/daxpay-single-core/src/main/java/cn/daxpay/single/param/payment/pay/PayCancelParam.java @@ -16,9 +16,11 @@ import lombok.EqualsAndHashCode; @Schema(title = "支付订单撤销") public class PayCancelParam extends PaymentCommonParam { + /** 订单号 */ @Schema(description = "订单号") private String orderNo; + /** 商户订单号 */ @Schema(description = "商户订单号") private String bizOrderNo; } diff --git a/daxpay-single/daxpay-single-core/src/main/java/cn/daxpay/single/param/payment/pay/PayCloseParam.java b/daxpay-single/daxpay-single-core/src/main/java/cn/daxpay/single/param/payment/pay/PayCloseParam.java index d372aae50..35eda5c9b 100644 --- a/daxpay-single/daxpay-single-core/src/main/java/cn/daxpay/single/param/payment/pay/PayCloseParam.java +++ b/daxpay-single/daxpay-single-core/src/main/java/cn/daxpay/single/param/payment/pay/PayCloseParam.java @@ -21,5 +21,5 @@ public class PayCloseParam extends PaymentCommonParam { /** 商户订单号 */ @Schema(description = "商户订单号") - private String bizTradeNo; + private String bizOrderNo; } diff --git a/daxpay-single/daxpay-single-core/src/main/java/cn/daxpay/single/result/pay/PayCancelResult.java b/daxpay-single/daxpay-single-core/src/main/java/cn/daxpay/single/result/pay/PayCancelResult.java new file mode 100644 index 000000000..769fc2bbf --- /dev/null +++ b/daxpay-single/daxpay-single-core/src/main/java/cn/daxpay/single/result/pay/PayCancelResult.java @@ -0,0 +1,19 @@ +package cn.daxpay.single.result.pay; + +import cn.daxpay.single.result.PaymentCommonResult; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.experimental.Accessors; + +/** + * 支付关闭响应参数 + * @author xxm + * @since 2024/4/23 + */ +@EqualsAndHashCode(callSuper = true) +@Data +@Accessors(chain = true) +@Schema(title = "支付撤销响应参数") +public class PayCancelResult extends PaymentCommonResult { +} diff --git a/daxpay-single/daxpay-single-gateway/src/main/java/cn/daxpay/single/gateway/controller/UniPayController.java b/daxpay-single/daxpay-single-gateway/src/main/java/cn/daxpay/single/gateway/controller/UniPayController.java index 697c9fbfa..51b581d9d 100644 --- a/daxpay-single/daxpay-single-gateway/src/main/java/cn/daxpay/single/gateway/controller/UniPayController.java +++ b/daxpay-single/daxpay-single-gateway/src/main/java/cn/daxpay/single/gateway/controller/UniPayController.java @@ -2,16 +2,19 @@ package cn.daxpay.single.gateway.controller; import cn.bootx.platform.common.core.annotation.IgnoreAuth; import cn.daxpay.single.code.PaymentApiCode; +import cn.daxpay.single.param.payment.pay.PayCancelParam; import cn.daxpay.single.param.payment.pay.PayCloseParam; import cn.daxpay.single.param.payment.pay.PayParam; import cn.daxpay.single.param.payment.refund.RefundParam; import cn.daxpay.single.param.payment.transfer.TransferParam; import cn.daxpay.single.result.DaxResult; +import cn.daxpay.single.result.pay.PayCancelResult; import cn.daxpay.single.result.pay.PayCloseResult; import cn.daxpay.single.result.pay.PayResult; import cn.daxpay.single.result.pay.RefundResult; import cn.daxpay.single.service.annotation.PaymentSign; import cn.daxpay.single.service.annotation.InitPaymentContext; +import cn.daxpay.single.service.core.payment.cancel.service.PayCancelService; import cn.daxpay.single.service.core.payment.close.service.PayCloseService; import cn.daxpay.single.service.core.payment.pay.service.PayService; import cn.daxpay.single.service.core.payment.refund.service.RefundService; @@ -38,6 +41,7 @@ public class UniPayController { private final PayService payService; private final RefundService refundService; private final PayCloseService payCloseService; + private final PayCancelService payCancelService; @PaymentSign @InitPaymentContext(PaymentApiCode.PAY) @@ -55,6 +59,14 @@ public class UniPayController { return DaxRes.ok(payCloseService.close(param)); } + @PaymentSign + @InitPaymentContext(PaymentApiCode.CANCEL) + @Operation(summary = "支付撤销接口") + @PostMapping("/cancel") + public DaxResult cancel(@RequestBody PayCancelParam param){ + return DaxRes.ok(payCancelService.cancel(param)); + } + @PaymentSign @InitPaymentContext(PaymentApiCode.REFUND) @Operation(summary = "统一退款接口") diff --git a/daxpay-single/daxpay-single-service/src/main/java/cn/daxpay/single/service/code/PayCloseTypeEnum.java b/daxpay-single/daxpay-single-service/src/main/java/cn/daxpay/single/service/code/PayCloseTypeEnum.java new file mode 100644 index 000000000..629186d7c --- /dev/null +++ b/daxpay-single/daxpay-single-service/src/main/java/cn/daxpay/single/service/code/PayCloseTypeEnum.java @@ -0,0 +1,25 @@ +package cn.daxpay.single.service.code; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** + * 订单关闭类型 + * @author xxm + * @since 2024/6/5 + */ +@Getter +@AllArgsConstructor +public enum PayCloseTypeEnum { + /** + * 订单关闭 + */ + CLOSE("close", "订单关闭"), + /** + * 订单撤销 + */ + CANCEL("cancel", "订单撤销"), + ; + final String code; + final String name; +} diff --git a/daxpay-single/daxpay-single-service/src/main/java/cn/daxpay/single/service/core/channel/alipay/service/AliPayCloseService.java b/daxpay-single/daxpay-single-service/src/main/java/cn/daxpay/single/service/core/channel/alipay/service/AliPayCloseService.java index 2d936af92..501438990 100644 --- a/daxpay-single/daxpay-single-service/src/main/java/cn/daxpay/single/service/core/channel/alipay/service/AliPayCloseService.java +++ b/daxpay-single/daxpay-single-service/src/main/java/cn/daxpay/single/service/core/channel/alipay/service/AliPayCloseService.java @@ -1,24 +1,24 @@ package cn.daxpay.single.service.core.channel.alipay.service; -import cn.bootx.platform.common.spring.exception.RetryableException; import cn.daxpay.single.code.PaySyncStatusEnum; -import cn.daxpay.single.service.code.AliPayCode; -import cn.daxpay.single.service.core.payment.sync.result.PayRemoteSyncResult; -import cn.daxpay.single.service.core.order.pay.entity.PayOrder; import cn.daxpay.single.exception.pay.PayFailureException; +import cn.daxpay.single.service.code.AliPayCode; +import cn.daxpay.single.service.core.order.pay.entity.PayOrder; +import cn.daxpay.single.service.core.payment.sync.result.PayRemoteSyncResult; import com.alipay.api.AlipayApiException; +import com.alipay.api.domain.AlipayTradeCancelModel; import com.alipay.api.domain.AlipayTradeCloseModel; +import com.alipay.api.response.AlipayTradeCancelResponse; import com.alipay.api.response.AlipayTradeCloseResponse; import com.ijpay.alipay.AliPayApi; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; -import org.springframework.retry.annotation.Retryable; import org.springframework.stereotype.Service; import java.util.Objects; /** - * 支付宝支付取消和支付关闭 + * 支付宝支付撤销和支付关闭 * * @author xxm * @since 2021/4/20 @@ -30,15 +30,13 @@ public class AliPayCloseService { private final AliPaySyncService aliPaySyncService; /** - * 关闭支付 此处使用交易关闭接口, 支付宝支持 交易关闭 和 交易撤销 两种关闭订单的方式, 区别如下 + * 关闭支付 此处使用交易关闭接口 * 交易关闭: 只有订单在未支付的状态下才可以进行关闭, 商户不需要额外申请就有此接口的权限 - * 交易撤销: 如果用户支付成功,会将此订单资金退还给用户. 限制时间为1天,过了24小时,该接口无法再使用。可以视为一个特殊的接口, 需要专门签约这个接口的权限 *

* 1. 如果未查询到支付单,视为支付关闭, * 2. 如果返回"当前交易状态不支持此操作", 同步网关支付状态, 判断网关是否已经被关闭 * */ - @Retryable(value = RetryableException.class) public void close(PayOrder payOrder) { AlipayTradeCloseModel model = new AlipayTradeCloseModel(); model.setOutTradeNo(payOrder.getOrderNo()); @@ -65,6 +63,38 @@ public class AliPayCloseService { } } + /** + * 交易撤销: 如果用户支付成功,会将此订单资金退还给用户. 限制时间为1天,过了24小时,该接口无法再使用。 + * 可以视为一个特殊的接口, 需要专门签约这个接口的权限 + */ + public void cancel(PayOrder payOrder) { + AlipayTradeCancelModel model = new AlipayTradeCancelModel(); + model.setOutTradeNo(payOrder.getOrderNo()); + + try { + AlipayTradeCancelResponse response = AliPayApi.tradeCancelToResponse(model); + if (!Objects.equals(AliPayCode.SUCCESS, response.getCode())) { + if (!Objects.equals(AliPayCode.SUCCESS, response.getCode())) { + // 如果返回"当前交易状态不支持此操作", 同步网关支付状态, 判断网关是否已经被关闭 + if (Objects.equals(response.getSubCode(),AliPayCode.ACQ_TRADE_STATUS_ERROR)){ + if (this.syncStatus(payOrder)){ + return; + } + } + // 返回"交易不存在"视同关闭成功 + if (Objects.equals(response.getSubCode(),AliPayCode.ACQ_TRADE_NOT_EXIST)){ + return; + } + log.error("网关返回关闭失败: {}", response.getSubMsg()); + throw new PayFailureException(response.getSubMsg()); + } + } + } catch (AlipayApiException e) { + log.error("关闭订单失败:", e); + throw new PayFailureException("关闭订单失败"); + } + } + /** * 关闭失败后, 获取支付网关的状态, 如果是关闭返回true, 其他情况抛出异常 diff --git a/daxpay-single/daxpay-single-service/src/main/java/cn/daxpay/single/service/core/channel/wechat/service/WeChatPayCloseService.java b/daxpay-single/daxpay-single-service/src/main/java/cn/daxpay/single/service/core/channel/wechat/service/WeChatPayCloseService.java index b14142df7..7be1efbe3 100644 --- a/daxpay-single/daxpay-single-service/src/main/java/cn/daxpay/single/service/core/channel/wechat/service/WeChatPayCloseService.java +++ b/daxpay-single/daxpay-single-service/src/main/java/cn/daxpay/single/service/core/channel/wechat/service/WeChatPayCloseService.java @@ -1,10 +1,10 @@ package cn.daxpay.single.service.core.channel.wechat.service; -import cn.bootx.platform.common.spring.exception.RetryableException; import cn.daxpay.single.exception.pay.PayFailureException; import cn.daxpay.single.service.code.WeChatPayCode; import cn.daxpay.single.service.core.channel.wechat.entity.WeChatPayConfig; import cn.daxpay.single.service.core.order.pay.entity.PayOrder; +import cn.hutool.core.codec.Base64; import cn.hutool.core.util.StrUtil; import com.ijpay.core.enums.SignType; import com.ijpay.core.kit.WxPayKit; @@ -12,9 +12,9 @@ import com.ijpay.wxpay.WxPayApi; import com.ijpay.wxpay.model.CloseOrderModel; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; -import org.springframework.retry.annotation.Retryable; import org.springframework.stereotype.Service; +import java.io.ByteArrayInputStream; import java.util.Map; /** @@ -31,7 +31,6 @@ public class WeChatPayCloseService { /** * 关闭支付, 微信对已经关闭的支付单也可以重复关闭 */ - @Retryable(value = RetryableException.class) public void close(PayOrder payOrder, WeChatPayConfig weChatPayConfig) { // 只有部分需要调用微信网关进行关闭 Map params = CloseOrderModel.builder() @@ -47,6 +46,32 @@ public class WeChatPayCloseService { this.verifyErrorMsg(result); } + /** + * 撤销接口 + */ + public void cancel(PayOrder payOrder, WeChatPayConfig weChatPayConfig){ + // 只有部分需要调用微信网关进行关闭 + Map params = CloseOrderModel.builder() + .appid(weChatPayConfig.getWxAppId()) + .mch_id(weChatPayConfig.getWxMchId()) + .out_trade_no(payOrder.getOrderNo()) + .nonce_str(WxPayKit.generateStr()) + .build() + .createSign(weChatPayConfig.getApiKeyV2(), SignType.HMACSHA256); + + // 获取证书文件 + if (StrUtil.isBlank(weChatPayConfig.getP12())){ + String errorMsg = "微信p.12证书未配置,无法进行退款"; + throw new PayFailureException(errorMsg); + } + byte[] fileBytes = Base64.decode(weChatPayConfig.getP12()); + ByteArrayInputStream inputStream = new ByteArrayInputStream(fileBytes); + // 证书密码为 微信商户号 + String xmlResult = WxPayApi.orderReverse(params,inputStream,weChatPayConfig.getWxMchId()); + Map result = WxPayKit.xmlToMap(xmlResult); + this.verifyErrorMsg(result); + } + /** * 验证错误信息 */ diff --git a/daxpay-single/daxpay-single-service/src/main/java/cn/daxpay/single/service/core/order/pay/entity/PayOrder.java b/daxpay-single/daxpay-single-service/src/main/java/cn/daxpay/single/service/core/order/pay/entity/PayOrder.java index 6bb7e72ff..0e4d56441 100644 --- a/daxpay-single/daxpay-single-service/src/main/java/cn/daxpay/single/service/core/order/pay/entity/PayOrder.java +++ b/daxpay-single/daxpay-single-service/src/main/java/cn/daxpay/single/service/core/order/pay/entity/PayOrder.java @@ -2,6 +2,7 @@ package cn.daxpay.single.service.core.order.pay.entity; import cn.bootx.platform.common.core.function.EntityBaseFunction; import cn.bootx.platform.common.mybatisplus.base.MpBaseEntity; +import cn.daxpay.single.code.PayMethodEnum; import cn.daxpay.single.code.PayOrderAllocStatusEnum; import cn.daxpay.single.code.PayChannelEnum; import cn.daxpay.single.code.PayStatusEnum; @@ -71,6 +72,7 @@ public class PayOrder extends MpBaseEntity implements EntityBaseFunction ORDER_FINISH = Arrays.asList(PayStatusEnum.CLOSE.getCode(), PayStatusEnum.SUCCESS.getCode()); + private final List ORDER_FINISH = Arrays.asList( + PayStatusEnum.CLOSE.getCode(), + PayStatusEnum.CANCEL.getCode(), + PayStatusEnum.SUCCESS.getCode()); /** * 查询 diff --git a/daxpay-single/daxpay-single-service/src/main/java/cn/daxpay/single/service/core/payment/cancel/factory/PayCancelStrategyFactory.java b/daxpay-single/daxpay-single-service/src/main/java/cn/daxpay/single/service/core/payment/cancel/factory/PayCancelStrategyFactory.java new file mode 100644 index 000000000..1ed871cd7 --- /dev/null +++ b/daxpay-single/daxpay-single-service/src/main/java/cn/daxpay/single/service/core/payment/cancel/factory/PayCancelStrategyFactory.java @@ -0,0 +1,38 @@ +package cn.daxpay.single.service.core.payment.cancel.factory; + +import cn.daxpay.single.code.PayChannelEnum; +import cn.daxpay.single.exception.pay.PayUnsupportedMethodException; +import cn.daxpay.single.service.core.payment.cancel.strategy.AliPayCancelStrategy; +import cn.daxpay.single.service.core.payment.cancel.strategy.WeChatPayCancelStrategy; +import cn.daxpay.single.service.func.AbsPayCancelStrategy; +import cn.hutool.extra.spring.SpringUtil; +import lombok.experimental.UtilityClass; + +/** + * 订单撤销策略工厂 + * @author xxm + * @since 2024/6/5 + */ +@UtilityClass +public class PayCancelStrategyFactory { + + /** + * 根据传入的支付通道创建策略 + * @return 支付策略 + */ + public static AbsPayCancelStrategy create(String channel) { + PayChannelEnum channelEnum = PayChannelEnum.findByCode(channel); + AbsPayCancelStrategy strategy; + switch (channelEnum) { + case ALI: + strategy = SpringUtil.getBean(AliPayCancelStrategy.class); + break; + case WECHAT: + strategy = SpringUtil.getBean(WeChatPayCancelStrategy.class); + break; + default: + throw new PayUnsupportedMethodException(); + } + return strategy; + } +} diff --git a/daxpay-single/daxpay-single-service/src/main/java/cn/daxpay/single/service/core/payment/cancel/service/PayCancelService.java b/daxpay-single/daxpay-single-service/src/main/java/cn/daxpay/single/service/core/payment/cancel/service/PayCancelService.java new file mode 100644 index 000000000..ebcc7158b --- /dev/null +++ b/daxpay-single/daxpay-single-service/src/main/java/cn/daxpay/single/service/core/payment/cancel/service/PayCancelService.java @@ -0,0 +1,113 @@ +package cn.daxpay.single.service.core.payment.cancel.service; + +import cn.bootx.platform.common.core.exception.RepetitiveOperationException; +import cn.daxpay.single.code.PayStatusEnum; +import cn.daxpay.single.exception.pay.PayFailureException; +import cn.daxpay.single.param.payment.pay.PayCancelParam; +import cn.daxpay.single.result.pay.PayCancelResult; +import cn.daxpay.single.service.code.PayCloseTypeEnum; +import cn.daxpay.single.service.common.local.PaymentContextLocal; +import cn.daxpay.single.service.core.order.pay.entity.PayOrder; +import cn.daxpay.single.service.core.order.pay.service.PayOrderQueryService; +import cn.daxpay.single.service.core.order.pay.service.PayOrderService; +import cn.daxpay.single.service.core.payment.cancel.factory.PayCancelStrategyFactory; +import cn.daxpay.single.service.core.payment.notice.service.ClientNoticeService; +import cn.daxpay.single.service.core.record.close.entity.PayCloseRecord; +import cn.daxpay.single.service.core.record.close.service.PayCloseRecordService; +import cn.daxpay.single.service.func.AbsPayCancelStrategy; +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.time.LocalDateTime; +import java.util.Objects; + +/** + * 支付关闭和撤销服务 + * @author xxm + * @since 2023/12/18 + */ +@Slf4j +@Service +@RequiredArgsConstructor +public class PayCancelService { + private final PayOrderService payOrderService; + private final PayOrderQueryService payOrderQueryService; + private final PayCloseRecordService payCloseRecordService; + private final ClientNoticeService clientsService;; + + private final LockTemplate lockTemplate; + + /** + * 撤销支付 + */ + public PayCancelResult cancel(PayCancelParam param){ + PayOrder payOrder = payOrderQueryService.findByBizOrOrderNo(param.getOrderNo(), param.getBizOrderNo()) + .orElseThrow(() -> new PayFailureException("支付订单不存在")); + LockInfo lock = lockTemplate.lock("payment:cancel:" + payOrder.getId(),10000, 50); + if (Objects.isNull(lock)){ + throw new RepetitiveOperationException("支付订单已在撤销中,请勿重复发起"); + } + try { + PayCancelResult result = new PayCancelResult(); + // 状态检查, 只有支付中可以进行撤销支付 + if (!Objects.equals(payOrder.getStatus(), PayStatusEnum.PROGRESS.getCode())) { + throw new PayFailureException("订单不是支付中, 无法进行撤销订单"); + } + try { + AbsPayCancelStrategy strategy = PayCancelStrategyFactory.create(payOrder.getChannel()); + // 设置支付订单 + strategy.setOrder(payOrder); + // 撤销前准备 + strategy.doBeforeCancelHandler(); + // 执行撤销策略 + strategy.doCancelHandler(); + // 成功处理 + this.successHandler(payOrder); + // 返回结果 + return result; + } catch (Exception e) { + log.error("撤销订单失败, id: {}:", payOrder.getId()); + log.error("撤销订单失败:", e); + // 记录撤销失败的记录 + this.saveRecord(payOrder, false, e.getMessage()); + throw new PayFailureException("撤销订单失败"); + } + } finally { + lockTemplate.releaseLock(lock); + } + } + + /** + * 成功后处理方法 + */ + private void successHandler(PayOrder payOrder){ + // 撤销订单 + payOrder.setStatus(PayStatusEnum.CANCEL.getCode()) + .setCloseTime(LocalDateTime.now()); + payOrderService.updateById(payOrder); + // 发送通知 + clientsService.registerPayNotice(payOrder,null); + this.saveRecord(payOrder,true,null); + } + + /** + * 保存撤销记录 + */ + private void saveRecord(PayOrder payOrder, boolean closed, String errMsg){ + String clientIp = PaymentContextLocal.get() + .getRequestInfo() + .getClientIp(); + PayCloseRecord record = new PayCloseRecord() + .setOrderNo(payOrder.getOrderNo()) + .setBizOrderNo(payOrder.getBizOrderNo()) + .setChannel(payOrder.getChannel()) + .setCloseType(PayCloseTypeEnum.CANCEL.getCode()) + .setClosed(closed) + .setErrorMsg(errMsg) + .setClientIp(clientIp); + payCloseRecordService.saveRecord(record); + } +} diff --git a/daxpay-single/daxpay-single-service/src/main/java/cn/daxpay/single/service/core/payment/cancel/strategy/AliPayCancelStrategy.java b/daxpay-single/daxpay-single-service/src/main/java/cn/daxpay/single/service/core/payment/cancel/strategy/AliPayCancelStrategy.java new file mode 100644 index 000000000..75865b439 --- /dev/null +++ b/daxpay-single/daxpay-single-service/src/main/java/cn/daxpay/single/service/core/payment/cancel/strategy/AliPayCancelStrategy.java @@ -0,0 +1,51 @@ +package cn.daxpay.single.service.core.payment.cancel.strategy; + +import cn.daxpay.single.code.PayChannelEnum; +import cn.daxpay.single.service.core.channel.alipay.entity.AliPayConfig; +import cn.daxpay.single.service.core.channel.alipay.service.AliPayCloseService; +import cn.daxpay.single.service.core.channel.alipay.service.AliPayConfigService; +import cn.daxpay.single.service.func.AbsPayCancelStrategy; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.context.annotation.Scope; +import org.springframework.stereotype.Service; + +import static org.springframework.beans.factory.config.BeanDefinition.SCOPE_PROTOTYPE; + +/** + * 支付宝撤销策略 + * @author xxm + * @since 2024/6/5 + */ +@Slf4j +@Scope(SCOPE_PROTOTYPE) +@Service +@RequiredArgsConstructor +public class AliPayCancelStrategy extends AbsPayCancelStrategy { + + private final AliPayConfigService alipayConfigService; + + private final AliPayCloseService aliPayCloseService; + + @Override + public PayChannelEnum getChannel() { + return PayChannelEnum.ALI; + } + + /** + * 关闭前的处理方式 + */ + @Override + public void doBeforeCancelHandler() { + AliPayConfig config = alipayConfigService.getConfig(); + alipayConfigService.initConfig(config); + } + + /** + * 关闭操作 + */ + @Override + public void doCancelHandler() { + aliPayCloseService.cancel(this.getOrder()); + } +} diff --git a/daxpay-single/daxpay-single-service/src/main/java/cn/daxpay/single/service/core/payment/cancel/strategy/WeChatPayCancelStrategy.java b/daxpay-single/daxpay-single-service/src/main/java/cn/daxpay/single/service/core/payment/cancel/strategy/WeChatPayCancelStrategy.java new file mode 100644 index 000000000..df91dde08 --- /dev/null +++ b/daxpay-single/daxpay-single-service/src/main/java/cn/daxpay/single/service/core/payment/cancel/strategy/WeChatPayCancelStrategy.java @@ -0,0 +1,58 @@ +package cn.daxpay.single.service.core.payment.cancel.strategy; + +import cn.daxpay.single.code.PayChannelEnum; +import cn.daxpay.single.code.PayMethodEnum; +import cn.daxpay.single.service.core.channel.wechat.entity.WeChatPayConfig; +import cn.daxpay.single.service.core.channel.wechat.service.WeChatPayCloseService; +import cn.daxpay.single.service.core.channel.wechat.service.WeChatPayConfigService; +import cn.daxpay.single.service.func.AbsPayCancelStrategy; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.context.annotation.Scope; +import org.springframework.stereotype.Service; + +import java.util.Objects; + +import static org.springframework.beans.factory.config.BeanDefinition.SCOPE_PROTOTYPE; + +/** + * 微信支付关闭策略 + * @author xxm + * @since 2023/12/30 + */ +@Slf4j +@Scope(SCOPE_PROTOTYPE) +@Service +@RequiredArgsConstructor +public class WeChatPayCancelStrategy extends AbsPayCancelStrategy { + + private final WeChatPayConfigService weChatPayConfigService; + private final WeChatPayCloseService weChatPayCloseService; + + private WeChatPayConfig weChatPayConfig; + + @Override + public PayChannelEnum getChannel() { + return PayChannelEnum.WECHAT; + } + + /** + * 关闭前的处理方式 + */ + @Override + public void doBeforeCancelHandler() { + // 非当面付订单不可以关闭 + if (!Objects.equals(this.getOrder().getMethod(), PayMethodEnum.BARCODE.getCode())) { + throw new RuntimeException("微信当面付订单才可以被撤销"); + } + this.weChatPayConfig = weChatPayConfigService.getConfig(); + } + + /** + * 关闭操作 + */ + @Override + public void doCancelHandler() { + weChatPayCloseService.cancel(this.getOrder(), weChatPayConfig); + } +} diff --git a/daxpay-single/daxpay-single-service/src/main/java/cn/daxpay/single/service/core/payment/close/service/PayCloseService.java b/daxpay-single/daxpay-single-service/src/main/java/cn/daxpay/single/service/core/payment/close/service/PayCloseService.java index 3ebce3a2b..75e13c798 100644 --- a/daxpay-single/daxpay-single-service/src/main/java/cn/daxpay/single/service/core/payment/close/service/PayCloseService.java +++ b/daxpay-single/daxpay-single-service/src/main/java/cn/daxpay/single/service/core/payment/close/service/PayCloseService.java @@ -5,6 +5,7 @@ import cn.daxpay.single.code.PayStatusEnum; import cn.daxpay.single.exception.pay.PayFailureException; import cn.daxpay.single.param.payment.pay.PayCloseParam; import cn.daxpay.single.result.pay.PayCloseResult; +import cn.daxpay.single.service.code.PayCloseTypeEnum; import cn.daxpay.single.service.common.local.PaymentContextLocal; import cn.daxpay.single.service.core.order.pay.entity.PayOrder; import cn.daxpay.single.service.core.order.pay.service.PayOrderQueryService; @@ -43,7 +44,7 @@ public class PayCloseService { * 关闭支付 */ public PayCloseResult close(PayCloseParam param){ - PayOrder payOrder = payOrderQueryService.findByBizOrOrderNo(param.getOrderNo(), param.getBizTradeNo()) + PayOrder payOrder = payOrderQueryService.findByBizOrOrderNo(param.getOrderNo(), param.getBizOrderNo()) .orElseThrow(() -> new PayFailureException("支付订单不存在")); LockInfo lock = lockTemplate.lock("payment:close:" + payOrder.getId(),10000, 50); if (Objects.isNull(lock)){ @@ -83,7 +84,6 @@ public class PayCloseService { // 记录关闭失败的记录 this.saveRecord(payOrder, false, e.getMessage()); throw new PayFailureException("关闭订单失败"); - } } @@ -111,6 +111,7 @@ public class PayCloseService { .setOrderNo(payOrder.getOrderNo()) .setBizOrderNo(payOrder.getBizOrderNo()) .setChannel(payOrder.getChannel()) + .setCloseType(PayCloseTypeEnum.CLOSE.getCode()) .setClosed(closed) .setErrorMsg(errMsg) .setClientIp(clientIp); diff --git a/daxpay-single/daxpay-single-service/src/main/java/cn/daxpay/single/service/core/payment/pay/service/PayAssistService.java b/daxpay-single/daxpay-single-service/src/main/java/cn/daxpay/single/service/core/payment/pay/service/PayAssistService.java index 443af4892..a863f50be 100644 --- a/daxpay-single/daxpay-single-service/src/main/java/cn/daxpay/single/service/core/payment/pay/service/PayAssistService.java +++ b/daxpay-single/daxpay-single-service/src/main/java/cn/daxpay/single/service/core/payment/pay/service/PayAssistService.java @@ -200,7 +200,7 @@ public class PayAssistService { throw new PayFailureException("已经支付成功,请勿重新支付"); } // 支付失败类型状态 - List tradesStatus = Arrays.asList(FAIL.getCode(), CLOSE.getCode()); + List tradesStatus = Arrays.asList(FAIL.getCode(), CLOSE.getCode(), CANCEL.getCode()); if (tradesStatus.contains(payOrder.getStatus())) { throw new PayFailureException("支付失败或已经被关闭"); } diff --git a/daxpay-single/daxpay-single-service/src/main/java/cn/daxpay/single/service/core/payment/refund/service/RefundAssistService.java b/daxpay-single/daxpay-single-service/src/main/java/cn/daxpay/single/service/core/payment/refund/service/RefundAssistService.java index 8d0116621..7bf7de3af 100644 --- a/daxpay-single/daxpay-single-service/src/main/java/cn/daxpay/single/service/core/payment/refund/service/RefundAssistService.java +++ b/daxpay-single/daxpay-single-service/src/main/java/cn/daxpay/single/service/core/payment/refund/service/RefundAssistService.java @@ -83,6 +83,7 @@ public class RefundAssistService { List tradesStatus = Arrays.asList( PayStatusEnum.PROGRESS.getCode(), PayStatusEnum.CLOSE.getCode(), + PayStatusEnum.CANCEL.getCode(), PayStatusEnum.REFUNDED.getCode(), PayStatusEnum.REFUNDING.getCode(), PayStatusEnum.FAIL.getCode()); diff --git a/daxpay-single/daxpay-single-service/src/main/java/cn/daxpay/single/service/core/payment/repair/service/PayRepairService.java b/daxpay-single/daxpay-single-service/src/main/java/cn/daxpay/single/service/core/payment/repair/service/PayRepairService.java index c9f1a866e..7a8a03ea2 100644 --- a/daxpay-single/daxpay-single-service/src/main/java/cn/daxpay/single/service/core/payment/repair/service/PayRepairService.java +++ b/daxpay-single/daxpay-single-service/src/main/java/cn/daxpay/single/service/core/payment/repair/service/PayRepairService.java @@ -74,8 +74,7 @@ public class PayRepairService { repairResult.setAfterPayStatus(PayStatusEnum.CLOSE); break; case PROGRESS: - this.waitPay(order); - repairResult.setAfterPayStatus(PayStatusEnum.PROGRESS); + // TODO 保存为异常订单 break; case CLOSE_GATEWAY: this.closeRemote(order, repairStrategy); @@ -93,17 +92,6 @@ public class PayRepairService { return repairResult; } - /** - * 变更未待支付 - * TODO 后期保存为异常订单 - */ - private void waitPay(PayOrder order) { - // 修改订单支付状态为待支付 - order.setStatus(PayStatusEnum.PROGRESS.getCode()) - .setPayTime(null) - .setCloseTime(null); - payOrderService.updateById(order); - } /** * 变更为已支付 diff --git a/daxpay-single/daxpay-single-service/src/main/java/cn/daxpay/single/service/core/payment/sync/service/PaySyncService.java b/daxpay-single/daxpay-single-service/src/main/java/cn/daxpay/single/service/core/payment/sync/service/PaySyncService.java index a26d371e7..29e18b604 100644 --- a/daxpay-single/daxpay-single-service/src/main/java/cn/daxpay/single/service/core/payment/sync/service/PaySyncService.java +++ b/daxpay-single/daxpay-single-service/src/main/java/cn/daxpay/single/service/core/payment/sync/service/PaySyncService.java @@ -33,7 +33,6 @@ import org.springframework.transaction.annotation.Transactional; import java.time.LocalDateTime; import java.util.Arrays; -import java.util.Collections; import java.util.List; import java.util.Objects; @@ -163,7 +162,7 @@ public class PaySyncService { 未找到订单可能是发起支付用户未操作、支付已关闭、交易未找到三种情况 所以需要根据本地订单不同的状态进行特殊处理, 此处视为支付已关闭、交易未找到这两种, 处理方式相同, 都作为支付关闭处理 */ - List payCloseEnums = Collections.singletonList(PayStatusEnum.CLOSE.getCode()); + List payCloseEnums = Arrays.asList(PayStatusEnum.CLOSE.getCode(), PayStatusEnum.CANCEL.getCode()); List syncClose = Arrays.asList(CLOSED, NOT_FOUND, NOT_FOUND_UNKNOWN); if (payCloseEnums.contains(orderStatus) && syncClose.contains(syncStatus)){ return true; diff --git a/daxpay-single/daxpay-single-service/src/main/java/cn/daxpay/single/service/core/record/close/entity/PayCloseRecord.java b/daxpay-single/daxpay-single-service/src/main/java/cn/daxpay/single/service/core/record/close/entity/PayCloseRecord.java index b4e1adf2e..f96ac5ec6 100644 --- a/daxpay-single/daxpay-single-service/src/main/java/cn/daxpay/single/service/core/record/close/entity/PayCloseRecord.java +++ b/daxpay-single/daxpay-single-service/src/main/java/cn/daxpay/single/service/core/record/close/entity/PayCloseRecord.java @@ -3,6 +3,7 @@ package cn.daxpay.single.service.core.record.close.entity; import cn.bootx.platform.common.core.function.EntityBaseFunction; import cn.bootx.platform.common.mybatisplus.base.MpCreateEntity; import cn.daxpay.single.code.PayChannelEnum; +import cn.daxpay.single.service.code.PayCloseTypeEnum; import cn.daxpay.single.service.core.record.close.convert.PayCloseRecordConvert; import cn.daxpay.single.service.dto.record.close.PayCloseRecordDto; import cn.bootx.table.modify.annotation.DbColumn; @@ -39,6 +40,13 @@ public class PayCloseRecord extends MpCreateEntity implements EntityBaseFunction @DbColumn(comment = "关闭的支付通道") private String channel; + /** + * 关闭类型 关闭/撤销 + * @see PayCloseTypeEnum + */ + @DbColumn(comment = "关闭类型") + private String closeType; + /** * 是否关闭成功 */ diff --git a/daxpay-single/daxpay-single-service/src/main/java/cn/daxpay/single/service/dto/record/close/PayCloseRecordDto.java b/daxpay-single/daxpay-single-service/src/main/java/cn/daxpay/single/service/dto/record/close/PayCloseRecordDto.java index 0659b3068..b165e89ac 100644 --- a/daxpay-single/daxpay-single-service/src/main/java/cn/daxpay/single/service/dto/record/close/PayCloseRecordDto.java +++ b/daxpay-single/daxpay-single-service/src/main/java/cn/daxpay/single/service/dto/record/close/PayCloseRecordDto.java @@ -3,6 +3,7 @@ package cn.daxpay.single.service.dto.record.close; import cn.bootx.platform.common.core.rest.dto.BaseDto; import cn.daxpay.single.code.PayChannelEnum; import cn.bootx.table.modify.annotation.DbColumn; +import cn.daxpay.single.service.code.PayCloseTypeEnum; import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; import lombok.EqualsAndHashCode; @@ -34,6 +35,13 @@ public class PayCloseRecordDto extends BaseDto { @DbColumn(comment = "关闭的异步支付通道") private String channel; + /** + * 关闭类型 关闭/撤销 + * @see PayCloseTypeEnum + */ + @DbColumn(comment = "关闭类型") + private String closeType; + /** * 是否关闭成功 */ diff --git a/daxpay-single/daxpay-single-service/src/main/java/cn/daxpay/single/service/func/AbsPayCancelStrategy.java b/daxpay-single/daxpay-single-service/src/main/java/cn/daxpay/single/service/func/AbsPayCancelStrategy.java new file mode 100644 index 000000000..7ef7848d4 --- /dev/null +++ b/daxpay-single/daxpay-single-service/src/main/java/cn/daxpay/single/service/func/AbsPayCancelStrategy.java @@ -0,0 +1,28 @@ +package cn.daxpay.single.service.func; + +import cn.daxpay.single.service.core.order.pay.entity.PayOrder; +import lombok.Getter; +import lombok.Setter; + +/** + * 支付订单撤销接口 + * @author xxm + * @since 2024/6/5 + */ +@Getter +@Setter +public abstract class AbsPayCancelStrategy implements PayStrategy { + /** 支付订单 */ + private PayOrder order = null; + + + /** + * 撤销前的处理方式 + */ + public void doBeforeCancelHandler() {} + + /** + * 撤销操作 + */ + public abstract void doCancelHandler(); +} diff --git a/daxpay-single/daxpay-single-service/src/main/java/cn/daxpay/single/service/param/record/PayCloseRecordQuery.java b/daxpay-single/daxpay-single-service/src/main/java/cn/daxpay/single/service/param/record/PayCloseRecordQuery.java index 2fbdface8..686a991ce 100644 --- a/daxpay-single/daxpay-single-service/src/main/java/cn/daxpay/single/service/param/record/PayCloseRecordQuery.java +++ b/daxpay-single/daxpay-single-service/src/main/java/cn/daxpay/single/service/param/record/PayCloseRecordQuery.java @@ -38,7 +38,7 @@ public class PayCloseRecordQuery extends QueryOrder { * 是否关闭成功 */ @DbColumn(comment = "是否关闭成功") - private boolean closed; + private Boolean closed; /** 错误码 */ @DbColumn(comment = "错误码")