From cad508def69c402efe0b6d7179eeabd6d572d630 Mon Sep 17 00:00:00 2001 From: YunaiV Date: Mon, 10 Jul 2023 23:06:00 +0800 Subject: [PATCH] =?UTF-8?q?mall=20+=20pay=EF=BC=9A=201.=20bar=20=E6=89=AB?= =?UTF-8?q?=E7=A0=81=E6=94=AF=E4=BB=98=E6=88=90=E5=8A=9F=E5=90=8E=EF=BC=8C?= =?UTF-8?q?=E9=A2=9D=E5=A4=96=E8=BF=94=E5=9B=9E=20notify=202.=20notify=20?= =?UTF-8?q?=E5=9C=A8=E6=94=AF=E4=BB=98=E5=9B=9E=E8=B0=83=E6=97=B6=EF=BC=8C?= =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E5=B9=82=E7=AD=89=E5=A4=84=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../dto/order/PayOrderUnifiedRespDTO.java | 14 ++- .../client/impl/weixin/WxBarPayClient.java | 12 ++- .../module/pay/enums/ErrorCodeConstants.java | 1 + .../admin/order/vo/PayOrderSubmitRespVO.java | 8 +- .../app/order/AppPayOrderController.http | 2 +- .../pay/convert/order/PayOrderConvert.java | 2 +- .../service/order/PayOrderServiceImpl.java | 89 ++++++++++++------- .../src/views/pay/cashier/index.vue | 6 +- 8 files changed, 81 insertions(+), 53 deletions(-) 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 815f63ec3..441b4ece1 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,7 +1,7 @@ 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 cn.iocoder.yudao.framework.pay.core.enums.order.PayOrderStatusRespEnum; import lombok.Data; /** @@ -24,19 +24,15 @@ public class PayOrderUnifiedRespDTO { private String displayContent; /** - * 支付状态 + * 同步的通知信息 * - * 枚举 {@link PayOrderStatusRespEnum} 类 + * 目前只有 bar 条码支付才会出现,它是支付发起时,直接返回是否支付成功的,而其它支付还是异步通知 */ - private Integer status; + private PayOrderNotifyRespDTO notify; public PayOrderUnifiedRespDTO(String displayMode, String displayContent) { - this(displayMode, displayContent, PayOrderStatusRespEnum.WAITING.getStatus()); - } - - public PayOrderUnifiedRespDTO(String displayMode, String displayContent, Integer status) { this.displayMode = displayMode; this.displayContent = displayContent; - this.status = status; } + } 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 47df5625c..4dab16bfb 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 @@ -5,13 +5,13 @@ import cn.hutool.core.thread.ThreadUtil; import cn.hutool.core.util.StrUtil; import cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils; import cn.iocoder.yudao.framework.common.util.json.JsonUtils; +import cn.iocoder.yudao.framework.pay.core.client.dto.notify.PayOrderNotifyRespDTO; 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.PayRefundUnifiedReqDTO; import cn.iocoder.yudao.framework.pay.core.client.dto.refund.PayRefundUnifiedRespDTO; 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; import com.github.binarywang.wxpay.bean.request.WxPayMicropayRequest; import com.github.binarywang.wxpay.bean.result.WxPayMicropayResult; import com.github.binarywang.wxpay.constant.WxPayConstants; @@ -70,9 +70,15 @@ public class WxBarPayClient extends AbstractWxPayClient { try { WxPayMicropayResult response = client.micropay(request); // 支付成功(例如说,用户输入了密码) + PayOrderNotifyRespDTO notify = PayOrderNotifyRespDTO.builder() + .orderExtensionNo(response.getOutTradeNo()) + .channelOrderNo(response.getTransactionId()) + .channelUserId(response.getOpenid()) + .successTime(parseDateV2(response.getTimeEnd())) + .build(); return new PayOrderUnifiedRespDTO(PayOrderDisplayModeEnum.BAR_CODE.getMode(), - JsonUtils.toJsonString(response), - PayOrderStatusRespEnum.SUCCESS.getStatus()); + JsonUtils.toJsonString(response)) + .setNotify(notify); } catch (WxPayException ex) { // 如果不满足这 3 种任一的,则直接抛出 WxPayException 异常,不仅需处理 // 1. SYSTEMERROR:接口返回错误:请立即调用被扫订单结果查询API,查询当前订单状态,并根据订单的状态决定下一步的操作。 diff --git a/yudao-module-pay/yudao-module-pay-api/src/main/java/cn/iocoder/yudao/module/pay/enums/ErrorCodeConstants.java b/yudao-module-pay/yudao-module-pay-api/src/main/java/cn/iocoder/yudao/module/pay/enums/ErrorCodeConstants.java index fe2beb50b..3d73f181e 100644 --- a/yudao-module-pay/yudao-module-pay-api/src/main/java/cn/iocoder/yudao/module/pay/enums/ErrorCodeConstants.java +++ b/yudao-module-pay/yudao-module-pay-api/src/main/java/cn/iocoder/yudao/module/pay/enums/ErrorCodeConstants.java @@ -25,6 +25,7 @@ public interface ErrorCodeConstants { ErrorCode PAY_ORDER_NOT_FOUND = new ErrorCode(1007002000, "支付订单不存在"); ErrorCode PAY_ORDER_STATUS_IS_NOT_WAITING = new ErrorCode(1007002001, "支付订单不处于待支付"); ErrorCode PAY_ORDER_STATUS_IS_NOT_SUCCESS = new ErrorCode(1007002002, "支付订单不处于已支付"); + ErrorCode PAY_ORDER_IS_EXPIRED = new ErrorCode(1007002003, "支付订单已经过期"); // ========== ORDER 模块(拓展单) 1007003000 ========== ErrorCode PAY_ORDER_EXTENSION_NOT_FOUND = new ErrorCode(1007003000, "支付交易拓展单不存在"); diff --git a/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/controller/admin/order/vo/PayOrderSubmitRespVO.java b/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/controller/admin/order/vo/PayOrderSubmitRespVO.java index 8122b9667..8dcd9df2f 100644 --- a/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/controller/admin/order/vo/PayOrderSubmitRespVO.java +++ b/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/controller/admin/order/vo/PayOrderSubmitRespVO.java @@ -1,19 +1,17 @@ package cn.iocoder.yudao.module.pay.controller.admin.order.vo; import io.swagger.v3.oas.annotations.media.Schema; -import lombok.AllArgsConstructor; -import lombok.Builder; import lombok.Data; -import lombok.NoArgsConstructor; -import lombok.experimental.Accessors; @Schema(description = "管理后台 - 支付订单提交 Response VO") @Data public class PayOrderSubmitRespVO { + @Schema(description = "支付状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "10") // 参见 PayOrderStatusEnum 枚举 + private Integer status; + @Schema(description = "展示模式", requiredMode = Schema.RequiredMode.REQUIRED, example = "url") // 参见 PayDisplayModeEnum 枚举 private String displayMode; - @Schema(description = "展示内容", requiredMode = Schema.RequiredMode.REQUIRED) private String displayContent; diff --git a/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/controller/app/order/AppPayOrderController.http b/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/controller/app/order/AppPayOrderController.http index 8b4008455..14ce54ef9 100644 --- a/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/controller/app/order/AppPayOrderController.http +++ b/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/controller/app/order/AppPayOrderController.http @@ -19,7 +19,7 @@ tenant-id: {{appTenentId}} "id": 202, "channelCode": "wx_bar", "channelExtras": { - "authCode": "132990241553789274" + "authCode": "134042110834344848" } } diff --git a/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/convert/order/PayOrderConvert.java b/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/convert/order/PayOrderConvert.java index 9dd4dd019..ad45cd905 100755 --- a/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/convert/order/PayOrderConvert.java +++ b/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/convert/order/PayOrderConvert.java @@ -93,7 +93,7 @@ public interface PayOrderConvert { PayOrderUnifiedReqDTO convert2(PayOrderSubmitReqVO reqVO, String userIp); - PayOrderSubmitRespVO convert(PayOrderUnifiedRespDTO bean); + PayOrderSubmitRespVO convert(PayOrderDO order, PayOrderUnifiedRespDTO unifiedRespDTO); AppPayOrderSubmitRespVO convert3(PayOrderSubmitRespVO bean); 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 10206f859..aa84882de 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 @@ -1,9 +1,11 @@ package cn.iocoder.yudao.module.pay.service.order; import cn.hutool.core.date.DateUtil; +import cn.hutool.core.lang.Pair; import cn.hutool.core.util.ObjectUtil; import cn.hutool.core.util.RandomUtil; import cn.iocoder.yudao.framework.common.pojo.PageResult; +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; @@ -28,6 +30,7 @@ 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.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; @@ -41,6 +44,7 @@ import javax.annotation.Resource; import java.time.LocalDateTime; import java.util.Collection; 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; @@ -109,22 +113,19 @@ public class PayOrderServiceImpl implements PayOrderService { } // 创建支付交易单 - order = PayOrderConvert.INSTANCE.convert(reqDTO).setAppId(app.getId()); - // 商户相关字段 - order.setNotifyUrl(app.getPayNotifyUrl()) - .setNotifyStatus(PayOrderNotifyStatusEnum.NO.getStatus()); - // 订单相关字段 - order.setStatus(PayOrderStatusEnum.WAITING.getStatus()); - // 退款相关字段 - // todo @芋艿 创建支付的订单的退款状态枚举是不是有问题,应该是 PayRefundTypeEnum 吧 您这填写的是 PayOrderNotifyStatusEnum 回调状态枚举 - order.setRefundStatus(PayOrderNotifyStatusEnum.NO.getStatus()) - .setRefundTimes(0).setRefundPrice(0L); + order = PayOrderConvert.INSTANCE.convert(reqDTO).setAppId(app.getId()) + // 商户相关字段 + .setNotifyUrl(app.getPayNotifyUrl()).setNotifyStatus(PayOrderNotifyStatusEnum.NO.getStatus()) + // 订单相关字段 + .setStatus(PayOrderStatusEnum.WAITING.getStatus()) + // 退款相关字段 + .setRefundStatus(PayRefundTypeEnum.NO.getStatus()).setRefundTimes(0).setRefundPrice(0L); orderMapper.insert(order); - // 最终返回 return order.getId(); } @Override + @Transactional(rollbackFor = Exception.class) public PayOrderSubmitRespVO submitPayOrder(PayOrderSubmitReqVO reqVO, String userIp) { // 1. 获得 PayOrderDO ,并校验其是否存在 PayOrderDO order = validatePayOrderCanSubmit(reqVO.getId()); @@ -150,9 +151,15 @@ public class PayOrderServiceImpl implements PayOrderService { .setAmount(order.getPrice()).setExpireTime(order.getExpireTime()); PayOrderUnifiedRespDTO unifiedOrderRespDTO = client.unifiedOrder(unifiedOrderReqDTO); - // TODO 轮询三方接口,是否已经支付的任务 + // 4. 如果调用直接支付成功,则直接更新支付单状态为成功。例如说:付款码支付,免密支付时,就直接验证支付成功 + if (unifiedOrderRespDTO.getNotify() != null) { + notifyPayOrderSuccess(channel, unifiedOrderRespDTO.getNotify(), null); + // 此处需要读取最新的状态 + order = orderMapper.selectById(order.getId()); + } + // 返回成功 - return PayOrderConvert.INSTANCE.convert(unifiedOrderRespDTO); + return PayOrderConvert.INSTANCE.convert(order, unifiedOrderRespDTO); } private PayOrderDO validatePayOrderCanSubmit(Long id) { @@ -163,6 +170,9 @@ public class PayOrderServiceImpl implements PayOrderService { if (!PayOrderStatusEnum.WAITING.getStatus().equals(order.getStatus())) { // 校验状态,必须是待支付 throw exception(ErrorCodeConstants.PAY_ORDER_STATUS_IS_NOT_WAITING); } + if (LocalDateTimeUtils.beforeNow(order.getExpireTime())) { // 校验是否过期 + throw exception(ErrorCodeConstants.PAY_ORDER_IS_EXPIRED); + } return order; } @@ -214,17 +224,22 @@ public class PayOrderServiceImpl implements PayOrderService { public void notifyPayOrder(Long channelId, PayOrderNotifyRespDTO notify, PayNotifyReqDTO rawNotify) { // 校验支付渠道是否有效 PayChannelDO channel = channelService.validPayChannel(channelId); - TenantUtils.execute(channel.getTenantId(), () -> { - // 1. 更新 PayOrderExtensionDO 支付成功 - PayOrderExtensionDO orderExtension = updatePayOrderExtensionSuccess(notify.getOrderExtensionNo(), - rawNotify); - // 2. 更新 PayOrderDO 支付成功 - PayOrderDO order = updatePayOrderSuccess(channel, orderExtension, notify); + // 更新支付订单为已支付 + TenantUtils.execute(channel.getTenantId(), () -> notifyPayOrderSuccess(channel, notify, rawNotify)); + } - // 3. 插入支付通知记录 - notifyService.createPayNotifyTask(PayNotifyTaskCreateReqDTO.builder() - .type(PayNotifyTypeEnum.ORDER.getType()).dataId(order.getId()).build()); - }); + private void notifyPayOrderSuccess(PayChannelDO channel, PayOrderNotifyRespDTO notify, PayNotifyReqDTO rawNotify) { + // 1. 更新 PayOrderExtensionDO 支付成功 + PayOrderExtensionDO orderExtension = updatePayOrderExtensionSuccess(notify.getOrderExtensionNo(), rawNotify); + // 2. 更新 PayOrderDO 支付成功 + Pair order = updatePayOrderSuccess(channel, orderExtension, notify); + if (order.getKey()) { // 如果之前已经成功回调,则直接返回,不用重复记录支付通知记录;例如说:支付平台重复回调 + return; + } + + // 3. 插入支付通知记录 + notifyService.createPayNotifyTask(PayNotifyTaskCreateReqDTO.builder() + .type(PayNotifyTypeEnum.ORDER.getType()).dataId(order.getValue().getId()).build()); } /** @@ -235,15 +250,20 @@ public class PayOrderServiceImpl implements PayOrderService { * @return PayOrderExtensionDO 对象 */ private PayOrderExtensionDO updatePayOrderExtensionSuccess(String no, PayNotifyReqDTO rawNotify) { - // 1.1 查询 PayOrderExtensionDO + // 1. 查询 PayOrderExtensionDO PayOrderExtensionDO orderExtension = orderExtensionMapper.selectByNo(no); if (orderExtension == null) { throw exception(ErrorCodeConstants.PAY_ORDER_EXTENSION_NOT_FOUND); } + if (PayOrderStatusEnum.isSuccess(orderExtension.getStatus())) { // 如果已经是成功,直接返回,不用重复更新 + log.info("[updatePayOrderSuccess][支付拓展单({}) 已经是已支付,无需更新为已支付]", orderExtension.getId()); + return orderExtension; + } if (ObjectUtil.notEqual(orderExtension.getStatus(), PayOrderStatusEnum.WAITING.getStatus())) { // 校验状态,必须是待支付 throw exception(ErrorCodeConstants.PAY_ORDER_EXTENSION_STATUS_IS_NOT_WAITING); } - // 1.2 更新 PayOrderExtensionDO + + // 2. 更新 PayOrderExtensionDO int updateCounts = orderExtensionMapper.updateByIdAndStatus(orderExtension.getId(), PayOrderStatusEnum.WAITING.getStatus(), PayOrderExtensionDO.builder().id(orderExtension.getId()) .status(PayOrderStatusEnum.SUCCESS.getStatus()) @@ -261,19 +281,26 @@ public class PayOrderServiceImpl implements PayOrderService { * @param channel 支付渠道 * @param orderExtension 支付拓展单 * @param notify 通知回调 - * @return PayOrderDO 对象 + * @return key:是否之前已经成功回调 + * value:PayOrderDO 对象 */ - private PayOrderDO updatePayOrderSuccess(PayChannelDO channel, PayOrderExtensionDO orderExtension, - PayOrderNotifyRespDTO notify) { - // 2.1 判断 PayOrderDO 是否处于待支付 + private Pair updatePayOrderSuccess(PayChannelDO channel, PayOrderExtensionDO orderExtension, + PayOrderNotifyRespDTO notify) { + // 1. 判断 PayOrderDO 是否处于待支付 PayOrderDO order = orderMapper.selectById(orderExtension.getOrderId()); if (order == null) { throw exception(ErrorCodeConstants.PAY_ORDER_NOT_FOUND); } + if (PayOrderStatusEnum.isSuccess(order.getStatus()) // 如果已经是成功,直接返回,不用重复更新 + && Objects.equals(order.getSuccessExtensionId(), orderExtension.getId())) { + log.info("[updatePayOrderSuccess][支付订单({}) 已经是已支付,无需更新为已支付]", order.getId()); + return Pair.of(true, order); + } if (!PayOrderStatusEnum.WAITING.getStatus().equals(order.getStatus())) { // 校验状态,必须是待支付 throw exception(ErrorCodeConstants.PAY_ORDER_STATUS_IS_NOT_WAITING); } - // 2.2 更新 PayOrderDO + + // 2. 更新 PayOrderDO int updateCounts = orderMapper.updateByIdAndStatus(order.getId(), PayOrderStatusEnum.WAITING.getStatus(), PayOrderDO.builder().status(PayOrderStatusEnum.SUCCESS.getStatus()) .channelId(channel.getId()).channelCode(channel.getCode()) @@ -284,7 +311,7 @@ public class PayOrderServiceImpl implements PayOrderService { throw exception(ErrorCodeConstants.PAY_ORDER_STATUS_IS_NOT_WAITING); } log.info("[updatePayOrderSuccess][支付订单({}) 更新为已支付]", order.getId()); - return order; + return Pair.of(false, order); } } diff --git a/yudao-ui-admin/src/views/pay/cashier/index.vue b/yudao-ui-admin/src/views/pay/cashier/index.vue index 3f2048473..0c5ba5c88 100644 --- a/yudao-ui-admin/src/views/pay/cashier/index.vue +++ b/yudao-ui-admin/src/views/pay/cashier/index.vue @@ -27,7 +27,7 @@
- +
{{ channel.name }}
@@ -36,7 +36,7 @@
- +
{{ channel.name }}
@@ -132,7 +132,7 @@ export default { code: "wx_lite" }, { name: '微信 App 支付', - icon: require("@/assets/images/pay/icon/wx_lite.svg"), + icon: require("@/assets/images/pay/icon/wx_app.svg"), code: "wx_app" }, { name: '模拟支付',