From 518e89dc4b192cbfd965ec1b4fb204b3a8f3a796 Mon Sep 17 00:00:00 2001 From: YunaiV Date: Sat, 15 Jul 2023 10:35:41 +0800 Subject: [PATCH 01/10] =?UTF-8?q?mall=20+=20pay=EF=BC=9A=201.=20=E9=87=8D?= =?UTF-8?q?=E6=9E=84=E6=94=AF=E4=BB=98=E5=9B=9E=E8=B0=83=E7=9A=84=E9=80=BB?= =?UTF-8?q?=E8=BE=91=EF=BC=8C=E5=B0=86=E5=9B=9E=E8=B0=83=E8=A7=A3=E6=9E=90?= =?UTF-8?q?=E6=94=B9=E6=88=90=20PayOrderRespDTO=EF=BC=8C=E4=B8=BA=E5=90=8E?= =?UTF-8?q?=E7=BB=AD=E8=BD=AE=E8=AF=A2=E5=81=9A=E9=93=BA=E5=9E=AB=202.=20?= =?UTF-8?q?=E8=B0=83=E6=95=B4=E9=80=80=E6=AC=BE=E5=8D=95=E7=9A=84=E8=A1=A8?= =?UTF-8?q?=E7=BB=93=E6=9E=84=203.=20=E8=B0=83=E6=95=B4=E9=80=80=E6=AC=BE?= =?UTF-8?q?=E8=B0=83=E7=94=A8=E7=9A=84=E5=AE=9E=E7=8E=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../util/validation/ValidationUtils.java | 8 ++ .../framework/pay/core/client/PayClient.java | 22 ++-- .../client/dto/notify/PayNotifyReqDTO.java | 29 ----- .../dto/notify/PayOrderNotifyRespDTO.java | 38 ------- .../dto/notify/PayRefundNotifyRespDTO.java | 58 ---------- .../client/dto/order/PayOrderRespDTO.java | 55 +++++++++ .../dto/order/PayOrderUnifiedReqDTO.java | 10 +- .../dto/order/PayOrderUnifiedRespDTO.java | 6 +- .../client/dto/refund/PayRefundRespDTO.java | 40 +++++++ .../dto/refund/PayRefundUnifiedReqDTO.java | 38 +++---- .../dto/refund/PayRefundUnifiedRespDTO.java | 24 ---- .../core/client/impl/AbstractPayClient.java | 8 +- .../impl/alipay/AbstractAlipayPayClient.java | 104 ++++++++++-------- .../impl/alipay/AlipayAppPayClient.java | 4 +- .../impl/alipay/AlipayBarPayClient.java | 4 +- .../client/impl/alipay/AlipayPcPayClient.java | 4 +- .../client/impl/alipay/AlipayQrPayClient.java | 4 +- .../impl/alipay/AlipayWapPayClient.java | 4 +- .../impl/weixin/AbstractWxPayClient.java | 35 +++--- .../client/impl/weixin/WxAppPayClient.java | 4 +- .../client/impl/weixin/WxBarPayClient.java | 19 ++-- .../client/impl/weixin/WxH5PayClient.java | 4 +- .../client/impl/weixin/WxNativePayClient.java | 14 +-- .../client/impl/weixin/WxPubPayClient.java | 12 +- .../enums/order/PayOrderStatusRespEnum.java | 14 ++- .../refund/PayNotifyRefundStatusEnum.java | 23 ---- .../core/enums/refund/PayRefundRespEnum.java | 23 ---- .../enums/refund/PayRefundStatusRespEnum.java | 28 +++++ .../PayClientFactoryImplIntegrationTest.java | 4 +- .../impl/alipay/AlipayQrPayClientTest.java | 6 +- .../pay/enums/refund/PayRefundStatusEnum.java | 12 +- .../admin/notify/PayNotifyController.java | 16 ++- .../admin/refund/vo/PayRefundExcelVO.java | 3 - .../pay/convert/refund/PayRefundConvert.java | 7 +- .../pay/dal/dataobject/order/PayOrderDO.java | 3 +- .../dataobject/order/PayOrderExtensionDO.java | 11 +- .../dal/dataobject/refund/PayRefundDO.java | 81 ++++++-------- .../pay/dal/mysql/refund/PayRefundMapper.java | 4 +- .../channel/PayChannelServiceImpl.java | 23 ++-- .../pay/service/order/PayOrderService.java | 6 +- .../service/order/PayOrderServiceImpl.java | 45 ++++---- .../pay/service/refund/PayRefundService.java | 8 +- .../service/refund/PayRefundServiceImpl.java | 52 ++++----- .../pay/service/app/PayAppServiceTest.java | 4 - .../service/refund/PayRefundServiceTest.java | 4 +- yudao-ui-admin/src/api/pay/channel.js | 10 +- 46 files changed, 436 insertions(+), 499 deletions(-) delete mode 100644 yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/dto/notify/PayNotifyReqDTO.java delete mode 100644 yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/dto/notify/PayOrderNotifyRespDTO.java delete mode 100644 yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/dto/notify/PayRefundNotifyRespDTO.java create mode 100644 yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/dto/order/PayOrderRespDTO.java create mode 100644 yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/dto/refund/PayRefundRespDTO.java delete mode 100644 yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/dto/refund/PayRefundUnifiedRespDTO.java delete mode 100644 yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/enums/refund/PayNotifyRefundStatusEnum.java delete mode 100644 yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/enums/refund/PayRefundRespEnum.java create mode 100644 yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/enums/refund/PayRefundStatusRespEnum.java diff --git a/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/validation/ValidationUtils.java b/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/validation/ValidationUtils.java index 7e6f89d5a..436a9e669 100644 --- a/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/validation/ValidationUtils.java +++ b/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/validation/ValidationUtils.java @@ -1,10 +1,12 @@ package cn.iocoder.yudao.framework.common.util.validation; import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.lang.Assert; import org.springframework.util.StringUtils; import javax.validation.ConstraintViolation; import javax.validation.ConstraintViolationException; +import javax.validation.Validation; import javax.validation.Validator; import java.util.Set; import java.util.regex.Pattern; @@ -37,6 +39,12 @@ public class ValidationUtils { && PATTERN_XML_NCNAME.matcher(str).matches(); } + public static void validate(Object object, Class... groups) { + Validator validator = Validation.buildDefaultValidatorFactory().getValidator(); + Assert.notNull(validator); + validate(validator, object, groups); + } + public static void validate(Validator validator, Object object, Class... groups) { Set> constraintViolations = validator.validate(object, groups); if (CollUtil.isNotEmpty(constraintViolations)) { diff --git a/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/PayClient.java b/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/PayClient.java index 6db0a0040..0d0139000 100644 --- a/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/PayClient.java +++ b/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/PayClient.java @@ -1,12 +1,12 @@ package cn.iocoder.yudao.framework.pay.core.client; -import cn.iocoder.yudao.framework.pay.core.client.dto.notify.PayNotifyReqDTO; -import cn.iocoder.yudao.framework.pay.core.client.dto.notify.PayOrderNotifyRespDTO; -import cn.iocoder.yudao.framework.pay.core.client.dto.notify.PayRefundNotifyRespDTO; +import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderRespDTO; import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderUnifiedReqDTO; import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderUnifiedRespDTO; +import cn.iocoder.yudao.framework.pay.core.client.dto.refund.PayRefundRespDTO; import cn.iocoder.yudao.framework.pay.core.client.dto.refund.PayRefundUnifiedReqDTO; -import cn.iocoder.yudao.framework.pay.core.client.dto.refund.PayRefundUnifiedRespDTO; + +import java.util.Map; /** * 支付客户端,用于对接各支付渠道的 SDK,实现发起支付、退款等功能 @@ -32,20 +32,22 @@ public interface PayClient { /** * 调用支付渠道,进行退款 + * * @param reqDTO 统一退款请求信息 - * @return 各支付渠道的统一返回结果 + * @return 退款信息 */ - PayRefundUnifiedRespDTO unifiedRefund(PayRefundUnifiedReqDTO reqDTO); + PayRefundRespDTO unifiedRefund(PayRefundUnifiedReqDTO reqDTO); /** * 解析回调数据 * - * @param rawNotify 通知内容 + * @param params HTTP 回调接口 content type 为 application/x-www-form-urlencoded 的所有参数 + * @param body HTTP 回调接口的 request body * @return 回调对象 - * 1. {@link PayRefundNotifyRespDTO} 退款通知 - * 2. {@link PayOrderNotifyRespDTO} 支付通知 + * 1. {@link PayRefundRespDTO} 退款通知 + * 2. {@link PayOrderRespDTO} 支付通知 */ - default Object parseNotify(PayNotifyReqDTO rawNotify) { + default Object parseNotify(Map params, String body) { throw new UnsupportedOperationException("未实现 parseNotify 方法!"); } diff --git a/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/dto/notify/PayNotifyReqDTO.java b/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/dto/notify/PayNotifyReqDTO.java deleted file mode 100644 index 65d0d01ad..000000000 --- a/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/dto/notify/PayNotifyReqDTO.java +++ /dev/null @@ -1,29 +0,0 @@ -package cn.iocoder.yudao.framework.pay.core.client.dto.notify; - -import lombok.Builder; -import lombok.Data; -import lombok.ToString; - -import java.util.Map; - - -/** - * 支付订单,退款订单回调,渠道的统一通知请求数据 - */ -@Data -@ToString -@Builder -public class PayNotifyReqDTO { - - - /** - * HTTP 回调接口的 request body - */ - private String body; - - /** - * HTTP 回调接口 content type 为 application/x-www-form-urlencoded 的所有参数 - */ - private Map params; - -} diff --git a/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/dto/notify/PayOrderNotifyRespDTO.java b/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/dto/notify/PayOrderNotifyRespDTO.java deleted file mode 100644 index 239ddda23..000000000 --- a/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/dto/notify/PayOrderNotifyRespDTO.java +++ /dev/null @@ -1,38 +0,0 @@ -package cn.iocoder.yudao.framework.pay.core.client.dto.notify; - -import lombok.AllArgsConstructor; -import lombok.Builder; -import lombok.Data; -import lombok.NoArgsConstructor; - -import java.time.LocalDateTime; - -/** - * 支付通知 Response DTO - * - * @author 芋道源码 - */ -@Data -@Builder -@NoArgsConstructor -@AllArgsConstructor -public class PayOrderNotifyRespDTO { - - /** - * 支付订单号(支付模块的) - */ - private String orderExtensionNo; - /** - * 支付渠道编号 - */ - private String channelOrderNo; - /** - * 支付渠道用户编号 - */ - private String channelUserId; - /** - * 支付成功时间 - */ - private LocalDateTime successTime; - -} diff --git a/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/dto/notify/PayRefundNotifyRespDTO.java b/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/dto/notify/PayRefundNotifyRespDTO.java deleted file mode 100644 index 9c05b3ac0..000000000 --- a/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/dto/notify/PayRefundNotifyRespDTO.java +++ /dev/null @@ -1,58 +0,0 @@ -package cn.iocoder.yudao.framework.pay.core.client.dto.notify; - -import cn.iocoder.yudao.framework.pay.core.enums.refund.PayNotifyRefundStatusEnum; -import lombok.Builder; -import lombok.Data; -import lombok.ToString; - -import java.time.LocalDateTime; - -/** - * 从渠道返回数据中解析得到的支付退款通知的Notify DTO - * - * @author jason - */ -@Data -@ToString -@Builder -public class PayRefundNotifyRespDTO { - - /** - * 支付渠道编号 - */ - private String channelOrderNo; - - /** - * 交易订单号,根据规则生成 - * 调用支付渠道时,使用该字段作为对接的订单号。 - * 1. 调用微信支付 https://api.mch.weixin.qq.com/pay/unifiedorder 时,使用该字段作为 out_trade_no - * 2. 调用支付宝 https://opendocs.alipay.com/apis 时,使用该字段作为 out_trade_no - * 这里对应 pay_extension 里面的 no - * 例如说,P202110132239124200055 - */ - private String tradeNo; - - /** - * https://api.mch.weixin.qq.com/v3/refund/domestic/refunds 中的 out_refund_no - * https://opendocs.alipay.com/apis alipay.trade.refund 中的 out_request_no - * 退款请求号。 - * 标识一次退款请求,需要保证在交易号下唯一,如需部分退款,则此参数必传。 - * 注:针对同一次退款请求,如果调用接口失败或异常了,重试时需要保证退款请求号不能变更, - * 防止该笔交易重复退款。支付宝会保证同样的退款请求号多次请求只会退一次。 - * 退款单请求号,根据规则生成 - * - * 例如说,RR202109181134287570000 - */ - private String reqNo; - - /** - * 退款是否成功 - */ - private PayNotifyRefundStatusEnum status; - - /** - * 退款成功时间 - */ - private LocalDateTime refundSuccessTime; - -} diff --git a/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/dto/order/PayOrderRespDTO.java b/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/dto/order/PayOrderRespDTO.java new file mode 100644 index 000000000..3f98cf841 --- /dev/null +++ b/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/dto/order/PayOrderRespDTO.java @@ -0,0 +1,55 @@ +package cn.iocoder.yudao.framework.pay.core.client.dto.order; + +import cn.iocoder.yudao.framework.pay.core.enums.order.PayOrderStatusRespEnum; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.time.LocalDateTime; + +/** + * 渠道支付订单 Response DTO + * + * @author 芋道源码 + */ +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class PayOrderRespDTO { + + /** + * 支付状态 + * + * 枚举:{@link PayOrderStatusRespEnum} + */ + private Integer status; + + /** + * 外部订单号 + * + * 对应 PayOrderExtensionDO 的 no 字段 + */ + private String outTradeNo; + + /** + * 支付渠道编号 + */ + private String channelOrderNo; + /** + * 支付渠道用户编号 + */ + private String channelUserId; + + /** + * 支付成功时间 + */ + private LocalDateTime successTime; + + /** + * 原始的异步通知结果 + */ + private Object rawData; + +} diff --git a/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/dto/order/PayOrderUnifiedReqDTO.java b/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/dto/order/PayOrderUnifiedReqDTO.java index ba31b08ad..bd92df13a 100644 --- a/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/dto/order/PayOrderUnifiedReqDTO.java +++ b/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/dto/order/PayOrderUnifiedReqDTO.java @@ -28,10 +28,12 @@ public class PayOrderUnifiedReqDTO { // ========== 商户相关字段 ========== /** - * 商户订单编号 + * 外部订单号 + * + * 对应 PayOrderExtensionDO 的 no 字段 */ - @NotEmpty(message = "商户订单编号不能为空") - private String merchantOrderId; + @NotEmpty(message = "外部订单编号不能为空") + private String outTradeNo; /** * 商品标题 */ @@ -63,7 +65,7 @@ public class PayOrderUnifiedReqDTO { */ @NotNull(message = "支付金额不能为空") @DecimalMin(value = "0", inclusive = false, message = "支付金额必须大于零") - private Integer amount; + private Integer price; /** * 支付过期时间 diff --git a/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/dto/order/PayOrderUnifiedRespDTO.java b/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/dto/order/PayOrderUnifiedRespDTO.java index 441b4ece1..7a282b4eb 100644 --- a/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/dto/order/PayOrderUnifiedRespDTO.java +++ b/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/dto/order/PayOrderUnifiedRespDTO.java @@ -1,6 +1,5 @@ package cn.iocoder.yudao.framework.pay.core.client.dto.order; -import cn.iocoder.yudao.framework.pay.core.client.dto.notify.PayOrderNotifyRespDTO; import cn.iocoder.yudao.framework.pay.core.enums.order.PayOrderDisplayModeEnum; import lombok.Data; @@ -24,11 +23,12 @@ public class PayOrderUnifiedRespDTO { private String displayContent; /** - * 同步的通知信息 + * 渠道支付订单 * + * 只有在订单直接支付成功时,才会进行返回。 * 目前只有 bar 条码支付才会出现,它是支付发起时,直接返回是否支付成功的,而其它支付还是异步通知 */ - private PayOrderNotifyRespDTO notify; + private PayOrderRespDTO order; public PayOrderUnifiedRespDTO(String displayMode, String displayContent) { this.displayMode = displayMode; diff --git a/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/dto/refund/PayRefundRespDTO.java b/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/dto/refund/PayRefundRespDTO.java new file mode 100644 index 000000000..5268eddc4 --- /dev/null +++ b/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/dto/refund/PayRefundRespDTO.java @@ -0,0 +1,40 @@ +package cn.iocoder.yudao.framework.pay.core.client.dto.refund; + +import cn.iocoder.yudao.framework.pay.core.enums.refund.PayRefundStatusRespEnum; +import lombok.Data; + +import java.time.LocalDateTime; + +/** + * 渠道退款订单 Response DTO + * + * @author jason + */ +@Data +public class PayRefundRespDTO { + + /** + * 退款状态 + * + * 枚举 {@link PayRefundStatusRespEnum} + */ + private Integer status; + + /** + * 渠道退款单号 + * + * 对应 PayRefundDO.channelRefundNo 字段 + */ + private String channelRefundNo; + + /** + * 退款成功时间 + */ + private LocalDateTime successTime; + + /** + * 原始的异步通知结果 + */ + private Object rawData; + +} diff --git a/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/dto/refund/PayRefundUnifiedReqDTO.java b/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/dto/refund/PayRefundUnifiedReqDTO.java index e19a04aa0..d6f66ab63 100644 --- a/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/dto/refund/PayRefundUnifiedReqDTO.java +++ b/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/dto/refund/PayRefundUnifiedReqDTO.java @@ -24,33 +24,20 @@ import javax.validation.constraints.NotNull; public class PayRefundUnifiedReqDTO { /** - * 用户 IP + * 外部订单号 + * + * 对应 PayOrderExtensionDO 的 no 字段 */ - private String userIp; - - // TODO @jason:这个是否为非必传字段呀,只需要传递 payTradeNo 字段即可。尽可能精简 - /** - * https://api.mch.weixin.qq.com/v3/refund/domestic/refunds 中的 transaction_id - * https://opendocs.alipay.com/apis alipay.trade.refund 中的 trade_no - * 渠道订单号 - */ - private String channelOrderNo; + @NotEmpty(message = "外部订单编号不能为空") + private String outTradeNo; /** - * https://api.mch.weixin.qq.com/v3/refund/domestic/refunds 中的 out_trade_no - * https://opendocs.alipay.com/apis alipay.trade.refund 中的 out_trade_no - * 支付交易号 {PayOrderExtensionDO no字段} 和 渠道订单号 不能同时为空 + * 外部退款号 + * + * 对应 PayRefundDO 的 no 字段 */ - private String payTradeNo; - - /** - * https://api.mch.weixin.qq.com/v3/refund/domestic/refunds 中的 out_refund_no - * https://opendocs.alipay.com/apis alipay.trade.refund 中的 out_trade_no - * 退款请求单号 同一退款请求单号多次请求只退一笔。 - * 使用 商户的退款单号。{PayRefundDO 字段 merchantRefundNo} - */ - @NotEmpty(message = "退款请求单号") - private String merchantRefundId; + @NotEmpty(message = "退款请求单号不能为空") + private String outRefundNo; /** * 退款原因 @@ -63,11 +50,12 @@ public class PayRefundUnifiedReqDTO { */ @NotNull(message = "退款金额不能为空") @DecimalMin(value = "0", inclusive = false, message = "支付金额必须大于零") - private Integer amount; + private Integer price; /** - * 退款结果 notify 回调地址, 支付宝退款不需要回调地址, 微信需要 + * 退款结果的 notify 回调地址 */ + @NotEmpty(message = "支付结果的回调地址不能为空") @URL(message = "支付结果的 notify 回调地址必须是 URL 格式") private String notifyUrl; diff --git a/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/dto/refund/PayRefundUnifiedRespDTO.java b/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/dto/refund/PayRefundUnifiedRespDTO.java deleted file mode 100644 index 104dc2bbe..000000000 --- a/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/dto/refund/PayRefundUnifiedRespDTO.java +++ /dev/null @@ -1,24 +0,0 @@ -package cn.iocoder.yudao.framework.pay.core.client.dto.refund; - -import lombok.AllArgsConstructor; -import lombok.Builder; -import lombok.Data; -import lombok.NoArgsConstructor; -import lombok.experimental.Accessors; -/** - * 统一退款 Response DTO - * - * @author jason - */ -@Accessors(chain = true) -@Builder -@NoArgsConstructor -@AllArgsConstructor -@Data -public class PayRefundUnifiedRespDTO { - - /** - * 渠道退款单编号 - */ - private String channelRefundId; -} diff --git a/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/AbstractPayClient.java b/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/AbstractPayClient.java index 0225739ca..f25bb9881 100644 --- a/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/AbstractPayClient.java +++ b/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/AbstractPayClient.java @@ -5,8 +5,8 @@ import cn.iocoder.yudao.framework.pay.core.client.PayClient; import cn.iocoder.yudao.framework.pay.core.client.PayClientConfig; import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderUnifiedReqDTO; import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderUnifiedRespDTO; +import cn.iocoder.yudao.framework.pay.core.client.dto.refund.PayRefundRespDTO; import cn.iocoder.yudao.framework.pay.core.client.dto.refund.PayRefundUnifiedReqDTO; -import cn.iocoder.yudao.framework.pay.core.client.dto.refund.PayRefundUnifiedRespDTO; import cn.iocoder.yudao.framework.pay.core.client.exception.PayException; import lombok.extern.slf4j.Slf4j; @@ -92,10 +92,10 @@ public abstract class AbstractPayClient implemen throws Throwable; @Override - public PayRefundUnifiedRespDTO unifiedRefund(PayRefundUnifiedReqDTO reqDTO) { + public PayRefundRespDTO unifiedRefund(PayRefundUnifiedReqDTO reqDTO) { Validation.buildDefaultValidatorFactory().getValidator().validate(reqDTO); // 执行统一退款 - PayRefundUnifiedRespDTO resp; + PayRefundRespDTO resp; try { resp = doUnifiedRefund(reqDTO); } catch (ServiceException ex) { @@ -109,7 +109,7 @@ public abstract class AbstractPayClient implemen return resp; } - protected abstract PayRefundUnifiedRespDTO doUnifiedRefund(PayRefundUnifiedReqDTO reqDTO) throws Throwable; + protected abstract PayRefundRespDTO doUnifiedRefund(PayRefundUnifiedReqDTO reqDTO) throws Throwable; // ========== 各种工具方法 ========== diff --git a/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/alipay/AbstractAlipayPayClient.java b/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/alipay/AbstractAlipayPayClient.java index 60472fefb..1da5f2631 100644 --- a/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/alipay/AbstractAlipayPayClient.java +++ b/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/alipay/AbstractAlipayPayClient.java @@ -2,16 +2,19 @@ package cn.iocoder.yudao.framework.pay.core.client.impl.alipay; import cn.hutool.core.bean.BeanUtil; import cn.hutool.core.date.LocalDateTimeUtil; +import cn.hutool.core.lang.Assert; +import cn.hutool.core.util.StrUtil; import cn.hutool.http.HttpUtil; import cn.iocoder.yudao.framework.common.util.json.JsonUtils; -import cn.iocoder.yudao.framework.pay.core.client.dto.notify.PayNotifyReqDTO; -import cn.iocoder.yudao.framework.pay.core.client.dto.notify.PayOrderNotifyRespDTO; -import cn.iocoder.yudao.framework.pay.core.client.dto.notify.PayRefundNotifyRespDTO; +import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderRespDTO; +import cn.iocoder.yudao.framework.pay.core.client.dto.refund.PayRefundRespDTO; import cn.iocoder.yudao.framework.pay.core.client.dto.refund.PayRefundUnifiedReqDTO; -import cn.iocoder.yudao.framework.pay.core.client.dto.refund.PayRefundUnifiedRespDTO; import cn.iocoder.yudao.framework.pay.core.client.impl.AbstractPayClient; -import cn.iocoder.yudao.framework.pay.core.enums.refund.PayNotifyRefundStatusEnum; -import com.alipay.api.*; +import cn.iocoder.yudao.framework.pay.core.enums.order.PayOrderStatusRespEnum; +import com.alipay.api.AlipayApiException; +import com.alipay.api.AlipayConfig; +import com.alipay.api.AlipayResponse; +import com.alipay.api.DefaultAlipayClient; import com.alipay.api.domain.AlipayTradeRefundModel; import com.alipay.api.internal.util.AlipaySignature; import com.alipay.api.request.AlipayTradeRefundRequest; @@ -22,6 +25,8 @@ import lombok.extern.slf4j.Slf4j; import java.nio.charset.StandardCharsets; import java.time.LocalDateTime; import java.util.Map; +import java.util.Objects; +import java.util.function.Supplier; import static cn.hutool.core.date.DatePattern.NORM_DATETIME_FORMATTER; import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception0; @@ -52,69 +57,72 @@ public abstract class AbstractAlipayPayClient extends AbstractPayClient params, String body) { + // 补充说明:支付宝退款时,没有回调,这点和微信支付是不同的。并且,退款分成部分退款、和全部退款。 + // ① 部分退款:是会有回调,但是它回调的是订单状态的同步回调,不是退款订单的回调 + // ② 全部退款:Wap 支付有订单状态的同步回调,但是 PC/扫码又没有 + // 所以,这里在解析时,即使是退款导致的订单状态同步,我们也忽略不做为“退款同步”,而是订单的回调。 + // 实际上,支付宝退款只要发起成功,就可以认为退款成功,不需要等待回调。 + // 1. 校验回调数据 - String body = rawNotify.getBody(); - Map params = rawNotify.getParams(); Map bodyObj = HttpUtil.decodeParamMap(body, StandardCharsets.UTF_8); AlipaySignature.rsaCheckV1(bodyObj, config.getAlipayPublicKey(), - StandardCharsets.UTF_8.name(), "RSA2"); + StandardCharsets.UTF_8.name(), config.getSignType()); - // 2.1 退款的情况 - if (bodyObj.containsKey("refund_fee")) { - return PayRefundNotifyRespDTO.builder().channelOrderNo(bodyObj.get("trade_no")) - .tradeNo(bodyObj.get("out_trade_no")).reqNo(bodyObj.get("out_biz_no")) - .status(PayNotifyRefundStatusEnum.SUCCESS) - .refundSuccessTime(parseTime(params.get("gmt_refund"))) - .build(); - } - // 2.2 支付的情况 - return PayOrderNotifyRespDTO.builder() - .orderExtensionNo(bodyObj.get("out_trade_no")) + // 2. 解析订单的状态 + String tradeStatus = bodyObj.get("trade_status"); + PayOrderStatusRespEnum status = Objects.equals("WAIT_BUYER_PAY", tradeStatus) ? PayOrderStatusRespEnum.WAITING + : Objects.equals("TRADE_SUCCESS", tradeStatus) ? PayOrderStatusRespEnum.SUCCESS + : Objects.equals("TRADE_CLOSED", tradeStatus) ? PayOrderStatusRespEnum.CLOSED : null; + Assert.notNull(status, (Supplier) () -> { + throw new IllegalArgumentException(StrUtil.format("body({}) 的 trade_status 不正确", body)); + }); + return PayOrderRespDTO.builder() + .status(Objects.requireNonNull(status).getStatus()) + .outTradeNo(bodyObj.get("out_trade_no")) .channelOrderNo(bodyObj.get("trade_no")) .channelUserId(bodyObj.get("seller_id")) - .successTime(parseTime(params.get("notify_time"))) + .successTime(parseTime(params.get("gmt_payment"))) + .rawData(body) .build(); } diff --git a/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/alipay/AlipayAppPayClient.java b/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/alipay/AlipayAppPayClient.java index 19fd8b072..9ea80fa9b 100644 --- a/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/alipay/AlipayAppPayClient.java +++ b/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/alipay/AlipayAppPayClient.java @@ -31,10 +31,10 @@ public class AlipayAppPayClient extends AbstractAlipayPayClient { // 1.1 构建 AlipayTradeAppPayModel 请求 AlipayTradeAppPayModel model = new AlipayTradeAppPayModel(); // ① 通用的参数 - model.setOutTradeNo(reqDTO.getMerchantOrderId()); + model.setOutTradeNo(reqDTO.getOutTradeNo()); model.setSubject(reqDTO.getSubject()); model.setBody(reqDTO.getBody()); - model.setTotalAmount(formatAmount(reqDTO.getAmount())); + model.setTotalAmount(formatAmount(reqDTO.getPrice())); model.setProductCode(" QUICK_MSECURITY_PAY"); // 销售产品码:无线快捷支付产品 // ② 个性化的参数【无】 // ③ 支付宝扫码支付只有一种展示 diff --git a/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/alipay/AlipayBarPayClient.java b/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/alipay/AlipayBarPayClient.java index 42579fb8e..1f94f2a56 100644 --- a/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/alipay/AlipayBarPayClient.java +++ b/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/alipay/AlipayBarPayClient.java @@ -39,10 +39,10 @@ public class AlipayBarPayClient extends AbstractAlipayPayClient { // 1.1 构建 AlipayTradePayModel 请求 AlipayTradePayModel model = new AlipayTradePayModel(); // ① 通用的参数 - model.setOutTradeNo(reqDTO.getMerchantOrderId()); + model.setOutTradeNo(reqDTO.getOutTradeNo()); model.setSubject(reqDTO.getSubject()); model.setBody(reqDTO.getBody()); - model.setTotalAmount(formatAmount(reqDTO.getAmount())); + model.setTotalAmount(formatAmount(reqDTO.getPrice())); model.setScene("bar_code"); // 当面付条码支付场景 // ② 个性化的参数 model.setAuthCode(authCode); diff --git a/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/alipay/AlipayPcPayClient.java b/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/alipay/AlipayPcPayClient.java index d1a7eef14..02ae7c98c 100644 --- a/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/alipay/AlipayPcPayClient.java +++ b/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/alipay/AlipayPcPayClient.java @@ -33,10 +33,10 @@ public class AlipayPcPayClient extends AbstractAlipayPayClient { // 1.1 构建 AlipayTradePagePayModel 请求 AlipayTradePagePayModel model = new AlipayTradePagePayModel(); // ① 通用的参数 - model.setOutTradeNo(reqDTO.getMerchantOrderId()); + model.setOutTradeNo(reqDTO.getOutTradeNo()); model.setSubject(reqDTO.getSubject()); model.setBody(reqDTO.getBody()); - model.setTotalAmount(formatAmount(reqDTO.getAmount())); + model.setTotalAmount(formatAmount(reqDTO.getPrice())); model.setTimeExpire(formatTime(reqDTO.getExpireTime())); model.setProductCode("FAST_INSTANT_TRADE_PAY"); // 销售产品码. 目前 PC 支付场景下仅支持 FAST_INSTANT_TRADE_PAY // ② 个性化的参数 diff --git a/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/alipay/AlipayQrPayClient.java b/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/alipay/AlipayQrPayClient.java index 27b2ba5c4..f2deee58a 100644 --- a/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/alipay/AlipayQrPayClient.java +++ b/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/alipay/AlipayQrPayClient.java @@ -29,10 +29,10 @@ public class AlipayQrPayClient extends AbstractAlipayPayClient { // 1.1 构建 AlipayTradePrecreateModel 请求 AlipayTradePrecreateModel model = new AlipayTradePrecreateModel(); // ① 通用的参数 - model.setOutTradeNo(reqDTO.getMerchantOrderId()); + model.setOutTradeNo(reqDTO.getOutTradeNo()); model.setSubject(reqDTO.getSubject()); model.setBody(reqDTO.getBody()); - model.setTotalAmount(formatAmount(reqDTO.getAmount())); + model.setTotalAmount(formatAmount(reqDTO.getPrice())); model.setProductCode("FACE_TO_FACE_PAYMENT"); // 销售产品码. 目前扫码支付场景下仅支持 FACE_TO_FACE_PAYMENT // ② 个性化的参数【无】 // ③ 支付宝扫码支付只有一种展示,考虑到前端可能希望二维码扫描后,手机打开 diff --git a/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/alipay/AlipayWapPayClient.java b/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/alipay/AlipayWapPayClient.java index 1db837b69..d3acde46a 100644 --- a/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/alipay/AlipayWapPayClient.java +++ b/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/alipay/AlipayWapPayClient.java @@ -30,10 +30,10 @@ public class AlipayWapPayClient extends AbstractAlipayPayClient { // 1.1 构建 AlipayTradeWapPayModel 请求 AlipayTradeWapPayModel model = new AlipayTradeWapPayModel(); // ① 通用的参数 - model.setOutTradeNo(reqDTO.getMerchantOrderId()); + model.setOutTradeNo(reqDTO.getOutTradeNo()); model.setSubject(reqDTO.getSubject()); model.setBody(reqDTO.getBody()); - model.setTotalAmount(formatAmount(reqDTO.getAmount())); + model.setTotalAmount(formatAmount(reqDTO.getPrice())); model.setProductCode("QUICK_WAP_PAY"); // 销售产品码. 目前 Wap 支付场景下仅支持 QUICK_WAP_PAY // ② 个性化的参数【无】 // ③ 支付宝 Wap 支付只有一种展示:URL diff --git a/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/weixin/AbstractWxPayClient.java b/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/weixin/AbstractWxPayClient.java index fa5a793d5..49314f135 100644 --- a/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/weixin/AbstractWxPayClient.java +++ b/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/weixin/AbstractWxPayClient.java @@ -6,8 +6,7 @@ import cn.hutool.core.date.TemporalAccessorUtil; import cn.hutool.core.lang.Assert; import cn.hutool.core.util.StrUtil; import cn.iocoder.yudao.framework.common.util.io.FileUtils; -import cn.iocoder.yudao.framework.pay.core.client.dto.notify.PayNotifyReqDTO; -import cn.iocoder.yudao.framework.pay.core.client.dto.notify.PayOrderNotifyRespDTO; +import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderRespDTO; import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderUnifiedReqDTO; import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderUnifiedRespDTO; import cn.iocoder.yudao.framework.pay.core.client.impl.AbstractPayClient; @@ -22,11 +21,13 @@ import lombok.extern.slf4j.Slf4j; import java.time.LocalDateTime; import java.time.ZoneId; +import java.util.Map; import java.util.Objects; import static cn.hutool.core.date.DatePattern.PURE_DATETIME_PATTERN; import static cn.hutool.core.date.DatePattern.UTC_WITH_XXX_OFFSET_PATTERN; -import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.*; +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.invalidParamException; import static cn.iocoder.yudao.framework.common.util.json.JsonUtils.toJsonString; /** @@ -103,47 +104,47 @@ public abstract class AbstractWxPayClient extends AbstractPayClient params, String body) { + log.info("[parseNotify][微信支付回调 data 数据: {}]", body); try { // 微信支付 v2 回调结果处理 switch (config.getApiVersion()) { case WxPayClientConfig.API_VERSION_V2: - return parseOrderNotifyV2(rawNotify); + return parseOrderNotifyV2(body); case WxPayClientConfig.API_VERSION_V3: - return parseOrderNotifyV3(rawNotify); + return parseOrderNotifyV3(body); default: throw new IllegalArgumentException(String.format("未知的 API 版本(%s)", config.getApiVersion())); } } catch (WxPayException e) { - log.error("[parseNotify][rawNotify({}) 解析失败]", toJsonString(rawNotify), e); + log.error("[parseNotify][params({}) body({}) 解析失败]", params, body, e); // throw buildPayException(e); throw new RuntimeException(e); // TODO 芋艿:缺一个异常翻译 } } - private PayOrderNotifyRespDTO parseOrderNotifyV2(PayNotifyReqDTO data) throws WxPayException { - WxPayOrderNotifyResult notifyResult = client.parseOrderNotifyResult(data.getBody()); + private PayOrderRespDTO parseOrderNotifyV2(String body) throws WxPayException { + WxPayOrderNotifyResult notifyResult = client.parseOrderNotifyResult(body); Assert.isTrue(Objects.equals(notifyResult.getResultCode(), "SUCCESS"), "支付结果非 SUCCESS"); // 转换结果 - return PayOrderNotifyRespDTO + return PayOrderRespDTO .builder() - .orderExtensionNo(notifyResult.getOutTradeNo()) + .outTradeNo(notifyResult.getOutTradeNo()) .channelOrderNo(notifyResult.getTransactionId()) .channelUserId(notifyResult.getOpenid()) .successTime(parseDateV2(notifyResult.getTimeEnd())) .build(); } - private PayOrderNotifyRespDTO parseOrderNotifyV3(PayNotifyReqDTO data) throws WxPayException { - WxPayOrderNotifyV3Result notifyResult = client.parseOrderNotifyV3Result(data.getBody(), null); + private PayOrderRespDTO parseOrderNotifyV3(String body) throws WxPayException { + WxPayOrderNotifyV3Result notifyResult = client.parseOrderNotifyV3Result(body, null); WxPayOrderNotifyV3Result.DecryptNotifyResult result = notifyResult.getResult(); // 转换结果 Assert.isTrue(Objects.equals(notifyResult.getResult().getTradeState(), "SUCCESS"), "支付结果非 SUCCESS"); - return PayOrderNotifyRespDTO.builder() - .orderExtensionNo(result.getOutTradeNo()) + return PayOrderRespDTO.builder() + .outTradeNo(result.getOutTradeNo()) .channelOrderNo(result.getTradeState()) .channelUserId(result.getPayer() != null ? result.getPayer().getOpenid() : null) .successTime(parseDateV3(result.getSuccessTime())) @@ -175,7 +176,7 @@ public abstract class AbstractWxPayClient extends AbstractPayClientJSAPI 支付 的 out_trade_no 字段 + * 2. 支付宝支付:对应 电脑网站支付 的 out_trade_no 字段 * * 例如说,P202110132239124200055 */ @@ -70,7 +71,7 @@ public class PayOrderExtensionDO extends BaseDO { /** * 支付渠道的额外参数 * - * 参见 https://www.pingxx.com/api/支付渠道%20extra%20参数说明.html + * 参见 参数说明 */ @TableField(typeHandler = JacksonTypeHandler.class) private Map channelExtras; diff --git a/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/dal/dataobject/refund/PayRefundDO.java b/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/dal/dataobject/refund/PayRefundDO.java index 8330f0075..898a13699 100644 --- a/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/dal/dataobject/refund/PayRefundDO.java +++ b/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/dal/dataobject/refund/PayRefundDO.java @@ -5,6 +5,7 @@ import cn.iocoder.yudao.framework.pay.core.enums.channel.PayChannelEnum; import cn.iocoder.yudao.module.pay.dal.dataobject.app.PayAppDO; import cn.iocoder.yudao.module.pay.dal.dataobject.channel.PayChannelDO; import cn.iocoder.yudao.module.pay.dal.dataobject.order.PayOrderDO; +import cn.iocoder.yudao.module.pay.enums.notify.PayNotifyStatusEnum; import cn.iocoder.yudao.module.pay.enums.refund.PayRefundStatusEnum; import cn.iocoder.yudao.module.pay.enums.refund.PayRefundTypeEnum; import com.baomidou.mybatisplus.annotation.KeySequence; @@ -37,6 +38,14 @@ public class PayRefundDO extends BaseDO { */ @TableId private Long id; + /** + * 外部退款号,根据规则生成 + * + * 调用支付渠道时,使用该字段作为对接的退款号: + * 1. 微信退款:对应 申请退款 的 out_refund_no 字段 + * 2. 支付宝退款:对应 的 out_request_no 字段 + */ + private String no; /** * 应用编号 @@ -63,47 +72,27 @@ public class PayRefundDO extends BaseDO { */ private Long orderId; - /** - * 交易订单号,根据规则生成 - * 调用支付渠道时,使用该字段作为对接的订单号。 - * 1. 调用微信支付 https://api.mch.weixin.qq.com/v3/refund/domestic/refunds 时,使用该字段作为 out_trade_no - * 2. 调用支付宝 https://opendocs.alipay.com/apis 时,使用该字段作为 out_trade_no - * 这里对应 pay_extension 里面的 no - * 例如说,P202110132239124200055 - */ - private String tradeNo; - // ========== 商户相关字段 ========== /** * 商户订单编号 + * + * 例如说,内部系统 A 的订单号,需要保证每个 PayAppDO 唯一 */ private String merchantOrderId; - /** - * 商户退款订单号, 由商户系统产生, 由他们保证唯一,不能为空,通知商户时会传该字段。 - * 例如说,内部系统 A 的退款订单号。需要保证每个 PayMerchantDO 唯一 - * 个商户退款订单,对应一条退款请求记录。可多次提交。 渠道保持幂等 - * 使用商户退款单,作为退款请求号 - * https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter3_1_9.shtml 中的 out_refund_no - * https://opendocs.alipay.com/apis alipay.trade.refund 中的 out_request_no - * 退款请求号。 - * 标识一次退款请求,需要保证在交易号下唯一,如需部分退款,则此参数必传。 - * 注:针对同一次退款请求,如果调用接口失败或异常了,重试时需要保证退款请求号不能变更, - * 防止该笔交易重复退款。支付宝会保证同样的退款请求号多次请求只会退一次。 - * 退款单请求号,根据规则生成 - * 例如说,R202109181134287570000 + * 商户退款订单号 + * + * 例如说,内部系统 A 的订单号,需要保证每个 PayAppDO 唯一 */ - // TODO @jason:merchantRefundNo =》merchantRefundOId - private String merchantRefundNo; - + private String merchantRefundId; /** * 异步通知地址 */ private String notifyUrl; - /** * 通知商户退款结果的回调状态 - * TODO 0 未发送 1 已发送 + * + * 枚举 {@link PayNotifyStatusEnum} */ private Integer notifyStatus; @@ -142,22 +131,27 @@ public class PayRefundDO extends BaseDO { // ========== 渠道相关字段 ========== /** - * 渠道订单号,pay_order 中的channel_order_no 对应 + * 渠道订单号 + * + * 冗余 {@link PayOrderDO#getChannelOrderNo()} */ private String channelOrderNo; /** - * 微信中的 refund_id - * https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter3_1_9.shtml - * 支付宝没有 - * 渠道退款单号,渠道返回 + * 渠道退款单号 + * + * 1. 微信退款:对应 申请退款 的 refund_id 字段 + * 2. 支付宝退款:没有字段 */ private String channelRefundNo; + /** + * 退款成功时间 + */ + private LocalDateTime successTime; /** * 调用渠道的错误码 */ private String channelErrorCode; - /** * 调用渠道报错时,错误信息 */ @@ -165,22 +159,15 @@ public class PayRefundDO extends BaseDO { /** * 支付渠道的额外参数 - * 参见 https://www.pingxx.com/api/Refunds%20退款概述.html + * + * 参见 参数说明 */ private String channelExtras; - /** - * TODO - * 退款失效时间 + * 支付渠道异步通知的内容 + * + * 在退款成功后,会记录回调的数据 */ - private LocalDateTime expireTime; - /** - * 退款成功时间 - */ - private LocalDateTime successTime; - /** - * 退款通知时间 - */ - private LocalDateTime notifyTime; + private String channelNotifyData; } diff --git a/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/dal/mysql/refund/PayRefundMapper.java b/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/dal/mysql/refund/PayRefundMapper.java index 35a6bd5a7..42bd00b81 100755 --- a/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/dal/mysql/refund/PayRefundMapper.java +++ b/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/dal/mysql/refund/PayRefundMapper.java @@ -45,8 +45,10 @@ public interface PayRefundMapper extends BaseMapperX { return selectOne("req_no", reqNo); } + // TODO 芋艿:要重构 default PayRefundDO selectByTradeNoAndMerchantRefundNo(String tradeNo, String merchantRefundNo){ - return selectOne("trade_no", tradeNo, "merchant_refund_no", merchantRefundNo); +// return selectOne("trade_no", tradeNo, "merchant_refund_no", merchantRefundNo); + return null; } } diff --git a/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/channel/PayChannelServiceImpl.java b/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/channel/PayChannelServiceImpl.java index 69adddc4d..45604c04c 100644 --- a/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/channel/PayChannelServiceImpl.java +++ b/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/channel/PayChannelServiceImpl.java @@ -83,17 +83,20 @@ public class PayChannelServiceImpl implements PayChannelService { */ @Scheduled(initialDelay = 60, fixedRate = 60, timeUnit = TimeUnit.SECONDS) public void refreshLocalCache() { - // 情况一:如果缓存里没有数据,则直接刷新缓存 - if (CollUtil.isEmpty(channelCache)) { - initLocalCache(); - return; - } + // 注意:忽略自动多租户,因为要全局初始化缓存 + TenantUtils.executeIgnore(() -> { + // 情况一:如果缓存里没有数据,则直接刷新缓存 + if (CollUtil.isEmpty(channelCache)) { + initLocalCache(); + return; + } - // 情况二,如果缓存里数据,则通过 updateTime 判断是否有数据变更,有变更则刷新缓存 - LocalDateTime maxTime = CollectionUtils.getMaxValue(channelCache, PayChannelDO::getUpdateTime); - if (channelMapper.selectCountByUpdateTimeGt(maxTime) > 0) { - initLocalCache(); - } + // 情况二,如果缓存里数据,则通过 updateTime 判断是否有数据变更,有变更则刷新缓存 + LocalDateTime maxTime = CollectionUtils.getMaxValue(channelCache, PayChannelDO::getUpdateTime); + if (channelMapper.selectCountByUpdateTimeGt(maxTime) > 0) { + initLocalCache(); + } + }); } @Override diff --git a/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/order/PayOrderService.java b/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/order/PayOrderService.java index 890bacd62..5f4c6aa21 100755 --- a/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/order/PayOrderService.java +++ b/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/order/PayOrderService.java @@ -2,8 +2,7 @@ package cn.iocoder.yudao.module.pay.service.order; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils; -import cn.iocoder.yudao.framework.pay.core.client.dto.notify.PayNotifyReqDTO; -import cn.iocoder.yudao.framework.pay.core.client.dto.notify.PayOrderNotifyRespDTO; +import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderRespDTO; import cn.iocoder.yudao.module.pay.api.order.dto.PayOrderCreateReqDTO; import cn.iocoder.yudao.module.pay.controller.admin.order.vo.PayOrderExportReqVO; import cn.iocoder.yudao.module.pay.controller.admin.order.vo.PayOrderPageReqVO; @@ -103,8 +102,7 @@ public interface PayOrderService { * * @param channelId 渠道编号 * @param notify 通知 - * @param rawNotify 通知数据 */ - void notifyPayOrder(Long channelId, PayOrderNotifyRespDTO notify, PayNotifyReqDTO rawNotify); + void notifyPayOrder(Long channelId, PayOrderRespDTO notify); } diff --git a/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/order/PayOrderServiceImpl.java b/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/order/PayOrderServiceImpl.java index d27c52212..884b182df 100755 --- a/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/order/PayOrderServiceImpl.java +++ b/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/order/PayOrderServiceImpl.java @@ -9,10 +9,10 @@ import cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils; import cn.iocoder.yudao.framework.pay.config.PayProperties; import cn.iocoder.yudao.framework.pay.core.client.PayClient; import cn.iocoder.yudao.framework.pay.core.client.PayClientFactory; -import cn.iocoder.yudao.framework.pay.core.client.dto.notify.PayNotifyReqDTO; -import cn.iocoder.yudao.framework.pay.core.client.dto.notify.PayOrderNotifyRespDTO; +import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderRespDTO; import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderUnifiedReqDTO; import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderUnifiedRespDTO; +import cn.iocoder.yudao.framework.pay.core.enums.order.PayOrderStatusRespEnum; import cn.iocoder.yudao.framework.tenant.core.util.TenantUtils; import cn.iocoder.yudao.module.pay.api.order.dto.PayOrderCreateReqDTO; import cn.iocoder.yudao.module.pay.controller.admin.order.vo.PayOrderExportReqVO; @@ -148,17 +148,17 @@ public class PayOrderServiceImpl implements PayOrderService { // 3. 调用三方接口 PayOrderUnifiedReqDTO unifiedOrderReqDTO = PayOrderConvert.INSTANCE.convert2(reqVO, userIp) // 商户相关的字段 - .setMerchantOrderId(orderExtension.getNo()) // 注意,此处使用的是 PayOrderExtensionDO.no 属性! + .setOutTradeNo(orderExtension.getNo()) // 注意,此处使用的是 PayOrderExtensionDO.no 属性! .setSubject(order.getSubject()).setBody(order.getBody()) .setNotifyUrl(genChannelPayNotifyUrl(channel)) .setReturnUrl(reqVO.getReturnUrl()) // 订单相关字段 - .setAmount(order.getPrice()).setExpireTime(order.getExpireTime()); + .setPrice(order.getPrice()).setExpireTime(order.getExpireTime()); PayOrderUnifiedRespDTO unifiedOrderRespDTO = client.unifiedOrder(unifiedOrderReqDTO); // 4. 如果调用直接支付成功,则直接更新支付单状态为成功。例如说:付款码支付,免密支付时,就直接验证支付成功 - if (unifiedOrderRespDTO.getNotify() != null) { - notifyPayOrderSuccess(channel, unifiedOrderRespDTO.getNotify(), null); + if (unifiedOrderRespDTO.getOrder() != null) { + notifyPayOrder(channel, unifiedOrderRespDTO.getOrder()); // 此处需要读取最新的状态 order = orderMapper.selectById(order.getId()); } @@ -226,16 +226,26 @@ public class PayOrderServiceImpl implements PayOrderService { @Override @Transactional(rollbackFor = Exception.class) - public void notifyPayOrder(Long channelId, PayOrderNotifyRespDTO notify, PayNotifyReqDTO rawNotify) { + public void notifyPayOrder(Long channelId, PayOrderRespDTO notify) { // 校验支付渠道是否有效 PayChannelDO channel = channelService.validPayChannel(channelId); // 更新支付订单为已支付 - TenantUtils.execute(channel.getTenantId(), () -> notifyPayOrderSuccess(channel, notify, rawNotify)); + TenantUtils.execute(channel.getTenantId(), () -> notifyPayOrder(channel, notify)); } - private void notifyPayOrderSuccess(PayChannelDO channel, PayOrderNotifyRespDTO notify, PayNotifyReqDTO rawNotify) { + private void notifyPayOrder(PayChannelDO channel, PayOrderRespDTO notify) { + // 情况一:支付成功的回调 + if (PayOrderStatusRespEnum.isSuccess(notify.getStatus())) { + notifyPayOrderSuccess(channel, notify); + return; + } + // 情况二:非支付成功的回调,进行忽略 + log.info("[notifyPayOrder][非支付成功的回调({}),直接忽略]", toJsonString(notify)); + } + + private void notifyPayOrderSuccess(PayChannelDO channel, PayOrderRespDTO notify) { // 1. 更新 PayOrderExtensionDO 支付成功 - PayOrderExtensionDO orderExtension = updatePayOrderExtensionSuccess(notify.getOrderExtensionNo(), rawNotify); + PayOrderExtensionDO orderExtension = updatePayOrderExtensionSuccess(notify); // 2. 更新 PayOrderDO 支付成功 Pair order = updatePayOrderSuccess(channel, orderExtension, notify); if (order.getKey()) { // 如果之前已经成功回调,则直接返回,不用重复记录支付通知记录;例如说:支付平台重复回调 @@ -250,13 +260,12 @@ public class PayOrderServiceImpl implements PayOrderService { /** * 更新 PayOrderExtensionDO 支付成功 * - * @param no 支付订单号(支付模块) - * @param rawNotify 通知数据 + * @param notify 通知 * @return PayOrderExtensionDO 对象 */ - private PayOrderExtensionDO updatePayOrderExtensionSuccess(String no, PayNotifyReqDTO rawNotify) { + private PayOrderExtensionDO updatePayOrderExtensionSuccess(PayOrderRespDTO notify) { // 1. 查询 PayOrderExtensionDO - PayOrderExtensionDO orderExtension = orderExtensionMapper.selectByNo(no); + PayOrderExtensionDO orderExtension = orderExtensionMapper.selectByNo(notify.getOutTradeNo()); if (orderExtension == null) { throw exception(ErrorCodeConstants.PAY_ORDER_EXTENSION_NOT_FOUND); } @@ -269,10 +278,8 @@ public class PayOrderServiceImpl implements PayOrderService { } // 2. 更新 PayOrderExtensionDO - int updateCounts = orderExtensionMapper.updateByIdAndStatus(orderExtension.getId(), - PayOrderStatusEnum.WAITING.getStatus(), PayOrderExtensionDO.builder().id(orderExtension.getId()) - .status(PayOrderStatusEnum.SUCCESS.getStatus()) - .channelNotifyData(toJsonString(rawNotify)).build()); + int updateCounts = orderExtensionMapper.updateByIdAndStatus(orderExtension.getId(), PayOrderStatusEnum.WAITING.getStatus(), + PayOrderExtensionDO.builder().status(PayOrderStatusEnum.SUCCESS.getStatus()).channelNotifyData(toJsonString(notify)).build()); if (updateCounts == 0) { // 校验状态,必须是待支付 throw exception(ErrorCodeConstants.PAY_ORDER_EXTENSION_STATUS_IS_NOT_WAITING); } @@ -290,7 +297,7 @@ public class PayOrderServiceImpl implements PayOrderService { * value:PayOrderDO 对象 */ private Pair updatePayOrderSuccess(PayChannelDO channel, PayOrderExtensionDO orderExtension, - PayOrderNotifyRespDTO notify) { + PayOrderRespDTO notify) { // 1. 判断 PayOrderDO 是否处于待支付 PayOrderDO order = orderMapper.selectById(orderExtension.getOrderId()); if (order == null) { diff --git a/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/refund/PayRefundService.java b/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/refund/PayRefundService.java index 14f525582..f7485c486 100755 --- a/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/refund/PayRefundService.java +++ b/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/refund/PayRefundService.java @@ -1,11 +1,10 @@ package cn.iocoder.yudao.module.pay.service.refund; -import cn.iocoder.yudao.framework.pay.core.client.dto.notify.PayNotifyReqDTO; -import cn.iocoder.yudao.framework.pay.core.client.dto.notify.PayRefundNotifyRespDTO; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.pay.core.client.dto.refund.PayRefundRespDTO; import cn.iocoder.yudao.module.pay.api.refund.dto.PayRefundCreateReqDTO; import cn.iocoder.yudao.module.pay.controller.admin.refund.vo.PayRefundExportReqVO; import cn.iocoder.yudao.module.pay.controller.admin.refund.vo.PayRefundPageReqVO; -import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.module.pay.dal.dataobject.refund.PayRefundDO; import java.util.List; @@ -62,8 +61,7 @@ public interface PayRefundService { * * @param channelId 渠道编号 * @param notify 通知 - * @param rawNotify 通知数据 */ - void notifyPayRefund(Long channelId, PayRefundNotifyRespDTO notify, PayNotifyReqDTO rawNotify); + void notifyPayRefund(Long channelId, PayRefundRespDTO notify); } diff --git a/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/refund/PayRefundServiceImpl.java b/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/refund/PayRefundServiceImpl.java index a5f815bdd..0950fe471 100755 --- a/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/refund/PayRefundServiceImpl.java +++ b/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/refund/PayRefundServiceImpl.java @@ -7,10 +7,9 @@ import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.pay.config.PayProperties; import cn.iocoder.yudao.framework.pay.core.client.PayClient; import cn.iocoder.yudao.framework.pay.core.client.PayClientFactory; -import cn.iocoder.yudao.framework.pay.core.client.dto.notify.PayNotifyReqDTO; -import cn.iocoder.yudao.framework.pay.core.client.dto.notify.PayRefundNotifyRespDTO; +import cn.iocoder.yudao.framework.pay.core.client.dto.refund.PayRefundRespDTO; import cn.iocoder.yudao.framework.pay.core.client.dto.refund.PayRefundUnifiedReqDTO; -import cn.iocoder.yudao.framework.pay.core.enums.refund.PayNotifyRefundStatusEnum; +import cn.iocoder.yudao.framework.pay.core.enums.refund.PayRefundStatusRespEnum; import cn.iocoder.yudao.module.pay.api.refund.dto.PayRefundCreateReqDTO; import cn.iocoder.yudao.module.pay.controller.admin.refund.vo.PayRefundExportReqVO; import cn.iocoder.yudao.module.pay.controller.admin.refund.vo.PayRefundPageReqVO; @@ -39,14 +38,13 @@ import org.springframework.transaction.annotation.Transactional; import org.springframework.validation.annotation.Validated; import javax.annotation.Resource; -import java.time.LocalDateTime; import java.util.List; import java.util.Objects; /** * 退款订单 Service 实现类 * - * @author aquan + * @author jason */ @Service @Slf4j @@ -116,7 +114,7 @@ public class PayRefundServiceImpl implements PayRefundService { } // TODO 芋艿:待实现 - String merchantRefundId = RandomUtil.randomNumbers(16); + String merchantRefundId = "rrr" + RandomUtil.randomNumbers(16); // 校验退款的条件 validatePayRefund(reqDTO, order); @@ -128,12 +126,11 @@ public class PayRefundServiceImpl implements PayRefundService { PayOrderExtensionDO orderExtensionDO = orderExtensionService.getOrderExtension(order.getSuccessExtensionId()); PayRefundDO payRefundDO = refundMapper.selectByTradeNoAndMerchantRefundNo(orderExtensionDO.getNo(), merchantRefundId); // TODO 芋艿:需要优化 - if(Objects.nonNull(payRefundDO)){ + if (Objects.nonNull(payRefundDO)) { // 退款订单已经提交过。 //TODO 校验相同退款单的金额 // TODO @jason:咱要不封装一个 ObjectUtils.equalsAny - if (Objects.equals(PayRefundStatusEnum.SUCCESS.getStatus(), payRefundDO.getStatus()) - || Objects.equals(PayRefundStatusEnum.CLOSE.getStatus(), payRefundDO.getStatus())) { + if (Objects.equals(PayRefundStatusEnum.SUCCESS.getStatus(), payRefundDO.getStatus())) { //已成功退款 throw ServiceExceptionUtil.exception(ErrorCodeConstants.PAY_REFUND_SUCCEED); } @@ -147,14 +144,14 @@ public class PayRefundServiceImpl implements PayRefundService { .channelCode(order.getChannelCode()) .channelId(order.getChannelId()) .orderId(order.getId()) - .merchantRefundNo(merchantRefundId) // TODO 芋艿:需要优化 + .merchantRefundId(merchantRefundId) .notifyUrl(app.getRefundNotifyUrl()) .payPrice(order.getPrice()) .refundPrice(reqDTO.getPrice()) .userIp(reqDTO.getUserIp()) .merchantOrderId(order.getMerchantOrderId()) - .tradeNo(orderExtensionDO.getNo()) - .status(PayRefundStatusEnum.CREATE.getStatus()) + .no(orderExtensionDO.getNo()) + .status(PayRefundStatusEnum.WAITING.getStatus()) .reason(reqDTO.getReason()) .notifyStatus(PayOrderNotifyStatusEnum.NO.getStatus()) .type(refundType.getStatus()) @@ -163,11 +160,9 @@ public class PayRefundServiceImpl implements PayRefundService { } // TODO @jason:搞到 convert 里。一些额外的自动,手动 set 下; PayRefundUnifiedReqDTO unifiedReqDTO = new PayRefundUnifiedReqDTO(); - unifiedReqDTO.setUserIp(reqDTO.getUserIp()) - .setAmount(reqDTO.getPrice()) - .setChannelOrderNo(order.getChannelOrderNo()) - .setPayTradeNo(orderExtensionDO.getNo()) - .setMerchantRefundId(merchantRefundId) // TODO 芋艿:需要优化 + unifiedReqDTO.setPrice(reqDTO.getPrice()) + .setOutTradeNo(orderExtensionDO.getNo()) + .setOutRefundNo(merchantRefundId) // TODO 芋艿:需要优化 .setNotifyUrl(genChannelPayNotifyUrl(channel)) // TODO 芋艿:优化下 notifyUrl .setReason(reqDTO.getReason()); // 向渠道发起退款申请 @@ -191,24 +186,25 @@ public class PayRefundServiceImpl implements PayRefundService { @Override @Transactional(rollbackFor = Exception.class) - public void notifyPayRefund(Long channelId, PayRefundNotifyRespDTO notify, PayNotifyReqDTO rawNotify) { + public void notifyPayRefund(Long channelId, PayRefundRespDTO notify) { // 校验支付渠道是否有效 // TODO 芋艿:需要重构下这块的逻辑 PayChannelDO channel = channelService.validPayChannel(channelId); - if (Objects.equals(PayNotifyRefundStatusEnum.SUCCESS, notify.getStatus())){ + if (PayRefundStatusRespEnum.isSuccess(notify.getStatus())) { payRefundSuccess(notify); } else { - //TODO 支付异常, 支付宝似乎没有支付异常的通知。 // TODO @jason:那这里可以考虑打个 error logger @芋艿 微信是否存在支付异常通知 } } - private void payRefundSuccess(PayRefundNotifyRespDTO refundNotify) { + private void payRefundSuccess(PayRefundRespDTO refundNotify) { // 校验退款单存在 - PayRefundDO refundDO = refundMapper.selectByTradeNoAndMerchantRefundNo(refundNotify.getTradeNo(), - refundNotify.getReqNo()); + PayRefundDO refundDO = null; // TODO 芋艿:临时注释 +// PayRefundDO refundDO = refundMapper.selectByTradeNoAndMerchantRefundNo(refundNotify.getTradeNo(), +// refundNotify.getReqNo()); if (refundDO == null) { - log.error("[payRefundSuccess][不存在 seqNo 为{} 的支付退款单]", refundNotify.getReqNo()); + // TODO 芋艿:临时注释 +// log.error("[payRefundSuccess][不存在 seqNo 为{} 的支付退款单]", refundNotify.getReqNo()); throw ServiceExceptionUtil.exception(ErrorCodeConstants.PAY_REFUND_NOT_FOUND); } @@ -233,10 +229,10 @@ public class PayRefundServiceImpl implements PayRefundService { // 更新退款订单 PayRefundDO updateRefundDO = new PayRefundDO(); updateRefundDO.setId(refundDO.getId()) - .setSuccessTime(refundNotify.getRefundSuccessTime()) - .setChannelRefundNo(refundNotify.getChannelOrderNo()) - .setTradeNo(refundNotify.getTradeNo()) - .setNotifyTime(LocalDateTime.now()) + .setSuccessTime(refundNotify.getSuccessTime()) + // TODO 芋艿:如下两行,临时注释 +// .setChannelRefundNo(refundNotify.getChannelOrderNo()) +// .setNo(refundNotify.getTradeNo()) .setStatus(PayRefundStatusEnum.SUCCESS.getStatus()); refundMapper.updateById(updateRefundDO); diff --git a/yudao-module-pay/yudao-module-pay-biz/src/test/java/cn/iocoder/yudao/module/pay/service/app/PayAppServiceTest.java b/yudao-module-pay/yudao-module-pay-biz/src/test/java/cn/iocoder/yudao/module/pay/service/app/PayAppServiceTest.java index 81536aba3..e90b84685 100644 --- a/yudao-module-pay/yudao-module-pay-biz/src/test/java/cn/iocoder/yudao/module/pay/service/app/PayAppServiceTest.java +++ b/yudao-module-pay/yudao-module-pay-biz/src/test/java/cn/iocoder/yudao/module/pay/service/app/PayAppServiceTest.java @@ -3,9 +3,7 @@ package cn.iocoder.yudao.module.pay.service.app; import cn.hutool.core.util.RandomUtil; import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils; import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest; -import cn.iocoder.yudao.framework.test.core.util.RandomUtils; import cn.iocoder.yudao.module.pay.controller.admin.app.vo.PayAppCreateReqVO; import cn.iocoder.yudao.module.pay.controller.admin.app.vo.PayAppPageReqVO; import cn.iocoder.yudao.module.pay.controller.admin.app.vo.PayAppUpdateReqVO; @@ -18,8 +16,6 @@ import org.springframework.boot.test.mock.mockito.MockBean; import org.springframework.context.annotation.Import; import javax.annotation.Resource; -import java.time.LocalDateTime; -import java.util.Collections; import java.util.Map; import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.buildBetweenTime; diff --git a/yudao-module-pay/yudao-module-pay-biz/src/test/java/cn/iocoder/yudao/module/pay/service/refund/PayRefundServiceTest.java b/yudao-module-pay/yudao-module-pay-biz/src/test/java/cn/iocoder/yudao/module/pay/service/refund/PayRefundServiceTest.java index a3692a4e2..8365f1ab6 100755 --- a/yudao-module-pay/yudao-module-pay-biz/src/test/java/cn/iocoder/yudao/module/pay/service/refund/PayRefundServiceTest.java +++ b/yudao-module-pay/yudao-module-pay-biz/src/test/java/cn/iocoder/yudao/module/pay/service/refund/PayRefundServiceTest.java @@ -62,7 +62,7 @@ public class PayRefundServiceTest extends BaseDbUnitTest { o.setChannelId(1L); o.setChannelCode(PayChannelEnum.WX_PUB.getCode()); o.setOrderId(1L); - o.setTradeNo("OT0000001"); + o.setNo("OT0000001"); o.setMerchantOrderId("MOT0000001"); o.setMerchantRefundNo("MRF0000001"); o.setNotifyUrl("https://www.cancanzi.com"); @@ -127,7 +127,7 @@ public class PayRefundServiceTest extends BaseDbUnitTest { o.setChannelId(1L); o.setChannelCode(PayChannelEnum.WX_PUB.getCode()); o.setOrderId(1L); - o.setTradeNo("OT0000001"); + o.setNo("OT0000001"); o.setMerchantOrderId("MOT0000001"); o.setMerchantRefundNo("MRF0000001"); o.setNotifyUrl("https://www.cancanzi.com"); diff --git a/yudao-ui-admin/src/api/pay/channel.js b/yudao-ui-admin/src/api/pay/channel.js index 727fde092..3f06805b7 100644 --- a/yudao-ui-admin/src/api/pay/channel.js +++ b/yudao-ui-admin/src/api/pay/channel.js @@ -28,14 +28,14 @@ export function deleteChannel(id) { } // 获得支付渠道 -export function getChannel(appId,code) { +export function getChannel(appId, code) { return request({ - url: '/pay/channel/get-channel', + url: '/pay/channel/get', + method: 'get', params:{ - appId:appId, - code:code + appId, + code }, - method: 'get' }) } From c44ace601160ae562e85e38240c318591257f93b Mon Sep 17 00:00:00 2001 From: YunaiV Date: Sat, 15 Jul 2023 20:36:04 +0800 Subject: [PATCH 02/10] =?UTF-8?q?mall=20+=20pay=EF=BC=9A=201.=20=E5=AE=8C?= =?UTF-8?q?=E6=88=90=E6=94=AF=E4=BB=98=E5=AE=9D=E7=9A=84=E9=80=80=E6=AC=BE?= =?UTF-8?q?=E9=87=8D=E6=9E=84=202.=20=E5=AE=8C=E6=88=90=20demo=20=E6=A8=A1?= =?UTF-8?q?=E5=9D=97=E7=9A=84=E9=80=80=E6=AC=BE=E6=8E=A5=E5=85=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../client/dto/refund/PayRefundRespDTO.java | 7 + .../impl/alipay/AbstractAlipayPayClient.java | 2 +- .../api/refund/dto/PayRefundCreateReqDTO.java | 20 +- .../module/pay/enums/ErrorCodeConstants.java | 7 +- .../pay/enums/order/PayOrderStatusEnum.java | 10 + .../module/pay/api/order/PayOrderApiImpl.java | 2 +- .../pay/api/refund/PayRefundApiImpl.java | 4 +- .../admin/notify/PayNotifyController.java | 4 +- .../admin/order/PayOrderController.java | 2 +- .../app/order/AppPayOrderController.java | 2 +- .../pay/convert/refund/PayRefundConvert.java | 18 +- .../pay/dal/dataobject/order/PayOrderDO.java | 6 +- .../dal/dataobject/refund/PayRefundDO.java | 13 - .../pay/dal/mysql/refund/PayRefundMapper.java | 48 ++- .../PayOrderRefundStatusEnum.java} | 6 +- .../service/demo/PayDemoOrderServiceImpl.java | 11 +- .../order/PayOrderExtensionService.java | 1 + .../pay/service/order/PayOrderService.java | 31 +- .../service/order/PayOrderServiceImpl.java | 94 ++++-- .../pay/service/refund/PayRefundService.java | 2 +- .../service/refund/PayRefundServiceImpl.java | 283 +++++++++--------- .../service/order/PayOrderServiceTest.java | 14 +- .../service/refund/PayRefundServiceTest.java | 32 +- 23 files changed, 339 insertions(+), 280 deletions(-) rename yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/enums/{refund/PayRefundTypeEnum.java => order/PayOrderRefundStatusEnum.java} (74%) diff --git a/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/dto/refund/PayRefundRespDTO.java b/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/dto/refund/PayRefundRespDTO.java index 5268eddc4..6d10dc9a0 100644 --- a/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/dto/refund/PayRefundRespDTO.java +++ b/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/dto/refund/PayRefundRespDTO.java @@ -20,6 +20,13 @@ public class PayRefundRespDTO { */ private Integer status; + /** + * 外部退款号 + * + * 对应 PayRefundDO 的 no 字段 + */ + private String outRefundNo; + /** * 渠道退款单号 * diff --git a/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/alipay/AbstractAlipayPayClient.java b/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/alipay/AbstractAlipayPayClient.java index 1da5f2631..657c64360 100644 --- a/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/alipay/AbstractAlipayPayClient.java +++ b/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/alipay/AbstractAlipayPayClient.java @@ -68,7 +68,6 @@ public abstract class AbstractAlipayPayClient extends AbstractPayClient submitPayOrder(@RequestBody PayOrderSubmitReqVO reqVO) { - PayOrderSubmitRespVO respVO = payOrderService.submitPayOrder(reqVO, getClientIP()); + PayOrderSubmitRespVO respVO = payOrderService.submitOrder(reqVO, getClientIP()); return success(respVO); } diff --git a/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/controller/app/order/AppPayOrderController.java b/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/controller/app/order/AppPayOrderController.java index b634909df..9100d3498 100644 --- a/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/controller/app/order/AppPayOrderController.java +++ b/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/controller/app/order/AppPayOrderController.java @@ -40,7 +40,7 @@ public class AppPayOrderController { @PostMapping("/submit") @Operation(summary = "提交支付订单") public CommonResult submitPayOrder(@RequestBody AppPayOrderSubmitReqVO reqVO) { - PayOrderSubmitRespVO respVO = payOrderService.submitPayOrder(reqVO, getClientIP()); + PayOrderSubmitRespVO respVO = payOrderService.submitOrder(reqVO, getClientIP()); return success(PayOrderConvert.INSTANCE.convert3(respVO)); } diff --git a/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/convert/refund/PayRefundConvert.java b/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/convert/refund/PayRefundConvert.java index 12141377a..eb2b2053d 100755 --- a/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/convert/refund/PayRefundConvert.java +++ b/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/convert/refund/PayRefundConvert.java @@ -1,12 +1,11 @@ package cn.iocoder.yudao.module.pay.convert.refund; import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.module.pay.api.refund.dto.PayRefundCreateReqDTO; +import cn.iocoder.yudao.module.pay.api.refund.dto.PayRefundRespDTO; import cn.iocoder.yudao.module.pay.controller.admin.refund.vo.*; -import cn.iocoder.yudao.module.pay.dal.dataobject.order.PayOrderDO; import cn.iocoder.yudao.module.pay.dal.dataobject.refund.PayRefundDO; import org.mapstruct.Mapper; -import org.mapstruct.Mapping; -import org.mapstruct.Mappings; import org.mapstruct.factory.Mappers; import java.math.BigDecimal; @@ -44,8 +43,6 @@ public interface PayRefundConvert { PageResult convertPage(PageResult page); - List convertList02(List list); - /** * 退款订单DO 转 导出excel VO * @@ -67,7 +64,6 @@ public interface PayRefundConvert { payRefundExcelVO.setNotifyUrl(bean.getNotifyUrl()); payRefundExcelVO.setNotifyStatus(bean.getNotifyStatus()); payRefundExcelVO.setStatus(bean.getStatus()); - payRefundExcelVO.setType(bean.getType()); payRefundExcelVO.setReason(bean.getReason()); payRefundExcelVO.setUserIp(bean.getUserIp()); payRefundExcelVO.setChannelOrderNo(bean.getChannelOrderNo()); @@ -84,12 +80,8 @@ public interface PayRefundConvert { return payRefundExcelVO; } - //TODO 太多需要处理了, 暂时不用 - @Mappings(value = { - @Mapping(source = "price", target = "payPrice"), - @Mapping(source = "id", target = "orderId"), - @Mapping(target = "status",ignore = true) - }) - PayRefundDO convert(PayOrderDO orderDO); + PayRefundDO convert(PayRefundCreateReqDTO bean); + + PayRefundRespDTO convert02(PayRefundDO bean); } diff --git a/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/dal/dataobject/order/PayOrderDO.java b/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/dal/dataobject/order/PayOrderDO.java index 485aa0c90..62b43721e 100644 --- a/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/dal/dataobject/order/PayOrderDO.java +++ b/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/dal/dataobject/order/PayOrderDO.java @@ -6,7 +6,7 @@ import cn.iocoder.yudao.module.pay.dal.dataobject.app.PayAppDO; import cn.iocoder.yudao.module.pay.dal.dataobject.channel.PayChannelDO; import cn.iocoder.yudao.module.pay.enums.order.PayOrderNotifyStatusEnum; import cn.iocoder.yudao.module.pay.enums.order.PayOrderStatusEnum; -import cn.iocoder.yudao.module.pay.enums.refund.PayRefundTypeEnum; +import cn.iocoder.yudao.module.pay.enums.order.PayOrderRefundStatusEnum; import com.baomidou.mybatisplus.annotation.KeySequence; import com.baomidou.mybatisplus.annotation.TableName; import lombok.*; @@ -127,7 +127,7 @@ public class PayOrderDO extends BaseDO { /** * 退款状态 * - * 枚举 {@link PayRefundTypeEnum} + * 枚举 {@link PayOrderRefundStatusEnum} */ private Integer refundStatus; /** @@ -137,7 +137,7 @@ public class PayOrderDO extends BaseDO { /** * 退款总金额,单位:分 */ - private Long refundPrice; + private Integer refundPrice; // ========== 渠道相关字段 ========== /** diff --git a/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/dal/dataobject/refund/PayRefundDO.java b/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/dal/dataobject/refund/PayRefundDO.java index 898a13699..1a13a8115 100644 --- a/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/dal/dataobject/refund/PayRefundDO.java +++ b/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/dal/dataobject/refund/PayRefundDO.java @@ -7,7 +7,6 @@ import cn.iocoder.yudao.module.pay.dal.dataobject.channel.PayChannelDO; import cn.iocoder.yudao.module.pay.dal.dataobject.order.PayOrderDO; import cn.iocoder.yudao.module.pay.enums.notify.PayNotifyStatusEnum; import cn.iocoder.yudao.module.pay.enums.refund.PayRefundStatusEnum; -import cn.iocoder.yudao.module.pay.enums.refund.PayRefundTypeEnum; import com.baomidou.mybatisplus.annotation.KeySequence; import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableName; @@ -104,12 +103,6 @@ public class PayRefundDO extends BaseDO { */ private Integer status; - /** - * 退款类型(部分退款,全部退款) - * - * 枚举 {@link PayRefundTypeEnum} - */ - private Integer type; /** * 支付金额,单位:分 */ @@ -157,12 +150,6 @@ public class PayRefundDO extends BaseDO { */ private String channelErrorMsg; - /** - * 支付渠道的额外参数 - * - * 参见 参数说明 - */ - private String channelExtras; /** * 支付渠道异步通知的内容 * diff --git a/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/dal/mysql/refund/PayRefundMapper.java b/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/dal/mysql/refund/PayRefundMapper.java index 42bd00b81..629650e0f 100755 --- a/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/dal/mysql/refund/PayRefundMapper.java +++ b/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/dal/mysql/refund/PayRefundMapper.java @@ -1,11 +1,13 @@ package cn.iocoder.yudao.module.pay.dal.mysql.refund; -import cn.iocoder.yudao.module.pay.controller.admin.refund.vo.PayRefundExportReqVO; -import cn.iocoder.yudao.module.pay.controller.admin.refund.vo.PayRefundPageReqVO; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; +import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; import cn.iocoder.yudao.framework.mybatis.core.query.QueryWrapperX; +import cn.iocoder.yudao.module.pay.controller.admin.refund.vo.PayRefundExportReqVO; +import cn.iocoder.yudao.module.pay.controller.admin.refund.vo.PayRefundPageReqVO; import cn.iocoder.yudao.module.pay.dal.dataobject.refund.PayRefundDO; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import org.apache.ibatis.annotations.Mapper; import java.util.List; @@ -13,6 +15,34 @@ import java.util.List; @Mapper public interface PayRefundMapper extends BaseMapperX { + default Long selectCountByAppId(Long appId) { + return selectCount(PayRefundDO::getAppId, appId); + } + + default PayRefundDO selectByAppIdAndMerchantRefundId(Long appId, String merchantRefundId) { + return selectOne(new LambdaQueryWrapperX() + .eq(PayRefundDO::getAppId, appId) + .eq(PayRefundDO::getMerchantRefundId, merchantRefundId)); + } + + default Long selectCountByAppIdAndOrderId(Long appId, Long orderId, Integer status) { + return selectCount(new LambdaQueryWrapperX() + .eq(PayRefundDO::getAppId, appId) + .eq(PayRefundDO::getOrderId, orderId) + .eq(PayRefundDO::getStatus, status)); + } + + default PayRefundDO selectByAppIdAndNo(Long appId, String no) { + return selectOne(new LambdaQueryWrapperX() + .eq(PayRefundDO::getAppId, appId) + .eq(PayRefundDO::getNo, no)); + } + + default int updateByIdAndStatus(Long id, Integer status, PayRefundDO update) { + return update(update, new LambdaQueryWrapper() + .eq(PayRefundDO::getId, id).eq(PayRefundDO::getStatus, status)); + } + default PageResult selectPage(PayRefundPageReqVO reqVO) { return selectPage(reqVO, new QueryWrapperX() .eqIfPresent("app_id", reqVO.getAppId()) @@ -37,18 +67,4 @@ public interface PayRefundMapper extends BaseMapperX { .orderByDesc("id")); } - default Long selectCountByApp(Long appId) { - return selectCount(PayRefundDO::getAppId, appId); - } - - default PayRefundDO selectByReqNo(String reqNo) { - return selectOne("req_no", reqNo); - } - - // TODO 芋艿:要重构 - default PayRefundDO selectByTradeNoAndMerchantRefundNo(String tradeNo, String merchantRefundNo){ -// return selectOne("trade_no", tradeNo, "merchant_refund_no", merchantRefundNo); - return null; - } - } diff --git a/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/enums/refund/PayRefundTypeEnum.java b/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/enums/order/PayOrderRefundStatusEnum.java similarity index 74% rename from yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/enums/refund/PayRefundTypeEnum.java rename to yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/enums/order/PayOrderRefundStatusEnum.java index 5fb10a399..b9dbcb67b 100644 --- a/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/enums/refund/PayRefundTypeEnum.java +++ b/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/enums/order/PayOrderRefundStatusEnum.java @@ -1,4 +1,4 @@ -package cn.iocoder.yudao.module.pay.enums.refund; +package cn.iocoder.yudao.module.pay.enums.order; import cn.iocoder.yudao.framework.common.core.IntArrayValuable; import lombok.AllArgsConstructor; @@ -11,10 +11,10 @@ import lombok.Getter; */ @Getter @AllArgsConstructor -public enum PayRefundTypeEnum implements IntArrayValuable { +public enum PayOrderRefundStatusEnum implements IntArrayValuable { NO(0, "未退款"), - SOME(10, "部分退款"), + PART(10, "部分退款"), ALL(20, "全部退款") ; diff --git a/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/demo/PayDemoOrderServiceImpl.java b/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/demo/PayDemoOrderServiceImpl.java index 71968f19b..c9bcbbd56 100644 --- a/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/demo/PayDemoOrderServiceImpl.java +++ b/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/demo/PayDemoOrderServiceImpl.java @@ -184,12 +184,17 @@ public class PayDemoOrderServiceImpl implements PayDemoOrderService { // 1. 校验订单是否可以退款 PayDemoOrderDO order = validateDemoOrderCanRefund(id); - // 2.1 创建退款单 + // 2.1 生成退款单号 + // 一般来说,用户发起退款的时候,都会单独插入一个售后维权表,然后使用该表的 id 作为 refundId + // 这里我们是个简单的 demo,所以没有售后维权表,直接使用订单 id + "-refund" 来演示 + String refundId = order.getId() + "-refund"; + // 2.2 创建退款单 Long payRefundId = payRefundApi.createPayRefund(new PayRefundCreateReqDTO() .setAppId(PAY_APP_ID).setUserIp(getClientIP()) // 支付应用 - .setPayOrderId(order.getPayOrderId()) // 支付单号 + .setMerchantOrderId(String.valueOf(order.getId())) // 支付单号 + .setMerchantRefundId(refundId) .setReason("想退钱").setPrice(order.getPrice()));// 价格信息 - // 2.2 更新退款单到 demo 订单 + // 2.3 更新退款单到 demo 订单 payDemoOrderMapper.updateById(new PayDemoOrderDO().setId(id) .setPayRefundId(payRefundId).setRefundPrice(order.getPrice())); } diff --git a/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/order/PayOrderExtensionService.java b/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/order/PayOrderExtensionService.java index f707bc0df..2db86a832 100755 --- a/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/order/PayOrderExtensionService.java +++ b/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/order/PayOrderExtensionService.java @@ -7,6 +7,7 @@ import java.util.Collection; import java.util.List; import java.util.Map; +// TODO 芋艿:合并到 PayOrder 里; /** * 支付订单 Service 接口 * diff --git a/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/order/PayOrderService.java b/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/order/PayOrderService.java index 5f4c6aa21..8aadac856 100755 --- a/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/order/PayOrderService.java +++ b/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/order/PayOrderService.java @@ -31,6 +31,15 @@ public interface PayOrderService { */ PayOrderDO getOrder(Long id); + /** + * 获得支付订单 + * + * @param appId 应用编号 + * @param merchantOrderId 商户订单编号 + * @return 支付订单 + */ + PayOrderDO getOrder(Long appId, String merchantOrderId); + /** * 获得指定应用的订单数量 * @@ -70,11 +79,11 @@ public interface PayOrderService { /** * 根据订单 ID 集合获取订单商品名称Map集合 * - * @param idList 订单 ID 集合 + * @param ids 订单 ID 集合 * @return 订单商品 map 集合 */ - default Map getOrderSubjectMap(Collection idList) { - List list = getOrderSubjectList(idList); + default Map getOrderSubjectMap(Collection ids) { + List list = getOrderSubjectList(ids); return CollectionUtils.convertMap(list, PayOrderDO::getId); } @@ -84,7 +93,7 @@ public interface PayOrderService { * @param reqDTO 创建请求 * @return 支付单编号 */ - Long createPayOrder(@Valid PayOrderCreateReqDTO reqDTO); + Long createOrder(@Valid PayOrderCreateReqDTO reqDTO); /** * 提交支付 @@ -94,8 +103,8 @@ public interface PayOrderService { * @param userIp 提交 IP * @return 提交结果 */ - PayOrderSubmitRespVO submitPayOrder(@Valid PayOrderSubmitReqVO reqVO, - @NotEmpty(message = "提交 IP 不能为空") String userIp); + PayOrderSubmitRespVO submitOrder(@Valid PayOrderSubmitReqVO reqVO, + @NotEmpty(message = "提交 IP 不能为空") String userIp); /** * 通知支付单成功 @@ -103,6 +112,14 @@ public interface PayOrderService { * @param channelId 渠道编号 * @param notify 通知 */ - void notifyPayOrder(Long channelId, PayOrderRespDTO notify); + void notifyOrder(Long channelId, PayOrderRespDTO notify); + + /** + * 更新支付订单的退款金额 + * + * @param id 编号 + * @param incrRefundPrice 增加的退款金额 + */ + void updateOrderRefundPrice(Long id, Integer incrRefundPrice); } diff --git a/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/order/PayOrderServiceImpl.java b/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/order/PayOrderServiceImpl.java index 884b182df..df3c6ce90 100755 --- a/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/order/PayOrderServiceImpl.java +++ b/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/order/PayOrderServiceImpl.java @@ -26,11 +26,10 @@ import cn.iocoder.yudao.module.pay.dal.dataobject.order.PayOrderDO; import cn.iocoder.yudao.module.pay.dal.dataobject.order.PayOrderExtensionDO; import cn.iocoder.yudao.module.pay.dal.mysql.order.PayOrderExtensionMapper; import cn.iocoder.yudao.module.pay.dal.mysql.order.PayOrderMapper; -import cn.iocoder.yudao.module.pay.enums.ErrorCodeConstants; import cn.iocoder.yudao.module.pay.enums.notify.PayNotifyTypeEnum; import cn.iocoder.yudao.module.pay.enums.order.PayOrderNotifyStatusEnum; +import cn.iocoder.yudao.module.pay.enums.order.PayOrderRefundStatusEnum; import cn.iocoder.yudao.module.pay.enums.order.PayOrderStatusEnum; -import cn.iocoder.yudao.module.pay.enums.refund.PayRefundTypeEnum; import cn.iocoder.yudao.module.pay.service.app.PayAppService; import cn.iocoder.yudao.module.pay.service.channel.PayChannelService; import cn.iocoder.yudao.module.pay.service.notify.PayNotifyService; @@ -48,6 +47,7 @@ import java.util.Objects; import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; import static cn.iocoder.yudao.framework.common.util.json.JsonUtils.toJsonString; +import static cn.iocoder.yudao.module.pay.enums.ErrorCodeConstants.*; /** * 支付订单 Service 实现类 @@ -82,6 +82,11 @@ public class PayOrderServiceImpl implements PayOrderService { return orderMapper.selectById(id); } + @Override + public PayOrderDO getOrder(Long appId, String merchantOrderId) { + return orderMapper.selectByAppIdAndMerchantOrderId(appId, merchantOrderId); + } + @Override public Long getOrderCountByAppId(Long appId) { return orderMapper.selectCountByAppId(appId); @@ -104,7 +109,7 @@ public class PayOrderServiceImpl implements PayOrderService { } @Override - public Long createPayOrder(PayOrderCreateReqDTO reqDTO) { + public Long createOrder(PayOrderCreateReqDTO reqDTO) { // 校验 App PayAppDO app = appService.validPayApp(reqDTO.getAppId()); @@ -112,7 +117,7 @@ public class PayOrderServiceImpl implements PayOrderService { PayOrderDO order = orderMapper.selectByAppIdAndMerchantOrderId( reqDTO.getAppId(), reqDTO.getMerchantOrderId()); if (order != null) { - log.warn("[createPayOrder][appId({}) merchantOrderId({}) 已经存在对应的支付单({})]", order.getAppId(), + log.warn("[createOrder][appId({}) merchantOrderId({}) 已经存在对应的支付单({})]", order.getAppId(), order.getMerchantOrderId(), toJsonString(order)); // 理论来说,不会出现这个情况 return order.getId(); } @@ -124,16 +129,16 @@ public class PayOrderServiceImpl implements PayOrderService { // 订单相关字段 .setStatus(PayOrderStatusEnum.WAITING.getStatus()) // 退款相关字段 - .setRefundStatus(PayRefundTypeEnum.NO.getStatus()).setRefundTimes(0).setRefundPrice(0L); + .setRefundStatus(PayOrderRefundStatusEnum.NO.getStatus()).setRefundTimes(0).setRefundPrice(0); orderMapper.insert(order); return order.getId(); } @Override @Transactional(rollbackFor = Exception.class) - public PayOrderSubmitRespVO submitPayOrder(PayOrderSubmitReqVO reqVO, String userIp) { + public PayOrderSubmitRespVO submitOrder(PayOrderSubmitReqVO reqVO, String userIp) { // 1. 获得 PayOrderDO ,并校验其是否存在 - PayOrderDO order = validatePayOrderCanSubmit(reqVO.getId()); + PayOrderDO order = validateOrderCanSubmit(reqVO.getId()); // 1.2 校验支付渠道是否有效 PayChannelDO channel = validatePayChannelCanSubmit(order.getAppId(), reqVO.getChannelCode()); PayClient client = payClientFactory.getPayClient(channel.getId()); @@ -167,16 +172,16 @@ public class PayOrderServiceImpl implements PayOrderService { return PayOrderConvert.INSTANCE.convert(order, unifiedOrderRespDTO); } - private PayOrderDO validatePayOrderCanSubmit(Long id) { + private PayOrderDO validateOrderCanSubmit(Long id) { PayOrderDO order = orderMapper.selectById(id); if (order == null) { // 是否存在 - throw exception(ErrorCodeConstants.PAY_ORDER_NOT_FOUND); + throw exception(PAY_ORDER_NOT_FOUND); } if (!PayOrderStatusEnum.WAITING.getStatus().equals(order.getStatus())) { // 校验状态,必须是待支付 - throw exception(ErrorCodeConstants.PAY_ORDER_STATUS_IS_NOT_WAITING); + throw exception(PAY_ORDER_STATUS_IS_NOT_WAITING); } if (LocalDateTimeUtils.beforeNow(order.getExpireTime())) { // 校验是否过期 - throw exception(ErrorCodeConstants.PAY_ORDER_IS_EXPIRED); + throw exception(PAY_ORDER_IS_EXPIRED); } return order; } @@ -184,14 +189,12 @@ public class PayOrderServiceImpl implements PayOrderService { private PayChannelDO validatePayChannelCanSubmit(Long appId, String channelCode) { // 校验 App appService.validPayApp(appId); - // 校验支付渠道是否有效 PayChannelDO channel = channelService.validPayChannel(appId, channelCode); - // 校验支付客户端是否正确初始化 PayClient client = payClientFactory.getPayClient(channel.getId()); if (client == null) { log.error("[validatePayChannelCanSubmit][渠道编号({}) 找不到对应的支付客户端]", channel.getId()); - throw exception(ErrorCodeConstants.PAY_CHANNEL_CLIENT_NOT_FOUND); + throw exception(PAY_CHANNEL_CLIENT_NOT_FOUND); } return channel; } @@ -226,28 +229,55 @@ public class PayOrderServiceImpl implements PayOrderService { @Override @Transactional(rollbackFor = Exception.class) - public void notifyPayOrder(Long channelId, PayOrderRespDTO notify) { + public void notifyOrder(Long channelId, PayOrderRespDTO notify) { // 校验支付渠道是否有效 PayChannelDO channel = channelService.validPayChannel(channelId); // 更新支付订单为已支付 TenantUtils.execute(channel.getTenantId(), () -> notifyPayOrder(channel, notify)); } + @Override + public void updateOrderRefundPrice(Long id, Integer incrRefundPrice) { + PayOrderDO order = orderMapper.selectById(id); + if (order == null) { + throw exception(PAY_ORDER_NOT_FOUND); + } + if (!PayOrderStatusEnum.isSuccess(order.getStatus())) { + throw exception(PAY_REFUND_PRICE_EXCEED); + } + if (order.getRefundPrice() + incrRefundPrice > order.getPrice()) { + throw exception(PAY_REFUND_PRICE_EXCEED); + } + + // 更新订单 + PayOrderDO updateObj = new PayOrderDO() + .setRefundPrice(order.getRefundPrice() + incrRefundPrice) + .setRefundTimes(order.getRefundTimes() + 1); + if (Objects.equals(updateObj.getRefundPrice(), order.getPrice())) { + updateObj.setStatus(PayOrderStatusEnum.CLOSED.getStatus()) + .setRefundStatus(PayOrderRefundStatusEnum.ALL.getStatus()); + } else { + updateObj.setStatus(PayOrderStatusEnum.CLOSED.getStatus()) + .setRefundStatus(PayOrderRefundStatusEnum.PART.getStatus()); + } + orderMapper.updateByIdAndStatus(id, PayOrderStatusEnum.SUCCESS.getStatus(), updateObj); + } + private void notifyPayOrder(PayChannelDO channel, PayOrderRespDTO notify) { // 情况一:支付成功的回调 if (PayOrderStatusRespEnum.isSuccess(notify.getStatus())) { - notifyPayOrderSuccess(channel, notify); + notifyOrderSuccess(channel, notify); return; } // 情况二:非支付成功的回调,进行忽略 log.info("[notifyPayOrder][非支付成功的回调({}),直接忽略]", toJsonString(notify)); } - private void notifyPayOrderSuccess(PayChannelDO channel, PayOrderRespDTO notify) { + private void notifyOrderSuccess(PayChannelDO channel, PayOrderRespDTO notify) { // 1. 更新 PayOrderExtensionDO 支付成功 - PayOrderExtensionDO orderExtension = updatePayOrderExtensionSuccess(notify); + PayOrderExtensionDO orderExtension = updateOrderExtensionSuccess(notify); // 2. 更新 PayOrderDO 支付成功 - Pair order = updatePayOrderSuccess(channel, orderExtension, notify); + Pair order = updateOrderExtensionSuccess(channel, orderExtension, notify); if (order.getKey()) { // 如果之前已经成功回调,则直接返回,不用重复记录支付通知记录;例如说:支付平台重复回调 return; } @@ -263,27 +293,27 @@ public class PayOrderServiceImpl implements PayOrderService { * @param notify 通知 * @return PayOrderExtensionDO 对象 */ - private PayOrderExtensionDO updatePayOrderExtensionSuccess(PayOrderRespDTO notify) { + private PayOrderExtensionDO updateOrderExtensionSuccess(PayOrderRespDTO notify) { // 1. 查询 PayOrderExtensionDO PayOrderExtensionDO orderExtension = orderExtensionMapper.selectByNo(notify.getOutTradeNo()); if (orderExtension == null) { - throw exception(ErrorCodeConstants.PAY_ORDER_EXTENSION_NOT_FOUND); + throw exception(PAY_ORDER_EXTENSION_NOT_FOUND); } if (PayOrderStatusEnum.isSuccess(orderExtension.getStatus())) { // 如果已经是成功,直接返回,不用重复更新 - log.info("[updatePayOrderSuccess][支付拓展单({}) 已经是已支付,无需更新为已支付]", orderExtension.getId()); + log.info("[updateOrderExtensionSuccess][支付拓展单({}) 已经是已支付,无需更新为已支付]", orderExtension.getId()); return orderExtension; } if (ObjectUtil.notEqual(orderExtension.getStatus(), PayOrderStatusEnum.WAITING.getStatus())) { // 校验状态,必须是待支付 - throw exception(ErrorCodeConstants.PAY_ORDER_EXTENSION_STATUS_IS_NOT_WAITING); + throw exception(PAY_ORDER_EXTENSION_STATUS_IS_NOT_WAITING); } // 2. 更新 PayOrderExtensionDO - int updateCounts = orderExtensionMapper.updateByIdAndStatus(orderExtension.getId(), PayOrderStatusEnum.WAITING.getStatus(), + int updateCounts = orderExtensionMapper.updateByIdAndStatus(orderExtension.getId(), orderExtension.getStatus(), PayOrderExtensionDO.builder().status(PayOrderStatusEnum.SUCCESS.getStatus()).channelNotifyData(toJsonString(notify)).build()); if (updateCounts == 0) { // 校验状态,必须是待支付 - throw exception(ErrorCodeConstants.PAY_ORDER_EXTENSION_STATUS_IS_NOT_WAITING); + throw exception(PAY_ORDER_EXTENSION_STATUS_IS_NOT_WAITING); } - log.info("[updatePayOrderSuccess][支付拓展单({}) 更新为已支付]", orderExtension.getId()); + log.info("[updateOrderExtensionSuccess][支付拓展单({}) 更新为已支付]", orderExtension.getId()); return orderExtension; } @@ -296,20 +326,20 @@ public class PayOrderServiceImpl implements PayOrderService { * @return key:是否之前已经成功回调 * value:PayOrderDO 对象 */ - private Pair updatePayOrderSuccess(PayChannelDO channel, PayOrderExtensionDO orderExtension, + private Pair updateOrderExtensionSuccess(PayChannelDO channel, PayOrderExtensionDO orderExtension, PayOrderRespDTO notify) { // 1. 判断 PayOrderDO 是否处于待支付 PayOrderDO order = orderMapper.selectById(orderExtension.getOrderId()); if (order == null) { - throw exception(ErrorCodeConstants.PAY_ORDER_NOT_FOUND); + throw exception(PAY_ORDER_NOT_FOUND); } if (PayOrderStatusEnum.isSuccess(order.getStatus()) // 如果已经是成功,直接返回,不用重复更新 && Objects.equals(order.getSuccessExtensionId(), orderExtension.getId())) { - log.info("[updatePayOrderSuccess][支付订单({}) 已经是已支付,无需更新为已支付]", order.getId()); + log.info("[updateOrderExtensionSuccess][支付订单({}) 已经是已支付,无需更新为已支付]", order.getId()); return Pair.of(true, order); } if (!PayOrderStatusEnum.WAITING.getStatus().equals(order.getStatus())) { // 校验状态,必须是待支付 - throw exception(ErrorCodeConstants.PAY_ORDER_STATUS_IS_NOT_WAITING); + throw exception(PAY_ORDER_STATUS_IS_NOT_WAITING); } // 2. 更新 PayOrderDO @@ -320,9 +350,9 @@ public class PayOrderServiceImpl implements PayOrderService { .channelOrderNo(notify.getChannelOrderNo()).channelUserId(notify.getChannelUserId()) .notifyTime(LocalDateTime.now()).build()); if (updateCounts == 0) { // 校验状态,必须是待支付 - throw exception(ErrorCodeConstants.PAY_ORDER_STATUS_IS_NOT_WAITING); + throw exception(PAY_ORDER_STATUS_IS_NOT_WAITING); } - log.info("[updatePayOrderSuccess][支付订单({}) 更新为已支付]", order.getId()); + log.info("[updateOrderExtensionSuccess][支付订单({}) 更新为已支付]", order.getId()); return Pair.of(false, order); } diff --git a/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/refund/PayRefundService.java b/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/refund/PayRefundService.java index f7485c486..ef5c15803 100755 --- a/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/refund/PayRefundService.java +++ b/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/refund/PayRefundService.java @@ -62,6 +62,6 @@ public interface PayRefundService { * @param channelId 渠道编号 * @param notify 通知 */ - void notifyPayRefund(Long channelId, PayRefundRespDTO notify); + void notifyRefund(Long channelId, PayRefundRespDTO notify); } diff --git a/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/refund/PayRefundServiceImpl.java b/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/refund/PayRefundServiceImpl.java index 0950fe471..03d22943a 100755 --- a/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/refund/PayRefundServiceImpl.java +++ b/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/refund/PayRefundServiceImpl.java @@ -1,8 +1,7 @@ package cn.iocoder.yudao.module.pay.service.refund; +import cn.hutool.core.date.DateUtil; import cn.hutool.core.util.RandomUtil; -import cn.hutool.core.util.StrUtil; -import cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.pay.config.PayProperties; import cn.iocoder.yudao.framework.pay.core.client.PayClient; @@ -10,22 +9,23 @@ import cn.iocoder.yudao.framework.pay.core.client.PayClientFactory; import cn.iocoder.yudao.framework.pay.core.client.dto.refund.PayRefundRespDTO; import cn.iocoder.yudao.framework.pay.core.client.dto.refund.PayRefundUnifiedReqDTO; import cn.iocoder.yudao.framework.pay.core.enums.refund.PayRefundStatusRespEnum; +import cn.iocoder.yudao.framework.tenant.core.util.TenantUtils; import cn.iocoder.yudao.module.pay.api.refund.dto.PayRefundCreateReqDTO; import cn.iocoder.yudao.module.pay.controller.admin.refund.vo.PayRefundExportReqVO; import cn.iocoder.yudao.module.pay.controller.admin.refund.vo.PayRefundPageReqVO; +import cn.iocoder.yudao.module.pay.convert.refund.PayRefundConvert; import cn.iocoder.yudao.module.pay.dal.dataobject.app.PayAppDO; import cn.iocoder.yudao.module.pay.dal.dataobject.channel.PayChannelDO; import cn.iocoder.yudao.module.pay.dal.dataobject.order.PayOrderDO; import cn.iocoder.yudao.module.pay.dal.dataobject.order.PayOrderExtensionDO; import cn.iocoder.yudao.module.pay.dal.dataobject.refund.PayRefundDO; -import cn.iocoder.yudao.module.pay.dal.mysql.order.PayOrderMapper; import cn.iocoder.yudao.module.pay.dal.mysql.refund.PayRefundMapper; import cn.iocoder.yudao.module.pay.enums.ErrorCodeConstants; +import cn.iocoder.yudao.module.pay.enums.notify.PayNotifyStatusEnum; import cn.iocoder.yudao.module.pay.enums.notify.PayNotifyTypeEnum; -import cn.iocoder.yudao.module.pay.enums.order.PayOrderNotifyStatusEnum; +import cn.iocoder.yudao.module.pay.enums.order.PayOrderRefundStatusEnum; import cn.iocoder.yudao.module.pay.enums.order.PayOrderStatusEnum; import cn.iocoder.yudao.module.pay.enums.refund.PayRefundStatusEnum; -import cn.iocoder.yudao.module.pay.enums.refund.PayRefundTypeEnum; import cn.iocoder.yudao.module.pay.service.app.PayAppService; import cn.iocoder.yudao.module.pay.service.channel.PayChannelService; import cn.iocoder.yudao.module.pay.service.notify.PayNotifyService; @@ -38,8 +38,11 @@ import org.springframework.transaction.annotation.Transactional; import org.springframework.validation.annotation.Validated; import javax.annotation.Resource; +import java.time.LocalDateTime; import java.util.List; -import java.util.Objects; + +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; +import static cn.iocoder.yudao.framework.common.util.json.JsonUtils.toJsonString; /** * 退款订单 Service 实现类 @@ -59,8 +62,6 @@ public class PayRefundServiceImpl implements PayRefundService { @Resource private PayRefundMapper refundMapper; - @Resource - private PayOrderMapper orderMapper; // TODO @jason:需要改成不直接操作 db; @Resource private PayOrderService orderService; @@ -80,7 +81,7 @@ public class PayRefundServiceImpl implements PayRefundService { @Override public Long getRefundCountByAppId(Long appId) { - return refundMapper.selectCountByApp(appId); + return refundMapper.selectCountByAppId(appId); } @Override @@ -96,82 +97,82 @@ public class PayRefundServiceImpl implements PayRefundService { @Override @Transactional(rollbackFor = Exception.class) public Long createPayRefund(PayRefundCreateReqDTO reqDTO) { - // 获得 PayOrderDO - PayOrderDO order = orderService.getOrder(reqDTO.getPayOrderId()); - // 校验订单是否存在 - if (Objects.isNull(order) ) { - throw ServiceExceptionUtil.exception(ErrorCodeConstants.PAY_ORDER_NOT_FOUND); - } - // 校验 App - PayAppDO app = appService.validPayApp(order.getAppId()); - // 校验支付渠道是否有效 + // 1.1 校验 App + PayAppDO app = appService.validPayApp(reqDTO.getAppId()); + // 1.2 校验支付订单 + PayOrderDO order = validatePayOrderCanRefund(reqDTO); + // 1.3 校验支付渠道是否有效 PayChannelDO channel = channelService.validPayChannel(order.getChannelId()); - // 校验支付客户端是否正确初始化 PayClient client = payClientFactory.getPayClient(channel.getId()); if (client == null) { log.error("[refund][渠道编号({}) 找不到对应的支付客户端]", channel.getId()); - throw ServiceExceptionUtil.exception(ErrorCodeConstants.PAY_CHANNEL_CLIENT_NOT_FOUND); + throw exception(ErrorCodeConstants.PAY_CHANNEL_CLIENT_NOT_FOUND); + } + // 1.4 校验退款订单是否已经存在 + PayRefundDO refund = refundMapper.selectByAppIdAndMerchantRefundId( + app.getId(), reqDTO.getMerchantRefundId()); + if (refund != null) { + throw exception(ErrorCodeConstants.PAY_REFUND_EXISTS); } - // TODO 芋艿:待实现 - String merchantRefundId = "rrr" + RandomUtil.randomNumbers(16); - - // 校验退款的条件 - validatePayRefund(reqDTO, order); - // 退款类型 - PayRefundTypeEnum refundType = PayRefundTypeEnum.SOME; - if (Objects.equals(reqDTO.getPrice(), order.getPrice())) { - refundType = PayRefundTypeEnum.ALL; - } - PayOrderExtensionDO orderExtensionDO = orderExtensionService.getOrderExtension(order.getSuccessExtensionId()); - PayRefundDO payRefundDO = refundMapper.selectByTradeNoAndMerchantRefundNo(orderExtensionDO.getNo(), - merchantRefundId); // TODO 芋艿:需要优化 - if (Objects.nonNull(payRefundDO)) { - // 退款订单已经提交过。 - //TODO 校验相同退款单的金额 - // TODO @jason:咱要不封装一个 ObjectUtils.equalsAny - if (Objects.equals(PayRefundStatusEnum.SUCCESS.getStatus(), payRefundDO.getStatus())) { - //已成功退款 - throw ServiceExceptionUtil.exception(ErrorCodeConstants.PAY_REFUND_SUCCEED); - } - //可以重复提交,保证 退款请求号 一致,由渠道保证幂等 - } else { - // 成功,插入退款单 状态为生成.没有和渠道交互 - // TODO @jason:搞到 convert 里。一些额外的自动,手动 set 下; - payRefundDO = PayRefundDO.builder() - .appId(order.getAppId()) - .channelOrderNo(order.getChannelOrderNo()) - .channelCode(order.getChannelCode()) - .channelId(order.getChannelId()) - .orderId(order.getId()) - .merchantRefundId(merchantRefundId) - .notifyUrl(app.getRefundNotifyUrl()) - .payPrice(order.getPrice()) - .refundPrice(reqDTO.getPrice()) - .userIp(reqDTO.getUserIp()) - .merchantOrderId(order.getMerchantOrderId()) - .no(orderExtensionDO.getNo()) - .status(PayRefundStatusEnum.WAITING.getStatus()) - .reason(reqDTO.getReason()) - .notifyStatus(PayOrderNotifyStatusEnum.NO.getStatus()) - .type(refundType.getStatus()) - .build(); - refundMapper.insert(payRefundDO); - } - // TODO @jason:搞到 convert 里。一些额外的自动,手动 set 下; + // 2.1 插入退款单 + refund = PayRefundConvert.INSTANCE.convert(reqDTO) + .setNo(generateRefundNo()).setOrderId(order.getId()) + .setChannelId(order.getChannelId()).setChannelCode(order.getChannelCode()) + // 商户相关的字段 + .setNotifyUrl(app.getRefundNotifyUrl()).setNotifyStatus(PayNotifyStatusEnum.WAITING.getStatus()) + // 渠道相关字段 + .setChannelOrderNo(order.getChannelOrderNo()) + // 退款相关字段 + .setStatus(PayRefundStatusEnum.WAITING.getStatus()) + .setPayPrice(order.getPrice()).setRefundPrice(reqDTO.getPrice()); + refundMapper.insert(refund); + // 2.2 向渠道发起退款申请 + PayOrderExtensionDO orderExtension = orderExtensionService.getOrderExtension(order.getSuccessExtensionId()); PayRefundUnifiedReqDTO unifiedReqDTO = new PayRefundUnifiedReqDTO(); unifiedReqDTO.setPrice(reqDTO.getPrice()) - .setOutTradeNo(orderExtensionDO.getNo()) - .setOutRefundNo(merchantRefundId) // TODO 芋艿:需要优化 + .setOutTradeNo(orderExtension.getNo()) + .setOutRefundNo(refund.getNo()) .setNotifyUrl(genChannelPayNotifyUrl(channel)) // TODO 芋艿:优化下 notifyUrl .setReason(reqDTO.getReason()); - // 向渠道发起退款申请 - client.unifiedRefund(unifiedReqDTO); - // 检查是否失败,失败抛出业务异常。 - // TODO 渠道的异常记录。 - // TODO @jason:可以先打个 warn log 哈; + PayRefundRespDTO refundRespDTO = client.unifiedRefund(unifiedReqDTO); // TODO 增加一个 channelErrorCode、channelErrorMsg 字段 + // 2.3 处理退款返回 + notifyRefund(channel, refundRespDTO); + // 成功在 退款回调中处理 - return payRefundDO.getId(); + return refund.getId(); + } + + /** + * 校验支付订单是否可以退款 + * + * @param reqDTO 退款申请信息 + * @return 支付订单 + */ + private PayOrderDO validatePayOrderCanRefund(PayRefundCreateReqDTO reqDTO) { + PayOrderDO order = orderService.getOrder(reqDTO.getAppId(), reqDTO.getMerchantOrderId()); + if (order == null) { + throw exception(ErrorCodeConstants.PAY_ORDER_NOT_FOUND); + } + // 校验状态,必须是支付状态 + if (!PayOrderStatusEnum.SUCCESS.getStatus().equals(order.getStatus())) { + throw exception(ErrorCodeConstants.PAY_ORDER_STATUS_IS_NOT_SUCCESS); + } + + // 是否已经全额退款 + if (PayOrderRefundStatusEnum.ALL.getStatus().equals(order.getRefundStatus())) { + throw exception(ErrorCodeConstants.PAY_REFUND_ALL_REFUNDED); + } + // 校验金额 退款金额不能大于原定的金额 + if (reqDTO.getPrice() + order.getRefundPrice() > order.getPrice()){ + throw exception(ErrorCodeConstants.PAY_REFUND_PRICE_EXCEED); + } + // 是否有退款中的订单 + if (refundMapper.selectCountByAppIdAndOrderId(reqDTO.getAppId(), order.getId(), + PayRefundStatusEnum.WAITING.getStatus()) > 0) { + throw exception(ErrorCodeConstants.PAY_REFUND_HAS_REFUNDING); + } + return order; } /** @@ -184,88 +185,80 @@ public class PayRefundServiceImpl implements PayRefundService { return payProperties.getCallbackUrl() + "/" + channel.getId(); } + private String generateRefundNo() { +// wx +// 2014 +// 10 +// 27 +// 20 +// 09 +// 39 +// 5522657 +// a690389285100 + // 目前的算法 + // 时间序列,年月日时分秒 14 位 + // 纯随机,6 位 TODO 芋艿:此处估计是会有问题的,后续在调整 + return DateUtil.format(LocalDateTime.now(), "yyyyMMddHHmmss") + // 时间序列 + RandomUtil.randomInt(100000, 999999) // 随机。为什么是这个范围,因为偷懒 + ; + } + @Override @Transactional(rollbackFor = Exception.class) - public void notifyPayRefund(Long channelId, PayRefundRespDTO notify) { + public void notifyRefund(Long channelId, PayRefundRespDTO notify) { + // 校验支付渠道是否有效 + channelService.validPayChannel(channelId); + // 通知结果 + // 校验支付渠道是否有效 - // TODO 芋艿:需要重构下这块的逻辑 PayChannelDO channel = channelService.validPayChannel(channelId); - if (PayRefundStatusRespEnum.isSuccess(notify.getStatus())) { - payRefundSuccess(notify); - } else { - // TODO @jason:那这里可以考虑打个 error logger @芋艿 微信是否存在支付异常通知 - } - } - - private void payRefundSuccess(PayRefundRespDTO refundNotify) { - // 校验退款单存在 - PayRefundDO refundDO = null; // TODO 芋艿:临时注释 -// PayRefundDO refundDO = refundMapper.selectByTradeNoAndMerchantRefundNo(refundNotify.getTradeNo(), -// refundNotify.getReqNo()); - if (refundDO == null) { - // TODO 芋艿:临时注释 -// log.error("[payRefundSuccess][不存在 seqNo 为{} 的支付退款单]", refundNotify.getReqNo()); - throw ServiceExceptionUtil.exception(ErrorCodeConstants.PAY_REFUND_NOT_FOUND); - } - - // 得到已退金额 - PayOrderDO payOrderDO = orderService.getOrder(refundDO.getOrderId()); - Long refundedAmount = payOrderDO.getRefundPrice(); - - PayOrderStatusEnum orderStatus = PayOrderStatusEnum.SUCCESS; - if(Objects.equals(payOrderDO.getPrice(), refundedAmount+ refundDO.getRefundPrice())){ - //支付金额 = 已退金额 + 本次退款金额。 - orderStatus = PayOrderStatusEnum.CLOSED; - } - // 更新支付订单 - PayOrderDO updateOrderDO = new PayOrderDO(); - updateOrderDO.setId(refundDO.getOrderId()) - .setRefundPrice(refundedAmount + refundDO.getRefundPrice()) - .setStatus(orderStatus.getStatus()) - .setRefundTimes(payOrderDO.getRefundTimes() + 1) - .setRefundStatus(refundDO.getType()); - orderMapper.updateById(updateOrderDO); - // 更新退款订单 - PayRefundDO updateRefundDO = new PayRefundDO(); - updateRefundDO.setId(refundDO.getId()) - .setSuccessTime(refundNotify.getSuccessTime()) - // TODO 芋艿:如下两行,临时注释 -// .setChannelRefundNo(refundNotify.getChannelOrderNo()) -// .setNo(refundNotify.getTradeNo()) - .setStatus(PayRefundStatusEnum.SUCCESS.getStatus()); - refundMapper.updateById(updateRefundDO); - - // 插入退款通知记录 - // TODO 通知商户成功或者失败. 现在通知似乎没有实现, 只是回调 - notifyService.createPayNotifyTask(PayNotifyTaskCreateReqDTO.builder() - .type(PayNotifyTypeEnum.REFUND.getType()).dataId(refundDO.getId()).build()); + TenantUtils.execute(channel.getTenantId(), () -> notifyRefund(channel, notify)); } - /** - * 校验是否进行退款 - * - * @param reqDTO 退款申请信息 - * @param order 原始支付订单信息 - */ - private void validatePayRefund(PayRefundCreateReqDTO reqDTO, PayOrderDO order) { - // 校验状态,必须是支付状态 - if (!PayOrderStatusEnum.SUCCESS.getStatus().equals(order.getStatus())) { - throw ServiceExceptionUtil.exception(ErrorCodeConstants.PAY_ORDER_STATUS_IS_NOT_SUCCESS); + private void notifyRefund(PayChannelDO channel, PayRefundRespDTO notify) { + if (PayRefundStatusRespEnum.isSuccess(notify.getStatus())) { + notifyRefundSuccess(channel, notify); + } else { + notifyRefundFailure(channel, notify); } - // 是否已经全额退款 - if (PayRefundTypeEnum.ALL.getStatus().equals(order.getRefundStatus())) { - throw ServiceExceptionUtil.exception(ErrorCodeConstants.PAY_REFUND_ALL_REFUNDED); + } + + private void notifyRefundSuccess(PayChannelDO channel, PayRefundRespDTO notify) { + // 1.1 查询 PayRefundDO + PayRefundDO refund = refundMapper.selectByAppIdAndNo( + channel.getAppId(), notify.getOutRefundNo()); + if (refund == null) { + throw exception(ErrorCodeConstants.PAY_REFUND_NOT_FOUND); } - // 校验金额 退款金额不能大于 原定的金额 - if (reqDTO.getPrice() + order.getRefundPrice() > order.getPrice()){ - throw ServiceExceptionUtil.exception(ErrorCodeConstants.PAY_PRICE_PRICE_EXCEED); + if (PayRefundStatusEnum.isSuccess(refund.getStatus())) { // 如果已经是成功,直接返回,不用重复更新 + return; } - // 校验渠道订单号 - if (StrUtil.isEmpty(order.getChannelOrderNo())) { - throw ServiceExceptionUtil.exception(ErrorCodeConstants.PAY_REFUND_CHN_ORDER_NO_IS_NULL); + if (!PayRefundStatusEnum.WAITING.getStatus().equals(refund.getStatus())) { + throw exception(ErrorCodeConstants.PAY_REFUND_STATUS_IS_NOT_WAITING); } - //TODO 退款的期限 退款次数的控制 + + // 1.2 更新 PayRefundDO + PayRefundDO updateRefundObj = new PayRefundDO() + .setSuccessTime(notify.getSuccessTime()) + .setChannelRefundNo(notify.getChannelRefundNo()) + .setStatus(PayRefundStatusEnum.SUCCESS.getStatus()) + .setChannelNotifyData(toJsonString(notify)); + int updateCounts = refundMapper.updateByIdAndStatus(refund.getId(), refund.getStatus(), updateRefundObj); + if (updateCounts == 0) { // 校验状态,必须是等待状态 + throw exception(ErrorCodeConstants.PAY_REFUND_STATUS_IS_NOT_WAITING); + } + + // 2. 更新订单 + orderService.updateOrderRefundPrice(refund.getOrderId(), refund.getRefundPrice()); + + // 3. 插入退款通知记录 + notifyService.createPayNotifyTask(PayNotifyTaskCreateReqDTO.builder() + .type(PayNotifyTypeEnum.REFUND.getType()).dataId(refund.getId()).build()); + } + + private void notifyRefundFailure(PayChannelDO channel, PayRefundRespDTO notify) { + // TODO 芋艿:未实现 } } diff --git a/yudao-module-pay/yudao-module-pay-biz/src/test/java/cn/iocoder/yudao/module/pay/service/order/PayOrderServiceTest.java b/yudao-module-pay/yudao-module-pay-biz/src/test/java/cn/iocoder/yudao/module/pay/service/order/PayOrderServiceTest.java index 7d62c3cc4..8048e38ca 100755 --- a/yudao-module-pay/yudao-module-pay-biz/src/test/java/cn/iocoder/yudao/module/pay/service/order/PayOrderServiceTest.java +++ b/yudao-module-pay/yudao-module-pay-biz/src/test/java/cn/iocoder/yudao/module/pay/service/order/PayOrderServiceTest.java @@ -13,7 +13,7 @@ import cn.iocoder.yudao.module.pay.dal.dataobject.order.PayOrderDO; import cn.iocoder.yudao.module.pay.dal.mysql.order.PayOrderMapper; import cn.iocoder.yudao.module.pay.enums.order.PayOrderNotifyStatusEnum; import cn.iocoder.yudao.module.pay.enums.order.PayOrderStatusEnum; -import cn.iocoder.yudao.module.pay.enums.refund.PayRefundTypeEnum; +import cn.iocoder.yudao.module.pay.enums.order.PayOrderRefundStatusEnum; import cn.iocoder.yudao.module.pay.service.app.PayAppService; import cn.iocoder.yudao.module.pay.service.channel.PayChannelService; import cn.iocoder.yudao.module.pay.service.notify.PayNotifyService; @@ -85,7 +85,7 @@ public class PayOrderServiceTest extends BaseDbUnitTest { o.setSuccessTime(LocalDateTime.of(2018, 1, 1, 10, 10, 2)); o.setNotifyTime(LocalDateTime.of(2018, 1, 1, 10, 10, 15)); o.setSuccessExtensionId(1L); - o.setRefundStatus(PayRefundTypeEnum.NO.getStatus()); + o.setRefundStatus(PayOrderRefundStatusEnum.NO.getStatus()); o.setRefundTimes(0); o.setRefundPrice(0L); o.setChannelUserId("1008611"); @@ -106,7 +106,7 @@ public class PayOrderServiceTest extends BaseDbUnitTest { // 测试 status 不匹配 orderMapper.insert(cloneIgnoreId(dbOrder, o -> o.setStatus(PayOrderStatusEnum.CLOSED.getStatus()))); // 测试 refundStatus 不匹配 - orderMapper.insert(cloneIgnoreId(dbOrder, o -> o.setRefundStatus(PayRefundTypeEnum.ALL.getStatus()))); + orderMapper.insert(cloneIgnoreId(dbOrder, o -> o.setRefundStatus(PayOrderRefundStatusEnum.ALL.getStatus()))); // 测试 createTime 不匹配 orderMapper.insert(cloneIgnoreId(dbOrder, o -> o.setCreateTime(LocalDateTime.of(2019, 1, 1, 10, 10, 1)))); @@ -118,7 +118,7 @@ public class PayOrderServiceTest extends BaseDbUnitTest { reqVO.setMerchantOrderId(merchantOrderId); reqVO.setNotifyStatus(PayOrderNotifyStatusEnum.SUCCESS.getStatus()); reqVO.setStatus(PayOrderStatusEnum.SUCCESS.getStatus()); - reqVO.setRefundStatus(PayRefundTypeEnum.NO.getStatus()); + reqVO.setRefundStatus(PayOrderRefundStatusEnum.NO.getStatus()); reqVO.setCreateTime((new LocalDateTime[]{LocalDateTime.of(2018, 1, 1, 10, 1, 0), LocalDateTime.of(2018, 1, 1, 10, 1, 0)})); // 调用 PageResult pageResult = orderService.getOrderPage(reqVO); @@ -153,7 +153,7 @@ public class PayOrderServiceTest extends BaseDbUnitTest { o.setSuccessTime(LocalDateTime.of(2018, 1, 1, 10, 10, 2)); o.setNotifyTime(LocalDateTime.of(2018, 1, 1, 10, 10, 15)); o.setSuccessExtensionId(1L); - o.setRefundStatus(PayRefundTypeEnum.NO.getStatus()); + o.setRefundStatus(PayOrderRefundStatusEnum.NO.getStatus()); o.setRefundTimes(0); o.setRefundPrice(0L); o.setChannelUserId("1008611"); @@ -175,7 +175,7 @@ public class PayOrderServiceTest extends BaseDbUnitTest { // 测试 status 不匹配 orderMapper.insert(cloneIgnoreId(dbOrder, o -> o.setStatus(PayOrderStatusEnum.CLOSED.getStatus()))); // 测试 refundStatus 不匹配 - orderMapper.insert(cloneIgnoreId(dbOrder, o -> o.setRefundStatus(PayRefundTypeEnum.ALL.getStatus()))); + orderMapper.insert(cloneIgnoreId(dbOrder, o -> o.setRefundStatus(PayOrderRefundStatusEnum.ALL.getStatus()))); // 测试 createTime 不匹配 orderMapper.insert(cloneIgnoreId(dbOrder, o -> o.setCreateTime(LocalDateTime.of(2019, 1, 1, 10, 10, 1)))); @@ -187,7 +187,7 @@ public class PayOrderServiceTest extends BaseDbUnitTest { reqVO.setMerchantOrderId(merchantOrderId); reqVO.setNotifyStatus(PayOrderNotifyStatusEnum.SUCCESS.getStatus()); reqVO.setStatus(PayOrderStatusEnum.SUCCESS.getStatus()); - reqVO.setRefundStatus(PayRefundTypeEnum.NO.getStatus()); + reqVO.setRefundStatus(PayOrderRefundStatusEnum.NO.getStatus()); reqVO.setCreateTime((new LocalDateTime[]{LocalDateTime.of(2018, 1, 1, 10, 1, 0), LocalDateTime.of(2018, 1, 1, 10, 1, 0)})); // 调用 diff --git a/yudao-module-pay/yudao-module-pay-biz/src/test/java/cn/iocoder/yudao/module/pay/service/refund/PayRefundServiceTest.java b/yudao-module-pay/yudao-module-pay-biz/src/test/java/cn/iocoder/yudao/module/pay/service/refund/PayRefundServiceTest.java index 8365f1ab6..480256c07 100755 --- a/yudao-module-pay/yudao-module-pay-biz/src/test/java/cn/iocoder/yudao/module/pay/service/refund/PayRefundServiceTest.java +++ b/yudao-module-pay/yudao-module-pay-biz/src/test/java/cn/iocoder/yudao/module/pay/service/refund/PayRefundServiceTest.java @@ -11,7 +11,7 @@ import cn.iocoder.yudao.module.pay.dal.dataobject.refund.PayRefundDO; import cn.iocoder.yudao.module.pay.dal.mysql.refund.PayRefundMapper; import cn.iocoder.yudao.module.pay.enums.order.PayOrderNotifyStatusEnum; import cn.iocoder.yudao.module.pay.enums.refund.PayRefundStatusEnum; -import cn.iocoder.yudao.module.pay.enums.refund.PayRefundTypeEnum; +import cn.iocoder.yudao.module.pay.enums.order.PayOrderRefundStatusEnum; import cn.iocoder.yudao.module.pay.service.app.PayAppService; import cn.iocoder.yudao.module.pay.service.channel.PayChannelService; import cn.iocoder.yudao.module.pay.service.notify.PayNotifyService; @@ -64,11 +64,11 @@ public class PayRefundServiceTest extends BaseDbUnitTest { o.setOrderId(1L); o.setNo("OT0000001"); o.setMerchantOrderId("MOT0000001"); - o.setMerchantRefundNo("MRF0000001"); + o.setMerchantRefundId("MRF0000001"); o.setNotifyUrl("https://www.cancanzi.com"); o.setNotifyStatus(PayOrderNotifyStatusEnum.SUCCESS.getStatus()); o.setStatus(PayRefundStatusEnum.SUCCESS.getStatus()); - o.setType(PayRefundTypeEnum.SOME.getStatus()); + o.setType(PayOrderRefundStatusEnum.PART.getStatus()); o.setPayPrice(100); o.setRefundPrice(500); o.setReason("就是想退款了,你有意见吗"); @@ -77,10 +77,7 @@ public class PayRefundServiceTest extends BaseDbUnitTest { o.setChannelRefundNo("CHR0000001"); o.setChannelErrorCode(""); o.setChannelErrorMsg(""); - o.setChannelExtras(""); - o.setExpireTime(LocalDateTime.of(2021, 1, 1, 10, 10, 30)); o.setSuccessTime(LocalDateTime.of(2021, 1, 1, 10, 10, 15)); - o.setNotifyTime(LocalDateTime.of(2021, 1, 1, 10, 10, 20)); o.setCreateTime(LocalDateTime.of(2021, 1, 1, 10, 10, 10)); o.setUpdateTime(LocalDateTime.of(2021, 1, 1, 10, 10, 35)); }); @@ -90,14 +87,14 @@ public class PayRefundServiceTest extends BaseDbUnitTest { // 测试 channelCode 不匹配 refundMapper.insert(cloneIgnoreId(dbRefund, o -> o.setChannelCode(PayChannelEnum.ALIPAY_APP.getCode()))); // 测试 merchantRefundNo 不匹配 - refundMapper.insert(cloneIgnoreId(dbRefund, o -> o.setMerchantRefundNo("MRF1111112"))); + refundMapper.insert(cloneIgnoreId(dbRefund, o -> o.setMerchantRefundId("MRF1111112"))); // 测试 notifyStatus 不匹配 refundMapper.insert( cloneIgnoreId(dbRefund, o -> o.setNotifyStatus(PayOrderNotifyStatusEnum.FAILURE.getStatus()))); // 测试 status 不匹配 - refundMapper.insert(cloneIgnoreId(dbRefund, o -> o.setStatus(PayRefundStatusEnum.CLOSE.getStatus()))); + refundMapper.insert(cloneIgnoreId(dbRefund, o -> o.setStatus(PayRefundStatusEnum.FAILURE.getStatus()))); // 测试 type 不匹配 - refundMapper.insert(cloneIgnoreId(dbRefund, o -> o.setType(PayRefundTypeEnum.ALL.getStatus()))); + refundMapper.insert(cloneIgnoreId(dbRefund, o -> o.setType(PayOrderRefundStatusEnum.ALL.getStatus()))); // 测试 createTime 不匹配 refundMapper.insert(cloneIgnoreId(dbRefund, o -> o.setCreateTime(LocalDateTime.of(2022, 1, 1, 10, 10, 10)))); @@ -108,7 +105,7 @@ public class PayRefundServiceTest extends BaseDbUnitTest { reqVO.setMerchantRefundNo("MRF0000001"); reqVO.setNotifyStatus(PayOrderNotifyStatusEnum.SUCCESS.getStatus()); reqVO.setStatus(PayRefundStatusEnum.SUCCESS.getStatus()); - reqVO.setType(PayRefundTypeEnum.SOME.getStatus()); + reqVO.setType(PayOrderRefundStatusEnum.PART.getStatus()); reqVO.setCreateTime((new LocalDateTime[]{LocalDateTime.of(2021, 1, 1, 10, 10, 10), LocalDateTime.of(2021, 1, 1, 10, 10, 12)})); // 调用 @@ -129,11 +126,11 @@ public class PayRefundServiceTest extends BaseDbUnitTest { o.setOrderId(1L); o.setNo("OT0000001"); o.setMerchantOrderId("MOT0000001"); - o.setMerchantRefundNo("MRF0000001"); + o.setMerchantRefundId("MRF0000001"); o.setNotifyUrl("https://www.cancanzi.com"); o.setNotifyStatus(PayOrderNotifyStatusEnum.SUCCESS.getStatus()); o.setStatus(PayRefundStatusEnum.SUCCESS.getStatus()); - o.setType(PayRefundTypeEnum.SOME.getStatus()); + o.setType(PayOrderRefundStatusEnum.PART.getStatus()); o.setPayPrice(100); o.setRefundPrice(500); o.setReason("就是想退款了,你有意见吗"); @@ -142,10 +139,7 @@ public class PayRefundServiceTest extends BaseDbUnitTest { o.setChannelRefundNo("CHR0000001"); o.setChannelErrorCode(""); o.setChannelErrorMsg(""); - o.setChannelExtras(""); - o.setExpireTime(LocalDateTime.of(2021, 1, 1, 10, 10, 30)); o.setSuccessTime(LocalDateTime.of(2021, 1, 1, 10, 10, 15)); - o.setNotifyTime(LocalDateTime.of(2021, 1, 1, 10, 10, 20)); o.setCreateTime(LocalDateTime.of(2021, 1, 1, 10, 10, 10)); o.setUpdateTime(LocalDateTime.of(2021, 1, 1, 10, 10, 35)); }); @@ -155,14 +149,14 @@ public class PayRefundServiceTest extends BaseDbUnitTest { // 测试 channelCode 不匹配 refundMapper.insert(cloneIgnoreId(dbRefund, o -> o.setChannelCode(PayChannelEnum.ALIPAY_APP.getCode()))); // 测试 merchantRefundNo 不匹配 - refundMapper.insert(cloneIgnoreId(dbRefund, o -> o.setMerchantRefundNo("MRF1111112"))); + refundMapper.insert(cloneIgnoreId(dbRefund, o -> o.setMerchantRefundId("MRF1111112"))); // 测试 notifyStatus 不匹配 refundMapper.insert( cloneIgnoreId(dbRefund, o -> o.setNotifyStatus(PayOrderNotifyStatusEnum.FAILURE.getStatus()))); // 测试 status 不匹配 - refundMapper.insert(cloneIgnoreId(dbRefund, o -> o.setStatus(PayRefundStatusEnum.CLOSE.getStatus()))); + refundMapper.insert(cloneIgnoreId(dbRefund, o -> o.setStatus(PayRefundStatusEnum.FAILURE.getStatus()))); // 测试 type 不匹配 - refundMapper.insert(cloneIgnoreId(dbRefund, o -> o.setType(PayRefundTypeEnum.ALL.getStatus()))); + refundMapper.insert(cloneIgnoreId(dbRefund, o -> o.setType(PayOrderRefundStatusEnum.ALL.getStatus()))); // 测试 createTime 不匹配 refundMapper.insert(cloneIgnoreId(dbRefund, o -> o.setCreateTime(LocalDateTime.of(2022, 1, 1, 10, 10, 10)))); @@ -174,7 +168,7 @@ public class PayRefundServiceTest extends BaseDbUnitTest { reqVO.setMerchantRefundNo("MRF0000001"); reqVO.setNotifyStatus(PayOrderNotifyStatusEnum.SUCCESS.getStatus()); reqVO.setStatus(PayRefundStatusEnum.SUCCESS.getStatus()); - reqVO.setType(PayRefundTypeEnum.SOME.getStatus()); + reqVO.setType(PayOrderRefundStatusEnum.PART.getStatus()); reqVO.setCreateTime((new LocalDateTime[]{LocalDateTime.of(2021, 1, 1, 10, 10, 10), LocalDateTime.of(2021, 1, 1, 10, 10, 12)})); // 调用 From a6437b7cc553ba4ac3f766a8ff1fe49ae6580a40 Mon Sep 17 00:00:00 2001 From: YunaiV Date: Sat, 15 Jul 2023 21:18:36 +0800 Subject: [PATCH 03/10] =?UTF-8?q?mall=20+=20pay=EF=BC=9A=201.=20=E5=90=8E?= =?UTF-8?q?=E5=8F=B0=E7=9A=84=E6=94=B6=E9=93=B6=E5=8F=B0=EF=BC=8C=E6=8E=A5?= =?UTF-8?q?=E5=85=A5=E5=BE=AE=E4=BF=A1=E6=89=AB=E7=A0=81=E6=94=AF=E4=BB=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/assets/images/pay/icon/wx_bar.svg | 1 + yudao-ui-admin/src/utils/constants.js | 4 +++ .../src/views/pay/cashier/index.vue | 33 +++++++++++++++++-- 3 files changed, 36 insertions(+), 2 deletions(-) create mode 100644 yudao-ui-admin/src/assets/images/pay/icon/wx_bar.svg diff --git a/yudao-ui-admin/src/assets/images/pay/icon/wx_bar.svg b/yudao-ui-admin/src/assets/images/pay/icon/wx_bar.svg new file mode 100644 index 000000000..11292e6e7 --- /dev/null +++ b/yudao-ui-admin/src/assets/images/pay/icon/wx_bar.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/yudao-ui-admin/src/utils/constants.js b/yudao-ui-admin/src/utils/constants.js index 10819e385..801ca76cc 100644 --- a/yudao-ui-admin/src/utils/constants.js +++ b/yudao-ui-admin/src/utils/constants.js @@ -132,6 +132,10 @@ export const PayChannelEnum = { "code": "wx_app", "name": "微信 APP 支付" }, + WX_BAR: { + "code": "wx_bar", + "name": "微信条码支付" + }, ALIPAY_PC: { "code": "alipay_pc", "name": "支付宝 PC 网站支付" diff --git a/yudao-ui-admin/src/views/pay/cashier/index.vue b/yudao-ui-admin/src/views/pay/cashier/index.vue index ef73ed4db..41d8ce159 100644 --- a/yudao-ui-admin/src/views/pay/cashier/index.vue +++ b/yudao-ui-admin/src/views/pay/cashier/index.vue @@ -26,7 +26,7 @@
-
+
{{ channel.name }}
@@ -125,6 +125,10 @@ export default { name: '微信 App 支付', icon: require("@/assets/images/pay/icon/wx_app.svg"), code: "wx_app" + }, { + name: '微信条码支付', + icon: require("@/assets/images/pay/icon/wx_bar.svg"), + code: "wx_bar" }, { name: '模拟支付', icon: require("@/assets/images/pay/icon/mock.svg"), @@ -195,6 +199,15 @@ export default { } return; } + if (channelCode === PayChannelEnum.WX_BAR.code) { + this.barCode = { + channelCode: channelCode, + value: '', + title: '“微信”条码支付', + visible: true + } + return; + } // 默认的提交处理 this.submit0(channelCode) @@ -207,7 +220,16 @@ export default { returnUrl: location.href, // 支付成功后,支付渠道跳转回当前页;再由当前页,跳转回 {@link returnUrl} 对应的地址 ...this.buildSubmitParam(channelCode) }).then(response => { + // 直接返回已支付的情况,例如说扫码支付 const data = response.data + if (data.status === PayOrderStatusEnum.SUCCESS.status) { + this.clearQueryInterval(); + this.$message.success('支付成功!'); + this.goReturnUrl(); + return + } + + // 展示对应的界面 if (data.displayMode === PayDisplayModeEnum.URL.mode) { this.displayUrl(channelCode, data) } else if (data.displayMode === PayDisplayModeEnum.QR_CODE.mode) { @@ -230,11 +252,18 @@ export default { } } } + // ② 微信 BarCode 支付时,需要传递 authCode 条形码 + if (channelCode === PayChannelEnum.WX_BAR.code) { + return { + "channelExtras": { + "authCode": this.barCode.value + } + } + } return {} }, /** 提交支付后,URL 的展示形式 */ displayUrl(channelCode, data) { - // window.open(data.displayContent) location.href = data.displayContent this.submitLoading = false }, From ecdc39612f5f82f2280530e2be467ad8fa563649 Mon Sep 17 00:00:00 2001 From: YunaiV Date: Sat, 15 Jul 2023 23:14:42 +0800 Subject: [PATCH 04/10] =?UTF-8?q?mall=20+=20pay=EF=BC=9A=201.=20=E5=90=8E?= =?UTF-8?q?=E5=8F=B0=E7=9A=84=E6=94=B6=E9=93=B6=E5=8F=B0=EF=BC=8C=E6=8E=A5?= =?UTF-8?q?=E5=85=A5=E5=BE=AE=E4=BF=A1=E6=89=AB=E7=A0=81=E6=94=AF=E4=BB=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../pay/core/client/impl/weixin/WxNativePayClient.java | 4 ++-- yudao-ui-admin/src/assets/images/pay/icon/wx_native.svg | 1 + yudao-ui-admin/src/utils/constants.js | 4 ++++ yudao-ui-admin/src/views/pay/cashier/index.vue | 4 ++++ 4 files changed, 11 insertions(+), 2 deletions(-) create mode 100644 yudao-ui-admin/src/assets/images/pay/icon/wx_native.svg diff --git a/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/weixin/WxNativePayClient.java b/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/weixin/WxNativePayClient.java index feaf0b9f7..002a85ec8 100644 --- a/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/weixin/WxNativePayClient.java +++ b/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/weixin/WxNativePayClient.java @@ -50,7 +50,7 @@ public class WxNativePayClient extends AbstractWxPayClient { WxPayNativeOrderResult response = client.createOrder(request); // 转换结果 - return new PayOrderUnifiedRespDTO(PayOrderDisplayModeEnum.QR_CODE_URL.getMode(), + return new PayOrderUnifiedRespDTO(PayOrderDisplayModeEnum.QR_CODE.getMode(), response.getCodeUrl()); } @@ -68,7 +68,7 @@ public class WxNativePayClient extends AbstractWxPayClient { WxPayNativeOrderResult response = client.createOrderV3(TradeTypeEnum.NATIVE, request); // 转换结果 - return new PayOrderUnifiedRespDTO(PayOrderDisplayModeEnum.QR_CODE_URL.getMode(), + return new PayOrderUnifiedRespDTO(PayOrderDisplayModeEnum.QR_CODE.getMode(), response.getCodeUrl()); } diff --git a/yudao-ui-admin/src/assets/images/pay/icon/wx_native.svg b/yudao-ui-admin/src/assets/images/pay/icon/wx_native.svg new file mode 100644 index 000000000..bf3ba2b61 --- /dev/null +++ b/yudao-ui-admin/src/assets/images/pay/icon/wx_native.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/yudao-ui-admin/src/utils/constants.js b/yudao-ui-admin/src/utils/constants.js index 801ca76cc..781b0a0a9 100644 --- a/yudao-ui-admin/src/utils/constants.js +++ b/yudao-ui-admin/src/utils/constants.js @@ -132,6 +132,10 @@ export const PayChannelEnum = { "code": "wx_app", "name": "微信 APP 支付" }, + WX_NATIVE: { + "code": "wx_native", + "name": "微信扫码支付" + }, WX_BAR: { "code": "wx_bar", "name": "微信条码支付" diff --git a/yudao-ui-admin/src/views/pay/cashier/index.vue b/yudao-ui-admin/src/views/pay/cashier/index.vue index 41d8ce159..90e5d647e 100644 --- a/yudao-ui-admin/src/views/pay/cashier/index.vue +++ b/yudao-ui-admin/src/views/pay/cashier/index.vue @@ -125,6 +125,10 @@ export default { name: '微信 App 支付', icon: require("@/assets/images/pay/icon/wx_app.svg"), code: "wx_app" + }, { + name: '微信扫码支付', + icon: require("@/assets/images/pay/icon/wx_native.svg"), + code: "wx_native" }, { name: '微信条码支付', icon: require("@/assets/images/pay/icon/wx_bar.svg"), From 7caf8e900f393c051abcc41c4f2abf4be30df87e Mon Sep 17 00:00:00 2001 From: YunaiV Date: Sun, 16 Jul 2023 19:24:29 +0800 Subject: [PATCH 05/10] =?UTF-8?q?mall=20+=20pay=EF=BC=9A=201.=20=E5=A2=9E?= =?UTF-8?q?=E5=8A=A0=E5=BE=AE=E4=BF=A1=20bar=20=E5=92=8C=20native=20?= =?UTF-8?q?=E7=9A=84=E9=9B=86=E6=88=90=E6=B5=8B=E8=AF=95=EF=BC=8C=E6=96=B9?= =?UTF-8?q?=E4=BE=BF=E8=B0=83=E8=AF=95=E9=85=8D=E7=BD=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../dto/refund/PayRefundUnifiedReqDTO.java | 10 +- .../impl/alipay/AbstractAlipayPayClient.java | 3 +- .../impl/weixin/AbstractWxPayClient.java | 51 ++++++++ .../client/impl/weixin/WxAppPayClient.java | 7 -- .../client/impl/weixin/WxBarPayClient.java | 7 -- .../client/impl/weixin/WxH5PayClient.java | 7 -- .../client/impl/weixin/WxNativePayClient.java | 11 +- .../client/impl/weixin/WxPubPayClient.java | 8 -- .../weixin/WxBarPayClientIntegrationTest.java | 111 ++++++++++++++++++ .../WxNativePayClientIntegrationTest.java | 85 ++++++++++++++ .../service/refund/PayRefundServiceImpl.java | 5 +- .../service/order/PayOrderServiceTest.java | 4 +- .../service/refund/PayRefundServiceTest.java | 7 -- 13 files changed, 265 insertions(+), 51 deletions(-) create mode 100644 yudao-framework/yudao-spring-boot-starter-biz-pay/src/test/java/cn/iocoder/yudao/framework/pay/core/client/impl/weixin/WxBarPayClientIntegrationTest.java create mode 100644 yudao-framework/yudao-spring-boot-starter-biz-pay/src/test/java/cn/iocoder/yudao/framework/pay/core/client/impl/weixin/WxNativePayClientIntegrationTest.java diff --git a/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/dto/refund/PayRefundUnifiedReqDTO.java b/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/dto/refund/PayRefundUnifiedReqDTO.java index d6f66ab63..4f5e203d2 100644 --- a/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/dto/refund/PayRefundUnifiedReqDTO.java +++ b/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/dto/refund/PayRefundUnifiedReqDTO.java @@ -45,12 +45,20 @@ public class PayRefundUnifiedReqDTO { @NotEmpty(message = "退款原因不能为空") private String reason; + /** + * 支付金额,单位:分 + * + * 目前微信支付在退款的时候,必须传递该字段 + */ + @NotNull(message = "支付金额不能为空") + @DecimalMin(value = "0", inclusive = false, message = "支付金额必须大于零") + private Integer payPrice; /** * 退款金额,单位:分 */ @NotNull(message = "退款金额不能为空") @DecimalMin(value = "0", inclusive = false, message = "支付金额必须大于零") - private Integer price; + private Integer refundPrice; /** * 退款结果的 notify 回调地址 diff --git a/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/alipay/AbstractAlipayPayClient.java b/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/alipay/AbstractAlipayPayClient.java index 657c64360..a252e6dc2 100644 --- a/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/alipay/AbstractAlipayPayClient.java +++ b/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/alipay/AbstractAlipayPayClient.java @@ -67,7 +67,7 @@ public abstract class AbstractAlipayPayClient extends AbstractPayClient params, String body) { diff --git a/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/weixin/WxAppPayClient.java b/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/weixin/WxAppPayClient.java index 0739ea9e2..9618d3eec 100644 --- a/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/weixin/WxAppPayClient.java +++ b/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/weixin/WxAppPayClient.java @@ -2,8 +2,6 @@ package cn.iocoder.yudao.framework.pay.core.client.impl.weixin; import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderUnifiedReqDTO; import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderUnifiedRespDTO; -import cn.iocoder.yudao.framework.pay.core.client.dto.refund.PayRefundRespDTO; -import cn.iocoder.yudao.framework.pay.core.client.dto.refund.PayRefundUnifiedReqDTO; import cn.iocoder.yudao.framework.pay.core.enums.channel.PayChannelEnum; import com.github.binarywang.wxpay.constant.WxPayConstants; import com.github.binarywang.wxpay.exception.WxPayException; @@ -20,11 +18,6 @@ public class WxAppPayClient extends AbstractWxPayClient { super.doInit(WxPayConstants.TradeType.APP); } - @Override - protected PayRefundRespDTO doUnifiedRefund(PayRefundUnifiedReqDTO reqDTO) throws Throwable { - return null; - } - @Override protected PayOrderUnifiedRespDTO doUnifiedOrderV2(PayOrderUnifiedReqDTO reqDTO) throws WxPayException { return null; diff --git a/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/weixin/WxBarPayClient.java b/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/weixin/WxBarPayClient.java index 3f2975cf2..b6c78ea5a 100644 --- a/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/weixin/WxBarPayClient.java +++ b/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/weixin/WxBarPayClient.java @@ -8,8 +8,6 @@ import cn.iocoder.yudao.framework.common.util.json.JsonUtils; import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderRespDTO; import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderUnifiedReqDTO; import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderUnifiedRespDTO; -import cn.iocoder.yudao.framework.pay.core.client.dto.refund.PayRefundRespDTO; -import cn.iocoder.yudao.framework.pay.core.client.dto.refund.PayRefundUnifiedReqDTO; import cn.iocoder.yudao.framework.pay.core.enums.channel.PayChannelEnum; import cn.iocoder.yudao.framework.pay.core.enums.order.PayOrderDisplayModeEnum; import cn.iocoder.yudao.framework.pay.core.enums.order.PayOrderStatusRespEnum; @@ -104,11 +102,6 @@ public class WxBarPayClient extends AbstractWxPayClient { return doUnifiedOrderV2(reqDTO); } - @Override - protected PayRefundRespDTO doUnifiedRefund(PayRefundUnifiedReqDTO reqDTO) { - return null; - } - // ========== 各种工具方法 ========== static String getAuthCode(PayOrderUnifiedReqDTO reqDTO) { diff --git a/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/weixin/WxH5PayClient.java b/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/weixin/WxH5PayClient.java index 33470f56a..b70706d24 100644 --- a/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/weixin/WxH5PayClient.java +++ b/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/weixin/WxH5PayClient.java @@ -2,8 +2,6 @@ package cn.iocoder.yudao.framework.pay.core.client.impl.weixin; import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderUnifiedReqDTO; import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderUnifiedRespDTO; -import cn.iocoder.yudao.framework.pay.core.client.dto.refund.PayRefundRespDTO; -import cn.iocoder.yudao.framework.pay.core.client.dto.refund.PayRefundUnifiedReqDTO; import cn.iocoder.yudao.framework.pay.core.enums.channel.PayChannelEnum; import com.github.binarywang.wxpay.constant.WxPayConstants; import com.github.binarywang.wxpay.exception.WxPayException; @@ -20,11 +18,6 @@ public class WxH5PayClient extends AbstractWxPayClient { super.doInit(WxPayConstants.TradeType.MWEB); } - @Override - protected PayRefundRespDTO doUnifiedRefund(PayRefundUnifiedReqDTO reqDTO) throws Throwable { - return null; - } - @Override protected PayOrderUnifiedRespDTO doUnifiedOrderV2(PayOrderUnifiedReqDTO reqDTO) throws WxPayException { return null; diff --git a/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/weixin/WxNativePayClient.java b/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/weixin/WxNativePayClient.java index 002a85ec8..b8abdb20d 100644 --- a/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/weixin/WxNativePayClient.java +++ b/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/weixin/WxNativePayClient.java @@ -2,8 +2,6 @@ package cn.iocoder.yudao.framework.pay.core.client.impl.weixin; import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderUnifiedReqDTO; import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderUnifiedRespDTO; -import cn.iocoder.yudao.framework.pay.core.client.dto.refund.PayRefundRespDTO; -import cn.iocoder.yudao.framework.pay.core.client.dto.refund.PayRefundUnifiedReqDTO; import cn.iocoder.yudao.framework.pay.core.enums.channel.PayChannelEnum; import cn.iocoder.yudao.framework.pay.core.enums.order.PayOrderDisplayModeEnum; import com.github.binarywang.wxpay.bean.order.WxPayNativeOrderResult; @@ -65,16 +63,11 @@ public class WxNativePayClient extends AbstractWxPayClient { request.setSceneInfo(new WxPayUnifiedOrderV3Request.SceneInfo().setPayerClientIp(reqDTO.getUserIp())); request.setNotifyUrl(reqDTO.getNotifyUrl()); // 执行请求 - WxPayNativeOrderResult response = client.createOrderV3(TradeTypeEnum.NATIVE, request); + String response = client.createOrderV3(TradeTypeEnum.NATIVE, request); // 转换结果 return new PayOrderUnifiedRespDTO(PayOrderDisplayModeEnum.QR_CODE.getMode(), - response.getCodeUrl()); - } - - @Override - protected PayRefundRespDTO doUnifiedRefund(PayRefundUnifiedReqDTO reqDTO) throws Throwable { - return null; + response); } } diff --git a/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/weixin/WxPubPayClient.java b/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/weixin/WxPubPayClient.java index eebd86efa..e0f8c9628 100644 --- a/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/weixin/WxPubPayClient.java +++ b/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/weixin/WxPubPayClient.java @@ -5,8 +5,6 @@ import cn.hutool.core.util.StrUtil; import cn.iocoder.yudao.framework.common.util.json.JsonUtils; import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderUnifiedReqDTO; import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderUnifiedRespDTO; -import cn.iocoder.yudao.framework.pay.core.client.dto.refund.PayRefundRespDTO; -import cn.iocoder.yudao.framework.pay.core.client.dto.refund.PayRefundUnifiedReqDTO; import cn.iocoder.yudao.framework.pay.core.enums.channel.PayChannelEnum; import cn.iocoder.yudao.framework.pay.core.enums.order.PayOrderDisplayModeEnum; import com.github.binarywang.wxpay.bean.order.WxPayMpOrderResult; @@ -83,12 +81,6 @@ public class WxPubPayClient extends AbstractWxPayClient { JsonUtils.toJsonString(response)); } - @Override - protected PayRefundRespDTO doUnifiedRefund(PayRefundUnifiedReqDTO reqDTO) { - // TODO 需要实现 - throw new UnsupportedOperationException(); - } - // ========== 各种工具方法 ========== static String getOpenid(PayOrderUnifiedReqDTO reqDTO) { diff --git a/yudao-framework/yudao-spring-boot-starter-biz-pay/src/test/java/cn/iocoder/yudao/framework/pay/core/client/impl/weixin/WxBarPayClientIntegrationTest.java b/yudao-framework/yudao-spring-boot-starter-biz-pay/src/test/java/cn/iocoder/yudao/framework/pay/core/client/impl/weixin/WxBarPayClientIntegrationTest.java new file mode 100644 index 000000000..d32879bc6 --- /dev/null +++ b/yudao-framework/yudao-spring-boot-starter-biz-pay/src/test/java/cn/iocoder/yudao/framework/pay/core/client/impl/weixin/WxBarPayClientIntegrationTest.java @@ -0,0 +1,111 @@ +package cn.iocoder.yudao.framework.pay.core.client.impl.weixin; + +import cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils; +import cn.iocoder.yudao.framework.common.util.json.JsonUtils; +import com.github.binarywang.wxpay.bean.request.WxPayMicropayRequest; +import com.github.binarywang.wxpay.bean.request.WxPayRefundRequest; +import com.github.binarywang.wxpay.bean.request.WxPayRefundV3Request; +import com.github.binarywang.wxpay.bean.result.WxPayMicropayResult; +import com.github.binarywang.wxpay.bean.result.WxPayRefundResult; +import com.github.binarywang.wxpay.bean.result.WxPayRefundV3Result; +import com.github.binarywang.wxpay.config.WxPayConfig; +import com.github.binarywang.wxpay.constant.WxPayConstants; +import com.github.binarywang.wxpay.exception.WxPayException; +import com.github.binarywang.wxpay.service.WxPayService; +import com.github.binarywang.wxpay.service.impl.WxPayServiceImpl; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; + +import java.time.Duration; + +import static cn.iocoder.yudao.framework.pay.core.client.impl.weixin.AbstractWxPayClient.formatDateV2; + +/** + * {@link WxBarPayClient} 的集成测试,用于快速调试微信条码支付 + * + * @author 芋道源码 + */ +@Disabled +public class WxBarPayClientIntegrationTest { + + @Test + public void testPayV2() throws WxPayException { + // 创建 config 配置 + WxPayConfig config = buildWxPayConfigV2(); + // 创建 WxPayService 客户端 + WxPayService client = new WxPayServiceImpl(); + client.setConfig(config); + + // 执行发起支付 + WxPayMicropayRequest request = WxPayMicropayRequest.newBuilder() + .outTradeNo(String.valueOf(System.currentTimeMillis())) + .body("测试支付-body") + .detail("测试支付-detail") + .totalFee(1) // 单位分 + .timeExpire(formatDateV2(LocalDateTimeUtils.addTime(Duration.ofMinutes(2)))) + .spbillCreateIp("127.0.0.1") + .authCode("131276541518138032") + .build(); + System.out.println("========= request =========="); + System.out.println(JsonUtils.toJsonPrettyString(request)); + WxPayMicropayResult response = client.micropay(request); + System.out.println("========= response =========="); + System.out.println(JsonUtils.toJsonPrettyString(response)); + } + + @Test + public void testRefundV2() throws WxPayException { + // 创建 config 配置 + WxPayConfig config = buildWxPayConfigV2(); + // 创建 WxPayService 客户端 + WxPayService client = new WxPayServiceImpl(); + client.setConfig(config); + + // 执行发起退款 + WxPayRefundRequest request = new WxPayRefundRequest() + .setOutTradeNo("1689504162805") + .setOutRefundNo(String.valueOf(System.currentTimeMillis())) + .setRefundFee(1) + .setRefundDesc("就是想退了") + .setTotalFee(1); + System.out.println("========= request =========="); + System.out.println(JsonUtils.toJsonPrettyString(request)); + WxPayRefundResult response = client.refund(request); + System.out.println("========= response =========="); + System.out.println(JsonUtils.toJsonPrettyString(response)); + } + + @Test + public void testRefundV3() throws WxPayException { + // 创建 config 配置 + WxPayConfig config = buildWxPayConfigV2(); + // 创建 WxPayService 客户端 + WxPayService client = new WxPayServiceImpl(); + client.setConfig(config); + + // 执行发起退款 + WxPayRefundV3Request request = new WxPayRefundV3Request() + .setOutTradeNo("1689506325635") + .setOutRefundNo(String.valueOf(System.currentTimeMillis())) + .setAmount(new WxPayRefundV3Request.Amount().setTotal(1).setRefund(1).setCurrency("CNY")) + .setReason("就是想退了"); + System.out.println("========= request =========="); + System.out.println(JsonUtils.toJsonPrettyString(request)); + WxPayRefundV3Result response = client.refundV3(request); + System.out.println("========= response =========="); + System.out.println(JsonUtils.toJsonPrettyString(response)); + } + + private WxPayConfig buildWxPayConfigV2() { + WxPayConfig config = new WxPayConfig(); + config.setAppId("wx62056c0d5e8db250"); + config.setMchId("1545083881"); + config.setMchKey("dS1ngeN63JLr3NRbvPH9AJy3MyUxZdim"); + config.setSignType(WxPayConstants.SignType.MD5); + config.setKeyPath("/Users/yunai/Downloads/wx_pay/apiclient_cert.p12"); + config.setPrivateCertPath("/Users/yunai/Downloads/wx_pay/apiclient_cert.pem"); + config.setPrivateKeyPath("/Users/yunai/Downloads/wx_pay/apiclient_key.pem"); + return config; + } + +} diff --git a/yudao-framework/yudao-spring-boot-starter-biz-pay/src/test/java/cn/iocoder/yudao/framework/pay/core/client/impl/weixin/WxNativePayClientIntegrationTest.java b/yudao-framework/yudao-spring-boot-starter-biz-pay/src/test/java/cn/iocoder/yudao/framework/pay/core/client/impl/weixin/WxNativePayClientIntegrationTest.java new file mode 100644 index 000000000..e411a6236 --- /dev/null +++ b/yudao-framework/yudao-spring-boot-starter-biz-pay/src/test/java/cn/iocoder/yudao/framework/pay/core/client/impl/weixin/WxNativePayClientIntegrationTest.java @@ -0,0 +1,85 @@ +package cn.iocoder.yudao.framework.pay.core.client.impl.weixin; + +import cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils; +import cn.iocoder.yudao.framework.common.util.json.JsonUtils; +import com.github.binarywang.wxpay.bean.request.WxPayRefundV3Request; +import com.github.binarywang.wxpay.bean.request.WxPayUnifiedOrderV3Request; +import com.github.binarywang.wxpay.bean.result.WxPayRefundV3Result; +import com.github.binarywang.wxpay.bean.result.enums.TradeTypeEnum; +import com.github.binarywang.wxpay.config.WxPayConfig; +import com.github.binarywang.wxpay.exception.WxPayException; +import com.github.binarywang.wxpay.service.WxPayService; +import com.github.binarywang.wxpay.service.impl.WxPayServiceImpl; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; + +import java.time.Duration; + +import static cn.iocoder.yudao.framework.pay.core.client.impl.weixin.AbstractWxPayClient.formatDateV3; + +/** + * {@link WxNativePayClient} 的集成测试,用于快速调试微信扫码支付 + * + * @author 芋道源码 + */ +@Disabled +public class WxNativePayClientIntegrationTest { + + @Test + public void testPayV3() throws WxPayException { + // 创建 config 配置 + WxPayConfig config = buildWxPayConfigV3(); + // 创建 WxPayService 客户端 + WxPayService client = new WxPayServiceImpl(); + client.setConfig(config); + + // 执行发起支付 + WxPayUnifiedOrderV3Request request = new WxPayUnifiedOrderV3Request() + .setOutTradeNo(String.valueOf(System.currentTimeMillis())) + .setDescription("测试支付-body") + .setAmount(new WxPayUnifiedOrderV3Request.Amount().setTotal(1)) // 单位分 + .setTimeExpire(formatDateV3(LocalDateTimeUtils.addTime(Duration.ofMinutes(2)))) + .setSceneInfo(new WxPayUnifiedOrderV3Request.SceneInfo().setPayerClientIp("127.0.0.1")) + .setNotifyUrl("http://127.0.0.1:48080"); + System.out.println("========= request =========="); + System.out.println(JsonUtils.toJsonPrettyString(request)); + String response = client.createOrderV3(TradeTypeEnum.NATIVE, request); + System.out.println("========= response =========="); + System.out.println(JsonUtils.toJsonPrettyString(response)); + } + + @Test + public void testRefundV3() throws WxPayException { + // 创建 config 配置 + WxPayConfig config = buildWxPayConfigV3(); + // 创建 WxPayService 客户端 + WxPayService client = new WxPayServiceImpl(); + client.setConfig(config); + + // 执行发起退款 + WxPayRefundV3Request request = new WxPayRefundV3Request() + .setOutTradeNo("1689506153043") + .setOutRefundNo(String.valueOf(System.currentTimeMillis())) + .setAmount(new WxPayRefundV3Request.Amount().setTotal(1).setRefund(1).setCurrency("CNY")) + .setReason("就是想退了"); + System.out.println("========= request =========="); + System.out.println(JsonUtils.toJsonPrettyString(request)); + WxPayRefundV3Result response = client.refundV3(request); + System.out.println("========= response =========="); + System.out.println(JsonUtils.toJsonPrettyString(response)); + } + + private WxPayConfig buildWxPayConfigV3() { + WxPayConfig config = new WxPayConfig(); + config.setAppId("wx62056c0d5e8db250"); + config.setMchId("1545083881"); + config.setMchKey("dS1ngeN63JLr3NRbvPH9AJy3MyUxZdim"); + config.setApiV3Key("459arNsYHl1mgkiO6H9ZH5KkhFXSxaA4"); +// config.setCertSerialNo(serialNo); + config.setKeyPath("/Users/yunai/Downloads/wx_pay/apiclient_cert.p12"); + config.setPrivateCertPath("/Users/yunai/Downloads/wx_pay/apiclient_cert.pem"); + config.setPrivateKeyPath("/Users/yunai/Downloads/wx_pay/apiclient_key.pem"); + return config; + } + +} diff --git a/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/refund/PayRefundServiceImpl.java b/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/refund/PayRefundServiceImpl.java index 03d22943a..7624f203c 100755 --- a/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/refund/PayRefundServiceImpl.java +++ b/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/refund/PayRefundServiceImpl.java @@ -129,8 +129,9 @@ public class PayRefundServiceImpl implements PayRefundService { refundMapper.insert(refund); // 2.2 向渠道发起退款申请 PayOrderExtensionDO orderExtension = orderExtensionService.getOrderExtension(order.getSuccessExtensionId()); - PayRefundUnifiedReqDTO unifiedReqDTO = new PayRefundUnifiedReqDTO(); - unifiedReqDTO.setPrice(reqDTO.getPrice()) + PayRefundUnifiedReqDTO unifiedReqDTO = new PayRefundUnifiedReqDTO() + .setPayPrice(order.getPrice()) + .setRefundPrice(reqDTO.getPrice()) .setOutTradeNo(orderExtension.getNo()) .setOutRefundNo(refund.getNo()) .setNotifyUrl(genChannelPayNotifyUrl(channel)) // TODO 芋艿:优化下 notifyUrl diff --git a/yudao-module-pay/yudao-module-pay-biz/src/test/java/cn/iocoder/yudao/module/pay/service/order/PayOrderServiceTest.java b/yudao-module-pay/yudao-module-pay-biz/src/test/java/cn/iocoder/yudao/module/pay/service/order/PayOrderServiceTest.java index 8048e38ca..c225fa5b9 100755 --- a/yudao-module-pay/yudao-module-pay-biz/src/test/java/cn/iocoder/yudao/module/pay/service/order/PayOrderServiceTest.java +++ b/yudao-module-pay/yudao-module-pay-biz/src/test/java/cn/iocoder/yudao/module/pay/service/order/PayOrderServiceTest.java @@ -87,7 +87,7 @@ public class PayOrderServiceTest extends BaseDbUnitTest { o.setSuccessExtensionId(1L); o.setRefundStatus(PayOrderRefundStatusEnum.NO.getStatus()); o.setRefundTimes(0); - o.setRefundPrice(0L); + o.setRefundPrice(0); o.setChannelUserId("1008611"); o.setChannelOrderNo(channelOrderId); o.setUpdateTime(LocalDateTime.of(2018, 1, 1, 10, 10, 15)); @@ -155,7 +155,7 @@ public class PayOrderServiceTest extends BaseDbUnitTest { o.setSuccessExtensionId(1L); o.setRefundStatus(PayOrderRefundStatusEnum.NO.getStatus()); o.setRefundTimes(0); - o.setRefundPrice(0L); + o.setRefundPrice(0); o.setChannelUserId("1008611"); o.setChannelOrderNo(channelOrderId); o.setUpdateTime(LocalDateTime.of(2018, 1, 1, 10, 10, 15)); diff --git a/yudao-module-pay/yudao-module-pay-biz/src/test/java/cn/iocoder/yudao/module/pay/service/refund/PayRefundServiceTest.java b/yudao-module-pay/yudao-module-pay-biz/src/test/java/cn/iocoder/yudao/module/pay/service/refund/PayRefundServiceTest.java index 480256c07..e97cdc027 100755 --- a/yudao-module-pay/yudao-module-pay-biz/src/test/java/cn/iocoder/yudao/module/pay/service/refund/PayRefundServiceTest.java +++ b/yudao-module-pay/yudao-module-pay-biz/src/test/java/cn/iocoder/yudao/module/pay/service/refund/PayRefundServiceTest.java @@ -68,7 +68,6 @@ public class PayRefundServiceTest extends BaseDbUnitTest { o.setNotifyUrl("https://www.cancanzi.com"); o.setNotifyStatus(PayOrderNotifyStatusEnum.SUCCESS.getStatus()); o.setStatus(PayRefundStatusEnum.SUCCESS.getStatus()); - o.setType(PayOrderRefundStatusEnum.PART.getStatus()); o.setPayPrice(100); o.setRefundPrice(500); o.setReason("就是想退款了,你有意见吗"); @@ -93,8 +92,6 @@ public class PayRefundServiceTest extends BaseDbUnitTest { cloneIgnoreId(dbRefund, o -> o.setNotifyStatus(PayOrderNotifyStatusEnum.FAILURE.getStatus()))); // 测试 status 不匹配 refundMapper.insert(cloneIgnoreId(dbRefund, o -> o.setStatus(PayRefundStatusEnum.FAILURE.getStatus()))); - // 测试 type 不匹配 - refundMapper.insert(cloneIgnoreId(dbRefund, o -> o.setType(PayOrderRefundStatusEnum.ALL.getStatus()))); // 测试 createTime 不匹配 refundMapper.insert(cloneIgnoreId(dbRefund, o -> o.setCreateTime(LocalDateTime.of(2022, 1, 1, 10, 10, 10)))); @@ -105,7 +102,6 @@ public class PayRefundServiceTest extends BaseDbUnitTest { reqVO.setMerchantRefundNo("MRF0000001"); reqVO.setNotifyStatus(PayOrderNotifyStatusEnum.SUCCESS.getStatus()); reqVO.setStatus(PayRefundStatusEnum.SUCCESS.getStatus()); - reqVO.setType(PayOrderRefundStatusEnum.PART.getStatus()); reqVO.setCreateTime((new LocalDateTime[]{LocalDateTime.of(2021, 1, 1, 10, 10, 10), LocalDateTime.of(2021, 1, 1, 10, 10, 12)})); // 调用 @@ -130,7 +126,6 @@ public class PayRefundServiceTest extends BaseDbUnitTest { o.setNotifyUrl("https://www.cancanzi.com"); o.setNotifyStatus(PayOrderNotifyStatusEnum.SUCCESS.getStatus()); o.setStatus(PayRefundStatusEnum.SUCCESS.getStatus()); - o.setType(PayOrderRefundStatusEnum.PART.getStatus()); o.setPayPrice(100); o.setRefundPrice(500); o.setReason("就是想退款了,你有意见吗"); @@ -155,8 +150,6 @@ public class PayRefundServiceTest extends BaseDbUnitTest { cloneIgnoreId(dbRefund, o -> o.setNotifyStatus(PayOrderNotifyStatusEnum.FAILURE.getStatus()))); // 测试 status 不匹配 refundMapper.insert(cloneIgnoreId(dbRefund, o -> o.setStatus(PayRefundStatusEnum.FAILURE.getStatus()))); - // 测试 type 不匹配 - refundMapper.insert(cloneIgnoreId(dbRefund, o -> o.setType(PayOrderRefundStatusEnum.ALL.getStatus()))); // 测试 createTime 不匹配 refundMapper.insert(cloneIgnoreId(dbRefund, o -> o.setCreateTime(LocalDateTime.of(2022, 1, 1, 10, 10, 10)))); From 66ed61c6416e36adcc7227202130b86737bc9dda Mon Sep 17 00:00:00 2001 From: YunaiV Date: Mon, 17 Jul 2023 07:56:33 +0800 Subject: [PATCH 06/10] =?UTF-8?q?mall=20+=20pay=EF=BC=9A=201.=20=E5=BE=AE?= =?UTF-8?q?=E4=BF=A1=E6=94=AF=E4=BB=98=E9=85=8D=E7=BD=AE=EF=BC=8C=E8=A1=A5?= =?UTF-8?q?=E5=85=A8=20apiclient=5Fcert.p12=20=E8=AF=81=E4=B9=A6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../impl/weixin/AbstractWxPayClient.java | 17 ++- .../client/impl/weixin/WxPayClientConfig.java | 26 ++--- .../weixin/WxBarPayClientIntegrationTest.java | 6 +- .../WxNativePayClientIntegrationTest.java | 4 +- .../pay/app/components/wechatChannelForm.vue | 104 ++++++++++++------ yudao-ui-admin/src/views/pay/app/index.vue | 24 ++++ 6 files changed, 121 insertions(+), 60 deletions(-) diff --git a/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/weixin/AbstractWxPayClient.java b/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/weixin/AbstractWxPayClient.java index 5c7fa3940..01ae466c2 100644 --- a/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/weixin/AbstractWxPayClient.java +++ b/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/weixin/AbstractWxPayClient.java @@ -1,6 +1,7 @@ package cn.iocoder.yudao.framework.pay.core.client.impl.weixin; import cn.hutool.core.bean.BeanUtil; +import cn.hutool.core.codec.Base64; import cn.hutool.core.date.LocalDateTimeUtil; import cn.hutool.core.date.TemporalAccessorUtil; import cn.hutool.core.lang.Assert; @@ -34,6 +35,7 @@ import static cn.hutool.core.date.DatePattern.UTC_WITH_XXX_OFFSET_PATTERN; import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.invalidParamException; import static cn.iocoder.yudao.framework.common.util.json.JsonUtils.toJsonString; +import static cn.iocoder.yudao.framework.pay.core.client.impl.weixin.WxPayClientConfig.API_VERSION_V2; /** * 微信支付抽象类,实现微信统一的接口、以及部分实现(退款) @@ -59,12 +61,17 @@ public abstract class AbstractWxPayClient extends AbstractPayClient - * 注意,可通过 {@link #main(String[])} 读取 + * apiclient_cert.pem 证书文件的对应的字符串 */ @NotBlank(message = "apiclient_cert 不能为空", groups = V3.class) private String privateCertContent; /** * apiV3 密钥值 */ - @NotBlank(message = "apiV3 密钥值 不能为空", groups = V3.class) + @NotBlank(message = "apiV3 密钥值不能为空", groups = V3.class) private String apiV3Key; /** diff --git a/yudao-framework/yudao-spring-boot-starter-biz-pay/src/test/java/cn/iocoder/yudao/framework/pay/core/client/impl/weixin/WxBarPayClientIntegrationTest.java b/yudao-framework/yudao-spring-boot-starter-biz-pay/src/test/java/cn/iocoder/yudao/framework/pay/core/client/impl/weixin/WxBarPayClientIntegrationTest.java index d32879bc6..f36cd0b04 100644 --- a/yudao-framework/yudao-spring-boot-starter-biz-pay/src/test/java/cn/iocoder/yudao/framework/pay/core/client/impl/weixin/WxBarPayClientIntegrationTest.java +++ b/yudao-framework/yudao-spring-boot-starter-biz-pay/src/test/java/cn/iocoder/yudao/framework/pay/core/client/impl/weixin/WxBarPayClientIntegrationTest.java @@ -44,7 +44,7 @@ public class WxBarPayClientIntegrationTest { .totalFee(1) // 单位分 .timeExpire(formatDateV2(LocalDateTimeUtils.addTime(Duration.ofMinutes(2)))) .spbillCreateIp("127.0.0.1") - .authCode("131276541518138032") + .authCode("134298744426278497") .build(); System.out.println("========= request =========="); System.out.println(JsonUtils.toJsonPrettyString(request)); @@ -63,7 +63,7 @@ public class WxBarPayClientIntegrationTest { // 执行发起退款 WxPayRefundRequest request = new WxPayRefundRequest() - .setOutTradeNo("1689504162805") + .setOutTradeNo("1689545667276") .setOutRefundNo(String.valueOf(System.currentTimeMillis())) .setRefundFee(1) .setRefundDesc("就是想退了") @@ -103,8 +103,6 @@ public class WxBarPayClientIntegrationTest { config.setMchKey("dS1ngeN63JLr3NRbvPH9AJy3MyUxZdim"); config.setSignType(WxPayConstants.SignType.MD5); config.setKeyPath("/Users/yunai/Downloads/wx_pay/apiclient_cert.p12"); - config.setPrivateCertPath("/Users/yunai/Downloads/wx_pay/apiclient_cert.pem"); - config.setPrivateKeyPath("/Users/yunai/Downloads/wx_pay/apiclient_key.pem"); return config; } diff --git a/yudao-framework/yudao-spring-boot-starter-biz-pay/src/test/java/cn/iocoder/yudao/framework/pay/core/client/impl/weixin/WxNativePayClientIntegrationTest.java b/yudao-framework/yudao-spring-boot-starter-biz-pay/src/test/java/cn/iocoder/yudao/framework/pay/core/client/impl/weixin/WxNativePayClientIntegrationTest.java index e411a6236..5e73601c2 100644 --- a/yudao-framework/yudao-spring-boot-starter-biz-pay/src/test/java/cn/iocoder/yudao/framework/pay/core/client/impl/weixin/WxNativePayClientIntegrationTest.java +++ b/yudao-framework/yudao-spring-boot-starter-biz-pay/src/test/java/cn/iocoder/yudao/framework/pay/core/client/impl/weixin/WxNativePayClientIntegrationTest.java @@ -58,7 +58,7 @@ public class WxNativePayClientIntegrationTest { // 执行发起退款 WxPayRefundV3Request request = new WxPayRefundV3Request() - .setOutTradeNo("1689506153043") + .setOutTradeNo("1689545729695") .setOutRefundNo(String.valueOf(System.currentTimeMillis())) .setAmount(new WxPayRefundV3Request.Amount().setTotal(1).setRefund(1).setCurrency("CNY")) .setReason("就是想退了"); @@ -73,10 +73,8 @@ public class WxNativePayClientIntegrationTest { WxPayConfig config = new WxPayConfig(); config.setAppId("wx62056c0d5e8db250"); config.setMchId("1545083881"); - config.setMchKey("dS1ngeN63JLr3NRbvPH9AJy3MyUxZdim"); config.setApiV3Key("459arNsYHl1mgkiO6H9ZH5KkhFXSxaA4"); // config.setCertSerialNo(serialNo); - config.setKeyPath("/Users/yunai/Downloads/wx_pay/apiclient_cert.p12"); config.setPrivateCertPath("/Users/yunai/Downloads/wx_pay/apiclient_cert.pem"); config.setPrivateKeyPath("/Users/yunai/Downloads/wx_pay/apiclient_key.pem"); return config; diff --git a/yudao-ui-admin/src/views/pay/app/components/wechatChannelForm.vue b/yudao-ui-admin/src/views/pay/app/components/wechatChannelForm.vue index b28fd4d2b..af210e5d9 100644 --- a/yudao-ui-admin/src/views/pay/app/components/wechatChannelForm.vue +++ b/yudao-ui-admin/src/views/pay/app/components/wechatChannelForm.vue @@ -8,8 +8,8 @@ - - + + @@ -29,29 +29,41 @@ - - - -
- - + + - + + + + + + 点击上传 + + +
+
+ + + + 点击上传 @@ -64,18 +76,17 @@ 点击上传
- +